diff options
246 files changed, 6071 insertions, 2529 deletions
diff --git a/.gitignore b/.gitignore index 565a3a3839..c925f19f41 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ /exported /exported-* /hostnamectl +/image.raw /install-tree /journalctl /libtool diff --git a/.mkosi/mkosi.arch b/.mkosi/mkosi.arch new file mode 100644 index 0000000000..4c44f288ae --- /dev/null +++ b/.mkosi/mkosi.arch @@ -0,0 +1,67 @@ +# This file is part of systemd. +# +# Copyright 2016 Zeal Jagannatha +# +# 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/>. + +# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). +# Simply invoke "mkosi" in the project directory to build an OS image. + +[Distribution] +Distribution=arch + +[Output] +Format=raw_btrfs +Bootable=yes + +[Partitions] +RootSize=2G + +[Packages] +Cache=/var/cache/pacman/pkg/ +BuildPackages= + acl + autoconf + automake + bzip2 + cryptsetup + curl + dbus + docbook-xsl + elfutils + gcc + git + gnu-efi-libs + gnutls + gperf + intltool + iptables + kmod + libcap + libgcrypt + libidn + libmicrohttpd + libseccomp + libtool + libutil-linux + libxkbcommon + libxslt + lz4 + make + pam + pkgconfig + python + python-lxml + qrencode + xz diff --git a/.mkosi/mkosi.fedora b/.mkosi/mkosi.fedora new file mode 100644 index 0000000000..0af20c924a --- /dev/null +++ b/.mkosi/mkosi.fedora @@ -0,0 +1,71 @@ +# This file is part of systemd. +# +# Copyright 2016 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/>. + +# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). +# Simply invoke "mkosi" in the project directory to build an OS image. + +[Distribution] +Distribution=fedora +Release=25 + +[Output] +Format=raw_btrfs +Bootable=yes + +[Partitions] +RootSize=2G + +[Packages] +BuildPackages= + audit-libs-devel + autoconf + automake + bzip2-devel + cryptsetup-devel + dbus-devel + docbook-style-xsl + elfutils-devel + gcc + git + gnu-efi + gnu-efi-devel + gnutls-devel + gperf + intltool + iptables-devel + kmod-devel + libacl-devel + libblkid-devel + libcap-devel + libcurl-devel + libgcrypt-devel + libidn-devel + libmicrohttpd-devel + libmount-devel + libseccomp-devel + libselinux-devel + libtool + libxkbcommon-devel + libxslt + lz4-devel + make + pam-devel + pkgconfig + python3-devel + python3-lxml + qrencode-devel + xz-devel diff --git a/DISTRO_PORTING b/DISTRO_PORTING index a397d700c6..9f61bd6de6 100644 --- a/DISTRO_PORTING +++ b/DISTRO_PORTING @@ -41,6 +41,13 @@ NTP POOL: NTP servers, then you will get served wrong time, and will rely on services that might not be supported for long. +PAM: + The default PAM config shipped by systemd is really bare bones. + It does not include many modules your distro might want to enable + to provide a more seamless experience. For example, limits set in + /etc/security/limits.conf will not be read unless you load pam_limits. + Make sure you add modules your distro expects from user services. + CONTRIBUTING UPSTREAM: We generally do no longer accept distribution-specific diff --git a/Makefile.am b/Makefile.am index 3a617560e0..b09b0cf167 100644 --- a/Makefile.am +++ b/Makefile.am @@ -157,6 +157,7 @@ nodist_zshcompletion_DATA = $(nodist_zshcompletion_data) endif udevlibexec_PROGRAMS = gperf_gperf_sources = +rootlib_LTLIBRARIES = in_files = $(filter %.in,$(EXTRA_DIST)) in_in_files = $(filter %.in.in, $(in_files)) @@ -250,16 +251,6 @@ AM_CFLAGS = $(OUR_CFLAGS) AM_LDFLAGS = $(OUR_LDFLAGS) # ------------------------------------------------------------------------------ -define move-to-rootlibdir - if test "$(libdir)" != "$(rootlibdir)"; then \ - $(MKDIR_P) $(DESTDIR)$(rootlibdir) && \ - so_img_name=$$(readlink $(DESTDIR)$(libdir)/$$libname) && \ - rm -f $(DESTDIR)$(libdir)/$$libname && \ - $(LN_S) --relative -f $(DESTDIR)$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/$$libname && \ - mv $(DESTDIR)$(libdir)/$$libname.* $(DESTDIR)$(rootlibdir); \ - fi -endef - INSTALL_DIRS = SHUTDOWN_TARGET_WANTS = @@ -533,7 +524,6 @@ dist_systemunit_DATA_busnames += \ nodist_systemunit_DATA = \ units/getty@.service \ units/serial-getty@.service \ - units/console-shell.service \ units/console-getty.service \ units/container-getty@.service \ units/systemd-initctl.service \ @@ -596,7 +586,6 @@ dist_systempreset_DATA = \ EXTRA_DIST += \ units/getty@.service.m4 \ units/serial-getty@.service.m4 \ - units/console-shell.service.m4.in \ units/console-getty.service.m4.in \ units/container-getty@.service.m4.in \ units/rescue.service.in \ @@ -1648,8 +1637,14 @@ EXTRA_DIST += \ test/test-execute/exec-personality-aarch64.service \ test/test-execute/exec-privatedevices-no.service \ test/test-execute/exec-privatedevices-yes.service \ + test/test-execute/exec-privatedevices-no-capability-mknod.service \ + test/test-execute/exec-privatedevices-yes-capability-mknod.service \ test/test-execute/exec-privatetmp-no.service \ test/test-execute/exec-privatetmp-yes.service \ + test/test-execute/exec-readonlypaths.service \ + test/test-execute/exec-readonlypaths-mount-propagation.service \ + test/test-execute/exec-readwritepaths-mount-propagation.service \ + test/test-execute/exec-inaccessiblepaths-mount-propagation.service \ test/test-execute/exec-spec-interpolation.service \ test/test-execute/exec-systemcallerrornumber.service \ test/test-execute/exec-systemcallfilter-failing2.service \ @@ -3283,15 +3278,6 @@ libsystemd_la_LIBADD = \ libbasic.la \ libsystemd-journal-internal.la -libsystemd-install-hook: - libname=libsystemd.so && $(move-to-rootlibdir) - -libsystemd-uninstall-hook: - rm -f $(DESTDIR)$(rootlibdir)/libsystemd.so* - -INSTALL_EXEC_HOOKS += libsystemd-install-hook -UNINSTALL_EXEC_HOOKS += libsystemd-uninstall-hook - pkgconfiglib_DATA += \ src/libsystemd/libsystemd.pc @@ -3304,7 +3290,7 @@ pkginclude_HEADERS += \ src/systemd/sd-id128.h \ src/systemd/sd-daemon.h -lib_LTLIBRARIES += \ +rootlib_LTLIBRARIES += \ libsystemd.la tests += \ @@ -3636,7 +3622,7 @@ tests += \ include_HEADERS += \ src/libudev/libudev.h -lib_LTLIBRARIES += \ +rootlib_LTLIBRARIES += \ libudev.la libudev_la_SOURCES =\ @@ -3668,16 +3654,6 @@ pkgconfiglib_DATA += \ EXTRA_DIST += \ src/libudev/libudev.pc.in -# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed -libudev-install-hook: - libname=libudev.so && $(move-to-rootlibdir) - -libudev-uninstall-hook: - rm -f $(DESTDIR)$(rootlibdir)/libudev.so* - -INSTALL_EXEC_HOOKS += libudev-install-hook -UNINSTALL_EXEC_HOOKS += libudev-uninstall-hook - # ------------------------------------------------------------------------------ noinst_LTLIBRARIES += \ libudev-internal.la @@ -5056,7 +5032,7 @@ libnss_systemd_la_LIBADD = \ libsystemd-internal.la \ libbasic.la -lib_LTLIBRARIES += \ +rootlib_LTLIBRARIES += \ libnss_systemd.la # ------------------------------------------------------------------------------ @@ -5078,7 +5054,7 @@ libnss_myhostname_la_LIBADD = \ libsystemd-internal.la \ libbasic.la -lib_LTLIBRARIES += \ +rootlib_LTLIBRARIES += \ libnss_myhostname.la endif @@ -5177,7 +5153,7 @@ libnss_mymachines_la_LIBADD = \ libsystemd-internal.la \ libbasic.la -lib_LTLIBRARIES += \ +rootlib_LTLIBRARIES += \ libnss_mymachines.la endif @@ -5476,7 +5452,7 @@ libnss_resolve_la_LIBADD = \ libbasic.la \ -ldl -lib_LTLIBRARIES += \ +rootlib_LTLIBRARIES += \ libnss_resolve.la systemd_resolve_SOURCES = \ @@ -5650,6 +5626,8 @@ libnetworkd_core_la_SOURCES = \ src/network/networkd-netdev-bond.c \ src/network/networkd-netdev-bridge.h \ src/network/networkd-netdev-bridge.c \ + src/network/networkd-netdev-vcan.h \ + src/network/networkd-netdev-vcan.c \ src/network/networkd-link-bus.c \ src/network/networkd-ipv4ll.c \ src/network/networkd-dhcp4.c \ @@ -6576,7 +6554,7 @@ print-%: @echo $($*) git-contrib: - @git shortlog -s `git describe --abbrev=0`.. | cut -c8- | awk '{ print $$0 "," }' | sort -u + @git shortlog -s `git describe --abbrev=0`.. | cut -c8- | sed 's/ / /g' | awk '{ print $$0 "," }' | sort -u EXTRA_DIST += \ tools/gdb-sd_dump_hashmaps.py @@ -2,11 +2,185 @@ systemd System and Service Manager CHANGES WITH 232 in spe + * The new RemoveIPC= option can be used to remove IPC objects owned by + the user or group of a service when that service exits. + + * ProtectSystem= option gained a new value "strict", which causes the + whole file system tree with the exception of /dev, /proc, and /sys, + to be remounted read-only for a service. + + The new ProtectedKernelTunables= options can be used to disable + modification of configuration files in /sys and /proc by a service. + Various directories and files are remounted read-only, so access is + restricted even if the file permissions would allow it. + + The new ProtectControlGroups= option can be used to disable write + access by a service to /sys/fs/cgroup. + + * Various systemd services have been hardened with + ProtectKernelTunables=yes, ProtectControlGroups=yes, + RestrictAddressFamilies=. + + * Support for dynamically creating users for the lifetime of a service + has been added. If DynamicUser=yes is specified, user and group IDs + will be allocated from the range 61184..65519 for the lifetime of the + service. They can be resolved using the new nss-systemd.so NSS + module. The module must be enabled in /etc/nsswitch.conf. Services + started in this way have PrivateTmp= and RemoveIPC= enabled, so that + any resources allocated by the service will be cleaned up when the + service exits. They also have ProtectHome=read-only and + ProtectSystem=strict enabled, so they are not able to make any + permanent modifications to the system. + + The nss-systemd module also always resolves root and nobody, making + it possible to have no /etc/passwd or /etc/group files in minimal + container systems. + + * Services may be started with their own user namespace using the new + PrivateUsers= option. Only root, nobody, and the uid/gid under which + the service is running are mapped. All other users are mapped to + nobody. + + * Support for the cgroup namespace has been added to systemd-nspawn. If + supported by kernel, the container system started by systemd-nspawn + will have its own view of the cgroup hierarchy. This new behaviour + can be disabled using $SYSTEMD_NSPAWN_USE_CGNS environment variable. + + * The new MemorySwapMax= option can be used to limit the maximum swap + usage under the unified cgroup hierarchy. + + * Support for the CPU controller in the unified cgroup hierarchy has + been added, via the CPUWeight=, CPUStartupWeight=, CPUAccounting= + options. This controller requires out-of-tree patches for the kernel + and the support is provisional. + + * .automount units may now be transient. + + * systemd-mount is a new tool which wraps mount(8) to pull in + additional dependencies through transient .mount and .automount + units. For example, this automatically runs fsck on the block device + before mounting, and allows the automount logic to be used. + + * LazyUnmount=yes option for mount units has been added to expose the + umount --lazy option. Similarly, ForceUnmount=yes exposes the --force + option. + + * /efi will be used as the mount point of the EFI boot partition, if + the directory is present, and the mount point was not configured + through other means (e.g. fstab). If /efi directory does not exist, + /boot will be used as before. This makes it easier to automatically + mount the EFI partition on systems where /boot is used for something + else. + + * disk/by-id and disk/by-path symlinks are now created for NVMe drives. + + * Two new user session targets have been added to support running + graphical sessions under the systemd --user instance: + graphical-session.target and graphical-session-pre.target. See + systemd.special(7) for a description of how those targets should be + used. + + * The vconsole initialization code has been significantly reworked to + use KD_FONT_OP_GET/SET ioctls instead of KD_FONT_OP_COPY and better + support unicode keymaps. Font and keymap configuration will now be + copied to all allocated virtual consoles. + + * FreeBSD's bhyve virtualization is now detected. + + * Information recorded in the journal for core dumps now includes the + contents of /proc/mountinfo and the command line of the process at + the top of the process hierarchy (which is usually the init process + of the container). + + * systemd-journal-gatewayd learned the --directory option to serve + files from the specified location. + + * journalctl --root=… can be used to peruse the journal in the + /var/log/ directories inside of a container tree. This is similar to + the existing --machine= option, but does not require the container to + be active. + + * The hardware database has been extended to support + ID_INPUT_TRACKBALL, used in addition to ID_INPUT_MOUSE to identify + trackball devices. + + MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL hwdb property has been added to + specify the click rate for mice which include a horizontal wheel with + a click rate that is different than the one for the vertical wheel. + + * systemd-run gained a new --wait option that makes service execution + synchronous. + + systemctl gained a new --wait option that causes the start command to + wait until the units being started have terminated again. + + * A new journal output mode "short-full" has been added which uses + timestamps with abbreviated English day names and adds a timezone + suffix. Those timestamps include more information and can be parsed + by journalctl. + + * /etc/resolv.conf will be bind-mounted into containers started by + systemd-nspawn, if possible, so any changes to resolv.conf contents + are automatically propagated to the container. + + * The number of instances for socket-activated services originating + from a single IP can be limited with MaxConnectionsPerSource=, + extending the existing setting of MaxConnections. + + * systemd-networkd gained support for vcan ("Virtual CAN") interface + configuration. + + * .netdev and .network configuration can now be extended through + drop-ins. + + * UDP Segmentation Offload, TCP Segmentation Offload, Generic + Segmentation Offload, Generic Receive Offload, Large Receive Offload + can be enabled and disabled using the new UDPSegmentationOffload=, + TCPSegmentationOffload=, GenericSegmentationOffload=, + GenericReceiveOffload=, LargeReceiveOffload= options in the + [Link] section of .link files. + + Spanning Tree Protocol enablement, Priority, Aging Time, and the + Default Port VLAN ID can be configured for bridge devices using the + new STP=, Priority=, AgeingTimeSec=, and DefaultPVID= settings in the + [Bridge] section of .netdev files. + + The route table to which routes received over DHCP or RA should be + added can be configured with the new RouteTable= option in the [DHCP] + and [IPv6AcceptRA] sections of .network files. + + Address Resolution Protocol can be disabled on links managed by + systemd-networkd using the ARP=no setting in the [Link] section of + .network files. + + * $SERVICE_RESULT, $EXIT_CODE, $EXIT_STATUS are set for ExecStop= and + ExecStopPost= commands. + + * systemd-sysctl will now configure kernel parameters in the order + they occur in the configuration files. This matches what sysctl + has been traditionally doing. + + * kernel-install "plugins" that are executed to perform various + tasks after a new kernel is added and before an old one is removed + can now return a special value to terminate the procedure and + prevent any later plugins from running. + * Journald's SplitMode=login setting has been deprecated. It has been - removed from documentation, and it's use is discouraged. In a future + removed from documentation, and its use is discouraged. In a future release it will be completely removed, and made equivalent to current default of SplitMode=uid. + * Storage=both option setting in /etc/systemd/coredump.conf has been + removed. With fast LZ4 compression storing the core dump twice is not + useful. + + * The --share-system systemd-nspawn option has been replaced with an + (undocumented) variable $SYSTEMD_NSPAWN_SHARE_SYSTEM, but the use of + this functionality is discouraged. In addition the variables + $SYSTEMD_NSPAWN_SHARE_NS_IPC, $SYSTEMD_NSPAWN_SHARE_NS_PID, + $SYSTEMD_NSPAWN_SHARE_NS_UTS may be used to control the unsharing of + individual namespaces. + CHANGES WITH 231: * In service units the various ExecXYZ= settings have been extended @@ -223,6 +397,9 @@ CHANGES WITH 231: local changes made to systemd in a pristine, defined environment. See HACKING for details. + * configure learned the --with-support-url= option to specify the + distribution's bugtracker. + Contributions from: Alban Crequy, Alessandro Puccetti, Alessio Igor Bogani, Alexander Kuleshov, Alexander Kurtz, Alex Gaynor, Andika Triwidada, Andreas Pokorny, Andreas Rammhold, Andrew Jeddeloh, Ansgar @@ -120,7 +120,7 @@ REQUIREMENTS: libcap libmount >= 2.27.1 (from util-linux) (util-linux *must* be built with --enable-libmount-force-mountinfo) - libseccomp >= 1.0.0 (optional) + libseccomp >= 2.3.1 (optional) libblkid >= 2.24 (from util-linux) (optional) libkmod >= 15 (optional) PAM >= 1.1.2 (optional) @@ -23,16 +23,23 @@ External: Janitorial Clean-ups: -* code cleanup: retire FOREACH_WORD_QUOTED, port to extract_first_word() loops instead +* code cleanup: retire FOREACH_WORD_QUOTED, port to extract_first_word() loops instead. + For example, most conf parsing callbacks should use it. * replace manual readdir() loops with FOREACH_DIRENT or FOREACH_DIRENT_ALL -* Get rid of the last strerror() invocations in favour of %m and strerror_r() - * Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again Features: +* on cgroupsv2 add DelegateControllers=, to pick the precise cgroup controllers to delegate + +* in networkd, when matching device types, fix up DEVTYPE rubbish the kernel passes to us + +* enable LockMLOCK to take a percentage value relative to physical memory + +* switch to ProtectSystem=strict for all our long-running services where that's possible + * introduce an "invocation ID" for units, that is randomly generated, and identifies each runtime-cycle of a unit. It should be set freshly each time we traverse inactive → activating/active, and should be the primary key to @@ -41,8 +48,9 @@ Features: the cgroup of a services. The former is accessible without privileges, the latter ensures the ID cannot be faked. -* Introduce ProtectSystem=strict for making the entire OS hierarchy read-only - except for a select few +* If RootDirectory= is used, mount /proc, /sys, /dev into it, if not mounted yet + +* Permit masking specific netlink APIs with RestrictAddressFamily= * nspawn: start UID allocation loop from hash of container name @@ -56,15 +64,13 @@ Features: * ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc -* ProtectMount= (drop mount/umount/pivot_root from seccomp, disallow fuse via DeviceAllow, imply Mountflags=slave) - -* ProtectDevices= should also take iopl/ioperm/pciaccess away +* ProtectKernelModules= (drops CAP_SYS_MODULE and filters the kmod syscalls) -* ProtectKeyRing= to take keyring calls away +* ProtectTracing= (drops CAP_SYS_PTRACE, blocks ptrace syscall, makes /sys/kernel/tracing go away) -* ProtectControlGroups= which mounts all of /sys/fs/cgroup read-only +* ProtectMount= (drop mount/umount/pivot_root from seccomp, disallow fuse via DeviceAllow, imply Mountflags=slave) -* ProtectKernelTunables= which mounts /sys and /proc/sys read-only +* ProtectKeyRing= to take keyring calls away * RemoveKeyRing= to remove all keyring entries of the specified user @@ -73,9 +79,6 @@ Features: * Add BindDirectory= for allowing arbitrary, private bind mounts for services -* Beef up RootDirectory= to use namespacing/bind mounts as soon as fs - namespaces are enabled by the service - * Add RootImage= for mounting a disk image or file as root directory * RestrictNamespaces= or so in services (taking away the ability to create namespaces, with setns, unshare, clone) @@ -114,8 +117,6 @@ Features: * journald: sigbus API via a signal-handler safe function that people may call from the SIGBUS handler -* when using UTF8, ellipsize with "…" rather than "...", so that we can show more contents before truncating - * move specifier expansion from service_spawn() into load-fragment.c * optionally, also require WATCHDOG=1 notifications during service start-up and shutdown @@ -144,8 +145,6 @@ Features: * PID 1 should send out sd_notify("WATCHDOG=1") messages (for usage in the --user mode, and when run via nspawn) -* consider throwing a warning if a service declares it wants to be "Before=" a .device unit. - * there's probably something wrong with having user mounts below /sys, as we have for debugfs. for exmaple, src/core/mount.c handles mounts prefixed with /sys generally special. @@ -183,7 +182,7 @@ Features: * implement a per-service firewall based on net_cls * Port various tools to make use of verbs.[ch], where applicable: busctl, - bootctl, coredumpctl, hostnamectl, localectl, systemd-analyze, timedatectl + coredumpctl, hostnamectl, localectl, systemd-analyze, timedatectl * hostnamectl: show root image uuid @@ -200,7 +199,7 @@ Features: * synchronize console access with BSD locks: http://lists.freedesktop.org/archives/systemd-devel/2014-October/024582.html -* as soon as we have kdbus, and sender timestamps, revisit coalescing multiple parallel daemon reloads: +* as soon as we have sender timestamps, revisit coalescing multiple parallel daemon reloads: http://lists.freedesktop.org/archives/systemd-devel/2014-December/025862.html * in systemctl list-unit-files: show the install value the presets would suggest for a service in a third column @@ -240,10 +239,6 @@ Features: * timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM -* extract_many_words() should probably be used by a lot of code that - currently uses FOREACH_WORD and friends. For example, most conf - parsing callbacks should use it. - * merge ~/.local/share and ~/.local/lib into one similar /usr/lib and /usr/share.... * systemd.show_status= should probably have a mode where only failed @@ -300,9 +295,6 @@ Features: * MessageQueueMessageSize= (and suchlike) should use parse_iec_size(). -* "busctl status" works only as root on dbus1, since we cannot read - /proc/$PID/exe - * implement Distribute= in socket units to allow running multiple service instances processing the listening socket, and open this up for ReusePort= @@ -313,8 +305,6 @@ Features: and passes this back to PID1 via SCM_RIGHTS. This also could be used to allow Chown/chgrp on sockets without requiring NSS in PID 1. -* New service property: maximum CPU runtime for a service - * introduce bus call FreezeUnit(s, b), as well as "systemctl freeze $UNIT" and "systemctl thaw $UNIT" as wrappers around this. The calls should SIGSTOP all unit processes in a loop until all processes of @@ -351,12 +341,10 @@ Features: error. Currently, we just ignore it and read the unit from the search path anyway. -* refuse boot if /etc/os-release is missing or /etc/machine-id cannot be set up +* refuse boot if /usr/lib/os-release is missing or /etc/machine-id cannot be set up * btrfs raid assembly: some .device jobs stay stuck in the queue -* make sure gdm does not use multi-user-x but the new default X configuration file, and then remove multi-user-x from systemd - * man: the documentation of Restart= currently is very misleading and suggests the tools from ExecStartPre= might get restarted. * load .d/*.conf dropins for device units @@ -613,9 +601,6 @@ Features: * currently x-systemd.timeout is lost in the initrd, since crypttab is copied into dracut, but fstab is not * nspawn: - - to allow "linking" of nspawn containers, extend --network-bridge= so - that it can dynamically create bridge interfaces that are refcounted - by the containers on them. For each group of containers to link together - nspawn -x should support ephemeral instances of gpt images - emulate /dev/kmsg using CUSE and turn off the syslog syscall with seccomp. That should provide us with a useful log buffer that @@ -624,8 +609,6 @@ Features: - as soon as networkd has a bus interface, hook up --network-interface=, --network-bridge= with networkd, to trigger netdev creation should an interface be missing - - don't copy /etc/resolv.conf from host into container unless we are in - shared-network mode - a nice way to boot up without machine id set, so that it is set at boot automatically for supporting --ephemeral. Maybe hash the host machine id together with the machine name to generate the machine id for the container @@ -691,7 +674,7 @@ Features: * coredump: - save coredump in Windows/Mozilla minidump format - - move PID 1 segfaults to /var/lib/systemd/coredump? + - when truncating coredumps, also log the full size that the process had, and make a metadata field so we can report truncated coredumps * support crash reporting operation modes (https://live.gnome.org/GnomeOS/Design/Whiteboards/ProblemReporting) @@ -758,7 +741,6 @@ Features: - GC unreferenced jobs (such as .device jobs) - move PAM code into its own binary - when we automatically restart a service, ensure we restart its rdeps, too. - - for services: do not set $HOME in services unless requested - hide PAM options in fragment parser when compile time disabled - Support --test based on current system state - If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle(). @@ -808,7 +790,6 @@ Features: - add reduced [Link] support to .network files - add Scope= parsing option for [Network] - properly handle routerless dhcp leases - - add more attribute support for SIT tunnel - work with non-Ethernet devices - add support for more bond options - dhcp: do we allow configuring dhcp routes on interfaces that are not the one we got the dhcp info from? @@ -825,7 +806,6 @@ Features: 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) - whenever uplink info changes, make DHCP server send out FORCERENEW * networkd-wait-online: @@ -847,6 +827,7 @@ Features: or interface down - some servers don't do rapid commit without a filled in IA_NA, verify this behavior + - RouteTable= ? External: diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index 8de8597fe9..2c72d31290 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -88,6 +88,17 @@ Process @COREDUMP_PID@ (@COREDUMP_COMM@) crashed and dumped core. This usually indicates a programming error in the crashing program and should be reported to its vendor as a bug. +-- 5aadd8e954dc4b1a8c954d63fd9e1137 +Subject: Core file was truncated to @SIZE_LIMIT@ bytes. +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:coredump.conf(5) + +The process had more memory mapped than the configured maximum for processing +and storage by systemd-coredump(8). Only the first @SIZE_LIMIT@ bytes were +saved. This core might still be usable, but various tools like gdb(1) will warn +about the file being truncated. + -- fc2e22bc6ee647b6b90729ab34a250b1 de Subject: Speicherabbild für Prozess @COREDUMP_PID@ (@COREDUMP_COMM) generiert Defined-By: systemd diff --git a/catalog/systemd.pl.catalog.in b/catalog/systemd.pl.catalog.in index 33c2122974..5eead5c92c 100644 --- a/catalog/systemd.pl.catalog.in +++ b/catalog/systemd.pl.catalog.in @@ -1,7 +1,7 @@ # This file is part of systemd. # # Copyright 2012 Lennart Poettering -# Copyright 2014, 2015, 2016 Piotr Drąg +# Copyright 2014-2016 Piotr Drąg # # 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 @@ -29,15 +29,15 @@ Subject: Uruchomiono dziennik Defined-By: systemd Support: %SUPPORT_URL% -Systemowy proces dziennika został uruchomiony, otworzył pliki dziennika do -zapisu i jest gotowy do przetwarzania żądań. +Systemowy proces dziennika został uruchomiony, otworzył pliki dziennika +do zapisu i jest gotowy do przetwarzania żądań. -- d93fb3c9c24d451a97cea615ce59c00b Subject: Zatrzymano dziennik Defined-By: systemd Support: %SUPPORT_URL% -Systemowy proces dziennika został wyłączony i zamknął wszystkie obecnie +Systemowy proces dziennika został wyłączony i zamknął wszystkie obecnie aktywne pliki dziennika. -- ec387f577b844b8fa948f33cad9a75e6 @@ -48,28 +48,28 @@ Support: %SUPPORT_URL% @JOURNAL_NAME@ (@JOURNAL_PATH@) obecnie używa @CURRENT_USE_PRETTY@. Maksymalnie może używać @MAX_USE_PRETTY@. Zostawianie co najmniej @DISK_KEEP_FREE_PRETTY@ wolnego (z obecnie dostępnego @DISK_AVAILABLE_PRETTY@ miejsca na dysku). -Wymuszone ograniczenie użycia wynosi więc @LIMIT_PRETTY@, z czego @AVAILABLE_PRETTY@ jest nadal dostępne. +Wymuszone ograniczenie użycia wynosi więc @LIMIT_PRETTY@, z czego @AVAILABLE_PRETTY@ jest nadal dostępne. Ograniczenia kontrolujące ilość miejsca na dysku używanego przez dziennik można konfigurować za pomocą ustawień SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= -w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej +w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej informacji. -- a596d6fe7bfa4994828e72309e95d61e -Subject: Ograniczono komunikaty z usługi +Subject: Ograniczono komunikaty z usługi Defined-By: systemd Support: %SUPPORT_URL% Documentation: man:journald.conf(5) -Usługa zapisała za dużo komunikatów w określonym czasie. Komunikaty z usługi -zostały pominięte. +Usługa zapisała za dużo komunikatów w określonym czasie. +Komunikaty z usługi zostały pominięte. -Proszę zauważyć, że tylko komunikaty z danej usługi zostały pominięte. Nie ma -to wpływu na komunikaty innych usług. +Proszę zauważyć, że tylko komunikaty z danej usługi zostały pominięte. +Nie ma to wpływu na komunikaty innych usług. Ograniczenia kontrolujące pomijanie komunikatów mogą być konfigurowane -za pomocą opcji RateLimitIntervalSec= i RateLimitBurst= w pliku +za pomocą opcji RateLimitIntervalSec= i RateLimitBurst= w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej informacji. -- e9bf28e6e834481bb6f48f548ad13606 @@ -86,18 +86,29 @@ Defined-By: systemd Support: %SUPPORT_URL% Documentation: man:core(5) -Proces @COREDUMP_PID@ (@COREDUMP_COMM@) uległ awarii i zrzucił plik core. +Proces @COREDUMP_PID@ (@COREDUMP_COMM@) uległ awarii i zrzucił plik core. -Zwykle wskazuje to na błąd programistyczny w danym programie i powinno zostać +Zwykle wskazuje to na błąd programistyczny w danym programie i powinno zostać zgłoszone jego producentowi jako błąd. +-- 5aadd8e954dc4b1a8c954d63fd9e1137 +Subject: Plik core został skrócony do @SIZE_LIMIT@ B. +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:coredump.conf(5) + +Proces miał więcej zmapowanej pamięci niż maksimum dla przetwarzania i miejsca +skonfigurowane przez systemd-coredump(8). Tylko pierwsze @SIZE_LIMIT@ B +zostało zapisanych. Ten plik core może nadal być używalny, ale narzędzia typu +gdb(1) będą ostrzegały o skróceniu pliku. + -- 8d45620c1a4348dbb17410da57c60c66 Subject: Utworzono nową sesję @SESSION_ID@ dla użytkownika @USER_ID@ Defined-By: systemd Support: %SUPPORT_URL% Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat -Nowa sesja o identyfikatorze @SESSION_ID@ została utworzona dla użytkownika +Nowa sesja o identyfikatorze @SESSION_ID@ została utworzona dla użytkownika @USER_ID@. Proces prowadzący sesji: @LEADER@. @@ -108,7 +119,7 @@ Defined-By: systemd Support: %SUPPORT_URL% Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat -Sesja o identyfikatorze @SESSION_ID@ została zakończona. +Sesja o identyfikatorze @SESSION_ID@ została zakończona. -- fcbefc5da23d428093f97c82a9290f7b Subject: Dostępne jest nowe stanowisko @SEAT_ID@ @@ -116,7 +127,7 @@ Defined-By: systemd Support: %SUPPORT_URL% Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat -Nowe stanowisko @SEAT_ID@ zostało skonfigurowane i jest teraz dostępne. +Nowe stanowisko @SEAT_ID@ zostało skonfigurowane i jest teraz dostępne. -- e7852bfe46784ed0accde04bc864c2d5 Subject: Usunięto stanowisko @SEAT_ID@ @@ -124,7 +135,7 @@ Defined-By: systemd Support: %SUPPORT_URL% Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat -Stanowisko @SEAT_ID@ zostało usunięte i nie jest już dostępne. +Stanowisko @SEAT_ID@ zostało usunięte i nie jest już dostępne. -- c7a787079b354eaaa9e77b371893cd27 Subject: Zmiana czasu @@ -175,8 +186,8 @@ Subject: Zainicjowano wyłączenie systemu Defined-By: systemd Support: %SUPPORT_URL% -Zainicjowano wyłączenie systemd. Wyłączenie zostało rozpoczęte i wszystkie -usługi systemowe zostały zakończone, a wszystkie systemy plików odmontowane. +Zainicjowano wyłączenie systemd. Wyłączenie zostało rozpoczęte i wszystkie +usługi systemowe zostały zakończone, a wszystkie systemy plików odmontowane. -- 7d4958e842da4a758f6c1cdc7b36dcc5 Subject: Rozpoczęto uruchamianie jednostki @UNIT@ @@ -238,7 +249,7 @@ Subject: Nie można wykonać procesu @EXECUTABLE@ Defined-By: systemd Support: %SUPPORT_URL% -Proces @EXECUTABLE@ nie mógł zostać wykonany i się nie powiódł. +Proces @EXECUTABLE@ nie mógł zostać wykonany i się nie powiódł. Numer błędu zwrócony przez ten proces: @ERRNO@. @@ -249,25 +260,25 @@ Support: %SUPPORT_URL% Jeden lub więcej komunikatów nie może zostać przekazanych do usługi syslog uruchomionej obok journald. Zwykle oznacza to, że implementacja syslog nie -jest w stanie nadążyć za prędkością kolejki komunikatów. +jest w stanie nadążyć za prędkością kolejki komunikatów. -- 1dee0369c7fc4736b7099b38ecb46ee7 Subject: Punkt montowania nie jest pusty Defined-By: systemd Support: %SUPPORT_URL% -Katalog @WHERE@ został podany jako punkt montowania (drugie pole w pliku -/etc/fstab lub pole Where= w pliku jednostki systemd) i nie jest pusty. Nie -wpływa to na montowanie, ale wcześniej istniejące pliki w tym katalogu stają +Katalog @WHERE@ został podany jako punkt montowania (drugie pole w pliku +/etc/fstab lub pole Where= w pliku jednostki systemd) i nie jest pusty. Nie +wpływa to na montowanie, ale wcześniej istniejące pliki w tym katalogu stają się niedostępne. Aby zobaczyć te pliki, proszę ręcznie zamontować system -plików w innym położeniu. +plików w innym położeniu. -- 24d8d4452573402496068381a6312df2 Subject: Uruchomiono maszynę wirtualną lub kontener Defined-By: systemd Support: %SUPPORT_URL% -Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została uruchomiona i jest +Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została uruchomiona i jest gotowa do użycia. -- 58432bd3bace477cb514b56381b8a758 @@ -284,15 +295,15 @@ Support: %SUPPORT_URL% Documentation: man:systemd-resolved.service(8) resolved.conf(5) Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer -DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona. +DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona. To zdarzenie będzie miało miejsce, jeśli skonfigurowano DNSSEC=allow-downgrade -w pliku resolved.conf, a skonfigurowany serwer DNS jest niezgodny z DNSSEC. +w pliku resolved.conf, a skonfigurowany serwer DNS jest niezgodny z DNSSEC. Proszę zauważyć, że używanie tego trybu umożliwia ataki wyłączające DNSSEC, ponieważ atakujący będzie mógł wyłączyć walidację DNSSEC na komputerze przez -umieszczenie odpowiednich odpowiedzi DNS w kanale komunikacji. +umieszczenie odpowiednich odpowiedzi DNS w kanale komunikacji. -To zdarzenie może wskazywać, że serwer DNS jest faktycznie niezgodny z DNSSEC, +To zdarzenie może wskazywać, że serwer DNS jest faktycznie niezgodny z DNSSEC, albo że atakującemu udało się upozorować atak tego typu. -- 1675d7f172174098b1108bf8c7dc8f5d diff --git a/configure.ac b/configure.ac index 36061c0ba9..ccd212ef13 100644 --- a/configure.ac +++ b/configure.ac @@ -459,7 +459,7 @@ AM_CONDITIONAL(HAVE_LIBMOUNT, [test "$have_libmount" = "yes"]) have_seccomp=no AC_ARG_ENABLE(seccomp, AS_HELP_STRING([--disable-seccomp], [Disable optional SECCOMP support])) if test "x$enable_seccomp" != "xno"; then - PKG_CHECK_MODULES(SECCOMP, [libseccomp >= 1.0.0], + PKG_CHECK_MODULES(SECCOMP, [libseccomp >= 2.3.1], [AC_DEFINE(HAVE_SECCOMP, 1, [Define if seccomp is available]) have_seccomp=yes M4_DEFINES="$M4_DEFINES -DHAVE_SECCOMP"], @@ -1656,7 +1656,6 @@ AC_MSG_RESULT([ nss-myhostname: ${have_myhostname} hwdb: ${enable_hwdb} tpm: ${have_tpm} - kdbus: ${have_kdbus} Python: ${have_python} man pages: ${have_manpages} test coverage: ${have_coverage} diff --git a/hwdb/20-bluetooth-vendor-product.hwdb b/hwdb/20-bluetooth-vendor-product.hwdb index 5089ab4e04..ab6e321ff6 100644 --- a/hwdb/20-bluetooth-vendor-product.hwdb +++ b/hwdb/20-bluetooth-vendor-product.hwdb @@ -424,7 +424,7 @@ bluetooth:v008A* ID_VENDOR_FROM_DATABASE=Jawbone bluetooth:v008B* - ID_VENDOR_FROM_DATABASE=Topcorn Positioning Systems, LLC + ID_VENDOR_FROM_DATABASE=Topcon Positioning Systems, LLC bluetooth:v008C* ID_VENDOR_FROM_DATABASE=Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.) @@ -1231,7 +1231,7 @@ bluetooth:v0197* ID_VENDOR_FROM_DATABASE=WiSilica Inc bluetooth:v0198* - ID_VENDOR_FROM_DATABASE=Vengit Limited + ID_VENDOR_FROM_DATABASE=VENGIT Korlátolt Felelősségű Társaság bluetooth:v0199* ID_VENDOR_FROM_DATABASE=SALTO SYSTEMS S.L. @@ -2828,3 +2828,201 @@ bluetooth:v03AB* bluetooth:v03AC* ID_VENDOR_FROM_DATABASE=Smablo LTD + +bluetooth:v03AD* + ID_VENDOR_FROM_DATABASE=XiQ + +bluetooth:v03AE* + ID_VENDOR_FROM_DATABASE=Allswell Inc. + +bluetooth:v03AF* + ID_VENDOR_FROM_DATABASE=Comm-N-Sense Corp DBA Verigo + +bluetooth:v03B0* + ID_VENDOR_FROM_DATABASE=VIBRADORM GmbH + +bluetooth:v03B1* + ID_VENDOR_FROM_DATABASE=Otodata Wireless Network Inc. + +bluetooth:v03B2* + ID_VENDOR_FROM_DATABASE=Propagation Systems Limited + +bluetooth:v03B3* + ID_VENDOR_FROM_DATABASE=Midwest Instruments & Controls + +bluetooth:v03B4* + ID_VENDOR_FROM_DATABASE=Alpha Nodus, inc. + +bluetooth:v03B5* + ID_VENDOR_FROM_DATABASE=petPOMM, Inc + +bluetooth:v03B6* + ID_VENDOR_FROM_DATABASE=Mattel + +bluetooth:v03B7* + ID_VENDOR_FROM_DATABASE=Airbly Inc. + +bluetooth:v03B8* + ID_VENDOR_FROM_DATABASE=A-Safe Limited + +bluetooth:v03B9* + ID_VENDOR_FROM_DATABASE=FREDERIQUE CONSTANT SA + +bluetooth:v03BA* + ID_VENDOR_FROM_DATABASE=Maxscend Microelectronics Company Limited + +bluetooth:v03BB* + ID_VENDOR_FROM_DATABASE=Abbott Diabetes Care + +bluetooth:v03BC* + ID_VENDOR_FROM_DATABASE=ASB Bank Ltd + +bluetooth:v03BD* + ID_VENDOR_FROM_DATABASE=amadas + +bluetooth:v03BE* + ID_VENDOR_FROM_DATABASE=Applied Science, Inc. + +bluetooth:v03BF* + ID_VENDOR_FROM_DATABASE=iLumi Solutions Inc. + +bluetooth:v03C0* + ID_VENDOR_FROM_DATABASE=Arch Systems Inc. + +bluetooth:v03C1* + ID_VENDOR_FROM_DATABASE=Ember Technologies, Inc. + +bluetooth:v03C2* + ID_VENDOR_FROM_DATABASE=Snapchat Inc + +bluetooth:v03C3* + ID_VENDOR_FROM_DATABASE=Casambi Technologies Oy + +bluetooth:v03C4* + ID_VENDOR_FROM_DATABASE=Pico Technology Inc. + +bluetooth:v03C5* + ID_VENDOR_FROM_DATABASE=St. Jude Medical, Inc. + +bluetooth:v03C6* + ID_VENDOR_FROM_DATABASE=Intricon + +bluetooth:v03C7* + ID_VENDOR_FROM_DATABASE=Structural Health Systems, Inc. + +bluetooth:v03C8* + ID_VENDOR_FROM_DATABASE=Avvel International + +bluetooth:v03C9* + ID_VENDOR_FROM_DATABASE=Gallagher Group + +bluetooth:v03CA* + ID_VENDOR_FROM_DATABASE=In2things Automation Pvt. Ltd. + +bluetooth:v03CB* + ID_VENDOR_FROM_DATABASE=SYSDEV Srl + +bluetooth:v03CC* + ID_VENDOR_FROM_DATABASE=Vonkil Technologies Ltd + +bluetooth:v03CD* + ID_VENDOR_FROM_DATABASE=Wynd Technologies, Inc. + +bluetooth:v03CE* + ID_VENDOR_FROM_DATABASE=CONTRINEX S.A. + +bluetooth:v03CF* + ID_VENDOR_FROM_DATABASE=MIRA, Inc. + +bluetooth:v03D0* + ID_VENDOR_FROM_DATABASE=Watteam Ltd + +bluetooth:v03D1* + ID_VENDOR_FROM_DATABASE=Density Inc. + +bluetooth:v03D2* + ID_VENDOR_FROM_DATABASE=IOT Pot India Private Limited + +bluetooth:v03D3* + ID_VENDOR_FROM_DATABASE=Sigma Connectivity AB + +bluetooth:v03D4* + ID_VENDOR_FROM_DATABASE=PEG PEREGO SPA + +bluetooth:v03D5* + ID_VENDOR_FROM_DATABASE=Wyzelink Systems Inc. + +bluetooth:v03D6* + ID_VENDOR_FROM_DATABASE=Yota Devices LTD + +bluetooth:v03D7* + ID_VENDOR_FROM_DATABASE=FINSECUR + +bluetooth:v03D8* + ID_VENDOR_FROM_DATABASE=Zen-Me Labs Ltd + +bluetooth:v03D9* + ID_VENDOR_FROM_DATABASE=3IWare Co., Ltd. + +bluetooth:v03DA* + ID_VENDOR_FROM_DATABASE=EnOcean GmbH + +bluetooth:v03DB* + ID_VENDOR_FROM_DATABASE=Instabeat, Inc + +bluetooth:v03DC* + ID_VENDOR_FROM_DATABASE=Nima Labs + +bluetooth:v03DD* + ID_VENDOR_FROM_DATABASE=Andreas Stihl AG & Co. KG + +bluetooth:v03DE* + ID_VENDOR_FROM_DATABASE=Nathan Rhoades LLC + +bluetooth:v03DF* + ID_VENDOR_FROM_DATABASE=Grob Technologies, LLC + +bluetooth:v03E0* + ID_VENDOR_FROM_DATABASE=Actions (Zhuhai) Technology Co., Limited + +bluetooth:v03E1* + ID_VENDOR_FROM_DATABASE=SPD Development Company Ltd + +bluetooth:v03E2* + ID_VENDOR_FROM_DATABASE=Sensoan Oy + +bluetooth:v03E3* + ID_VENDOR_FROM_DATABASE=Qualcomm Life Inc + +bluetooth:v03E4* + ID_VENDOR_FROM_DATABASE=Chip-ing AG + +bluetooth:v03E5* + ID_VENDOR_FROM_DATABASE=ffly4u + +bluetooth:v03E6* + ID_VENDOR_FROM_DATABASE=IoT Instruments Oy + +bluetooth:v03E7* + ID_VENDOR_FROM_DATABASE=TRUE Fitness Technology + +bluetooth:v03E8* + ID_VENDOR_FROM_DATABASE=Reiner Kartengeraete GmbH & Co. KG. + +bluetooth:v03E9* + ID_VENDOR_FROM_DATABASE=SHENZHEN LEMONJOY TECHNOLOGY CO., LTD. + +bluetooth:v03EA* + ID_VENDOR_FROM_DATABASE=Hello Inc. + +bluetooth:v03EB* + ID_VENDOR_FROM_DATABASE=Evollve Inc. + +bluetooth:v03EC* + ID_VENDOR_FROM_DATABASE=Jigowatts Inc. + +bluetooth:v03ED* + ID_VENDOR_FROM_DATABASE=BASIC MICRO.COM,INC. + +bluetooth:v03EE* + ID_VENDOR_FROM_DATABASE=CUBE TECHNOLOGIES diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb index 8651946db7..0b692a1b5d 100644 --- a/hwdb/60-evdev.hwdb +++ b/hwdb/60-evdev.hwdb @@ -209,6 +209,14 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*P50* EVDEV_ABS_35=::44 EVDEV_ABS_36=::67 +# Lenovo *40 series +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pvrThinkPad??40:* +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pvrThinkPad??40?:* + EVDEV_ABS_00=::41 + EVDEV_ABS_01=::37 + EVDEV_ABS_35=::41 + EVDEV_ABS_36=::37 + # Lenovo T460 evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*T460* EVDEV_ABS_00=1266:5677:44 diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 5e2ab393bd..2b30896dff 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -108,6 +108,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon4th:* +# Lenovo Thinkpad X1 Tablet +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Tablet:* POINTINGSTICK_SENSITIVITY=200 POINTINGSTICK_CONST_ACCEL=1.0 diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py index f55562250d..e163edbc51 100755 --- a/hwdb/parse_hwdb.py +++ b/hwdb/parse_hwdb.py @@ -1,22 +1,30 @@ #!/usr/bin/python3 -# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ +# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ # -# This file is part of systemd. +# This file is part of systemd. It is distrubuted under the MIT license, see +# below. # -# Copyright 2016 Zbigniew Jędrzejewski-Szmek +# Copyright 2016 Zbigniew Jędrzejewski-Szmek # -# 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. +# The MIT License (MIT) # -# 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. +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see <http://www.gnu.org/licenses/>. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import functools import glob diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml index 4f95680a3a..77b4dac51c 100644 --- a/man/coredump.conf.xml +++ b/man/coredump.conf.xml @@ -83,16 +83,13 @@ <varlistentry> <term><varname>Storage=</varname></term> - <listitem><para>Controls where to store cores. One of - <literal>none</literal>, <literal>external</literal>, - <literal>journal</literal>, and <literal>both</literal>. When - <literal>none</literal>, the core dumps will be logged but not - stored permanently. When <literal>external</literal> (the - default), cores will be stored in <filename>/var/lib/systemd/coredump</filename>. - When <literal>journal</literal>, cores will be stored in - the journal and rotated following normal journal - rotation patterns. When <literal>both</literal>, cores - will be stored in both locations.</para> + <listitem><para>Controls where to store cores. One of <literal>none</literal>, + <literal>external</literal>, and <literal>journal</literal>. When + <literal>none</literal>, the core dumps will be logged (included the traceback if + possible), but not stored permanently. When <literal>external</literal> (the + default), cores will be stored in <filename>/var/lib/systemd/coredump/</filename>. + When <literal>journal</literal>, cores will be stored in the journal and rotated + following normal journal rotation patterns.</para> <para>When cores are stored in the journal, they might be compressed following journal compression settings, see diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml index 4bdc167f79..9a28862ceb 100644 --- a/man/dnssec-trust-anchors.d.xml +++ b/man/dnssec-trust-anchors.d.xml @@ -160,14 +160,12 @@ <refsect1> <title>Negative Trust Anchors</title> - <para>Negative trust anchors define domains where DNSSEC - validation shall be turned off. Negative trust anchor files are - found at the same location as positive trust anchor files, and - follow the same overriding rules. They are text files with the - <filename>.negative</filename> suffix. Empty lines and lines whose - first character is <literal>;</literal> are ignored. Each line - specifies one domain name where DNSSEC validation shall be - disabled on.</para> + <para>Negative trust anchors define domains where DNSSEC validation shall be turned + off. Negative trust anchor files are found at the same location as positive trust anchor files, + and follow the same overriding rules. They are text files with the + <filename>.negative</filename> suffix. Empty lines and lines whose first character is + <literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS + subtree where validation shall be disabled.</para> <para>Negative trust anchors are useful to support private DNS subtrees that are not referenced from the Internet DNS hierarchy, diff --git a/man/kernel-install.xml b/man/kernel-install.xml index d7e27de758..4a8a46cef4 100644 --- a/man/kernel-install.xml +++ b/man/kernel-install.xml @@ -72,9 +72,12 @@ in <filename>/usr/lib/kernel/install.d/</filename>. This can be used to override a system-supplied executables with a local file if needed; a symbolic link in <filename>/etc/kernel/install.d/</filename> with the same name as an executable in <filename>/usr/lib/kernel/install.d/</filename>, - pointing to /dev/null, disables the executable entirely. Executables must have the + pointing to <filename>/dev/null</filename>, disables the executable entirely. Executables must have the extension <literal>.install</literal>; other extensions are ignored.</para> + <para>An executable should return <constant>0</constant> on success. It may also + return <constant>77</constant> to cause the whole operation to terminate + (executables later in lexical order will be skipped).</para> </refsect1> <refsect1> diff --git a/man/machinectl.xml b/man/machinectl.xml index 7056fd4204..eaa247714b 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -186,12 +186,11 @@ <varlistentry> <term><option>--uid=</option></term> - <listitem><para>When used with the <command>shell</command> - command, chooses the user ID to open the interactive shell - session as. If this switch is not specified, defaults to - <literal>root</literal>. Note that this switch is not - supported for the <command>login</command> command (see - below).</para></listitem> + <listitem><para>When used with the <command>shell</command> command, chooses the user ID to + open the interactive shell session as. If the argument to the <command>shell</command> + command also specifies an user name, this option is ignored. If the name is not specified + in either way, <literal>root</literal> will be used by default. Note that this switch is + not supported for the <command>login</command> command (see below).</para></listitem> </varlistentry> <varlistentry> diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index e6cc1d982a..d66e8ba521 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -85,7 +85,7 @@ group: compat mymachines systemd shadow: compat -hosts: files mymachines <command>resolve</command> myhostname +hosts: files mymachines <command>resolve [!UNAVAIL=return]</command> dns networks: files protocols: db files @@ -95,6 +95,8 @@ rpc: db files netgroup: nis</programlisting> + <para>This keeps the <command>dns</command> module as a fallback for cases where the <command>nss-resolve</command> + module is not installed.</para> </refsect1> <refsect1> diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 7556c6ff31..4fc1ef1b33 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -206,13 +206,25 @@ <term><varname>Cache=</varname></term> <listitem><para>Takes a boolean argument. If "yes" (the default), resolving a domain name which already got queried earlier will return the previous result as long as it is still valid, and thus does not result in a new - network request. Be aware that that turning off caching comes at a performance penalty, which is particularly + network request. Be aware that turning off caching comes at a performance penalty, which is particularly high when DNSSEC is used.</para> <para>Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address (such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>DNSStubListener=</varname></term> + <listitem><para>Takes a boolean argument or one of <literal>udp</literal> and <literal>tcp</literal>. If + <literal>udp</literal> (the default), a DNS stub resolver will listen for UDP requests on address 127.0.0.53 + port 53. If <literal>tcp</literal>, the stub will listen for TCP requests on the same address and port. If + <literal>yes</literal>, the stub listens for both UDP and TCP requests. If <literal>no</literal>, the stub + listener is disabled.</para> + + <para>Note that the DNS stub listener is turned off implicitly when its listening address and port are already + in use.</para></listitem> + </varlistentry> + </variablelist> </refsect1> diff --git a/man/sd_bus_negotiate_fds.xml b/man/sd_bus_negotiate_fds.xml index a538b13cf0..1501e1427d 100644 --- a/man/sd_bus_negotiate_fds.xml +++ b/man/sd_bus_negotiate_fds.xml @@ -99,41 +99,27 @@ setting as negotiated by the program ultimately activated. By default, file descriptor passing is enabled for both.</para> - <para><function>sd_bus_negotiate_timestamps()</function> controls - whether implicit sender timestamps shall be attached automatically - to all incoming messages. Takes a bus object and a boolean, which, - when true, enables timestamping, and, when false, disables it. - Use + <para><function>sd_bus_negotiate_timestamp()</function> controls whether implicit sender + timestamps shall be attached automatically to all incoming messages. Takes a bus object and a + boolean, which, when true, enables timestamping, and, when false, disables it. Use <citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_bus_message_get_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_bus_message_get_seqnum</refentrytitle><manvolnum>3</manvolnum></citerefentry> - to query the timestamps of incoming messages. If negotiation is - disabled or not supported, these calls will fail with - <constant>-ENODATA</constant>. Note that not all transports - support timestamping of messages. Specifically, timestamping is - only available on the kdbus transport, but not on dbus1. The - timestamping is applied by the kernel and cannot be manipulated by - userspace. By default, message timestamping is not negotiated for + to query the timestamps of incoming messages. If negotiation is disabled or not supported, these + calls will fail with <constant>-ENODATA</constant>. Note that currently no transports support + timestamping of messages. By default, message timestamping is not negotiated for connections.</para> - <para><function>sd_bus_negotiate_creds()</function> controls - whether and which implicit sender credentials shall be attached - automatically to all incoming messages. Takes a bus object and a - boolean indicating whether to enable or disable the credential - parts encoded in the bit mask value argument. Note that not all - transports support attaching sender credentials to messages, or do - not support all types of sender credential parameters, or might - suppress them under certain circumstances for individual - messages. Specifically, implicit sender credentials on messages - are only fully supported on kdbus transports, and dbus1 only - supports <constant>SD_BUS_CREDS_UNIQUE_NAME</constant>. The sender - credentials are attached by the kernel and cannot be manipulated - by userspace, and are thus suitable for authorization - decisions. By default, only - <constant>SD_BUS_CREDS_WELL_KNOWN_NAMES</constant> and - <constant>SD_BUS_CREDS_UNIQUE_NAME</constant> are enabled. In - fact, these two credential fields are always sent along and cannot - be turned off.</para> + <para><function>sd_bus_negotiate_creds()</function> controls whether and which implicit sender + credentials shall be attached automatically to all incoming messages. Takes a bus object and a + boolean indicating whether to enable or disable the credential parts encoded in the bit mask + value argument. Note that not all transports support attaching sender credentials to messages, + or do not support all types of sender credential parameters, or might suppress them under + certain circumstances for individual messages. Specifically, dbus1 only supports + <constant>SD_BUS_CREDS_UNIQUE_NAME</constant>. The sender credentials are suitable for + authorization decisions. By default, only <constant>SD_BUS_CREDS_WELL_KNOWN_NAMES</constant> and + <constant>SD_BUS_CREDS_UNIQUE_NAME</constant> are enabled. In fact, these two credential fields + are always sent along and cannot be turned off.</para> <para>The <function>sd_bus_negotiate_fds()</function> function may be called only before the connection has been started with diff --git a/man/systemctl.xml b/man/systemctl.xml index 7e0ac9613a..e738b5aecd 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -363,7 +363,20 @@ to finish. If this is not specified, the job will be verified, enqueued and <command>systemctl</command> will wait until the unit's start-up is completed. By passing this - argument, it is only verified and enqueued.</para> + argument, it is only verified and enqueued. This option may not be + combined with <option>--wait</option>.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--wait</option></term> + + <listitem> + <para>Synchronously wait for started units to terminate again. + This option may not be combined with <option>--no-block</option>. + Note that this will wait forever if any given unit never terminates + (by itself or by getting stopped explicitly); particularly services + which use <literal>RemainAfterExit=yes</literal>.</para> </listitem> </varlistentry> @@ -613,7 +626,7 @@ <listitem> <para>When used with <command>list-dependencies</command>, - <command>list-units</command> or <command>list-machines</command>, the + <command>list-units</command> or <command>list-machines</command>, the output is printed as a list instead of a tree, and the bullet circles are omitted.</para> </listitem> @@ -1073,8 +1086,8 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service <listitem> <para>Reenable one or more units, as specified on the command line. This is a combination of <command>disable</command> and <command>enable</command> and is useful to reset the symlinks a unit file is - enabled with to the defaults configured in its <literal>[Install]</literal> section. This commands expects - a unit uname only, it does not accept paths to unit files.</para> + enabled with to the defaults configured in its <literal>[Install]</literal> section. This command expects + a unit name only, it does not accept paths to unit files.</para> </listitem> </varlistentry> diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index bc37765dff..8fa7cd3329 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -181,14 +181,15 @@ <option>--log-target=</option>, described in <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para> - <para><command>systemd-analyze verify</command> will load unit - files and print warnings if any errors are detected. Files - specified on the command line will be loaded, but also any other - units referenced by them. This command works by prepending the - directories for all command line arguments at the beginning of the - unit load path, which means that all units files found in those - directories will be used in preference to the unit files found in - the standard locations, even if not listed explicitly.</para> + <para><command>systemd-analyze verify</command> will load unit files and print + warnings if any errors are detected. Files specified on the command line will be + loaded, but also any other units referenced by them. The full unit search path is + formed by combining the directories for all command line arguments, and the usual unit + load paths (variable <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be + used to replace or augment the compiled in set of unit load paths; see + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>). + All units files present in the directories containing the command line arguments will + be used in preference to the other paths.</para> <para>If no command is passed, <command>systemd-analyze time</command> is implied.</para> diff --git a/man/systemd-journal-remote.xml b/man/systemd-journal-remote.xml index f208f8deb4..ee2d5c2486 100644 --- a/man/systemd-journal-remote.xml +++ b/man/systemd-journal-remote.xml @@ -250,20 +250,19 @@ </varlistentry> <varlistentry> - <term><option>--compress</option></term> - <term><option>--no-compress</option></term> + <term><option>--compress</option> [<replaceable>BOOL</replaceable>]</term> - <listitem><para>Compress or not, respectively, the data in the - journal using XZ.</para></listitem> + <listitem><para>If this is set to <literal>yes</literal> then compress + the data in the journal using XZ. The default is <literal>yes</literal>. + </para></listitem> </varlistentry> <varlistentry> - <term><option>--seal</option></term> - <term><option>--no-seal</option></term> + <term><option>--seal</option> [<replaceable>BOOL</replaceable>]</term> - <listitem><para>Periodically sign or not, respectively, the - data in the journal using Forward Secure Sealing. - </para></listitem> + <listitem><para>If this is set to <literal>yes</literal> then + periodically sign the data in the journal using Forward Secure Sealing. + The default is <literal>no</literal>.</para></listitem> </varlistentry> <varlistentry> diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 97b348b565..5ac54df81a 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -405,35 +405,36 @@ purposes (usually in the range beyond the host's UID/GID 65536). The parameter may be specified as follows:</para> <orderedlist> - <listitem><para>The value <literal>no</literal> turns off user namespacing. This is the default.</para></listitem> - - <listitem><para>The value <literal>yes</literal> (or the omission of a parameter) turns on user - namespacing. The UID/GID range to use is determined automatically from the file ownership of the root - directory of the container's directory tree. To use this option, make sure to prepare the directory tree in - advance, and ensure that all files and directories in it are owned by UIDs/GIDs in the range you'd like to - use. Also, make sure that used file ACLs exclusively reference UIDs/GIDs in the appropriate range. If this - mode is used the number of UIDs/GIDs assigned to the container for use is 65536, and the UID/GID of the - root directory must be a multiple of 65536.</para></listitem> - - <listitem><para>The value "pick" turns on user namespacing. In this case the UID/GID range is automatically - chosen. As first step, the file owner of the root directory of the container's directory tree is read, and it - is checked that it is currently not used by the system otherwise (in particular, that no other container is - using it). If this check is successful, the UID/GID range determined this way is used, similar to the - behaviour if "yes" is specified. If the check is not successful (and thus the UID/GID range indicated in the - root directory's file owner is already used elsewhere) a new – currently unused – UID/GID range of 65536 - UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and 1878982656, always starting at a - multiple of 65536. This setting implies <option>--private-users-chown</option> (see below), which has the - effect that the files and directories in the container's directory tree will be owned by the appropriate - users of the range picked. Using this option makes user namespace behaviour fully automatic. Note that the - first invocation of a previously unused container image might result in picking a new UID/GID range for it, - and thus in the (possibly expensive) file ownership adjustment operation. However, subsequent invocations of - the container will be cheap (unless of course the picked UID/GID range is assigned to a different use by - then).</para></listitem> - - <listitem><para>Finally if one or two colon-separated numeric parameters are specified, user namespacing is - turned on, too. The first parameter specifies the first host UID/GID to assign to the container, the second - parameter specifies the number of host UIDs/GIDs to assign to the container. If the second parameter is - omitted, 65536 UIDs/GIDs are assigned.</para></listitem> + <listitem><para>If one or two colon-separated numers are specified, user namespacing is turned on. The first + parameter specifies the first host UID/GID to assign to the container, the second parameter specifies the + number of host UIDs/GIDs to assign to the container. If the second parameter is omitted, 65536 UIDs/GIDs are + assigned.</para></listitem> + + <listitem><para>If the parameter is omitted, or true, user namespacing is turned on. The UID/GID range to + use is determined automatically from the file ownership of the root directory of the container's directory + tree. To use this option, make sure to prepare the directory tree in advance, and ensure that all files and + directories in it are owned by UIDs/GIDs in the range you'd like to use. Also, make sure that used file ACLs + exclusively reference UIDs/GIDs in the appropriate range. If this mode is used the number of UIDs/GIDs + assigned to the container for use is 65536, and the UID/GID of the root directory must be a multiple of + 65536.</para></listitem> + + <listitem><para>If the parameter is false, user namespacing is turned off. This is the default.</para> + </listitem> + + <listitem><para>The special value <literal>pick</literal> turns on user namespacing. In this case the UID/GID + range is automatically chosen. As first step, the file owner of the root directory of the container's + directory tree is read, and it is checked that it is currently not used by the system otherwise (in + particular, that no other container is using it). If this check is successful, the UID/GID range determined + this way is used, similar to the behaviour if "yes" is specified. If the check is not successful (and thus + the UID/GID range indicated in the root directory's file owner is already used elsewhere) a new – currently + unused – UID/GID range of 65536 UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and + 1878982656, always starting at a multiple of 65536. This setting implies + <option>--private-users-chown</option> (see below), which has the effect that the files and directories in + the container's directory tree will be owned by the appropriate users of the range picked. Using this option + makes user namespace behaviour fully automatic. Note that the first invocation of a previously unused + container image might result in picking a new UID/GID range for it, and thus in the (possibly expensive) file + ownership adjustment operation. However, subsequent invocations of the container will be cheap (unless of + course the picked UID/GID range is assigned to a different use by then).</para></listitem> </orderedlist> <para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the @@ -454,17 +455,6 @@ </varlistentry> <varlistentry> - <term><option>-U</option></term> - - <listitem><para>If the kernel supports the user namespaces feature, equivalent to - <option>--private-users=pick</option>, otherwise equivalent to - <option>--private-users=no</option>.</para> - - <para>Note that <option>-U</option> is the default if the <filename>systemd-nspawn@.service</filename> template unit - file is used.</para></listitem> - </varlistentry> - - <varlistentry> <term><option>--private-users-chown</option></term> <listitem><para>If specified, all files and directories in the container's directory tree will adjusted so that @@ -477,6 +467,23 @@ </varlistentry> <varlistentry> + <term><option>-U</option></term> + + <listitem><para>If the kernel supports the user namespaces feature, equivalent to + <option>--private-users=pick --private-users-chown</option>, otherwise equivalent to + <option>--private-users=no</option>.</para> + + <para>Note that <option>-U</option> is the default if the + <filename>systemd-nspawn@.service</filename> template unit file is used.</para> + + <para>Note: it is possible to undo the effect of <option>--private-users-chown</option> (or + <option>-U</option>) on the file system by redoing the operation with the first UID of 0:</para> + + <programlisting>systemd-nspawn … --private-users=0 --private-users-chown</programlisting> + </listitem> + </varlistentry> + + <varlistentry> <term><option>--private-network</option></term> <listitem><para>Disconnect networking of the container from @@ -1007,8 +1014,8 @@ <example> <title>Download a Fedora image and start a shell in it</title> - <programlisting># machinectl pull-raw --verify=no http://ftp.halifax.rwth-aachen.de/fedora/linux/releases/21/Cloud/Images/x86_64/Fedora-Cloud-Base-20141203-21.x86_64.raw.xz -# systemd-nspawn -M Fedora-Cloud-Base-20141203-21</programlisting> + <programlisting># machinectl pull-raw --verify=no http://ftp.halifax.rwth-aachen.de/fedora/linux/releases/24/CloudImages/x86_64/images/Fedora-Cloud-Base-24-1.2.x86_64.raw.xz +# systemd-nspawn -M Fedora-Cloud-Base-24-1.2.x86_64.raw</programlisting> <para>This downloads an image using <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> diff --git a/man/systemd-resolve.xml b/man/systemd-resolve.xml index ca26bb4d49..2bc917ac26 100644 --- a/man/systemd-resolve.xml +++ b/man/systemd-resolve.xml @@ -135,7 +135,7 @@ TXT).</para> <para>The <option>--openpgp</option> switch may be used to query PGP keys stored as - <ulink url="https://tools.ietf.org/html/draft-wouters-dane-openpgp-02">OPENPGPKEY</ulink> resource records. + <ulink url="https://tools.ietf.org/html/rfc7929">OPENPGPKEY</ulink> resource records. When this option is specified one or more e-mail address must be specified.</para> <para>The <option>--tlsa</option> switch maybe be used to query TLS public @@ -339,7 +339,7 @@ www.0pointer.net: 2a01:238:43ed:c300:10c3:bcf3:3266:da74 </example> <example> - <title>Retrieve the MX record of the <literal>0pointer.net</literal> domain</title> + <title>Retrieve the MX record of the <literal>yahoo.com</literal> domain</title> <programlisting>$ systemd-resolve -t MX yahoo.com --legend=no yahoo.com. IN MX 1 mta7.am0.yahoodns.net diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index 1bb40fd234..a268397d09 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -106,6 +106,17 @@ </varlistentry> <varlistentry> + <term><varname>CtrlAltDelBurstAction=</varname></term> + + <listitem><para>Defines what action will be performed + if user presses Ctr-Alt-Delete more than 7 times in 2s. + Can be set to <literal>reboot-force</literal>, <literal>poweroff-force</literal> + or disabled with <literal>ignore</literal>. Defaults to + <literal>reboot-force</literal>. + </para></listitem> + </varlistentry> + + <varlistentry> <term><varname>CPUAffinity=</varname></term> <listitem><para>Configures the initial CPU affinity for the diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index bcedebd5bb..5e6787338d 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -160,14 +160,18 @@ use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running as part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by these users/groups around, as a different unit might get the same UID/GID assigned later on, and thus gain access to - these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>RemoveIPC=</varname> and + these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>RemoveIPC=</varname>, <varname>PrivateTmp=</varname> are implied. This ensures that the lifetime of IPC objects and temporary files created by the executed processes is bound to the runtime of the service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp</filename> and <filename>/var/tmp</filename> are usually the only world-writable directories on a system this ensures that a unit making use of dynamic user/group allocation - cannot leave files around after unit termination. Use <varname>RuntimeDirectory=</varname> (see below) in order - to assign a writable runtime directory to a service, owned by the dynamic user/group and removed automatically - when the unit is terminated. Defaults to off.</para></listitem> + cannot leave files around after unit termination. Moreover <varname>ProtectSystem=strict</varname> and + <varname>ProtectHome=read-only</varname> are implied, thus prohibiting the service to write to arbitrary file + system locations. In order to allow the service to write to certain directories, they have to be whitelisted + using <varname>ReadWritePaths=</varname>, but care must be taken so that UID/GID recycling doesn't + create security issues involving files created by the service. Use <varname>RuntimeDirectory=</varname> (see + below) in order to assign a writable runtime directory to a service, owned by the dynamic user/group and + removed automatically when the unit is terminated. Defaults to off.</para></listitem> </varlistentry> <varlistentry> @@ -817,49 +821,37 @@ <listitem><para>Controls which capabilities to include in the capability bounding set for the executed process. See <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry> for - details. Takes a whitespace-separated list of capability names as read by <citerefentry - project='mankier'><refentrytitle>cap_from_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>, - e.g. <constant>CAP_SYS_ADMIN</constant>, <constant>CAP_DAC_OVERRIDE</constant>, - <constant>CAP_SYS_PTRACE</constant>. Capabilities listed will be included in the bounding set, all others are - removed. If the list of capabilities is prefixed with <literal>~</literal>, all but the listed capabilities - will be included, the effect of the assignment inverted. Note that this option also affects the respective - capabilities in the effective, permitted and inheritable capability sets. If this option is not used, the - capability bounding set is not modified on process execution, hence no limits on the capabilities of the - process are enforced. This option may appear more than once, in which case the bounding sets are merged. If the - empty string is assigned to this option, the bounding set is reset to the empty capability set, and all prior - settings have no effect. If set to <literal>~</literal> (without any further argument), the bounding set is - reset to the full set of available capabilities, also undoing any previous settings. This does not affect - commands prefixed with <literal>+</literal>.</para></listitem> + details. Takes a whitespace-separated list of capability names, e.g. <constant>CAP_SYS_ADMIN</constant>, + <constant>CAP_DAC_OVERRIDE</constant>, <constant>CAP_SYS_PTRACE</constant>. Capabilities listed will be + included in the bounding set, all others are removed. If the list of capabilities is prefixed with + <literal>~</literal>, all but the listed capabilities will be included, the effect of the assignment + inverted. Note that this option also affects the respective capabilities in the effective, permitted and + inheritable capability sets. If this option is not used, the capability bounding set is not modified on process + execution, hence no limits on the capabilities of the process are enforced. This option may appear more than + once, in which case the bounding sets are merged. If the empty string is assigned to this option, the bounding + set is reset to the empty capability set, and all prior settings have no effect. If set to + <literal>~</literal> (without any further argument), the bounding set is reset to the full set of available + capabilities, also undoing any previous settings. This does not affect commands prefixed with + <literal>+</literal>.</para></listitem> </varlistentry> <varlistentry> <term><varname>AmbientCapabilities=</varname></term> - <listitem><para>Controls which capabilities to include in the - ambient capability set for the executed process. Takes a - whitespace-separated list of capability names as read by - <citerefentry project='mankier'><refentrytitle>cap_from_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>, - e.g. <constant>CAP_SYS_ADMIN</constant>, - <constant>CAP_DAC_OVERRIDE</constant>, - <constant>CAP_SYS_PTRACE</constant>. This option may appear more than - once in which case the ambient capability sets are merged. - If the list of capabilities is prefixed with <literal>~</literal>, all - but the listed capabilities will be included, the effect of the - assignment inverted. If the empty string is - assigned to this option, the ambient capability set is reset to - the empty capability set, and all prior settings have no effect. - If set to <literal>~</literal> (without any further argument), the - ambient capability set is reset to the full set of available - capabilities, also undoing any previous settings. Note that adding - capabilities to ambient capability set adds them to the process's - inherited capability set. - </para><para> - Ambient capability sets are useful if you want to execute a process - as a non-privileged user but still want to give it some capabilities. - Note that in this case option <constant>keep-caps</constant> is - automatically added to <varname>SecureBits=</varname> to retain the - capabilities over the user change. <varname>AmbientCapabilities=</varname> does not affect - commands prefixed with <literal>+</literal>.</para></listitem> + <listitem><para>Controls which capabilities to include in the ambient capability set for the executed + process. Takes a whitespace-separated list of capability names, e.g. <constant>CAP_SYS_ADMIN</constant>, + <constant>CAP_DAC_OVERRIDE</constant>, <constant>CAP_SYS_PTRACE</constant>. This option may appear more than + once in which case the ambient capability sets are merged. If the list of capabilities is prefixed with + <literal>~</literal>, all but the listed capabilities will be included, the effect of the assignment + inverted. If the empty string is assigned to this option, the ambient capability set is reset to the empty + capability set, and all prior settings have no effect. If set to <literal>~</literal> (without any further + argument), the ambient capability set is reset to the full set of available capabilities, also undoing any + previous settings. Note that adding capabilities to ambient capability set adds them to the process's inherited + capability set. </para><para> Ambient capability sets are useful if you want to execute a process as a + non-privileged user but still want to give it some capabilities. Note that in this case option + <constant>keep-caps</constant> is automatically added to <varname>SecureBits=</varname> to retain the + capabilities over the user change. <varname>AmbientCapabilities=</varname> does not affect commands prefixed + with <literal>+</literal>.</para></listitem> </varlistentry> <varlistentry> @@ -885,48 +877,34 @@ <term><varname>ReadOnlyPaths=</varname></term> <term><varname>InaccessiblePaths=</varname></term> - <listitem><para>Sets up a new file system namespace for - executed processes. These options may be used to limit access - a process might have to the main file system hierarchy. Each - setting takes a space-separated list of paths relative to - the host's root directory (i.e. the system running the service manager). - Note that if entries contain symlinks, they are resolved from the host's root directory as well. - Entries (files or directories) listed in - <varname>ReadWritePaths=</varname> are accessible from - within the namespace with the same access rights as from - outside. Entries listed in - <varname>ReadOnlyPaths=</varname> are accessible for - reading only, writing will be refused even if the usual file - access controls would permit this. Entries listed in - <varname>InaccessiblePaths=</varname> will be made - inaccessible for processes inside the namespace, and may not - countain any other mountpoints, including those specified by - <varname>ReadWritePaths=</varname> or - <varname>ReadOnlyPaths=</varname>. - Note that restricting access with these options does not extend - to submounts of a directory that are created later on. - Non-directory paths can be specified as well. These - options may be specified more than once, in which case all - paths listed will have limited access from within the - namespace. If the empty string is assigned to this option, the - specific list is reset, and all prior assignments have no - effect.</para> - <para>Paths in - <varname>ReadOnlyPaths=</varname> - and - <varname>InaccessiblePaths=</varname> - may be prefixed with - <literal>-</literal>, in which case - they will be ignored when they do not - exist. Note that using this - setting will disconnect propagation of - mounts from the service to the host - (propagation in the opposite direction - continues to work). This means that - this setting may not be used for - services which shall be able to - install mount points in the main mount - namespace.</para></listitem> + <listitem><para>Sets up a new file system namespace for executed processes. These options may be used to limit + access a process might have to the file system hierarchy. Each setting takes a space-separated list of paths + relative to the host's root directory (i.e. the system running the service manager). Note that if paths + contain symlinks, they are resolved relative to the root directory set with + <varname>RootDirectory=</varname>.</para> + + <para>Paths listed in <varname>ReadWritePaths=</varname> are accessible from within the namespace with the same + access modes as from outside of it. Paths listed in <varname>ReadOnlyPaths=</varname> are accessible for + reading only, writing will be refused even if the usual file access controls would permit this. Nest + <varname>ReadWritePaths=</varname> inside of <varname>ReadOnlyPaths=</varname> in order to provide writable + subdirectories within read-only directories. Use <varname>ReadWritePaths=</varname> in order to whitelist + specific paths for write access if <varname>ProtectSystem=strict</varname> is used. Paths listed in + <varname>InaccessiblePaths=</varname> will be made inaccessible for processes inside the namespace (along with + everything below them in the file system hierarchy).</para> + + <para>Note that restricting access with these options does not extend to submounts of a directory that are + created later on. Non-directory paths may be specified as well. These options may be specified more than once, + in which case all paths listed will have limited access from within the namespace. If the empty string is + assigned to this option, the specific list is reset, and all prior assignments have no effect.</para> + + <para>Paths in <varname>ReadWritePaths=</varname>, <varname>ReadOnlyPaths=</varname> and + <varname>InaccessiblePaths=</varname> may be prefixed with <literal>-</literal>, in which case they will be ignored + when they do not exist. Note that using this setting will disconnect propagation of mounts from the service to + the host (propagation in the opposite direction continues to work). This means that this setting may not be used + for services which shall be able to install mount points in the main mount namespace. Note that the effect of + these settings may be undone by privileged processes. In order to set up an effective sandboxed environment for + a unit it is thus recommended to combine these settings with either + <varname>CapabilityBoundingSet=~CAP_SYS_ADMIN</varname> or <varname>SystemCallFilter=~@mount</varname>.</para></listitem> </varlistentry> <varlistentry> @@ -941,37 +919,33 @@ private <filename>/tmp</filename> and <filename>/var/tmp</filename> namespace by using the <varname>JoinsNamespaceOf=</varname> directive, see <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for - details. Note that using this setting will disconnect propagation of mounts from the service to the host - (propagation in the opposite direction continues to work). This means that this setting may not be used for - services which shall be able to install mount points in the main mount namespace. This setting is implied if - <varname>DynamicUser=</varname> is set.</para></listitem> + details. This setting is implied if <varname>DynamicUser=</varname> is set. For this setting the same + restrictions regarding mount propagation and privileges apply as for <varname>ReadOnlyPaths=</varname> and + related calls, see above.</para></listitem> + </varlistentry> <varlistentry> <term><varname>PrivateDevices=</varname></term> - <listitem><para>Takes a boolean argument. If true, sets up a - new /dev namespace for the executed processes and only adds - API pseudo devices such as <filename>/dev/null</filename>, - <filename>/dev/zero</filename> or - <filename>/dev/random</filename> (as well as the pseudo TTY - subsystem) to it, but no physical devices such as - <filename>/dev/sda</filename>. This is useful to securely turn - off physical device access by the executed process. Defaults - to false. Enabling this option will also remove - <constant>CAP_MKNOD</constant> from the capability bounding - set for the unit (see above), and set + <listitem><para>Takes a boolean argument. If true, sets up a new /dev namespace for the executed processes and + only adds API pseudo devices such as <filename>/dev/null</filename>, <filename>/dev/zero</filename> or + <filename>/dev/random</filename> (as well as the pseudo TTY subsystem) to it, but no physical devices such as + <filename>/dev/sda</filename>, system memory <filename>/dev/mem</filename>, system ports + <filename>/dev/port</filename> and others. This is useful to securely turn off physical device access by the + executed process. Defaults to false. Enabling this option will install a system call filter to block low-level + I/O system calls that are grouped in the <varname>@raw-io</varname> set, will also remove + <constant>CAP_MKNOD</constant> from the capability bounding set for the unit (see above), and set <varname>DevicePolicy=closed</varname> (see <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> - for details). Note that using this setting will disconnect - propagation of mounts from the service to the host - (propagation in the opposite direction continues to work). - This means that this setting may not be used for services - which shall be able to install mount points in the main mount - namespace. The /dev namespace will be mounted read-only and 'noexec'. - The latter may break old programs which try to set up executable - memory by using <citerefentry><refentrytitle>mmap</refentrytitle><manvolnum>2</manvolnum></citerefentry> - of <filename>/dev/zero</filename> instead of using <constant>MAP_ANON</constant>.</para></listitem> + for details). Note that using this setting will disconnect propagation of mounts from the service to the host + (propagation in the opposite direction continues to work). This means that this setting may not be used for + services which shall be able to install mount points in the main mount namespace. The /dev namespace will be + mounted read-only and 'noexec'. The latter may break old programs which try to set up executable memory by + using <citerefentry><refentrytitle>mmap</refentrytitle><manvolnum>2</manvolnum></citerefentry> of + <filename>/dev/zero</filename> instead of using <constant>MAP_ANON</constant>. This setting is implied if + <varname>DynamicUser=</varname> is set. For this setting the same restrictions regarding mount propagation and + privileges apply as for <varname>ReadOnlyPaths=</varname> and related calls, see above.</para></listitem> </varlistentry> <varlistentry> @@ -1020,74 +994,80 @@ <varlistentry> <term><varname>ProtectSystem=</varname></term> - <listitem><para>Takes a boolean argument or - <literal>full</literal>. If true, mounts the - <filename>/usr</filename> and <filename>/boot</filename> - directories read-only for processes invoked by this unit. If - set to <literal>full</literal>, the <filename>/etc</filename> - directory is mounted read-only, too. This setting ensures that - any modification of the vendor-supplied operating system (and - optionally its configuration) is prohibited for the service. - It is recommended to enable this setting for all long-running - services, unless they are involved with system updates or need - to modify the operating system in other ways. Note however - that processes retaining the CAP_SYS_ADMIN capability can undo - the effect of this setting. This setting is hence particularly - useful for daemons which have this capability removed, for - example with <varname>CapabilityBoundingSet=</varname>. - Defaults to off.</para></listitem> + <listitem><para>Takes a boolean argument or the special values <literal>full</literal> or + <literal>strict</literal>. If true, mounts the <filename>/usr</filename> and <filename>/boot</filename> + directories read-only for processes invoked by this unit. If set to <literal>full</literal>, the + <filename>/etc</filename> directory is mounted read-only, too. If set to <literal>strict</literal> the entire + file system hierarchy is mounted read-only, except for the API file system subtrees <filename>/dev</filename>, + <filename>/proc</filename> and <filename>/sys</filename> (protect these directories using + <varname>PrivateDevices=</varname>, <varname>ProtectKernelTunables=</varname>, + <varname>ProtectControlGroups=</varname>). This setting ensures that any modification of the vendor-supplied + operating system (and optionally its configuration, and local mounts) is prohibited for the service. It is + recommended to enable this setting for all long-running services, unless they are involved with system updates + or need to modify the operating system in other ways. If this option is used, + <varname>ReadWritePaths=</varname> may be used to exclude specific directories from being made read-only. This + setting is implied if <varname>DynamicUser=</varname> is set. For this setting the same restrictions regarding + mount propagation and privileges apply as for <varname>ReadOnlyPaths=</varname> and related calls, see + above. Defaults to off.</para></listitem> </varlistentry> <varlistentry> <term><varname>ProtectHome=</varname></term> - <listitem><para>Takes a boolean argument or - <literal>read-only</literal>. If true, the directories - <filename>/home</filename>, <filename>/root</filename> and - <filename>/run/user</filename> - are made inaccessible and empty for processes invoked by this - unit. If set to <literal>read-only</literal>, the three - directories are made read-only instead. It is recommended to - enable this setting for all long-running services (in - particular network-facing ones), to ensure they cannot get - access to private user data, unless the services actually - require access to the user's private data. Note however that - processes retaining the CAP_SYS_ADMIN capability can undo the - effect of this setting. This setting is hence particularly - useful for daemons which have this capability removed, for - example with <varname>CapabilityBoundingSet=</varname>. - Defaults to off.</para></listitem> + <listitem><para>Takes a boolean argument or <literal>read-only</literal>. If true, the directories + <filename>/home</filename>, <filename>/root</filename> and <filename>/run/user</filename> are made inaccessible + and empty for processes invoked by this unit. If set to <literal>read-only</literal>, the three directories are + made read-only instead. It is recommended to enable this setting for all long-running services (in particular + network-facing ones), to ensure they cannot get access to private user data, unless the services actually + require access to the user's private data. This setting is implied if <varname>DynamicUser=</varname> is + set. For this setting the same restrictions regarding mount propagation and privileges apply as for + <varname>ReadOnlyPaths=</varname> and related calls, see above.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>ProtectKernelTunables=</varname></term> + + <listitem><para>Takes a boolean argument. If true, kernel variables accessible through + <filename>/proc/sys</filename>, <filename>/sys</filename>, <filename>/proc/sysrq-trigger</filename>, + <filename>/proc/latency_stats</filename>, <filename>/proc/acpi</filename>, + <filename>/proc/timer_stats</filename>, <filename>/proc/fs</filename> and <filename>/proc/irq</filename> will + be made read-only to all processes of the unit. Usually, tunable kernel variables should only be written at + boot-time, with the <citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> + mechanism. Almost no services need to write to these at runtime; it is hence recommended to turn this on for + most services. For this setting the same restrictions regarding mount propagation and privileges apply as for + <varname>ReadOnlyPaths=</varname> and related calls, see above. Defaults to off.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>ProtectControlGroups=</varname></term> + + <listitem><para>Takes a boolean argument. If true, the Linux Control Groups (<citerefentry + project='man-pages'><refentrytitle>cgroups</refentrytitle><manvolnum>7</manvolnum></citerefentry>) hierarchies + accessible through <filename>/sys/fs/cgroup</filename> will be made read-only to all processes of the + unit. Except for container managers no services should require write access to the control groups hierarchies; + it is hence recommended to turn this on for most services. For this setting the same restrictions regarding + mount propagation and privileges apply as for <varname>ReadOnlyPaths=</varname> and related calls, see + above. Defaults to off.</para></listitem> </varlistentry> <varlistentry> <term><varname>MountFlags=</varname></term> - <listitem><para>Takes a mount propagation flag: - <option>shared</option>, <option>slave</option> or - <option>private</option>, which control whether mounts in the - file system namespace set up for this unit's processes will - receive or propagate mounts or unmounts. See - <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>2</manvolnum></citerefentry> - for details. Defaults to <option>shared</option>. Use - <option>shared</option> to ensure that mounts and unmounts are - propagated from the host to the container and vice versa. Use - <option>slave</option> to run processes so that none of their - mounts and unmounts will propagate to the host. Use - <option>private</option> to also ensure that no mounts and - unmounts from the host will propagate into the unit processes' - namespace. Note that <option>slave</option> means that file - systems mounted on the host might stay mounted continuously in - the unit's namespace, and thus keep the device busy. Note that - the file system namespace related options - (<varname>PrivateTmp=</varname>, - <varname>PrivateDevices=</varname>, - <varname>ProtectSystem=</varname>, - <varname>ProtectHome=</varname>, - <varname>ReadOnlyPaths=</varname>, - <varname>InaccessiblePaths=</varname> and - <varname>ReadWritePaths=</varname>) require that mount - and unmount propagation from the unit's file system namespace - is disabled, and hence downgrade <option>shared</option> to + <listitem><para>Takes a mount propagation flag: <option>shared</option>, <option>slave</option> or + <option>private</option>, which control whether mounts in the file system namespace set up for this unit's + processes will receive or propagate mounts or unmounts. See <citerefentry + project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>2</manvolnum></citerefentry> for + details. Defaults to <option>shared</option>. Use <option>shared</option> to ensure that mounts and unmounts + are propagated from the host to the container and vice versa. Use <option>slave</option> to run processes so + that none of their mounts and unmounts will propagate to the host. Use <option>private</option> to also ensure + that no mounts and unmounts from the host will propagate into the unit processes' namespace. Note that + <option>slave</option> means that file systems mounted on the host might stay mounted continuously in the + unit's namespace, and thus keep the device busy. Note that the file system namespace related options + (<varname>PrivateTmp=</varname>, <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>, + <varname>ProtectHome=</varname>, <varname>ProtectKernelTunables=</varname>, + <varname>ProtectControlGroups=</varname>, <varname>ReadOnlyPaths=</varname>, + <varname>InaccessiblePaths=</varname>, <varname>ReadWritePaths=</varname>) require that mount and unmount + propagation from the unit's file system namespace is disabled, and hence downgrade <option>shared</option> to <option>slave</option>. </para></listitem> </varlistentry> @@ -1322,7 +1302,15 @@ </table> Note, that as new system calls are added to the kernel, additional system calls might be added to the groups - above, so the contents of the sets may change between systemd versions.</para></listitem> + above, so the contents of the sets may change between systemd versions.</para> + + <para>It is recommended to combine the file system namespacing related options with + <varname>SystemCallFilter=~@mount</varname>, in order to prohibit the unit's processes to undo the + mappings. Specifically these are the options <varname>PrivateTmp=</varname>, + <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>, <varname>ProtectHome=</varname>, + <varname>ProtectKernelTunables=</varname>, <varname>ProtectControlGroups=</varname>, + <varname>ReadOnlyPaths=</varname>, <varname>InaccessiblePaths=</varname> and + <varname>ReadWritePaths=</varname>.</para></listitem> </varlistentry> <varlistentry> @@ -1346,7 +1334,8 @@ identifiers to include in the system call filter. The known architecture identifiers are <constant>x86</constant>, <constant>x86-64</constant>, <constant>x32</constant>, - <constant>arm</constant> as well as the special identifier + <constant>arm</constant>, <constant>s390</constant>, + <constant>s390x</constant> as well as the special identifier <constant>native</constant>. Only system calls of the specified architectures will be permitted to processes of this unit. This is an effective way to disable compatibility with @@ -1629,8 +1618,8 @@ <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service "result". Currently, the following values are defined: <literal>timeout</literal> (in case of an operation timeout), <literal>exit-code</literal> (if a service process exited with a non-zero exit code; see - <varname>$EXIT_STATUS</varname> below for the actual exit status returned), <literal>signal</literal> (if a - service process was terminated abnormally by a signal; see <varname>$EXIT_STATUS</varname> below for the actual + <varname>$EXIT_CODE</varname> below for the actual exit code returned), <literal>signal</literal> (if a + service process was terminated abnormally by a signal; see <varname>$EXIT_CODE</varname> below for the actual signal used for the termination), <literal>core-dump</literal> (if a service process terminated abnormally and dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the service but it missed the deadline), or <literal>resources</literal> (a catch-all condition in case a system operation @@ -1675,32 +1664,32 @@ <row> <entry morerows="1" valign="top"><literal>timeout</literal></entry> <entry valign="top"><literal>killed</literal></entry> - <entry><literal>TERM</literal><sbr/><literal>KILL</literal></entry> + <entry><literal>TERM</literal>, <literal>KILL</literal></entry> </row> <row> <entry valign="top"><literal>exited</literal></entry> - <entry><literal>0</literal><sbr/><literal>1</literal><sbr/><literal>2</literal><sbr/><literal - >3</literal><sbr/>…<sbr/><literal>255</literal></entry> + <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal + >3</literal>, …, <literal>255</literal></entry> </row> <row> <entry valign="top"><literal>exit-code</literal></entry> <entry valign="top"><literal>exited</literal></entry> - <entry><literal>0</literal><sbr/><literal>1</literal><sbr/><literal>2</literal><sbr/><literal - >3</literal><sbr/>…<sbr/><literal>255</literal></entry> + <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal + >3</literal>, …, <literal>255</literal></entry> </row> <row> <entry valign="top"><literal>signal</literal></entry> <entry valign="top"><literal>killed</literal></entry> - <entry><literal>HUP</literal><sbr/><literal>INT</literal><sbr/><literal>KILL</literal><sbr/>…</entry> + <entry><literal>HUP</literal>, <literal>INT</literal>, <literal>KILL</literal>, …</entry> </row> <row> <entry valign="top"><literal>core-dump</literal></entry> <entry valign="top"><literal>dumped</literal></entry> - <entry><literal>ABRT</literal><sbr/><literal>SEGV</literal><sbr/><literal>QUIT</literal><sbr/>…</entry> + <entry><literal>ABRT</literal>, <literal>SEGV</literal>, <literal>QUIT</literal>, …</entry> </row> <row> @@ -1710,12 +1699,12 @@ </row> <row> <entry><literal>killed</literal></entry> - <entry><literal>TERM</literal><sbr/><literal>KILL</literal></entry> + <entry><literal>TERM</literal>, <literal>KILL</literal></entry> </row> <row> <entry><literal>exited</literal></entry> - <entry><literal>0</literal><sbr/><literal>1</literal><sbr/><literal>2</literal><sbr/><literal - >3</literal><sbr/>…<sbr/><literal>255</literal></entry> + <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal + >3</literal>, …, <literal>255</literal></entry> </row> <row> diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 1f9f071b94..ffb66e735b 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -58,31 +58,38 @@ <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. </para> - <para>Virtual Network Device files must have the extension - <filename>.netdev</filename>; other extensions are ignored. - Virtual network devices are created as soon as networkd is - started. If a netdev with the specified name already exists, - networkd will use that as-is rather than create its own. Note that - the settings of the pre-existing netdev will not be changed by + <para>The main Virtual Network Device file must have the extension <filename>.netdev</filename>; + other extensions are ignored. Virtual network devices are created as soon as networkd is + started. If a netdev with the specified name already exists, networkd will use that as-is rather + than create its own. Note that the settings of the pre-existing netdev will not be changed by networkd.</para> - <para>The <filename>.netdev</filename> files are read from the - files located in the system network directory - <filename>/usr/lib/systemd/network</filename>, the volatile - runtime network directory - <filename>/run/systemd/network</filename> and the local - administration network directory - <filename>/etc/systemd/network</filename>. All configuration files - are collectively sorted and processed in lexical order, regardless - of the directories in which they live. However, files with - identical filenames replace each other. Files in - <filename>/etc</filename> have the highest priority, files in - <filename>/run</filename> take precedence over files with the same - name in <filename>/usr/lib</filename>. This can be used to - override a system-supplied configuration file with a local file if - needed. As a special case, an empty file (file size 0) or symlink - with the same name pointing to <filename>/dev/null</filename> - disables the configuration file entirely (it is "masked").</para> + <para>The <filename>.netdev</filename> files are read from the files located in the system + network directory <filename>/usr/lib/systemd/network</filename>, the volatile runtime network + directory <filename>/run/systemd/network</filename> and the local administration network + directory <filename>/etc/systemd/network</filename>. All configuration files are collectively + sorted and processed in lexical order, regardless of the directories in which they live. + However, files with identical filenames replace each other. Files in <filename>/etc</filename> + have the highest priority, files in <filename>/run</filename> take precedence over files with + the same name in <filename>/usr/lib</filename>. This can be used to override a system-supplied + configuration file with a local file if needed. As a special case, an empty file (file size 0) + or symlink with the same name pointing to <filename>/dev/null</filename> disables the + configuration file entirely (it is "masked").</para> + + <para>Along with the netdev file <filename>foo.netdev</filename>, a "drop-in" directory + <filename>foo.netdev.d/</filename> may exist. All files with the suffix <literal>.conf</literal> + from this directory will be parsed after the file itself is parsed. This is useful to alter or + add configuration settings, without having to modify the main configuration file. Each drop-in + file must have appropriate section headers.</para> + + <para>In addition to <filename>/etc/systemd/network</filename>, drop-in <literal>.d</literal> + directories can be placed in <filename>/usr/lib/systemd/network</filename> or + <filename>/run/systemd/network</filename> directories. Drop-in files in + <filename>/etc</filename> take precedence over those in <filename>/run</filename> which in turn + take precedence over those in <filename>/usr/lib</filename>. Drop-in files under any of these + directories take precedence over the main netdev file wherever located. (Of course, since + <filename>/run</filename> is temporary and <filename>/usr/lib</filename> is for vendors, it is + unlikely drop-ins should be used in either of those places.)</para> </refsect1> <refsect1> @@ -163,7 +170,10 @@ <entry>A virtual extensible LAN (vxlan), for connecting Cloud computing deployments.</entry></row> <row><entry><varname>vrf</varname></entry> - <entry>A Virtual Routing and Forwarding (<ulink url="https://www.kernel.org/doc/Documentation/networking/vrf.txt">VRF</ulink>) interface to create separate routing and forwarding domains.</entry></row> + <entry>A Virtual Routing and Forwarding (<ulink url="https://www.kernel.org/doc/Documentation/networking/vrf.txt">VRF</ulink>) interface to create separate routing and forwarding domains.</entry></row> + + <row><entry><varname>vcan</varname></entry> + <entry>The virtual CAN driver (vcan). Similar to the network loopback devices, vcan offers a virtual local CAN interface.</entry></row> </tbody> </tgroup> @@ -318,7 +328,7 @@ <term><varname>AgeingTimeSec=</varname></term> <listitem> <para>This specifies the number of seconds a MAC Address will be kept in - the forwaring database after having a packet received from this MAC Address.</para> + the forwarding database after having a packet received from this MAC Address.</para> </listitem> </varlistentry> <varlistentry> @@ -527,7 +537,7 @@ </listitem> </varlistentry> <varlistentry> - <term><varname>UDPCheckSum=</varname></term> + <term><varname>UDPChecksum=</varname></term> <listitem> <para>A boolean. When true, transmitting UDP checksums when doing VXLAN/IPv4 is turned on.</para> </listitem> @@ -539,11 +549,23 @@ </listitem> </varlistentry> <varlistentry> - <term><varname>UDP6ZeroCheckSumRx=</varname></term> + <term><varname>UDP6ZeroChecksumRx=</varname></term> <listitem> <para>A boolean. When true, receiving zero checksums in VXLAN/IPv6 is turned on.</para> </listitem> </varlistentry> + <varlistentry> + <term><varname>RemoteChecksumTx=</varname></term> + <listitem> + <para>A boolean. When true, remote transmit checksum offload of VXLAN is turned on.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>RemoteChecksumRx=</varname></term> + <listitem> + <para>A boolean. When true, remote receive checksum offload in VXLAN is turned on.</para> + </listitem> + </varlistentry> <varlistentry> <term><varname>GroupPolicyExtension=</varname></term> <listitem> diff --git a/man/systemd.network.xml b/man/systemd.network.xml index c332cd7bdc..2fb4907634 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -58,31 +58,40 @@ <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. </para> - <para>Network files must have the extension - <filename>.network</filename>; other extensions are ignored. - Networks are applied to links whenever the links appear.</para> - - <para>The <filename>.network</filename> files are read from the - files located in the system network directory - <filename>/usr/lib/systemd/network</filename>, the volatile - runtime network directory - <filename>/run/systemd/network</filename> and the local - administration network directory - <filename>/etc/systemd/network</filename>. All configuration files - are collectively sorted and processed in lexical order, regardless - of the directories in which they live. However, files with - identical filenames replace each other. Files in - <filename>/etc</filename> have the highest priority, files in - <filename>/run</filename> take precedence over files with the same - name in <filename>/usr/lib</filename>. This can be used to - override a system-supplied configuration file with a local file if - needed. As a special case, an empty file (file size 0) or symlink - with the same name pointing to <filename>/dev/null</filename> - disables the configuration file entirely (it is "masked").</para> - - <para>Note that an interface without any static IPv6 addresses configured, and neither DHCPv6 nor IPv6LL enabled, - shall be considered to have no IPv6 support. IPv6 will be automatically disabled for that interface by writing "1" - to <filename>/proc/sys/net/ipv6/conf/<replaceable>ifname</replaceable>/disable_ipv6</filename>. + <para>The main network file must have the extension <filename>.network</filename>; other + extensions are ignored. Networks are applied to links whenever the links appear.</para> + + <para>The <filename>.network</filename> files are read from the files located in the system + network directory <filename>/usr/lib/systemd/network</filename>, the volatile runtime network + directory <filename>/run/systemd/network</filename> and the local administration network + directory <filename>/etc/systemd/network</filename>. All configuration files are collectively + sorted and processed in lexical order, regardless of the directories in which they live. + However, files with identical filenames replace each other. Files in <filename>/etc</filename> + have the highest priority, files in <filename>/run</filename> take precedence over files with + the same name in <filename>/usr/lib</filename>. This can be used to override a system-supplied + configuration file with a local file if needed. As a special case, an empty file (file size 0) + or symlink with the same name pointing to <filename>/dev/null</filename> disables the + configuration file entirely (it is "masked").</para> + + <para>Along with the network file <filename>foo.network</filename>, a "drop-in" directory + <filename>foo.network.d/</filename> may exist. All files with the suffix + <literal>.conf</literal> from this directory will be parsed after the file itself is + parsed. This is useful to alter or add configuration settings, without having to modify the main + configuration file. Each drop-in file must have appropriate section headers.</para> + + <para>In addition to <filename>/etc/systemd/network</filename>, drop-in <literal>.d</literal> + directories can be placed in <filename>/usr/lib/systemd/network</filename> or + <filename>/run/systemd/network</filename> directories. Drop-in files in + <filename>/etc</filename> take precedence over those in <filename>/run</filename> which in turn + take precedence over those in <filename>/usr/lib</filename>. Drop-in files under any of these + directories take precedence over the main netdev file wherever located. (Of course, since + <filename>/run</filename> is temporary and <filename>/usr/lib</filename> is for vendors, it is + unlikely drop-ins should be used in either of those places.)</para> + + <para>Note that an interface without any static IPv6 addresses configured, and neither DHCPv6 + nor IPv6LL enabled, shall be considered to have no IPv6 support. IPv6 will be automatically + disabled for that interface by writing "1" to + <filename>/proc/sys/net/ipv6/conf/<replaceable>ifname</replaceable>/disable_ipv6</filename>. </para> </refsect1> @@ -458,24 +467,31 @@ <varlistentry> <term><varname>Domains=</varname></term> <listitem> - <para>The domains used for DNS host name resolution on this link. Takes a list of DNS domain names which - are used as search suffixes for extending single-label host names (host names containing no dots) to become - fully qualified domain names (FQDNs). If a single-label host name is resolved on this interface, each of - the specified search domains are appended to it in turn, converting it into a fully qualified domain name, - until one of them may be successfully resolved.</para> - - <para>The specified domains are also used for routing of DNS queries: look-ups for host names ending in the - domains specified here are preferably routed to the DNS servers configured for this interface. If a domain - name is prefixed with <literal>~</literal>, the domain name becomes a pure "routing" domain, is used for - DNS query routing purposes only and is not used in the described domain search logic. By specifying a - routing domain of <literal>~.</literal> (the tilde indicating definition of a routing domain, the dot - referring to the DNS root domain which is the implied suffix of all valid DNS names) it is possible to - route all DNS traffic preferably to the DNS server specified for this interface. The route domain logic is - particularly useful on multi-homed hosts with DNS servers serving particular private DNS zones on each - interface.</para> + <para>A list of domains which should be resolved using the DNS servers on this link. Each item in the list + should be a domain name, optionally prefixed with a tilde (<literal>~</literal>). The domains with the + prefix are called "routing-only domains". The domains without the prefix are called "search domains" and + are first used as search suffixes for extending single-label host names (host names containing no dots) to + become fully qualified domain names (FQDNs). If a single-label host name is resolved on this interface, + each of the specified search domains are appended to it in turn, converting it into a fully qualified + domain name, until one of them may be successfully resolved.</para> + + <para>Both "search" and "routing-only" domains are used for routing of DNS queries: look-ups for host names + ending in those domains (hence also single label names, if any "search domains" are listed), are routed to + the DNS servers configured for this interface. The domain routing logic is particularly useful on + multi-homed hosts with DNS servers serving particular private DNS zones on each interface.</para> + + <para>The "routing-only" domain <literal>~.</literal> (the tilde indicating definition of a routing domain, + the dot referring to the DNS root domain which is the implied suffix of all valid DNS names) has special + effect. It causes all DNS traffic which does not match another configured domain routing entry to be routed + to DNS servers specified for this interface. This setting is useful to prefer a certain set of DNS servers + if a link on which they are connected is available.</para> <para>This setting is read by - <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> + <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. + "Search domains" correspond to the <varname>domain</varname> and <varname>search</varname> entries in + <citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + Domain name routing has no equivalent in the traditional glibc API, which has no concept of domain + name servers limited to a specific link.</para> </listitem> </varlistentry> <varlistentry> @@ -679,6 +695,57 @@ which is then configured to use them explicitly.</para> </listitem> </varlistentry> + <varlistentry> + <term><varname>HomeAddress=</varname></term> + <listitem> + <para>Takes a boolean argument. Designates this address the "home address" as defined in + <ulink url="https://tools.ietf.org/html/rfc6275">RFC 6275</ulink>. + Supported only on IPv6. Defaults to false.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>DuplicateAddressDetection=</varname></term> + <listitem> + <para>Takes a boolean argument. Do not perform Duplicate Address Detection + <ulink url="https://tools.ietf.org/html/rfc4862">RFC 4862</ulink> when adding this address. + Supported only on IPv6. Defaults to false.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>ManageTemporaryAddress=</varname></term> + <listitem> + <para>Takes a boolean argument. If true the kernel manage temporary addresses created + from this one as template on behalf of Privacy Extensions + <ulink url="https://tools.ietf.org/html/rfc3041">RFC 3041</ulink>. For this to become + active, the use_tempaddr sysctl setting has to be set to a value greater than zero. + The given address needs to have a prefix length of 64. This flag allows to use privacy + extensions in a manually configured network, just like if stateless auto-configuration + was active. Defaults to false. </para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>PrefixRoute=</varname></term> + <listitem> + <para>Takes a boolean argument. When adding or modifying an IPv6 address, the userspace + application needs a way to suppress adding a prefix route. This is for example relevant + together with IFA_F_MANAGERTEMPADDR, where userspace creates autoconf generated addresses, + but depending on on-link, no route for the prefix should be added. Defaults to false.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>AutoJoin=</varname></term> + <listitem> + <para>Takes a boolean argument. Joining multicast group on ethernet level via + <command>ip maddr</command> command would not work if we have an Ethernet switch that does + IGMP snooping since the switch would not replicate multicast packets on ports that did not + have IGMP reports for the multicast addresses. Linux vxlan interfaces created via + <command>ip link add vxlan</command> or networkd's netdev kind vxlan have the group option + that enables then to do the required join. By extending ip address command with option + <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS) vxlan + interfaces as well as other tunneling mechanisms that need to receive multicast traffic. + Defaults to <literal>no</literal>.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> @@ -908,6 +975,15 @@ DHCP server.</para> </listitem> </varlistentry> + + <varlistentry> + <term><varname>RouteTable=<replaceable>num</replaceable></varname></term> + <listitem> + <para>The table identifier for DHCP routes (a number between 1 and 4294967295, or 0 to unset). + The table can be retrieved using <command>ip route show table <replaceable>num</replaceable></command>. + </para> + </listitem> + </varlistentry> </variablelist> </refsect1> @@ -948,6 +1024,16 @@ project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> </listitem> </varlistentry> + + <varlistentry> + <term><varname>RouteTable=<replaceable>num</replaceable></varname></term> + <listitem> + <para>The table identifier for the routes received in the Router Advertisement + (a number between 1 and 4294967295, or 0 to unset). + The table can be retrieved using <command>ip route show table <replaceable>num</replaceable></command>. + </para> + </listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 26e5d3ce7b..d759e17289 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -294,10 +294,10 @@ <term><varname>ListenUSBFunction=</varname></term> <listitem><para>Specifies a <ulink url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">USB - FunctionFS</ulink> endpoint location to listen on, for + FunctionFS</ulink> endpoints location to listen on, for implementation of USB gadget functions. This expects an - absolute file system path as the argument. Behavior otherwise - is very similar to the <varname>ListenFIFO=</varname> + absolute file system path of functionfs mount point as the argument. + Behavior otherwise is very similar to the <varname>ListenFIFO=</varname> directive above. Use this to open the FunctionFS endpoint <filename>ep0</filename>. When using this option, the activated service has to have the @@ -445,7 +445,7 @@ <varlistentry> <term><varname>MaxConnectionsPerSource=</varname></term> <listitem><para>The maximum number of connections for a service per source IP address. - This is is very similar to the <varname>MaxConnections=</varname> directive + This is very similar to the <varname>MaxConnections=</varname> directive above. Disabled by default.</para> </listitem> </varlistentry> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index f818e772a9..9778283fec 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -144,71 +144,71 @@ <option>false</option> and <option>off</option> are equivalent.</para> - <para>Time span values encoded in unit files can be written in various formats. A stand-alone number specifies a - time in seconds. If suffixed with a time unit, the unit is honored. A concatenation of multiple values with units - is supported, in which case the values are added up. Example: <literal>50</literal> refers to 50 seconds; - <literal>2min 200ms</literal> refers to 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units - are understood: <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>, + <para>Time span values encoded in unit files can be written in various formats. A stand-alone + number specifies a time in seconds. If suffixed with a time unit, the unit is honored. A + concatenation of multiple values with units is supported, in which case the values are added + up. Example: <literal>50</literal> refers to 50 seconds; <literal>2min 200ms</literal> refers to + 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units are understood: + <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>, <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>. For details see <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> - <para>Empty lines and lines starting with <literal>#</literal> or <literal>;</literal> 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> - - <para>Units can be aliased (have an alternative name), by creating a symlink from the new name to the existing name - in one of the unit search paths. For example, <filename>systemd-networkd.service</filename> has the alias - <filename>dbus-org.freedesktop.network1.service</filename>, created during installation as the symlink - <filename>/usr/lib/systemd/system/dbus-org.freedesktop.network1.service</filename>. In addition, unit files may - specify aliases through the <varname>Alias=</varname> directive in the [Install] section; those aliases are only - effective when the unit is enabled. When the unit is enabled, symlinks will be created for those names, and removed - when the unit is disabled. For example, <filename>reboot.target</filename> specifies - <varname>Alias=ctrl-alt-del.target</varname>, so when enabled it will be invoked whenever CTRL+ALT+DEL is - pressed. Alias names may be used in commands like <command>enable</command>, <command>disable</command>, - <command>start</command>, <command>stop</command>, <command>status</command>, …, and in unit dependency directives - <varname>Wants=</varname>, <varname>Requires=</varname>, <varname>Before=</varname>, <varname>After=</varname>, …, - with the limitation that aliases specified through <varname>Alias=</varname> are only effective when the unit is - enabled. Aliases cannot be used with the <command>preset</command> command.</para> - - <para>Along with a unit file <filename>foo.service</filename>, the - directory <filename>foo.service.wants/</filename> may exist. All - unit files symlinked from such a directory are implicitly added as - dependencies of type <varname>Wants=</varname> to the unit. This - is useful to hook units into the start-up of other units, without - having to modify their unit files. For details about the semantics - of <varname>Wants=</varname>, see below. The preferred way to - create symlinks in the <filename>.wants/</filename> directory of a - unit file is with the <command>enable</command> command of the + <para>Empty lines and lines starting with <literal>#</literal> or <literal>;</literal> 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> + + <para>Units can be aliased (have an alternative name), by creating a symlink from the new name + to the existing name in one of the unit search paths. For example, + <filename>systemd-networkd.service</filename> has the alias + <filename>dbus-org.freedesktop.network1.service</filename>, created during installation as the + symlink <filename>/usr/lib/systemd/system/dbus-org.freedesktop.network1.service</filename>. In + addition, unit files may specify aliases through the <varname>Alias=</varname> directive in the + [Install] section; those aliases are only effective when the unit is enabled. When the unit is + enabled, symlinks will be created for those names, and removed when the unit is disabled. For + example, <filename>reboot.target</filename> specifies + <varname>Alias=ctrl-alt-del.target</varname>, so when enabled it will be invoked whenever + CTRL+ALT+DEL is pressed. Alias names may be used in commands like <command>enable</command>, + <command>disable</command>, <command>start</command>, <command>stop</command>, + <command>status</command>, …, and in unit dependency directives <varname>Wants=</varname>, + <varname>Requires=</varname>, <varname>Before=</varname>, <varname>After=</varname>, …, with the + limitation that aliases specified through <varname>Alias=</varname> are only effective when the + unit is enabled. Aliases cannot be used with the <command>preset</command> command.</para> + + <para>Along with a unit file <filename>foo.service</filename>, the directory + <filename>foo.service.wants/</filename> may exist. All unit files symlinked from such a + directory are implicitly added as dependencies of type <varname>Wants=</varname> to the unit. + This is useful to hook units into the start-up of other units, without having to modify their + unit files. For details about the semantics of <varname>Wants=</varname>, see below. The + preferred way to create symlinks in the <filename>.wants/</filename> directory of a unit file is + with the <command>enable</command> command of the <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> - tool which reads information from the [Install] section of unit - files (see below). A similar functionality exists for - <varname>Requires=</varname> type dependencies as well, the - directory suffix is <filename>.requires/</filename> in this - case.</para> + tool which reads information from the [Install] section of unit files (see below). A similar + functionality exists for <varname>Requires=</varname> type dependencies as well, the directory + suffix is <filename>.requires/</filename> in this case.</para> <para>Along with a unit file <filename>foo.service</filename>, a "drop-in" directory - <filename>foo.service.d/</filename> may exist. All files with the suffix <literal>.conf</literal> from this - directory will be parsed after the file itself is parsed. This is useful to alter or add configuration settings for - a unit, without having to modify unit files. Each drop-in file must have appropriate section headers. Note that for - instantiated units, this logic will first look for the instance <literal>.d/</literal> subdirectory and read its - <literal>.conf</literal> files, followed by the template <literal>.d/</literal> subdirectory and the - <literal>.conf</literal> files there. Also note that settings from the <literal>[Install]</literal> section are not - honoured in drop-in unit files, and have no effect.</para> - - <para>In addition to <filename>/etc/systemd/system</filename>, - the drop-in <literal>.conf</literal> files for system services - can be placed in <filename>/usr/lib/systemd/system</filename> or - <filename>/run/systemd/system</filename> directories. Drop-in - files in <filename>/etc</filename> take precedence over those in - <filename>/run</filename> which in turn take precedence over - those in <filename>/usr/lib</filename>. Drop-in files under any of - these directories take precedence over unit files wherever located. - (Of course, since <filename>/run</filename> is temporary and - <filename>/usr/lib</filename> is for vendors, it is unlikely - drop-ins should be used in either of those places.)</para> - <!-- Note that we do not document .include here, as we - consider it mostly obsolete, and want people to - use .d/ drop-ins instead. --> + <filename>foo.service.d/</filename> may exist. All files with the suffix + <literal>.conf</literal> from this directory will be parsed after the file itself is + parsed. This is useful to alter or add configuration settings for a unit, without having to + modify unit files. Each drop-in file must have appropriate section headers. Note that for + instantiated units, this logic will first look for the instance <literal>.d/</literal> + subdirectory and read its <literal>.conf</literal> files, followed by the template + <literal>.d/</literal> subdirectory and the <literal>.conf</literal> files there. Also note that + settings from the <literal>[Install]</literal> section are not honoured in drop-in unit files, + and have no effect.</para> + + <para>In addition to <filename>/etc/systemd/system</filename>, the drop-in <literal>.d</literal> + directories for system services can be placed in <filename>/usr/lib/systemd/system</filename> or + <filename>/run/systemd/system</filename> directories. Drop-in files in <filename>/etc</filename> + take precedence over those in <filename>/run</filename> which in turn take precedence over those + in <filename>/usr/lib</filename>. Drop-in files under any of these directories take precedence + over unit files wherever located. (Of course, since <filename>/run</filename> is temporary and + <filename>/usr/lib</filename> is for vendors, it is unlikely drop-ins should be used in either + of those places.)</para> + + <!-- Note that we do not document .include here, as we consider it mostly obsolete, and want + people to use .d/ drop-ins instead. --> <para>Some unit names reflect paths existing in the file system namespace. Example: a device unit diff --git a/man/systemd.xml b/man/systemd.xml index e30333e209..7f24a874ed 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -272,7 +272,7 @@ <title>Concepts</title> <para>systemd provides a dependency system between various - entities called "units" of 12 different types. Units encapsulate + entities called "units" of 11 different types. Units encapsulate various objects that are relevant for system boot-up and maintenance. The majority of units are configured in unit configuration files, whose syntax and basic set of options is diff --git a/mkosi.build b/mkosi.build index 09d835d45b..94a6667e42 100755 --- a/mkosi.build +++ b/mkosi.build @@ -20,7 +20,6 @@ # This is a build script for OS image generation using mkosi (https://github.com/systemd/mkosi). # Simply invoke "mkosi" in the project directory to build an OS image. -git clean -dfqx ./autogen.sh c make -j `nproc` make install diff --git a/mkosi.default b/mkosi.default index 1c161df836..2718c9e2a0 100644..120000 --- a/mkosi.default +++ b/mkosi.default @@ -1,72 +1 @@ -# This file is part of systemd. -# -# Copyright 2016 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/>. - -# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). -# Simply invoke "mkosi" in the project directory to build an OS image. - -[Distribution] -Distribution=fedora -Release=24 - -[Output] -Format=raw_btrfs -Bootable=yes - -[Partitions] -RootSize=2G - -[Packages] -Cache=/var/tmp/dnf-cache -BuildPackages= - audit-libs-devel - autoconf - automake - bzip2-devel - cryptsetup-devel - dbus-devel - docbook-style-xsl - elfutils-devel - gcc - git - gnu-efi - gnu-efi-devel - gnutls-devel - gperf - intltool - iptables-devel - kmod-devel - libacl-devel - libblkid-devel - libcap-devel - libcurl-devel - libgcrypt-devel - libidn-devel - libmicrohttpd-devel - libmount-devel - libseccomp-devel - libselinux-devel - libtool - libxkbcommon-devel - libxslt - lz4-devel - make - pam-devel - pkgconfig - python3-devel - python3-lxml - qrencode-devel - xz-devel +.mkosi/mkosi.fedora
\ No newline at end of file diff --git a/po/LINGUAS b/po/LINGUAS index 2f1ba199ac..287d42b047 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1,6 +1,7 @@ be be@latin bg +cs da de el diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000000..d5f1dcafeb --- /dev/null +++ b/po/cs.po @@ -0,0 +1,582 @@ +# Czech translation for systemd. +# Copyright (C) 2016 systemd's author and translators. +# This file is distributed under the same license as the systemd package. +# Daniel Maixner <xskipy@gmail.com>, 2016 +# Daniel Rusek <mail@asciiwolf.com>, 2016 +# +msgid "" +msgstr "" +"Project-Id-Version: systemd master\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-04-23 14:24+0200\n" +"PO-Revision-Date: 2016-09-22 16:00+0200\n" +"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1 +msgid "Send passphrase back to system" +msgstr "Odeslat heslo zpět do systému" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2 +msgid "" +"Authentication is required to send the entered passphrase back to the system." +msgstr "" +"Autentizace je vyžadována pro odeslání zadaného hesla do systému." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3 +msgid "Manage system services or other units" +msgstr "Správa systémových služeb nebo dalších jednotek" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4 +msgid "Authentication is required to manage system services or other units." +msgstr "" +"Autentizace je vyžadována pro správu systémových služeb nebo dalších " +"jednotek." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5 +msgid "Manage system service or unit files" +msgstr "Správa systémové služby nebo souborů jednotky" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6 +msgid "Authentication is required to manage system service or unit files." +msgstr "Autentizace je vyžadována pro správu systémové služby nebo souborů jednotky." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7 +msgid "Set or unset system and service manager environment variables" +msgstr "" +"Nastavení nebo rušení proměnných správce systému a služeb" + + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8 +msgid "" +"Authentication is required to set or unset system and service manager " +"environment variables." +msgstr "" +"Autentizace je vyžadována pro nastavení nebo rušení proměnných správce " +"systému a služeb." + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9 +msgid "Reload the systemd state" +msgstr "Znovu načíst stav systemd" + +#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10 +msgid "Authentication is required to reload the systemd state." +msgstr "Autentizace je vyžadována pro znovu načtení stavu systemd." + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 +msgid "Set host name" +msgstr "Nastavení názvu stroje" + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 +msgid "Authentication is required to set the local host name." +msgstr "Autentizace je vyžadována pro nastavení lokálního názvu stroje." + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 +msgid "Set static host name" +msgstr "Nastavení statického názvu stoje" + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 +msgid "" +"Authentication is required to set the statically configured local host name, " +"as well as the pretty host name." +msgstr "" +"Autentizace je vyžadována pro nastavení staticky konfigurovaného názvu " +"lokálního stroje, stejně tak pro změnu uživatelsky přívětivého jména." + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5 +msgid "Set machine information" +msgstr "Nastavení informací o stroji" + +#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6 +msgid "Authentication is required to set local machine information." +msgstr "Autentizace je vyžadována pro nastavení informací o stroji." + +#: ../src/import/org.freedesktop.import1.policy.in.h:1 +msgid "Import a VM or container image" +msgstr "Import obrazu virtuální stroje nebo kontejneru" + +#: ../src/import/org.freedesktop.import1.policy.in.h:2 +msgid "Authentication is required to import a VM or container image" +msgstr "" +"Autentizace je vyžadována pro import obrazu virtuálního stroje nebo kontejneru" + +#: ../src/import/org.freedesktop.import1.policy.in.h:3 +msgid "Export a VM or container image" +msgstr "Export obrazu virtuálního stroje nebo kontejneru" + +#: ../src/import/org.freedesktop.import1.policy.in.h:4 +msgid "Authentication is required to export a VM or container image" +msgstr "" +"Autentizace je vyžadována pro export obrazu virtuálního stroje nebo kontejneru" + +#: ../src/import/org.freedesktop.import1.policy.in.h:5 +msgid "Download a VM or container image" +msgstr "Stáhnout obraz virtuálního stroje nebo kontejneru" + +#: ../src/import/org.freedesktop.import1.policy.in.h:6 +msgid "Authentication is required to download a VM or container image" +msgstr "" +"Autentizace je vyžadována pro stažení obrazu virtuálního stroje nebo kontejneru" + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:1 +msgid "Set system locale" +msgstr "Nastavení lokalizace systému" + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:2 +msgid "Authentication is required to set the system locale." +msgstr "Autentizace je vyžadována pro nastavení lokalizace systému." + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:3 +msgid "Set system keyboard settings" +msgstr "Nastavení systémové konfigurace klávesnice" + +#: ../src/locale/org.freedesktop.locale1.policy.in.h:4 +msgid "Authentication is required to set the system keyboard settings." +msgstr "" +"Autentizace je vyžadována pro nastavení systémové konfigurace klávesnice." + +#: ../src/login/org.freedesktop.login1.policy.in.h:1 +msgid "Allow applications to inhibit system shutdown" +msgstr "Povolit aplikacím zakázat vypnutí systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:2 +msgid "" +"Authentication is required for an application to inhibit system shutdown." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat vypnutí systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:3 +msgid "Allow applications to delay system shutdown" +msgstr "Povolit aplikacím odložit vypnutí systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:4 +msgid "Authentication is required for an application to delay system shutdown." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím odložit vypnutí systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:5 +msgid "Allow applications to inhibit system sleep" +msgstr "Povolit aplikacím zakázat uspání systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:6 +msgid "Authentication is required for an application to inhibit system sleep." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat uspání systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:7 +msgid "Allow applications to delay system sleep" +msgstr "Povolit aplikacím odložit uspání systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:8 +msgid "Authentication is required for an application to delay system sleep." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím odložit uspání systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:9 +msgid "Allow applications to inhibit automatic system suspend" +msgstr "Povolit aplikacím zakázat automatické vypnutí systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:10 +msgid "" +"Authentication is required for an application to inhibit automatic system " +"suspend." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat automatické " +"vypnutí systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:11 +msgid "Allow applications to inhibit system handling of the power key" +msgstr "" +"Povolit aplikacím zakázat chovaní systému na stisknutí vypínacího tlačítka" + +#: ../src/login/org.freedesktop.login1.policy.in.h:12 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the power key." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat chovaní systému na " +"stisknutí vypínacího tlačítka." + +#: ../src/login/org.freedesktop.login1.policy.in.h:13 +msgid "Allow applications to inhibit system handling of the suspend key" +msgstr "" +"Povolit aplikacím zakázat chovaní systému na stisknutí uspávacího tlačítka" + +#: ../src/login/org.freedesktop.login1.policy.in.h:14 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the suspend key." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat chovaní systému na " +"stisknutí uspávacího tlačítka." + +#: ../src/login/org.freedesktop.login1.policy.in.h:15 +msgid "Allow applications to inhibit system handling of the hibernate key" +msgstr "" +"Povolit aplikacím zakázat chovaní systému na stisknutí tlačítka hibernace" + +#: ../src/login/org.freedesktop.login1.policy.in.h:16 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the hibernate key." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat chovaní systému na " +"stisknutí tlačítka hibernace." + +#: ../src/login/org.freedesktop.login1.policy.in.h:17 +msgid "Allow applications to inhibit system handling of the lid switch" +msgstr "Povolit aplikacím zakázat chovaní systému na zavření víka" + +#: ../src/login/org.freedesktop.login1.policy.in.h:18 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the lid switch." +msgstr "" +"Autentizace je vyžadována pro povolení aplikacím zakázat chovaní systému na " +"zavření víka." + +#: ../src/login/org.freedesktop.login1.policy.in.h:19 +msgid "Allow non-logged-in user to run programs" +msgstr "Povolit nepřihlášenému uživateli spouštět programy" + +#: ../src/login/org.freedesktop.login1.policy.in.h:20 +msgid "Explicit request is required to run programs as a non-logged-in user." +msgstr "" +"Speciální požadavek je třeba ke spuštění programů jako nepřihlášený uživatel." + +#: ../src/login/org.freedesktop.login1.policy.in.h:21 +msgid "Allow non-logged-in users to run programs" +msgstr "Povolit nepřihlášeným uživatelům spouštět programy" + +#: ../src/login/org.freedesktop.login1.policy.in.h:22 +msgid "Authentication is required to run programs as a non-logged-in user." +msgstr "" +"Autentizace je vyžadována ke spuštění programů jako nepřihlášený uživatel." + +#: ../src/login/org.freedesktop.login1.policy.in.h:23 +msgid "Allow attaching devices to seats" +msgstr "Povolit připojování zařízení ke stanovišti" + +#: ../src/login/org.freedesktop.login1.policy.in.h:24 +msgid "Authentication is required for attaching a device to a seat." +msgstr "Autentizace je vyžadována pro připojování zařízení ke stanovišti." + +#: ../src/login/org.freedesktop.login1.policy.in.h:25 +msgid "Flush device to seat attachments" +msgstr "Odstranění přiřazení zařízení ke stanovištím" + +#: ../src/login/org.freedesktop.login1.policy.in.h:26 +msgid "" +"Authentication is required for resetting how devices are attached to seats." +msgstr "" +"Autentizace je vyžadována pro reset způsobu jak jsou zařízení přiřazována ke " +"stanovištím." + +#: ../src/login/org.freedesktop.login1.policy.in.h:27 +msgid "Power off the system" +msgstr "Vypnutí systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:28 +msgid "Authentication is required for powering off the system." +msgstr "Autentizace je vyžadována pro vypnutí systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:29 +msgid "Power off the system while other users are logged in" +msgstr "Vypnout systém, i když jsou přihlášeni další uživatelé" + +#: ../src/login/org.freedesktop.login1.policy.in.h:30 +msgid "" +"Authentication is required for powering off the system while other users are " +"logged in." +msgstr "" +"Autentizace je vyžadována pro vypnutí systému, když jsou přihlášeni " +"další uživatelé." + +#: ../src/login/org.freedesktop.login1.policy.in.h:31 +msgid "Power off the system while an application asked to inhibit it" +msgstr "Vypnout systém, i když aplikace požádala o zákaz vypnutí" + +#: ../src/login/org.freedesktop.login1.policy.in.h:32 +msgid "" +"Authentication is required for powering off the system while an application " +"asked to inhibit it." +msgstr "" +"Autentizace je vyžadována pro vypnutí systému, když aplikace požádala o " +"zákaz vypnutí." + +#: ../src/login/org.freedesktop.login1.policy.in.h:33 +msgid "Reboot the system" +msgstr "Restartovat systém" + +#: ../src/login/org.freedesktop.login1.policy.in.h:34 +msgid "Authentication is required for rebooting the system." +msgstr "Autentizace je vyžadována pro restartovaní systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:35 +msgid "Reboot the system while other users are logged in" +msgstr "Restartovat systém, i když jsou přihlášeni další uživatelé" + +#: ../src/login/org.freedesktop.login1.policy.in.h:36 +msgid "" +"Authentication is required for rebooting the system while other users are " +"logged in." +msgstr "" +"Autentizace je vyžadována pro restart systému, když jsou přihlášeni " +"další uživatelé." + +#: ../src/login/org.freedesktop.login1.policy.in.h:37 +msgid "Reboot the system while an application asked to inhibit it" +msgstr "Restartovat systém, i když aplikace požádala o zákaz restartu" + +#: ../src/login/org.freedesktop.login1.policy.in.h:38 +msgid "" +"Authentication is required for rebooting the system while an application " +"asked to inhibit it." +msgstr "" +"Autentizace je vyžadována pro restart systému, když aplikace požádala o " +"zákaz restartu." + +#: ../src/login/org.freedesktop.login1.policy.in.h:39 +msgid "Suspend the system" +msgstr "Uspání systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:40 +msgid "Authentication is required for suspending the system." +msgstr "Autentizace je vyžadována pro uspání systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:41 +msgid "Suspend the system while other users are logged in" +msgstr "Uspat systém, i když jsou přihlášeni další uživatelé" + +#: ../src/login/org.freedesktop.login1.policy.in.h:42 +msgid "" +"Authentication is required for suspending the system while other users are " +"logged in." +msgstr "" +"Autentizace je vyžadována pro uspání systému, když jsou přihlášeni " +"další uživatelé." + +#: ../src/login/org.freedesktop.login1.policy.in.h:43 +msgid "Suspend the system while an application asked to inhibit it" +msgstr "Uspat systém, i když aplikace požádala o zákaz uspání" + +#: ../src/login/org.freedesktop.login1.policy.in.h:44 +msgid "" +"Authentication is required for suspending the system while an application " +"asked to inhibit it." +msgstr "" +"Autentizace je vyžadována pro uspání systému, když aplikace požádala o " +"zákaz uspání." + +#: ../src/login/org.freedesktop.login1.policy.in.h:45 +msgid "Hibernate the system" +msgstr "Hibernace systému" + +#: ../src/login/org.freedesktop.login1.policy.in.h:46 +msgid "Authentication is required for hibernating the system." +msgstr "Autentizace je vyžadována k hibernaci systému." + +#: ../src/login/org.freedesktop.login1.policy.in.h:47 +msgid "Hibernate the system while other users are logged in" +msgstr "Hibernovat systém, i když jsou přihlášeni další uživatelé" + +#: ../src/login/org.freedesktop.login1.policy.in.h:48 +msgid "" +"Authentication is required for hibernating the system while other users are " +"logged in." +msgstr "" +"Autentizace je vyžadována pro hibernaci systému, když jsou přihlášeni " +"další uživatelé." + +#: ../src/login/org.freedesktop.login1.policy.in.h:49 +msgid "Hibernate the system while an application asked to inhibit it" +msgstr "Hibernace systému, i když aplikace požádala o zákaz hibernace" + +#: ../src/login/org.freedesktop.login1.policy.in.h:50 +msgid "" +"Authentication is required for hibernating the system while an application " +"asked to inhibit it." +msgstr "" +"Autentizace je vyžadována pro hibernaci systému, když aplikace požádala o " +"zákaz hibernace." + +#: ../src/login/org.freedesktop.login1.policy.in.h:51 +msgid "Manage active sessions, users and seats" +msgstr "Správa aktivních sezení, uživatelů a stanovišť" + +#: ../src/login/org.freedesktop.login1.policy.in.h:52 +msgid "" +"Authentication is required for managing active sessions, users and seats." +msgstr "" +"Autentizace je vyžadována pro správu aktivních sezení, uživatelů a stanovišť." + +#: ../src/login/org.freedesktop.login1.policy.in.h:53 +msgid "Lock or unlock active sessions" +msgstr "Zamčení nebo odemčení aktivních sezení" + +#: ../src/login/org.freedesktop.login1.policy.in.h:54 +msgid "Authentication is required to lock or unlock active sessions." +msgstr "Autentizace je vyžadována pro zamčení nebo odemčení aktivních sezení." + +#: ../src/login/org.freedesktop.login1.policy.in.h:55 +msgid "Allow indication to the firmware to boot to setup interface" +msgstr "Povolit indikaci firmwaru bootovat instalační prostředí" + +#: ../src/login/org.freedesktop.login1.policy.in.h:56 +msgid "" +"Authentication is required to indicate to the firmware to boot to setup " +"interface." +msgstr "" +"Autentizace je vyžadována k povolení indikace firmwaru bootovat instalační " +"prostředí." + +#: ../src/login/org.freedesktop.login1.policy.in.h:57 +msgid "Set a wall message" +msgstr "Nastavit zprávu všem uživatelům" + +#: ../src/login/org.freedesktop.login1.policy.in.h:58 +msgid "Authentication is required to set a wall message" +msgstr "Autentizace je vyžadována k nastavení zprávy všem uživatelům" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:1 +msgid "Log into a local container" +msgstr "Přihlásit se do lokálního kontejneru" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:2 +msgid "Authentication is required to log into a local container." +msgstr "Autentizace je vyžadována pro přihlášení do lokálního kontejneru." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:3 +msgid "Log into the local host" +msgstr "Přihlásit se na lokální stroj" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:4 +msgid "Authentication is required to log into the local host." +msgstr "Autentizace je vyžadována pro přihlášení k lokálnímu stroji." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:5 +msgid "Acquire a shell in a local container" +msgstr "Získání shellu v lokálním kontejneru" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:6 +msgid "Authentication is required to acquire a shell in a local container." +msgstr "Autentizace je vyžadována pro získání shellu v lokálním kontejneru." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:7 +msgid "Acquire a shell on the local host" +msgstr "Získání shellu na lokálním stroji" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:8 +msgid "Authentication is required to acquire a shell on the local host." +msgstr "Autentizace je vyžadována pro získání shellu na lokálním stroji." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:9 +msgid "Acquire a pseudo TTY in a local container" +msgstr "Získání Pseudo TTY v lokálním kontejneru" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:10 +msgid "" +"Authentication is required to acquire a pseudo TTY in a local container." +msgstr "Autentizace je vyžadována pro získání pseudo TTY v lokálním kontejneru." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:11 +msgid "Acquire a pseudo TTY on the local host" +msgstr "Získání pseudo TTY na lokálním stroji" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:12 +msgid "Authentication is required to acquire a pseudo TTY on the local host." +msgstr "Autentizace je vyžadována pro získání pseudo TTY na lokálním stroji." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:13 +msgid "Manage local virtual machines and containers" +msgstr "Spravovat lokální virtuální stroje a kontejnery" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:14 +msgid "" +"Authentication is required to manage local virtual machines and containers." +msgstr "" +"Autentizace je vyžadována pro správu lokálních virtuálních strojů a kontejnerů." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:15 +msgid "Manage local virtual machine and container images" +msgstr "Spravovat lokální obrazy virtuálních strojů a kontejnerů" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:16 +msgid "" +"Authentication is required to manage local virtual machine and container " +"images." +msgstr "" +"Autentizace je vyžadována ke správě obrazů virtuálních strojů a kontejnerů." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1 +msgid "Set system time" +msgstr "Nastavit systémový čas" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2 +msgid "Authentication is required to set the system time." +msgstr "Autentizace je vyžadována pro nastavení systémového času." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3 +msgid "Set system timezone" +msgstr "Nastavit systémovou časovou zónu" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4 +msgid "Authentication is required to set the system timezone." +msgstr "Autentizace je vyžadována pro nastavení systémové časové zóny." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5 +msgid "Set RTC to local timezone or UTC" +msgstr "Nastavit RTC na lokální časovou zónu nebo UTC" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6 +msgid "" +"Authentication is required to control whether the RTC stores the local or " +"UTC time." +msgstr "" +"Autentizace je vyžadována pro kontrolu jestli RTC ukládá lokální časovou " +"zónu nebo UTC čas." + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7 +msgid "Turn network time synchronization on or off" +msgstr "Zapnout nebo vypnout synchronizaci s časem ze sítě" + +#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8 +msgid "" +"Authentication is required to control whether network time synchronization " +"shall be enabled." +msgstr "Autentizace je vyžadována pro kontrolu synchronizace času ze sítě." + +#: ../src/core/dbus-unit.c:450 +msgid "Authentication is required to start '$(unit)'." +msgstr "Autentizace je vyžadována pro spuštění „$(unit)”." + +#: ../src/core/dbus-unit.c:451 +msgid "Authentication is required to stop '$(unit)'." +msgstr "Autentizace je vyžadována pro vypnutí „$(unit)”." + +#: ../src/core/dbus-unit.c:452 +msgid "Authentication is required to reload '$(unit)'." +msgstr "Autentizace je vyžadována pro znovu načtení „$(unit)”." + +#: ../src/core/dbus-unit.c:453 ../src/core/dbus-unit.c:454 +msgid "Authentication is required to restart '$(unit)'." +msgstr "Autentizace je vyžadována pro restart „$(unit)”." + +#: ../src/core/dbus-unit.c:560 +msgid "Authentication is required to kill '$(unit)'." +msgstr "Autentizace je vyžadována pro ukončení „$(unit)”." + +#: ../src/core/dbus-unit.c:590 +msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." +msgstr "" +"Autentizace je vyžadována pro resetování chybného stavu " +"„$(unit)”." + +#: ../src/core/dbus-unit.c:622 +msgid "Authentication is required to set properties on '$(unit)'." +msgstr "Autentizace je vyžadována pro nastavení vlastností na „$(unit)”." @@ -1,13 +1,15 @@ -# translation of pl.po to Polish -# Piotr Drąg <piotrdrag@gmail.com>, 2011, 2013, 2014, 2015, 2016. +# Polish translation for systemd. +# Copyright © 2011-2016 the systemd authors. +# This file is distributed under the same license as the systemd package. +# Piotr Drąg <piotrdrag@gmail.com>, 2011, 2013-2016. # Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>, 2011. # msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-04-23 14:24+0200\n" -"PO-Revision-Date: 2016-04-23 14:25+0200\n" +"POT-Creation-Date: 2016-10-05 19:01+0200\n" +"PO-Revision-Date: 2016-10-05 19:02+0200\n" "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n" "Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n" "Language: pl\n" @@ -19,13 +21,13 @@ msgstr "" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1 msgid "Send passphrase back to system" -msgstr "Wysłanie hasła z powrotem do systemu" +msgstr "Wysłanie hasła z powrotem do systemu" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2 msgid "" "Authentication is required to send the entered passphrase back to the system." msgstr "" -"Wymagane jest uwierzytelnienie, aby wysłać podane hasło z powrotem do " +"Wymagane jest uwierzytelnienie, aby wysłać podane hasło z powrotem do " "systemu." #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3 @@ -51,7 +53,7 @@ msgstr "" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7 msgid "Set or unset system and service manager environment variables" msgstr "" -"Ustawienie lub usunięcie zmiennych środowiskowych menedżera systemu i usług" +"Ustawienie lub usunięcie zmiennych środowiskowych menedżera systemu i usług" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8 msgid "" @@ -59,7 +61,7 @@ msgid "" "environment variables." msgstr "" "Wymagane jest uwierzytelnienie, aby ustawić lub usunąć zmienne środowiskowe " -"menedżera systemu i usług." +"menedżera systemu i usług." #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9 msgid "Reload the systemd state" @@ -87,16 +89,16 @@ msgid "" "as well as the pretty host name." msgstr "" "Wymagane jest uwierzytelnienie, aby ustawić statycznie skonfigurowaną nazwę " -"lokalnego komputera, a także jego ładną nazwę." +"lokalnego komputera, a także jego ładną nazwę." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5 msgid "Set machine information" -msgstr "Ustawienie informacji o komputerze" +msgstr "Ustawienie informacji o komputerze" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6 msgid "Authentication is required to set local machine information." msgstr "" -"Wymagane jest uwierzytelnienie, aby ustawić informacje o lokalnym komputerze." +"Wymagane jest uwierzytelnienie, aby ustawić informacje o lokalnym komputerze." #: ../src/import/org.freedesktop.import1.policy.in.h:1 msgid "Import a VM or container image" @@ -410,14 +412,14 @@ msgstr "" #: ../src/login/org.freedesktop.login1.policy.in.h:51 msgid "Manage active sessions, users and seats" -msgstr "Zarządzanie aktywnymi sesjami, użytkownikami i stanowiskami" +msgstr "Zarządzanie aktywnymi sesjami, użytkownikami i stanowiskami" #: ../src/login/org.freedesktop.login1.policy.in.h:52 msgid "" "Authentication is required for managing active sessions, users and seats." msgstr "" "Wymagane jest uwierzytelnienie, aby zarządzać aktywnymi sesjami, " -"użytkownikami i stanowiskami." +"użytkownikami i stanowiskami." #: ../src/login/org.freedesktop.login1.policy.in.h:53 msgid "Lock or unlock active sessions" @@ -468,12 +470,12 @@ msgstr "" #: ../src/machine/org.freedesktop.machine1.policy.in.h:5 msgid "Acquire a shell in a local container" -msgstr "Uzyskanie powłoki w lokalnym kontenerze" +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." +"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" @@ -486,13 +488,13 @@ msgstr "" #: ../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" +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." +"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" @@ -506,18 +508,18 @@ msgstr "" #: ../src/machine/org.freedesktop.machine1.policy.in.h:13 msgid "Manage local virtual machines and containers" -msgstr "Zarządzanie lokalnymi maszynami wirtualnymi i kontenerami" +msgstr "Zarządzanie lokalnymi maszynami wirtualnymi i kontenerami" #: ../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." +"wirtualnymi i kontenerami." #: ../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" +msgstr "Zarządzanie lokalnymi obrazami maszyn wirtualnych i kontenerów" #: ../src/machine/org.freedesktop.machine1.policy.in.h:16 msgid "" @@ -525,7 +527,7 @@ msgid "" "images." msgstr "" "Wymagane jest uwierzytelnienie, aby zarządzać lokalnymi obrazami maszyn " -"wirtualnych i kontenerów." +"wirtualnych i kontenerów." #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1 msgid "Set system time" @@ -567,36 +569,36 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację " "czasu przez sieć." -#: ../src/core/dbus-unit.c:450 +#: ../src/core/dbus-unit.c:459 msgid "Authentication is required to start '$(unit)'." msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”." -#: ../src/core/dbus-unit.c:451 +#: ../src/core/dbus-unit.c:460 msgid "Authentication is required to stop '$(unit)'." msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”." -#: ../src/core/dbus-unit.c:452 +#: ../src/core/dbus-unit.c:461 msgid "Authentication is required to reload '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”." -#: ../src/core/dbus-unit.c:453 ../src/core/dbus-unit.c:454 +#: ../src/core/dbus-unit.c:462 ../src/core/dbus-unit.c:463 msgid "Authentication is required to restart '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”." -#: ../src/core/dbus-unit.c:560 +#: ../src/core/dbus-unit.c:570 msgid "Authentication is required to kill '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby wymusić wyłączenie jednostki „$(unit)”." -#: ../src/core/dbus-unit.c:590 +#: ../src/core/dbus-unit.c:601 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:622 +#: ../src/core/dbus-unit.c:634 msgid "Authentication is required to set properties on '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”." @@ -3,20 +3,22 @@ # This file is distributed under the same license as the systemd package. # Josef Andersson <josef.andersson@fripost.org>, 2015. # Sebastian Rasmussen <sebras@gmail.com>, 2015. +# Andreas Henriksson <andreas@fatal.se>, 2016. +# msgid "" msgstr "" "Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-11-22 16:37+0100\n" -"PO-Revision-Date: 2015-03-14 11:09+0100\n" -"Last-Translator: Sebastian Rasmussen <sebras@gmail.com>\n" +"PO-Revision-Date: 2016-09-29 11:58+0200\n" +"Last-Translator: Andreas Henriksson <andreas@fatal.se>\n" "Language-Team: Swedish\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Gtranslator 2.91.7\n" #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1 msgid "Send passphrase back to system" @@ -72,7 +74,7 @@ msgstr "Ange värdnamn" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 msgid "Authentication is required to set the local host name." -msgstr "Autentisering krävs för att ange lokalt värdnamn." +msgstr "Autentisering krävs för att ställa in lokalt värdnamn." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 msgid "Set static host name" @@ -83,16 +85,16 @@ msgid "" "Authentication is required to set the statically configured local host name, " "as well as the pretty host name." msgstr "" -"Autentisering krävs för att ange det statiskt konfigurerade lokala " +"Autentisering krävs för att ställa in det statiskt konfigurerade lokala " "värdnamnet såväl som det stiliga värdnamnet." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5 msgid "Set machine information" -msgstr "Ange datorinformation" +msgstr "Ställa in datorinformation" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6 msgid "Authentication is required to set local machine information." -msgstr "Autentisering krävs för att ange lokal datorinformation." +msgstr "Autentisering krävs för att ställa in lokal datorinformation." #: ../src/import/org.freedesktop.import1.policy.in.h:1 msgid "Import a VM or container image" @@ -124,7 +126,7 @@ msgstr "Ange systemlokal" #: ../src/locale/org.freedesktop.locale1.policy.in.h:2 msgid "Authentication is required to set the system locale." -msgstr "Autentisering krävs för att ange systemlokal." +msgstr "Autentisering krävs för att ställa in systemlokal." #: ../src/locale/org.freedesktop.locale1.policy.in.h:3 msgid "Set system keyboard settings" @@ -132,7 +134,7 @@ msgstr "Ange systeminställningar för tangentbord" #: ../src/locale/org.freedesktop.locale1.policy.in.h:4 msgid "Authentication is required to set the system keyboard settings." -msgstr "Autentisering krävs för att ange systeminställningar för tangentbord." +msgstr "Autentisering krävs för att ställa in systeminställningar för tangentbord." #: ../src/login/org.freedesktop.login1.policy.in.h:1 msgid "Allow applications to inhibit system shutdown" @@ -410,79 +412,76 @@ msgstr "Autentisering krävs för att låsa eller låsa upp aktiva sessioner." #: ../src/login/org.freedesktop.login1.policy.in.h:53 msgid "Allow indication to the firmware to boot to setup interface" msgstr "" +"Tillåt indikering till firmware att starta upp i inställningsgränssnitt" #: ../src/login/org.freedesktop.login1.policy.in.h:54 -#, fuzzy msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." -msgstr "Autentisering krävs för att ange lokalt värdnamn." +msgstr "" +"Autentisering krävs för att indikera till firmware att starta upp till " +"inställningsgränssnitt." #: ../src/login/org.freedesktop.login1.policy.in.h:55 msgid "Set a wall message" -msgstr "" +msgstr "Ange ett väggmeddelande" #: ../src/login/org.freedesktop.login1.policy.in.h:56 -#, fuzzy msgid "Authentication is required to set a wall message" -msgstr "Autentisering krävs för att ange lokalt värdnamn." +msgstr "Autentisering krävs för att ställa in ett väggmeddelande" #: ../src/machine/org.freedesktop.machine1.policy.in.h:1 msgid "Log into a local container" -msgstr "Logga till en lokal behållare" +msgstr "Logga in i en lokal behållare" #: ../src/machine/org.freedesktop.machine1.policy.in.h:2 msgid "Authentication is required to log into a local container." -msgstr "Autentisering krävs för att logga till en lokal behållare" +msgstr "Autentisering krävs för att logga in i en lokal behållare" #: ../src/machine/org.freedesktop.machine1.policy.in.h:3 -#, fuzzy msgid "Log into the local host" -msgstr "Logga till en lokal behållare" +msgstr "Logga in på en lokal värd" #: ../src/machine/org.freedesktop.machine1.policy.in.h:4 -#, fuzzy msgid "Authentication is required to log into the local host." -msgstr "Autentisering krävs för att logga till en lokal behållare" +msgstr "Autentisering krävs för att logga in på den lokala värden" #: ../src/machine/org.freedesktop.machine1.policy.in.h:5 -#, fuzzy msgid "Acquire a shell in a local container" -msgstr "Logga till en lokal behållare" +msgstr "Förvärva en kommandotolk i en lokal behållare" #: ../src/machine/org.freedesktop.machine1.policy.in.h:6 -#, fuzzy msgid "Authentication is required to acquire a shell in a local container." -msgstr "Autentisering krävs för att logga till en lokal behållare" +msgstr "" +"Autentisering krävs för att förvärva en kommandotolk i en lokal behållare." #: ../src/machine/org.freedesktop.machine1.policy.in.h:7 msgid "Acquire a shell on the local host" -msgstr "" +msgstr "Förvärva en kommandotolk på den lokala värden" #: ../src/machine/org.freedesktop.machine1.policy.in.h:8 -#, fuzzy msgid "Authentication is required to acquire a shell on the local host." -msgstr "Autentisering krävs för att ange lokalt värdnamn." +msgstr "" +"Autentisering krävs för att förvärva en kommandotolk på den lokala värden." #: ../src/machine/org.freedesktop.machine1.policy.in.h:9 -#, fuzzy msgid "Acquire a pseudo TTY in a local container" -msgstr "Logga till en lokal behållare" +msgstr "Förvärva en pseudo TTY i en lokal behållare" #: ../src/machine/org.freedesktop.machine1.policy.in.h:10 -#, fuzzy msgid "" "Authentication is required to acquire a pseudo TTY in a local container." -msgstr "Autentisering krävs för att logga till en lokal behållare" +msgstr "" +"Autentisering krävs för att förvärva en pseudo TTY i en lokal behållare" #: ../src/machine/org.freedesktop.machine1.policy.in.h:11 msgid "Acquire a pseudo TTY on the local host" -msgstr "" +msgstr "Förvärva en pseudo TTY på den lokala värden" #: ../src/machine/org.freedesktop.machine1.policy.in.h:12 -#, fuzzy msgid "Authentication is required to acquire a pseudo TTY on the local host." -msgstr "Autentisering krävs för att ange lokalt värdnamn." +msgstr "" +"Autentisering krävs för att förvärva en pseudo TTY på den lokala värden." #: ../src/machine/org.freedesktop.machine1.policy.in.h:13 msgid "Manage local virtual machines and containers" @@ -512,7 +511,7 @@ msgstr "Ange systemtid" #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2 msgid "Authentication is required to set the system time." -msgstr "Autentisering krävs för ange systemtiden." +msgstr "Autentisering krävs för ställa in systemtiden." #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3 msgid "Set system timezone" @@ -520,7 +519,7 @@ msgstr "Ange systemets tidszon" #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4 msgid "Authentication is required to set the system timezone." -msgstr "Autentisering krävs för att ange systemets tidszon." +msgstr "Autentisering krävs för att ställa in systemets tidszon." #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5 msgid "Set RTC to local timezone or UTC" @@ -549,39 +548,34 @@ msgstr "" "nätverkstid ska vara aktiverat." #: ../src/core/dbus-unit.c:428 -#, fuzzy msgid "Authentication is required to start '$(unit)'." -msgstr "Autentisering krävs för ange systemtiden." +msgstr "Autentisering krävs för att starta \"$(unit)\"." #: ../src/core/dbus-unit.c:429 -#, fuzzy msgid "Authentication is required to stop '$(unit)'." -msgstr "Autentisering krävs för ange systemtiden." +msgstr "Autentisering krävs för att stoppa \"$(unit)\"." #: ../src/core/dbus-unit.c:430 -#, fuzzy msgid "Authentication is required to reload '$(unit)'." -msgstr "Autentisering krävs för att läsa om tillståndet för systemd." +msgstr "Autentisering krävs för att läsa om tillståndet för \"$(unit)\"." #: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432 -#, fuzzy msgid "Authentication is required to restart '$(unit)'." -msgstr "Autentisering krävs för ange systemtiden." +msgstr "Autentisering krävs för att starta om \"$(unit)\"." #: ../src/core/dbus-unit.c:535 -#, fuzzy msgid "Authentication is required to kill '$(unit)'." -msgstr "Autentisering krävs för att logga till en lokal behållare" +msgstr "Autentisering krävs för att döda \"$(unit)\"." #: ../src/core/dbus-unit.c:565 -#, fuzzy msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." -msgstr "Autentisering krävs för att ange lokalt värdnamn." +msgstr "" +"Autentisering krävs för att återställa det \"fallerade\" tillståndet för " +"\"$(unit)\"." #: ../src/core/dbus-unit.c:597 -#, fuzzy msgid "Authentication is required to set properties on '$(unit)'." -msgstr "Autentisering krävs för ange systemtiden." +msgstr "Autentisering krävs för att ställa in egenskaper på \"$(unit)\"." #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress" #~ msgstr "Tryck Ctrl+C för att avbryta alla pågående filsystemskontroller." diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run index 022331e6a9..4116ba7eca 100644 --- a/shell-completion/bash/systemd-run +++ b/shell-completion/bash/systemd-run @@ -36,7 +36,8 @@ _systemd_run() { -r --remain-after-exit --send-sighup -H --host -M --machine --service-type --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' + --uid --gid --nice --setenv -p --property --no-ask-password + --wait' local mode=--system local i diff --git a/shell-completion/zsh/_systemd-run b/shell-completion/zsh/_systemd-run index 6362b97766..da9f73a6d0 100644 --- a/shell-completion/zsh/_systemd-run +++ b/shell-completion/zsh/_systemd-run @@ -57,4 +57,5 @@ _arguments \ '--on-unit-inactive=[Run after SEC seconds from the last deactivation]:SEC' \ '--on-calendar=[Realtime timer]:SPEC' \ '--timer-property=[Set timer unit property]:NAME=VALUE' \ + '--wait=[Wait until service stopped again]' \ '*::command:_command' diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 5fd3ee49eb..0ce0276d92 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -71,6 +71,7 @@ static int prepare_filename(const char *filename, char **ret) { } static int generate_path(char **var, char **filenames) { + const char *old; char **filename; _cleanup_strv_free_ char **ans = NULL; @@ -90,9 +91,19 @@ static int generate_path(char **var, char **filenames) { assert_se(strv_uniq(ans)); - r = strv_extend(&ans, ""); - if (r < 0) - return r; + /* First, prepend our directories. Second, if some path was specified, use that, and + * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c. + * Treat explicit empty path to mean that nothing should be appended. + */ + old = getenv("SYSTEMD_UNIT_PATH"); + if (!streq_ptr(old, "")) { + if (!old) + old = ":"; + + r = strv_extend(&ans, old); + if (r < 0) + return r; + } *var = strv_join(ans, ":"); if (!*var) diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c index 45be135a23..7c59f60d5f 100644 --- a/src/backlight/backlight.c +++ b/src/backlight/backlight.c @@ -167,7 +167,7 @@ static bool validate_device(struct udev *udev, struct udev_device *device) { continue; v = udev_device_get_sysattr_value(other, "type"); - if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware")) + if (!STRPTR_IN_SET(v, "platform", "firmware")) continue; /* OK, so there's another backlight device, and it's a diff --git a/src/basic/architecture.c b/src/basic/architecture.c index b1c8e91f50..b74dc0db78 100644 --- a/src/basic/architecture.c +++ b/src/basic/architecture.c @@ -123,6 +123,14 @@ int uname_architecture(void) { { "crisv32", ARCHITECTURE_CRIS }, #elif defined(__nios2__) { "nios2", ARCHITECTURE_NIOS2 }, +#elif defined(__riscv__) + { "riscv32", ARCHITECTURE_RISCV32 }, + { "riscv64", ARCHITECTURE_RISCV64 }, +# if __SIZEOF_POINTER__ == 4 + { "riscv", ARCHITECTURE_RISCV32 }, +# elif __SIZEOF_POINTER__ == 8 + { "riscv", ARCHITECTURE_RISCV64 }, +# endif #else #error "Please register your architecture here!" #endif @@ -174,6 +182,8 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = { [ARCHITECTURE_TILEGX] = "tilegx", [ARCHITECTURE_CRIS] = "cris", [ARCHITECTURE_NIOS2] = "nios2", + [ARCHITECTURE_RISCV32] = "riscv32", + [ARCHITECTURE_RISCV64] = "riscv64", }; DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/basic/architecture.h b/src/basic/architecture.h index b3e4d85906..5a77c31932 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -58,6 +58,8 @@ enum { ARCHITECTURE_TILEGX, ARCHITECTURE_CRIS, ARCHITECTURE_NIOS2, + ARCHITECTURE_RISCV32, + ARCHITECTURE_RISCV64, _ARCHITECTURE_MAX, _ARCHITECTURE_INVALID = -1 }; @@ -191,6 +193,16 @@ int uname_architecture(void); #elif defined(__nios2__) # define native_architecture() ARCHITECTURE_NIOS2 # define LIB_ARCH_TUPLE "nios2-linux-gnu" +#elif defined(__riscv__) +# if __SIZEOF_POINTER__ == 4 +# define native_architecture() ARCHITECTURE_RISCV32 +# define LIB_ARCH_TUPLE "riscv32-linux-gnu" +# elif __SIZEOF_POINTER__ == 8 +# define native_architecture() ARCHITECTURE_RISCV64 +# define LIB_ARCH_TUPLE "riscv64-linux-gnu" +# else +# error "Unrecognized riscv architecture variant" +# endif #else # error "Please register your architecture here!" #endif diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c index 5741fecdd6..d1c9695973 100644 --- a/src/basic/audit-util.c +++ b/src/basic/audit-util.c @@ -92,8 +92,11 @@ bool use_audit(void) { int fd; fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); - if (fd < 0) - cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT; + if (fd < 0) { + cached_use = !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT, EPERM); + if (errno == EPERM) + log_debug_errno(errno, "Audit access prohibited, won't talk to audit"); + } else { cached_use = true; safe_close(fd); diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index d4c5bd6937..c3de20a0e8 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -31,6 +31,7 @@ #include "log.h" #include "macro.h" #include "parse-util.h" +#include "user-util.h" #include "util.h" int have_effective_cap(int value) { @@ -295,8 +296,9 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { if (setresgid(gid, gid, gid) < 0) return log_error_errno(errno, "Failed to change group ID: %m"); - if (setgroups(0, NULL) < 0) - return log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); + r = maybe_setgroups(0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); /* Ensure we keep the permitted caps across the setresuid() */ if (prctl(PR_SET_KEEPCAPS, 1) < 0) diff --git a/src/basic/def.h b/src/basic/def.h index 1a7a0f4928..2266eff650 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -79,7 +79,7 @@ #endif /* Return a nulstr for a standard cascade of configuration paths, - * suitable to pass to conf_files_list_nulstr() or config_parse_many() + * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr() * to implement drop-in directories for extending configuration * files. */ #define CONF_PATHS_NULSTR(n) \ diff --git a/src/basic/escape.c b/src/basic/escape.c index 01daf11ce7..4a1ec4505e 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -333,7 +333,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi assert(remaining > 0); if (*f != '\\') { - /* A literal literal, copy verbatim */ + /* A literal, copy verbatim */ *(t++) = *f; continue; } diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c index d488cfc59f..59557f8afe 100644 --- a/src/basic/exit-status.c +++ b/src/basic/exit-status.c @@ -24,12 +24,12 @@ #include "macro.h" #include "set.h" -const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { +const char* exit_status_to_string(int status, ExitStatusLevel level) { /* We cast to int here, so that -Wenum doesn't complain that * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */ - switch ((int) status) { + switch (status) { case EXIT_SUCCESS: return "SUCCESS"; @@ -39,7 +39,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { } if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) { - switch ((int) status) { + switch (status) { case EXIT_CHDIR: return "CHDIR"; @@ -140,19 +140,19 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { case EXIT_RUNTIME_DIRECTORY: return "RUNTIME_DIRECTORY"; - case EXIT_CHOWN: - return "CHOWN"; - case EXIT_MAKE_STARTER: return "MAKE_STARTER"; + case EXIT_CHOWN: + return "CHOWN"; + case EXIT_SMACK_PROCESS_LABEL: return "SMACK_PROCESS_LABEL"; } } if (level == EXIT_STATUS_LSB) { - switch ((int) status) { + switch (status) { case EXIT_INVALIDARGUMENT: return "INVALIDARGUMENT"; @@ -177,34 +177,23 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { return NULL; } - -bool is_clean_exit(int code, int status, ExitStatusSet *success_status) { +bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) { if (code == CLD_EXITED) return status == 0 || (success_status && set_contains(success_status->status, INT_TO_PTR(status))); - /* If a daemon does not implement handlers for some of the - * signals that's not considered an unclean shutdown */ + /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */ if (code == CLD_KILLED) - return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) || + return + (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) || (success_status && set_contains(success_status->signal, INT_TO_PTR(status))); return false; } -bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) { - - if (is_clean_exit(code, status, success_status)) - return true; - - return - code == CLD_EXITED && - IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED); -} - void exit_status_set_free(ExitStatusSet *x) { assert(x); diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h index 2309f68815..0cfdfd7891 100644 --- a/src/basic/exit-status.h +++ b/src/basic/exit-status.h @@ -31,7 +31,7 @@ * https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */ -typedef enum ExitStatus { +enum { /* EXIT_SUCCESS defined by libc */ /* EXIT_FAILURE defined by libc */ EXIT_INVALIDARGUMENT = 2, @@ -82,7 +82,7 @@ typedef enum ExitStatus { EXIT_MAKE_STARTER, EXIT_CHOWN, EXIT_SMACK_PROCESS_LABEL, -} ExitStatus; +}; typedef enum ExitStatusLevel { EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */ @@ -96,10 +96,14 @@ typedef struct ExitStatusSet { Set *signal; } ExitStatusSet; -const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_; +const char* exit_status_to_string(int status, ExitStatusLevel level) _const_; -bool is_clean_exit(int code, int status, ExitStatusSet *success_status); -bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status); +typedef enum ExitClean { + EXIT_CLEAN_DAEMON, + EXIT_CLEAN_COMMAND, +} ExitClean; + +bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status); void exit_status_set_free(ExitStatusSet *x); bool exit_status_set_is_empty(ExitStatusSet *x); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index a5920e7d36..1cfb7a98f5 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1043,7 +1043,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { if (r < 0) return r; - fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); + fd = mkostemp_safe(t); if (fd < 0) { free(t); return -errno; @@ -1076,7 +1076,7 @@ int fflush_and_check(FILE *f) { } /* This is much like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern, int flags) { +int mkostemp_safe(char *pattern) { _cleanup_umask_ mode_t u = 0; int fd; @@ -1084,7 +1084,7 @@ int mkostemp_safe(char *pattern, int flags) { u = umask(077); - fd = mkostemp(pattern, flags); + fd = mkostemp(pattern, O_CLOEXEC); if (fd < 0) return -errno; @@ -1289,7 +1289,7 @@ int open_tmpfile_unlinkable(const char *directory, int flags) { /* Fall back to unguessable name + unlinking */ p = strjoina(directory, "/systemd-tmp-XXXXXX"); - fd = mkostemp_safe(p, flags); + fd = mkostemp_safe(p); if (fd < 0) return fd; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 9ac497d9eb..b58c83e64a 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -71,7 +71,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root int fflush_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); -int mkostemp_safe(char *pattern, int flags); +int mkostemp_safe(char *pattern); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); int tempfn_random(const char *p, const char *extra, char **ret); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index ce87257bc1..86d9ad7e36 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -597,3 +597,190 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } + +int chase_symlinks(const char *path, const char *_root, char **ret) { + _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; + _cleanup_close_ int fd = -1; + unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */ + char *todo; + int r; + + assert(path); + + /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following + * symlinks relative to a root directory, instead of the root of the host. + * + * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly + * this means the path parameter passed in is not prefixed by it. + * + * Algorithmically this operates on two path buffers: "done" are the components of the path we already + * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to + * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning + * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no + * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races + * at a minimum. */ + + r = path_make_absolute_cwd(path, &buffer); + if (r < 0) + return r; + + if (_root) { + r = path_make_absolute_cwd(_root, &root); + if (r < 0) + return r; + } + + fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) + return -errno; + + todo = buffer; + for (;;) { + _cleanup_free_ char *first = NULL; + _cleanup_close_ int child = -1; + struct stat st; + size_t n, m; + + /* Determine length of first component in the path */ + n = strspn(todo, "/"); /* The slashes */ + m = n + strcspn(todo + n, "/"); /* The entire length of the component */ + + /* Extract the first component. */ + first = strndup(todo, m); + if (!first) + return -ENOMEM; + + todo += m; + + /* Just a single slash? Then we reached the end. */ + if (isempty(first) || path_equal(first, "/")) + break; + + /* Just a dot? Then let's eat this up. */ + if (path_equal(first, "/.")) + continue; + + /* Two dots? Then chop off the last bit of what we already found out. */ + if (path_equal(first, "/..")) { + _cleanup_free_ char *parent = NULL; + int fd_parent = -1; + + if (isempty(done) || path_equal(done, "/")) + return -EINVAL; + + parent = dirname_malloc(done); + if (!parent) + return -ENOMEM; + + /* Don't allow this to leave the root dir */ + if (root && + path_startswith(done, root) && + !path_startswith(parent, root)) + return -EINVAL; + + free(done); + done = parent; + parent = NULL; + + fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd_parent < 0) + return -errno; + + safe_close(fd); + fd = fd_parent; + + continue; + } + + /* Otherwise let's see what this is. */ + child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (child < 0) + return -errno; + + if (fstat(child, &st) < 0) + return -errno; + + if (S_ISLNK(st.st_mode)) { + _cleanup_free_ char *destination = NULL; + + /* This is a symlink, in this case read the destination. But let's make sure we don't follow + * symlinks without bounds. */ + if (--max_follow <= 0) + return -ELOOP; + + r = readlinkat_malloc(fd, first + n, &destination); + if (r < 0) + return r; + if (isempty(destination)) + return -EINVAL; + + if (path_is_absolute(destination)) { + + /* An absolute destination. Start the loop from the beginning, but use the root + * directory as base. */ + + safe_close(fd); + fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) + return -errno; + + free(buffer); + buffer = destination; + destination = NULL; + + todo = buffer; + free(done); + + /* Note that we do not revalidate the root, we take it as is. */ + if (isempty(root)) + done = NULL; + else { + done = strdup(root); + if (!done) + return -ENOMEM; + } + + } else { + char *joined; + + /* A relative destination. If so, this is what we'll prefix what's left to do with what + * we just read, and start the loop again, but remain in the current directory. */ + + joined = strjoin("/", destination, todo, NULL); + if (!joined) + return -ENOMEM; + + free(buffer); + todo = buffer = joined; + } + + continue; + } + + /* If this is not a symlink, then let's just add the name we read to what we already verified. */ + if (!done) { + done = first; + first = NULL; + } else { + if (!strextend(&done, first, NULL)) + return -ENOMEM; + } + + /* And iterate again, but go one directory further down. */ + safe_close(fd); + fd = child; + child = -1; + } + + if (!done) { + /* Special case, turn the empty string into "/", to indicate the root directory. */ + done = strdup("/"); + if (!done) + return -ENOMEM; + } + + *ret = done; + done = NULL; + + return 0; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 2c3b9a1c74..31df47cf1e 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -77,3 +77,5 @@ union inotify_event_buffer { }; int inotify_add_watch_fd(int fd, int what, uint32_t mask); + +int chase_symlinks(const char *path, const char *_root, char **ret); diff --git a/src/basic/list.h b/src/basic/list.h index 5962aa4211..c3771a177f 100644 --- a/src/basic/list.h +++ b/src/basic/list.h @@ -142,6 +142,8 @@ } else { \ if ((_b->name##_prev = _a->name##_prev)) \ _b->name##_prev->name##_next = _b; \ + else \ + *_head = _b; \ _b->name##_next = _a; \ _a->name##_prev = _b; \ } \ diff --git a/src/basic/missing.h b/src/basic/missing.h index 13ff51cd35..4a78269e33 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -1052,6 +1052,10 @@ typedef int32_t key_serial_t; #define ETHERTYPE_LLDP 0x88cc #endif +#ifndef IFA_F_MCAUTOJOIN +#define IFA_F_MCAUTOJOIN 0x400 +#endif + #endif #include "missing_syscall.h" diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index bfa04394fe..b9affb4e70 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -36,6 +36,7 @@ #include "set.h" #include "stdio-util.h" #include "string-util.h" +#include "strv.h" static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) { char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; @@ -287,10 +288,12 @@ int umount_recursive(const char *prefix, int flags) { continue; if (umount2(p, flags) < 0) { - r = -errno; + r = log_debug_errno(errno, "Failed to umount %s: %m", p); continue; } + log_debug("Successfully unmounted %s", p); + again = true; n++; @@ -311,24 +314,21 @@ static int get_mount_flags(const char *path, unsigned long *flags) { return 0; } -int bind_remount_recursive(const char *prefix, bool ro) { +int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; int r; - /* Recursively remount a directory (and all its submounts) - * read-only or read-write. If the directory is already - * mounted, we reuse the mount and simply mark it - * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write - * operation). If it isn't we first make it one. Afterwards we - * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all - * submounts we can access, too. When mounts are stacked on - * the same mount point we only care for each individual - * "top-level" mount on each point, as we cannot - * influence/access the underlying mounts anyway. We do not - * have any effect on future submounts that might get - * propagated, they migt be writable. This includes future - * submounts that have been triggered via autofs. */ + /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already + * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write + * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to + * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each + * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We + * do not have any effect on future submounts that might get propagated, they migt be writable. This includes + * future submounts that have been triggered via autofs. + * + * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the + * remount operation. Note that we'll ignore the blacklist for the top-level path. */ cleaned = strdup(prefix); if (!cleaned) @@ -385,6 +385,33 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (r < 0) return r; + if (!path_startswith(p, cleaned)) + continue; + + /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall + * operate on. */ + if (!path_equal(cleaned, p)) { + bool blacklisted = false; + char **i; + + STRV_FOREACH(i, blacklist) { + + if (path_equal(*i, cleaned)) + continue; + + if (!path_startswith(*i, cleaned)) + continue; + + if (path_startswith(p, *i)) { + blacklisted = true; + log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned); + break; + } + } + if (blacklisted) + continue; + } + /* Let's ignore autofs mounts. If they aren't * triggered yet, we want to avoid triggering * them, as we don't make any guarantees for @@ -396,12 +423,9 @@ int bind_remount_recursive(const char *prefix, bool ro) { continue; } - if (path_startswith(p, cleaned) && - !set_contains(done, p)) { - + if (!set_contains(done, p)) { r = set_consume(todo, p); p = NULL; - if (r == -EEXIST) continue; if (r < 0) @@ -418,8 +442,7 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (!set_contains(done, cleaned) && !set_contains(todo, cleaned)) { - /* The prefix directory itself is not yet a - * mount, make it one. */ + /* The prefix directory itself is not yet a mount, make it one. */ if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) return -errno; @@ -430,6 +453,8 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) return -errno; + log_debug("Made top-level directory %s a mount point.", prefix); + x = strdup(cleaned); if (!x) return -ENOMEM; @@ -447,8 +472,7 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (r < 0) return r; - /* Deal with mount points that are obstructed by a - * later mount */ + /* Deal with mount points that are obstructed by a later mount */ r = path_is_mount_point(x, 0); if (r == -ENOENT || r == 0) continue; @@ -463,6 +487,7 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) return -errno; + log_debug("Remounted %s read-only.", x); } } } diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h index f46989ebb3..74730de663 100644 --- a/src/basic/mount-util.h +++ b/src/basic/mount-util.h @@ -35,7 +35,7 @@ int path_is_mount_point(const char *path, int flags); int repeat_unmount(const char *path, int flags); int umount_recursive(const char *target, int flags); -int bind_remount_recursive(const char *prefix, bool ro); +int bind_remount_recursive(const char *prefix, bool ro, char **blacklist); int mount_move_root(const char *path); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index b2fa81a294..c32e961af4 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -34,9 +34,11 @@ #include "alloc-util.h" #include "extract-word.h" #include "fs-util.h" +#include "glob-util.h" #include "log.h" #include "macro.h" #include "missing.h" +#include "parse-util.h" #include "path-util.h" #include "stat-util.h" #include "string-util.h" @@ -814,3 +816,69 @@ bool is_device_path(const char *path) { path_startswith(path, "/dev/") || path_startswith(path, "/sys/"); } + +int systemd_installation_has_version(const char *root, unsigned minimal_version) { + const char *pattern; + int r; + + /* Try to guess if systemd installation is later than the specified version. This + * is hacky and likely to yield false negatives, particularly if the installation + * is non-standard. False positives should be relatively rare. + */ + + NULSTR_FOREACH(pattern, + /* /lib works for systems without usr-merge, and for systems with a sane + * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary + * for Gentoo which does a merge without making /lib a symlink. + */ + "lib/systemd/libsystemd-shared-*.so\0" + "usr/lib/systemd/libsystemd-shared-*.so\0") { + + _cleanup_strv_free_ char **names = NULL; + _cleanup_free_ char *path = NULL; + char *c, **name; + + path = prefix_root(root, pattern); + if (!path) + return -ENOMEM; + + r = glob_extend(&names, path); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + + assert_se((c = endswith(path, "*.so"))); + *c = '\0'; /* truncate the glob part */ + + STRV_FOREACH(name, names) { + /* This is most likely to run only once, hence let's not optimize anything. */ + char *t, *t2; + unsigned version; + + t = startswith(*name, path); + if (!t) + continue; + + t2 = endswith(t, ".so"); + if (!t2) + continue; + + t2[0] = '\0'; /* truncate the suffix */ + + r = safe_atou(t, &version); + if (r < 0) { + log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name); + continue; + } + + log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).", + *name, version, + version >= minimal_version ? "OK" : "too old"); + if (version >= minimal_version) + return true; + } + } + + return false; +} diff --git a/src/basic/path-util.h b/src/basic/path-util.h index a27c13fcc3..78472f0961 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -125,3 +125,5 @@ char *file_in_same_dir(const char *path, const char *filename); bool hidden_or_backup_file(const char *filename) _pure_; bool is_device_path(const char *path); + +int systemd_installation_has_version(const char *root, unsigned minimal_version); diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 6093e47172..1662c04705 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -441,7 +441,7 @@ const char* socket_address_get_path(const SocketAddress *a) { } bool socket_ipv6_is_supported(void) { - if (access("/proc/net/sockstat6", F_OK) != 0) + if (access("/proc/net/if_inet6", F_OK) != 0) return false; return true; @@ -1060,3 +1060,20 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng return NULL; } + +int socket_ioctl_fd(void) { + int fd; + + /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for + * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not + * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more + * generic AF_NETLINK. */ + + fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC); + if (fd < 0) + return -errno; + + return fd; +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 2536b085f9..2ef572badb 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -154,3 +154,5 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ }) + +int socket_ioctl_fd(void); diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 5d4510e1b3..dc7de5dab8 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -443,7 +443,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le if (old_length <= 3 || old_length <= new_length) return strndup(s, old_length); - r = new0(char, new_length+1); + r = new0(char, new_length+3); if (!r) return NULL; @@ -453,12 +453,12 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le x = new_length - 3; memcpy(r, s, x); - r[x] = '.'; - r[x+1] = '.'; - r[x+2] = '.'; + r[x] = 0xe2; /* tri-dot ellipsis: … */ + r[x+1] = 0x80; + r[x+2] = 0xa6; memcpy(r + x + 3, - s + old_length - (new_length - x - 3), - new_length - x - 3); + s + old_length - (new_length - x - 1), + new_length - x - 1); return r; } diff --git a/src/basic/strv.h b/src/basic/strv.h index 683ce83a2a..fec2597db0 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -141,6 +141,11 @@ void strv_print(char **l); }) #define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) +#define STRPTR_IN_SET(x, ...) \ + ({ \ + const char* _x = (x); \ + _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ + }) #define FOREACH_STRING(x, ...) \ for (char **_l = ({ \ diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 0ef1f6393e..fedff1362c 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -40,8 +40,6 @@ #include "strv.h" #include "time-util.h" -static nsec_t timespec_load_nsec(const struct timespec *ts); - static clockid_t map_clock_id(clockid_t c) { /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will @@ -198,7 +196,7 @@ usec_t timespec_load(const struct timespec *ts) { (usec_t) ts->tv_nsec / NSEC_PER_USEC; } -static nsec_t timespec_load_nsec(const struct timespec *ts) { +nsec_t timespec_load_nsec(const struct timespec *ts) { assert(ts); if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 99be5ce6ee..558b0b5b7f 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -111,6 +111,7 @@ static inline bool triple_timestamp_is_set(triple_timestamp *ts) { usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); usec_t timespec_load(const struct timespec *ts) _pure_; +nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; struct timespec *timespec_store(struct timespec *ts, usec_t u); usec_t timeval_load(const struct timeval *tv) _pure_; diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 122d9a0c7c..de6c93056e 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -31,14 +31,16 @@ #include <unistd.h> #include <utmp.h> -#include "missing.h" #include "alloc-util.h" #include "fd-util.h" +#include "fileio.h" #include "formats-util.h" #include "macro.h" +#include "missing.h" #include "parse-util.h" #include "path-util.h" #include "string-util.h" +#include "strv.h" #include "user-util.h" #include "utf8.h" @@ -175,6 +177,35 @@ int get_user_creds( return 0; } +int get_user_creds_clean( + const char **username, + uid_t *uid, gid_t *gid, + const char **home, + const char **shell) { + + int r; + + /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */ + + r = get_user_creds(username, uid, gid, home, shell); + if (r < 0) + return r; + + if (shell && + (isempty(*shell) || PATH_IN_SET(*shell, + "/bin/nologin", + "/sbin/nologin", + "/usr/bin/nologin", + "/usr/sbin/nologin"))) + *shell = NULL; + + if (home && + (isempty(*home) || path_equal(*home, "/"))) + *home = NULL; + + return 0; +} + int get_group_creds(const char **groupname, gid_t *gid) { struct group *g; gid_t id; @@ -429,9 +460,11 @@ int get_shell(char **_s) { } int reset_uid_gid(void) { + int r; - if (setgroups(0, NULL) < 0) - return -errno; + r = maybe_setgroups(0, NULL); + if (r < 0) + return r; if (setresgid(0, 0, 0) < 0) return -errno; @@ -572,3 +605,32 @@ bool valid_home(const char *p) { return true; } + +int maybe_setgroups(size_t size, const gid_t *list) { + int r; + + /* Check if setgroups is allowed before we try to drop all the auxiliary groups */ + if (size == 0) { /* Dropping all aux groups? */ + _cleanup_free_ char *setgroups_content = NULL; + bool can_setgroups; + + r = read_one_line_file("/proc/self/setgroups", &setgroups_content); + if (r == -ENOENT) + /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */ + can_setgroups = true; + else if (r < 0) + return r; + else + can_setgroups = streq(setgroups_content, "allow"); + + if (!can_setgroups) { + log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'"); + return 0; + } + } + + if (setgroups(size, list) < 0) + return -errno; + + return 0; +} diff --git a/src/basic/user-util.h b/src/basic/user-util.h index f569363811..dfea561bde 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -40,6 +40,7 @@ char* getlogname_malloc(void); char* getusername_malloc(void); int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); +int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); int get_group_creds(const char **groupname, gid_t *gid); char* uid_to_name(uid_t uid); @@ -85,3 +86,5 @@ bool valid_user_group_name(const char *u); bool valid_user_group_name_or_id(const char *u); bool valid_gecos(const char *d); bool valid_home(const char *p); + +int maybe_setgroups(size_t size, const gid_t *list); diff --git a/src/basic/util.c b/src/basic/util.c index 9d66d28eb7..ec7939dc83 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -467,7 +467,7 @@ bool in_initrd(void) { * 2. the root file system must be a memory file system * * The second check is extra paranoia, since misdetecting an - * initrd can have bad bad consequences due the initrd + * initrd can have bad consequences due the initrd * emptying when transititioning to the main systemd. */ diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 7c016387c1..4ac11a9bb0 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -209,12 +209,35 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p return EFI_SUCCESS; } +/* + * According to TCG EFI Protocol Specification for TPM 2.0 family, + * all events generated after the invocation of EFI_TCG2_GET_EVENT_LOG + * shall be stored in an instance of an EFI_CONFIGURATION_TABLE aka + * EFI TCG 2.0 final events table. Hence, it is necessary to trigger the + * internal switch through calling get_event_log() in order to allow + * to retrieve the logs from OS runtime. + */ +static EFI_STATUS trigger_tcg2_final_events_table(const EFI_TCG2 *tcg) +{ + return uefi_call_wrapper(tcg->GetEventLog, 5, tcg, + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, NULL, + NULL, NULL); +} static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINT64 buffer_size, const CHAR16 *description) { EFI_STATUS status; EFI_TCG2_EVENT *tcg_event; UINTN desc_len; + static BOOLEAN triggered = FALSE; + + if (triggered == FALSE) { + status = trigger_tcg2_final_events_table(tcg); + if (EFI_ERROR(status)) + return status; + + triggered = TRUE; + } desc_len = StrLen(description) * sizeof(CHAR16); diff --git a/src/core/automount.c b/src/core/automount.c index 00295cf769..bdc0e06965 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -271,6 +271,11 @@ static int automount_coldplug(Unit *u) { return r; (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); + if (a->deserialized_state == AUTOMOUNT_RUNNING) { + r = automount_start_expire(a); + if (r < 0) + log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); + } } automount_set_state(a, a->deserialized_state); diff --git a/src/core/busname.c b/src/core/busname.c index 7952cd31aa..a69e3831f6 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -868,7 +868,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { n->control_pid = 0; - if (is_clean_exit(code, status, NULL)) + if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) f = BUSNAME_SUCCESS; else if (code == CLD_EXITED) f = BUSNAME_FAILURE_EXIT_CODE; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 7e33a2d201..eec4500c8c 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -707,6 +707,8 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectKernelTunables", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_tunables), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1072,7 +1074,8 @@ int bus_exec_context_set_transient_property( "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", - "RestrictRealtime", "DynamicUser", "RemoveIPC")) { + "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", + "ProtectControlGroups")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -1106,6 +1109,10 @@ int bus_exec_context_set_transient_property( c->dynamic_user = b; else if (streq(name, "RemoveIPC")) c->remove_ipc = b; + else if (streq(name, "ProtectKernelTunables")) + c->protect_kernel_tunables = b; + else if (streq(name, "ProtectControlGroups")) + c->protect_control_groups = b; unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); } diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 1b86bdde43..5020dfba4b 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -1167,7 +1167,7 @@ void bus_unit_send_removed_signal(Unit *u) { int r; assert(u); - if (!u->sent_dbus_new_signal) + if (!u->sent_dbus_new_signal || u->in_dbus_queue) bus_unit_send_change_signal(u); if (!u->id) diff --git a/src/core/dbus.c b/src/core/dbus.c index 1e41a42aa6..070974fe66 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -964,10 +964,6 @@ static int bus_init_private(Manager *m) { if (m->private_listen_fd >= 0) return 0; - /* We don't need the private socket if we have kdbus */ - if (m->kdbus_fd >= 0) - return 0; - if (MANAGER_IS_SYSTEM(m)) { /* We want the private bus only when running as init */ diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c index 310aaa94e1..1043da3eb7 100644 --- a/src/core/dynamic-user.c +++ b/src/core/dynamic-user.c @@ -233,7 +233,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) { if (st.st_nlink > 0) break; - /* Oh, bummer, we got got the lock, but the file was unlinked between the time we opened it and + /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and * got the lock. Close it, and try again. */ lock_fd = safe_close(lock_fd); } diff --git a/src/core/execute.c b/src/core/execute.c index 2026137721..d5c4e60796 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -781,9 +781,10 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ k++; } - if (setgroups(k, gids) < 0) { + r = maybe_setgroups(k, gids); + if (r < 0) { free(gids); - return -errno; + return r; } free(gids); @@ -837,14 +838,19 @@ static int null_conv( return PAM_CONV_ERR; } +#endif + static int setup_pam( const char *name, const char *user, uid_t uid, + gid_t gid, const char *tty, char ***env, int fds[], unsigned n_fds) { +#ifdef HAVE_PAM + static const struct pam_conv conv = { .conv = null_conv, .appdata_ptr = NULL @@ -944,8 +950,14 @@ static int setup_pam( * and this will make PR_SET_PDEATHSIG work in most cases. * If this fails, ignore the error - but expect sd-pam threads * to fail to exit normally */ + + r = maybe_setgroups(0, NULL); + if (r < 0) + log_warning_errno(r, "Failed to setgroups() in sd-pam: %m"); + if (setresgid(gid, gid, gid) < 0) + log_warning_errno(errno, "Failed to setresgid() in sd-pam: %m"); if (setresuid(uid, uid, uid) < 0) - log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m"); + log_warning_errno(errno, "Failed to setresuid() in sd-pam: %m"); (void) ignore_signals(SIGPIPE, -1); @@ -1038,8 +1050,10 @@ fail: closelog(); return r; -} +#else + return 0; #endif +} static void rename_process_from_path(const char *path) { char process_name[11]; @@ -1273,6 +1287,10 @@ static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) if (!seccomp) return -ENOMEM; + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + r = seccomp_rule_add( seccomp, SCMP_ACT_ERRNO(EPERM), @@ -1322,6 +1340,10 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) { if (!seccomp) return -ENOMEM; + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + /* Determine the highest policy constant we want to allow */ for (i = 0; i < ELEMENTSOF(permitted_policies); i++) if (permitted_policies[i] > max_policy) @@ -1375,12 +1397,121 @@ finish: return r; } +static int apply_protect_sysctl(Unit *u, const ExecContext *c) { + scmp_filter_ctx *seccomp; + int r; + + assert(c); + + /* Turn off the legacy sysctl() system call. Many distributions turn this off while building the kernel, but + * let's protect even those systems where this is left on in the kernel. */ + + if (skip_seccomp_unavailable(u, "ProtectKernelTunables=")) + return 0; + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(_sysctl), + 0); + if (r < 0) + goto finish; + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); + return r; +} + +static int apply_private_devices(Unit *u, const ExecContext *c) { + const SystemCallFilterSet *set; + scmp_filter_ctx *seccomp; + const char *sys; + bool syscalls_found = false; + int r; + + assert(c); + + /* If PrivateDevices= is set, also turn off iopl and all @raw-io syscalls. */ + + if (skip_seccomp_unavailable(u, "PrivateDevices=")) + return 0; + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + + for (set = syscall_filter_sets; set->set_name; set++) + if (streq(set->set_name, "@raw-io")) { + syscalls_found = true; + break; + } + + /* We should never fail here */ + if (!syscalls_found) { + r = -EOPNOTSUPP; + goto finish; + } + + NULSTR_FOREACH(sys, set->value) { + int id; + bool add = true; + +#ifndef __NR_s390_pci_mmio_read + if (streq(sys, "s390_pci_mmio_read")) + add = false; +#endif +#ifndef __NR_s390_pci_mmio_write + if (streq(sys, "s390_pci_mmio_write")) + add = false; +#endif + + if (!add) + continue; + + id = seccomp_syscall_resolve_name(sys); + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + id, 0); + if (r < 0) + goto finish; + } + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); + return r; +} + #endif static void do_idle_pipe_dance(int idle_pipe[4]) { assert(idle_pipe); - idle_pipe[1] = safe_close(idle_pipe[1]); idle_pipe[2] = safe_close(idle_pipe[2]); @@ -1581,7 +1712,9 @@ static bool exec_needs_mount_namespace( if (context->private_devices || context->protect_system != PROTECT_SYSTEM_NO || - context->protect_home != PROTECT_HOME_NO) + context->protect_home != PROTECT_HOME_NO || + context->protect_kernel_tunables || + context->protect_control_groups) return true; return false; @@ -1740,6 +1873,111 @@ static int setup_private_users(uid_t uid, gid_t gid) { return 0; } +static int setup_runtime_directory( + const ExecContext *context, + const ExecParameters *params, + uid_t uid, + gid_t gid) { + + char **rt; + int r; + + assert(context); + assert(params); + + STRV_FOREACH(rt, context->runtime_directory) { + _cleanup_free_ char *p; + + p = strjoin(params->runtime_prefix, "/", *rt, NULL); + if (!p) + return -ENOMEM; + + r = mkdir_p_label(p, context->runtime_directory_mode); + if (r < 0) + return r; + + r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid); + if (r < 0) + return r; + } + + return 0; +} + +static int setup_smack( + const ExecContext *context, + const ExecCommand *command) { + +#ifdef HAVE_SMACK + int r; + + assert(context); + assert(command); + + if (!mac_smack_use()) + return 0; + + if (context->smack_process_label) { + r = mac_smack_apply_pid(0, context->smack_process_label); + if (r < 0) + return r; + } +#ifdef SMACK_DEFAULT_PROCESS_LABEL + else { + _cleanup_free_ char *exec_label = NULL; + + r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); + if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) + return r; + + r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); + if (r < 0) + return r; + } +#endif +#endif + + return 0; +} + +static int compile_read_write_paths( + const ExecContext *context, + const ExecParameters *params, + char ***ret) { + + _cleanup_strv_free_ char **l = NULL; + char **rt; + + /* Compile the list of writable paths. This is the combination of the explicitly configured paths, plus all + * runtime directories. */ + + if (strv_isempty(context->read_write_paths) && + strv_isempty(context->runtime_directory)) { + *ret = NULL; /* NOP if neither is set */ + return 0; + } + + l = strv_copy(context->read_write_paths); + if (!l) + return -ENOMEM; + + STRV_FOREACH(rt, context->runtime_directory) { + char *s; + + s = strjoin(params->runtime_prefix, "/", *rt, NULL); + if (!s) + return -ENOMEM; + + if (strv_consume(&l, s) < 0) + return -ENOMEM; + } + + *ret = l; + l = NULL; + + return 0; +} + static void append_socket_pair(int *array, unsigned *n, int pair[2]) { assert(array); assert(n); @@ -1796,6 +2034,37 @@ static int close_remaining_fds( return close_all_fds(dont_close, n_dont_close); } +static bool context_has_address_families(const ExecContext *c) { + assert(c); + + return c->address_families_whitelist || + !set_isempty(c->address_families); +} + +static bool context_has_syscall_filters(const ExecContext *c) { + assert(c); + + return c->syscall_whitelist || + !set_isempty(c->syscall_filter) || + !set_isempty(c->syscall_archs); +} + +static bool context_has_no_new_privileges(const ExecContext *c) { + assert(c); + + if (c->no_new_privileges) + return true; + + if (have_effective_cap(CAP_SYS_ADMIN)) /* if we are privileged, we don't need NNP */ + return false; + + return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */ + c->memory_deny_write_execute || + c->restrict_realtime || + c->protect_kernel_tunables || + context_has_syscall_filters(c); +} + static int send_user_lookup( Unit *unit, int user_lookup_fd, @@ -1940,22 +2209,14 @@ static int exec_child( } else { if (context->user) { username = context->user; - r = get_user_creds(&username, &uid, &gid, &home, &shell); + r = get_user_creds_clean(&username, &uid, &gid, &home, &shell); if (r < 0) { *exit_status = EXIT_USER; return r; } - /* Don't set $HOME or $SHELL if they are are not particularly enlightening anyway. */ - if (isempty(home) || path_equal(home, "/")) - home = NULL; - - if (isempty(shell) || PATH_IN_SET(shell, - "/bin/nologin", - "/sbin/nologin", - "/usr/bin/nologin", - "/usr/sbin/nologin")) - shell = NULL; + /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway + * (i.e. are "/" or "/bin/nologin"). */ } if (context->group) { @@ -2108,28 +2369,10 @@ static int exec_child( } if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) { - char **rt; - - STRV_FOREACH(rt, context->runtime_directory) { - _cleanup_free_ char *p; - - p = strjoin(params->runtime_prefix, "/", *rt, NULL); - if (!p) { - *exit_status = EXIT_RUNTIME_DIRECTORY; - return -ENOMEM; - } - - r = mkdir_p_label(p, context->runtime_directory_mode); - if (r < 0) { - *exit_status = EXIT_RUNTIME_DIRECTORY; - return r; - } - - r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid); - if (r < 0) { - *exit_status = EXIT_RUNTIME_DIRECTORY; - return r; - } + r = setup_runtime_directory(context, params, uid, gid); + if (r < 0) { + *exit_status = EXIT_RUNTIME_DIRECTORY; + return r; } } @@ -2168,49 +2411,22 @@ static int exec_child( } accum_env = strv_env_clean(accum_env); - umask(context->umask); + (void) umask(context->umask); if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { - r = enforce_groups(context, username, gid); + r = setup_smack(context, command); if (r < 0) { - *exit_status = EXIT_GROUP; + *exit_status = EXIT_SMACK_PROCESS_LABEL; return r; } -#ifdef HAVE_SMACK - if (context->smack_process_label) { - r = mac_smack_apply_pid(0, context->smack_process_label); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#ifdef SMACK_DEFAULT_PROCESS_LABEL - else { - _cleanup_free_ char *exec_label = NULL; - - r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); - if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#endif -#endif -#ifdef HAVE_PAM if (context->pam_name && username) { - r = setup_pam(context->pam_name, username, uid, context->tty_path, &accum_env, fds, n_fds); + r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds); if (r < 0) { *exit_status = EXIT_PAM; return r; } } -#endif } if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) { @@ -2222,8 +2438,8 @@ static int exec_child( } needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime); - if (needs_mount_namespace) { + _cleanup_free_ char **rw = NULL; char *tmp = NULL, *var = NULL; /* The runtime struct only contains the parent @@ -2239,14 +2455,22 @@ static int exec_child( var = strjoina(runtime->var_tmp_dir, "/tmp"); } + r = compile_read_write_paths(context, params, &rw); + if (r < 0) { + *exit_status = EXIT_NAMESPACE; + return r; + } + r = setup_namespace( (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL, - context->read_write_paths, + rw, context->read_only_paths, context->inaccessible_paths, tmp, var, context->private_devices, + context->protect_kernel_tunables, + context->protect_control_groups, context->protect_home, context->protect_system, context->mount_flags); @@ -2264,6 +2488,14 @@ static int exec_child( } } + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { + r = enforce_groups(context, username, gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + } + if (context->working_directory_home) wd = home; else if (context->working_directory) @@ -2335,11 +2567,6 @@ static int exec_child( if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { - bool use_address_families = context->address_families_whitelist || - !set_isempty(context->address_families); - bool use_syscall_filter = context->syscall_whitelist || - !set_isempty(context->syscall_filter) || - !set_isempty(context->syscall_archs); int secure_bits = context->secure_bits; for (i = 0; i < _RLIMIT_MAX; i++) { @@ -2416,15 +2643,14 @@ static int exec_child( return -errno; } - if (context->no_new_privileges || - (!have_effective_cap(CAP_SYS_ADMIN) && (use_address_families || context->memory_deny_write_execute || context->restrict_realtime || use_syscall_filter))) + if (context_has_no_new_privileges(context)) if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { *exit_status = EXIT_NO_NEW_PRIVILEGES; return -errno; } #ifdef HAVE_SECCOMP - if (use_address_families) { + if (context_has_address_families(context)) { r = apply_address_families(unit, context); if (r < 0) { *exit_status = EXIT_ADDRESS_FAMILIES; @@ -2448,7 +2674,23 @@ static int exec_child( } } - if (use_syscall_filter) { + if (context->protect_kernel_tunables) { + r = apply_protect_sysctl(unit, context); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return r; + } + } + + if (context->private_devices) { + r = apply_private_devices(unit, context); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return r; + } + } + + if (context_has_syscall_filters(context)) { r = apply_seccomp(unit, context); if (r < 0) { *exit_status = EXIT_SECCOMP; @@ -2880,6 +3122,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sNonBlocking: %s\n" "%sPrivateTmp: %s\n" "%sPrivateDevices: %s\n" + "%sProtectKernelTunables: %s\n" + "%sProtectControlGroups: %s\n" "%sPrivateNetwork: %s\n" "%sPrivateUsers: %s\n" "%sProtectHome: %s\n" @@ -2893,6 +3137,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->non_blocking), prefix, yes_no(c->private_tmp), prefix, yes_no(c->private_devices), + prefix, yes_no(c->protect_kernel_tunables), + prefix, yes_no(c->protect_control_groups), prefix, yes_no(c->private_network), prefix, yes_no(c->private_users), prefix, protect_home_to_string(c->protect_home), diff --git a/src/core/execute.h b/src/core/execute.h index 6082c42aba..449180c903 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -174,6 +174,8 @@ struct ExecContext { bool private_users; ProtectSystem protect_system; ProtectHome protect_home; + bool protect_kernel_tunables; + bool protect_control_groups; bool no_new_privileges; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 2e6c965aec..c49c1d6732 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -89,6 +89,8 @@ $1.ReadOnlyPaths, config_parse_namespace_path_strv, 0, $1.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) $1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp) $1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices) +$1.ProtectKernelTunables, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_tunables) +$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups) $1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) $1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users) $1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context) diff --git a/src/core/main.c b/src/core/main.c index 7d8322ebd8..4b82a57b3c 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -131,6 +131,7 @@ static bool arg_default_memory_accounting = false; static bool arg_default_tasks_accounting = true; static uint64_t arg_default_tasks_max = UINT64_MAX; static sd_id128_t arg_machine_id = {}; +static CADBurstAction arg_cad_burst_action = CAD_BURST_ACTION_REBOOT; noreturn static void freeze_or_reboot(void) { @@ -202,7 +203,7 @@ noreturn static void crash(int sig) { pid, sigchld_code_to_string(status.si_code), status.si_status, strna(status.si_code == CLD_EXITED - ? exit_status_to_string(status.si_status, EXIT_STATUS_FULL) + ? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL) : signal_to_string(status.si_status))); else log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid); @@ -648,6 +649,8 @@ static int config_parse_join_controllers(const char *unit, return 0; } +static DEFINE_CONFIG_PARSE_ENUM(config_parse_cad_burst_action, cad_burst_action, CADBurstAction, "Failed to parse service restart specifier"); + static int parse_config_file(void) { const ConfigTableItem items[] = { @@ -702,6 +705,7 @@ static int parse_config_file(void) { { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max }, + { "Manager", "CtrlAltDelBurstAction", config_parse_cad_burst_action, 0, &arg_cad_burst_action}, {} }; @@ -715,7 +719,7 @@ static int parse_config_file(void) { CONF_PATHS_NULSTR("systemd/system.conf.d") : CONF_PATHS_NULSTR("systemd/user.conf.d"); - config_parse_many(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL); + config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL); /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY * like everywhere else. */ @@ -996,10 +1000,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_MACHINE_ID: r = set_machine_id(optarg); - if (r < 0) { - log_error("MachineID '%s' is not valid.", optarg); - return r; - } + if (r < 0) + return log_error_errno(r, "MachineID '%s' is not valid.", optarg); break; case 'h': @@ -1530,15 +1532,9 @@ int main(int argc, char *argv[]) { * need to do that for user instances since they never log * into the console. */ log_show_color(colors_enabled()); - make_null_stdio(); - } - - /* Initialize default unit */ - r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET); - if (r < 0) { - log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET); - error_message = "Failed to set default unit"; - goto finish; + r = make_null_stdio(); + if (r < 0) + log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m"); } r = initialize_join_controllers(); @@ -1588,6 +1584,16 @@ int main(int argc, char *argv[]) { goto finish; } + /* Initialize default unit */ + if (!arg_default_unit) { + arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET); + if (!arg_default_unit) { + r = log_oom(); + error_message = "Failed to set default unit"; + goto finish; + } + } + if (arg_action == ACTION_TEST && geteuid() == 0) { log_error("Don't run test mode as root."); @@ -1796,6 +1802,7 @@ int main(int argc, char *argv[]) { m->initrd_timestamp = initrd_timestamp; m->security_start_timestamp = security_start_timestamp; m->security_finish_timestamp = security_finish_timestamp; + m->cad_burst_action = arg_cad_burst_action; manager_set_defaults(m); manager_set_show_status(m, arg_show_status); diff --git a/src/core/manager.c b/src/core/manager.c index b58f68fa7a..c1dce62a18 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -590,7 +590,7 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = - m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = + m->dev_autofs_fd = m->private_listen_fd = m->cgroup_inotify_fd = m->ask_password_inotify_fd = -1; m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1; @@ -661,9 +661,8 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { goto fail; } - /* Note that we set up neither kdbus, nor the notify fd - * here. We do that after deserialization, since they might - * have gotten serialized across the reexec. */ + /* Note that we do not set up the notify fd here. We do that after deserialization, + * since they might have gotten serialized across the reexec. */ m->taint_usr = dir_is_empty("/usr") > 0; @@ -879,7 +878,6 @@ static int manager_connect_bus(Manager *m, bool reexecuting) { return 0; try_bus_connect = - m->kdbus_fd >= 0 || reexecuting || (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")); @@ -1084,7 +1082,6 @@ Manager* manager_free(Manager *m) { safe_close(m->notify_fd); safe_close(m->cgroups_agent_fd); safe_close(m->time_change_fd); - safe_close(m->kdbus_fd); safe_close_pair(m->user_lookup_fds); manager_close_ask_password(m); @@ -1239,9 +1236,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { return r; /* Make sure the transient directory always exists, so that it remains in the search path */ - r = mkdir_p_label(m->lookup_paths.transient, 0755); - if (r < 0) - return r; + if (!m->test_run) { + r = mkdir_p_label(m->lookup_paths.transient, 0755); + if (r < 0) + return r; + } dual_timestamp_get(&m->generators_start_timestamp); r = manager_run_generators(m); @@ -1287,7 +1286,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (q < 0 && r == 0) r = q; - /* We might have deserialized the kdbus control fd, but if we didn't, then let's create the bus now. */ + /* Let's connect to the bus now. */ (void) manager_connect_bus(m, !!serialization); (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed); @@ -1660,13 +1659,12 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui return 0; } -static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) { +static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; assert(m); assert(u); assert(buf); - assert(n > 0); tags = strv_split(buf, "\n\r"); if (!tags) { @@ -1676,8 +1674,14 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const if (UNIT_VTABLE(u)->notify_message) UNIT_VTABLE(u)->notify_message(u, pid, tags, fds); - else - log_unit_debug(u, "Got notification message for unit. Ignoring."); + else if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + _cleanup_free_ char *x = NULL, *y = NULL; + + x = cescape(buf); + if (x) + y = ellipsize(x, 20, 90); + log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y)); + } } static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { @@ -1703,7 +1707,6 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t struct cmsghdr *cmsg; struct ucred *ucred = NULL; - bool found = false; Unit *u1, *u2, *u3; int r, *fd_array = NULL; unsigned n_fds = 0; @@ -1717,12 +1720,15 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t return 0; } - n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC); if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; + if (IN_SET(errno, EAGAIN, EINTR)) + return 0; /* Spurious wakeup, try again */ - return -errno; + /* If this is any other, real error, then let's stop processing this socket. This of course means we + * won't take notification messages anymore, but that's still better than busy looping around this: + * being woken up over and over again but being unable to actually read the message off the socket. */ + return log_error_errno(errno, "Failed to receive notification message: %m"); } CMSG_FOREACH(cmsg, &msghdr) { @@ -1745,7 +1751,8 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r = fdset_new_array(&fds, fd_array, n_fds); if (r < 0) { close_many(fd_array, n_fds); - return log_oom(); + log_oom(); + return 0; } } @@ -1754,38 +1761,40 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t return 0; } - if ((size_t) n >= sizeof(buf)) { + if ((size_t) n >= sizeof(buf) || (msghdr.msg_flags & MSG_TRUNC)) { log_warning("Received notify message exceeded maximum size. Ignoring."); return 0; } + /* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes. We permit one + * trailing NUL byte in the message, but don't expect it. */ + if (n > 1 && memchr(buf, 0, n-1)) { + log_warning("Received notify message with embedded NUL bytes. Ignoring."); + return 0; + } + + /* Make sure it's NUL-terminated. */ buf[n] = 0; /* Notify every unit that might be interested, but try * to avoid notifying the same one multiple times. */ 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; - } + if (u1) + manager_invoke_notify_message(m, u1, ucred->pid, buf, fds); 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; - } + if (u2 && u2 != u1) + manager_invoke_notify_message(m, u2, ucred->pid, buf, fds); 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; - } + if (u3 && u3 != u2 && u3 != u1) + manager_invoke_notify_message(m, u3, ucred->pid, buf, fds); - if (!found) + if (!u1 && !u2 && !u3) log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid); if (fdset_size(fds) > 0) - log_warning("Got auxiliary fds with notification message, closing all."); + log_warning("Got extra auxiliary fds with notification message, closing them."); return 0; } @@ -1890,6 +1899,35 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) { return r; } +static void manager_handle_ctrl_alt_del(Manager *m) { + /* If the user presses C-A-D more than + * 7 times within 2s, we reboot/shutdown immediately, + * unless it was disabled in system.conf */ + + if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == CAD_BURST_ACTION_IGNORE) + manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); + else { + switch (m->cad_burst_action) { + + case CAD_BURST_ACTION_REBOOT: + m->exit_code = MANAGER_REBOOT; + break; + + case CAD_BURST_ACTION_POWEROFF: + m->exit_code = MANAGER_POWEROFF; + break; + + default: + assert_not_reached("Unknown action."); + } + + log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.", + cad_burst_action_to_string(m->cad_burst_action)); + status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.", + cad_burst_action_to_string(m->cad_burst_action)); + } +} + static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { Manager *m = userdata; ssize_t n; @@ -1908,14 +1946,17 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t for (;;) { n = read(m->signal_fd, &sfsi, sizeof(sfsi)); if (n != sizeof(sfsi)) { + if (n >= 0) { + log_warning("Truncated read from signal fd (%zu bytes)!", n); + return 0; + } - if (n >= 0) - return -EIO; - - if (errno == EINTR || errno == EAGAIN) + if (IN_SET(errno, EINTR, EAGAIN)) break; - return -errno; + /* We return an error here, which will kill this handler, + * to avoid a busy loop on read error. */ + return log_error_errno(errno, "Reading from signal fd failed: %m"); } log_received_signal(sfsi.ssi_signo == SIGCHLD || @@ -1941,19 +1982,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t case SIGINT: if (MANAGER_IS_SYSTEM(m)) { - - /* If the user presses C-A-D more than - * 7 times within 2s, we reboot - * immediately. */ - - if (ratelimit_test(&m->ctrl_alt_del_ratelimit)) - manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); - else { - log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); - status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); - m->exit_code = MANAGER_REBOOT; - } - + manager_handle_ctrl_alt_del(m); break; } @@ -2481,16 +2510,6 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "user-lookup=%i %i\n", copy0, copy1); } - if (m->kdbus_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, m->kdbus_fd); - if (copy < 0) - return copy; - - fprintf(f, "kdbus-fd=%i\n", copy); - } - bus_track_serialize(m->subscribed, f, "subscribed"); r = dynamic_user_serialize(m, f, fds); @@ -2678,16 +2697,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { m->user_lookup_fds[1] = fdset_remove(fds, fd1); } - } else if (startswith(l, "kdbus-fd=")) { - int fd; - - if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse kdbus fd: %s", l + 9); - else { - safe_close(m->kdbus_fd); - m->kdbus_fd = fdset_remove(fds, fd); - } - } else if (startswith(l, "dynamic-user=")) dynamic_user_deserialize_one(m, l + 13, fds); else if (startswith(l, "destroy-ipc-uid=")) @@ -2699,7 +2708,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { if (strv_extend(&m->deserialized_subscribed, l+11) < 0) log_oom(); - } else + } else if (!startswith(l, "kdbus-fd=")) /* ignore this one */ log_debug("Unknown serialization item '%s'", l); } @@ -3560,3 +3569,11 @@ static const char *const manager_state_table[_MANAGER_STATE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState); + +static const char *const cad_burst_action_table[_CAD_BURST_ACTION_MAX] = { + [CAD_BURST_ACTION_IGNORE] = "ignore", + [CAD_BURST_ACTION_REBOOT] = "reboot-force", + [CAD_BURST_ACTION_POWEROFF] = "poweroff-force", +}; + +DEFINE_STRING_TABLE_LOOKUP(cad_burst_action, CADBurstAction); diff --git a/src/core/manager.h b/src/core/manager.h index b9f2e4b5a1..495440b446 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -62,6 +62,14 @@ typedef enum ManagerExitCode { _MANAGER_EXIT_CODE_INVALID = -1 } ManagerExitCode; +typedef enum CADBurstAction { + CAD_BURST_ACTION_IGNORE, + CAD_BURST_ACTION_REBOOT, + CAD_BURST_ACTION_POWEROFF, + _CAD_BURST_ACTION_MAX, + _CAD_BURST_ACTION_INVALID = -1 +} CADBurstAction; + typedef enum StatusType { STATUS_TYPE_EPHEMERAL, STATUS_TYPE_NORMAL, @@ -294,9 +302,6 @@ struct Manager { * value where Unit objects are contained. */ Hashmap *units_requiring_mounts_for; - /* Reference to the kdbus bus control fd */ - int kdbus_fd; - /* Used for processing polkit authorization responses */ Hashmap *polkit_registry; @@ -307,8 +312,9 @@ struct Manager { Hashmap *uid_refs; Hashmap *gid_refs; - /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */ + /* When the user hits C-A-D more than 7 times per 2s, do something immediately... */ RateLimit ctrl_alt_del_ratelimit; + CADBurstAction cad_burst_action; const char *unit_log_field; const char *unit_log_format_string; @@ -401,3 +407,6 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value); const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; + +const char *cad_burst_action_to_string(CADBurstAction a) _const_; +CADBurstAction cad_burst_action_from_string(const char *s) _pure_; diff --git a/src/core/mount.c b/src/core/mount.c index 04025b83b9..f5e67b1d78 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -830,7 +830,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { fail: log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m"); - if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) + if (IN_SET(state, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL)) mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); else mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); @@ -986,18 +986,19 @@ static int mount_start(Unit *u) { /* We cannot fulfill this request right now, try again later * please! */ - if (m->state == MOUNT_UNMOUNTING || - m->state == MOUNT_UNMOUNTING_SIGTERM || - m->state == MOUNT_UNMOUNTING_SIGKILL || - m->state == MOUNT_MOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGKILL) + if (IN_SET(m->state, + MOUNT_UNMOUNTING, + MOUNT_UNMOUNTING_SIGTERM, + MOUNT_UNMOUNTING_SIGKILL, + MOUNT_MOUNTING_SIGTERM, + MOUNT_MOUNTING_SIGKILL)) return -EAGAIN; /* Already on it! */ if (m->state == MOUNT_MOUNTING) return 0; - assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); + assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED)); r = unit_start_limit_test(u); if (r < 0) { @@ -1019,19 +1020,21 @@ static int mount_stop(Unit *u) { assert(m); /* Already on it */ - if (m->state == MOUNT_UNMOUNTING || - m->state == MOUNT_UNMOUNTING_SIGKILL || - m->state == MOUNT_UNMOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGKILL) + if (IN_SET(m->state, + MOUNT_UNMOUNTING, + MOUNT_UNMOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, + MOUNT_MOUNTING_SIGTERM, + MOUNT_MOUNTING_SIGKILL)) return 0; - assert(m->state == MOUNT_MOUNTING || - m->state == MOUNT_MOUNTING_DONE || - m->state == MOUNT_MOUNTED || - m->state == MOUNT_REMOUNTING || - m->state == MOUNT_REMOUNTING_SIGTERM || - m->state == MOUNT_REMOUNTING_SIGKILL); + assert(IN_SET(m->state, + MOUNT_MOUNTING, + MOUNT_MOUNTING_DONE, + MOUNT_MOUNTED, + MOUNT_REMOUNTING, + MOUNT_REMOUNTING_SIGTERM, + MOUNT_REMOUNTING_SIGKILL)); mount_enter_unmounting(m); return 1; @@ -1159,7 +1162,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { m->control_pid = 0; - if (is_clean_exit(code, status, NULL)) + if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) f = MOUNT_SUCCESS; else if (code == CLD_EXITED) f = MOUNT_FAILURE_EXIT_CODE; @@ -1197,9 +1200,10 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { case MOUNT_MOUNTING_SIGKILL: case MOUNT_MOUNTING_SIGTERM: - if (f == MOUNT_SUCCESS) - mount_enter_mounted(m, f); - else if (m->from_proc_self_mountinfo) + if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo) + /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are + * happy. If we see the first condition first, we should see the the second condition + * immediately after – or /bin/mount lies to us and is broken. */ mount_enter_mounted(m, f); else mount_enter_dead(m, f); diff --git a/src/core/namespace.c b/src/core/namespace.c index 52a2505d94..43a2f4ba6e 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -29,6 +29,7 @@ #include "alloc-util.h" #include "dev-setup.h" #include "fd-util.h" +#include "fs-util.h" #include "loopback-setup.h" #include "missing.h" #include "mkdir.h" @@ -53,61 +54,230 @@ typedef enum MountMode { PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV, - READWRITE + READWRITE, } MountMode; typedef struct BindMount { - const char *path; + const char *path; /* stack memory, doesn't need to be freed explicitly */ + char *chased; /* malloc()ed memory, needs to be freed */ MountMode mode; - bool done; - bool ignore; + bool ignore; /* Ignore if path does not exist */ } BindMount; +typedef struct TargetMount { + const char *path; + MountMode mode; + bool ignore; /* Ignore if path does not exist */ +} TargetMount; + +/* + * The following Protect tables are to protect paths and mark some of them + * READONLY, in case a path is covered by an option from another table, then + * it is marked READWRITE in the current one, and the more restrictive mode is + * applied from that other table. This way all options can be combined in a + * safe and comprehensible way for users. + */ + +/* ProtectKernelTunables= option and the related filesystem APIs */ +static const TargetMount protect_kernel_tunables_table[] = { + { "/proc/sys", READONLY, false }, + { "/proc/sysrq-trigger", READONLY, true }, + { "/proc/latency_stats", READONLY, true }, + { "/proc/mtrr", READONLY, true }, + { "/proc/apm", READONLY, true }, + { "/proc/acpi", READONLY, true }, + { "/proc/timer_stats", READONLY, true }, + { "/proc/asound", READONLY, true }, + { "/proc/bus", READONLY, true }, + { "/proc/fs", READONLY, true }, + { "/proc/irq", READONLY, true }, + { "/sys", READONLY, false }, + { "/sys/kernel/debug", READONLY, true }, + { "/sys/kernel/tracing", READONLY, true }, + { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */ +}; + +/* + * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of + * system should be protected by ProtectSystem= + */ +static const TargetMount protect_home_read_only_table[] = { + { "/home", READONLY, true }, + { "/run/user", READONLY, true }, + { "/root", READONLY, true }, +}; + +/* ProtectHome=yes table */ +static const TargetMount protect_home_yes_table[] = { + { "/home", INACCESSIBLE, true }, + { "/run/user", INACCESSIBLE, true }, + { "/root", INACCESSIBLE, true }, +}; + +/* ProtectSystem=yes table */ +static const TargetMount protect_system_yes_table[] = { + { "/usr", READONLY, false }, + { "/boot", READONLY, true }, + { "/efi", READONLY, true }, +}; + +/* ProtectSystem=full includes ProtectSystem=yes */ +static const TargetMount protect_system_full_table[] = { + { "/usr", READONLY, false }, + { "/boot", READONLY, true }, + { "/efi", READONLY, true }, + { "/etc", READONLY, false }, +}; + +/* + * ProtectSystem=strict table. In this strict mode, we mount everything + * read-only, except for /proc, /dev, /sys which are the kernel API VFS, + * which are left writable, but PrivateDevices= + ProtectKernelTunables= + * protect those, and these options should be fully orthogonal. + * (And of course /home and friends are also left writable, as ProtectHome= + * shall manage those, orthogonally). + */ +static const TargetMount protect_system_strict_table[] = { + { "/", READONLY, false }, + { "/proc", READWRITE, false }, /* ProtectKernelTunables= */ + { "/sys", READWRITE, false }, /* ProtectKernelTunables= */ + { "/dev", READWRITE, false }, /* PrivateDevices= */ + { "/home", READWRITE, true }, /* ProtectHome= */ + { "/run/user", READWRITE, true }, /* ProtectHome= */ + { "/root", READWRITE, true }, /* ProtectHome= */ +}; + +static void set_bind_mount(BindMount **p, const char *path, MountMode mode, bool ignore) { + (*p)->path = path; + (*p)->mode = mode; + (*p)->ignore = ignore; +} + static int append_mounts(BindMount **p, char **strv, MountMode mode) { char **i; assert(p); STRV_FOREACH(i, strv) { + bool ignore = false; - (*p)->ignore = false; - (*p)->done = false; - - if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') { - (*p)->ignore = true; + if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) { (*i)++; + ignore = true; } if (!path_is_absolute(*i)) return -EINVAL; - (*p)->path = *i; - (*p)->mode = mode; + set_bind_mount(p, *i, mode, ignore); (*p)++; } return 0; } -static int mount_path_compare(const void *a, const void *b) { - const BindMount *p = a, *q = b; - int d; +static int append_target_mounts(BindMount **p, const char *root_directory, const TargetMount *mounts, const size_t size) { + unsigned i; - d = path_compare(p->path, q->path); + assert(p); + assert(mounts); + + for (i = 0; i < size; i++) { + /* + * Here we assume that the ignore field is set during + * declaration we do not support "-" at the beginning. + */ + const TargetMount *m = &mounts[i]; + const char *path = prefix_roota(root_directory, m->path); + + if (!path_is_absolute(path)) + return -EINVAL; + + set_bind_mount(p, path, m->mode, m->ignore); + (*p)++; + } + + return 0; +} + +static int append_protect_kernel_tunables(BindMount **p, const char *root_directory) { + assert(p); + + return append_target_mounts(p, root_directory, protect_kernel_tunables_table, + ELEMENTSOF(protect_kernel_tunables_table)); +} - if (d == 0) { - /* If the paths are equal, check the mode */ - if (p->mode < q->mode) - return -1; +static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home) { + int r = 0; - if (p->mode > q->mode) - return 1; + assert(p); + if (protect_home == PROTECT_HOME_NO) return 0; + + switch (protect_home) { + case PROTECT_HOME_READ_ONLY: + r = append_target_mounts(p, root_directory, protect_home_read_only_table, + ELEMENTSOF(protect_home_read_only_table)); + break; + case PROTECT_HOME_YES: + r = append_target_mounts(p, root_directory, protect_home_yes_table, + ELEMENTSOF(protect_home_yes_table)); + break; + default: + r = -EINVAL; + break; } + return r; +} + +static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system) { + int r = 0; + + assert(p); + + if (protect_system == PROTECT_SYSTEM_NO) + return 0; + + switch (protect_system) { + case PROTECT_SYSTEM_STRICT: + r = append_target_mounts(p, root_directory, protect_system_strict_table, + ELEMENTSOF(protect_system_strict_table)); + break; + case PROTECT_SYSTEM_YES: + r = append_target_mounts(p, root_directory, protect_system_yes_table, + ELEMENTSOF(protect_system_yes_table)); + break; + case PROTECT_SYSTEM_FULL: + r = append_target_mounts(p, root_directory, protect_system_full_table, + ELEMENTSOF(protect_system_full_table)); + break; + default: + r = -EINVAL; + break; + } + + return r; +} + +static int mount_path_compare(const void *a, const void *b) { + const BindMount *p = a, *q = b; + int d; + /* If the paths are not equal, then order prefixes first */ - return d; + d = path_compare(p->path, q->path); + if (d != 0) + return d; + + /* If the paths are equal, check the mode */ + if (p->mode < q->mode) + return -1; + + if (p->mode > q->mode) + return 1; + + return 0; } static void drop_duplicates(BindMount *m, unsigned *n) { @@ -116,16 +286,110 @@ static void drop_duplicates(BindMount *m, unsigned *n) { assert(m); assert(n); + /* Drops duplicate entries. Expects that the array is properly ordered already. */ + for (f = m, t = m, previous = NULL; f < m+*n; f++) { - /* The first one wins */ - if (previous && path_equal(f->path, previous->path)) + /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() + * above. */ + if (previous && path_equal(f->path, previous->path)) { + log_debug("%s is duplicate.", f->path); continue; + } *t = *f; - previous = t; + t++; + } + + *n = t - m; +} + +static void drop_inaccessible(BindMount *m, unsigned *n) { + BindMount *f, *t; + const char *clear = NULL; + + assert(m); + assert(n); + + /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly + * ordered already. */ + for (f = m, t = m; f < m+*n; f++) { + + /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop + * it, as inaccessible paths really should drop the entire subtree. */ + if (clear && path_startswith(f->path, clear)) { + log_debug("%s is masked by %s.", f->path, clear); + continue; + } + + clear = f->mode == INACCESSIBLE ? f->path : NULL; + + *t = *f; + t++; + } + + *n = t - m; +} + +static void drop_nop(BindMount *m, unsigned *n) { + BindMount *f, *t; + + assert(m); + assert(n); + + /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the + * list is ordered by prefixes. */ + + for (f = m, t = m; f < m+*n; f++) { + + /* Only suppress such subtrees for READONLY and READWRITE entries */ + if (IN_SET(f->mode, READONLY, READWRITE)) { + BindMount *p; + bool found = false; + + /* Now let's find the first parent of the entry we are looking at. */ + for (p = t-1; p >= m; p--) { + if (path_startswith(f->path, p->path)) { + found = true; + break; + } + } + + /* We found it, let's see if it's the same mode, if so, we can drop this entry */ + if (found && p->mode == f->mode) { + log_debug("%s is redundant by %s", f->path, p->path); + continue; + } + } + + *t = *f; + t++; + } + + *n = t - m; +} + +static void drop_outside_root(const char *root_directory, BindMount *m, unsigned *n) { + BindMount *f, *t; + + assert(m); + assert(n); + + if (!root_directory) + return; + + /* Drops all mounts that are outside of the root directory. */ + + for (f = m, t = m; f < m+*n; f++) { + + if (!path_startswith(f->path, root_directory)) { + log_debug("%s is outside of root directory.", f->path); + continue; + } + + *t = *f; t++; } @@ -278,24 +542,23 @@ static int apply_mount( const char *what; int r; - struct stat target; assert(m); + log_debug("Applying namespace mount on %s", m->path); + switch (m->mode) { - case INACCESSIBLE: + case INACCESSIBLE: { + struct stat target; /* First, get rid of everything that is below if there * is anything... Then, overmount it with an * inaccessible path. */ - umount_recursive(m->path, 0); + (void) umount_recursive(m->path, 0); - if (lstat(m->path, &target) < 0) { - if (m->ignore && errno == ENOENT) - return 0; - return -errno; - } + if (lstat(m->path, &target) < 0) + return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", m->path); what = mode_to_inaccessible_node(target.st_mode); if (!what) { @@ -303,11 +566,20 @@ static int apply_mount( return -ELOOP; } break; + } + case READONLY: case READWRITE: - /* Nothing to mount here, we just later toggle the - * MS_RDONLY bit for the mount point */ - return 0; + + r = path_is_mount_point(m->path, 0); + if (r < 0) + return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", m->path); + if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */ + return 0; + + /* This isn't a mount point yet, let's make it one. */ + what = m->path; + break; case PRIVATE_TMP: what = tmp_dir; @@ -326,38 +598,104 @@ static int apply_mount( assert(what); - r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL); - if (r >= 0) { - log_debug("Successfully mounted %s to %s", what, m->path); - return r; - } else { - if (m->ignore && errno == ENOENT) - return 0; + if (mount(what, m->path, NULL, MS_BIND|MS_REC, NULL) < 0) return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, m->path); - } + + log_debug("Successfully mounted %s to %s", what, m->path); + return 0; } -static int make_read_only(BindMount *m) { - int r; +static int make_read_only(BindMount *m, char **blacklist) { + int r = 0; assert(m); if (IN_SET(m->mode, INACCESSIBLE, READONLY)) - r = bind_remount_recursive(m->path, true); - else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) { - r = bind_remount_recursive(m->path, false); - if (r == 0 && m->mode == PRIVATE_DEV) /* can be readonly but the submounts can't*/ - if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) - r = -errno; + r = bind_remount_recursive(m->path, true, blacklist); + else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/ + if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) + r = -errno; } else - r = 0; - - if (m->ignore && r == -ENOENT) return 0; + /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only + * already stays this way. This improves compatibility with container managers, where we won't attempt to undo + * read-only mounts already applied. */ + return r; } +static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) { + BindMount *f, *t; + int r; + + assert(m); + assert(n); + + /* Since mount() will always follow symlinks and we need to take the different root directory into account we + * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we + * can't resolve the path, and which have been marked for such removal. */ + + for (f = m, t = m; f < m+*n; f++) { + + r = chase_symlinks(f->path, root_directory, &f->chased); + if (r == -ENOENT && f->ignore) /* Doesn't exist? Then remove it! */ + continue; + if (r < 0) + return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); + + if (path_equal(f->path, f->chased)) + f->chased = mfree(f->chased); + else { + log_debug("Chased %s → %s", f->path, f->chased); + f->path = f->chased; + } + + *t = *f; + t++; + } + + *n = t - m; + return 0; +} + +static unsigned namespace_calculate_mounts( + char** read_write_paths, + char** read_only_paths, + char** inaccessible_paths, + const char* tmp_dir, + const char* var_tmp_dir, + bool private_dev, + bool protect_sysctl, + bool protect_cgroups, + ProtectHome protect_home, + ProtectSystem protect_system) { + + unsigned protect_home_cnt; + unsigned protect_system_cnt = + (protect_system == PROTECT_SYSTEM_STRICT ? + ELEMENTSOF(protect_system_strict_table) : + ((protect_system == PROTECT_SYSTEM_FULL) ? + ELEMENTSOF(protect_system_full_table) : + ((protect_system == PROTECT_SYSTEM_YES) ? + ELEMENTSOF(protect_system_yes_table) : 0))); + + protect_home_cnt = + (protect_home == PROTECT_HOME_YES ? + ELEMENTSOF(protect_home_yes_table) : + ((protect_home == PROTECT_HOME_READ_ONLY) ? + ELEMENTSOF(protect_home_read_only_table) : 0)); + + return !!tmp_dir + !!var_tmp_dir + + strv_length(read_write_paths) + + strv_length(read_only_paths) + + strv_length(inaccessible_paths) + + private_dev + + (protect_sysctl ? ELEMENTSOF(protect_kernel_tunables_table) : 0) + + (protect_cgroups ? 1 : 0) + + protect_home_cnt + protect_system_cnt; +} + int setup_namespace( const char* root_directory, char** read_write_paths, @@ -366,28 +704,31 @@ int setup_namespace( const char* tmp_dir, const char* var_tmp_dir, bool private_dev, + bool protect_sysctl, + bool protect_cgroups, ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags) { BindMount *m, *mounts = NULL; + bool make_slave = false; unsigned n; int r = 0; if (mount_flags == 0) mount_flags = MS_SHARED; - if (unshare(CLONE_NEWNS) < 0) - return -errno; + n = namespace_calculate_mounts(read_write_paths, + read_only_paths, + inaccessible_paths, + tmp_dir, var_tmp_dir, + private_dev, protect_sysctl, + protect_cgroups, protect_home, + protect_system); - n = !!tmp_dir + !!var_tmp_dir + - strv_length(read_write_paths) + - strv_length(read_only_paths) + - strv_length(inaccessible_paths) + - private_dev + - (protect_home != PROTECT_HOME_NO ? 3 : 0) + - (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) + - (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0); + /* Set mount slave mode */ + if (root_directory || n > 0) + make_slave = true; if (n > 0) { m = mounts = (BindMount *) alloca0(n * sizeof(BindMount)); @@ -421,94 +762,112 @@ int setup_namespace( m++; } - if (protect_home != PROTECT_HOME_NO) { - const char *home_dir, *run_user_dir, *root_dir; + if (protect_sysctl) + append_protect_kernel_tunables(&m, root_directory); - home_dir = prefix_roota(root_directory, "/home"); - home_dir = strjoina("-", home_dir); - run_user_dir = prefix_roota(root_directory, "/run/user"); - run_user_dir = strjoina("-", run_user_dir); - root_dir = prefix_roota(root_directory, "/root"); - root_dir = strjoina("-", root_dir); - - r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir), - protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE); - if (r < 0) - return r; + if (protect_cgroups) { + m->path = prefix_roota(root_directory, "/sys/fs/cgroup"); + m->mode = READONLY; + m++; } - if (protect_system != PROTECT_SYSTEM_NO) { - const char *usr_dir, *boot_dir, *etc_dir; - - usr_dir = prefix_roota(root_directory, "/usr"); - boot_dir = prefix_roota(root_directory, "/boot"); - boot_dir = strjoina("-", boot_dir); - etc_dir = prefix_roota(root_directory, "/etc"); + r = append_protect_home(&m, root_directory, protect_home); + if (r < 0) + return r; - r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL - ? STRV_MAKE(usr_dir, boot_dir, etc_dir) - : STRV_MAKE(usr_dir, boot_dir), READONLY); - if (r < 0) - return r; - } + r = append_protect_system(&m, root_directory, protect_system); + if (r < 0) + return r; assert(mounts + n == m); + /* Resolve symlinks manually first, as mount() will always follow them relative to the host's + * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit + * racy. */ + r = chase_all_symlinks(root_directory, mounts, &n); + if (r < 0) + goto finish; + qsort(mounts, n, sizeof(BindMount), mount_path_compare); + drop_duplicates(mounts, &n); + drop_outside_root(root_directory, mounts, &n); + drop_inaccessible(mounts, &n); + drop_nop(mounts, &n); + } + + if (unshare(CLONE_NEWNS) < 0) { + r = -errno; + goto finish; } - if (n > 0 || root_directory) { + if (make_slave) { /* Remount / as SLAVE so that nothing now mounted in the namespace shows up in the parent */ - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - return -errno; + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { + r = -errno; + goto finish; + } } if (root_directory) { - /* Turn directory into bind mount */ - if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) - return -errno; + /* Turn directory into bind mount, if it isn't one yet */ + r = path_is_mount_point(root_directory, AT_SYMLINK_FOLLOW); + if (r < 0) + goto finish; + if (r == 0) { + if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) { + r = -errno; + goto finish; + } + } } if (n > 0) { + char **blacklist; + unsigned j; + + /* First round, add in all special mounts we need */ for (m = mounts; m < mounts + n; ++m) { r = apply_mount(m, tmp_dir, var_tmp_dir); if (r < 0) - goto fail; + goto finish; } + /* Create a blacklist we can pass to bind_mount_recursive() */ + blacklist = newa(char*, n+1); + for (j = 0; j < n; j++) + blacklist[j] = (char*) mounts[j].path; + blacklist[j] = NULL; + + /* Second round, flip the ro bits if necessary. */ for (m = mounts; m < mounts + n; ++m) { - r = make_read_only(m); + r = make_read_only(m, blacklist); if (r < 0) - goto fail; + goto finish; } } if (root_directory) { /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */ r = mount_move_root(root_directory); - - /* at this point, we cannot rollback */ if (r < 0) - return r; + goto finish; } /* Remount / as the desired mode. Not that this will not * reestablish propagation from our side to the host, since * what's disconnected is disconnected. */ - if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) - /* at this point, we cannot rollback */ - return -errno; + if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) { + r = -errno; + goto finish; + } - return 0; + r = 0; -fail: - if (n > 0) { - for (m = mounts; m < mounts + n; ++m) - if (m->done) - (void) umount2(m->path, MNT_DETACH); - } +finish: + for (m = mounts; m < mounts + n; m++) + free(m->chased); return r; } @@ -658,6 +1017,7 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { [PROTECT_SYSTEM_NO] = "no", [PROTECT_SYSTEM_YES] = "yes", [PROTECT_SYSTEM_FULL] = "full", + [PROTECT_SYSTEM_STRICT] = "strict", }; DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem); diff --git a/src/core/namespace.h b/src/core/namespace.h index 1aedf5f208..6505bcc499 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -35,6 +35,7 @@ typedef enum ProtectSystem { PROTECT_SYSTEM_NO, PROTECT_SYSTEM_YES, PROTECT_SYSTEM_FULL, + PROTECT_SYSTEM_STRICT, _PROTECT_SYSTEM_MAX, _PROTECT_SYSTEM_INVALID = -1 } ProtectSystem; @@ -46,6 +47,8 @@ int setup_namespace(const char *chroot, const char *tmp_dir, const char *var_tmp_dir, bool private_dev, + bool protect_sysctl, + bool protect_cgroups, ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags); diff --git a/src/core/service.c b/src/core/service.c index 969c62bd83..98edc437a2 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1256,10 +1256,16 @@ static int service_spawn( socklen_t salen = sizeof(sa); r = getpeername(s->socket_fd, &sa.sa, &salen); - if (r < 0) - return -errno; + if (r < 0) { + r = -errno; + + /* ENOTCONN is legitimate if the endpoint disappeared on shutdown. + * This connection is over, but the socket unit lives on. */ + if (r != -ENOTCONN || !IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST)) + return r; + } - if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) { + if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) { _cleanup_free_ char *addr = NULL; char *t; int port; @@ -2594,8 +2600,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) : - is_clean_exit_lsb(code, status, &s->success_status)) + if (is_clean_exit(code, status, s->type == SERVICE_ONESHOT ? EXIT_CLEAN_COMMAND : EXIT_CLEAN_DAEMON, &s->success_status)) f = SERVICE_SUCCESS; else if (code == CLD_EXITED) f = SERVICE_FAILURE_EXIT_CODE; diff --git a/src/core/socket.c b/src/core/socket.c index 70d55dd9ed..1b4a1b3dc3 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1334,14 +1334,9 @@ static int usbffs_select_ep(const struct dirent *d) { static int usbffs_dispatch_eps(SocketPort *p) { _cleanup_free_ struct dirent **ent = NULL; - _cleanup_free_ char *path = NULL; int r, i, n, k; - path = dirname_malloc(p->path); - if (!path) - return -ENOMEM; - - r = scandir(path, &ent, usbffs_select_ep, alphasort); + r = scandir(p->path, &ent, usbffs_select_ep, alphasort); if (r < 0) return -errno; @@ -1356,7 +1351,7 @@ static int usbffs_dispatch_eps(SocketPort *p) { for (i = 0; i < n; ++i) { _cleanup_free_ char *ep = NULL; - ep = path_make_absolute(ent[i]->d_name, path); + ep = path_make_absolute(ent[i]->d_name, p->path); if (!ep) return -ENOMEM; @@ -2748,7 +2743,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; - if (is_clean_exit(code, status, NULL)) + if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) f = SOCKET_SUCCESS; else if (code == CLD_EXITED) f = SOCKET_FAILURE_EXIT_CODE; diff --git a/src/core/swap.c b/src/core/swap.c index fb222b6858..fee9e7b0e6 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -988,7 +988,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; - if (is_clean_exit(code, status, NULL)) + if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) f = SWAP_SUCCESS; else if (code == CLD_EXITED) f = SWAP_FAILURE_EXIT_CODE; diff --git a/src/core/system.conf b/src/core/system.conf index c6bb050aac..746572b7ff 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -21,6 +21,7 @@ #CrashChangeVT=no #CrashShell=no #CrashReboot=no +#CtrlAltDelBurstAction=reboot-force #CPUAffinity=1 2 #JoinControllers=cpu,cpuacct net_cls,net_prio #RuntimeWatchdogSec=0 diff --git a/src/core/umount.c b/src/core/umount.c index c21a2be54e..1e5459ed80 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -375,7 +375,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e /* If we are in a container, don't attempt to read-only mount anything as that brings no real benefits, but might confuse the host, as we remount - the superblock here, not the bind mound. */ + the superblock here, not the bind mount. */ if (detect_container() <= 0) { _cleanup_free_ char *options = NULL; /* MS_REMOUNT requires that the data parameter diff --git a/src/core/unit.c b/src/core/unit.c index de22f657c6..693f75c928 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2225,6 +2225,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen return 0; } + if (d == UNIT_BEFORE && other->type == UNIT_DEVICE) { + log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id); + return 0; + } + r = set_ensure_allocated(&u->dependencies[d], NULL); if (r < 0) return r; @@ -3377,8 +3382,14 @@ int unit_patch_contexts(Unit *u) { return -ENOMEM; } + /* If the dynamic user option is on, let's make sure that the unit can't leave its UID/GID + * around in the file system or on IPC objects. Hence enforce a strict sandbox. */ + ec->private_tmp = true; ec->remove_ipc = true; + ec->protect_system = PROTECT_SYSTEM_STRICT; + if (ec->protect_home == PROTECT_HOME_NO) + ec->protect_home = PROTECT_HOME_READ_ONLY; } } diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index be724aed4e..db60d0af7a 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -28,9 +28,10 @@ #include <elfutils/libdwfl.h> #endif +#include "sd-daemon.h" #include "sd-journal.h" #include "sd-login.h" -#include "sd-daemon.h" +#include "sd-messages.h" #include "acl-util.h" #include "alloc-util.h" @@ -93,7 +94,6 @@ typedef enum CoredumpStorage { COREDUMP_STORAGE_NONE, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_JOURNAL, - COREDUMP_STORAGE_BOTH, _COREDUMP_STORAGE_MAX, _COREDUMP_STORAGE_INVALID = -1 } CoredumpStorage; @@ -102,7 +102,6 @@ static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = { [COREDUMP_STORAGE_NONE] = "none", [COREDUMP_STORAGE_EXTERNAL] = "external", [COREDUMP_STORAGE_JOURNAL] = "journal", - [COREDUMP_STORAGE_BOTH] = "both", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage); @@ -128,13 +127,17 @@ static int parse_config(void) { {} }; - return config_parse_many(PKGSYSCONFDIR "/coredump.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf", CONF_PATHS_NULSTR("systemd/coredump.conf.d"), "Coredump\0", config_item_table_lookup, items, false, NULL); } +static inline uint64_t storage_size_max(void) { + return arg_storage == COREDUMP_STORAGE_EXTERNAL ? arg_external_size_max : arg_journal_size_max; +} + static int fix_acl(int fd, uid_t uid) { #ifdef HAVE_ACL @@ -247,7 +250,7 @@ static int maybe_remove_external_coredump(const char *filename, uint64_t size) { /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */ - if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) && + if (arg_storage == COREDUMP_STORAGE_EXTERNAL && size <= arg_external_size_max) return 0; @@ -331,12 +334,13 @@ static int save_external_coredump( /* Is coredumping disabled? Then don't bother saving/processing the coredump. * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */ - log_info("Core dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); + log_info("Resource limits disable core dumping for process %s (%s).", + context[CONTEXT_PID], context[CONTEXT_COMM]); return -EBADSLT; } /* Never store more than the process configured, or than we actually shall keep or process */ - max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max)); + max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max())); r = make_filename(context, &fn); if (r < 0) @@ -349,19 +353,18 @@ static int save_external_coredump( return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); - if (r == -EFBIG) { - log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); - goto fail; - } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { - log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); - goto fail; - } else if (r < 0) { - log_error_errno(r, "Failed to dump coredump to file: %m"); + if (r < 0) { + log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; - } + } else if (r == 1) + log_struct(LOG_INFO, + LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size), + "SIZE_LIMIT=%zu", max_size, + LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE), + NULL); if (fstat(fd, &st) < 0) { - log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); + log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp)); goto fail; } @@ -372,8 +375,7 @@ static int save_external_coredump( #if defined(HAVE_XZ) || defined(HAVE_LZ4) /* If we will remove the coredump anyway, do not compress. */ - if (maybe_remove_external_coredump(NULL, st.st_size) == 0 - && arg_compress) { + if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) { _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; _cleanup_close_ int fd_compressed = -1; @@ -678,7 +680,7 @@ static int submit_coredump( _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL; - uint64_t coredump_size; + uint64_t coredump_size = UINT64_MAX; int r; assert(context); @@ -705,7 +707,9 @@ static int submit_coredump( coredump_filename = strjoina("COREDUMP_FILENAME=", filename); IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename); - } + } else if (arg_storage == COREDUMP_STORAGE_EXTERNAL) + log_info("The core will not be stored: size %zu is greater than %zu (the configured maximum)", + coredump_size, arg_external_size_max); /* Vacuum again, but exclude the coredump we just created */ (void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use); @@ -730,7 +734,9 @@ static int submit_coredump( log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); else log_warning_errno(r, "Failed to generate stack trace: %m"); - } + } else + log_debug("Not generating stack trace: core size %zu is greater than %zu (the configured maximum)", + coredump_size, arg_process_size_max); if (!core_message) #endif @@ -740,18 +746,22 @@ log: IOVEC_SET_STRING(iovec[n_iovec++], core_message); /* Optionally store the entire coredump in the journal */ - if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) && - coredump_size <= arg_journal_size_max) { - size_t sz = 0; - - /* Store the coredump itself in the journal */ - - r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz); - if (r >= 0) { - iovec[n_iovec].iov_base = coredump_data; - iovec[n_iovec].iov_len = sz; - n_iovec++; - } + if (arg_storage == COREDUMP_STORAGE_JOURNAL) { + if (coredump_size <= arg_journal_size_max) { + size_t sz = 0; + + /* Store the coredump itself in the journal */ + + r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz); + if (r >= 0) { + iovec[n_iovec].iov_base = coredump_data; + iovec[n_iovec].iov_len = sz; + n_iovec++; + } else + log_warning_errno(r, "Failed to attach the core to the journal entry: %m"); + } else + log_info("The core will not be stored: size %zu is greater than %zu (the configured maximum)", + coredump_size, arg_journal_size_max); } assert(n_iovec <= n_iovec_allocated); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index bbf8793e57..0e5351e621 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -280,11 +280,10 @@ static int retrieve(const void *data, free(*var); *var = v; - return 0; + return 1; } -static void print_field(FILE* file, sd_journal *j) { - _cleanup_free_ char *value = NULL; +static int print_field(FILE* file, sd_journal *j) { const void *d; size_t l; @@ -293,37 +292,59 @@ static void print_field(FILE* file, sd_journal *j) { assert(arg_field); - SD_JOURNAL_FOREACH_DATA(j, d, l) - retrieve(d, l, arg_field, &value); + /* A (user-specified) field may appear more than once for a given entry. + * We will print all of the occurences. + * This is different below for fields that systemd-coredump uses, + * because they cannot meaningfully appear more than once. + */ + SD_JOURNAL_FOREACH_DATA(j, d, l) { + _cleanup_free_ char *value = NULL; + int r; + + r = retrieve(d, l, arg_field, &value); + if (r < 0) + return r; + if (r > 0) + fprintf(file, "%s\n", value); + } - if (value) - fprintf(file, "%s\n", value); + return 0; } +#define RETRIEVE(d, l, name, arg) \ + { \ + int _r = retrieve(d, l, name, &arg); \ + if (_r < 0) \ + return _r; \ + if (_r > 0) \ + continue; \ + } + static int print_list(FILE* file, sd_journal *j, int had_legend) { _cleanup_free_ char *pid = NULL, *uid = NULL, *gid = NULL, *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, - *filename = NULL; + *filename = NULL, *coredump = NULL; const void *d; size_t l; usec_t t; char buf[FORMAT_TIMESTAMP_MAX]; int r; - bool present; + const char *present; assert(file); assert(j); SD_JOURNAL_FOREACH_DATA(j, d, l) { - retrieve(d, l, "COREDUMP_PID", &pid); - retrieve(d, l, "COREDUMP_UID", &uid); - retrieve(d, l, "COREDUMP_GID", &gid); - retrieve(d, l, "COREDUMP_SIGNAL", &sgnl); - retrieve(d, l, "COREDUMP_EXE", &exe); - retrieve(d, l, "COREDUMP_COMM", &comm); - retrieve(d, l, "COREDUMP_CMDLINE", &cmdline); - retrieve(d, l, "COREDUMP_FILENAME", &filename); + RETRIEVE(d, l, "COREDUMP_PID", pid); + RETRIEVE(d, l, "COREDUMP_UID", uid); + RETRIEVE(d, l, "COREDUMP_GID", gid); + RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl); + RETRIEVE(d, l, "COREDUMP_EXE", exe); + RETRIEVE(d, l, "COREDUMP_COMM", comm); + RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline); + RETRIEVE(d, l, "COREDUMP_FILENAME", filename); + RETRIEVE(d, l, "COREDUMP", coredump); } if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) { @@ -336,7 +357,6 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { return log_error_errno(r, "Failed to get realtime timestamp: %m"); format_timestamp(buf, sizeof(buf), t); - present = filename && access(filename, F_OK) == 0; if (!had_legend && !arg_no_legend) fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", @@ -345,16 +365,28 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { 5, "UID", 5, "GID", 3, "SIG", - 1, "PRESENT", + 8, "COREFILE", "EXE"); - fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", + if (filename) + if (access(filename, R_OK) == 0) + present = "present"; + else if (errno == ENOENT) + present = "missing"; + else + present = "error"; + else if (coredump) + present = "journal"; + else + present = "none"; + + fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", FORMAT_TIMESTAMP_WIDTH, buf, 6, strna(pid), 5, strna(uid), 5, strna(gid), 3, strna(sgnl), - 1, present ? "*" : "", + 8, present, strna(exe ?: (comm ?: cmdline))); return 0; @@ -367,7 +399,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { *unit = NULL, *user_unit = NULL, *session = NULL, *boot_id = NULL, *machine_id = NULL, *hostname = NULL, *slice = NULL, *cgroup = NULL, *owner_uid = NULL, - *message = NULL, *timestamp = NULL, *filename = NULL; + *message = NULL, *timestamp = NULL, *filename = NULL, + *coredump = NULL; const void *d; size_t l; int r; @@ -376,25 +409,26 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { assert(j); SD_JOURNAL_FOREACH_DATA(j, d, l) { - retrieve(d, l, "COREDUMP_PID", &pid); - retrieve(d, l, "COREDUMP_UID", &uid); - retrieve(d, l, "COREDUMP_GID", &gid); - retrieve(d, l, "COREDUMP_SIGNAL", &sgnl); - retrieve(d, l, "COREDUMP_EXE", &exe); - retrieve(d, l, "COREDUMP_COMM", &comm); - retrieve(d, l, "COREDUMP_CMDLINE", &cmdline); - retrieve(d, l, "COREDUMP_UNIT", &unit); - retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit); - retrieve(d, l, "COREDUMP_SESSION", &session); - retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid); - retrieve(d, l, "COREDUMP_SLICE", &slice); - retrieve(d, l, "COREDUMP_CGROUP", &cgroup); - retrieve(d, l, "COREDUMP_TIMESTAMP", ×tamp); - retrieve(d, l, "COREDUMP_FILENAME", &filename); - retrieve(d, l, "_BOOT_ID", &boot_id); - retrieve(d, l, "_MACHINE_ID", &machine_id); - retrieve(d, l, "_HOSTNAME", &hostname); - retrieve(d, l, "MESSAGE", &message); + RETRIEVE(d, l, "COREDUMP_PID", pid); + RETRIEVE(d, l, "COREDUMP_UID", uid); + RETRIEVE(d, l, "COREDUMP_GID", gid); + RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl); + RETRIEVE(d, l, "COREDUMP_EXE", exe); + RETRIEVE(d, l, "COREDUMP_COMM", comm); + RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline); + RETRIEVE(d, l, "COREDUMP_UNIT", unit); + RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit); + RETRIEVE(d, l, "COREDUMP_SESSION", session); + RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid); + RETRIEVE(d, l, "COREDUMP_SLICE", slice); + RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup); + RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp); + RETRIEVE(d, l, "COREDUMP_FILENAME", filename); + RETRIEVE(d, l, "COREDUMP", coredump); + RETRIEVE(d, l, "_BOOT_ID", boot_id); + RETRIEVE(d, l, "_MACHINE_ID", machine_id); + RETRIEVE(d, l, "_HOSTNAME", hostname); + RETRIEVE(d, l, "MESSAGE", message); } if (need_space) @@ -477,7 +511,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { if (unit) fprintf(file, " Unit: %s\n", unit); if (user_unit) - fprintf(file, " User Unit: %s\n", unit); + fprintf(file, " User Unit: %s\n", user_unit); if (slice) fprintf(file, " Slice: %s\n", slice); if (session) @@ -505,8 +539,13 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { if (hostname) fprintf(file, " Hostname: %s\n", hostname); - if (filename && access(filename, F_OK) == 0) - fprintf(file, " Coredump: %s\n", filename); + if (filename) + fprintf(file, " Storage: %s%s\n", filename, + access(filename, R_OK) < 0 ? " (inaccessible)" : ""); + else if (coredump) + fprintf(file, " Storage: journal\n"); + else + fprintf(file, " Storage: none\n"); if (message) { _cleanup_free_ char *m = NULL; @@ -534,15 +573,15 @@ static int focus(sd_journal *j) { return r; } -static void print_entry(sd_journal *j, unsigned n_found) { +static int print_entry(sd_journal *j, unsigned n_found) { assert(j); if (arg_action == ACTION_INFO) - print_info(stdout, j, n_found); + return print_info(stdout, j, n_found); else if (arg_field) - print_field(stdout, j); + return print_field(stdout, j); else - print_list(stdout, j, n_found); + return print_list(stdout, j, n_found); } static int dump_list(sd_journal *j) { @@ -561,10 +600,13 @@ static int dump_list(sd_journal *j) { if (r < 0) return r; - print_entry(j, 0); + return print_entry(j, 0); } else { - SD_JOURNAL_FOREACH(j) - print_entry(j, n_found++); + SD_JOURNAL_FOREACH(j) { + r = print_entry(j, n_found++); + if (r < 0) + return r; + } if (!arg_field && n_found <= 0) { log_notice("No coredumps found."); @@ -575,122 +617,142 @@ static int dump_list(sd_journal *j) { return 0; } -static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { +static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) { const char *data; _cleanup_free_ char *filename = NULL; size_t len; - int r; + int r, fd; + _cleanup_close_ int fdt = -1; + char *temp = NULL; - assert((fd >= 0) != !!path); - assert(!!path == !!unlink_temp); + assert(!(file && path)); /* At most one can be specified */ + assert(!!path == !!unlink_temp); /* Those must be specified together */ - /* Prefer uncompressed file to journal (probably cached) to - * compressed file (probably uncached). */ + /* Look for a coredump on disk first. */ r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len); - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m"); - else if (r == 0) + if (r == 0) retrieve(data, len, "COREDUMP_FILENAME", &filename); + else { + if (r != -ENOENT) + return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m"); + /* Check that we can have a COREDUMP field. We still haven't set a high + * data threshold, so we'll get a few kilobytes at most. + */ - if (filename && access(filename, R_OK) < 0) { - log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - "File %s is not readable: %m", filename); - filename = mfree(filename); + r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); + if (r == -ENOENT) + return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk)."); + if (r < 0) + return log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); } - if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) { - if (path) { + if (filename) { + if (access(filename, R_OK) < 0) + return log_error_errno(errno, "File \"%s\" is not readable: %m", filename); + + if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) { *path = filename; filename = NULL; + + return 0; } + } - return 0; - } else { - _cleanup_close_ int fdt = -1; - char *temp = NULL; + if (path) { + const char *vt; - if (fd < 0) { - const char *vt; + /* Create a temporary file to write the uncompressed core to. */ - r = var_tmp_dir(&vt); - if (r < 0) - return log_error_errno(r, "Failed to acquire temporary directory path: %m"); + r = var_tmp_dir(&vt); + if (r < 0) + return log_error_errno(r, "Failed to acquire temporary directory path: %m"); - temp = strjoin(vt, "/coredump-XXXXXX", NULL); - if (!temp) - return log_oom(); + temp = strjoin(vt, "/coredump-XXXXXX", NULL); + if (!temp) + return log_oom(); - fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); - if (fdt < 0) - return log_error_errno(fdt, "Failed to create temporary file: %m"); - log_debug("Created temporary file %s", temp); + fdt = mkostemp_safe(temp); + if (fdt < 0) + return log_error_errno(fdt, "Failed to create temporary file: %m"); + log_debug("Created temporary file %s", temp); - fd = fdt; + fd = fdt; + } else { + /* If neither path or file are specified, we will write to stdout. Let's now check + * if stdout is connected to a tty. We checked that the file exists, or that the + * core might be stored in the journal. In this second case, if we found the entry, + * in all likelyhood we will be able to access the COREDUMP= field. In either case, + * we stop before doing any "real" work, i.e. before starting decompression or + * reading from the file or creating temporary files. + */ + if (!file) { + if (on_tty()) + return log_error_errno(ENOTTY, "Refusing to dump core to tty" + " (use shell redirection or specify --output)."); + file = stdout; } - r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); - if (r == 0) { - ssize_t sz; - - assert(len >= 9); - data += 9; - len -= 9; - - sz = write(fdt, data, len); - if (sz < 0) { - r = log_error_errno(errno, - "Failed to write temporary file: %m"); - goto error; - } - if (sz != (ssize_t) len) { - log_error("Short write to temporary file."); - r = -EIO; - goto error; - } - } else if (filename) { + fd = fileno(file); + } + + if (filename) { #if defined(HAVE_XZ) || defined(HAVE_LZ4) - _cleanup_close_ int fdf; - - fdf = open(filename, O_RDONLY | O_CLOEXEC); - if (fdf < 0) { - r = log_error_errno(errno, - "Failed to open %s: %m", - filename); - goto error; - } + _cleanup_close_ int fdf; - r = decompress_stream(filename, fdf, fd, -1); - if (r < 0) { - log_error_errno(r, "Failed to decompress %s: %m", filename); - goto error; - } -#else - log_error("Cannot decompress file. Compiled without compression support."); - r = -EOPNOTSUPP; + fdf = open(filename, O_RDONLY | O_CLOEXEC); + if (fdf < 0) { + r = log_error_errno(errno, "Failed to open %s: %m", filename); goto error; -#endif - } else { - if (r == -ENOENT) - log_error("Cannot retrieve coredump from journal or disk."); - else - log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); + } + + r = decompress_stream(filename, fdf, fd, -1); + if (r < 0) { + log_error_errno(r, "Failed to decompress %s: %m", filename); goto error; } +#else + log_error("Cannot decompress file. Compiled without compression support."); + r = -EOPNOTSUPP; + goto error; +#endif + } else { + ssize_t sz; + + /* We want full data, nothing truncated. */ + sd_journal_set_data_threshold(j, 0); + + r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); + if (r < 0) + return log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); - if (temp) { - *path = temp; - *unlink_temp = true; + assert(len >= 9); + data += 9; + len -= 9; + + sz = write(fd, data, len); + if (sz < 0) { + r = log_error_errno(errno, "Failed to write output: %m"); + goto error; + } + if (sz != (ssize_t) len) { + log_error("Short write to output."); + r = -EIO; + goto error; } + } - return 0; + if (temp) { + *path = temp; + *unlink_temp = true; + } + return 0; error: - if (temp) { - unlink(temp); - log_debug("Removed temporary file %s", temp); - } - return r; + if (temp) { + unlink(temp); + log_debug("Removed temporary file %s", temp); } + return r; } static int dump_core(sd_journal* j) { @@ -704,17 +766,12 @@ static int dump_core(sd_journal* j) { print_info(arg_output ? stdout : stderr, j, false); - if (on_tty() && !arg_output) { - log_error("Refusing to dump core to tty."); - return -ENOTTY; - } - - r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL); + r = save_core(j, arg_output, NULL, NULL); if (r < 0) - return log_error_errno(r, "Coredump retrieval failed: %m"); + return r; r = sd_journal_previous(j); - if (r >= 0) + if (r > 0) log_warning("More than one entry matches, ignoring rest."); return 0; @@ -760,9 +817,9 @@ static int run_gdb(sd_journal *j) { return -ENOENT; } - r = save_core(j, -1, &path, &unlink_path); + r = save_core(j, NULL, &path, &unlink_path); if (r < 0) - return log_error_errno(r, "Failed to retrieve core: %m"); + return r; pid = fork(); if (pid < 0) { @@ -836,9 +893,6 @@ int main(int argc, char *argv[]) { } } - /* We want full data, nothing truncated. */ - sd_journal_set_data_threshold(j, 0); - SET_FOREACH(match, matches, it) { r = sd_journal_add_match(j, match, strlen(match)); if (r != 0) { diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index 4795324667..07c57fb567 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -278,7 +278,7 @@ static int set_hostname(sd_bus *bus, char **args, unsigned n) { /* Now that we set the pretty hostname, let's clean up the parameter and use that as static * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping - * multiple dots, and and dropping weird chars. Note that we clean the name up only if we also are + * multiple dots, and dropping weird chars. Note that we clean the name up only if we also are * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what * he does and pass the name as-is. */ h = strdup(hostname); diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 080a2cd138..197f905b7d 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -451,7 +451,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * r = context_update_kernel_hostname(c); if (r < 0) { log_error_errno(r, "Failed to set host name: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m"); } log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME])); @@ -512,13 +512,13 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ r = context_update_kernel_hostname(c); if (r < 0) { log_error_errno(r, "Failed to set host name: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m"); } r = context_write_data_static_hostname(c); if (r < 0) { log_error_errno(r, "Failed to write static host name: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m"); } log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME])); @@ -593,7 +593,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess r = context_write_data_machine_info(c); if (r < 0) { log_error_errno(r, "Failed to write machine info: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m"); } log_info("Changed %s to '%s'", diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c index e12cd93d1c..be4ef5f9e9 100644 --- a/src/hwdb/hwdb.c +++ b/src/hwdb/hwdb.c @@ -85,6 +85,8 @@ struct trie_child_entry { struct trie_value_entry { size_t key_off; size_t value_off; + size_t filename_off; + size_t line_number; }; static int trie_children_cmp(const void *v1, const void *v2) { @@ -157,9 +159,11 @@ static int trie_values_cmp(const void *v1, const void *v2, void *arg) { } static int trie_node_add_value(struct trie *trie, struct trie_node *node, - const char *key, const char *value) { - ssize_t k, v; + const char *key, const char *value, + const char *filename, size_t line_number) { + ssize_t k, v, fn; struct trie_value_entry *val; + int r; k = strbuf_add_string(trie->strings, key, strlen(key)); if (k < 0) @@ -167,6 +171,9 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node, v = strbuf_add_string(trie->strings, value, strlen(value)); if (v < 0) return v; + fn = strbuf_add_string(trie->strings, filename, strlen(filename)); + if (v < 0) + return v; if (node->values_count) { struct trie_value_entry search = { @@ -176,8 +183,20 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node, val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); if (val) { + /* + * At this point we have 2 identical properties on the same match-string. We + * strictly order them by filename+line-number, since we know the dynamic + * runtime lookup does the same for multiple matching nodes. + */ + r = strcmp(filename, trie->strings->buf + val->filename_off); + if (r < 0 || + (r == 0 && line_number < val->line_number)) + return 0; + /* replace existing earlier key with new value */ val->value_off = v; + val->filename_off = fn; + val->line_number = line_number; return 0; } } @@ -190,13 +209,16 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node, node->values = val; node->values[node->values_count].key_off = k; node->values[node->values_count].value_off = v; + node->values[node->values_count].filename_off = fn; + node->values[node->values_count].line_number = line_number; node->values_count++; qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); return 0; } static int trie_insert(struct trie *trie, struct trie_node *node, const char *search, - const char *key, const char *value) { + const char *key, const char *value, + const char *filename, uint64_t line_number) { size_t i = 0; int err = 0; @@ -250,7 +272,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se c = search[i]; if (c == '\0') - return trie_node_add_value(trie, node, key, value); + return trie_node_add_value(trie, node, key, value, filename, line_number); child = node_lookup(node, c); if (!child) { @@ -274,7 +296,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se return err; } - return trie_node_add_value(trie, child, key, value); + return trie_node_add_value(trie, child, key, value, filename, line_number); } node = child; @@ -303,7 +325,7 @@ static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { for (i = 0; i < node->children_count; i++) trie->strings_off += sizeof(struct trie_child_entry_f); for (i = 0; i < node->values_count; i++) - trie->strings_off += sizeof(struct trie_value_entry_f); + trie->strings_off += sizeof(struct trie_value_entry2_f); } static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { @@ -349,12 +371,14 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { /* append values array */ for (i = 0; i < node->values_count; i++) { - struct trie_value_entry_f v = { + struct trie_value_entry2_f v = { .key_off = htole64(trie->strings_off + node->values[i].key_off), .value_off = htole64(trie->strings_off + node->values[i].value_off), + .filename_off = htole64(trie->strings_off + node->values[i].filename_off), + .line_number = htole64(node->values[i].line_number), }; - fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); + fwrite(&v, sizeof(struct trie_value_entry2_f), 1, trie->f); trie->values_count++; } @@ -375,7 +399,7 @@ static int trie_store(struct trie *trie, const char *filename) { .header_size = htole64(sizeof(struct trie_header_f)), .node_size = htole64(sizeof(struct trie_node_f)), .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), - .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), + .value_entry_size = htole64(sizeof(struct trie_value_entry2_f)), }; int err; @@ -431,14 +455,15 @@ static int trie_store(struct trie *trie, const char *filename) { log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", t.children_count * sizeof(struct trie_child_entry_f), t.children_count); log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.values_count * sizeof(struct trie_value_entry_f), t.values_count); + t.values_count * sizeof(struct trie_value_entry2_f), t.values_count); log_debug("string store: %8zu bytes", trie->strings->len); log_debug("strings start: %8"PRIu64, t.strings_off); return 0; } -static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename) { +static int insert_data(struct trie *trie, char **match_list, char *line, + const char *filename, size_t line_number) { char *value, **entry; value = strchr(line, '='); @@ -460,7 +485,7 @@ static int insert_data(struct trie *trie, char **match_list, char *line, const c } STRV_FOREACH(entry, match_list) - trie_insert(trie, trie->root, *entry, line, value); + trie_insert(trie, trie->root, *entry, line, value, filename, line_number); return 0; } @@ -474,6 +499,7 @@ static int import_file(struct trie *trie, const char *filename) { _cleanup_fclose_ FILE *f = NULL; char line[LINE_MAX]; _cleanup_strv_free_ char **match_list = NULL; + size_t line_number = 0; char *match = NULL; int r; @@ -485,6 +511,8 @@ static int import_file(struct trie *trie, const char *filename) { size_t len; char *pos; + ++line_number; + /* comment line */ if (line[0] == '#') continue; @@ -546,7 +574,7 @@ static int import_file(struct trie *trie, const char *filename) { /* first data */ state = HW_DATA; - insert_data(trie, match_list, line, filename); + insert_data(trie, match_list, line, filename, line_number); break; case HW_DATA: @@ -564,7 +592,7 @@ static int import_file(struct trie *trie, const char *filename) { break; } - insert_data(trie, match_list, line, filename); + insert_data(trie, match_list, line, filename, line_number); break; }; } diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index c2b5a5f205..7325adee8f 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -475,20 +475,20 @@ static int request_handler_entries( r = open_journal(m); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m"); if (request_parse_accept(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header."); if (request_parse_range(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header."); if (request_parse_arguments(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments."); if (m->discrete) { if (!m->cursor) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification."); m->n_entries = 1; m->n_entries_set = true; @@ -501,7 +501,7 @@ static int request_handler_entries( else if (m->n_skip < 0) r = sd_journal_seek_tail(m->journal); if (r < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal."); response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL); if (!response) @@ -633,14 +633,14 @@ static int request_handler_fields( r = open_journal(m); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m"); if (request_parse_accept(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header."); r = sd_journal_query_unique(m->journal, field); if (r < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.\n"); + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields."); response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL); if (!response) @@ -699,10 +699,10 @@ static int request_handler_file( fd = open(path, O_RDONLY|O_CLOEXEC); if (fd < 0) - return mhd_respondf(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path); + return mhd_respondf(connection, errno, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m", path); if (fstat(fd, &st) < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n"); + return mhd_respondf(connection, errno, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m"); response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0); if (!response) @@ -766,15 +766,15 @@ static int request_handler_machine( r = open_journal(m); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m"); r = sd_id128_get_machine(&mid); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %m"); r = sd_id128_get_boot(&bid); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %m"); hostname = gethostname_malloc(); if (!hostname) @@ -782,11 +782,11 @@ static int request_handler_machine( r = sd_journal_get_usage(m->journal, &usage); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m"); r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to); if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m"); if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT) (void) parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL); @@ -844,8 +844,7 @@ static int request_handler( assert(method); if (!streq(method, "GET")) - return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, - "Unsupported method.\n"); + return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method."); if (!*connection_cls) { @@ -875,7 +874,7 @@ static int request_handler( if (streq(url, "/machine")) return request_handler_machine(connection, *connection_cls); - return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.\n"); + return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found."); } static void help(void) { diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 80e2adb100..d86c3681b1 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -127,6 +127,10 @@ static int spawn_child(const char* child, char** argv) { if (r < 0) log_warning_errno(errno, "Failed to close write end of pipe: %m"); + r = fd_nonblock(fd[0], true); + if (r < 0) + log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m"); + return fd[0]; } @@ -524,13 +528,12 @@ static int process_http_upload( log_warning("Failed to process data for connection %p", connection); if (r == -E2BIG) return mhd_respondf(connection, - MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, - "Entry is too large, maximum is %u bytes.\n", - DATA_SIZE_MAX); + r, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, + "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes."); else return mhd_respondf(connection, - MHD_HTTP_UNPROCESSABLE_ENTITY, - "Processing failed: %s.", strerror(-r)); + r, MHD_HTTP_UNPROCESSABLE_ENTITY, + "Processing failed: %m."); } } @@ -541,13 +544,14 @@ static int process_http_upload( remaining = source_non_empty(source); if (remaining > 0) { - log_warning("Premature EOFbyte. %zu bytes lost.", remaining); - return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED, + log_warning("Premature EOF byte. %zu bytes lost.", remaining); + return mhd_respondf(connection, + 0, MHD_HTTP_EXPECTATION_FAILED, "Premature EOF. %zu bytes of trailing data not processed.", remaining); } - return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n"); + return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK."); }; static int request_handler( @@ -577,19 +581,16 @@ static int request_handler( *connection_cls); if (!streq(method, "POST")) - return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, - "Unsupported method.\n"); + return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method."); if (!streq(url, "/upload")) - return mhd_respond(connection, MHD_HTTP_NOT_FOUND, - "Not found.\n"); + return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found."); header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type"); if (!header || !streq(header, "application/vnd.fdo.journal")) return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE, - "Content-Type: application/vnd.fdo.journal" - " is required.\n"); + "Content-Type: application/vnd.fdo.journal is required."); { const union MHD_ConnectionInfo *ci; @@ -599,7 +600,7 @@ static int request_handler( if (!ci) { log_error("MHD_get_connection_info failed: cannot get remote fd"); return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - "Cannot check remote address"); + "Cannot check remote address."); } fd = ci->connect_fd; @@ -614,7 +615,7 @@ static int request_handler( r = getpeername_pretty(fd, false, &hostname); if (r < 0) return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - "Cannot check remote hostname"); + "Cannot check remote hostname."); } assert(hostname); @@ -623,8 +624,7 @@ static int request_handler( if (r == -ENOMEM) return respond_oom(connection); else if (r < 0) - return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - strerror(-r)); + return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "%m"); hostname = NULL; return MHD_YES; @@ -1198,7 +1198,7 @@ static int parse_config(void) { { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, {}}; - return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf", CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), "Remote\0", config_item_table_lookup, items, false, NULL); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 4647cfdeb3..c0f967ab94 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -542,7 +542,7 @@ static int parse_config(void) { { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, {}}; - return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf", CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), "Upload\0", config_item_table_lookup, items, false, NULL); diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c index 2f16b02e9a..cae10203c6 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/journal-remote/microhttpd-util.c @@ -48,7 +48,7 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) { static int mhd_respond_internal(struct MHD_Connection *connection, enum MHD_RequestTerminationCode code, - char *buffer, + const char *buffer, size_t size, enum MHD_ResponseMemoryMode mode) { struct MHD_Response *response; @@ -56,7 +56,7 @@ static int mhd_respond_internal(struct MHD_Connection *connection, assert(connection); - response = MHD_create_response_from_buffer(size, buffer, mode); + response = MHD_create_response_from_buffer(size, (char*) buffer, mode); if (!response) return MHD_NO; @@ -72,19 +72,25 @@ int mhd_respond(struct MHD_Connection *connection, enum MHD_RequestTerminationCode code, const char *message) { + const char *fmt; + + fmt = strjoina(message, "\n"); + return mhd_respond_internal(connection, code, - (char*) message, strlen(message), + fmt, strlen(message) + 1, MHD_RESPMEM_PERSISTENT); } int mhd_respond_oom(struct MHD_Connection *connection) { - return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n"); + return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory."); } int mhd_respondf(struct MHD_Connection *connection, + int error, enum MHD_RequestTerminationCode code, const char *format, ...) { + const char *fmt; char *m; int r; va_list ap; @@ -92,8 +98,12 @@ int mhd_respondf(struct MHD_Connection *connection, assert(connection); assert(format); + if (error < 0) + error = -error; + errno = -error; + fmt = strjoina(format, "\n"); va_start(ap, format); - r = vasprintf(&m, format, ap); + r = vasprintf(&m, fmt, ap); va_end(ap); if (r < 0) diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index ea160f212b..af26ab69fe 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -39,8 +39,9 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0); #define respond_oom(connection) log_oom(), mhd_respond_oom(connection) int mhd_respondf(struct MHD_Connection *connection, + int error, unsigned code, - const char *format, ...) _printf_(3,4); + const char *format, ...) _printf_(4,5); int mhd_respond(struct MHD_Connection *connection, unsigned code, diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 7504326bff..349ef74e81 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -333,8 +333,13 @@ JournalFile* journal_file_close(JournalFile *f) { #ifdef HAVE_GCRYPT /* Write the final tag */ - if (f->seal && f->writable) - journal_file_append_tag(f); + if (f->seal && f->writable) { + int r; + + r = journal_file_append_tag(f); + if (r < 0) + log_error_errno(r, "Failed to append tag when closing journal: %m"); + } #endif if (f->post_change_timer) { @@ -1369,6 +1374,12 @@ static int journal_file_append_data( if (r < 0) return r; +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); + if (r < 0) + return r; +#endif + /* The linking might have altered the window, so let's * refresh our pointer */ r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); @@ -1393,12 +1404,6 @@ static int journal_file_append_data( fo->field.head_data_offset = le64toh(p); } -#ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); - if (r < 0) - return r; -#endif - if (ret) *ret = o; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 4105abfccc..9e4d8a28a5 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -118,6 +118,11 @@ static void flush_progress(void) { log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \ } while (0) +#define error_errno(_offset, error, _fmt, ...) do { \ + flush_progress(); \ + log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \ + } while (0) + static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) { uint64_t i; @@ -168,8 +173,8 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o le64toh(o->object.size) - offsetof(Object, data.payload), &b, &alloc, &b_size, 0); if (r < 0) { - error(offset, "%s decompression failed: %s", - object_compressed_to_string(compression), strerror(-r)); + error_errno(offset, r, "%s decompression failed: %m", + object_compressed_to_string(compression)); return r; } @@ -912,7 +917,7 @@ int journal_file_verify( r = journal_file_object_verify(f, p, o); if (r < 0) { - error(p, "Invalid object contents: %s", strerror(-r)); + error_errno(p, r, "Invalid object contents: %m"); goto fail; } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 381e219390..4350925fb0 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1632,7 +1632,7 @@ static int setup_keys(void) { n /= arg_interval; safe_close(fd); - fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC); + fd = mkostemp_safe(k); if (fd < 0) { r = log_error_errno(fd, "Failed to open %s: %m", k); goto finish; @@ -1684,9 +1684,9 @@ static int setup_keys(void) { "at a safe location and should not be saved locally on disk.\n" "\n\t%s", ansi_highlight(), ansi_normal(), + p, ansi_highlight(), ansi_normal(), - ansi_highlight_red(), - p); + ansi_highlight_red()); fflush(stderr); } for (i = 0; i < seed_size; i++) { diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index fce799a6ce..f48639cf58 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -190,7 +190,7 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) { if (k <= 20) return burst; - burst = (burst * (k-20)) / 4; + burst = (burst * (k-16)) / 4; /* * Example: @@ -261,7 +261,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u return 1 + s; } - if (p->num <= burst) { + if (p->num < burst) { p->num++; return 1; } diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 3507910919..a762558e3d 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1474,7 +1474,7 @@ static int server_parse_proc_cmdline(Server *s) { static int server_parse_config_file(Server *s) { assert(s); - return config_parse_many(PKGSYSCONFDIR "/journald.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf", CONF_PATHS_NULSTR("systemd/journald.conf.d"), "Journal\0", config_item_perf_lookup, journald_gperf_lookup, @@ -1587,7 +1587,7 @@ static int dispatch_notify_event(sd_event_source *es, int fd, uint32_t revents, assert(s->notify_fd == fd); /* The $NOTIFY_SOCKET is writable again, now send exactly one - * message on it. Either it's the wtachdog event, the initial + * message on it. Either it's the watchdog event, the initial * READY=1 event or an stdout stream event. If there's nothing * to write anymore, turn our event source off. The next time * there's something to send it will be turned on again. */ diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 4ad16ee41c..bc092f3c12 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -393,6 +393,9 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { p = s->buffer; remaining = s->length; + + /* XXX: This function does nothing if (s->length == 0) */ + for (;;) { char *end; size_t skip; diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index 898c876450..b7d9e7bffa 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -55,7 +55,7 @@ static Hashmap * test_import(const char* contents, ssize_t size, int code) { assert_se(h = hashmap_new(&catalog_hash_ops)); - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(write(fd, contents, size) == size); @@ -182,7 +182,7 @@ static void test_catalog_update(void) { static char name[] = "/tmp/test-catalog.XXXXXX"; int r; - r = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + r = mkostemp_safe(name); assert_se(r >= 0); database = name; diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 68c9a4d76c..00e5222a1c 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -167,7 +167,7 @@ static void test_compress_stream(int compression, log_debug("/* test compression */"); - assert_se((dst = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC)) >= 0); + assert_se((dst = mkostemp_safe(pattern)) >= 0); assert_se(compress(src, dst, -1) == 0); @@ -178,7 +178,7 @@ static void test_compress_stream(int compression, log_debug("/* test decompression */"); - assert_se((dst2 = mkostemp_safe(pattern2, O_RDWR|O_CLOEXEC)) >= 0); + assert_se((dst2 = mkostemp_safe(pattern2)) >= 0); assert_se(stat(srcfile, &st) == 0); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index 5e063f4d04..35cae23bf8 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -36,10 +36,9 @@ static bool arg_keep = false; -noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) { - log_internal(LOG_CRIT, 0, file, line, func, - "'%s' failed at %s:%u (%s): %s.", - text, file, line, func, strerror(eno)); +noreturn static void log_assert_errno(const char *text, int error, const char *file, int line, const char *func) { + log_internal(LOG_CRIT, error, file, line, func, + "'%s' failed at %s:%u (%s): %m", text, file, line, func); abort(); } diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c index 009aabf55e..0ad49aeb5f 100644 --- a/src/journal/test-mmap-cache.c +++ b/src/journal/test-mmap-cache.c @@ -36,15 +36,15 @@ int main(int argc, char *argv[]) { assert_se(m = mmap_cache_new()); - x = mkostemp_safe(px, O_RDWR|O_CLOEXEC); + x = mkostemp_safe(px); assert_se(x >= 0); unlink(px); - y = mkostemp_safe(py, O_RDWR|O_CLOEXEC); + y = mkostemp_safe(py); assert_se(y >= 0); unlink(py); - z = mkostemp_safe(pz, O_RDWR|O_CLOEXEC); + z = mkostemp_safe(pz); assert_se(z >= 0); unlink(pz); diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install index c66bcfc092..0c0ee718ac 100644 --- a/src/kernel-install/kernel-install +++ b/src/kernel-install/kernel-install @@ -19,6 +19,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see <http://www.gnu.org/licenses/>. +SKIP_REMAINING=77 + usage() { echo "Usage:" @@ -123,7 +125,11 @@ case $COMMAND in for f in "${PLUGINS[@]}"; do if [[ -x $f ]]; then "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE" - ((ret+=$?)) + x=$? + if [[ $x == $SKIP_REMAINING ]]; then + return 0 + fi + ((ret+=$x)) fi done ;; @@ -132,7 +138,11 @@ case $COMMAND in for f in "${PLUGINS[@]}"; do if [[ -x $f ]]; then "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS" - ((ret+=$?)) + x=$? + if [[ $x == $SKIP_REMAINING ]]; then + return 0 + fi + ((ret+=$x)) fi done diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index a69193aa32..9cc28ed564 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -52,6 +52,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER_MAPPING, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_GROUP_MAPPING, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), @@ -64,6 +66,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), + SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY), SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), @@ -82,6 +85,25 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY), SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "FORMERR", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "SERVFAIL", EHOSTDOWN), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NXDOMAIN", ENXIO), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTIMP", ENOSYS), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "REFUSED", EACCES), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "YXDOMAIN", EEXIST), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "YRRSET", EEXIST), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NXRRSET", ENOENT), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTAUTH", EACCES), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTZONE", EREMOTE), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADVERS", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADKEY", EKEYREJECTED), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADTIME", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADMODE", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADNAME", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADALG", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADTRUNC", EBADMSG), + SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADCOOKIE", EBADR), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index eb042e9c81..2c3f591053 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -2003,8 +2003,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (streq_ptr(argv[optind], "monitor") || - streq_ptr(argv[optind], "capture")) { + if (STRPTR_IN_SET(argv[optind], "monitor", "capture")) { r = sd_bus_set_monitor(bus, true); if (r < 0) { diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index 048c0d19e2..fc60830059 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -351,7 +351,7 @@ finish: static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { bool *x = userdata; - log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m))); + log_error_errno(sd_bus_message_get_errno(m), "Quit callback: %m"); *x = 1; return 1; diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index 82237af115..6fdcfa4128 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -27,12 +27,17 @@ int main(int argc, char *argv[]) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; int r; + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + if (cg_all_unified() == -ENOMEDIUM) { - puts("Skipping test: /sys/fs/cgroup/ not available"); + log_info("Skipping test: /sys/fs/cgroup/ not available"); return EXIT_TEST_SKIP; } r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); + log_full_errno(r < 0 ? LOG_ERR : LOG_DEBUG, r, "sd_bus_creds_new_from_pid: %m"); assert_se(r >= 0); bus_creds_dump(creds, NULL, true); diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 0c4ad966bd..411453e08d 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -36,6 +36,7 @@ #include "parse-util.h" #include "path-util.h" #include "set.h" +#include "socket-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -629,9 +630,9 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { if (r < 0) return r; - sk = socket(PF_INET, SOCK_DGRAM, 0); + sk = socket_ioctl_fd(); if (sk < 0) - return -errno; + return sk; r = ioctl(sk, SIOCGIFNAME, &ifr); if (r < 0) diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h index 8ffb5e5c74..4fff94ec76 100644 --- a/src/libsystemd/sd-hwdb/hwdb-internal.h +++ b/src/libsystemd/sd-hwdb/hwdb-internal.h @@ -70,3 +70,11 @@ struct trie_value_entry_f { le64_t key_off; le64_t value_off; } _packed_; + +/* v2 extends v1 with filename and line-number */ +struct trie_value_entry2_f { + le64_t key_off; + le64_t value_off; + le64_t filename_off; + le64_t line_number; +} _packed_; diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c index 062fa97b17..488e101ea8 100644 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ b/src/libsystemd/sd-hwdb/sd-hwdb.c @@ -97,15 +97,20 @@ static void linebuf_rem_char(struct linebuf *buf) { linebuf_rem(buf, 1); } -static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) { - return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size)); +static const struct trie_child_entry_f *trie_node_child(sd_hwdb *hwdb, const struct trie_node_f *node, size_t idx) { + const char *base = (const char *)node; + + base += le64toh(hwdb->head->node_size); + base += idx * le64toh(hwdb->head->child_entry_size); + return (const struct trie_child_entry_f *)base; } -static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) { +static const struct trie_value_entry_f *trie_node_value(sd_hwdb *hwdb, const struct trie_node_f *node, size_t idx) { const char *base = (const char *)node; base += le64toh(hwdb->head->node_size); base += node->children_count * le64toh(hwdb->head->child_entry_size); + base += idx * le64toh(hwdb->head->value_entry_size); return (const struct trie_value_entry_f *)base; } @@ -129,19 +134,20 @@ static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_ struct trie_child_entry_f search; search.c = c; - child = bsearch(&search, trie_node_children(hwdb, node), node->children_count, + child = bsearch(&search, (const char *)node + le64toh(hwdb->head->node_size), node->children_count, le64toh(hwdb->head->child_entry_size), trie_children_cmp_f); if (child) return trie_node_from_off(hwdb, child->child_off); return NULL; } -static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) { +static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *entry) { + const char *key; int r; assert(hwdb); - assert(key); - assert(value); + + key = trie_string(hwdb, entry->key_off); /* * Silently ignore all properties which do not start with a @@ -152,11 +158,25 @@ static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) key++; + if (le64toh(hwdb->head->value_entry_size) >= sizeof(struct trie_value_entry2_f)) { + const struct trie_value_entry2_f *old, *entry2; + + entry2 = (const struct trie_value_entry2_f *)entry; + old = ordered_hashmap_get(hwdb->properties, key); + if (old) { + /* on duplicates, we order by filename and line-number */ + r = strcmp(trie_string(hwdb, entry2->filename_off), trie_string(hwdb, old->filename_off)); + if (r < 0 || + (r == 0 && entry2->line_number < old->line_number)) + return 0; + } + } + r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops); if (r < 0) return r; - r = ordered_hashmap_replace(hwdb->properties, key, (char*)value); + r = ordered_hashmap_replace(hwdb->properties, key, (void *)entry); if (r < 0) return r; @@ -177,7 +197,7 @@ static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t linebuf_add(buf, prefix + p, len); for (i = 0; i < node->children_count; i++) { - const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i]; + const struct trie_child_entry_f *child = trie_node_child(hwdb, node, i); linebuf_add_char(buf, child->c); err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search); @@ -188,8 +208,7 @@ static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0) for (i = 0; i < le64toh(node->values_count); i++) { - err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off), - trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off)); + err = hwdb_add_property(hwdb, trie_node_value(hwdb, node, i)); if (err < 0) return err; } @@ -254,8 +273,7 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) { size_t n; for (n = 0; n < le64toh(node->values_count); n++) { - err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off), - trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off)); + err = hwdb_add_property(hwdb, trie_node_value(hwdb, node, n)); if (err < 0) return err; } @@ -410,7 +428,7 @@ static int properties_prepare(sd_hwdb *hwdb, const char *modalias) { } _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) { - const char *value; + const struct trie_value_entry_f *entry; int r; assert_return(hwdb, -EINVAL); @@ -422,11 +440,11 @@ _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, c if (r < 0) return r; - value = ordered_hashmap_get(hwdb->properties, key); - if (!value) + entry = ordered_hashmap_get(hwdb->properties, key); + if (!entry) return -ENOENT; - *_value = value; + *_value = trie_string(hwdb, entry->value_off); return 0; } @@ -449,8 +467,8 @@ _public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) { } _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) { + const struct trie_value_entry_f *entry; const void *k; - void *v; assert_return(hwdb, -EINVAL); assert_return(key, -EINVAL); @@ -459,12 +477,12 @@ _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **val if (hwdb->properties_modified) return -EAGAIN; - ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k); + ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, (void **)&entry, &k); if (!k) return 0; *key = k; - *value = v; + *value = trie_string(hwdb, entry->value_off); return 1; } diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 566a050432..1c10dd55a7 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -21,6 +21,7 @@ #include <sys/socket.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> +#include <linux/can/netlink.h> #include <linux/in6.h> #include <linux/veth.h> #include <linux/if_bridge.h> @@ -303,49 +304,48 @@ static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6", [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", [NL_UNION_LINK_INFO_DATA_VRF] = "vrf", + [NL_UNION_LINK_INFO_DATA_VCAN] = "vcan", }; DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); static const NLTypeSystem rtnl_link_info_data_type_systems[] = { - [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types), - .types = rtnl_link_info_data_bond_types }, - [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types), - .types = rtnl_link_info_data_bridge_types }, - [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types), - .types = rtnl_link_info_data_vlan_types }, - [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types), - .types = rtnl_link_info_data_veth_types }, - [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), - .types = rtnl_link_info_data_macvlan_types }, - [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), - .types = rtnl_link_info_data_macvlan_types }, - [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), - .types = rtnl_link_info_data_ipvlan_types }, - [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), - .types = rtnl_link_info_data_vxlan_types }, - [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), - .types = rtnl_link_info_data_iptun_types }, - [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types), + .types = rtnl_link_info_data_bond_types }, + [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types), + .types = rtnl_link_info_data_bridge_types }, + [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types), + .types = rtnl_link_info_data_vlan_types }, + [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types), + .types = rtnl_link_info_data_veth_types }, + [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), + .types = rtnl_link_info_data_ipvlan_types }, + [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), + .types = rtnl_link_info_data_vxlan_types }, + [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), + .types = rtnl_link_info_data_iptun_types }, + [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), - .types = rtnl_link_info_data_iptun_types }, - [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), - .types = rtnl_link_info_data_ipvti_types }, - [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), - .types = rtnl_link_info_data_ipvti_types }, - [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types), - .types = rtnl_link_info_data_ip6tnl_types }, - - [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types), - .types = rtnl_link_info_data_vrf_types }, - + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), + .types = rtnl_link_info_data_iptun_types }, + [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), + .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), + .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types), + .types = rtnl_link_info_data_ip6tnl_types }, + [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types), + .types = rtnl_link_info_data_vrf_types }, }; static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index 7c0e598b26..42e96173de 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -87,6 +87,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL, NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, NL_UNION_LINK_INFO_DATA_VRF, + NL_UNION_LINK_INFO_DATA_VCAN, _NL_UNION_LINK_INFO_DATA_MAX, _NL_UNION_LINK_INFO_DATA_INVALID = -1 } NLUnionLinkInfoData; diff --git a/src/locale/localed.c b/src/locale/localed.c index 298f176e40..1cb049e74a 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -334,7 +334,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er r = locale_write_data(c, &settings); if (r < 0) { log_error_errno(r, "Failed to set locale: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m"); } locale_update_system_manager(c, sd_bus_message_get_bus(m)); @@ -403,7 +403,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro r = vconsole_write_data(c); if (r < 0) { log_error_errno(r, "Failed to set virtual console keymap: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %m"); } log_info("Changed virtual console keymap to '%s' toggle '%s'", @@ -592,7 +592,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err r = x11_write_data(c); if (r < 0) { log_error_errno(r, "Failed to set X11 keyboard layout: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r)); + return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %m"); } log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", diff --git a/src/login/logind-session.c b/src/login/logind-session.c index b6da237397..ba1bcc2630 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -611,7 +611,7 @@ static int session_stop_scope(Session *s, bool force) { return 0; /* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything - * that is left in in the scope is "left-over". Informing systemd about this has the benefit that it will log + * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log * when killing any processes left after this point. */ r = manager_abandon_scope(s->manager, s->scope, &error); if (r < 0) diff --git a/src/login/logind-user.c b/src/login/logind-user.c index e0e73b034d..2dc5fa7665 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -354,14 +354,12 @@ static int user_mkdir_runtime_path(User *u) { r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); if (r < 0) { - if (errno != EPERM) { + if (errno != EPERM && errno != EACCES) { r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path); goto fail; } - /* Lacking permissions, maybe - * CAP_SYS_ADMIN-less container? In this case, - * just use a normal directory. */ + log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s, assuming containerized execution, ignoring: %m", u->runtime_path); r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid); if (r < 0) { diff --git a/src/login/logind.c b/src/login/logind.c index bbbf4aef57..a9a06f5e28 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -1002,7 +1002,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us static int manager_parse_config_file(Manager *m) { assert(m); - return config_parse_many(PKGSYSCONFDIR "/logind.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf", CONF_PATHS_NULSTR("systemd/logind.conf.d"), "Login\0", config_item_perf_lookup, logind_gperf_lookup, diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4 index f188a8e548..fe38b24fef 100644 --- a/src/login/systemd-user.m4 +++ b/src/login/systemd-user.m4 @@ -2,11 +2,9 @@ # # Used by systemd --user instances. -account include system-auth - m4_ifdef(`HAVE_SELINUX', session required pam_selinux.so close session required pam_selinux.so nottys open )m4_dnl session required pam_loginuid.so -session include system-auth +session optional pam_systemd.so diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 74e1a349bc..7b9be3b425 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1326,10 +1326,12 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - if (streq(name, ".host")) - log_info("Connected to the local host. Press ^] three times within 1s to exit session."); - else - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + if (!arg_quiet) { + if (streq(name, ".host")) + log_info("Connected to the local host. Press ^] three times within 1s to exit session."); + else + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + } sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); @@ -1353,17 +1355,54 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT if (last_char != '\n') fputc('\n', stdout); - if (machine_died) - log_info("Machine %s terminated.", name); - else if (streq(name, ".host")) - log_info("Connection to the local host terminated."); - else - log_info("Connection to machine %s terminated.", name); + if (!arg_quiet) { + if (machine_died) + log_info("Machine %s terminated.", name); + else if (streq(name, ".host")) + log_info("Connection to the local host terminated."); + else + log_info("Connection to machine %s terminated.", name); + } sd_event_get_exit_code(event, &ret); return ret; } +static int parse_machine_uid(const char *spec, const char **machine, char **uid) { + /* + * Whatever is specified in the spec takes priority over global arguments. + */ + char *_uid = NULL; + const char *_machine = NULL; + + if (spec) { + const char *at; + + at = strchr(spec, '@'); + if (at) { + if (at == spec) + /* Do the same as ssh and refuse "@host". */ + return -EINVAL; + + _machine = at + 1; + _uid = strndup(spec, at - spec); + if (!_uid) + return -ENOMEM; + } else + _machine = spec; + }; + + if (arg_uid && !_uid) { + _uid = strdup(arg_uid); + if (!_uid) + return -ENOMEM; + } + + *uid = _uid; + *machine = isempty(_machine) ? ".host" : _machine; + return 0; +} + static int login_machine(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1439,7 +1478,8 @@ static int shell_machine(int argc, char *argv[], void *userdata) { _cleanup_(sd_event_unrefp) sd_event *event = NULL; int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match, *machine, *path, *uid = NULL; + const char *pty, *match, *machine, *path; + _cleanup_free_ char *uid = NULL; assert(bus); @@ -1470,22 +1510,9 @@ static int shell_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; - - if (arg_uid) - uid = arg_uid; - else if (machine) { - const char *at; - - at = strchr(machine, '@'); - if (at) { - uid = strndupa(machine, at - machine); - machine = at + 1; - } - } - - if (isempty(machine)) - machine = ".host"; + r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid); + if (r < 0) + return log_error_errno(r, "Failed to parse machine specification: %m"); match = strjoina("type='signal'," "sender='org.freedesktop.machine1'," @@ -2602,6 +2629,7 @@ static int clean_images(int argc, char *argv[], void *userdata) { } static int help(int argc, char *argv[], void *userdata) { + pager_open(arg_no_pager, false); printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Send control commands to or query the virtual machine and container\n" diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 5e2462cba2..e40f40a263 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -444,7 +444,9 @@ static int method_register_machine_internal(sd_bus_message *message, bool read_n r = cg_pid_get_unit(m->leader, &m->unit); if (r < 0) { - r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r)); + r = sd_bus_error_set_errnof(error, r, + "Failed to determine unit of process "PID_FMT" : %m", + m->leader); goto fail; } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index d2df9b7560..6f7f41bf7d 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -122,7 +122,7 @@ static void setup_state_to_color(const char *state, const char **on, const char } else if (streq_ptr(state, "configuring")) { *on = ansi_highlight_yellow(); *off = ansi_normal(); - } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { + } else if (STRPTR_IN_SET(state, "failed", "linger")) { *on = ansi_highlight_red(); *off = ansi_normal(); } else diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 5498e352d8..ed52d5e42d 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -571,6 +571,21 @@ int address_configure( address->flags |= IFA_F_PERMANENT; + if (address->home_address) + address->flags |= IFA_F_HOMEADDRESS; + + if (address->duplicate_address_detection) + address->flags |= IFA_F_NODAD; + + if (address->manage_temporary_address) + address->flags |= IFA_F_MANAGETEMPADDR; + + if (address->prefix_route) + address->flags |= IFA_F_NOPREFIXROUTE; + + if (address->autojoin) + address->flags |= IFA_F_MCAUTOJOIN; + r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff)); if (r < 0) return log_error_errno(r, "Could not set flags: %m"); @@ -856,6 +871,50 @@ int config_parse_lifetime(const char *unit, return 0; } +int config_parse_address_flags(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 *network = userdata; + _cleanup_address_free_ Address *n = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); + return 0; + } + + if (streq(lvalue, "HomeAddress")) + n->home_address = r; + else if (streq(lvalue, "DuplicateAddressDetection")) + n->duplicate_address_detection = r; + else if (streq(lvalue, "ManageTemporaryAddress")) + n->manage_temporary_address = r; + else if (streq(lvalue, "PrefixRoute")) + n->prefix_route = r; + else if (streq(lvalue, "AutoJoin")) + n->autojoin = r; + + return 0; +} + bool address_is_ready(const Address *a) { assert(a); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 03c4bea7c6..bc3b4fc7f3 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -53,6 +53,11 @@ struct Address { union in_addr_union in_addr_peer; bool ip_masquerade_done:1; + bool duplicate_address_detection; + bool manage_temporary_address; + bool home_address; + bool prefix_route; + bool autojoin; LIST_FIELDS(Address, addresses); }; @@ -77,3 +82,4 @@ int config_parse_address(const char *unit, const char *filename, unsigned line, 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); int config_parse_lifetime(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_flags(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-conf.c b/src/network/networkd-conf.c index c03e2b2ebf..49bb8c18f6 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -29,7 +29,7 @@ int manager_parse_config_file(Manager *m) { assert(m); - return config_parse_many(PKGSYSCONFDIR "/networkd.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf", CONF_PATHS_NULSTR("systemd/networkd.conf.d"), "DHCP\0", config_item_perf_lookup, networkd_gperf_lookup, diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 12fb8e3fce..76d3d132ea 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -95,6 +95,7 @@ static int link_set_dhcp_routes(Link *link) { route_gw->scope = RT_SCOPE_LINK; route_gw->protocol = RTPROT_DHCP; route_gw->priority = link->network->dhcp_route_metric; + route_gw->table = link->network->dhcp_route_table; r = route_configure(route_gw, link, dhcp4_route_handler); if (r < 0) @@ -106,6 +107,7 @@ static int link_set_dhcp_routes(Link *link) { route->gw.in = gateway; route->prefsrc.in = address; route->priority = link->network->dhcp_route_metric; + route->table = link->network->dhcp_route_table; r = route_configure(route, link, dhcp4_route_handler); if (r < 0) { @@ -136,6 +138,7 @@ static int link_set_dhcp_routes(Link *link) { assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0); assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0); route->priority = link->network->dhcp_route_metric; + route->table = link->network->dhcp_route_table; r = route_configure(route, link, dhcp4_route_handler); if (r < 0) diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index be8aebee2d..ed5a47589e 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -107,20 +107,28 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; sd_netlink *rtnl; int r; + uint8_t flags; + Bridge *bridge; assert(link); + assert(link->network); assert(link->manager); assert(fdb_entry); rtnl = link->manager->rtnl; + bridge = BRIDGE(link->network->bridge); /* create new RTM message */ r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE); if (r < 0) return rtnl_log_create_error(r); - /* only NTF_SELF flag supported. */ - r = sd_rtnl_message_neigh_set_flags(req, NTF_SELF); + if (bridge) + flags = NTF_MASTER; + else + flags = NTF_SELF; + + r = sd_rtnl_message_neigh_set_flags(req, flags); if (r < 0) return rtnl_log_create_error(r); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index aab40a0eb1..d9e060b6cf 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -942,6 +942,19 @@ static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { return sd_dhcp_server_set_ntp(s, addresses, n_addresses); } +static int link_set_bridge_fdb(Link *link) { + FdbEntry *fdb_entry; + int r; + + LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { + r = fdb_entry_configure(link, fdb_entry); + if (r < 0) + return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); + } + + return 0; +} + static int link_enter_set_addresses(Link *link) { Address *ad; int r; @@ -950,6 +963,10 @@ static int link_enter_set_addresses(Link *link) { assert(link->network); assert(link->state != _LINK_STATE_INVALID); + r = link_set_bridge_fdb(link); + if (r < 0) + return r; + link_set_state(link, LINK_STATE_SETTING_ADDRESSES); LIST_FOREACH(addresses, ad, link->network->static_addresses) { @@ -1119,21 +1136,6 @@ static int link_set_bridge_vlan(Link *link) { return r; } -static int link_set_bridge_fdb(Link *link) { - FdbEntry *fdb_entry; - int r = 0; - - LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { - r = fdb_entry_configure(link, fdb_entry); - if (r < 0) { - log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); - break; - } - } - - return r; -} - static int link_set_proxy_arp(Link *link) { const char *p = NULL; int r; @@ -1787,6 +1789,31 @@ static int link_down(Link *link) { return 0; } +static int link_up_can(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + + log_link_debug(link, "Bringing CAN link up"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link flags: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + static int link_handle_bound_to_list(Link *link) { Link *l; Iterator i; @@ -2431,6 +2458,19 @@ static int link_configure(Link *link) { assert(link->network); assert(link->state == LINK_STATE_PENDING); + if (streq_ptr(link->kind, "vcan")) { + + if (!(link->flags & IFF_UP)) { + r = link_up_can(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; + } + /* Drop foreign config, but ignore loopback or critical devices. * We do not want to remove loopback address or addresses used for root NFS. */ if (!(link->flags & IFF_LOOPBACK) && !(link->network->dhcp_critical)) { @@ -2439,10 +2479,6 @@ static int link_configure(Link *link) { return r; } - r = link_set_bridge_fdb(link); - if (r < 0) - return r; - r = link_set_proxy_arp(link); if (r < 0) return r; @@ -2957,7 +2993,8 @@ static int link_carrier_lost(Link *link) { if (r < 0) return r; - if (link->state != LINK_STATE_UNMANAGED) { + if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING)) { + log_link_debug(link, "State is %s, dropping config", link_state_to_string(link->state)); r = link_drop_foreign_config(link); if (r < 0) return r; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index d9c18b32a5..c2b7970623 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -94,7 +94,7 @@ static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { } route->family = AF_INET6; - route->table = RT_TABLE_MAIN; + route->table = link->network->ipv6_accept_ra_route_table; route->protocol = RTPROT_RA; route->pref = preference; route->gw.in6 = gateway; @@ -214,7 +214,7 @@ static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) } route->family = AF_INET6; - route->table = RT_TABLE_MAIN; + route->table = link->network->ipv6_accept_ra_route_table; route->protocol = RTPROT_RA; route->flags = RTM_F_PREFIX; route->dst_prefixlen = prefixlen; @@ -285,7 +285,7 @@ static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { } route->family = AF_INET6; - route->table = RT_TABLE_MAIN; + route->table = link->network->ipv6_accept_ra_route_table; route->protocol = RTPROT_RA; route->pref = preference; route->gw.in6 = gateway; diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index bdbea7d770..002ad94210 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -39,7 +39,7 @@ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, vo return 1; } - log_netdev_debug(netdev, "Bridge parametres set success"); + log_netdev_debug(netdev, "Bridge parameters set success"); return 1; } diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 6dbb627f15..323eaa8032 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -63,8 +63,13 @@ VXLAN.L2MissNotification, config_parse_bool, 0, VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) +VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) diff --git a/src/network/networkd-netdev-vcan.c b/src/network/networkd-netdev-vcan.c new file mode 100644 index 0000000000..bfce6e1962 --- /dev/null +++ b/src/network/networkd-netdev-vcan.c @@ -0,0 +1,25 @@ +/*** + This file is part of systemd. + + Copyright 2016 Susant Sahani + + 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 "networkd-netdev-vcan.h" + +const NetDevVTable vcan_vtable = { + .object_size = sizeof(VCan), + .create_type = NETDEV_CREATE_INDEPENDENT, +}; diff --git a/src/network/networkd-netdev-vcan.h b/src/network/networkd-netdev-vcan.h new file mode 100644 index 0000000000..6ba47fd70e --- /dev/null +++ b/src/network/networkd-netdev-vcan.h @@ -0,0 +1,34 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Susant Sahani + + 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 VCan VCan; + +#include <linux/can/netlink.h> + +#include "networkd-netdev.h" + +struct VCan { + NetDev meta; +}; + +DEFINE_NETDEV_CAST(VCAN, VCan); + +extern const NetDevVTable vcan_vtable; diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c index 724f9861be..706e52b698 100644 --- a/src/network/networkd-netdev-vxlan.c +++ b/src/network/networkd-netdev-vxlan.c @@ -112,6 +112,14 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %m"); + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_TX, v->remote_csum_tx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_REMCSUM_TX attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_RX, v->remote_csum_rx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_REMCSUM_RX attribute: %m"); + r = sd_netlink_message_append_u16(m, IFLA_VXLAN_PORT, htobe16(v->dest_port)); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m"); diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h index 4614c66fd1..3906820afb 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -50,6 +50,8 @@ struct VxLan { bool udpcsum; bool udp6zerocsumtx; bool udp6zerocsumrx; + bool remote_csum_tx; + bool remote_csum_rx; bool group_policy; struct ifla_vxlan_port_range port_range; diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index e7edc366af..a210ba1242 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -34,7 +34,6 @@ #include "string-util.h" const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { - [NETDEV_KIND_BRIDGE] = &bridge_vtable, [NETDEV_KIND_BOND] = &bond_vtable, [NETDEV_KIND_VLAN] = &vlan_vtable, @@ -56,7 +55,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_TAP] = &tap_vtable, [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable, [NETDEV_KIND_VRF] = &vrf_vtable, - + [NETDEV_KIND_VCAN] = &vcan_vtable, }; static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { @@ -81,7 +80,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_TAP] = "tap", [NETDEV_KIND_IP6TNL] = "ip6tnl", [NETDEV_KIND_VRF] = "vrf", - + [NETDEV_KIND_VCAN] = "vcan", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); @@ -516,7 +515,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_netlink_message_close_container(m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); r = sd_netlink_message_close_container(m); if (r < 0) @@ -577,6 +576,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { _cleanup_netdev_unref_ NetDev *netdev = NULL; _cleanup_free_ NetDev *netdev_raw = NULL; _cleanup_fclose_ FILE *file = NULL; + const char *dropin_dirname; int r; assert(manager); @@ -600,11 +600,12 @@ static int netdev_load_one(Manager *manager, const char *filename) { return log_oom(); netdev_raw->kind = _NETDEV_KIND_INVALID; + dropin_dirname = strjoina(basename(filename), ".d"); - r = config_parse(NULL, filename, file, - "Match\0NetDev\0", - config_item_perf_lookup, network_netdev_gperf_lookup, - true, false, true, netdev_raw); + r = config_parse_many(filename, network_dirs, dropin_dirname, + "Match\0NetDev\0", + config_item_perf_lookup, network_netdev_gperf_lookup, + true, netdev_raw); if (r < 0) return r; @@ -620,7 +621,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { return 0; if (netdev_raw->kind == _NETDEV_KIND_INVALID) { - log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename); + log_warning("NetDev has no Kind configured in %s. Ignoring", filename); return 0; } diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index 09863e72b4..31b55e2791 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -56,6 +56,7 @@ typedef enum NetDevKind { NETDEV_KIND_TUN, NETDEV_KIND_TAP, NETDEV_KIND_VRF, + NETDEV_KIND_VCAN, _NETDEV_KIND_MAX, _NETDEV_KIND_INVALID = -1 } NetDevKind; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index b96f0b7210..5587961b9f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -70,6 +70,11 @@ Address.Peer, config_parse_address, Address.Broadcast, config_parse_broadcast, 0, 0 Address.Label, config_parse_label, 0, 0 Address.PreferredLifetime, config_parse_lifetime, 0, 0 +Address.HomeAddress, config_parse_address_flags, 0, 0 +Address.DuplicateAddressDetection, config_parse_address_flags, 0, 0 +Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0 +Address.PrefixRoute, config_parse_address_flags, 0, 0 +Address.AutoJoin, config_parse_address_flags, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 @@ -92,10 +97,12 @@ DHCP.VendorClassIdentifier, config_parse_string, DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) +DHCP.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, dhcp_route_table) DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) +IPv6AcceptRA.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, ipv6_accept_ra_route_table) DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 49faba5b12..584cb96979 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -40,6 +40,7 @@ static int network_load_one(Manager *manager, const char *filename) { _cleanup_network_free_ Network *network = NULL; _cleanup_fclose_ FILE *file = NULL; char *d; + const char *dropin_dirname; Route *route; Address *address; int r; @@ -110,6 +111,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->dhcp_send_hostname = true; network->dhcp_route_metric = DHCP_ROUTE_METRIC; network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID; + network->dhcp_route_table = RT_TABLE_MAIN; network->dhcp_server_emit_dns = true; network->dhcp_server_emit_ntp = true; @@ -136,22 +138,25 @@ static int network_load_one(Manager *manager, const char *filename) { network->proxy_arp = -1; network->arp = -1; network->ipv6_accept_ra_use_dns = true; - - r = config_parse(NULL, filename, file, - "Match\0" - "Link\0" - "Network\0" - "Address\0" - "Route\0" - "DHCP\0" - "DHCPv4\0" /* compat */ - "DHCPServer\0" - "IPv6AcceptRA\0" - "Bridge\0" - "BridgeFDB\0" - "BridgeVLAN\0", - config_item_perf_lookup, network_network_gperf_lookup, - false, false, true, network); + network->ipv6_accept_ra_route_table = RT_TABLE_MAIN; + + dropin_dirname = strjoina(network->name, ".network.d"); + + r = config_parse_many(filename, network_dirs, dropin_dirname, + "Match\0" + "Link\0" + "Network\0" + "Address\0" + "Route\0" + "DHCP\0" + "DHCPv4\0" /* compat */ + "DHCPServer\0" + "IPv6AcceptRA\0" + "Bridge\0" + "BridgeFDB\0" + "BridgeVLAN\0", + config_item_perf_lookup, network_network_gperf_lookup, + false, network); if (r < 0) return r; @@ -479,9 +484,10 @@ int config_parse_netdev(const char *unit, case NETDEV_KIND_MACVTAP: case NETDEV_KIND_IPVLAN: case NETDEV_KIND_VXLAN: + case NETDEV_KIND_VCAN: r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Can not add NetDev '%s' to network: %m", rvalue); return 0; } @@ -1029,6 +1035,36 @@ int config_parse_dnssec_negative_trust_anchors( return 0; } +int config_parse_dhcp_route_table(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) { + uint32_t rt; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &rt); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Unable to read RouteTable, ignoring assignment: %s", rvalue); + return 0; + } + + *((uint32_t *)data) = rt; + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 5460eb4d1c..ef4b499ab9 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -123,6 +123,7 @@ struct Network { bool dhcp_use_routes; bool dhcp_use_timezone; unsigned dhcp_route_metric; + uint32_t dhcp_route_table; /* DHCP Server Support */ bool dhcp_server; @@ -166,6 +167,7 @@ struct Network { bool ipv6_accept_ra_use_dns; DHCPUseDomains ipv6_accept_ra_use_domains; + uint32_t ipv6_accept_ra_route_table; union in_addr_union ipv6_token; IPv6PrivacyExtensions ipv6_privacy_extensions; @@ -228,6 +230,7 @@ int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigne int config_parse_dnssec_negative_trust_anchors(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_use_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_lldp_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_dhcp_route_table(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); diff --git a/src/network/networkd.h b/src/network/networkd.h index c4bd712147..cb1b73145e 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -43,6 +43,7 @@ #include "networkd-netdev-vlan.h" #include "networkd-netdev-vrf.h" #include "networkd-netdev-vxlan.h" +#include "networkd-netdev-vcan.h" #include "networkd-network.h" #include "networkd-util.h" diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 295b75341f..25d38aa742 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -314,19 +314,21 @@ int mount_all(const char *dest, } MountPoint; static const MountPoint mount_table[] = { - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true, false }, - { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true, false }, /* Bind mount first ...*/ - { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, true, true, true }, /* (except for this) */ - { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true, false }, /* ... then, make it r/o */ - { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, true }, - { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, false }, - { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false, false }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false }, - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false }, - { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false, false }, + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true, false }, + { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true, false }, /* Bind mount first ...*/ + { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, true, true, true }, /* (except for this) */ + { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true, false }, /* ... then, make it r/o */ + { "/proc/sysrq-trigger", "/proc/sysrq-trigger", NULL, NULL, MS_BIND, false, true, false }, /* Bind mount first ...*/ + { NULL, "/proc/sysrq-trigger", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, true, false }, /* ... then, make it r/o */ + { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, true }, + { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, false }, + { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false, false }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false }, + { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false, false }, #ifdef HAVE_SELINUX - { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, 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, false }, /* Then, make it r/o */ + { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, 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, false }, /* Then, make it r/o */ #endif }; @@ -356,7 +358,7 @@ int mount_all(const char *dest, continue; r = mkdir_p(where, 0755); - if (r < 0) { + if (r < 0 && r != -EEXIST) { if (mount_table[k].fatal) return log_error_errno(r, "Failed to create directory %s: %m", where); @@ -476,7 +478,7 @@ static int mount_bind(const char *dest, CustomMount *m) { return log_error_errno(errno, "mount(%s) failed: %m", where); if (m->read_only) { - r = bind_remount_recursive(where, true); + r = bind_remount_recursive(where, true, NULL); if (r < 0) return log_error_errno(r, "Read-only bind mount failed: %m"); } @@ -990,7 +992,7 @@ int setup_volatile_state( /* --volatile=state means we simply overmount /var with a tmpfs, and the rest read-only. */ - r = bind_remount_recursive(directory, true); + r = bind_remount_recursive(directory, true, NULL); if (r < 0) return log_error_errno(r, "Failed to remount %s read-only: %m", directory); @@ -1065,7 +1067,7 @@ int setup_volatile( bind_mounted = true; - r = bind_remount_recursive(t, true); + r = bind_remount_recursive(t, true, NULL); if (r < 0) { log_error_errno(r, "Failed to remount %s read-only: %m", t); goto fail; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 6d0420965a..a173d171e1 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -316,17 +316,10 @@ static int custom_mounts_prepare(void) { return 0; } -static int detect_unified_cgroup_hierarchy(void) { +static int detect_unified_cgroup_hierarchy(const char *directory) { const char *e; int r, all_unified, systemd_unified; - all_unified = cg_all_unified(); - systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - - if (all_unified < 0 || systemd_unified < 0) - return log_error_errno(all_unified < 0 ? all_unified : systemd_unified, - "Failed to determine whether the unified cgroups hierarchy is used: %m"); - /* Allow the user to control whether the unified hierarchy is used */ e = getenv("UNIFIED_CGROUP_HIERARCHY"); if (e) { @@ -341,12 +334,34 @@ static int detect_unified_cgroup_hierarchy(void) { return 0; } + all_unified = cg_all_unified(); + systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); + + if (all_unified < 0 || systemd_unified < 0) + return log_error_errno(all_unified < 0 ? all_unified : systemd_unified, + "Failed to determine whether the unified cgroups hierarchy is used: %m"); + /* Otherwise inherit the default from the host system */ - if (all_unified > 0) - arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL; - else if (systemd_unified > 0) - arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD; - else + if (all_unified > 0) { + /* Unified cgroup hierarchy support was added in 230. Unfortunately the detection + * routine only detects 231, so we'll have a false negative here for 230. */ + r = systemd_installation_has_version(directory, 230); + if (r < 0) + return log_error_errno(r, "Failed to determine systemd version in container: %m"); + if (r > 0) + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL; + else + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE; + } else if (systemd_unified > 0) { + /* Mixed cgroup hierarchy support was added in 232 */ + r = systemd_installation_has_version(directory, 232); + if (r < 0) + return log_error_errno(r, "Failed to determine systemd version in container: %m"); + if (r > 0) + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD; + else + arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE; + } else arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE; return 0; @@ -400,52 +415,52 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "directory", required_argument, NULL, 'D' }, - { "template", required_argument, NULL, ARG_TEMPLATE }, - { "ephemeral", no_argument, NULL, 'x' }, - { "user", required_argument, NULL, 'u' }, - { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, - { "as-pid2", no_argument, NULL, 'a' }, - { "boot", no_argument, NULL, 'b' }, - { "uuid", required_argument, NULL, ARG_UUID }, - { "read-only", no_argument, NULL, ARG_READ_ONLY }, - { "capability", required_argument, NULL, ARG_CAPABILITY }, - { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY }, - { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, - { "bind", required_argument, NULL, ARG_BIND }, - { "bind-ro", required_argument, NULL, ARG_BIND_RO }, - { "tmpfs", required_argument, NULL, ARG_TMPFS }, - { "overlay", required_argument, NULL, ARG_OVERLAY }, - { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, - { "machine", required_argument, NULL, 'M' }, - { "slice", required_argument, NULL, 'S' }, - { "setenv", required_argument, NULL, 'E' }, - { "selinux-context", required_argument, NULL, 'Z' }, - { "selinux-apifs-context", required_argument, NULL, 'L' }, - { "quiet", no_argument, NULL, 'q' }, - { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */ - { "register", required_argument, NULL, ARG_REGISTER }, - { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, - { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, - { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN }, - { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN }, - { "network-veth", no_argument, NULL, 'n' }, - { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA}, - { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, - { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE }, - { "personality", required_argument, NULL, ARG_PERSONALITY }, - { "image", required_argument, NULL, 'i' }, - { "volatile", optional_argument, NULL, ARG_VOLATILE }, - { "port", required_argument, NULL, 'p' }, - { "property", required_argument, NULL, ARG_PROPERTY }, - { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, - { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN}, - { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, - { "settings", required_argument, NULL, ARG_SETTINGS }, - { "chdir", required_argument, NULL, ARG_CHDIR }, - { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "directory", required_argument, NULL, 'D' }, + { "template", required_argument, NULL, ARG_TEMPLATE }, + { "ephemeral", no_argument, NULL, 'x' }, + { "user", required_argument, NULL, 'u' }, + { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, + { "as-pid2", no_argument, NULL, 'a' }, + { "boot", no_argument, NULL, 'b' }, + { "uuid", required_argument, NULL, ARG_UUID }, + { "read-only", no_argument, NULL, ARG_READ_ONLY }, + { "capability", required_argument, NULL, ARG_CAPABILITY }, + { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY }, + { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, + { "bind", required_argument, NULL, ARG_BIND }, + { "bind-ro", required_argument, NULL, ARG_BIND_RO }, + { "tmpfs", required_argument, NULL, ARG_TMPFS }, + { "overlay", required_argument, NULL, ARG_OVERLAY }, + { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, + { "machine", required_argument, NULL, 'M' }, + { "slice", required_argument, NULL, 'S' }, + { "setenv", required_argument, NULL, 'E' }, + { "selinux-context", required_argument, NULL, 'Z' }, + { "selinux-apifs-context", required_argument, NULL, 'L' }, + { "quiet", no_argument, NULL, 'q' }, + { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */ + { "register", required_argument, NULL, ARG_REGISTER }, + { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, + { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, + { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN }, + { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN }, + { "network-veth", no_argument, NULL, 'n' }, + { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA }, + { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, + { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE }, + { "personality", required_argument, NULL, ARG_PERSONALITY }, + { "image", required_argument, NULL, 'i' }, + { "volatile", optional_argument, NULL, ARG_VOLATILE }, + { "port", required_argument, NULL, 'p' }, + { "property", required_argument, NULL, ARG_PROPERTY }, + { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, + { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN }, + { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, + { "settings", required_argument, NULL, ARG_SETTINGS }, + { "chdir", required_argument, NULL, ARG_CHDIR }, + { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, {} }; @@ -898,15 +913,21 @@ static int parse_argv(int argc, char *argv[]) { break; - case ARG_PRIVATE_USERS: + case ARG_PRIVATE_USERS: { + int boolean = -1; - r = optarg ? parse_boolean(optarg) : 1; - if (r == 0) { + if (!optarg) + boolean = true; + else if (!in_charset(optarg, DIGITS)) + /* do *not* parse numbers as booleans */ + boolean = parse_boolean(optarg); + + if (boolean == false) { /* no: User namespacing off */ arg_userns_mode = USER_NAMESPACE_NO; arg_uid_shift = UID_INVALID; arg_uid_range = UINT32_C(0x10000); - } else if (r > 0) { + } else if (boolean == true) { /* yes: User namespacing on, UID range is read from root dir */ arg_userns_mode = USER_NAMESPACE_FIXED; arg_uid_shift = UID_INVALID; @@ -930,23 +951,27 @@ static int parse_argv(int argc, char *argv[]) { shift = buffer; range++; - if (safe_atou32(range, &arg_uid_range) < 0 || arg_uid_range <= 0) { - log_error("Failed to parse UID range: %s", range); - return -EINVAL; - } + r = safe_atou32(range, &arg_uid_range); + if (r < 0) + return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range); } else shift = optarg; - if (parse_uid(shift, &arg_uid_shift) < 0) { - log_error("Failed to parse UID: %s", optarg); - return -EINVAL; - } + r = parse_uid(shift, &arg_uid_shift); + if (r < 0) + return log_error_errno(r, "Failed to parse UID \"%s\": %m", optarg); arg_userns_mode = USER_NAMESPACE_FIXED; } + if (arg_uid_range <= 0) { + log_error("UID range cannot be 0."); + return -EINVAL; + } + arg_settings_mask |= SETTING_USERNS; break; + } case 'U': if (userns_supported()) { @@ -1045,7 +1070,8 @@ static int parse_argv(int argc, char *argv[]) { parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS); parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS); - if (arg_clone_ns_flags != (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)) { + if (!(arg_clone_ns_flags & CLONE_NEWPID) || + !(arg_clone_ns_flags & CLONE_NEWUTS)) { arg_register = false; if (arg_start_mode != START_PID1) { log_error("--boot cannot be used without namespacing."); @@ -1124,10 +1150,6 @@ static int parse_argv(int argc, char *argv[]) { arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; - r = detect_unified_cgroup_hierarchy(); - if (r < 0) - return r; - e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE"); if (e) arg_container_service_name = e; @@ -1219,7 +1241,13 @@ static int setup_timezone(const char *dest) { /* Fix the timezone, if possible */ r = readlink_malloc("/etc/localtime", &p); if (r < 0) { - log_warning("/etc/localtime is not a symlink, not updating container timezone."); + log_warning("host's /etc/localtime is not a symlink, not updating container timezone."); + /* to handle warning, delete /etc/localtime and replace it + * with a symbolic link to a time zone data file. + * + * Example: + * ln -s /usr/share/zoneinfo/UTC /etc/localtime + */ return 0; } @@ -1388,6 +1416,12 @@ static int copy_devnodes(const char *dest) { } else { if (mknod(to, st.st_mode, st.st_rdev) < 0) { + /* + * This is some sort of protection too against + * recursive userns chown on shared /dev/ + */ + if (errno == EEXIST) + log_notice("%s/dev/ should be an empty directory", dest); if (errno != EPERM) return log_error_errno(errno, "mknod(%s) failed: %m", to); @@ -1748,6 +1782,11 @@ static int setup_propagate(const char *root) { if (mount(NULL, q, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) return log_error_errno(errno, "Failed to make propagation mount read-only"); + /* machined will MS_MOVE into that directory, and that's only + * supported for non-shared mounts. */ + if (mount(NULL, q, NULL, MS_SLAVE, NULL) < 0) + return log_error_errno(errno, "Failed to make propagation mount slave"); + return 0; } @@ -2952,6 +2991,10 @@ static int outer_child( if (r < 0) return r; + r = detect_unified_cgroup_hierarchy(directory); + if (r < 0) + return r; + if (arg_userns_mode != USER_NAMESPACE_NO) { /* Let the parent know which UID shift we read from the image */ l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); @@ -2983,6 +3026,15 @@ 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"); + /* Mark everything as shared so our mounts get propagated down. This is + * required to make new bind mounts available in systemd services + * inside the containter that create a new mount namespace. + * See https://github.com/systemd/systemd/issues/3860 + * Further submounts (such as /dev) done after this will inherit the + * shared propagation mode.*/ + if (mount(NULL, directory, NULL, MS_SHARED|MS_REC, NULL) < 0) + return log_error_errno(errno, "MS_SHARED|MS_REC failed: %m"); + r = recursive_chown(directory, arg_uid_shift, arg_uid_range); if (r < 0) return r; @@ -3012,7 +3064,7 @@ static int outer_child( return r; if (arg_read_only) { - r = bind_remount_recursive(directory, true); + r = bind_remount_recursive(directory, true, NULL); if (r < 0) return log_error_errno(r, "Failed to make tree read-only: %m"); } @@ -3548,18 +3600,437 @@ static int load_settings(void) { return 0; } +static int run(int master, + const char* console, + const char *root_device, bool root_device_rw, + const char *home_device, bool home_device_rw, + const char *srv_device, bool srv_device_rw, + const char *esp_device, + bool interactive, + bool secondary, + FDSet *fds, + char veth_name[IFNAMSIZ], bool *veth_created, + union in_addr_union *exposed, + pid_t *pid, int *ret) { + + static const struct sigaction sa = { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP, + }; + + _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; + _cleanup_close_ int etc_passwd_lock = -1; + _cleanup_close_pair_ int + kmsg_socket_pair[2] = { -1, -1 }, + rtnl_socket_pair[2] = { -1, -1 }, + pid_socket_pair[2] = { -1, -1 }, + uuid_socket_pair[2] = { -1, -1 }, + notify_socket_pair[2] = { -1, -1 }, + uid_shift_socket_pair[2] = { -1, -1 }; + _cleanup_close_ int notify_socket= -1; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + ContainerStatus container_status = 0; + char last_char = 0; + int ifi = 0, r; + ssize_t l; + sigset_t mask_chld; + + assert_se(sigemptyset(&mask_chld) == 0); + assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely + * check with getpwuid() if the specific user already exists. Note that /etc might be + * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we + * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are + * really just an extra safety net. We kinda assume that the UID range we allocate from is + * really ours. */ + + etc_passwd_lock = take_etc_passwd_lock(NULL); + if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) + return log_error_errno(etc_passwd_lock, "Failed to take /etc/passwd lock: %m"); + } + + r = barrier_create(&barrier); + if (r < 0) + return log_error_errno(r, "Cannot initialize IPC barrier: %m"); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) + return log_error_errno(errno, "Failed to create kmsg socket pair: %m"); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) + return log_error_errno(errno, "Failed to create rtnl socket pair: %m"); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) + return log_error_errno(errno, "Failed to create pid socket pair: %m"); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) + return log_error_errno(errno, "Failed to create id socket pair: %m"); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0) + return log_error_errno(errno, "Failed to create notify socket pair: %m"); + + if (arg_userns_mode != USER_NAMESPACE_NO) + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) + return log_error_errno(errno, "Failed to create uid shift socket pair: %m"); + + /* Child can be killed before execv(), so handle SIGCHLD in order to interrupt + * parent's blocking calls and give it a chance to call wait() and terminate. */ + r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL); + if (r < 0) + return log_error_errno(errno, "Failed to change the signal mask: %m"); + + r = sigaction(SIGCHLD, &sa, NULL); + if (r < 0) + return log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); + + *pid = raw_clone(SIGCHLD|CLONE_NEWNS); + if (*pid < 0) + return log_error_errno(errno, "clone() failed%s: %m", + errno == EINVAL ? + ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : ""); + + if (*pid == 0) { + /* The outer child only has a file system namespace. */ + barrier_set_role(&barrier, BARRIER_CHILD); + + master = safe_close(master); + + kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); + rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); + pid_socket_pair[0] = safe_close(pid_socket_pair[0]); + uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]); + notify_socket_pair[0] = safe_close(notify_socket_pair[0]); + uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]); + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + r = outer_child(&barrier, + arg_directory, + console, + root_device, root_device_rw, + home_device, home_device_rw, + srv_device, srv_device_rw, + esp_device, + interactive, + secondary, + pid_socket_pair[1], + uuid_socket_pair[1], + notify_socket_pair[1], + kmsg_socket_pair[1], + rtnl_socket_pair[1], + uid_shift_socket_pair[1], + fds); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + barrier_set_role(&barrier, BARRIER_PARENT); + + fds = fdset_free(fds); + + 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]); + uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); + notify_socket_pair[1] = safe_close(notify_socket_pair[1]); + uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); + + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* The child just let us know the UID shift it might have read from the image. */ + l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0); + if (l < 0) + return log_error_errno(errno, "Failed to read UID shift: %m"); + + if (l != sizeof arg_uid_shift) { + log_error("Short read while reading UID shift."); + return -EIO; + } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* If we are supposed to pick the UID shift, let's try to use the shift read from the + * image, but if that's already in use, pick a new one, and report back to the child, + * which one we now picked. */ + + r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock); + if (r < 0) + return log_error_errno(r, "Failed to pick suitable UID/GID range: %m"); + + l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send UID shift: %m"); + if (l != sizeof arg_uid_shift) { + log_error("Short write while writing UID shift."); + return -EIO; + } + } + } + + /* Wait for the outer child. */ + r = wait_for_terminate_and_warn("namespace helper", *pid, NULL); + if (r != 0) + return r < 0 ? r : -EIO; + + /* And now retrieve the PID of the inner child. */ + l = recv(pid_socket_pair[0], pid, sizeof *pid, 0); + if (l < 0) + return log_error_errno(errno, "Failed to read inner child PID: %m"); + if (l != sizeof *pid) { + log_error("Short read while reading inner child PID."); + return -EIO; + } + + /* We also retrieve container UUID in case it was generated by outer child */ + l = recv(uuid_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0); + if (l < 0) + return log_error_errno(errno, "Failed to read container machine ID: %m"); + if (l != sizeof(arg_uuid)) { + log_error("Short read while reading container machined ID."); + return -EIO; + } + + /* We also retrieve the socket used for notifications generated by outer child */ + notify_socket = receive_one_fd(notify_socket_pair[0], 0); + if (notify_socket < 0) + return log_error_errno(notify_socket, + "Failed to receive notification socket from the outer child: %m"); + + log_debug("Init process invoked as PID "PID_FMT, *pid); + + if (arg_userns_mode != USER_NAMESPACE_NO) { + if (!barrier_place_and_sync(&barrier)) { /* #1 */ + log_error("Child died too early."); + return -ESRCH; + } + + r = setup_uid_map(*pid); + if (r < 0) + return r; + + (void) barrier_place(&barrier); /* #2 */ + } + + if (arg_private_network) { + + r = move_network_interfaces(*pid, arg_network_interfaces); + if (r < 0) + return r; + + if (arg_network_veth) { + r = setup_veth(arg_machine, *pid, veth_name, + arg_network_bridge || arg_network_zone); + if (r < 0) + return r; + else if (r > 0) + ifi = r; + + if (arg_network_bridge) { + /* Add the interface to a bridge */ + r = setup_bridge(veth_name, arg_network_bridge, false); + if (r < 0) + return r; + if (r > 0) + ifi = r; + } else if (arg_network_zone) { + /* Add the interface to a bridge, possibly creating it */ + r = setup_bridge(veth_name, arg_network_zone, true); + if (r < 0) + return r; + if (r > 0) + ifi = r; + } + } + + r = setup_veth_extra(arg_machine, *pid, arg_network_veth_extra); + if (r < 0) + return r; + + /* We created the primary and extra veth links now; let's remember this, so that we know to + remove them later on. Note that we don't bother with removing veth links that were created + here when their setup failed half-way, because in that case the kernel should be able to + remove them on its own, since they cannot be referenced by anything yet. */ + *veth_created = true; + + r = setup_macvlan(arg_machine, *pid, arg_network_macvlan); + if (r < 0) + return r; + + r = setup_ipvlan(arg_machine, *pid, arg_network_ipvlan); + if (r < 0) + return r; + } + + 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, + arg_container_service_name); + if (r < 0) + return r; + } + + r = sync_cgroup(*pid, arg_unified_cgroup_hierarchy); + if (r < 0) + return r; + + if (arg_keep_unit) { + r = create_subcgroup(*pid, arg_unified_cgroup_hierarchy); + if (r < 0) + return r; + } + + r = chown_cgroup(*pid, arg_uid_shift); + if (r < 0) + return r; + + /* Notify the child that the parent is ready with all + * its setup (including cgroup-ification), and that + * the child can now hand over control to the code to + * run inside the container. */ + (void) barrier_place(&barrier); /* #3 */ + + /* Block SIGCHLD here, before notifying child. + * process_pty() will handle it with the other signals. */ + assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0); + + /* Reset signal to default */ + r = default_signals(SIGCHLD, -1); + if (r < 0) + return log_error_errno(r, "Failed to reset SIGCHLD: %m"); + + r = sd_event_new(&event); + if (r < 0) + return log_error_errno(r, "Failed to get default event source: %m"); + + r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(*pid)); + if (r < 0) + return r; + + /* Let the child know that we are ready and wait that the child is completely ready now. */ + if (!barrier_place_and_sync(&barrier)) { /* #4 */ + log_error("Child died too early."); + return -ESRCH; + } + + /* At this point we have made use of the UID we picked, and thus nss-mymachines + * will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */ + etc_passwd_lock = safe_close(etc_passwd_lock); + + sd_notifyf(false, + "STATUS=Container running.\n" + "X_NSPAWN_LEADER_PID=" PID_FMT, *pid); + if (!arg_notify_ready) + sd_notify(false, "READY=1\n"); + + if (arg_kill_signal > 0) { + /* Try to kill the init system on SIGINT or SIGTERM */ + sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(*pid)); + sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(*pid)); + } else { + /* Immediately exit */ + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + } + + /* simply exit on sigchld */ + sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); + + if (arg_expose_ports) { + r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, exposed, &rtnl); + if (r < 0) + return r; + + (void) expose_port_execute(rtnl, arg_expose_ports, exposed); + } + + rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); + + r = pty_forward_new(event, master, + PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), + &forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(forward, &last_char); + + forward = pty_forward_free(forward); + + if (!arg_quiet && last_char != '\n') + putc('\n', stdout); + + /* Kill if it is not dead yet anyway */ + if (arg_register && !arg_keep_unit) + terminate_machine(*pid); + + /* Normally redundant, but better safe than sorry */ + kill(*pid, SIGKILL); + + r = wait_for_container(*pid, &container_status); + *pid = 0; + + if (r < 0) + /* We failed to wait for the container, or the container exited abnormally. */ + return r; + if (r > 0 || container_status == CONTAINER_TERMINATED) { + /* r > 0 → The container exited with a non-zero status. + * As a special case, we need to replace 133 with a different value, + * because 133 is special-cased in the service file to reboot the container. + * otherwise → The container exited with zero status and a reboot was not requested. + */ + if (r == 133) + r = EXIT_FAILURE; /* replace 133 with the general failure code */ + *ret = r; + return 0; /* finito */ + } + + /* CONTAINER_REBOOTED, loop again */ + + if (arg_keep_unit) { + /* Special handling if we are running as a service: instead of simply + * restarting the machine we want to restart the entire service, so let's + * inform systemd about this with the special exit code 133. The service + * file uses RestartForceExitStatus=133 so that this results in a full + * nspawn restart. This is necessary since we might have cgroup parameters + * set we want to have flushed out. */ + *ret = 0; + return 133; + } + + expose_port_flush(arg_expose_ports, exposed); + + (void) remove_veth_links(veth_name, arg_network_veth_extra); + *veth_created = false; + return 1; /* loop again */ +} + int main(int argc, char *argv[]) { _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL; bool root_device_rw = true, home_device_rw = true, srv_device_rw = true; _cleanup_close_ int master = -1, image_fd = -1; _cleanup_fdset_free_ FDSet *fds = NULL; - int r, n_fd_passed, loop_nr = -1; + int r, n_fd_passed, loop_nr = -1, ret = EXIT_FAILURE; char veth_name[IFNAMSIZ] = ""; bool secondary = false, remove_subvol = false; - sigset_t mask_chld; pid_t pid = 0; - int ret = EXIT_SUCCESS; union in_addr_union exposed = {}; _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; bool interactive, veth_created = false; @@ -3775,470 +4246,25 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - assert_se(sigemptyset(&mask_chld) == 0); - assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); - if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { r = log_error_errno(errno, "Failed to become subreaper: %m"); goto finish; } for (;;) { - static const struct sigaction sa = { - .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP, - }; - - _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; - _cleanup_close_ int etc_passwd_lock = -1; - _cleanup_close_pair_ int - kmsg_socket_pair[2] = { -1, -1 }, - rtnl_socket_pair[2] = { -1, -1 }, - pid_socket_pair[2] = { -1, -1 }, - uuid_socket_pair[2] = { -1, -1 }, - notify_socket_pair[2] = { -1, -1 }, - uid_shift_socket_pair[2] = { -1, -1 }; - _cleanup_close_ int notify_socket= -1; - _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(pty_forward_freep) PTYForward *forward = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - ContainerStatus container_status; - char last_char = 0; - int ifi = 0; - ssize_t l; - - if (arg_userns_mode == USER_NAMESPACE_PICK) { - /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely - * check with getpwuid() if the specific user already exists. Note that /etc might be - * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we - * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are - * really just an extra safety net. We kinda assume that the UID range we allocate from is - * really ours. */ - - etc_passwd_lock = take_etc_passwd_lock(NULL); - if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) { - log_error_errno(r, "Failed to take /etc/passwd lock: %m"); - goto finish; - } - } - - r = barrier_create(&barrier); - if (r < 0) { - log_error_errno(r, "Cannot initialize IPC barrier: %m"); - goto finish; - } - - 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_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_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create pid socket pair: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create id socket pair: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create notify socket pair: %m"); - goto finish; - } - - if (arg_userns_mode != USER_NAMESPACE_NO) - 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; - } - - /* Child can be killed before execv(), so handle SIGCHLD - * in order to interrupt parent's blocking calls and - * give it a chance to call wait() and terminate. */ - r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL); - if (r < 0) { - r = log_error_errno(errno, "Failed to change the signal mask: %m"); - goto finish; - } - - r = sigaction(SIGCHLD, &sa, NULL); - if (r < 0) { - r = log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); - goto finish; - } - - pid = raw_clone(SIGCHLD|CLONE_NEWNS); - if (pid < 0) { - if (errno == EINVAL) - r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m"); - else - r = log_error_errno(errno, "clone() failed: %m"); - - goto finish; - } - - if (pid == 0) { - /* The outer child only has a file system namespace. */ - barrier_set_role(&barrier, BARRIER_CHILD); - - master = safe_close(master); - - kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); - rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); - pid_socket_pair[0] = safe_close(pid_socket_pair[0]); - uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]); - notify_socket_pair[0] = safe_close(notify_socket_pair[0]); - uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]); - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - r = outer_child(&barrier, - arg_directory, - console, - root_device, root_device_rw, - home_device, home_device_rw, - srv_device, srv_device_rw, - esp_device, - interactive, - secondary, - pid_socket_pair[1], - uuid_socket_pair[1], - notify_socket_pair[1], - kmsg_socket_pair[1], - rtnl_socket_pair[1], - uid_shift_socket_pair[1], - fds); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - barrier_set_role(&barrier, BARRIER_PARENT); - - fds = fdset_free(fds); - - 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]); - uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); - notify_socket_pair[1] = safe_close(notify_socket_pair[1]); - uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); - - if (arg_userns_mode != USER_NAMESPACE_NO) { - /* The child just let us know the UID shift it might have read from the image. */ - l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read UID shift: %m"); - goto finish; - } - if (l != sizeof(arg_uid_shift)) { - log_error("Short read while reading UID shift."); - r = EIO; - goto finish; - } - - if (arg_userns_mode == USER_NAMESPACE_PICK) { - /* If we are supposed to pick the UID shift, let's try to use the shift read from the - * image, but if that's already in use, pick a new one, and report back to the child, - * which one we now picked. */ - - r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock); - if (r < 0) { - log_error_errno(r, "Failed to pick suitable UID/GID range: %m"); - goto finish; - } - - l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); - if (l < 0) { - r = log_error_errno(errno, "Failed to send UID shift: %m"); - goto finish; - } - if (l != sizeof(arg_uid_shift)) { - log_error("Short write while writing UID shift."); - r = -EIO; - goto finish; - } - } - } - - /* Wait for the outer child. */ - r = wait_for_terminate_and_warn("namespace helper", pid, NULL); - if (r < 0) - goto finish; - if (r != 0) { - r = -EIO; - goto finish; - } - pid = 0; - - /* And now retrieve the PID of the inner child. */ - l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read inner child PID: %m"); - goto finish; - } - if (l != sizeof(pid)) { - log_error("Short read while reading inner child PID."); - r = EIO; - goto finish; - } - - /* We also retrieve container UUID in case it was generated by outer child */ - l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read container machine ID: %m"); - goto finish; - } - if (l != sizeof(arg_uuid)) { - log_error("Short read while reading container machined ID."); - r = EIO; - goto finish; - } - - /* We also retrieve the socket used for notifications generated by outer child */ - notify_socket = receive_one_fd(notify_socket_pair[0], 0); - if (notify_socket < 0) { - r = log_error_errno(errno, "Failed to receive notification socket from the outer child: %m"); - goto finish; - } - - log_debug("Init process invoked as PID " PID_FMT, pid); - - if (arg_userns_mode != USER_NAMESPACE_NO) { - if (!barrier_place_and_sync(&barrier)) { /* #1 */ - log_error("Child died too early."); - r = -ESRCH; - goto finish; - } - - r = setup_uid_map(pid); - if (r < 0) - goto finish; - - (void) barrier_place(&barrier); /* #2 */ - } - - if (arg_private_network) { - - r = move_network_interfaces(pid, arg_network_interfaces); - if (r < 0) - goto finish; - - if (arg_network_veth) { - r = setup_veth(arg_machine, pid, veth_name, - arg_network_bridge || arg_network_zone); - if (r < 0) - goto finish; - else if (r > 0) - ifi = r; - - if (arg_network_bridge) { - /* Add the interface to a bridge */ - r = setup_bridge(veth_name, arg_network_bridge, false); - if (r < 0) - goto finish; - if (r > 0) - ifi = r; - } else if (arg_network_zone) { - /* Add the interface to a bridge, possibly creating it */ - r = setup_bridge(veth_name, arg_network_zone, true); - if (r < 0) - goto finish; - if (r > 0) - ifi = r; - } - } - - r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra); - if (r < 0) - goto finish; - - /* We created the primary and extra veth links now; let's remember this, so that we know to - remove them later on. Note that we don't bother with removing veth links that were created - here when their setup failed half-way, because in that case the kernel should be able to - remove them on its own, since they cannot be referenced by anything yet. */ - veth_created = true; - - r = setup_macvlan(arg_machine, pid, arg_network_macvlan); - if (r < 0) - goto finish; - - 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, - arg_container_service_name); - if (r < 0) - goto finish; - } - - r = sync_cgroup(pid, arg_unified_cgroup_hierarchy); - if (r < 0) - goto finish; - - 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; - - /* Notify the child that the parent is ready with all - * its setup (including cgroup-ification), and that - * the child can now hand over control to the code to - * run inside the container. */ - (void) barrier_place(&barrier); /* #3 */ - - /* Block SIGCHLD here, before notifying child. - * process_pty() will handle it with the other signals. */ - assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0); - - /* Reset signal to default */ - r = default_signals(SIGCHLD, -1); - if (r < 0) { - log_error_errno(r, "Failed to reset SIGCHLD: %m"); - goto finish; - } - - r = sd_event_new(&event); - if (r < 0) { - log_error_errno(r, "Failed to get default event source: %m"); - goto finish; - } - - r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(pid)); - if (r < 0) - goto finish; - - /* Let the child know that we are ready and wait that the child is completely ready now. */ - if (!barrier_place_and_sync(&barrier)) { /* #4 */ - log_error("Child died too early."); - r = -ESRCH; - goto finish; - } - - /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear - * in getpwuid(), thus we can release the /etc/passwd lock. */ - etc_passwd_lock = safe_close(etc_passwd_lock); - - sd_notifyf(false, - "STATUS=Container running.\n" - "X_NSPAWN_LEADER_PID=" PID_FMT, pid); - if (!arg_notify_ready) - sd_notify(false, "READY=1\n"); - - if (arg_kill_signal > 0) { - /* Try to kill the init system on SIGINT or SIGTERM */ - sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(pid)); - sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(pid)); - } else { - /* Immediately exit */ - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - } - - /* simply exit on sigchld */ - sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); - - if (arg_expose_ports) { - r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl); - if (r < 0) - goto finish; - - (void) expose_port_execute(rtnl, arg_expose_ports, &exposed); - } - - rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); - - r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), &forward); - if (r < 0) { - log_error_errno(r, "Failed to create PTY forwarder: %m"); - goto finish; - } - - r = sd_event_loop(event); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - - pty_forward_get_last_char(forward, &last_char); - - forward = pty_forward_free(forward); - - if (!arg_quiet && last_char != '\n') - putc('\n', stdout); - - /* Kill if it is not dead yet anyway */ - if (arg_register && !arg_keep_unit) - terminate_machine(pid); - - /* Normally redundant, but better safe than sorry */ - kill(pid, SIGKILL); - - r = wait_for_container(pid, &container_status); - pid = 0; - - if (r < 0) - /* We failed to wait for the container, or the - * container exited abnormally */ - goto finish; - else if (r > 0 || container_status == CONTAINER_TERMINATED) { - /* The container exited with a non-zero - * status, or with zero status and no reboot - * was requested. */ - ret = r; - break; - } - - /* CONTAINER_REBOOTED, loop again */ - - if (arg_keep_unit) { - /* Special handling if we are running as a - * service: instead of simply restarting the - * machine we want to restart the entire - * service, so let's inform systemd about this - * with the special exit code 133. The service - * file uses RestartForceExitStatus=133 so - * that this results in a full nspawn - * restart. This is necessary since we might - * have cgroup parameters set we want to have - * flushed out. */ - ret = 133; - r = 0; + r = run(master, + console, + root_device, root_device_rw, + home_device, home_device_rw, + srv_device, srv_device_rw, + esp_device, + interactive, secondary, + fds, + veth_name, &veth_created, + &exposed, + &pid, &ret); + if (r <= 0) break; - } - - expose_port_flush(arg_expose_ports, &exposed); - - (void) remove_veth_links(veth_name, arg_network_veth_extra); - veth_created = false; } finish: diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index 5ce10f1cbd..eea91e3e88 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -279,9 +279,12 @@ fallback: } fail: + /* When we arrive here, resolved runs and has answered (fallback to + * "dns" is handled earlier). So we have a definitive "no" answer and + * should not fall back to subsequent NSS modules via "UNAVAIL". */ *errnop = -r; *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } enum nss_status _nss_resolve_gethostbyname3_r( @@ -476,7 +479,7 @@ fallback: fail: *errnop = -r; *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } enum nss_status _nss_resolve_gethostbyaddr2_r( @@ -558,9 +561,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( goto fallback; - *errnop = -r; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; + goto fail; } r = sd_bus_message_enter_container(reply, 'a', "(is)"); @@ -668,7 +669,7 @@ fallback: fail: *errnop = -r; *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } NSS_GETHOSTBYNAME_FALLBACKS(resolve); diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index 6468d1eecd..c3bdcaf1da 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) { s = hashmap_remove(pids, PID_TO_PTR(si.si_pid)); if (s) { - if (!is_clean_exit(si.si_code, si.si_status, NULL)) { + if (!is_clean_exit(si.si_code, si.si_status, EXIT_CLEAN_COMMAND, NULL)) { if (si.si_code == CLD_EXITED) log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status); else diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 07e4cd7d1d..8aa79049b6 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -395,7 +395,7 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) { return 0; } -static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) { +static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char ifname[IF_NAMESIZE] = ""; @@ -430,7 +430,8 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); if (r < 0) { - log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); + if (warn_missing || r != -ENXIO) + log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); return r; } @@ -488,7 +489,8 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ return bus_log_parse_error(r); if (n == 0) { - log_error("%s: no records found", name); + if (warn_missing) + log_error("%s: no records found", name); return -ESRCH; } @@ -618,7 +620,7 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) { if (type == 0) type = arg_type ?: DNS_TYPE_A; - return resolve_record(bus, n, class, type); + return resolve_record(bus, n, class, type, true); invalid: log_error("Invalid DNS URI: %s", name); @@ -840,16 +842,34 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { } domain++; - r = string_hashsum_sha224(address, domain - 1 - address, &hashed); + r = string_hashsum_sha256(address, domain - 1 - address, &hashed); if (r < 0) return log_error_errno(r, "Hashing failed: %m"); + strshorten(hashed, 56); + full = strjoina(hashed, "._openpgpkey.", domain); log_debug("Looking up \"%s\".", full); - return resolve_record(bus, full, - arg_class ?: DNS_CLASS_IN, - arg_type ?: DNS_TYPE_OPENPGPKEY); + r = resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_OPENPGPKEY, false); + + if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */ + hashed = NULL; + r = string_hashsum_sha224(address, domain - 1 - address, &hashed); + if (r < 0) + return log_error_errno(r, "Hashing failed: %m"); + + full = strjoina(hashed, "._openpgpkey.", domain); + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_OPENPGPKEY, true); + } + + return r; } static int resolve_tlsa(sd_bus *bus, const char *address) { @@ -881,7 +901,7 @@ static int resolve_tlsa(sd_bus *bus, const char *address) { return resolve_record(bus, full, arg_class ?: DNS_CLASS_IN, - arg_type ?: DNS_TYPE_TLSA); + arg_type ?: DNS_TYPE_TLSA, true); } static int show_statistics(sd_bus *bus) { @@ -1877,7 +1897,7 @@ int main(int argc, char **argv) { while (argv[optind]) { int k; - k = resolve_record(bus, argv[optind], arg_class, arg_type); + k = resolve_record(bus, argv[optind], arg_class, arg_type, true); if (r == 0) r = k; diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index dd233e7c4a..abf3263178 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -23,8 +23,19 @@ #include "extract-word.h" #include "parse-util.h" #include "resolved-conf.h" +#include "string-table.h" #include "string-util.h" +DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting"); + +static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = { + [DNS_STUB_LISTENER_NO] = "no", + [DNS_STUB_LISTENER_UDP] = "udp", + [DNS_STUB_LISTENER_TCP] = "tcp", + [DNS_STUB_LISTENER_YES] = "yes", +}; +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES); + int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { union in_addr_union address; int family, r, ifindex = 0; @@ -221,7 +232,7 @@ int manager_parse_config_file(Manager *m) { assert(m); - r = config_parse_many(PKGSYSCONFDIR "/resolved.conf", + r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf", CONF_PATHS_NULSTR("systemd/resolved.conf.d"), "Resolve\0", config_item_perf_lookup, resolved_gperf_lookup, diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index e1fd2cceec..fc425a36b2 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -19,7 +19,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +typedef enum DnsStubListenerMode DnsStubListenerMode; + +enum DnsStubListenerMode { + DNS_STUB_LISTENER_NO, + DNS_STUB_LISTENER_UDP, + DNS_STUB_LISTENER_TCP, + DNS_STUB_LISTENER_YES, + _DNS_STUB_LISTENER_MODE_MAX, + _DNS_STUB_LISTENER_MODE_INVALID = -1 +}; + #include "resolved-manager.h" +#include "resolved-dns-server.h" int manager_parse_config_file(Manager *m); @@ -33,4 +45,7 @@ const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned len int config_parse_dns_servers(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_search_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_dnssec(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_dns_stub_listener_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); + +const char* dns_stub_listener_mode_to_string(DnsStubListenerMode p) _const_; +DnsStubListenerMode dns_stub_listener_mode_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index a8ad8fe342..337a8c473f 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -2143,7 +2143,7 @@ int dns_packet_extract(DnsPacket *p) { for (i = 0; i < n; i++) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - bool cache_flush; + bool cache_flush = false; r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); if (r < 0) @@ -2289,6 +2289,7 @@ static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = { [DNS_RCODE_BADNAME] = "BADNAME", [DNS_RCODE_BADALG] = "BADALG", [DNS_RCODE_BADTRUNC] = "BADTRUNC", + [DNS_RCODE_BADCOOKIE] = "BADCOOKIE", }; DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 7b7d4e14c9..054dc88a85 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -263,6 +263,7 @@ enum { DNS_RCODE_BADNAME = 20, DNS_RCODE_BADALG = 21, DNS_RCODE_BADTRUNC = 22, + DNS_RCODE_BADCOOKIE = 23, _DNS_RCODE_MAX_DEFINED, _DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */ }; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index ed0c6aa105..03811ac8e7 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -407,6 +407,7 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) { DnsSearchDomain *d; + DnsServer *dns_server; assert(s); assert(domain); @@ -447,6 +448,13 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if (dns_name_endswith(domain, d->name) > 0) return DNS_SCOPE_YES; + /* If the DNS server has route-only domains, don't send other requests + * to it. This would be a privacy violation, will most probably fail + * anyway, and adds unnecessary load. */ + dns_server = dns_scope_get_dns_server(s); + if (dns_server && dns_server_limited_domains(dns_server)) + return DNS_SCOPE_NO; + switch (s->protocol) { case DNS_PROTOCOL_DNS: diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 538bc61f81..01a83a76b2 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -26,7 +26,10 @@ typedef struct DnsScope DnsScope; #include "resolved-dns-cache.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" +#include "resolved-dns-query.h" +#include "resolved-dns-search-domain.h" #include "resolved-dns-server.h" +#include "resolved-dns-stream.h" #include "resolved-dns-zone.h" #include "resolved-link.h" diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 9b7b471600..97cc8c0e09 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -576,6 +576,27 @@ void dns_server_warn_downgrade(DnsServer *server) { server->warned_downgrade = true; } +bool dns_server_limited_domains(DnsServer *server) +{ + DnsSearchDomain *domain; + bool domain_restricted = false; + + /* Check if the server has route-only domains without ~., i. e. whether + * it should only be used for particular domains */ + if (!server->link) + return false; + + LIST_FOREACH(domains, domain, server->link->search_domains) + if (domain->route_only) { + domain_restricted = true; + /* ~. means "any domain", thus it is a global server */ + if (streq(DNS_SEARCH_DOMAIN_NAME(domain), ".")) + return false; + } + + return domain_restricted; +} + static void dns_server_hash_func(const void *p, struct siphash *state) { const DnsServer *s = p; diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index c1732faffd..83e288a202 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -128,6 +128,8 @@ bool dns_server_dnssec_supported(DnsServer *server); void dns_server_warn_downgrade(DnsServer *server); +bool dns_server_limited_domains(DnsServer *server); + DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex); void dns_server_unlink_all(DnsServer *first); diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h index e6569678fa..4cdb4f6806 100644 --- a/src/resolve/resolved-dns-stream.h +++ b/src/resolve/resolved-dns-stream.h @@ -25,6 +25,7 @@ typedef struct DnsStream DnsStream; #include "resolved-dns-packet.h" #include "resolved-dns-transaction.h" +#include "resolved-manager.h" /* Streams are used by three subsystems: * diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index d263cedcd9..e76de6c06a 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -25,6 +25,9 @@ * IP and UDP header sizes */ #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U) +static int manager_dns_stub_udp_fd(Manager *m); +static int manager_dns_stub_tcp_fd(Manager *m); + static int dns_stub_make_reply_packet( uint16_t id, int rcode, @@ -354,66 +357,48 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void return 0; } -int manager_dns_stub_udp_fd(Manager *m) { +static int manager_dns_stub_udp_fd(Manager *m) { static const int one = 1; - union sockaddr_union sa = { .in.sin_family = AF_INET, .in.sin_port = htobe16(53), .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), }; - + _cleanup_close_ int fd = -1; int r; if (m->dns_stub_udp_fd >= 0) return m->dns_stub_udp_fd; - m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_stub_udp_fd < 0) + fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) return -errno; - r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0) + return -errno; - r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0) + return -errno; - r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0) + return -errno; /* Make sure no traffic from outside the local host can leak to onto this socket */ - r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0) + return -errno; - r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } + if (bind(fd, &sa.sa, sizeof(sa.in)) < 0) + return -errno; - r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m); + r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m); if (r < 0) - goto fail; + return r; (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp"); + m->dns_stub_udp_fd = fd; + fd = -1; return m->dns_stub_udp_fd; - -fail: - m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd); - return r; } static int on_dns_stub_stream_packet(DnsStream *s) { @@ -461,102 +446,83 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void return 0; } -int manager_dns_stub_tcp_fd(Manager *m) { +static int manager_dns_stub_tcp_fd(Manager *m) { static const int one = 1; - union sockaddr_union sa = { .in.sin_family = AF_INET, .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), .in.sin_port = htobe16(53), }; - + _cleanup_close_ int fd = -1; int r; if (m->dns_stub_tcp_fd >= 0) return m->dns_stub_tcp_fd; - m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_stub_tcp_fd < 0) + fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) return -errno; - r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0) + return -errno; - r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0) + return -errno; - r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0) + return -errno; - r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0) + return -errno; /* Make sure no traffic from outside the local host can leak to onto this socket */ - r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3); - if (r < 0) { - r = -errno; - goto fail; - } + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0) + return -errno; - r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } + if (bind(fd, &sa.sa, sizeof(sa.in)) < 0) + return -errno; - r = listen(m->dns_stub_tcp_fd, SOMAXCONN); - if (r < 0) { - r = -errno; - goto fail; - } + if (listen(fd, SOMAXCONN) < 0) + return -errno; - r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m); + r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m); if (r < 0) - goto fail; + return r; (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp"); + m->dns_stub_tcp_fd = fd; + fd = -1; return m->dns_stub_tcp_fd; - -fail: - m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd); - return r; } int manager_dns_stub_start(Manager *m) { - int r; + const char *t = "UDP"; + int r = 0; assert(m); - r = manager_dns_stub_udp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - r = manager_dns_stub_tcp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; + if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP)) + r = manager_dns_stub_udp_fd(m); - return 0; + if (r >= 0 && + IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) { + t = "TCP"; + r = manager_dns_stub_tcp_fd(m); + } -eaddrinuse: - log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support."); - manager_dns_stub_stop(m); + if (IN_SET(r, -EADDRINUSE, -EPERM)) { + if (r == -EADDRINUSE) + log_warning_errno(r, + "Another process is already listening on %s socket 127.0.0.53:53.\n" + "Turning off local DNS stub support.", t); + else + log_warning_errno(r, + "Failed to listen on %s socket 127.0.0.53:53: %m.\n" + "Turning off local DNS stub support.", t); + manager_dns_stub_stop(m); + } else if (r < 0) + return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t); return 0; } diff --git a/src/resolve/resolved-dns-stub.h b/src/resolve/resolved-dns-stub.h index fce4d25ede..12b86f6753 100644 --- a/src/resolve/resolved-dns-stub.h +++ b/src/resolve/resolved-dns-stub.h @@ -24,8 +24,5 @@ /* 127.0.0.53 in native endian */ #define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U) -int manager_dns_stub_udp_fd(Manager *m); -int manager_dns_stub_tcp_fd(Manager *m); - void manager_dns_stub_stop(Manager *m); int manager_dns_stub_start(Manager *m); diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 96b066845d..5a1df70422 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -59,6 +59,8 @@ enum DnsTransactionSource { #include "resolved-dns-packet.h" #include "resolved-dns-question.h" #include "resolved-dns-scope.h" +#include "resolved-dns-server.h" +#include "resolved-dns-stream.h" struct DnsTransaction { DnsScope *scope; diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 2fd56bce26..446f85cdf4 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -14,9 +14,10 @@ struct ConfigPerfItem; %struct-type %includes %% -Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 -Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 -Resolve.Domains, config_parse_search_domains, 0, 0 -Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) -Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) -Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache) +Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 +Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 +Resolve.Domains, config_parse_search_domains, 0, 0 +Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) +Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) +Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache) +Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode) diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index 6a2343f9f7..c9b2a58c34 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -29,6 +29,7 @@ typedef struct Link Link; typedef struct LinkAddress LinkAddress; #include "resolved-dns-rr.h" +#include "resolved-dns-scope.h" #include "resolved-dns-search-domain.h" #include "resolved-dns-server.h" #include "resolved-manager.h" diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 92ade820ac..40f08e8044 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -501,6 +501,7 @@ int manager_new(Manager **ret) { m->mdns_support = RESOLVE_SUPPORT_NO; m->dnssec_mode = DEFAULT_DNSSEC_MODE; m->enable_cache = true; + m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP; m->read_resolv_conf = true; m->need_builtin_fallbacks = true; m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index deebd8e484..6b2208ed94 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -30,6 +30,7 @@ typedef struct Manager Manager; +#include "resolved-conf.h" #include "resolved-dns-query.h" #include "resolved-dns-search-domain.h" #include "resolved-dns-server.h" @@ -47,6 +48,7 @@ struct Manager { ResolveSupport mdns_support; DnssecMode dnssec_mode; bool enable_cache; + DnsStubListenerMode dns_stub_listener_mode; /* Network */ Hashmap *links; diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 31b25ca50f..801014caf5 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -154,6 +154,16 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { return; } + /* Check if the DNS server is limited to particular domains; + * resolv.conf does not have a syntax to express that, so it must not + * appear as a global name server to avoid routing unrelated domains to + * it (which is a privacy violation, will most probably fail anyway, + * and adds unnecessary load) */ + if (dns_server_limited_domains(s)) { + log_debug("DNS server %s has route-only domains, not using as global name server", dns_server_string(s)); + return; + } + if (*count == MAXNS) fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); (*count)++; diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 3bd8389c88..60afa151e3 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -18,3 +18,4 @@ #LLMNR=yes #DNSSEC=@DEFAULT_DNSSEC_MODE@ #Cache=yes +#DNSStubListener=udp diff --git a/src/run/run.c b/src/run/run.c index 2dd229868c..81b53fdfab 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -1168,17 +1168,21 @@ static int start_transient_scope( uid_t uid; gid_t gid; - r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell); + r = get_user_creds_clean(&arg_exec_user, &uid, &gid, &home, &shell); if (r < 0) return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); - r = strv_extendf(&user_env, "HOME=%s", home); - if (r < 0) - return log_oom(); + if (home) { + r = strv_extendf(&user_env, "HOME=%s", home); + if (r < 0) + return log_oom(); + } - r = strv_extendf(&user_env, "SHELL=%s", shell); - if (r < 0) - return log_oom(); + if (shell) { + r = strv_extendf(&user_env, "SHELL=%s", shell); + if (r < 0) + return log_oom(); + } r = strv_extendf(&user_env, "USER=%s", arg_exec_user); if (r < 0) diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 65151b19a6..2597cfc648 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -484,7 +484,7 @@ int ask_password_agent( (void) mkdir_p_label("/run/systemd/ask-password", 0755); - fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); + fd = mkostemp_safe(temp); if (fd < 0) { r = fd; goto finish; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index feb4a06737..c6bd2f145c 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", - "RestrictRealtime", "DynamicUser", "RemoveIPC")) { + "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectControlGroups")) { r = parse_boolean(eq); if (r < 0) diff --git a/src/shared/condition.c b/src/shared/condition.c index 6bb42c0692..f13fa6a9fd 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -37,6 +37,7 @@ #include "condition.h" #include "extract-word.h" #include "fd-util.h" +#include "fileio.h" #include "glob-util.h" #include "hostname-util.h" #include "ima-util.h" @@ -309,8 +310,45 @@ static int condition_test_needs_update(Condition *c) { if (lstat("/usr/", &usr) < 0) return true; - return usr.st_mtim.tv_sec > other.st_mtim.tv_sec || - (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec); + /* + * First, compare seconds as they are always accurate... + */ + if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec) + return usr.st_mtim.tv_sec > other.st_mtim.tv_sec; + + /* + * ...then compare nanoseconds. + * + * A false positive is only possible when /usr's nanoseconds > 0 + * (otherwise /usr cannot be strictly newer than the target file) + * AND the target file's nanoseconds == 0 + * (otherwise the filesystem supports nsec timestamps, see stat(2)). + */ + if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) { + _cleanup_free_ char *timestamp_str = NULL; + uint64_t timestamp; + int r; + + r = parse_env_file(p, NULL, "TimestampNSec", ×tamp_str, NULL); + if (r < 0) { + log_error_errno(-r, "Failed to parse timestamp file '%s', using mtime: %m", p); + return true; + } else if (r == 0) { + log_debug("No data in timestamp file '%s', using mtime", p); + return true; + } + + r = safe_atou64(timestamp_str, ×tamp); + if (r < 0) { + log_error_errno(-r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", + timestamp_str, p); + return true; + } + + other.st_mtim.tv_nsec = timestamp % NSEC_PER_SEC; + } + + return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec; } static int condition_test_first_boot(Condition *c) { diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index f31d219418..2ec0155b71 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -396,22 +396,18 @@ int config_parse(const char *unit, return 0; } -/* Parse each config file in the specified directories. */ -int config_parse_many(const char *conf_file, - const char *conf_file_dirs, - const char *sections, - ConfigItemLookup lookup, - const void *table, - bool relaxed, - void *userdata) { - _cleanup_strv_free_ char **files = NULL; +static int config_parse_many_files( + const char *conf_file, + char **files, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata) { + char **fn; int r; - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (r < 0) - return r; - if (conf_file) { r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata); if (r < 0) @@ -427,6 +423,56 @@ int config_parse_many(const char *conf_file, return 0; } +/* Parse each config file in the directories specified as nulstr. */ +int config_parse_many_nulstr( + const char *conf_file, + const char *conf_file_dirs, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata) { + + _cleanup_strv_free_ char **files = NULL; + int r; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) + return r; + + return config_parse_many_files(conf_file, files, + sections, lookup, table, relaxed, userdata); +} + +/* Parse each config file in the directories specified as strv. */ +int config_parse_many( + const char *conf_file, + const char* const* conf_file_dirs, + const char *dropin_dirname, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata) { + + _cleanup_strv_free_ char **dropin_dirs = NULL; + _cleanup_strv_free_ char **files = NULL; + const char *suffix; + int r; + + suffix = strjoina("/", dropin_dirname); + r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix); + if (r < 0) + return r; + + r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs); + if (r < 0) + return r; + + return config_parse_many_files(conf_file, files, + sections, lookup, table, relaxed, userdata); +} + #define DEFINE_PARSER(type, vartype, conv_func) \ int config_parse_##type( \ const char *unit, \ diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 3298dc0cea..26ff3df16f 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -84,24 +84,36 @@ int config_item_table_lookup(const void *table, const char *section, const char * ConfigPerfItem tables */ int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); -int config_parse(const char *unit, - const char *filename, - FILE *f, - const char *sections, /* nulstr */ - ConfigItemLookup lookup, - const void *table, - bool relaxed, - bool allow_include, - bool warn, - void *userdata); - -int config_parse_many(const char *conf_file, /* possibly NULL */ - const char *conf_file_dirs, /* nulstr */ - const char *sections, /* nulstr */ - ConfigItemLookup lookup, - const void *table, - bool relaxed, - void *userdata); +int config_parse( + const char *unit, + const char *filename, + FILE *f, + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + bool relaxed, + bool allow_include, + bool warn, + void *userdata); + +int config_parse_many_nulstr( + const char *conf_file, /* possibly NULL */ + const char *conf_file_dirs, /* nulstr */ + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata); + +int config_parse_many( + const char *conf_file, /* possibly NULL */ + const char* const* conf_file_dirs, + const char *dropin_dirname, + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata); /* Generic parsers */ 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); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 835557c6b2..892f0aadf5 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -131,6 +131,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (r == 0 && *n) return -EINVAL; + /* More than one trailing dot? */ + if (*n == '.') + return -EINVAL; + if (sz >= 1 && d) *d = 0; diff --git a/src/shared/gcrypt-util.h b/src/shared/gcrypt-util.h index cf33b3c59c..1da12a32be 100644 --- a/src/shared/gcrypt-util.h +++ b/src/shared/gcrypt-util.h @@ -37,3 +37,11 @@ static inline int string_hashsum_sha224(const char *s, size_t len, char **out) { return -EOPNOTSUPP; #endif } + +static inline int string_hashsum_sha256(const char *s, size_t len, char **out) { +#ifdef HAVE_GCRYPT + return string_hashsum(s, len, GCRY_MD_SHA256, out); +#else + return -EOPNOTSUPP; +#endif +} diff --git a/src/shared/install.c b/src/shared/install.c index 11770d887f..60a6d1312d 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -403,6 +403,9 @@ static bool chroot_symlinks_same(const char *root, const char *wd, const char *a /* This will give incorrect results if the paths are relative and go outside * of the chroot. False negatives are possible. */ + if (!root) + root = "/"; + a = strjoina(path_is_absolute(a) ? root : wd, "/", a); b = strjoina(path_is_absolute(b) ? root : wd, "/", b); return path_equal_or_files_same(a, b); @@ -1017,7 +1020,7 @@ static int config_parse_alias( type = unit_name_to_type(name); if (!unit_type_may_alias(type)) return log_syntax(unit, LOG_WARNING, filename, line, 0, - "Aliases are not allowed for %s units, ignoring.", + "Alias= is not allowed for %s units, ignoring.", unit_type_to_string(type)); return config_parse_strv(unit, filename, line, section, section_line, @@ -1095,7 +1098,7 @@ static int config_parse_default_instance( return 0; if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) return log_syntax(unit, LOG_WARNING, filename, line, 0, - "DefaultInstance only makes sense for template units, ignoring."); + "DefaultInstance= only makes sense for template units, ignoring."); r = install_full_printf(i, rvalue, &printed); if (r < 0) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 2f42381fc1..8116c7671f 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -39,6 +39,10 @@ const char* seccomp_arch_to_string(uint32_t c) { return "x32"; if (c == SCMP_ARCH_ARM) return "arm"; + if (c == SCMP_ARCH_S390) + return "s390"; + if (c == SCMP_ARCH_S390X) + return "s390x"; return NULL; } @@ -59,6 +63,10 @@ int seccomp_arch_from_string(const char *n, uint32_t *ret) { *ret = SCMP_ARCH_X32; else if (streq(n, "arm")) *ret = SCMP_ARCH_ARM; + else if (streq(n, "s390")) + *ret = SCMP_ARCH_S390; + else if (streq(n, "s390x")) + *ret = SCMP_ARCH_S390X; else return -EINVAL; @@ -85,6 +93,20 @@ int seccomp_add_secondary_archs(scmp_filter_ctx *c) { if (r < 0 && r != -EEXIST) return r; +#elif defined(__s390__) || defined(__s390x__) + int r; + + /* Add in all possible secondary archs we are aware of that + * this kernel might support. */ + + r = seccomp_arch_add(c, SCMP_ARCH_S390); + if (r < 0 && r != -EEXIST) + return r; + + r = seccomp_arch_add(c, SCMP_ARCH_S390X); + if (r < 0 && r != -EEXIST) + return r; + #endif return 0; diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index f00624d0f2..ed31a80c8d 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -58,7 +58,7 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { {} }; - config_parse_many(PKGSYSCONFDIR "/sleep.conf", + config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf", CONF_PATHS_NULSTR("systemd/sleep.conf.d"), "Sleep\0", config_item_table_lookup, items, false, NULL); diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index ce7c26e7d3..fbc1e0eb1a 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -41,12 +41,12 @@ static char **arg_prefixes = NULL; static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d"); -static int apply_all(Hashmap *sysctl_options) { +static int apply_all(OrderedHashmap *sysctl_options) { char *property, *value; Iterator i; int r = 0; - HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { + ORDERED_HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { int k; k = sysctl_write(property, value); @@ -62,7 +62,7 @@ static int apply_all(Hashmap *sysctl_options) { return r; } -static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) { +static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) { _cleanup_fclose_ FILE *f = NULL; int r; @@ -125,13 +125,13 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno } found: - existing = hashmap_get2(sysctl_options, p, &v); + existing = ordered_hashmap_get2(sysctl_options, p, &v); if (existing) { if (streq(value, existing)) continue; log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path); - free(hashmap_remove(sysctl_options, p)); + free(ordered_hashmap_remove(sysctl_options, p)); free(v); } @@ -145,7 +145,7 @@ found: return log_oom(); } - k = hashmap_put(sysctl_options, property, new_value); + k = ordered_hashmap_put(sysctl_options, property, new_value); if (k < 0) { log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); free(property); @@ -230,7 +230,7 @@ static int parse_argv(int argc, char *argv[]) { int main(int argc, char *argv[]) { int r = 0, k; - Hashmap *sysctl_options; + OrderedHashmap *sysctl_options; r = parse_argv(argc, argv); if (r <= 0) @@ -242,7 +242,7 @@ int main(int argc, char *argv[]) { umask(0022); - sysctl_options = hashmap_new(&string_hash_ops); + sysctl_options = ordered_hashmap_new(&string_hash_ops); if (!sysctl_options) { r = log_oom(); goto finish; @@ -280,7 +280,7 @@ int main(int argc, char *argv[]) { r = k; finish: - hashmap_free_free_free(sysctl_options); + ordered_hashmap_free_free_free(sysctl_options); strv_free(arg_prefixes); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 5912441168..18a8a4f248 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -118,6 +118,7 @@ static enum dependency { } arg_dependency = DEPENDENCY_FORWARD; static const char *arg_job_mode = "replace"; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; +static bool arg_wait = false; static bool arg_no_block = false; static bool arg_no_legend = false; static bool arg_no_pager = false; @@ -2679,13 +2680,86 @@ static const char *method_to_verb(const char *method) { return "n/a"; } +typedef struct { + sd_bus_slot *match; + sd_event *event; + Set *unit_paths; + bool any_failed; +} WaitContext; + +static void wait_context_free(WaitContext *c) { + c->match = sd_bus_slot_unref(c->match); + c->event = sd_event_unref(c->event); + c->unit_paths = set_free(c->unit_paths); +} + +static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + WaitContext *c = userdata; + const char *path; + int r; + + path = sd_bus_message_get_path(m); + if (!set_contains(c->unit_paths, path)) + return 0; + + /* Check if ActiveState changed to inactive/failed */ + /* (s interface, a{sv} changed_properties, as invalidated_properties) */ + r = sd_bus_message_skip(m, "s"); + if (r < 0) + return bus_log_parse_error(r); + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *s; + bool is_failed; + + r = sd_bus_message_read(m, "s", &s); + if (r < 0) + return bus_log_parse_error(r); + if (streq(s, "ActiveState")) { + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "s"); + if (r < 0) + return bus_log_parse_error(r); + r = sd_bus_message_read(m, "s", &s); + if (r < 0) + return bus_log_parse_error(r); + is_failed = streq(s, "failed"); + if (streq(s, "inactive") || is_failed) { + log_debug("%s became %s, dropping from --wait tracking", path, s); + set_remove(c->unit_paths, path); + c->any_failed |= is_failed; + } else + log_debug("ActiveState on %s changed to %s", path, s); + break; /* no need to dissect the rest of the message */ + } else { + /* other property */ + r = sd_bus_message_skip(m, "v"); + if (r < 0) + return bus_log_parse_error(r); + } + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + } + if (r < 0) + return bus_log_parse_error(r); + + if (set_isempty(c->unit_paths)) + sd_event_exit(c->event, EXIT_SUCCESS); + + return 0; +} + static int start_unit_one( sd_bus *bus, const char *method, const char *name, const char *mode, sd_bus_error *error, - BusWaitForJobs *w) { + BusWaitForJobs *w, + WaitContext *wait_context) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; const char *path; @@ -2696,6 +2770,40 @@ static int start_unit_one( assert(mode); assert(error); + if (wait_context) { + _cleanup_free_ char *unit_path = NULL; + const char* mt; + + log_debug("Watching for property changes of %s", name); + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "RefUnit", + error, + NULL, + "s", name); + if (r < 0) + return log_error_errno(r, "Failed to RefUnit %s: %s", name, bus_error_message(error, r)); + + unit_path = unit_dbus_path_from_name(name); + if (!unit_path) + return log_oom(); + + r = set_put_strdup(wait_context->unit_paths, unit_path); + if (r < 0) + return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path); + + mt = strjoina("type='signal'," + "interface='org.freedesktop.DBus.Properties'," + "path='", unit_path, "'," + "member='PropertiesChanged'"); + r = sd_bus_add_match(bus, &wait_context->match, mt, on_properties_changed, wait_context); + if (r < 0) + return log_error_errno(r, "Failed to add match for PropertiesChanged signal: %m"); + } + log_debug("Calling manager for %s on %s, %s", method, name, mode); r = sd_bus_call_method( @@ -2841,10 +2949,18 @@ static int start_unit(int argc, char *argv[], void *userdata) { const char *method, *mode, *one_name, *suffix = NULL; _cleanup_strv_free_ char **names = NULL; sd_bus *bus; + _cleanup_(wait_context_free) WaitContext wait_context = {}; char **name; int r = 0; - r = acquire_bus(BUS_MANAGER, &bus); + if (arg_wait && !strstr(argv[0], "start")) { + log_error("--wait may only be used with a command that starts units."); + return -EINVAL; + } + + /* we cannot do sender tracking on the private bus, so we need the full + * one for RefUnit to implement --wait */ + r = acquire_bus(arg_wait ? BUS_FULL : BUS_MANAGER, &bus); if (r < 0) return r; @@ -2888,11 +3004,36 @@ static int start_unit(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Could not watch jobs: %m"); } + if (arg_wait) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + wait_context.unit_paths = set_new(&string_hash_ops); + if (!wait_context.unit_paths) + return log_oom(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Subscribe", + &error, + NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to enable subscription: %s", bus_error_message(&error, r)); + r = sd_event_default(&wait_context.event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + r = sd_bus_attach_event(bus, wait_context.event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + } + STRV_FOREACH(name, names) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int q; - q = start_unit_one(bus, method, *name, mode, &error, w); + q = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); if (r >= 0 && q < 0) r = translate_bus_error_to_exit_status(q, &error); } @@ -2924,6 +3065,15 @@ static int start_unit(int argc, char *argv[], void *userdata) { check_triggering_units(bus, *name); } + if (r >= 0 && arg_wait) { + int q; + q = sd_event_loop(wait_context.event); + if (q < 0) + return log_error_errno(q, "Failed to run event loop: %m"); + if (wait_context.any_failed) + r = EXIT_FAILURE; + } + return r; } @@ -3121,7 +3271,7 @@ static int logind_check_inhibitors(enum action a) { if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) continue; - if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty"))) + if (sd_session_get_type(*s, &type) < 0 || !STR_IN_SET(type, "x11", "tty")) continue; sd_session_get_tty(*s, &tty); @@ -3622,7 +3772,7 @@ static void print_status_info( if (streq_ptr(i->active_state, "failed")) { active_on = ansi_highlight_red(); active_off = ansi_normal(); - } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) { + } else if (STRPTR_IN_SET(i->active_state, "active", "reloading")) { active_on = ansi_highlight_green(); active_off = ansi_normal(); } else @@ -3703,12 +3853,10 @@ static void print_status_info( if (!isempty(i->result) && !streq(i->result, "success")) printf(" (Result: %s)", i->result); - timestamp = (streq_ptr(i->active_state, "active") || - streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp : - (streq_ptr(i->active_state, "inactive") || - streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp : - streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp : - i->active_exit_timestamp; + timestamp = STRPTR_IN_SET(i->active_state, "active", "reloading") ? i->active_enter_timestamp : + STRPTR_IN_SET(i->active_state, "inactive", "failed") ? i->inactive_enter_timestamp : + STRPTR_IN_SET(i->active_state, "activating") ? i->inactive_exit_timestamp : + i->active_exit_timestamp; s1 = format_timestamp_relative(since1, sizeof(since1), timestamp); s2 = format_timestamp(since2, sizeof(since2), timestamp); @@ -3788,7 +3936,7 @@ static void print_status_info( argv = strv_join(p->argv, " "); printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv)); - good = is_clean_exit_lsb(p->code, p->status, NULL); + good = is_clean_exit(p->code, p->status, EXIT_CLEAN_DAEMON, NULL); if (!good) { on = ansi_highlight_red(); off = ansi_normal(); @@ -4583,7 +4731,8 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return 0; - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "IODeviceWeight") || streq(name, "BlockIODeviceWeight"))) { + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && + STR_IN_SET(name, "IODeviceWeight", "BlockIODeviceWeight")) { const char *path; uint64_t weight; @@ -4602,8 +4751,9 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return 0; - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (cgroup_io_limit_type_from_string(name) >= 0 || - streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) { + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && + (cgroup_io_limit_type_from_string(name) >= 0 || + STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth"))) { const char *path; uint64_t bandwidth; @@ -4695,12 +4845,14 @@ static int show_one( return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); if (streq_ptr(info.load_state, "not-found") && streq_ptr(info.active_state, "inactive")) { - log_error("Unit %s could not be found.", unit); + log_full(streq(verb, "status") ? LOG_ERR : LOG_DEBUG, + "Unit %s could not be found.", unit); if (streq(verb, "status")) return EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN; - return -ENOENT; + if (!streq(verb, "show")) + return -ENOENT; } r = sd_bus_message_rewind(reply, true); @@ -4765,10 +4917,11 @@ static int show_one( r = 0; if (show_properties) { char **pp; + int not_found_level = streq(verb, "show") ? LOG_DEBUG : LOG_WARNING; STRV_FOREACH(pp, arg_properties) if (!set_contains(found_properties, *pp)) { - log_warning("Property %s does not exist.", *pp); + log_full(not_found_level, "Property %s does not exist.", *pp); r = -ENXIO; } @@ -6584,6 +6737,7 @@ static void systemctl_help(void) { " -s --signal=SIGNAL Which signal to send\n" " --now Start or stop unit in addition to enabling or disabling it\n" " -q --quiet Suppress output\n" + " --wait For (re)start, wait until service stopped again\n" " --no-block Do not wait until operation finished\n" " --no-wall Don't send wall message before halt/power-off/reboot\n" " --no-reload Don't reload daemon after en-/dis-abling unit files\n" @@ -6854,6 +7008,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_FIRMWARE_SETUP, ARG_NOW, ARG_MESSAGE, + ARG_WAIT, }; static const struct option options[] = { @@ -6877,6 +7032,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, { "global", no_argument, NULL, ARG_GLOBAL }, + { "wait", no_argument, NULL, ARG_WAIT }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, @@ -7057,6 +7213,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_scope = UNIT_FILE_GLOBAL; break; + case ARG_WAIT: + arg_wait = true; + break; + case ARG_NO_BLOCK: arg_no_block = true; break; @@ -7232,6 +7392,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_wait && arg_no_block) { + log_error("--wait may not be combined with --no-block."); + return -EINVAL; + } + return 1; } diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 3c44d63021..79246ae060 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -40,6 +40,7 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) #define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) #define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) #define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 39821687b9..c2c80175a2 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "dirent-util.h" +#include "exit-status.h" #include "fd-util.h" #include "fileio.h" #include "hashmap.h" @@ -199,6 +200,13 @@ static int generate_unit_file(SysvStub *s) { if (s->pid_file) fprintf(f, "PIDFile=%s\n", s->pid_file); + /* Consider two special LSB exit codes a clean exit */ + if (s->has_lsb) + fprintf(f, + "SuccessExitStatus=%i %i\n", + EXIT_NOTINSTALLED, + EXIT_NOTCONFIGURED); + fprintf(f, "ExecStart=%s start\n" "ExecStop=%s stop\n", diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c index 430dda8e78..5b572bb0bf 100644 --- a/src/test/test-acl-util.c +++ b/src/test/test-acl-util.c @@ -35,7 +35,7 @@ static void test_add_acls_for_user(void) { uid_t uid; int r; - fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(fn); assert_se(fd >= 0); /* Use the mode that user journal files use */ diff --git a/src/test/test-async.c b/src/test/test-async.c index ada6d67c42..4ebc27f0bd 100644 --- a/src/test/test-async.c +++ b/src/test/test-async.c @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) { int fd; char name[] = "/tmp/test-asynchronous_close.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); asynchronous_close(fd); diff --git a/src/test/test-clock.c b/src/test/test-clock.c index 84f775e5bc..7d97328416 100644 --- a/src/test/test-clock.c +++ b/src/test/test-clock.c @@ -55,7 +55,7 @@ static void test_clock_is_localtime(void) { /* without an adjtime file we default to UTC */ assert_se(clock_is_localtime("/nonexisting/adjtime") == 0); - fd = mkostemp_safe(adjtime, O_WRONLY|O_CLOEXEC); + fd = mkostemp_safe(adjtime); assert_se(fd >= 0); log_info("adjtime test file: %s", adjtime); f = fdopen(fd, "w"); diff --git a/src/test/test-copy.c b/src/test/test-copy.c index 68154fc4e8..ed1ea51dbd 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -42,11 +42,11 @@ static void test_copy_file(void) { log_info("%s", __func__); - fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(fn); assert_se(fd >= 0); close(fd); - fd = mkostemp_safe(fn_copy, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(fn_copy); assert_se(fd >= 0); close(fd); @@ -71,9 +71,9 @@ static void test_copy_file_fd(void) { log_info("%s", __func__); - in_fd = mkostemp_safe(in_fn, O_RDWR); + in_fd = mkostemp_safe(in_fn); assert_se(in_fd >= 0); - out_fd = mkostemp_safe(out_fn, O_RDWR); + out_fd = mkostemp_safe(out_fn); assert_se(out_fd >= 0); assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0); @@ -207,10 +207,10 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY); assert_se(fd >= 0); - fd2 = mkostemp_safe(fn2, O_RDWR); + fd2 = mkostemp_safe(fn2); assert_se(fd2 >= 0); - fd3 = mkostemp_safe(fn3, O_WRONLY); + fd3 = mkostemp_safe(fn3); assert_se(fd3 >= 0); r = copy_bytes(fd, fd2, max_bytes, try_reflink); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index a9d09f59bc..e2f097c95e 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -48,6 +48,7 @@ static void test_dns_label_unescape(void) { test_dns_label_unescape_one("..", "", 20, -EINVAL); test_dns_label_unescape_one(".foobar", "", 20, -EINVAL); test_dns_label_unescape_one("foobar.", "foobar", 20, 6); + test_dns_label_unescape_one("foobar..", "foobar", 20, -EINVAL); } static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) { @@ -359,6 +360,7 @@ static void test_dns_name_is_valid_one(const char *s, int ret) { static void test_dns_name_is_valid(void) { test_dns_name_is_valid_one("foo", 1); test_dns_name_is_valid_one("foo.", 1); + test_dns_name_is_valid_one("foo..", 0); test_dns_name_is_valid_one("Foo", 1); test_dns_name_is_valid_one("foo.bar", 1); test_dns_name_is_valid_one("foo.bar.baz", 1); @@ -366,6 +368,7 @@ static void test_dns_name_is_valid(void) { test_dns_name_is_valid_one("foo..bar", 0); test_dns_name_is_valid_one(".foo.bar", 0); test_dns_name_is_valid_one("foo.bar.", 1); + test_dns_name_is_valid_one("foo.bar..", 0); test_dns_name_is_valid_one("\\zbar", 0); test_dns_name_is_valid_one("ä", 1); test_dns_name_is_valid_one("\n", 0); diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 23da10fa1a..a651f6b683 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(TEST_DIR) >= 0); r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); + log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 05ec1d2eb1..8b4ff22495 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -133,6 +133,28 @@ static void test_exec_privatedevices(Manager *m) { test(m, "exec-privatedevices-no.service", 0, CLD_EXITED); } +static void test_exec_privatedevices_capabilities(Manager *m) { + if (detect_container() > 0) { + log_notice("testing in container, skipping private device tests"); + return; + } + test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED); + test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED); +} + +static void test_exec_readonlypaths(Manager *m) { + test(m, "exec-readonlypaths.service", 0, CLD_EXITED); + test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED); +} + +static void test_exec_readwritepaths(Manager *m) { + test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED); +} + +static void test_exec_inaccessiblepaths(Manager *m) { + test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED); +} + static void test_exec_systemcallfilter(Manager *m) { #ifdef HAVE_SECCOMP if (!is_seccomp_available()) @@ -324,7 +346,7 @@ static int run_tests(UnitFileScope scope, test_function_t *tests) { r = manager_new(scope, true, &m); if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); + log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; } assert_se(r >= 0); @@ -345,6 +367,10 @@ int main(int argc, char *argv[]) { test_exec_ignoresigpipe, test_exec_privatetmp, test_exec_privatedevices, + test_exec_privatedevices_capabilities, + test_exec_readonlypaths, + test_exec_readwritepaths, + test_exec_inaccessiblepaths, test_exec_privatenetwork, test_exec_systemcallfilter, test_exec_systemcallerrornumber, diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 421d3bdeb3..f555bb976c 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -31,9 +31,9 @@ static void test_close_many(void) { char name1[] = "/tmp/test-close-many.XXXXXX"; char name2[] = "/tmp/test-close-many.XXXXXX"; - fds[0] = mkostemp_safe(name0, O_RDWR|O_CLOEXEC); - fds[1] = mkostemp_safe(name1, O_RDWR|O_CLOEXEC); - fds[2] = mkostemp_safe(name2, O_RDWR|O_CLOEXEC); + fds[0] = mkostemp_safe(name0); + fds[1] = mkostemp_safe(name1); + fds[2] = mkostemp_safe(name2); close_many(fds, 2); @@ -52,7 +52,7 @@ static void test_close_nointr(void) { char name[] = "/tmp/test-test-close_nointr.XXXXXX"; int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(close_nointr(fd) >= 0); assert_se(close_nointr(fd) < 0); diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c index 282aab1246..adbf99a7ec 100644 --- a/src/test/test-fdset.c +++ b/src/test/test-fdset.c @@ -31,7 +31,7 @@ static void test_fdset_new_fill(void) { _cleanup_fdset_free_ FDSet *fdset = NULL; char name[] = "/tmp/test-fdset_new_fill.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(fdset_new_fill(&fdset) >= 0); assert_se(fdset_contains(fdset, fd)); @@ -45,7 +45,7 @@ static void test_fdset_put_dup(void) { _cleanup_fdset_free_ FDSet *fdset = NULL; char name[] = "/tmp/test-fdset_put_dup.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); @@ -64,7 +64,7 @@ static void test_fdset_cloexec(void) { int flags = -1; char name[] = "/tmp/test-fdset_cloexec.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); @@ -91,7 +91,7 @@ static void test_fdset_close_others(void) { int flags = -1; char name[] = "/tmp/test-fdset_close_others.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); @@ -113,7 +113,7 @@ static void test_fdset_remove(void) { FDSet *fdset = NULL; char name[] = "/tmp/test-fdset_remove.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); @@ -136,7 +136,7 @@ static void test_fdset_iterate(void) { int c = 0; int a; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); @@ -161,7 +161,7 @@ static void test_fdset_isempty(void) { _cleanup_fdset_free_ FDSet *fdset = NULL; char name[] = "/tmp/test-fdset_isempty.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); @@ -179,7 +179,7 @@ static void test_fdset_steal_first(void) { _cleanup_fdset_free_ FDSet *fdset = NULL; char name[] = "/tmp/test-fdset_steal_first.XXXXXX"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); fdset = fdset_new(); diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 79609765e0..92663ef66f 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -45,11 +45,11 @@ static void test_parse_env_file(void) { char **i; unsigned k; - fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(p); assert_se(fd >= 0); close(fd); - fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(t); assert_se(fd >= 0); f = fdopen(fd, "w"); @@ -158,11 +158,11 @@ static void test_parse_multiline_env_file(void) { _cleanup_strv_free_ char **a = NULL, **b = NULL; char **i; - fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(p); assert_se(fd >= 0); close(fd); - fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(t); assert_se(fd >= 0); f = fdopen(fd, "w"); @@ -211,7 +211,7 @@ static void test_executable_is_script(void) { FILE *f; char *command; - fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(t); assert_se(fd >= 0); f = fdopen(fd, "w"); @@ -300,7 +300,7 @@ static void test_write_string_stream(void) { int fd; char buf[64]; - fd = mkostemp_safe(fn, O_RDWR); + fd = mkostemp_safe(fn); assert_se(fd >= 0); f = fdopen(fd, "r"); @@ -334,7 +334,7 @@ static void test_write_string_file(void) { char buf[64] = {}; _cleanup_close_ int fd; - fd = mkostemp_safe(fn, O_RDWR); + fd = mkostemp_safe(fn); assert_se(fd >= 0); assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0); @@ -350,7 +350,7 @@ static void test_write_string_file_no_create(void) { _cleanup_close_ int fd; char buf[64] = {0}; - fd = mkostemp_safe(fn, O_RDWR); + fd = mkostemp_safe(fn); assert_se(fd >= 0); assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0); @@ -390,7 +390,7 @@ static void test_load_env_file_pairs(void) { _cleanup_strv_free_ char **l = NULL; char **k, **v; - fd = mkostemp_safe(fn, O_RDWR); + fd = mkostemp_safe(fn); assert_se(fd >= 0); r = write_string_file(fn, @@ -433,7 +433,7 @@ static void test_search_and_fopen(void) { int r; FILE *f; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); close(fd); @@ -469,7 +469,7 @@ static void test_search_and_fopen_nulstr(void) { int r; FILE *f; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); close(fd); @@ -504,7 +504,7 @@ static void test_writing_tmpfile(void) { IOVEC_SET_STRING(iov[1], ALPHANUMERICAL "\n"); IOVEC_SET_STRING(iov[2], ""); - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); printf("tmpfile: %s", name); r = writev(fd, iov, 3); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 93eec3ef9c..53a3cdc663 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -20,21 +20,114 @@ #include <unistd.h> #include "alloc-util.h" -#include "fileio.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "mkdir.h" +#include "path-util.h" #include "rm-rf.h" #include "string-util.h" #include "strv.h" #include "util.h" +static void test_chase_symlinks(void) { + _cleanup_free_ char *result = NULL; + char temp[] = "/tmp/test-chase.XXXXXX"; + const char *top, *p, *q; + int r; + + assert_se(mkdtemp(temp)); + + top = strjoina(temp, "/top"); + assert_se(mkdir(top, 0700) >= 0); + + p = strjoina(top, "/dot"); + assert_se(symlink(".", p) >= 0); + + p = strjoina(top, "/dotdot"); + assert_se(symlink("..", p) >= 0); + + p = strjoina(top, "/dotdota"); + assert_se(symlink("../a", p) >= 0); + + p = strjoina(temp, "/a"); + assert_se(symlink("b", p) >= 0); + + p = strjoina(temp, "/b"); + assert_se(symlink("/usr", p) >= 0); + + p = strjoina(temp, "/start"); + assert_se(symlink("top/dot/dotdota", p) >= 0); + + r = chase_symlinks(p, NULL, &result); + assert_se(r >= 0); + assert_se(path_equal(result, "/usr")); + + result = mfree(result); + r = chase_symlinks(p, temp, &result); + assert_se(r == -ENOENT); + + q = strjoina(temp, "/usr"); + assert_se(mkdir(q, 0700) >= 0); + + r = chase_symlinks(p, temp, &result); + assert_se(r >= 0); + assert_se(path_equal(result, q)); + + p = strjoina(temp, "/slash"); + assert_se(symlink("/", p) >= 0); + + result = mfree(result); + r = chase_symlinks(p, NULL, &result); + assert_se(r >= 0); + assert_se(path_equal(result, "/")); + + result = mfree(result); + r = chase_symlinks(p, temp, &result); + assert_se(r >= 0); + assert_se(path_equal(result, temp)); + + p = strjoina(temp, "/slashslash"); + assert_se(symlink("///usr///", p) >= 0); + + result = mfree(result); + r = chase_symlinks(p, NULL, &result); + assert_se(r >= 0); + assert_se(path_equal(result, "/usr")); + + result = mfree(result); + r = chase_symlinks(p, temp, &result); + assert_se(r >= 0); + assert_se(path_equal(result, q)); + + result = mfree(result); + r = chase_symlinks("/etc/./.././", NULL, &result); + assert_se(r >= 0); + assert_se(path_equal(result, "/")); + + result = mfree(result); + r = chase_symlinks("/etc/./.././", "/etc", &result); + assert_se(r == -EINVAL); + + result = mfree(result); + r = chase_symlinks("/etc/machine-id/foo", NULL, &result); + assert_se(r == -ENOTDIR); + + result = mfree(result); + p = strjoina(temp, "/recursive-symlink"); + assert_se(symlink("recursive-symlink", p) >= 0); + r = chase_symlinks(p, NULL, &result); + assert_se(r == -ELOOP); + + assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); +} + static void test_unlink_noerrno(void) { char name[] = "/tmp/test-close_nointr.XXXXXX"; int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(close_nointr(fd) >= 0); @@ -83,7 +176,7 @@ static void test_get_files_in_directory(void) { } static void test_var_tmp(void) { - _cleanup_free_ char *tmpdir_backup = NULL; + _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL; const char *tmp_dir = NULL, *t; t = getenv("TMPDIR"); @@ -92,7 +185,21 @@ static void test_var_tmp(void) { assert_se(tmpdir_backup); } + t = getenv("TEMP"); + if (t) { + temp_backup = strdup(t); + assert_se(temp_backup); + } + + t = getenv("TMP"); + if (t) { + tmp_backup = strdup(t); + assert_se(tmp_backup); + } + assert(unsetenv("TMPDIR") >= 0); + assert(unsetenv("TEMP") >= 0); + assert(unsetenv("TMP") >= 0); assert_se(var_tmp_dir(&tmp_dir) >= 0); assert_se(streq(tmp_dir, "/var/tmp")); @@ -113,6 +220,16 @@ static void test_var_tmp(void) { assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0); assert_se(streq(getenv("TMPDIR"), tmpdir_backup)); } + + if (temp_backup) { + assert_se(setenv("TEMP", temp_backup, true) >= 0); + assert_se(streq(getenv("TEMP"), temp_backup)); + } + + if (tmp_backup) { + assert_se(setenv("TMP", tmp_backup, true) >= 0); + assert_se(streq(getenv("TMP"), tmp_backup)); + } } int main(int argc, char *argv[]) { @@ -120,6 +237,7 @@ int main(int argc, char *argv[]) { test_readlink_and_make_absolute(); test_get_files_in_directory(); test_var_tmp(); + test_chase_symlinks(); return 0; } diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c index 227d4290f0..9eea3eb608 100644 --- a/src/test/test-glob-util.c +++ b/src/test/test-glob-util.c @@ -30,7 +30,7 @@ static void test_glob_exists(void) { int fd = -1; int r; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); close(fd); diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index 1c3d13ed1d..d2c3ea5e0d 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -104,7 +104,7 @@ static void test_read_hostname_config(void) { char *hostname; int fd; - fd = mkostemp_safe(path, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(path); assert(fd > 0); close(fd); diff --git a/src/test/test-list.c b/src/test/test-list.c index 160064d06a..0ccd745cc9 100644 --- a/src/test/test-list.c +++ b/src/test/test-list.c @@ -132,6 +132,29 @@ int main(int argc, const char *argv[]) { assert_se(items[1].item_prev == &items[3]); assert_se(items[3].item_prev == NULL); + LIST_INSERT_BEFORE(item, head, &items[3], &items[0]); + assert_se(items[2].item_next == NULL); + assert_se(items[1].item_next == &items[2]); + assert_se(items[3].item_next == &items[1]); + assert_se(items[0].item_next == &items[3]); + + assert_se(items[2].item_prev == &items[1]); + assert_se(items[1].item_prev == &items[3]); + assert_se(items[3].item_prev == &items[0]); + assert_se(items[0].item_prev == NULL); + assert_se(head == &items[0]); + + LIST_REMOVE(item, head, &items[0]); + assert_se(LIST_JUST_US(item, &items[0])); + + assert_se(items[2].item_next == NULL); + assert_se(items[1].item_next == &items[2]); + assert_se(items[3].item_next == &items[1]); + + assert_se(items[2].item_prev == &items[1]); + assert_se(items[1].item_prev == &items[3]); + assert_se(items[3].item_prev == NULL); + LIST_INSERT_BEFORE(item, head, NULL, &items[0]); assert_se(items[0].item_next == NULL); assert_se(items[2].item_next == &items[0]); diff --git a/src/test/test-ns.c b/src/test/test-ns.c index 9248f2987c..c4d4da6d05 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -26,13 +26,18 @@ int main(int argc, char *argv[]) { const char * const writable[] = { "/home", + "-/home/lennart/projects/foobar", /* this should be masked automatically */ NULL }; const char * const readonly[] = { - "/", - "/usr", + /* "/", */ + /* "/usr", */ "/boot", + "/lib", + "/usr/lib", + "-/lib64", + "-/usr/lib64", NULL }; @@ -42,11 +47,12 @@ int main(int argc, char *argv[]) { }; char *root_directory; char *projects_directory; - int r; char tmp_dir[] = "/tmp/systemd-private-XXXXXX", var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX"; + log_set_max_level(LOG_DEBUG); + assert_se(mkdtemp(tmp_dir)); assert_se(mkdtemp(var_tmp_dir)); @@ -69,6 +75,8 @@ int main(int argc, char *argv[]) { tmp_dir, var_tmp_dir, true, + true, + true, PROTECT_HOME_NO, PROTECT_SYSTEM_NO, 0); diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 164a10d8a8..0b10d8e25e 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -511,7 +511,24 @@ static void test_hidden_or_backup_file(void) { assert_se(!hidden_or_backup_file("test.dpkg-old.foo")); } +static void test_systemd_installation_has_version(const char *path) { + int r; + const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999}; + unsigned i; + + for (i = 0; i < ELEMENTSOF(versions); i++) { + r = systemd_installation_has_version(path, versions[i]); + assert_se(r >= 0); + log_info("%s has systemd >= %u: %s", + path ?: "Current installation", versions[i], yes_no(r)); + } +} + int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + test_path(); test_find_binary(argv[0]); test_prefixes(); @@ -526,5 +543,7 @@ int main(int argc, char **argv) { test_filename_is_valid(); test_hidden_or_backup_file(); + test_systemd_installation_has_version(argv[1]); /* NULL is OK */ + return 0; } diff --git a/src/test/test-path.c b/src/test/test-path.c index 62181e22a0..4d3f0e9948 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -47,7 +47,7 @@ static int setup_test(Manager **m) { r = manager_new(UNIT_FILE_USER, true, &tmp); if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); + log_notice_errno(r, "Skipping test: manager_new: %m"); return -EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index c068f5c39e..7b37910c33 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(TEST_DIR) >= 0); r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); + log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index a10227f823..6c34250a01 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -31,7 +31,7 @@ static void test_files_same(void) { char name[] = "/tmp/test-files_same.XXXXXX"; char name_alias[] = "/tmp/test-files_same.alias"; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(symlink(name, name_alias) >= 0); @@ -47,7 +47,7 @@ static void test_is_symlink(void) { char name_link[] = "/tmp/test-is_symlink.link"; _cleanup_close_ int fd = -1; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(symlink(name, name_link) >= 0); diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 841a36782f..ce20f2dd5b 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -54,6 +54,25 @@ static void test_specifier_printf(void) { puts(w); } +static void test_str_in_set(void) { + assert_se(STR_IN_SET("x", "x", "y", "z")); + assert_se(!STR_IN_SET("X", "x", "y", "z")); + assert_se(!STR_IN_SET("", "x", "y", "z")); + assert_se(STR_IN_SET("x", "w", "x")); +} + +static void test_strptr_in_set(void) { + assert_se(STRPTR_IN_SET("x", "x", "y", "z")); + assert_se(!STRPTR_IN_SET("X", "x", "y", "z")); + assert_se(!STRPTR_IN_SET("", "x", "y", "z")); + assert_se(STRPTR_IN_SET("x", "w", "x")); + + assert_se(!STRPTR_IN_SET(NULL, "x", "y", "z")); + assert_se(!STRPTR_IN_SET(NULL, "")); + /* strv cannot contain a null, hence the result below */ + assert_se(!STRPTR_IN_SET(NULL, NULL)); +} + static const char* const input_table_multiple[] = { "one", "two", @@ -703,6 +722,8 @@ static void test_strv_fnmatch(void) { int main(int argc, char *argv[]) { test_specifier_printf(); + test_str_in_set(); + test_strptr_in_set(); test_strv_foreach(); test_strv_foreach_backwards(); test_strv_foreach_pair(); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 84b448a095..373a1b70ba 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -50,7 +50,7 @@ static void test_read_one_char(void) { char name[] = "/tmp/test-read_one_char.XXXXXX"; int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); file = fdopen(fd, "r+"); assert_se(file); diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c index b34ebeefb2..f35e6793b7 100644 --- a/src/test/test-tmpfiles.c +++ b/src/test/test-tmpfiles.c @@ -51,7 +51,7 @@ int main(int argc, char** argv) { log_debug("link1: %s", ans); assert_se(endswith(ans, " (deleted)")); - fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC); + fd2 = mkostemp_safe(pattern); assert_se(fd >= 0); assert_se(unlink(pattern) == 0); diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index ade0ff2a63..7ef087a2e3 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -56,12 +56,12 @@ static int test_unit_file_get_set(void) { r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); if (r == -EPERM || r == -EACCES) { - printf("Skipping test: unit_file_get_list: %s", strerror(-r)); + log_notice_errno(r, "Skipping test: unit_file_get_list: %m"); return EXIT_TEST_SKIP; } - log_full(r == 0 ? LOG_INFO : LOG_ERR, - "unit_file_get_list: %s", strerror(-r)); + log_full_errno(r == 0 ? LOG_INFO : LOG_ERR, r, + "unit_file_get_list: %m"); if (r < 0) return EXIT_FAILURE; @@ -117,7 +117,7 @@ static void test_config_parse_exec(void) { r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); + log_notice_errno(r, "Skipping test: manager_new: %m"); return; } @@ -485,7 +485,7 @@ static void test_load_env_file_1(void) { char name[] = "/tmp/test-load-env-file.XXXXXX"; _cleanup_close_ int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1)); @@ -508,7 +508,7 @@ static void test_load_env_file_2(void) { char name[] = "/tmp/test-load-env-file.XXXXXX"; _cleanup_close_ int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2)); @@ -526,7 +526,7 @@ static void test_load_env_file_3(void) { char name[] = "/tmp/test-load-env-file.XXXXXX"; _cleanup_close_ int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3)); @@ -542,7 +542,7 @@ static void test_load_env_file_4(void) { _cleanup_close_ int fd; int r; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4)); @@ -562,7 +562,7 @@ static void test_load_env_file_5(void) { char name[] = "/tmp/test-load-env-file.XXXXXX"; _cleanup_close_ int fd; - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + fd = mkostemp_safe(name); assert_se(fd >= 0); assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5)); diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c index 20c64a3354..bf25b112e1 100644 --- a/src/timesync/timesyncd-conf.c +++ b/src/timesync/timesyncd-conf.c @@ -98,7 +98,7 @@ int config_parse_servers( int manager_parse_config_file(Manager *m) { assert(m); - return config_parse_many(PKGSYSCONFDIR "/timesyncd.conf", + return config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf", CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"), "Time\0", config_item_perf_lookup, timesyncd_gperf_lookup, diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 8851af449d..b45490be1a 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -827,7 +827,7 @@ static int ask_on_consoles(int argc, char *argv[]) { break; } - if (!is_clean_exit(status.si_code, status.si_status, NULL)) + if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL)) log_error("Password agent failed with: %d", status.si_status); terminate_agents(pids); diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index b1aa0223fd..708a665576 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -25,6 +25,7 @@ #include "conf-parser.h" #include "ethtool-util.h" #include "log.h" +#include "socket-util.h" #include "string-table.h" #include "strxcpyx.h" #include "util.h" @@ -59,10 +60,9 @@ int ethtool_connect(int *ret) { assert_return(ret, -EINVAL); - fd = socket(PF_INET, SOCK_DGRAM, 0); + fd = socket_ioctl_fd(); if (fd < 0) - return -errno; - + return fd; *ret = fd; return 0; diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index eedd94e777..ece9248c2a 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -191,20 +191,12 @@ static int load_link(link_config_ctx *ctx, const char *filename) { } static bool enable_name_policy(void) { - _cleanup_free_ char *line = NULL; - const char *word, *state; + _cleanup_free_ char *value = NULL; int r; - size_t l; - r = proc_cmdline(&line); - if (r < 0) { - log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); - return true; - } - - FOREACH_WORD_QUOTED(word, l, line, state) - if (strneq(word, "net.ifnames=0", l)) - return false; + r = get_proc_cmdline_key("net.ifnames=", &value); + if (r > 0 && streq(value, "0")) + return false; return true; } diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index 6e9adc6e96..1825ee75a7 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -693,6 +693,15 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool parent = skip_subsystem(parent, "iucv"); supported_transport = true; supported_parent = true; + } else if (streq(subsys, "nvme")) { + const char *nsid = udev_device_get_sysattr_value(dev, "nsid"); + + if (nsid) { + path_prepend(&path, "nvme-%s", nsid); + parent = skip_subsystem(parent, "nvme"); + supported_parent = true; + supported_transport = true; + } } if (parent) diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 989decbe95..6f8e96a123 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -20,6 +20,7 @@ #include <string.h> #include <unistd.h> +#include "time-util.h" #include "udev-util.h" #include "udev.h" @@ -60,7 +61,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) { }; if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); + log_error("root privileges required"); return 1; } @@ -81,7 +82,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) { i = util_log_priority(optarg); if (i < 0) { - fprintf(stderr, "invalid number '%s'\n", optarg); + log_error("invalid number '%s'", optarg); return rc; } if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) @@ -110,7 +111,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) { break; case 'p': if (strchr(optarg, '=') == NULL) { - fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg); + log_error("expect <KEY>=<value> instead of '%s'", optarg); return rc; } if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) @@ -124,7 +125,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) { i = strtoul(optarg, &endp, 0); if (endp[0] != '\0' || i < 1) { - fprintf(stderr, "invalid number '%s'\n", optarg); + log_error("invalid number '%s'", optarg); return rc; } if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) @@ -134,13 +135,21 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) { break; } case 't': { + usec_t s; int seconds; + int r; - seconds = atoi(optarg); - if (seconds >= 0) + r = parse_sec(optarg, &s); + if (r < 0) + return log_error_errno(r, "Failed to parse timeout value '%s'.", optarg); + + if (((s + USEC_PER_SEC - 1) / USEC_PER_SEC) > INT_MAX) + log_error("Timeout value is out of range."); + else { + seconds = s != USEC_INFINITY ? (int) ((s + USEC_PER_SEC - 1) / USEC_PER_SEC) : INT_MAX; timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); + rc = 0; + } break; } case 'h': @@ -150,9 +159,9 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) { } if (optind < argc) - fprintf(stderr, "Extraneous argument: %s\n", argv[optind]); + log_error("Extraneous argument: %s", argv[optind]); else if (optind == 1) - fprintf(stderr, "Option missing\n"); + log_error("Option missing"); return rc; } diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 19f1c29198..535d317c27 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1738,8 +1738,13 @@ int main(int argc, char *argv[]) { log_info("starting version " VERSION); /* connect /dev/null to stdin, stdout, stderr */ - if (log_get_max_level() < LOG_DEBUG) - (void) make_null_stdio(); + if (log_get_max_level() < LOG_DEBUG) { + r = make_null_stdio(); + if (r < 0) + log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m"); + } + + pid = fork(); switch (pid) { diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index da306a4444..5cc5abfddf 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -23,67 +23,57 @@ #include "util.h" #define MESSAGE \ - "This file was created by systemd-update-done. Its only \n" \ - "purpose is to hold a timestamp of the time this directory\n" \ - "was updated. See systemd-update-done.service(8).\n" + "# This file was created by systemd-update-done. Its only \n" \ + "# purpose is to hold a timestamp of the time this directory\n" \ + "# was updated. See systemd-update-done.service(8).\n" static int apply_timestamp(const char *path, struct timespec *ts) { struct timespec twice[2] = { *ts, *ts }; - struct stat st; + int fd = -1; + _cleanup_fclose_ FILE *f = NULL; + int r; assert(path); assert(ts); - if (stat(path, &st) >= 0) { - /* Is the timestamp file already newer than the OS? If - * so, there's nothing to do. We ignore the nanosecond - * component of the timestamp, since some file systems - * do not support any better accuracy than 1s and we - * have no way to identify the accuracy - * available. Most notably ext4 on small disks (where - * 128 byte inodes are used) does not support better - * accuracy than 1s. */ - if (st.st_mtim.tv_sec > ts->tv_sec) - return 0; - - /* It is older? Then let's update it */ - if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) { - - if (errno == EROFS) - return log_debug("Can't update timestamp file %s, file system is read-only.", path); + /* + * We store the timestamp both as mtime of the file and in the file itself, + * to support filesystems which cannot store nanosecond-precision timestamps. + * Hence, don't bother updating the file, let's just rewrite it. + */ - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - } + r = mac_selinux_create_file_prepare(path, S_IFREG); + if (r < 0) + return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); - } else if (errno == ENOENT) { - _cleanup_close_ int fd = -1; - int r; + fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); + mac_selinux_create_file_clear(); - /* The timestamp file doesn't exist yet? Then let's create it. */ + if (fd < 0) { + if (errno == EROFS) + return log_debug("Can't create timestamp file %s, file system is read-only.", path); - r = mac_selinux_create_file_prepare(path, S_IFREG); - if (r < 0) - return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); - - fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); - mac_selinux_create_file_clear(); + return log_error_errno(errno, "Failed to create/open timestamp file %s: %m", path); + } - if (fd < 0) { - if (errno == EROFS) - return log_debug("Can't create timestamp file %s, file system is read-only.", path); + f = fdopen(fd, "w"); + if (!f) { + safe_close(fd); + return log_error_errno(errno, "Failed to fdopen() timestamp file %s: %m", path); + } - return log_error_errno(errno, "Failed to create timestamp file %s: %m", path); - } + (void) fprintf(f, + "%s" + "TimestampNSec=" NSEC_FMT "\n", + MESSAGE, timespec_load_nsec(ts)); - (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false); + fflush(f); - if (futimens(fd, twice) < 0) - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - } else - log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path); + if (futimens(fd, twice) < 0) + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); return 0; } diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index c0d76f9685..ac4ceb1486 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -75,7 +75,7 @@ static bool is_settable(int fd) { r = ioctl(fd, KDGKBMODE, &curr_mode); /* * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode. - * Oterwise we would (likely) interfere with X11's processing of the + * Otherwise we would (likely) interfere with X11's processing of the * key events. * * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html diff --git a/system-preset/90-systemd.preset b/system-preset/90-systemd.preset index 0f494b7552..6b5349dc8d 100644 --- a/system-preset/90-systemd.preset +++ b/system-preset/90-systemd.preset @@ -18,7 +18,6 @@ enable systemd-resolved.service enable systemd-networkd-wait-online.service disable console-getty.service -disable console-shell.service disable debug-shell.service disable halt.target diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh index 0c7d4439a2..fa6cf4181a 100755 --- a/test/TEST-03-JOBS/test-jobs.sh +++ b/test/TEST-03-JOBS/test-jobs.sh @@ -49,4 +49,32 @@ systemctl stop --job-mode=replace-irreversibly unstoppable.service || exit 1 # Shutdown of the container/VM will hang if not. systemctl start unstoppable.service || exit 1 +# Test waiting for a started unit(s) to terminate again +cat <<EOF > /run/systemd/system/wait2.service +[Unit] +Description=Wait for 2 seconds +[Service] +ExecStart=/bin/sh -ec 'sleep 2' +EOF +cat <<EOF > /run/systemd/system/wait5fail.service +[Unit] +Description=Wait for 5 seconds and fail +[Service] +ExecStart=/bin/sh -ec 'sleep 5; false' +EOF + +# wait2 succeeds +START_SEC=$(date -u '+%s') +systemctl start --wait wait2.service || exit 1 +END_SEC=$(date -u '+%s') +ELAPSED=$(($END_SEC-$START_SEC)) +[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 3 ]] || exit 1 + +# wait5fail fails, so systemctl should fail +START_SEC=$(date -u '+%s') +! systemctl start --wait wait2.service wait5fail.service || exit 1 +END_SEC=$(date -u '+%s') +ELAPSED=$(($END_SEC-$START_SEC)) +[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1 + touch /testok diff --git a/test/networkd-test.py b/test/networkd-test.py index bfa1bf3580..3091722fc1 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -54,7 +54,6 @@ class ClientTestBase: self.workdir_obj = tempfile.TemporaryDirectory() self.workdir = self.workdir_obj.name self.config = '/run/systemd/network/test_eth42.network' - os.makedirs(os.path.dirname(self.config), exist_ok=True) # avoid "Failed to open /dev/tty" errors in containers os.environ['SYSTEMD_LOG_TARGET'] = 'journal' @@ -77,10 +76,14 @@ class ClientTestBase: def tearDown(self): self.shutdown_iface() - if os.path.exists(self.config): - os.unlink(self.config) subprocess.call(['systemctl', 'stop', 'systemd-networkd']) + def writeConfig(self, fname, contents): + os.makedirs(os.path.dirname(fname), exist_ok=True) + with open(fname, 'w') as f: + f.write(contents) + self.addCleanup(os.remove, fname) + def show_journal(self, unit): '''Show journal of given unit since start of the test''' @@ -107,8 +110,8 @@ class ClientTestBase: def do_test(self, coldplug=True, ipv6=False, extra_opts='', online_timeout=10, dhcp_mode='yes'): subprocess.check_call(['systemctl', 'start', 'systemd-resolved']) - with open(self.config, 'w') as f: - f.write('''[Match] + self.writeConfig(self.config, '''\ +[Match] Name=%s [Network] DHCP=%s @@ -225,6 +228,32 @@ DHCP=%s self.do_test(coldplug=False, ipv6=True) def test_route_only_dns(self): + self.writeConfig('/run/systemd/network/myvpn.netdev', '''\ +[NetDev] +Name=dummy0 +Kind=dummy +MACAddress=12:34:56:78:9a:bc''') + self.writeConfig('/run/systemd/network/myvpn.network', '''\ +[Match] +Name=dummy0 +[Network] +Address=192.168.42.100 +DNS=192.168.42.1 +Domains= ~company''') + + self.do_test(coldplug=True, ipv6=False, + extra_opts='IPv6AcceptRouterAdvertisements=False') + + with open(RESOLV_CONF) as f: + contents = f.read() + # ~company is not a search domain, only a routing domain + self.assertNotRegex(contents, 'search.*company') + # our global server should appear + self.assertIn('nameserver 192.168.5.1\n', contents) + # should not have domain-restricted server as global server + self.assertNotIn('nameserver 192.168.42.1\n', contents) + + def test_route_only_dns_all_domains(self): with open('/run/systemd/network/myvpn.netdev', 'w') as f: f.write('''[NetDev] Name=dummy0 @@ -236,7 +265,7 @@ Name=dummy0 [Network] Address=192.168.42.100 DNS=192.168.42.1 -Domains= ~company''') +Domains= ~company ~.''') self.addCleanup(os.remove, '/run/systemd/network/myvpn.netdev') self.addCleanup(os.remove, '/run/systemd/network/myvpn.network') @@ -245,10 +274,14 @@ Domains= ~company''') with open(RESOLV_CONF) as f: contents = f.read() - # ~company is not a search domain, only a routing domain - self.assertNotRegex(contents, 'search.*company') - # our global server should appear - self.assertIn('nameserver 192.168.5.1\n', contents) + + # ~company is not a search domain, only a routing domain + self.assertNotRegex(contents, 'search.*company') + + # our global server should appear + self.assertIn('nameserver 192.168.5.1\n', contents) + # should have company server as global server due to ~. + self.assertIn('nameserver 192.168.42.1\n', contents) @unittest.skipUnless(have_dnsmasq, 'dnsmasq not installed') @@ -259,7 +292,7 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): super().setUp() self.dnsmasq = None - def create_iface(self, ipv6=False): + def create_iface(self, ipv6=False, dnsmasq_opts=None): '''Create test interface with DHCP server behind it''' # add veth pair @@ -280,6 +313,8 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): extra_opts = ['--enable-ra', '--dhcp-range=2600::10,2600::20'] else: extra_opts = [] + if dnsmasq_opts: + extra_opts += dnsmasq_opts self.dnsmasq = subprocess.Popen( ['dnsmasq', '--keep-in-foreground', '--log-queries', '--log-facility=' + self.dnsmasq_log, '--conf-file=/dev/null', @@ -304,6 +339,80 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): with open(self.dnsmasq_log) as f: sys.stdout.write('\n\n---- dnsmasq log ----\n%s\n------\n\n' % f.read()) + def test_resolved_domain_restricted_dns(self): + '''resolved: domain-restricted DNS servers''' + + # create interface for generic connections; this will map all DNS names + # to 192.168.42.1 + self.create_iface(dnsmasq_opts=['--address=/#/192.168.42.1']) + self.writeConfig('/run/systemd/network/general.network', '''\ +[Match] +Name=%s +[Network] +DHCP=ipv4 +IPv6AcceptRA=False''' % self.iface) + + # create second device/dnsmasq for a .company/.lab VPN interface + # static IPs for simplicity + subprocess.check_call(['ip', 'link', 'add', 'name', 'testvpnclient', 'type', + 'veth', 'peer', 'name', 'testvpnrouter']) + self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', 'testvpnrouter']) + subprocess.check_call(['ip', 'a', 'flush', 'dev', 'testvpnrouter']) + subprocess.check_call(['ip', 'a', 'add', '10.241.3.1/24', 'dev', 'testvpnrouter']) + subprocess.check_call(['ip', 'link', 'set', 'testvpnrouter', 'up']) + + vpn_dnsmasq_log = os.path.join(self.workdir, 'dnsmasq-vpn.log') + vpn_dnsmasq = subprocess.Popen( + ['dnsmasq', '--keep-in-foreground', '--log-queries', + '--log-facility=' + vpn_dnsmasq_log, '--conf-file=/dev/null', + '--dhcp-leasefile=/dev/null', '--bind-interfaces', + '--interface=testvpnrouter', '--except-interface=lo', + '--address=/math.lab/10.241.3.3', '--address=/cantina.company/10.241.4.4']) + self.addCleanup(vpn_dnsmasq.wait) + self.addCleanup(vpn_dnsmasq.kill) + + self.writeConfig('/run/systemd/network/vpn.network', '''\ +[Match] +Name=testvpnclient +[Network] +IPv6AcceptRA=False +Address=10.241.3.2/24 +DNS=10.241.3.1 +Domains= ~company ~lab''') + + subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + subprocess.check_call([self.networkd_wait_online, '--interface', self.iface, + '--interface=testvpnclient', '--timeout=20']) + + # ensure we start fresh with every test + subprocess.check_call(['systemctl', 'restart', 'systemd-resolved']) + + # test vpnclient specific domains; these should *not* be answered by + # the general DNS + out = subprocess.check_output(['systemd-resolve', 'math.lab']) + self.assertIn(b'math.lab: 10.241.3.3', out) + out = subprocess.check_output(['systemd-resolve', 'kettle.cantina.company']) + self.assertIn(b'kettle.cantina.company: 10.241.4.4', out) + + # test general domains + out = subprocess.check_output(['systemd-resolve', 'megasearch.net']) + self.assertIn(b'megasearch.net: 192.168.42.1', out) + + with open(self.dnsmasq_log) as f: + general_log = f.read() + with open(vpn_dnsmasq_log) as f: + vpn_log = f.read() + + # VPN domains should only be sent to VPN DNS + self.assertRegex(vpn_log, 'query.*math.lab') + self.assertRegex(vpn_log, 'query.*cantina.company') + self.assertNotIn('lab', general_log) + self.assertNotIn('company', general_log) + + # general domains should not be sent to the VPN DNS + self.assertRegex(general_log, 'query.*megasearch.net') + self.assertNotIn('megasearch.net', vpn_log) + class NetworkdClientTest(ClientTestBase, unittest.TestCase): '''Test networkd client against networkd server''' @@ -320,7 +429,8 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase): (fd, script) = tempfile.mkstemp(prefix='networkd-router.sh') self.addCleanup(os.remove, script) with os.fdopen(fd, 'w+') as f: - f.write('''#!/bin/sh -eu + f.write('''\ +#!/bin/sh -eu mkdir -p /run/systemd/network mkdir -p /run/systemd/netif mount -t tmpfs none /run/systemd/network @@ -398,20 +508,18 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ { s/^.*=// # we don't use this interface for this test self.if_router = None - with open('/run/systemd/network/test.netdev', 'w') as f: - f.write('''[NetDev] + self.writeConfig('/run/systemd/network/test.netdev', '''\ +[NetDev] Name=dummy0 Kind=dummy MACAddress=12:34:56:78:9a:bc''') - with open('/run/systemd/network/test.network', 'w') as f: - f.write('''[Match] + self.writeConfig('/run/systemd/network/test.network', '''\ +[Match] Name=dummy0 [Network] Address=192.168.42.100 DNS=192.168.42.1 Domains= one two three four five six seven eight nine ten''') - self.addCleanup(os.remove, '/run/systemd/network/test.netdev') - self.addCleanup(os.remove, '/run/systemd/network/test.network') subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) @@ -432,23 +540,18 @@ Domains= one two three four five six seven eight nine ten''') name_prefix = 'a' * 60 - with open('/run/systemd/network/test.netdev', 'w') as f: - f.write('''[NetDev] + self.writeConfig('/run/systemd/network/test.netdev', '''\ +[NetDev] Name=dummy0 Kind=dummy MACAddress=12:34:56:78:9a:bc''') - with open('/run/systemd/network/test.network', 'w') as f: - f.write('''[Match] + self.writeConfig('/run/systemd/network/test.network', '''\ +[Match] Name=dummy0 [Network] Address=192.168.42.100 DNS=192.168.42.1 -Domains=''') - for i in range(5): - f.write('%s%i ' % (name_prefix, i)) - - self.addCleanup(os.remove, '/run/systemd/network/test.netdev') - self.addCleanup(os.remove, '/run/systemd/network/test.network') +Domains={p}0 {p}1 {p}2 {p}3 {p}4'''.format(p=name_prefix)) subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) @@ -458,9 +561,38 @@ Domains=''') if ' one' in contents: break time.sleep(0.1) - self.assertRegex(contents, 'search .*%(p)s0 %(p)s1 %(p)s2' % {'p': name_prefix}) + self.assertRegex(contents, 'search .*{p}0 {p}1 {p}2'.format(p=name_prefix)) self.assertIn('# Total length of all search domains is too long, remaining ones ignored.', contents) + def test_dropin(self): + # we don't use this interface for this test + self.if_router = None + + self.writeConfig('/run/systemd/network/test.netdev', '''\ +[NetDev] +Name=dummy0 +Kind=dummy +MACAddress=12:34:56:78:9a:bc''') + self.writeConfig('/run/systemd/network/test.network', '''\ +[Match] +Name=dummy0 +[Network] +Address=192.168.42.100 +DNS=192.168.42.1''') + self.writeConfig('/run/systemd/network/test.network.d/dns.conf', '''\ +[Network] +DNS=127.0.0.1''') + + subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + + for timeout in range(50): + with open(RESOLV_CONF) as f: + contents = f.read() + if ' 127.0.0.1' in contents: + break + time.sleep(0.1) + self.assertIn('nameserver 192.168.42.1\n', contents) + self.assertIn('nameserver 127.0.0.1\n', contents) if __name__ == '__main__': unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, diff --git a/test/test-execute/exec-inaccessiblepaths-mount-propagation.service b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service new file mode 100644 index 0000000000..23c6ff3f93 --- /dev/null +++ b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test to make sure that InaccessiblePaths= disconnect mount propagation + +[Service] +InaccessiblePaths=-/i-dont-exist +ExecStart=/bin/sh -x -c 'mkdir -p /TEST; mount -t tmpfs tmpfs /TEST; grep TEST /proc/self/mountinfo && ! grep TEST /proc/$${PPID}/mountinfo && ! grep TEST /proc/1/mountinfo' +Type=oneshot diff --git a/test/test-execute/exec-privatedevices-no-capability-mknod.service b/test/test-execute/exec-privatedevices-no-capability-mknod.service new file mode 100644 index 0000000000..6d39469da8 --- /dev/null +++ b/test/test-execute/exec-privatedevices-no-capability-mknod.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test CAP_MKNOD capability for PrivateDevices=no + +[Service] +PrivateDevices=no +ExecStart=/bin/sh -x -c 'capsh --print | grep cap_mknod' +Type=oneshot diff --git a/test/test-execute/exec-privatedevices-yes-capability-mknod.service b/test/test-execute/exec-privatedevices-yes-capability-mknod.service new file mode 100644 index 0000000000..fb1fc2875a --- /dev/null +++ b/test/test-execute/exec-privatedevices-yes-capability-mknod.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test CAP_MKNOD capability for PrivateDevices=yes + +[Service] +PrivateDevices=yes +ExecStart=/bin/sh -x -c '! capsh --print | grep cap_mknod' +Type=oneshot diff --git a/test/test-execute/exec-readonlypaths-mount-propagation.service b/test/test-execute/exec-readonlypaths-mount-propagation.service new file mode 100644 index 0000000000..237cbb2efb --- /dev/null +++ b/test/test-execute/exec-readonlypaths-mount-propagation.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test to make sure that passing ReadOnlyPaths= disconnect mount propagation + +[Service] +ReadOnlyPaths=-/i-dont-exist +ExecStart=/bin/sh -x -c 'mkdir -p /TEST; mount -t tmpfs tmpfs /TEST; grep TEST /proc/self/mountinfo && ! grep TEST /proc/$${PPID}/mountinfo && ! grep TEST /proc/1/mountinfo' +Type=oneshot diff --git a/test/test-execute/exec-readonlypaths.service b/test/test-execute/exec-readonlypaths.service new file mode 100644 index 0000000000..6866fdc700 --- /dev/null +++ b/test/test-execute/exec-readonlypaths.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test for ReadOnlyPaths= + +[Service] +ReadOnlyPaths=/etc -/i-dont-exist /usr +ExecStart=/bin/sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var' +Type=oneshot diff --git a/test/test-execute/exec-readwritepaths-mount-propagation.service b/test/test-execute/exec-readwritepaths-mount-propagation.service new file mode 100644 index 0000000000..466ce6c747 --- /dev/null +++ b/test/test-execute/exec-readwritepaths-mount-propagation.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test to make sure that passing ReadWritePaths= disconnect mount propagation + +[Service] +ReadWritePaths=-/i-dont-exist +ExecStart=/bin/sh -x -c 'mkdir -p /TEST; mount -t tmpfs tmpfs /TEST; grep TEST /proc/self/mountinfo && ! grep TEST /proc/$${PPID}/mountinfo && ! grep TEST /proc/1/mountinfo' +Type=oneshot diff --git a/units/.gitignore b/units/.gitignore index 47e99154ee..8f4949258e 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,8 +1,6 @@ /user@.service.m4 /console-getty.service /console-getty.service.m4 -/console-shell.service -/console-shell.service.m4 /container-getty@.service /container-getty@.service.m4 /debug-shell.service diff --git a/units/console-shell.service.m4.in b/units/console-shell.service.m4.in deleted file mode 100644 index a345ec25d4..0000000000 --- a/units/console-shell.service.m4.in +++ /dev/null @@ -1,31 +0,0 @@ -# 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. - -[Unit] -Description=Console Shell -Documentation=man:sulogin(8) -After=systemd-user-sessions.service plymouth-quit-wait.service -m4_ifdef(`HAVE_SYSV_COMPAT', -After=rc-local.service -)m4_dnl -Before=getty.target - -[Service] -Environment=HOME=/root -WorkingDirectory=-/root -ExecStart=-@SULOGIN@ -ExecStopPost=-@SYSTEMCTL@ poweroff -Type=idle -StandardInput=tty-force -StandardOutput=inherit -StandardError=inherit -KillMode=process -IgnoreSIGPIPE=no -SendSIGHUP=yes - -[Install] -WantedBy=getty.target diff --git a/units/initrd-switch-root.target b/units/initrd-switch-root.target index f34768790b..934d82f667 100644 --- a/units/initrd-switch-root.target +++ b/units/initrd-switch-root.target @@ -12,5 +12,5 @@ DefaultDependencies=no Requires=initrd-switch-root.service Before=initrd-switch-root.service AllowIsolate=yes -Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service +Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in index 0b03a589ea..edc5a1722a 100644 --- a/units/systemd-hostnamed.service.in +++ b/units/systemd-hostnamed.service.in @@ -13,12 +13,16 @@ Documentation=http://www.freedesktop.org/wiki/Software/systemd/hostnamed [Service] ExecStart=@rootlibexecdir@/systemd-hostnamed BusName=org.freedesktop.hostname1 -CapabilityBoundingSet=CAP_SYS_ADMIN WatchdogSec=3min +CapabilityBoundingSet=CAP_SYS_ADMIN PrivateTmp=yes PrivateDevices=yes PrivateNetwork=yes ProtectSystem=yes ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelTunables=yes MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/units/systemd-importd.service.in b/units/systemd-importd.service.in index 0f5489e7e3..ac27c2bcba 100644 --- a/units/systemd-importd.service.in +++ b/units/systemd-importd.service.in @@ -13,9 +13,11 @@ Documentation=http://www.freedesktop.org/wiki/Software/systemd/importd [Service] ExecStart=@rootlibexecdir@/systemd-importd BusName=org.freedesktop.import1 -CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE -NoNewPrivileges=yes WatchdogSec=3min KillMode=mixed +CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE +NoNewPrivileges=yes MemoryDenyWriteExecute=yes -SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io diff --git a/units/systemd-journal-gatewayd.service.in b/units/systemd-journal-gatewayd.service.in index f4f845841d..efefaa4244 100644 --- a/units/systemd-journal-gatewayd.service.in +++ b/units/systemd-journal-gatewayd.service.in @@ -20,6 +20,11 @@ PrivateDevices=yes PrivateNetwork=yes ProtectSystem=full ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelTunables=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 # If there are many split upjournal files we need a lot of fds to # access them all and combine diff --git a/units/systemd-journal-remote.service.in b/units/systemd-journal-remote.service.in index fdf3da4b64..753dd6c158 100644 --- a/units/systemd-journal-remote.service.in +++ b/units/systemd-journal-remote.service.in @@ -11,15 +11,20 @@ Documentation=man:systemd-journal-remote(8) man:journal-remote.conf(5) Requires=systemd-journal-remote.socket [Service] -ExecStart=@rootlibexecdir@/systemd-journal-remote \ - --listen-https=-3 \ - --output=/var/log/journal/remote/ +ExecStart=@rootlibexecdir@/systemd-journal-remote --listen-https=-3 --output=/var/log/journal/remote/ User=systemd-journal-remote Group=systemd-journal-remote +WatchdogSec=3min PrivateTmp=yes PrivateDevices=yes PrivateNetwork=yes -WatchdogSec=3min +ProtectSystem=full +ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelTunables=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 [Install] Also=systemd-journal-remote.socket diff --git a/units/systemd-journal-upload.service.in b/units/systemd-journal-upload.service.in index 1f488ff425..c709543af5 100644 --- a/units/systemd-journal-upload.service.in +++ b/units/systemd-journal-upload.service.in @@ -11,13 +11,19 @@ Documentation=man:systemd-journal-upload(8) After=network.target [Service] -ExecStart=@rootlibexecdir@/systemd-journal-upload \ - --save-state +ExecStart=@rootlibexecdir@/systemd-journal-upload --save-state User=systemd-journal-upload SupplementaryGroups=systemd-journal +WatchdogSec=3min PrivateTmp=yes PrivateDevices=yes -WatchdogSec=3min +ProtectSystem=full +ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelTunables=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 # If there are many split up journal files we need a lot of fds to # access them all and combine diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in index 08ace8ae44..712ce55483 100644 --- a/units/systemd-journald.service.in +++ b/units/systemd-journald.service.in @@ -21,10 +21,12 @@ Restart=always RestartSec=0 NotifyAccess=all StandardOutput=null -CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE WatchdogSec=3min FileDescriptorStoreMax=1024 +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_NETLINK SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io # Increase the default a bit in order to allow many simultaneous diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in index 1f3151c2b5..df829e1164 100644 --- a/units/systemd-localed.service.in +++ b/units/systemd-localed.service.in @@ -13,12 +13,16 @@ Documentation=http://www.freedesktop.org/wiki/Software/systemd/localed [Service] ExecStart=@rootlibexecdir@/systemd-localed BusName=org.freedesktop.locale1 -CapabilityBoundingSet= WatchdogSec=3min +CapabilityBoundingSet= PrivateTmp=yes PrivateDevices=yes PrivateNetwork=yes ProtectSystem=yes ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelTunables=yes MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index bee08d011f..0b6de35733 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -23,9 +23,11 @@ ExecStart=@rootlibexecdir@/systemd-logind Restart=always RestartSec=0 BusName=org.freedesktop.login1 -CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG WatchdogSec=3min +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io # Increase the default a bit in order to allow many simultaneous diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in index dcf9f347b7..911ead79ee 100644 --- a/units/systemd-machined.service.in +++ b/units/systemd-machined.service.in @@ -15,9 +15,11 @@ After=machine.slice [Service] ExecStart=@rootlibexecdir@/systemd-machined BusName=org.freedesktop.machine1 -CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD WatchdogSec=3min +CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io # Note that machined cannot be placed in a mount namespace, since it diff --git a/units/systemd-networkd.service.m4.in b/units/systemd-networkd.service.m4.in index 38d967d2d1..a968d8bd45 100644 --- a/units/systemd-networkd.service.m4.in +++ b/units/systemd-networkd.service.m4.in @@ -27,11 +27,14 @@ Type=notify Restart=on-failure RestartSec=0 ExecStart=@rootlibexecdir@/systemd-networkd +WatchdogSec=3min CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER ProtectSystem=full ProtectHome=yes -WatchdogSec=3min +ProtectControlGroups=yes MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io [Install] diff --git a/units/systemd-resolved.service.m4.in b/units/systemd-resolved.service.m4.in index 15ab56a066..0f0440ddaf 100644 --- a/units/systemd-resolved.service.m4.in +++ b/units/systemd-resolved.service.m4.in @@ -23,11 +23,17 @@ Type=notify Restart=always RestartSec=0 ExecStart=@rootlibexecdir@/systemd-resolved +WatchdogSec=3min CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_NET_RAW CAP_NET_BIND_SERVICE +PrivateTmp=yes +PrivateDevices=yes ProtectSystem=full ProtectHome=yes -WatchdogSec=3min +ProtectControlGroups=yes +ProtectKernelTunables=yes MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io [Install] diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in index bc1795d747..e8c4d5ed4b 100644 --- a/units/systemd-timedated.service.in +++ b/units/systemd-timedated.service.in @@ -13,10 +13,14 @@ Documentation=http://www.freedesktop.org/wiki/Software/systemd/timedated [Service] ExecStart=@rootlibexecdir@/systemd-timedated BusName=org.freedesktop.timedate1 -CapabilityBoundingSet=CAP_SYS_TIME WatchdogSec=3min +CapabilityBoundingSet=CAP_SYS_TIME PrivateTmp=yes ProtectSystem=yes ProtectHome=yes +ProtectControlGroups=yes +ProtectKernelTunables=yes MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/units/systemd-timesyncd.service.in b/units/systemd-timesyncd.service.in index df1e339196..9a6c6ea60d 100644 --- a/units/systemd-timesyncd.service.in +++ b/units/systemd-timesyncd.service.in @@ -22,13 +22,17 @@ Type=notify Restart=always RestartSec=0 ExecStart=@rootlibexecdir@/systemd-timesyncd +WatchdogSec=3min CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER PrivateTmp=yes PrivateDevices=yes ProtectSystem=full ProtectHome=yes -WatchdogSec=3min +ProtectControlGroups=yes +ProtectKernelTunables=yes MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io [Install] diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in index 67e4c5fcd7..46d637883b 100644 --- a/units/systemd-udevd.service.in +++ b/units/systemd-udevd.service.in @@ -21,7 +21,10 @@ Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket Restart=always RestartSec=0 ExecStart=@rootlibexecdir@/systemd-udevd -MountFlags=slave KillMode=mixed WatchdogSec=3min TasksMax=infinity +MountFlags=slave +MemoryDenyWriteExecute=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 |