diff options
136 files changed, 2084 insertions, 833 deletions
diff --git a/Makefile.am b/Makefile.am index 50da458918..47c2ec8a8d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -633,7 +633,6 @@ EXTRA_DIST += \ units/halt-local.service.in GENERAL_ALIASES += \ - $(systemunitdir)/reboot.target $(pkgsysconfdir)/system/ctrl-alt-del.target \ $(systemunitdir)/machines.target $(pkgsysconfdir)/system/multi-user.target.wants/machines.target dist_doc_DATA = \ @@ -1685,6 +1684,10 @@ EXTRA_DIST += \ test/test-execute/exec-runtimedirectory-mode.service \ test/test-execute/exec-runtimedirectory-owner.service \ test/test-execute/exec-runtimedirectory-owner-nfsnobody.service \ + test/test-execute/exec-restrict-namespaces-no.service \ + test/test-execute/exec-restrict-namespaces-yes.service \ + test/test-execute/exec-restrict-namespaces-mnt.service \ + test/test-execute/exec-restrict-namespaces-mnt-blacklist.service \ test/bus-policy/hello.conf \ test/bus-policy/methods.conf \ test/bus-policy/ownerships.conf \ @@ -5611,37 +5614,39 @@ libnetworkd_core_la_CFLAGS = \ libnetworkd_core_la_SOURCES = \ src/libsystemd-network/network-internal.h \ - src/network/networkd.h \ + src/network/netdev/netdev.h \ + src/network/netdev/netdev.c \ + src/network/netdev/vrf.h \ + src/network/netdev/vrf.c \ + src/network/netdev/tunnel.h \ + src/network/netdev/tunnel.c \ + src/network/netdev/veth.h \ + src/network/netdev/veth.c \ + src/network/netdev/vxlan.h \ + src/network/netdev/vxlan.c \ + src/network/netdev/vlan.h \ + src/network/netdev/vlan.c \ + src/network/netdev/macvlan.h \ + src/network/netdev/macvlan.c \ + src/network/netdev/ipvlan.h \ + src/network/netdev/ipvlan.c \ + src/network/netdev/dummy.h \ + src/network/netdev/dummy.c \ + src/network/netdev/tuntap.h \ + src/network/netdev/tuntap.c \ + src/network/netdev/bond.h \ + src/network/netdev/bond.c \ + src/network/netdev/bridge.h \ + src/network/netdev/bridge.c \ + src/network/netdev/vcan.h \ + src/network/netdev/vcan.c \ + src/network/networkd-manager.h \ + src/network/networkd-manager.c \ + src/network/networkd-manager-bus.c \ src/network/networkd-conf.h \ src/network/networkd-conf.c \ src/network/networkd-link.h \ src/network/networkd-link.c \ - src/network/networkd-netdev.h \ - src/network/networkd-netdev.c \ - src/network/networkd-netdev-vrf.h \ - src/network/networkd-netdev-vrf.c \ - src/network/networkd-netdev-tunnel.h \ - src/network/networkd-netdev-tunnel.c \ - src/network/networkd-netdev-veth.h \ - src/network/networkd-netdev-veth.c \ - src/network/networkd-netdev-vxlan.h \ - src/network/networkd-netdev-vxlan.c \ - src/network/networkd-netdev-vlan.h \ - src/network/networkd-netdev-vlan.c \ - src/network/networkd-netdev-macvlan.h \ - src/network/networkd-netdev-macvlan.c \ - src/network/networkd-netdev-ipvlan.h \ - src/network/networkd-netdev-ipvlan.c \ - src/network/networkd-netdev-dummy.h \ - src/network/networkd-netdev-dummy.c \ - src/network/networkd-netdev-tuntap.h \ - src/network/networkd-netdev-tuntap.c \ - src/network/networkd-netdev-bond.h \ - src/network/networkd-netdev-bond.c \ - src/network/networkd-netdev-bridge.h \ - src/network/networkd-netdev-bridge.c \ - src/network/networkd-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 \ @@ -5655,8 +5660,6 @@ libnetworkd_core_la_SOURCES = \ src/network/networkd-address.c \ src/network/networkd-route.h \ src/network/networkd-route.c \ - src/network/networkd-manager.c \ - src/network/networkd-manager-bus.c \ src/network/networkd-fdb.h \ src/network/networkd-fdb.c \ src/network/networkd-brvlan.h \ @@ -5671,7 +5674,7 @@ libnetworkd_core_la_SOURCES = \ nodist_libnetworkd_core_la_SOURCES = \ src/network/networkd-gperf.c \ src/network/networkd-network-gperf.c \ - src/network/networkd-netdev-gperf.c + src/network/netdev/netdev-gperf.c libnetworkd_core_la_LIBADD = \ libsystemd-network.la \ @@ -5685,11 +5688,11 @@ systemd_networkd_wait_online_CFLAGS = \ systemd_networkd_wait_online_SOURCES = \ src/libsystemd-network/network-internal.h \ - src/network/networkd-wait-online.h \ - src/network/networkd-wait-online-link.h \ - src/network/networkd-wait-online.c \ - src/network/networkd-wait-online-manager.c \ - src/network/networkd-wait-online-link.c + src/network/wait-online/link.h \ + src/network/wait-online/link.c \ + src/network/wait-online/manager.h \ + src/network/wait-online/manager.c \ + src/network/wait-online/wait-online.c systemd_networkd_wait_online_LDADD = \ libsystemd-network.la \ @@ -5775,7 +5778,7 @@ endif gperf_gperf_sources += \ src/network/networkd-gperf.gperf \ src/network/networkd-network-gperf.gperf \ - src/network/networkd-netdev-gperf.gperf + src/network/netdev/netdev-gperf.gperf EXTRA_DIST += \ units/systemd-networkd.service.m4.in \ @@ -1,5 +1,22 @@ systemd System and Service Manager +CHANGES WITH 233 in spe + + * The confirmation spawn prompt has been reworked to offer the + following choices: + + (c)ontinue, proceed without asking anymore + (D)ump, show the state of the unit + (f)ail, don't execute the command and pretend it failed + (h)elp + (i)nfo, show a short summary of the unit + (j)obs, show jobs that are in progress + (s)kip, don't execute the command and pretend it succeeded + (y)es, execute the command + + The 'n' choice for the confirmation spawn prompt has been removed, + because its meaning was confusing. + CHANGES WITH 232: * The new RemoveIPC= option can be used to remove IPC objects owned by @@ -29,6 +29,11 @@ Janitorial Clean-ups: Features: +* replace all canonicalize_file_name() invocations by chase_symlinks(), in + particulr those where a rootdir is relevant. + +* add parse_ip_port() or so, that is like safe_atou16() but checks for != 0 + * drop nss-myhostname in favour of nss-resolve? * drop internal dlopen() based nss-dns fallback in nss-resolve, and rely on the @@ -560,7 +565,6 @@ Features: - man: maybe sort directives in man pages, and take sections from --help and apply them to man too * systemctl: - - systemctl list-jobs - show dependencies - add systemctl switch to dump transaction without executing it - Add a verbose mode to "systemctl start" and friends that explains what is being done or not done - "systemctl disable" on a static unit prints no message and does @@ -735,7 +739,6 @@ Features: - maybe introduce WantsMountsFor=? Usecase: http://lists.freedesktop.org/archives/systemd-devel/2015-January/027729.html - recreate systemd's D-Bus private socket file on SIGUSR2 - - 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. - hide PAM options in fragment parser when compile time disabled diff --git a/configure.ac b/configure.ac index 7f6b3b937c..65eaae1ae0 100644 --- a/configure.ac +++ b/configure.ac @@ -259,7 +259,7 @@ AC_CHECK_SIZEOF(rlim_t,,[ # we use python to build the man page index have_python=no AC_ARG_WITH([python], - [AS_HELP_STRING([--without-python], [Disable building the man page index and systemd-python (default: test)])]) + [AS_HELP_STRING([--without-python], [disable building the man page index and systemd-python (default: test)])]) have_lxml=no AS_IF([test "x$with_python" != "xno"], [ @@ -319,9 +319,10 @@ AC_CHECK_DECLS([ #include <linux/random.h> ]]) -AC_CHECK_TYPES([char16_t, char32_t, key_serial_t], +AC_CHECK_TYPES([char16_t, char32_t, key_serial_t, struct ethtool_link_settings], [], [], [[ #include <uchar.h> +#include <linux/ethtool.h> ]]) AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE, @@ -454,7 +455,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])) +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 >= 2.3.1], [AC_DEFINE(HAVE_SECCOMP, 1, [Define if seccomp is available]) @@ -469,7 +470,7 @@ AM_CONDITIONAL(HAVE_SECCOMP, [test "$have_seccomp" = "yes"]) # ------------------------------------------------------------------------------ have_ima=yes -AC_ARG_ENABLE([ima], AS_HELP_STRING([--disable-ima],[Disable optional IMA support]), +AC_ARG_ENABLE([ima], AS_HELP_STRING([--disable-ima], [disable optional IMA support]), [case "${enableval}" in yes) have_ima=yes ;; no) have_ima=no ;; @@ -483,7 +484,7 @@ fi # ------------------------------------------------------------------------------ have_selinux=no -AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support])) +AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [disable optional SELINUX support])) if test "x$enable_selinux" != "xno"; then PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9], [AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available]) @@ -497,7 +498,7 @@ fi AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) have_apparmor=no -AC_ARG_ENABLE(apparmor, AS_HELP_STRING([--disable-apparmor], [Disable optional AppArmor support])) +AC_ARG_ENABLE(apparmor, AS_HELP_STRING([--disable-apparmor], [disable optional AppArmor support])) if test "x$enable_apparmor" != "xno"; then PKG_CHECK_MODULES([APPARMOR], [libapparmor], [AC_DEFINE(HAVE_APPARMOR, 1, [Define if AppArmor is available]) @@ -529,7 +530,7 @@ AS_IF([test "x$enable_wheel_group" != "xno"], [ AC_ARG_WITH(debug-shell, AS_HELP_STRING([--with-debug-shell=PATH], - [Path to debug shell binary]), + [path to debug shell binary]), [SUSHELL="$withval"],[ AS_IF([test "x${have_selinux}" != "xno"], [SUSHELL="/sbin/sushell"] , [SUSHELL="/bin/sh"])]) @@ -537,7 +538,7 @@ AC_SUBST(SUSHELL) AC_ARG_WITH([debug-tty], AS_HELP_STRING([--with-debug-tty=PATH], - [Specify the tty device for debug shell]), + [specify the tty device for debug shell]), [DEBUGTTY="$withval"], [DEBUGTTY=/dev/tty9]) @@ -545,7 +546,7 @@ AC_SUBST(DEBUGTTY) AC_ARG_WITH([certificate-root], AS_HELP_STRING([--with-certificate-root=PATH], - [Specify the prefix for TLS certificates [/etc/ssl]]), + [specify the prefix for TLS certificates [/etc/ssl]]), [CERTIFICATEROOT="$withval"], [CERTIFICATEROOT="/etc/ssl"]) @@ -553,7 +554,7 @@ AC_SUBST(CERTIFICATEROOT) AC_ARG_WITH([support-url], AS_HELP_STRING([--with-support-url=URL], - [Specify the support URL to show in catalog entries included in systemd]), + [specify the support URL to show in catalog entries included in systemd]), [SUPPORT_URL="$withval"], [SUPPORT_URL=http://lists.freedesktop.org/mailman/listinfo/systemd-devel]) @@ -561,7 +562,7 @@ AC_SUBST(SUPPORT_URL) AC_ARG_WITH([nobody-user], AS_HELP_STRING([--with-nobody-user=NAME], - [Specify the name of the nobody user (the one with UID 65534)]), + [specify the name of the nobody user (the one with UID 65534)]), [NOBODY_USER_NAME="$withval"], [NOBODY_USER_NAME=nobody]) @@ -570,7 +571,7 @@ AC_DEFINE_UNQUOTED(NOBODY_USER_NAME, ["$NOBODY_USER_NAME"], [The name of the nob AC_ARG_WITH([nobody-group], AS_HELP_STRING([--with-nobody-group=NAME], - [Specify the name of the nobody group (the one with GID 65534)]), + [specify the name of the nobody group (the one with GID 65534)]), [NOBODY_GROUP_NAME="$withval"], [NOBODY_GROUP_NAME=nobody]) @@ -579,7 +580,7 @@ AC_DEFINE_UNQUOTED(NOBODY_GROUP_NAME, ["$NOBODY_GROUP_NAME"], [The name of the n # ------------------------------------------------------------------------------ have_xz=no -AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support])) +AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [disable optional XZ support])) AS_IF([test "x$enable_xz" != "xno"], [ PKG_CHECK_MODULES(XZ, [ liblzma ], [AC_DEFINE(HAVE_XZ, 1, [Define if XZ is available]) @@ -592,7 +593,7 @@ AM_CONDITIONAL(HAVE_XZ, [test "$have_xz" = "yes"]) # ------------------------------------------------------------------------------ have_zlib=no -AC_ARG_ENABLE(zlib, AS_HELP_STRING([--disable-zlib], [Disable optional ZLIB support])) +AC_ARG_ENABLE(zlib, AS_HELP_STRING([--disable-zlib], [disable optional ZLIB support])) AS_IF([test "x$enable_zlib" != "xno"], [ PKG_CHECK_MODULES(ZLIB, [ zlib ], [AC_DEFINE(HAVE_ZLIB, 1, [Define if ZLIB is available]) @@ -605,7 +606,7 @@ AM_CONDITIONAL(HAVE_ZLIB, [test "$have_zlib" = "yes"]) # ------------------------------------------------------------------------------ have_bzip2=no -AC_ARG_ENABLE(bzip2, AS_HELP_STRING([--enable-bzip2], [Enable optional BZIP2 support])) +AC_ARG_ENABLE(bzip2, AS_HELP_STRING([--disable-bzip2], [disable optional BZIP2 support])) AS_IF([test "x$enable_bzip2" != "xno"], [ AC_CHECK_HEADERS(bzlib.h, [AC_DEFINE(HAVE_BZIP2, 1, [Define if BZIP2 is available]) @@ -618,7 +619,7 @@ AM_CONDITIONAL(HAVE_BZIP2, [test "$have_bzip2" = "yes"]) # ------------------------------------------------------------------------------ have_lz4=no -AC_ARG_ENABLE(lz4, AS_HELP_STRING([--disable-lz4], [Disable optional LZ4 support])) +AC_ARG_ENABLE(lz4, AS_HELP_STRING([--disable-lz4], [disable optional LZ4 support])) AS_IF([test "x$enable_lz4" != "xno"], [ PKG_CHECK_MODULES(LZ4, [ liblz4 >= 125 ], [AC_DEFINE(HAVE_LZ4, 1, [Define in LZ4 is available]) @@ -633,7 +634,7 @@ AM_CONDITIONAL(HAVE_COMPRESSION, [test "$have_xz" = "yes" -o "$have_lz4" = "yes" # ------------------------------------------------------------------------------ AC_ARG_ENABLE([pam], - AS_HELP_STRING([--disable-pam],[Disable optional PAM support]), + AS_HELP_STRING([--disable-pam], [disable optional PAM support]), [case "${enableval}" in yes) have_pam=yes ;; no) have_pam=no ;; @@ -672,7 +673,7 @@ AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno]) # ------------------------------------------------------------------------------ AC_ARG_ENABLE([acl], - AS_HELP_STRING([--disable-acl],[Disable optional ACL support]), + AS_HELP_STRING([--disable-acl], [disable optional ACL support]), [case "${enableval}" in yes) have_acl=yes ;; no) have_acl=no ;; @@ -710,7 +711,7 @@ AC_SUBST(ACL_LIBS) AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno]) # ------------------------------------------------------------------------------ -AC_ARG_ENABLE([smack], AS_HELP_STRING([--disable-smack],[Disable optional SMACK support]), +AC_ARG_ENABLE([smack], AS_HELP_STRING([--disable-smack], [disable optional SMACK support]), [case "${enableval}" in yes) have_smack=yes ;; no) have_smack=no ;; @@ -745,7 +746,7 @@ AS_HELP_STRING([--with-smack-default-process-label=STRING], # ------------------------------------------------------------------------------ AC_ARG_ENABLE([gcrypt], - AS_HELP_STRING([--disable-gcrypt],[Disable optional GCRYPT support]), + AS_HELP_STRING([--disable-gcrypt], [disable optional GCRYPT support]), [case "${enableval}" in yes) have_gcrypt=yes ;; no) have_gcrypt=no ;; @@ -797,7 +798,7 @@ AM_CONDITIONAL([HAVE_GCRYPT], [test "x$have_gcrypt" != xno]) # ------------------------------------------------------------------------------ AC_ARG_ENABLE([audit], - AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]), + AS_HELP_STRING([--disable-audit], [disable optional AUDIT support]), [case "${enableval}" in yes) have_audit=yes ;; no) have_audit=no ;; @@ -835,7 +836,7 @@ AM_CONDITIONAL([HAVE_AUDIT], [test "x$have_audit" != xno]) # ------------------------------------------------------------------------------ AC_ARG_ENABLE([elfutils], - AS_HELP_STRING([--disable-elfutils],[Disable optional ELFUTILS support]), + AS_HELP_STRING([--disable-elfutils], [disable optional ELFUTILS support]), [case "${enableval}" in yes) have_elfutils=yes ;; no) have_elfutils=no ;; @@ -942,7 +943,7 @@ AM_CONDITIONAL(HAVE_REMOTE, [test "$have_microhttpd" = "yes" -o "$have_libcurl" # ------------------------------------------------------------------------------ have_libidn=no -AC_ARG_ENABLE(libidn, AS_HELP_STRING([--disable-libidn], [Disable optional LIBIDN support])) +AC_ARG_ENABLE(libidn, AS_HELP_STRING([--disable-libidn], [disable optional LIBIDN support])) if test "x$enable_libidn" != "xno"; then PKG_CHECK_MODULES(LIBIDN, [libidn], [AC_DEFINE(HAVE_LIBIDN, 1, [Define if libidn is available]) @@ -957,7 +958,7 @@ AM_CONDITIONAL(HAVE_LIBIDN, [test "$have_libidn" = "yes"]) # ------------------------------------------------------------------------------ have_libiptc=no -AC_ARG_ENABLE(libiptc, AS_HELP_STRING([--disable-libiptc], [Disable optional LIBIPTC support])) +AC_ARG_ENABLE(libiptc, AS_HELP_STRING([--disable-libiptc], [disable optional LIBIPTC support])) if test "x$enable_libiptc" != "xno"; then PKG_CHECK_MODULES(LIBIPTC, [libiptc], [AC_DEFINE(HAVE_LIBIPTC, 1, [Define if libiptc is available]) @@ -1052,7 +1053,7 @@ AM_CONDITIONAL(ENABLE_LOGIND, [test "$have_logind" = "yes"]) AS_IF([test "$have_logind" = "yes"], [ AC_DEFINE(HAVE_LOGIND, [1], [Logind support available]) ]) AC_ARG_WITH([kill-user-processes], - [AS_HELP_STRING([--without-kill-user-processes], [Set logind's KillUserProcesses=no by default])]) + [AS_HELP_STRING([--without-kill-user-processes], [set logind's KillUserProcesses=no by default])]) AS_IF([test "$with_kill_user_processes" != "no"], [kill_user_processes=true KILL_USER_PROCESSES=yes], @@ -1106,7 +1107,7 @@ AM_CONDITIONAL(ENABLE_TIMESYNCD, [test "$have_timesyncd" = "yes"]) AC_ARG_WITH(ntp-servers, AS_HELP_STRING([--with-ntp-servers=NTPSERVERS], - [Space-separated list of default NTP servers]), + [space-separated list of default NTP servers]), [NTP_SERVERS="$withval"], [NTP_SERVERS="time1.google.com time2.google.com time3.google.com time4.google.com" AC_MSG_WARN([*** Using Google NTP servers. @@ -1118,7 +1119,7 @@ AC_SUBST(NTP_SERVERS) AC_ARG_WITH(time-epoch, AS_HELP_STRING([--with-time-epoch=SECONDS], - [Time epoch for time clients]), + [time epoch for time clients]), [TIME_EPOCH="$withval"], [TIME_EPOCH="`stat -c %Y ${srcdir}/NEWS 2>/dev/null || echo 0`"]) @@ -1127,7 +1128,7 @@ AC_DEFINE_UNQUOTED(TIME_EPOCH, [$TIME_EPOCH], [Time Epoch]) # ------------------------------------------------------------------------------ AC_ARG_WITH(system-uid-max, AS_HELP_STRING([--with-system-uid-max=UID] - [Maximum UID for system users]), + [maximum UID for system users]), [SYSTEM_UID_MAX="$withval"], [SYSTEM_UID_MAX="`awk 'BEGIN { uid=999 } /^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }' /etc/login.defs 2>/dev/null || echo 999`"]) @@ -1137,7 +1138,7 @@ AC_SUBST(SYSTEM_UID_MAX) # ------------------------------------------------------------------------------ AC_ARG_WITH(system-gid-max, AS_HELP_STRING([--with-system-gid-max=GID] - [Maximum GID for system groups]), + [maximum GID for system groups]), [SYSTEM_GID_MAX="$withval"], [SYSTEM_GID_MAX="`awk 'BEGIN { gid=999 } /^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }' /etc/login.defs 2>/dev/null || echo 999`"]) @@ -1184,7 +1185,7 @@ AM_CONDITIONAL(ENABLE_RESOLVED, [test "$have_resolved" = "yes"]) AC_ARG_WITH(dns-servers, AS_HELP_STRING([--with-dns-servers=DNSSERVERS], - [Space-separated list of default DNS servers]), + [space-separated list of default DNS servers]), [DNS_SERVERS="$withval"], [DNS_SERVERS="8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844"]) @@ -1193,7 +1194,7 @@ AC_SUBST(DNS_SERVERS) AC_ARG_WITH(default-dnssec, AS_HELP_STRING([--with-default-dnssec=MODE], - [Default DNSSEC mode, defaults to "allow-downgrade"]), + [default DNSSEC mode, defaults to "allow-downgrade"]), [DEFAULT_DNSSEC_MODE="$withval"], [DEFAULT_DNSSEC_MODE="allow-downgrade"]) @@ -1243,7 +1244,7 @@ AC_SUBST([EFI_ARCH]) AC_SUBST([EFI_MACHINE_TYPE_NAME]) have_gnuefi=no -AC_ARG_ENABLE(gnuefi, AS_HELP_STRING([--enable-gnuefi], [Enable optional gnuefi support])) +AC_ARG_ENABLE(gnuefi, AS_HELP_STRING([--enable-gnuefi], [enable optional gnuefi support])) AS_IF([test "x$enable_gnuefi" != "xno"], [ AC_CHECK_HEADERS(efi/${EFI_ARCH}/efibind.h, [AC_DEFINE(HAVE_GNUEFI, 1, [Define if gnuefi is available]) @@ -1256,14 +1257,14 @@ AS_IF([test "x$enable_gnuefi" != "xno"], [ EFI_LIB_DIR="$efiroot" AC_ARG_WITH(efi-libdir, - AS_HELP_STRING([--with-efi-libdir=PATH], [Path to EFI lib directory]), + AS_HELP_STRING([--with-efi-libdir=PATH], [path to EFI lib directory]), [EFI_LIB_DIR="$withval"], [EFI_LIB_DIR="$efiroot"] ) AC_SUBST([EFI_LIB_DIR]) have_efi_lds=no AC_ARG_WITH(efi-ldsdir, - AS_HELP_STRING([--with-efi-ldsdir=PATH], [Path to EFI lds directory]), + AS_HELP_STRING([--with-efi-ldsdir=PATH], [path to EFI lds directory]), [EFI_LDS_DIR="$withval" && AS_IF([test -f "${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds"], [have_efi_lds=yes])], [AS_FOR([DIR], [EFI_LDS_DIR], ["${EFI_LIB_DIR}/gnuefi" "${EFI_LIB_DIR}"], @@ -1276,7 +1277,7 @@ AS_IF([test "x$enable_gnuefi" != "xno"], [ [have_gnuefi=no])]) AC_ARG_WITH(efi-includedir, - AS_HELP_STRING([--with-efi-includedir=PATH], [Path to EFI include directory]), + AS_HELP_STRING([--with-efi-includedir=PATH], [path to EFI include directory]), [EFI_INC_DIR="$withval"], [EFI_INC_DIR="/usr/include"] ) AC_SUBST([EFI_INC_DIR]) @@ -1285,7 +1286,7 @@ AM_CONDITIONAL(HAVE_GNUEFI, [test "x$have_gnuefi" = xyes]) # ------------------------------------------------------------------------------ have_tpm=no -AC_ARG_ENABLE([tpm], AS_HELP_STRING([--enable-tpm],[Enable optional TPM support]), +AC_ARG_ENABLE([tpm], AS_HELP_STRING([--enable-tpm], [enable optional TPM support]), [case "${enableval}" in yes) have_tpm=yes ;; no) have_tpm=no ;; @@ -1308,13 +1309,13 @@ AC_DEFINE_UNQUOTED(SD_TPM_PCR, [$SD_TPM_PCR], [TPM PCR register number to use]) # ------------------------------------------------------------------------------ AC_ARG_WITH(rc-local-script-path-start, AS_HELP_STRING([--with-rc-local-script-path-start=PATH], - [Path to /etc/rc.local]), + [path to /etc/rc.local]), [RC_LOCAL_SCRIPT_PATH_START="$withval"], [RC_LOCAL_SCRIPT_PATH_START="/etc/rc.local"]) AC_ARG_WITH(rc-local-script-path-stop, AS_HELP_STRING([--with-rc-local-script-path-stop=PATH], - [Path to /usr/sbin/halt.local]), + [path to /usr/sbin/halt.local]), [RC_LOCAL_SCRIPT_PATH_STOP="$withval"], [RC_LOCAL_SCRIPT_PATH_STOP="/usr/sbin/halt.local"]) @@ -1327,13 +1328,13 @@ AC_SUBST(RC_LOCAL_SCRIPT_PATH_STOP) # ------------------------------------------------------------------------------ AC_ARG_WITH(kbd-loadkeys, AS_HELP_STRING([--with-kbd-loadkeys=PATH], - [Path to loadkeys]), + [path to loadkeys]), [KBD_LOADKEYS="$withval"], [KBD_LOADKEYS="/usr/bin/loadkeys"]) AC_ARG_WITH(kbd-setfont, AS_HELP_STRING([--with-kbd-setfont=PATH], - [Path to setfont]), + [path to setfont]), [KBD_SETFONT="$withval"], [KBD_SETFONT="/usr/bin/setfont"]) @@ -1345,7 +1346,7 @@ AC_SUBST(KBD_SETFONT) AC_ARG_WITH(telinit, AS_HELP_STRING([--with-telinit=PATH], - [Path to telinit]), + [path to telinit]), [TELINIT="$withval"], [TELINIT="/lib/sysvinit/telinit"]) @@ -1375,7 +1376,7 @@ fi AM_CONDITIONAL(HAVE_MYHOSTNAME, [test "$have_myhostname" = "yes"]) # ------------------------------------------------------------------------------ -AC_ARG_ENABLE(hwdb, [AC_HELP_STRING([--disable-hwdb], [disable hardware database support])], +AC_ARG_ENABLE(hwdb, [AS_HELP_STRING([--disable-hwdb], [disable hardware database support])], enable_hwdb=$enableval, enable_hwdb=yes) AM_CONDITIONAL(ENABLE_HWDB, [test x$enable_hwdb = xyes]) @@ -1392,13 +1393,13 @@ AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"]) # ------------------------------------------------------------------------------ AC_ARG_ENABLE(hibernate, - [AC_HELP_STRING([--disable-hibernate], [disable hibernation support])], + [AS_HELP_STRING([--disable-hibernate], [disable hibernation support])], enable_hibernate=$enableval, enable_hibernate=yes) AM_CONDITIONAL(ENABLE_HIBERNATE, [test x$enable_hibernate = xyes]) # ------------------------------------------------------------------------------ AC_ARG_ENABLE(ldconfig, - [AC_HELP_STRING([--disable-ldconfig], [disable ldconfig])], + [AS_HELP_STRING([--disable-ldconfig], [disable ldconfig])], enable_ldconfig=$enableval, enable_ldconfig=yes) AM_CONDITIONAL(ENABLE_LDCONFIG, [test x$enable_ldconfig = xyes]) @@ -1409,13 +1410,13 @@ SYSTEM_SYSVRCND_PATH=/etc/rc.d AC_ARG_WITH([sysvinit-path], [AS_HELP_STRING([--with-sysvinit-path=PATH], - [Specify the path to where the SysV init scripts are located])], + [specify the path to where the SysV init scripts are located])], [SYSTEM_SYSVINIT_PATH="$withval"], []) AC_ARG_WITH([sysvrcnd-path], [AS_HELP_STRING([--with-sysvrcnd-path=PATH], - [Specify the path to the base directory for the SysV rcN.d directories])], + [specify the path to the base directory for the SysV rcN.d directories])], [SYSTEM_SYSVRCND_PATH="$withval"], []) @@ -1437,7 +1438,7 @@ AM_CONDITIONAL(HAVE_SYSV_COMPAT, test "$SYSTEM_SYSV_COMPAT" = "yes") AC_ARG_WITH([tty-gid], [AS_HELP_STRING([--with-tty-gid=GID], - [Specify the numeric GID of the 'tty' group])], + [specify the numeric GID of the 'tty' group])], [TTY_GID="$withval"], [TTY_GID="5"]) @@ -1463,7 +1464,7 @@ AC_ARG_WITH([dbussystemservicedir], AX_NORMALIZE_PATH([with_dbussystemservicedir]) AC_ARG_WITH([bashcompletiondir], - AS_HELP_STRING([--with-bashcompletiondir=DIR], [Bash completions directory]), + AS_HELP_STRING([--with-bashcompletiondir=DIR], [bash completions directory]), [], [AS_IF([$($PKG_CONFIG --exists bash-completion)], [ with_bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion) @@ -1474,7 +1475,7 @@ AM_CONDITIONAL(ENABLE_BASH_COMPLETION, [test "$with_bashcompletiondir" != "no"]) AX_NORMALIZE_PATH([with_bashcompletiondir]) AC_ARG_WITH([zshcompletiondir], - AS_HELP_STRING([--with-zshcompletiondir=DIR], [Zsh completions directory]), + AS_HELP_STRING([--with-zshcompletiondir=DIR], [zsh completions directory]), [], [with_zshcompletiondir=${datadir}/zsh/site-functions]) AM_CONDITIONAL(ENABLE_ZSH_COMPLETION, [test "$with_zshcompletiondir" != "no"]) AX_NORMALIZE_PATH([with_zshcompletiondir]) @@ -1492,26 +1493,26 @@ fi AX_NORMALIZE_PATH([with_rootprefix]) AC_ARG_WITH([rootlibdir], - AS_HELP_STRING([--with-rootlibdir=DIR], [Root directory for libraries necessary for boot]), + AS_HELP_STRING([--with-rootlibdir=DIR], [root directory for libraries necessary for boot]), [], [with_rootlibdir=${libdir}]) AX_NORMALIZE_PATH([with_rootlibdir]) AC_ARG_WITH([pamlibdir], - AS_HELP_STRING([--with-pamlibdir=DIR], [Directory for PAM modules]), + AS_HELP_STRING([--with-pamlibdir=DIR], [directory for PAM modules]), [], [with_pamlibdir=${with_rootlibdir}/security]) AX_NORMALIZE_PATH([with_pamlibdir]) AC_ARG_WITH([pamconfdir], - AS_HELP_STRING([--with-pamconfdir=DIR], [Directory for PAM configuration (pass no to disable installing)]), + AS_HELP_STRING([--with-pamconfdir=DIR], [directory for PAM configuration (pass no to disable installing)]), [], [with_pamconfdir=${sysconfdir}/pam.d]) AM_CONDITIONAL(ENABLE_PAM_CONFIG, [test "$with_pamconfdir" != "no"]) AX_NORMALIZE_PATH([with_pamconfdir]) AC_ARG_ENABLE([split-usr], - AS_HELP_STRING([--enable-split-usr], [Assume that /bin, /sbin aren\'t symlinks into /usr]), + AS_HELP_STRING([--enable-split-usr], [assume that /bin, /sbin aren't symlinks into /usr]), [], [AS_IF([test "x${ac_default_prefix}" != "x${with_rootprefix}"], [ enable_split_usr=yes @@ -1538,13 +1539,13 @@ AS_IF([test x"$cross_compiling" = "xyes"], [], [ ]) AC_ARG_ENABLE(tests, - [AC_HELP_STRING([--disable-tests], [disable tests, or enable extra tests with =unsafe])], + [AS_HELP_STRING([--disable-tests], [disable tests, or enable extra tests with =unsafe])], enable_tests=$enableval, enable_tests=yes) AM_CONDITIONAL(ENABLE_TESTS, [test x$enable_tests = xyes -o x$enable_tests = xunsafe]) AM_CONDITIONAL(ENABLE_UNSAFE_TESTS, [test x$enable_tests = xunsafe]) AC_ARG_ENABLE(debug, - [AC_HELP_STRING([--enable-debug@<:@=LIST@:>@], [enable extra debugging (hashmap,mmap-cache)])], + [AS_HELP_STRING([--enable-debug@<:@=LIST@:>@], [enable extra debugging (hashmap,mmap-cache)])], [if test "x$enableval" = "xyes"; then enableval="hashmap,mmap-cache" fi diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 9c87aecd30..a78aa5dfee 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -415,6 +415,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*Pavilion*dv7*Notebook*PC: KEYBOARD_KEY_c6=break KEYBOARD_KEY_94=reserved +# Pavilion x360 13 (Prevents random airplane mode activation) + evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[pP][aA][vV][iI][lL][iI][oO][nN]*13*x360*:pvr* + KEYBOARD_KEY_d7=unknown + # Elitebook evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Compaq*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*EliteBook*:pvr* diff --git a/man/machine-id.xml b/man/machine-id.xml index d318ec54ec..a722649de4 100644 --- a/man/machine-id.xml +++ b/man/machine-id.xml @@ -53,30 +53,31 @@ <refsect1> <title>Description</title> - <para>The <filename>/etc/machine-id</filename> file contains the - unique machine ID of the local system that is set during - installation. The machine ID is a single newline-terminated, - hexadecimal, 32-character, lowercase machine ID string. When - decoded from hexadecimal, this corresponds with a 16-byte/128-bit - string.</para> + <para>The <filename>/etc/machine-id</filename> file contains the unique machine ID of the local + system that is set during installation. The machine ID is a single newline-terminated, + hexadecimal, 32-character, lowercase ID. When decoded from hexadecimal, this corresponds to a + 16-byte/128-bit value.</para> <para>The machine ID is usually generated from a random source during system installation and stays constant for all subsequent boots. Optionally, for stateless systems, it is generated during runtime at early boot if it is found to be empty.</para> - <para>The machine ID does not change based on user configuration - or when hardware is replaced.</para> + <para>The machine ID does not change based on local or network configuration or when hardware is + replaced. Due to this and its greater length, it is a more useful replacement for the + <citerefentry project='man-pages'><refentrytitle>gethostid</refentrytitle><manvolnum>3</manvolnum></citerefentry> + call that POSIX specifies.</para> <para>This machine ID adheres to the same format and logic as the D-Bus machine ID.</para> - <para>Programs may use this ID to identify the host with a - globally unique ID in the network, which does not change even if - the local network configuration changes. Due to this and its - greater length, it is a more useful replacement for the - <citerefentry project='man-pages'><refentrytitle>gethostid</refentrytitle><manvolnum>3</manvolnum></citerefentry> - call that POSIX specifies.</para> + <para>This ID uniquely identifies the host. It should be considered "confidential", and must not + be exposed in untrusted environments, in particular on the network. If a stable unique + identifier that is tied to the machine is needed for some application, the machine ID or any + part of it must not be used directly. Instead the machine ID should be hashed with a + cryptographic, keyed hash function, using a fixed, application-specific key. That way the ID + will be properly unique, and derived in a constant way from the machine ID but there will be no + way to retrieve the original machine ID from the application-specific one.</para> <para>The <citerefentry><refentrytitle>systemd-machine-id-setup</refentrytitle><manvolnum>1</manvolnum></citerefentry> diff --git a/man/systemctl.xml b/man/systemctl.xml index dfa00e0c03..08c3a268bd 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -209,6 +209,10 @@ <varname>RequiresMountsFor=</varname>). Both explicitly and implicitly introduced dependencies are shown with <command>list-dependencies</command>.</para> + + <para>When passed to the <command>list-jobs</command> command, for each printed job show which other jobs are + waiting for it. May be combined with <option>--before</option> to show both the jobs waiting for each job as + well as all jobs each job is waiting for.</para> </listitem> </varlistentry> @@ -220,6 +224,10 @@ units that are ordered after the specified unit. In other words, recursively list units following the <varname>Before=</varname> dependency.</para> + + <para>When passed to the <command>list-jobs</command> command, for each printed job show which other jobs it + is waiting for. May be combined with <option>--after</option> to show both the jobs waiting for each job as + well as all jobs each job is waiting for.</para> </listitem> </varlistentry> @@ -1388,6 +1396,10 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service <para>List jobs that are in progress. If one or more <replaceable>PATTERN</replaceable>s are specified, only jobs for units matching one of them are shown.</para> + + <para>When combined with <option>--after</option> or <option>--before</option> the list is augmented with + information on which other job each job is waiting for, and which other jobs are waiting for it, see + above.</para> </listitem> </varlistentry> <varlistentry> diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index d26206710f..3af423b553 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -123,6 +123,11 @@ <entry>On 64-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory <filename>/</filename>.</entry> </row> <row> + <entry>993d8d3d-f80e-4225-855a-9daf8ed7ea97</entry> + <entry><filename>Root Partition (Itanium/IA-64)</filename></entry> + <entry>On Itanium systems, the first Itanium root partition on the disk the EFI ESP is located on is mounted to the root directory <filename>/</filename>.</entry> + </row> + <row> <entry>933ac7e1-2eb4-4f13-b844-0e14e2aef915</entry> <entry>Home Partition</entry> <entry>The first home partition on the disk the root partition is located on is mounted to <filename>/home</filename>.</entry> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 3b39a9c912..2ea4a53d18 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -952,13 +952,19 @@ 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> + <varname>InaccessiblePaths=</varname> may be prefixed with <literal>-</literal>, in which case they will be + ignored when they do not exist. If prefixed with <literal>+</literal> the paths are taken relative to the root + directory of the unit, as configured with <varname>RootDirectory=</varname>, instead of relative to the root + directory of the host (see above). When combining <literal>-</literal> and <literal>+</literal> on the same + path make sure to specify <literal>-</literal> first, and <literal>+</literal> second.</para> + + <para>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> @@ -999,7 +1005,11 @@ 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> + privileges apply as for <varname>ReadOnlyPaths=</varname> and related calls, see above. + If turned on and if running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> + capability (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> + is implied. + </para></listitem> </varlistentry> <varlistentry> @@ -1090,9 +1100,35 @@ 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. - Note that this option does not prevent kernel tuning through IPC interfaces and external programs. However - <varname>InaccessiblePaths=</varname> can be used to make some IPC file system objects - inaccessible.</para></listitem> + If turned on and if running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> + capability (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> + is implied. Note that this option does not prevent kernel tuning through IPC interfaces + and external programs. However <varname>InaccessiblePaths=</varname> can be used to + make some IPC file system objects inaccessible.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>ProtectKernelModules=</varname></term> + + <listitem><para>Takes a boolean argument. If true, explicit module loading will + be denied. This allows to turn off module load and unload operations on modular + kernels. It is recommended to turn this on for most services that do not need special + file systems or extra kernel modules to work. Default to off. Enabling this option + removes <constant>CAP_SYS_MODULE</constant> from the capability bounding set for + the unit, and installs a system call filter to block module system calls, + also <filename>/usr/lib/modules</filename> is made inaccessible. For this + setting the same restrictions regarding mount propagation and privileges + apply as for <varname>ReadOnlyPaths=</varname> and related calls, see above. + Note that limited automatic module loading due to user configuration or kernel + mapping tables might still happen as side effect of requested user operations, + both privileged and unprivileged. To disable module auto-load feature please see + <citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> + <constant>kernel.modules_disabled</constant> mechanism and + <filename>/proc/sys/kernel/modules_disabled</filename> documentation. + If turned on and if running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> + capability (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> + is implied. + </para></listitem> </varlistentry> <varlistentry> @@ -1237,7 +1273,7 @@ <listitem><para>Takes a boolean argument. If true, ensures that the service process and all its children can never gain new privileges through <function>execve()</function> (e.g. via setuid or setgid bits, or filesystem capabilities). This is the simplest and most effective way to ensure that a process and its children can never - elevate privileges again. Defaults to false, but in the user manager instance certain settings force + elevate privileges again. Defaults to false, but certain settings force <varname>NoNewPrivileges=yes</varname>, ignoring the value of this setting. This is the case when <varname>SystemCallFilter=</varname>, <varname>SystemCallArchitectures=</varname>, <varname>RestrictAddressFamilies=</varname>, <varname>RestrictNamespaces=</varname>, @@ -1482,27 +1518,11 @@ <citerefentry><refentrytitle>setns</refentrytitle><manvolnum>2</manvolnum></citerefentry> system calls, taking the specified flags parameters into account. Note that — if this option is used — in addition to restricting creation and switching of the specified types of namespaces (or all of them, if true) access to the - <function>setns()</function> system call with a zero flags parameter is prohibited.</para></listitem> - </varlistentry> - - <varlistentry> - <term><varname>ProtectKernelModules=</varname></term> - - <listitem><para>Takes a boolean argument. If true, explicit module loading will - be denied. This allows to turn off module load and unload operations on modular - kernels. It is recommended to turn this on for most services that do not need special - file systems or extra kernel modules to work. Default to off. Enabling this option - removes <constant>CAP_SYS_MODULE</constant> from the capability bounding set for - the unit, and installs a system call filter to block module system calls, - also <filename>/usr/lib/modules</filename> is made inaccessible. For this - setting the same restrictions regarding mount propagation and privileges - apply as for <varname>ReadOnlyPaths=</varname> and related calls, see above. - Note that limited automatic module loading due to user configuration or kernel - mapping tables might still happen as side effect of requested user operations, - both privileged and unprivileged. To disable module auto-load feature please see - <citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> - <constant>kernel.modules_disabled</constant> mechanism and - <filename>/proc/sys/kernel/modules_disabled</filename> documentation.</para></listitem> + <function>setns()</function> system call with a zero flags parameter is prohibited. + If running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> + capability (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> + is implied. + </para></listitem> </varlistentry> <varlistentry> @@ -1563,6 +1583,9 @@ that generate program code dynamically at runtime, such as JIT execution engines, or programs compiled making use of the code "trampoline" feature of various C compilers. This option improves service security, as it makes harder for software exploits to change running code dynamically. + If running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> + capability (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> + is implied. </para></listitem> </varlistentry> @@ -1573,7 +1596,10 @@ the unit are refused. This restricts access to realtime task scheduling policies such as <constant>SCHED_FIFO</constant>, <constant>SCHED_RR</constant> or <constant>SCHED_DEADLINE</constant>. See <citerefentry project='man-pages'><refentrytitle>sched</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details about - these scheduling policies. Realtime scheduling policies may be used to monopolize CPU time for longer periods + these scheduling policies. If running in user mode, or in system mode, but + without the <constant>CAP_SYS_ADMIN</constant> capability + (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> + is implied. Realtime scheduling policies may be used to monopolize CPU time for longer periods of time, and may hence be used to lock up or otherwise trigger Denial-of-Service situations on the system. It is hence recommended to restrict access to realtime scheduling to the few programs that actually require them. Defaults to off.</para></listitem> diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 8edbe758d9..023e24eeb3 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -359,6 +359,20 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>AutoNegotiation=</varname></term> + <listitem> + <para>Enables or disables automatic negotiation of transmission parameters. + Autonegotiation is a procedure by which two connected ethernet devices choose + common transmission parameters, such as speed, duplex mode, and flow control. + Takes a boolean value. Unset by default, which means that the kernel default + will be used.</para> + + <para>Note that if autonegotiation is enabled, speed and duplex settings are + read-only. If autonegotation is disabled, speed and duplex settings are writable + if the driver supports multiple link modes.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>WakeOnLan=</varname></term> <listitem> <para>The Wake-on-LAN policy to set for the device. The diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index b0f156f6df..68ff6f8f1c 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -241,6 +241,25 @@ </varlistentry> <varlistentry> + <term><option>x-systemd.mount-timeout=</option></term> + + <listitem><para>Configure how long systemd should wait for the + mount command to finish before giving up on an entry from + <filename>/etc/fstab</filename>. Specify a time in seconds or + explicitly append a unit such as <literal>s</literal>, + <literal>min</literal>, <literal>h</literal>, + <literal>ms</literal>.</para> + + <para>Note that this option can only be used in + <filename>/etc/fstab</filename>, and will be + ignored when part of the <varname>Options=</varname> + setting in a unit file.</para> + + <para>See <varname>TimeoutSec=</varname> below for + details.</para> + </listitem> + </varlistentry> + <varlistentry> <term><option>noauto</option></term> <term><option>auto</option></term> diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 2fb4907634..99283813fd 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -984,6 +984,13 @@ </para> </listitem> </varlistentry> + + <varlistentry> + <term><varname>ListenPort=</varname></term> + <listitem> + <para>Allow setting custom port for the DHCP client to listen on.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 5c65957bda..3ba6ab34db 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -663,6 +663,12 @@ or signal is specified in <varname>RestartForceExitStatus=</varname> (see below).</para> + <para>Note that service restart is subject to unit start rate + limiting configured with <varname>StartLimitIntervalSec=</varname> + and <varname>StartLimitBurst=</varname>, see + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for details.</para> + <para>Setting this to <option>on-failure</option> is the recommended choice for long-running services, in order to increase reliability by attempting automatic recovery from diff --git a/man/systemd.xml b/man/systemd.xml index 7f24a874ed..fb67ba7cb3 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -940,10 +940,14 @@ <varlistentry> <term><varname>systemd.confirm_spawn=</varname></term> - <listitem><para>Takes a boolean argument. If - <option>yes</option>, the system manager (PID 1) asks for - confirmation when spawning processes. Defaults to - <option>no</option>.</para></listitem> + <listitem><para>Takes a boolean argument or a path to the + virtual console where the confirmation messages should be + emitted. If <option>yes</option>, the system manager (PID 1) + asks for confirmation when spawning processes using + <option>/dev/console</option>. If a path or a console name + (such as <literal>ttyS0</literal>) is provided, the virtual + console pointed to by this path or described by the give name + will be used instead. Defaults to <option>no</option>.</para></listitem> </varlistentry> <varlistentry> diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index f744a84501..51d881c5fb 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -36,7 +36,9 @@ #include "log.h" #include "pager.h" #include "parse-util.h" +#ifdef HAVE_SECCOMP #include "seccomp-util.h" +#endif #include "special.h" #include "strv.h" #include "strxcpyx.h" diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 7c69ccdaf9..96da38d45e 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -395,7 +395,8 @@ int strv_env_replace(char ***l, char *p) { for (f = *l; f && *f; f++) if (env_match(*f, p)) { - free_and_replace(*f, p); + free(*f); + *f = p; strv_env_unset(f + 1, p); return 0; } diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index d6c1228463..dbe64a9a58 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -48,7 +48,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra /* Bail early if called after last value or with no input */ if (!*p) - goto finish_force_terminate; + goto finish; c = **p; if (!separators) diff --git a/src/basic/missing.h b/src/basic/missing.h index 4c013be608..a5ae5d9e79 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -1076,6 +1076,33 @@ typedef int32_t key_serial_t; #define IFA_F_MCAUTOJOIN 0x400 #endif +#ifndef HAVE_STRUCT_ETHTOOL_LINK_SETTINGS + +#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */ +#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ + +struct ethtool_link_settings { + __u32 cmd; + __u32 speed; + __u8 duplex; + __u8 port; + __u8 phy_address; + __u8 autoneg; + __u8 mdio_support; + __u8 eth_tp_mdix; + __u8 eth_tp_mdix_ctrl; + __s8 link_mode_masks_nwords; + __u32 reserved[8]; + __u32 link_mode_masks[0]; + /* layout of link_mode_masks fields: + * __u32 map_supported[link_mode_masks_nwords]; + * __u32 map_advertising[link_mode_masks_nwords]; + * __u32 map_lp_advertising[link_mode_masks_nwords]; + */ +}; + +#endif + #endif #include "missing_syscall.h" diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index eafdea9eb3..9a8ef825c5 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -144,12 +144,14 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { return 0; } -int ask_char(char *ret, const char *replies, const char *text, ...) { +#define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC) + +int ask_char(char *ret, const char *replies, const char *fmt, ...) { int r; assert(ret); assert(replies); - assert(text); + assert(fmt); for (;;) { va_list ap; @@ -159,8 +161,10 @@ int ask_char(char *ret, const char *replies, const char *text, ...) { if (colors_enabled()) fputs(ANSI_HIGHLIGHT, stdout); - va_start(ap, text); - vprintf(text, ap); + putchar('\r'); + + va_start(ap, fmt); + vprintf(fmt, ap); va_end(ap); if (colors_enabled()) @@ -168,9 +172,12 @@ int ask_char(char *ret, const char *replies, const char *text, ...) { fflush(stdout); - r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl); + r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, &need_nl); if (r < 0) { + if (r == -ETIMEDOUT) + continue; + if (r == -EBADMSG) { puts("Bad input, please try again."); continue; @@ -455,7 +462,7 @@ int acquire_terminal( goto fail; } - r = fd_wait_for_event(fd, POLLIN, ts + timeout - n); + r = fd_wait_for_event(notify, POLLIN, ts + timeout - n); if (r < 0) goto fail; diff --git a/src/basic/virt.c b/src/basic/virt.c index 69b0f96183..d8d57381ad 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -496,7 +496,7 @@ static int userns_has_mapping(const char *name) { f = fopen(name, "re"); if (!f) { log_debug_errno(errno, "Failed to open %s: %m", name); - return errno == -ENOENT ? false : -errno; + return errno == ENOENT ? false : -errno; } n = getline(&buf, &n_allocated, f); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index d7bb0496a0..23c1b44573 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -781,7 +781,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RestrictNamespace", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index ccf7453d47..effc45db45 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -65,7 +65,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error return r; /* Access is granted to the job owner */ - if (!sd_bus_track_contains(j->clients, sd_bus_message_get_sender(message))) { + if (!sd_bus_track_contains(j->bus_track, sd_bus_message_get_sender(message))) { /* And for everybody else consult PolicyKit */ r = bus_verify_manage_units_async(j->unit->manager, message, error); @@ -80,9 +80,61 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error return sd_bus_reply_method_return(message, NULL); } +int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ Job **list = NULL; + Job *j = userdata; + int r, i, n; + + if (strstr(sd_bus_message_get_member(message), "After")) + n = job_get_after(j, &list); + else + n = job_get_before(j, &list); + if (n < 0) + return n; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(usssoo)"); + if (r < 0) + return r; + + for (i = 0; i < n; i ++) { + _cleanup_free_ char *unit_path = NULL, *job_path = NULL; + + job_path = job_dbus_path(list[i]); + if (!job_path) + return -ENOMEM; + + unit_path = unit_dbus_path(list[i]->unit); + if (!unit_path) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(usssoo)", + list[i]->id, + list[i]->unit->id, + job_type_to_string(list[i]->type), + job_state_to_string(list[i]->state), + job_path, + unit_path); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + const sd_bus_vtable bus_job_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetAfter", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetBefore", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST), @@ -143,7 +195,7 @@ void bus_job_send_change_signal(Job *j) { j->in_dbus_queue = false; } - r = bus_foreach_bus(j->manager, j->clients, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); + r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); if (r < 0) log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id); @@ -187,7 +239,70 @@ void bus_job_send_removed_signal(Job *j) { if (!j->sent_dbus_new_signal) bus_job_send_change_signal(j); - r = bus_foreach_bus(j->manager, j->clients, send_removed_signal, j); + r = bus_foreach_bus(j->manager, j->bus_track, send_removed_signal, j); if (r < 0) log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id); } + +static int bus_job_track_handler(sd_bus_track *t, void *userdata) { + Job *j = userdata; + + assert(t); + assert(j); + + j->bus_track = sd_bus_track_unref(j->bus_track); /* make sure we aren't called again */ + + /* Last client dropped off the bus, maybe we should GC this now? */ + job_add_to_gc_queue(j); + return 0; +} + +static int bus_job_allocate_bus_track(Job *j) { + + assert(j); + + if (j->bus_track) + return 0; + + return sd_bus_track_new(j->unit->manager->api_bus, &j->bus_track, bus_job_track_handler, j); +} + +int bus_job_coldplug_bus_track(Job *j) { + int r = 0; + _cleanup_strv_free_ char **deserialized_clients = NULL; + + assert(j); + + deserialized_clients = j->deserialized_clients; + j->deserialized_clients = NULL; + + if (strv_isempty(deserialized_clients)) + return 0; + + if (!j->manager->api_bus) + return 0; + + r = bus_job_allocate_bus_track(j); + if (r < 0) + return r; + + return bus_track_add_name_many(j->bus_track, deserialized_clients); +} + +int bus_job_track_sender(Job *j, sd_bus_message *m) { + int r; + + assert(j); + assert(m); + + if (sd_bus_message_get_bus(m) != j->unit->manager->api_bus) { + j->ref_by_private_bus = true; + return 0; + } + + r = bus_job_allocate_bus_track(j); + if (r < 0) + return r; + + return sd_bus_track_add_sender(j->bus_track, m); +} diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h index 024d06719e..a4366a0720 100644 --- a/src/core/dbus-job.h +++ b/src/core/dbus-job.h @@ -26,6 +26,10 @@ extern const sd_bus_vtable bus_job_vtable[]; int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error); +int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error); void bus_job_send_change_signal(Job *j); void bus_job_send_removed_signal(Job *j); + +int bus_job_coldplug_bus_track(Job *j); +int bus_job_track_sender(Job *j, sd_bus_message *m); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 5a7922a249..9af49dd1bc 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2285,6 +2285,26 @@ static int method_get_unit_file_links(sd_bus_message *message, void *userdata, s return sd_bus_send(NULL, reply, NULL); } +static int method_get_job_waiting(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint32_t id; + Job *j; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &id); + if (r < 0) + return r; + + j = manager_get_job(m, id); + if (!j) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); + + return bus_job_method_get_waiting_jobs(message, j, error); +} + const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_VTABLE_START(0), @@ -2390,6 +2410,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetJobAfter", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetJobBefore", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index b6cb6e1350..2adc1d9288 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "cgroup-util.h" +#include "dbus-job.h" #include "dbus-unit.h" #include "dbus.h" #include "fd-util.h" @@ -1223,17 +1224,9 @@ int bus_unit_queue_job( if (r < 0) return r; - if (sd_bus_message_get_bus(message) == u->manager->api_bus) { - if (!j->clients) { - r = sd_bus_track_new(sd_bus_message_get_bus(message), &j->clients, NULL, NULL); - if (r < 0) - return r; - } - - r = sd_bus_track_add_sender(j->clients, message); - if (r < 0) - return r; - } + r = bus_job_track_sender(j, message); + if (r < 0) + return r; path = job_dbus_path(j); if (!path) @@ -1507,7 +1500,7 @@ int bus_unit_check_load_state(Unit *u, sd_bus_error *error) { return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id); } -static int bus_track_handler(sd_bus_track *t, void *userdata) { +static int bus_unit_track_handler(sd_bus_track *t, void *userdata) { Unit *u = userdata; assert(t); @@ -1519,7 +1512,7 @@ static int bus_track_handler(sd_bus_track *t, void *userdata) { return 0; } -static int allocate_bus_track(Unit *u) { +static int bus_unit_allocate_bus_track(Unit *u) { int r; assert(u); @@ -1527,7 +1520,7 @@ static int allocate_bus_track(Unit *u) { if (u->bus_track) return 0; - r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u); + r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_unit_track_handler, u); if (r < 0) return r; @@ -1545,7 +1538,7 @@ int bus_unit_track_add_name(Unit *u, const char *name) { assert(u); - r = allocate_bus_track(u); + r = bus_unit_allocate_bus_track(u); if (r < 0) return r; @@ -1557,7 +1550,7 @@ int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) { assert(u); - r = allocate_bus_track(u); + r = bus_unit_allocate_bus_track(u); if (r < 0) return r; diff --git a/src/core/dbus.c b/src/core/dbus.c index 070974fe66..07ab21f199 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -1054,8 +1054,8 @@ static void destroy_bus(Manager *m, sd_bus **bus) { m->subscribed = sd_bus_track_unref(m->subscribed); HASHMAP_FOREACH(j, m->jobs, i) - if (j->clients && sd_bus_track_get_bus(j->clients) == *bus) - j->clients = sd_bus_track_unref(j->clients); + if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus) + j->bus_track = sd_bus_track_unref(j->bus_track); /* Get rid of queued message on this bus */ if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus) @@ -1185,7 +1185,6 @@ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) { } int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) { - char **i; int r = 0; assert(m); @@ -1207,16 +1206,7 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) { if (r < 0) return r; - r = 0; - STRV_FOREACH(i, l) { - int k; - - k = sd_bus_track_add_name(*t, *i); - if (k < 0) - r = k; - } - - return r; + return bus_track_add_name_many(*t, l); } int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { diff --git a/src/core/device.c b/src/core/device.c index c572a6737c..074e93ffe2 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -831,6 +831,8 @@ const UnitVTable device_vtable = { "Device\0" "Install\0", + .gc_jobs = true, + .init = device_init, .done = device_done, .load = unit_load_fragment_and_dropin_optional, diff --git a/src/core/execute.c b/src/core/execute.c index f666f7c6ce..07ab067c05 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -624,7 +624,7 @@ static int chown_terminal(int fd, uid_t uid) { return 0; } -static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { +static int setup_confirm_stdio(const char *vc, int *_saved_stdin, int *_saved_stdout) { _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1; int r; @@ -639,12 +639,7 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { if (saved_stdout < 0) return -errno; - fd = acquire_terminal( - "/dev/console", - false, - false, - false, - DEFAULT_CONFIRM_USEC); + fd = acquire_terminal(vc, false, false, false, DEFAULT_CONFIRM_USEC); if (fd < 0) return fd; @@ -674,21 +669,27 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { return 0; } -_printf_(1, 2) static int write_confirm_message(const char *format, ...) { +static void write_confirm_error_fd(int err, int fd, const Unit *u) { + assert(err < 0); + + if (err == -ETIMEDOUT) + dprintf(fd, "Confirmation question timed out for %s, assuming positive response.\n", u->id); + else { + errno = -err; + dprintf(fd, "Couldn't ask confirmation for %s: %m, assuming positive response.\n", u->id); + } +} + +static void write_confirm_error(int err, const char *vc, const Unit *u) { _cleanup_close_ int fd = -1; - va_list ap; - assert(format); + assert(vc); - fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + fd = open_terminal(vc, O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) - return fd; - - va_start(ap, format); - vdprintf(fd, format, ap); - va_end(ap); + return; - return 0; + write_confirm_error_fd(err, fd, u); } static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { @@ -713,22 +714,96 @@ static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { return r; } -static int ask_for_confirmation(char *response, char **argv) { +enum { + CONFIRM_PRETEND_FAILURE = -1, + CONFIRM_PRETEND_SUCCESS = 0, + CONFIRM_EXECUTE = 1, +}; + +static int ask_for_confirmation(const char *vc, Unit *u, const char *cmdline) { int saved_stdout = -1, saved_stdin = -1, r; - _cleanup_free_ char *line = NULL; + _cleanup_free_ char *e = NULL; + char c; - r = setup_confirm_stdio(&saved_stdin, &saved_stdout); - if (r < 0) - return r; + /* For any internal errors, assume a positive response. */ + r = setup_confirm_stdio(vc, &saved_stdin, &saved_stdout); + if (r < 0) { + write_confirm_error(r, vc, u); + return CONFIRM_EXECUTE; + } - line = exec_command_line(argv); - if (!line) - return -ENOMEM; + /* confirm_spawn might have been disabled while we were sleeping. */ + if (manager_is_confirm_spawn_disabled(u->manager)) { + r = 1; + goto restore_stdio; + } - r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line); + e = ellipsize(cmdline, 60, 100); + if (!e) { + log_oom(); + r = CONFIRM_EXECUTE; + goto restore_stdio; + } - restore_confirm_stdio(&saved_stdin, &saved_stdout); + for (;;) { + r = ask_char(&c, "yfshiDjcn", "Execute %s? [y, f, s – h for help] ", e); + if (r < 0) { + write_confirm_error_fd(r, STDOUT_FILENO, u); + r = CONFIRM_EXECUTE; + goto restore_stdio; + } + + switch (c) { + case 'c': + printf("Resuming normal execution.\n"); + manager_disable_confirm_spawn(); + r = 1; + break; + case 'D': + unit_dump(u, stdout, " "); + continue; /* ask again */ + case 'f': + printf("Failing execution.\n"); + r = CONFIRM_PRETEND_FAILURE; + break; + case 'h': + printf(" c - continue, proceed without asking anymore\n" + " D - dump, show the state of the unit\n" + " f - fail, don't execute the command and pretend it failed\n" + " h - help\n" + " i - info, show a short summary of the unit\n" + " j - jobs, show jobs that are in progress\n" + " s - skip, don't execute the command and pretend it succeeded\n" + " y - yes, execute the command\n"); + continue; /* ask again */ + case 'i': + printf(" Description: %s\n" + " Unit: %s\n" + " Command: %s\n", + u->id, u->description, cmdline); + continue; /* ask again */ + case 'j': + manager_dump_jobs(u->manager, stdout, " "); + continue; /* ask again */ + case 'n': + /* 'n' was removed in favor of 'f'. */ + printf("Didn't understand 'n', did you mean 'f'?\n"); + continue; /* ask again */ + case 's': + printf("Skipping execution.\n"); + r = CONFIRM_PRETEND_SUCCESS; + break; + case 'y': + r = CONFIRM_EXECUTE; + break; + default: + assert_not_reached("Unhandled choice"); + } + break; + } +restore_stdio: + restore_confirm_stdio(&saved_stdin, &saved_stdout); return r; } @@ -2201,7 +2276,8 @@ static bool context_has_no_new_privileges(const ExecContext *c) { 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 */ + /* We need NNP if we have any form of seccomp and are unprivileged */ + return context_has_address_families(c) || c->memory_deny_write_execute || c->restrict_realtime || exec_context_restrict_namespaces_set(c) || @@ -2314,22 +2390,24 @@ static int exec_child( exec_context_tty_reset(context, params); - if (params->flags & EXEC_CONFIRM_SPAWN) { - char response; + if (unit_shall_confirm_spawn(unit)) { + const char *vc = params->confirm_spawn; + _cleanup_free_ char *cmdline = NULL; - r = ask_for_confirmation(&response, argv); - if (r == -ETIMEDOUT) - write_confirm_message("Confirmation question timed out, assuming positive response.\n"); - else if (r < 0) - write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r)); - else if (response == 's') { - write_confirm_message("Skipping execution.\n"); + cmdline = exec_command_line(argv); + if (!cmdline) { + *exit_status = EXIT_CONFIRM; + return -ENOMEM; + } + + r = ask_for_confirmation(vc, unit, cmdline); + if (r != CONFIRM_EXECUTE) { + if (r == CONFIRM_PRETEND_SUCCESS) { + *exit_status = EXIT_SUCCESS; + return 0; + } *exit_status = EXIT_CONFIRM; return -ECANCELED; - } else if (response == 'n') { - write_confirm_message("Failing execution.\n"); - *exit_status = 0; - return 0; } } diff --git a/src/core/execute.h b/src/core/execute.h index 56f880cffe..951c8f4da3 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -216,7 +216,6 @@ struct ExecContext { bool nice_set:1; bool ioprio_set:1; bool cpu_sched_set:1; - bool no_new_privileges_set:1; }; static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { @@ -226,16 +225,15 @@ static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { } typedef enum ExecFlags { - EXEC_CONFIRM_SPAWN = 1U << 0, - EXEC_APPLY_PERMISSIONS = 1U << 1, - EXEC_APPLY_CHROOT = 1U << 2, - EXEC_APPLY_TTY_STDIN = 1U << 3, + EXEC_APPLY_PERMISSIONS = 1U << 0, + EXEC_APPLY_CHROOT = 1U << 1, + EXEC_APPLY_TTY_STDIN = 1U << 2, /* The following are not used by execute.c, but by consumers internally */ - EXEC_PASS_FDS = 1U << 4, - EXEC_IS_CONTROL = 1U << 5, - EXEC_SETENV_RESULT = 1U << 6, - EXEC_SET_WATCHDOG = 1U << 7, + EXEC_PASS_FDS = 1U << 3, + EXEC_IS_CONTROL = 1U << 4, + EXEC_SETENV_RESULT = 1U << 5, + EXEC_SET_WATCHDOG = 1U << 6, } ExecFlags; struct ExecParameters { @@ -255,6 +253,8 @@ struct ExecParameters { const char *runtime_prefix; + const char *confirm_spawn; + usec_t watchdog_usec; int *idle_pipe; diff --git a/src/core/job.c b/src/core/job.c index ac6910a906..2ba4c78096 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -90,9 +90,12 @@ void job_free(Job *j) { if (j->in_dbus_queue) LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); + if (j->in_gc_queue) + LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j); + sd_event_source_unref(j->timer_event_source); - sd_bus_track_unref(j->clients); + sd_bus_track_unref(j->bus_track); strv_free(j->deserialized_clients); free(j); @@ -226,6 +229,9 @@ Job* job_install(Job *j) { log_unit_debug(j->unit, "Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); + + job_add_to_gc_queue(j); + return j; } @@ -267,7 +273,8 @@ JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool * this means the 'anchor' job (i.e. the one the user * explicitly asked for) is the requester. */ - if (!(l = new0(JobDependency, 1))) + l = new0(JobDependency, 1); + if (!l) return NULL; l->subject = subject; @@ -457,9 +464,7 @@ static bool job_is_runnable(Job *j) { if (j->type == JOB_NOP) return true; - if (j->type == JOB_START || - j->type == JOB_VERIFY_ACTIVE || - j->type == JOB_RELOAD) { + if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) { /* Immediate result is that the job is or might be * started. In this case let's wait for the @@ -476,8 +481,7 @@ static bool job_is_runnable(Job *j) { SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) if (other->job && - (other->job->type == JOB_STOP || - other->job->type == JOB_RESTART)) + IN_SET(other->job->type, JOB_STOP, JOB_RESTART)) return false; /* This means that for a service a and a service b where b @@ -641,6 +645,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR [JOB_DEPENDENCY] = "Dependency failed for %s.", [JOB_ASSERT] = "Assertion failed for %s.", [JOB_UNSUPPORTED] = "Starting of %s not supported.", + [JOB_COLLECTED] = "Unecessary job for %s was removed.", }; static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Stopped %s.", @@ -700,6 +705,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { [JOB_SKIPPED] = { ANSI_HIGHLIGHT, " INFO " }, [JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" }, [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" }, + [JOB_COLLECTED] = { ANSI_HIGHLIGHT, " INFO " }, }; const char *format; @@ -751,6 +757,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { [JOB_INVALID] = LOG_INFO, [JOB_ASSERT] = LOG_WARNING, [JOB_UNSUPPORTED] = LOG_WARNING, + [JOB_COLLECTED] = LOG_INFO, }; assert(u); @@ -862,6 +869,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr job_set_state(j, JOB_WAITING); job_add_to_run_queue(j); + job_add_to_gc_queue(j); goto finish; } @@ -905,11 +913,15 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr finish: /* Try to start the next jobs that can be started */ SET_FOREACH(other, u->dependencies[UNIT_AFTER], i) - if (other->job) + if (other->job) { job_add_to_run_queue(other->job); + job_add_to_gc_queue(other->job); + } SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i) - if (other->job) + if (other->job) { job_add_to_run_queue(other->job); + job_add_to_gc_queue(other->job); + } manager_check_finished(u->manager); @@ -1012,7 +1024,7 @@ int job_serialize(Job *j, FILE *f) { if (j->begin_usec > 0) fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec); - bus_track_serialize(j->clients, f, "subscribed"); + bus_track_serialize(j->bus_track, f, "subscribed"); /* End marker */ fputc('\n', f); @@ -1123,12 +1135,14 @@ int job_coldplug(Job *j) { /* After deserialization is complete and the bus connection * set up again, let's start watching our subscribers again */ - (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients); - j->deserialized_clients = strv_free(j->deserialized_clients); + (void) bus_job_coldplug_bus_track(j); if (j->state == JOB_WAITING) job_add_to_run_queue(j); + /* Maybe due to new dependencies we don't actually need this job anymore? */ + job_add_to_gc_queue(j); + if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY) return 0; @@ -1203,9 +1217,225 @@ int job_get_timeout(Job *j, usec_t *timeout) { return 1; } +bool job_check_gc(Job *j) { + Unit *other; + Iterator i; + + assert(j); + + /* Checks whether this job should be GC'ed away. We only do this for jobs of units that have no effect on their + * own and just track external state. For now the only unit type that qualifies for this are .device units. */ + + if (!UNIT_VTABLE(j->unit)->gc_jobs) + return true; + + if (sd_bus_track_count(j->bus_track) > 0) + return true; + + /* FIXME: So this is a bit ugly: for now we don't properly track references made via private bus connections + * (because it's nasty, as sd_bus_track doesn't apply to it). We simply remember that the job was once + * referenced by one, and reset this whenever we notice that no private bus connections are around. This means + * the GC is a bit too conservative when it comes to jobs created by private bus connections. */ + if (j->ref_by_private_bus) { + if (set_isempty(j->unit->manager->private_buses)) + j->ref_by_private_bus = false; + else + return true; + } + + if (j->type == JOB_NOP) + return true; + + /* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or + * start, hence let's not GC in that case. */ + SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) { + if (!other->job) + continue; + + if (other->job->ignore_order) + continue; + + if (IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) + return true; + } + + /* If we are going down, but something else is orederd After= us, then it needs to wait for us */ + if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) { + + SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) { + if (!other->job) + continue; + + if (other->job->ignore_order) + continue; + + return true; + } + } + + /* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is + * ordered before the job "other": + * + * we start + other start → stay + * we start + other stop → gc + * we stop + other start → stay + * we stop + other stop → gc + * + * "we" are ordered after "other": + * + * we start + other start → gc + * we start + other stop → gc + * we stop + other start → stay + * we stop + other stop → stay + * + */ + + return false; +} + +void job_add_to_gc_queue(Job *j) { + assert(j); + + if (j->in_gc_queue) + return; + + if (job_check_gc(j)) + return; + + LIST_PREPEND(gc_queue, j->unit->manager->gc_job_queue, j); + j->in_gc_queue = true; +} + +static int job_compare(const void *a, const void *b) { + Job *x = *(Job**) a, *y = *(Job**) b; + + if (x->id < y->id) + return -1; + if (x->id > y->id) + return 1; + + return 0; +} + +static size_t sort_job_list(Job **list, size_t n) { + Job *previous = NULL; + size_t a, b; + + /* Order by numeric IDs */ + qsort_safe(list, n, sizeof(Job*), job_compare); + + /* Filter out duplicates */ + for (a = 0, b = 0; a < n; a++) { + + if (previous == list[a]) + continue; + + previous = list[b++] = list[a]; + } + + return b; +} + +int job_get_before(Job *j, Job*** ret) { + _cleanup_free_ Job** list = NULL; + size_t n = 0, n_allocated = 0; + Unit *other = NULL; + Iterator i; + + /* Returns a list of all pending jobs that need to finish before this job may be started. */ + + assert(j); + assert(ret); + + if (j->ignore_order) { + *ret = NULL; + return 0; + } + + if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) { + + SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) { + if (!other->job) + continue; + + if (!GREEDY_REALLOC(list, n_allocated, n+1)) + return -ENOMEM; + list[n++] = other->job; + } + } + + SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) { + if (!other->job) + continue; + + if (!IN_SET(other->job->type, JOB_STOP, JOB_RESTART)) + continue; + + if (!GREEDY_REALLOC(list, n_allocated, n+1)) + return -ENOMEM; + list[n++] = other->job; + } + + n = sort_job_list(list, n); + + *ret = list; + list = NULL; + + return (int) n; +} + +int job_get_after(Job *j, Job*** ret) { + _cleanup_free_ Job** list = NULL; + size_t n = 0, n_allocated = 0; + Unit *other = NULL; + Iterator i; + + assert(j); + assert(ret); + + /* Returns a list of all pending jobs that are waiting for this job to finish. */ + + SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) { + if (!other->job) + continue; + + if (other->job->ignore_order) + continue; + + if (!IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) + continue; + + if (!GREEDY_REALLOC(list, n_allocated, n+1)) + return -ENOMEM; + list[n++] = other->job; + } + + if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) { + + SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) { + if (!other->job) + continue; + + if (other->job->ignore_order) + continue; + + if (!GREEDY_REALLOC(list, n_allocated, n+1)) + return -ENOMEM; + list[n++] = other->job; + } + } + + n = sort_job_list(list, n); + + *ret = list; + list = NULL; + + return (int) n; +} + static const char* const job_state_table[_JOB_STATE_MAX] = { [JOB_WAITING] = "waiting", - [JOB_RUNNING] = "running" + [JOB_RUNNING] = "running", }; DEFINE_STRING_TABLE_LOOKUP(job_state, JobState); @@ -1246,6 +1476,7 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = { [JOB_INVALID] = "invalid", [JOB_ASSERT] = "assert", [JOB_UNSUPPORTED] = "unsupported", + [JOB_COLLECTED] = "collected", }; DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); diff --git a/src/core/job.h b/src/core/job.h index 85368f0d30..bea743f462 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -107,6 +107,7 @@ enum JobResult { JOB_INVALID, /* JOB_RELOAD of inactive unit */ JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */ JOB_UNSUPPORTED, /* Couldn't start a unit, because the unit type is not supported on the system */ + JOB_COLLECTED, /* Job was garbage collected, since nothing needed it anymore */ _JOB_RESULT_MAX, _JOB_RESULT_INVALID = -1 }; @@ -122,8 +123,8 @@ struct JobDependency { LIST_FIELDS(JobDependency, subject); LIST_FIELDS(JobDependency, object); - bool matters; - bool conflicts; + bool matters:1; + bool conflicts:1; }; struct Job { @@ -133,6 +134,7 @@ struct Job { LIST_FIELDS(Job, transaction); LIST_FIELDS(Job, run_queue); LIST_FIELDS(Job, dbus_queue); + LIST_FIELDS(Job, gc_queue); LIST_HEAD(JobDependency, subject_list); LIST_HEAD(JobDependency, object_list); @@ -156,7 +158,7 @@ struct Job { * * There can be more than one client, because of job merging. */ - sd_bus_track *clients; + sd_bus_track *bus_track; char **deserialized_clients; JobResult result; @@ -168,6 +170,8 @@ struct Job { bool sent_dbus_new_signal:1; bool ignore_order:1; bool irreversible:1; + bool in_gc_queue:1; + bool ref_by_private_bus:1; }; Job* job_new(Unit *unit, JobType type); @@ -227,6 +231,12 @@ void job_shutdown_magic(Job *j); int job_get_timeout(Job *j, usec_t *timeout) _pure_; +bool job_check_gc(Job *j); +void job_add_to_gc_queue(Job *j); + +int job_get_before(Job *j, Job*** ret); +int job_get_after(Job *j, Job*** ret); + const char* job_type_to_string(JobType t) _const_; JobType job_type_from_string(const char *s) _pure_; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index cb2f384f47..f4ef5a0140 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -57,7 +57,7 @@ m4_ifdef(`HAVE_SECCOMP', $1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs) $1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context) $1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute) -$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context.restrict_namespaces) +$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context) $1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime) $1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)', `$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 52079980d8..970eed27c1 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3896,7 +3896,6 @@ int config_parse_no_new_privileges( } c->no_new_privileges = k; - c->no_new_privileges_set = true; return 0; } diff --git a/src/core/main.c b/src/core/main.c index f5f7df838d..5f9b1acad3 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -68,6 +68,7 @@ #include "mount-setup.h" #include "pager.h" #include "parse-util.h" +#include "path-util.h" #include "proc-cmdline.h" #include "process-util.h" #include "raw-clone.h" @@ -104,7 +105,7 @@ static bool arg_dump_core = true; static int arg_crash_chvt = -1; static bool arg_crash_shell = false; static bool arg_crash_reboot = false; -static bool arg_confirm_spawn = false; +static char *arg_confirm_spawn = NULL; static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; static bool arg_switched_root = false; static bool arg_no_pager = false; @@ -294,6 +295,28 @@ static int parse_crash_chvt(const char *value) { return 0; } +static int parse_confirm_spawn(const char *value, char **console) { + char *s; + int r; + + r = value ? parse_boolean(value) : 1; + if (r == 0) { + *console = NULL; + return 0; + } + + if (r > 0) /* on with default tty */ + s = strdup("/dev/console"); + else if (is_path(value)) /* on with fully qualified path */ + s = strdup(value); + else /* on with only a tty file name, not a fully qualified path */ + s = strjoin("/dev/", value); + if (!s) + return -ENOMEM; + *console = s; + return 0; +} + static int set_machine_id(const char *m) { sd_id128_t t; assert(m); @@ -355,11 +378,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat } else if (streq(key, "systemd.confirm_spawn") && value) { - r = parse_boolean(value); + arg_confirm_spawn = mfree(arg_confirm_spawn); + + r = parse_confirm_spawn(value, &arg_confirm_spawn); if (r < 0) - log_warning("Failed to parse confirm spawn switch %s. Ignoring.", value); - else - arg_confirm_spawn = r; + log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value); } else if (streq(key, "systemd.show_status") && value) { @@ -952,12 +975,11 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CONFIRM_SPAWN: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse confirm spawn boolean %s.", optarg); - return r; - } - arg_confirm_spawn = r; + arg_confirm_spawn = mfree(arg_confirm_spawn); + + r = parse_confirm_spawn(optarg, &arg_confirm_spawn); + if (r < 0) + return log_error_errno(r, "Failed to parse confirm spawn option: %m"); break; case ARG_SHOW_STATUS: @@ -1991,6 +2013,7 @@ finish: arg_default_rlimit[j] = mfree(arg_default_rlimit[j]); arg_default_unit = mfree(arg_default_unit); + arg_confirm_spawn = mfree(arg_confirm_spawn); arg_join_controllers = strv_free_free(arg_join_controllers); arg_default_environment = strv_free(arg_default_environment); arg_syscall_archs = set_free(arg_syscall_archs); diff --git a/src/core/manager.c b/src/core/manager.c index 52174eac07..1f663d3c1d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -111,6 +111,12 @@ static void manager_watch_jobs_in_progress(Manager *m) { assert(m); + /* We do not want to show the cylon animation if the user + * needs to confirm service executions otherwise confirmation + * messages will be screwed by the cylon animation. */ + if (!manager_is_confirm_spawn_disabled(m)) + return; + if (m->jobs_in_progress_event_source) return; @@ -981,10 +987,9 @@ good: unit_gc_mark_good(u, gc_marker); } -static unsigned manager_dispatch_gc_queue(Manager *m) { +static unsigned manager_dispatch_gc_unit_queue(Manager *m) { + unsigned n = 0, gc_marker; Unit *u; - unsigned n = 0; - unsigned gc_marker; assert(m); @@ -996,12 +1001,12 @@ static unsigned manager_dispatch_gc_queue(Manager *m) { gc_marker = m->gc_marker; - while ((u = m->gc_queue)) { + while ((u = m->gc_unit_queue)) { assert(u->in_gc_queue); unit_gc_sweep(u, gc_marker); - LIST_REMOVE(gc_queue, m->gc_queue, u); + LIST_REMOVE(gc_queue, m->gc_unit_queue, u); u->in_gc_queue = false; n++; @@ -1015,7 +1020,29 @@ static unsigned manager_dispatch_gc_queue(Manager *m) { } } - m->n_in_gc_queue = 0; + return n; +} + +static unsigned manager_dispatch_gc_job_queue(Manager *m) { + unsigned n = 0; + Job *j; + + assert(m); + + while ((j = m->gc_job_queue)) { + assert(j->in_gc_queue); + + LIST_REMOVE(gc_queue, m->gc_job_queue, j); + j->in_gc_queue = false; + + n++; + + if (job_check_gc(j)) + continue; + + log_unit_debug(j->unit, "Collecting job."); + (void) job_finish_and_invalidate(j, JOB_COLLECTED, false, false); + } return n; } @@ -1035,7 +1062,8 @@ static void manager_clear_jobs_and_units(Manager *m) { assert(!m->dbus_unit_queue); assert(!m->dbus_job_queue); assert(!m->cleanup_queue); - assert(!m->gc_queue); + assert(!m->gc_unit_queue); + assert(!m->gc_job_queue); assert(hashmap_isempty(m->jobs)); assert(hashmap_isempty(m->units)); @@ -2228,7 +2256,10 @@ int manager_loop(Manager *m) { if (manager_dispatch_load_queue(m) > 0) continue; - if (manager_dispatch_gc_queue(m) > 0) + if (manager_dispatch_gc_job_queue(m) > 0) + continue; + + if (manager_dispatch_gc_unit_queue(m) > 0) continue; if (manager_dispatch_cleanup_queue(m) > 0) @@ -2968,7 +2999,7 @@ void manager_check_finished(Manager *m) { manager_close_idle_pipe(m); /* Turn off confirm spawn now */ - m->confirm_spawn = false; + m->confirm_spawn = NULL; /* No need to update ask password status when we're going non-interactive */ manager_close_ask_password(m); @@ -3153,6 +3184,49 @@ static bool manager_get_show_status(Manager *m, StatusType type) { return false; } +const char *manager_get_confirm_spawn(Manager *m) { + static int last_errno = 0; + const char *vc = m->confirm_spawn; + struct stat st; + int r; + + /* Here's the deal: we want to test the validity of the console but don't want + * PID1 to go through the whole console process which might block. But we also + * want to warn the user only once if something is wrong with the console so we + * cannot do the sanity checks after spawning our children. So here we simply do + * really basic tests to hopefully trap common errors. + * + * If the console suddenly disappear at the time our children will really it + * then they will simply fail to acquire it and a positive answer will be + * assumed. New children will fallback to /dev/console though. + * + * Note: TTYs are devices that can come and go any time, and frequently aren't + * available yet during early boot (consider a USB rs232 dongle...). If for any + * reason the configured console is not ready, we fallback to the default + * console. */ + + if (!vc || path_equal(vc, "/dev/console")) + return vc; + + r = stat(vc, &st); + if (r < 0) + goto fail; + + if (!S_ISCHR(st.st_mode)) { + errno = ENOTTY; + goto fail; + } + + last_errno = 0; + return vc; +fail: + if (last_errno != errno) { + last_errno = errno; + log_warning_errno(errno, "Failed to open %s: %m, using default console", vc); + } + return "/dev/console"; +} + void manager_set_first_boot(Manager *m, bool b) { assert(m); @@ -3169,6 +3243,17 @@ void manager_set_first_boot(Manager *m, bool b) { m->first_boot = b; } +void manager_disable_confirm_spawn(void) { + (void) touch("/run/systemd/confirm_spawn_disabled"); +} + +bool manager_is_confirm_spawn_disabled(Manager *m) { + if (!m->confirm_spawn) + return true; + + return access("/run/systemd/confirm_spawn_disabled", F_OK) >= 0; +} + void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { va_list ap; diff --git a/src/core/manager.h b/src/core/manager.h index 35172fdba9..4a9a37caff 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -104,8 +104,9 @@ struct Manager { /* Units to remove */ LIST_HEAD(Unit, cleanup_queue); - /* Units to check when doing GC */ - LIST_HEAD(Unit, gc_queue); + /* Units and jobs to check when doing GC */ + LIST_HEAD(Unit, gc_unit_queue); + LIST_HEAD(Job, gc_job_queue); /* Units that should be realized */ LIST_HEAD(Unit, cgroup_queue); @@ -229,7 +230,6 @@ struct Manager { int pin_cgroupfs_fd; int gc_marker; - unsigned n_in_gc_queue; /* Flags */ ManagerExitCode exit_code:5; @@ -246,7 +246,7 @@ struct Manager { uint8_t return_value; ShowStatus show_status; - bool confirm_spawn; + char *confirm_spawn; bool no_console_output; ExecOutput default_std_output, default_std_error; @@ -403,3 +403,7 @@ 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 *manager_get_confirm_spawn(Manager *m); +bool manager_is_confirm_spawn_disabled(Manager *m); +void manager_disable_confirm_spawn(void); diff --git a/src/core/mount.c b/src/core/mount.c index 43e0f1c746..1c2be28d55 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -747,7 +747,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { return r; exec_params.environment = UNIT(m)->manager->environment; - exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(m)->manager); exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(m)->cgroup_path; exec_params.cgroup_delegate = m->cgroup_context.delegate; diff --git a/src/core/namespace.c b/src/core/namespace.c index 308e4d768e..e9ad26bfc3 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -58,17 +58,13 @@ typedef enum MountMode { } MountMode; typedef struct BindMount { - char *path; - MountMode mode; - bool ignore; /* Ignore if path does not exist */ + const char *path_const; /* Memory allocated on stack or static */ + MountMode mode:6; + bool ignore:1; /* Ignore if path does not exist? */ + bool has_prefix:1; /* Already is prefixed by the root dir? */ + char *path_malloc; /* Use this instead of 'path' if we had to allocate memory */ } 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 @@ -78,62 +74,62 @@ typedef struct TargetMount { */ /* 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 */ +static const BindMount 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 }, /* Obsolete API, there's no point in permitting access to this, ever */ + { "/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 */ }; /* ProtectKernelModules= option */ -static const TargetMount protect_kernel_modules_table[] = { +static const BindMount protect_kernel_modules_table[] = { #ifdef HAVE_SPLIT_USR - { "/lib/modules", INACCESSIBLE, true }, + { "/lib/modules", INACCESSIBLE, true }, #endif - { "/usr/lib/modules", INACCESSIBLE, true }, + { "/usr/lib/modules", INACCESSIBLE, true }, }; /* * 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 }, +static const BindMount 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 }, +static const BindMount 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 }, +static const BindMount 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 }, +static const BindMount protect_system_full_table[] = { + { "/usr", READONLY, false }, + { "/boot", READONLY, true }, + { "/efi", READONLY, true }, + { "/etc", READONLY, false }, }; /* @@ -144,7 +140,7 @@ static const TargetMount protect_system_full_table[] = { * (And of course /home and friends are also left writable, as ProtectHome= * shall manage those, orthogonally). */ -static const TargetMount protect_system_strict_table[] = { +static const BindMount protect_system_strict_table[] = { { "/", READONLY, false }, { "/proc", READWRITE, false }, /* ProtectKernelTunables= */ { "/sys", READWRITE, false }, /* ProtectKernelTunables= */ @@ -154,154 +150,107 @@ static const TargetMount protect_system_strict_table[] = { { "/root", READWRITE, true }, /* ProtectHome= */ }; -static void set_bind_mount(BindMount *p, char *path, MountMode mode, bool ignore) { - p->path = path; - p->mode = mode; - p->ignore = ignore; -} - -static int append_one_mount(BindMount **p, const char *root_directory, - const char *path, MountMode mode, bool ignore) { - char *lpath; +static const char *bind_mount_path(const BindMount *p) { assert(p); - lpath = prefix_root(root_directory, path); - if (!lpath) - return -ENOMEM; + /* Returns the path of this bind mount. If the malloc()-allocated ->path_buffer field is set we return that, + * otherwise the stack/static ->path field is returned. */ - set_bind_mount((*p)++, lpath, mode, ignore); - return 0; + return p->path_malloc ?: p->path_const; } -static int append_mounts(BindMount **p, char **strv, MountMode mode) { +static int append_access_mounts(BindMount **p, char **strv, MountMode mode) { char **i; assert(p); + /* Adds a list of user-supplied READWRITE/READONLY/INACCESSIBLE entries */ + STRV_FOREACH(i, strv) { - bool ignore = false; - char *path; + bool ignore = false, needs_prefix = false; + const char *e = *i; - if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) { - (*i)++; + /* Look for any prefixes */ + if (startswith(e, "-")) { + e++; ignore = true; } + if (startswith(e, "+")) { + e++; + needs_prefix = true; + } - if (!path_is_absolute(*i)) + if (!path_is_absolute(e)) return -EINVAL; - path = strdup(*i); - if (!path) - return -ENOMEM; - - set_bind_mount((*p)++, path, mode, ignore); + *((*p)++) = (BindMount) { + .path_const = e, + .mode = mode, + .ignore = ignore, + .has_prefix = !needs_prefix, + }; } return 0; } -static int append_target_mounts(BindMount **p, const char *root_directory, - const TargetMount *mounts, const size_t size, bool ignore_protect) { +static int append_static_mounts(BindMount **p, const BindMount *mounts, unsigned n, bool ignore_protect) { unsigned i; assert(p); assert(mounts); - for (i = 0; i < size; i++) { - bool ignore; - /* - * Here we assume that the ignore field is set during - * declaration we do not support "-" at the beginning. - */ - const TargetMount *m = &mounts[i]; - char *path; - - path = prefix_root(root_directory, m->path); - if (!path) - return -ENOMEM; - - if (!path_is_absolute(path)) - return -EINVAL; - - /* - * Ignore paths if they are not present. First we use our - * static tables otherwise fallback to Unit context. - */ - ignore = m->ignore || ignore_protect; + /* Adds a list of static pre-defined entries */ - set_bind_mount((*p)++, path, m->mode, ignore); - } + for (i = 0; i < n; i++) + *((*p)++) = (BindMount) { + .path_const = bind_mount_path(mounts+i), + .mode = mounts[i].mode, + .ignore = mounts[i].ignore || ignore_protect, + }; return 0; } -static int append_protect_kernel_tunables(BindMount **p, const char *root_directory, bool ignore_protect) { +static int append_protect_home(BindMount **p, ProtectHome protect_home, bool ignore_protect) { assert(p); - return append_target_mounts(p, root_directory, protect_kernel_tunables_table, - ELEMENTSOF(protect_kernel_tunables_table), ignore_protect); -} - -static int append_protect_kernel_modules(BindMount **p, const char *root_directory, bool ignore_protect) { - assert(p); - - return append_target_mounts(p, root_directory, protect_kernel_modules_table, - ELEMENTSOF(protect_kernel_modules_table), ignore_protect); -} - -static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home, bool ignore_protect) { - int r = 0; - - assert(p); + switch (protect_home) { - if (protect_home == PROTECT_HOME_NO) + case 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), - ignore_protect); - break; + return append_static_mounts(p, protect_home_read_only_table, ELEMENTSOF(protect_home_read_only_table), ignore_protect); + case PROTECT_HOME_YES: - r = append_target_mounts(p, root_directory, protect_home_yes_table, - ELEMENTSOF(protect_home_yes_table), ignore_protect); - break; + return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table), ignore_protect); + default: - r = -EINVAL; - break; + assert_not_reached("Unexpected ProtectHome= value"); } - - return r; } -static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system, bool ignore_protect) { - int r = 0; - +static int append_protect_system(BindMount **p, ProtectSystem protect_system, bool ignore_protect) { assert(p); - if (protect_system == PROTECT_SYSTEM_NO) + switch (protect_system) { + + case 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), ignore_protect); - break; + return append_static_mounts(p, protect_system_strict_table, ELEMENTSOF(protect_system_strict_table), ignore_protect); + case PROTECT_SYSTEM_YES: - r = append_target_mounts(p, root_directory, protect_system_yes_table, - ELEMENTSOF(protect_system_yes_table), ignore_protect); - break; + return append_static_mounts(p, protect_system_yes_table, ELEMENTSOF(protect_system_yes_table), ignore_protect); + case PROTECT_SYSTEM_FULL: - r = append_target_mounts(p, root_directory, protect_system_full_table, - ELEMENTSOF(protect_system_full_table), ignore_protect); - break; + return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table), ignore_protect); + default: - r = -EINVAL; - break; + assert_not_reached("Unexpected ProtectSystem= value"); } - - return r; } static int mount_path_compare(const void *a, const void *b) { @@ -309,7 +258,7 @@ static int mount_path_compare(const void *a, const void *b) { int d; /* If the paths are not equal, then order prefixes first */ - d = path_compare(p->path, q->path); + d = path_compare(bind_mount_path(p), bind_mount_path(q)); if (d != 0) return d; @@ -323,6 +272,34 @@ static int mount_path_compare(const void *a, const void *b) { return 0; } +static int prefix_where_needed(BindMount *m, unsigned n, const char *root_directory) { + unsigned i; + + /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs + * that. */ + + if (!root_directory) + return 0; + + for (i = 0; i < n; i++) { + char *s; + + if (m[i].has_prefix) + continue; + + s = prefix_root(root_directory, bind_mount_path(m+i)); + if (!s) + return -ENOMEM; + + free(m[i].path_malloc); + m[i].path_malloc = s; + + m[i].has_prefix = true; + } + + return 0; +} + static void drop_duplicates(BindMount *m, unsigned *n) { BindMount *f, *t, *previous; @@ -331,13 +308,13 @@ static void drop_duplicates(BindMount *m, unsigned *n) { /* Drops duplicate entries. Expects that the array is properly ordered already. */ - for (f = m, t = m, previous = NULL; f < m+*n; f++) { + for (f = m, t = m, previous = NULL; f < m + *n; f++) { /* 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); - f->path = mfree(f->path); + if (previous && path_equal(bind_mount_path(f), bind_mount_path(previous))) { + log_debug("%s is duplicate.", bind_mount_path(f)); + f->path_malloc = mfree(f->path_malloc); continue; } @@ -359,17 +336,17 @@ static void drop_inaccessible(BindMount *m, unsigned *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++) { + 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); - f->path = mfree(f->path); + if (clear && path_startswith(bind_mount_path(f), clear)) { + log_debug("%s is masked by %s.", bind_mount_path(f), clear); + f->path_malloc = mfree(f->path_malloc); continue; } - clear = f->mode == INACCESSIBLE ? f->path : NULL; + clear = f->mode == INACCESSIBLE ? bind_mount_path(f) : NULL; *t = *f; t++; @@ -387,7 +364,7 @@ static void drop_nop(BindMount *m, unsigned *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++) { + 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)) { @@ -396,7 +373,7 @@ static void drop_nop(BindMount *m, unsigned *n) { /* 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)) { + if (path_startswith(bind_mount_path(f), bind_mount_path(p))) { found = true; break; } @@ -404,8 +381,8 @@ static void drop_nop(BindMount *m, unsigned *n) { /* 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); - f->path = mfree(f->path); + log_debug("%s is redundant by %s", bind_mount_path(f), bind_mount_path(p)); + f->path_malloc = mfree(f->path_malloc); continue; } } @@ -423,16 +400,17 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned assert(m); assert(n); + /* Nothing to do */ if (!root_directory) return; /* Drops all mounts that are outside of the root directory. */ - for (f = m, t = m; f < m+*n; f++) { + 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); - f->path = mfree(f->path); + if (!path_startswith(bind_mount_path(f), root_directory)) { + log_debug("%s is outside of root directory.", bind_mount_path(f)); + f->path_malloc = mfree(f->path_malloc); continue; } @@ -548,11 +526,11 @@ static int mount_dev(BindMount *m) { * missing when the service is started with RootDirectory. This is * consistent with mount units creating the mount points when missing. */ - (void) mkdir_p_label(m->path, 0755); + (void) mkdir_p_label(bind_mount_path(m), 0755); /* Unmount everything in old /dev */ - umount_recursive(m->path, 0); - if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) { + umount_recursive(bind_mount_path(m), 0); + if (mount(dev, bind_mount_path(m), NULL, MS_MOVE, NULL) < 0) { r = -errno; goto fail; } @@ -592,7 +570,7 @@ static int apply_mount( assert(m); - log_debug("Applying namespace mount on %s", m->path); + log_debug("Applying namespace mount on %s", bind_mount_path(m)); switch (m->mode) { @@ -602,10 +580,10 @@ static int apply_mount( /* First, get rid of everything that is below if there * is anything... Then, overmount it with an * inaccessible path. */ - (void) umount_recursive(m->path, 0); + (void) umount_recursive(bind_mount_path(m), 0); - 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); + if (lstat(bind_mount_path(m), &target) < 0) + return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", bind_mount_path(m)); what = mode_to_inaccessible_node(target.st_mode); if (!what) { @@ -618,13 +596,13 @@ static int apply_mount( case READONLY: case READWRITE: - r = path_is_mount_point(m->path, 0); + r = path_is_mount_point(bind_mount_path(m), 0); if (r < 0) - return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", m->path); + return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", bind_mount_path(m)); 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; + what = bind_mount_path(m); break; case PRIVATE_TMP: @@ -644,10 +622,10 @@ static int apply_mount( assert(what); - 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); + if (mount(what, bind_mount_path(m), NULL, MS_BIND|MS_REC, NULL) < 0) + return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, bind_mount_path(m)); - log_debug("Successfully mounted %s to %s", what, m->path); + log_debug("Successfully mounted %s to %s", what, bind_mount_path(m)); return 0; } @@ -657,9 +635,9 @@ static int make_read_only(BindMount *m, char **blacklist) { assert(m); if (IN_SET(m->mode, INACCESSIBLE, READONLY)) - r = bind_remount_recursive(m->path, true, blacklist); + r = bind_remount_recursive(bind_mount_path(m), 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) + if (mount(NULL, bind_mount_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) r = -errno; } else return 0; @@ -671,9 +649,10 @@ static int make_read_only(BindMount *m, char **blacklist) { return r; } +/* Chase symlinks and remove failed paths from mounts */ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) { BindMount *f, *t; - int r; + int r = 0; assert(m); assert(n); @@ -684,21 +663,26 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned for (f = m, t = m; f < m + *n; f++) { _cleanup_free_ char *chased = NULL; + int k; + + k = chase_symlinks(bind_mount_path(f), root_directory, &chased); + if (k < 0) { + /* Get only real errors */ + if (r >= 0 && (k != -ENOENT || !f->ignore)) + r = k; - r = chase_symlinks(f->path, root_directory, &chased); - if (r == -ENOENT && f->ignore) { - /* Doesn't exist? Then remove it! */ - f->path = mfree(f->path); + /* Doesn't exist or failed? Then remove it and continue! */ + log_debug_errno(k, "Failed to chase symlinks for %s: %m", bind_mount_path(f)); + f->path_malloc = mfree(f->path_malloc); continue; } - if (r < 0) - return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); - if (!path_equal(f->path, chased)) { - log_debug("Chased %s → %s", f->path, chased); - r = free_and_replace(f->path, chased); - if (r < 0) - return r; + if (!path_equal(bind_mount_path(f), chased)) { + log_debug("Chased %s → %s", bind_mount_path(f), chased); + + free(f->path_malloc); + f->path_malloc = chased; + chased = NULL; } *t = *f; @@ -706,7 +690,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned } *n = t - m; - return 0; + return r; } static unsigned namespace_calculate_mounts( @@ -778,67 +762,73 @@ int setup_namespace( if (n_mounts > 0) { m = mounts = (BindMount *) alloca0(n_mounts * sizeof(BindMount)); - r = append_mounts(&m, read_write_paths, READWRITE); + r = append_access_mounts(&m, read_write_paths, READWRITE); if (r < 0) goto finish; - r = append_mounts(&m, read_only_paths, READONLY); + r = append_access_mounts(&m, read_only_paths, READONLY); if (r < 0) goto finish; - r = append_mounts(&m, inaccessible_paths, INACCESSIBLE); + r = append_access_mounts(&m, inaccessible_paths, INACCESSIBLE); if (r < 0) goto finish; if (tmp_dir) { - r = append_one_mount(&m, root_directory, "/tmp", PRIVATE_TMP, false); - if (r < 0) - goto finish; + *(m++) = (BindMount) { + .path_const = "/tmp", + .mode = PRIVATE_TMP, + }; } if (var_tmp_dir) { - r = append_one_mount(&m, root_directory, "/var/tmp", PRIVATE_VAR_TMP, false); - if (r < 0) - goto finish; + *(m++) = (BindMount) { + .path_const = "/var/tmp", + .mode = PRIVATE_VAR_TMP, + }; } if (ns_info->private_dev) { - r = append_one_mount(&m, root_directory, "/dev", PRIVATE_DEV, false); - if (r < 0) - goto finish; + *(m++) = (BindMount) { + .path_const = "/dev", + .mode = PRIVATE_DEV, + }; } if (ns_info->protect_kernel_tunables) { - r = append_protect_kernel_tunables(&m, root_directory, - ns_info->ignore_protect_paths); + r = append_static_mounts(&m, protect_kernel_tunables_table, ELEMENTSOF(protect_kernel_tunables_table), ns_info->ignore_protect_paths); if (r < 0) goto finish; } if (ns_info->protect_kernel_modules) { - r = append_protect_kernel_modules(&m, root_directory, - ns_info->ignore_protect_paths); + r = append_static_mounts(&m, protect_kernel_modules_table, ELEMENTSOF(protect_kernel_modules_table), ns_info->ignore_protect_paths); if (r < 0) goto finish; } if (ns_info->protect_control_groups) { - r = append_one_mount(&m, root_directory, "/sys/fs/cgroup", READONLY, false); - if (r < 0) - goto finish; + *(m++) = (BindMount) { + .path_const = "/sys/fs/cgroup", + .mode = READONLY, + }; } - r = append_protect_home(&m, root_directory, protect_home, - ns_info->ignore_protect_paths); + r = append_protect_home(&m, protect_home, ns_info->ignore_protect_paths); if (r < 0) goto finish; - r = append_protect_system(&m, root_directory, protect_system, false); + r = append_protect_system(&m, protect_system, false); if (r < 0) goto finish; assert(mounts + n_mounts == m); + /* Prepend the root directory where that's necessary */ + r = prefix_where_needed(mounts, n_mounts, root_directory); + if (r < 0) + goto finish; + /* 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. */ @@ -895,7 +885,7 @@ int setup_namespace( /* Create a blacklist we can pass to bind_mount_recursive() */ blacklist = newa(char*, n_mounts+1); for (j = 0; j < n_mounts; j++) - blacklist[j] = (char*) mounts[j].path; + blacklist[j] = (char*) bind_mount_path(mounts+j); blacklist[j] = NULL; /* Second round, flip the ro bits if necessary. */ @@ -925,7 +915,7 @@ int setup_namespace( finish: for (m = mounts; m < mounts + n_mounts; m++) - free(m->path); + free(m->path_malloc); return r; } diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index a61677e645..e824a2233c 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -66,6 +66,14 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="GetJobAfter"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" + send_member="GetJobBefore"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListUnits"/> <allow send_destination="org.freedesktop.systemd1" @@ -250,6 +258,14 @@ send_interface="org.freedesktop.systemd1.Job" send_member="Cancel"/> + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Job" + send_member="GetAfter"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Job" + send_member="GetBefore"/> + <allow receive_sender="org.freedesktop.systemd1"/> </policy> diff --git a/src/core/service.c b/src/core/service.c index 7aa1fba572..9ad4cf5070 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1335,7 +1335,7 @@ static int service_spawn( exec_params.fds = fds; exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; - exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/socket.c b/src/core/socket.c index ebacd74a47..1a53d47f21 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1778,7 +1778,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { exec_params.argv = argv; exec_params.environment = UNIT(s)->manager->environment; - exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/swap.c b/src/core/swap.c index b870ac88e3..bf404db8c3 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -636,7 +636,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { goto fail; exec_params.environment = UNIT(s)->manager->environment; - exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/unit.c b/src/core/unit.c index bba0f5d357..cba6342eca 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -389,10 +389,8 @@ void unit_add_to_gc_queue(Unit *u) { if (unit_check_gc(u)) return; - LIST_PREPEND(gc_queue, u->manager->gc_queue, u); + LIST_PREPEND(gc_queue, u->manager->gc_unit_queue, u); u->in_gc_queue = true; - - u->manager->n_in_gc_queue++; } void unit_add_to_dbus_queue(Unit *u) { @@ -570,10 +568,8 @@ void unit_free(Unit *u) { if (u->in_cleanup_queue) LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u); - if (u->in_gc_queue) { - LIST_REMOVE(gc_queue, u->manager->gc_queue, u); - u->manager->n_in_gc_queue--; - } + if (u->in_gc_queue) + LIST_REMOVE(gc_queue, u->manager->gc_unit_queue, u); if (u->in_cgroup_queue) LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); @@ -1515,6 +1511,17 @@ int unit_start_limit_test(Unit *u) { return emergency_action(u->manager, u->start_limit_action, u->reboot_arg, "unit failed"); } +bool unit_shall_confirm_spawn(Unit *u) { + + if (manager_is_confirm_spawn_disabled(u->manager)) + return false; + + /* For some reasons units remaining in the same process group + * as PID 1 fail to acquire the console even if it's not used + * by any process. So skip the confirmation question for them. */ + return !unit_get_exec_context(u)->same_pgrp; +} + /* Errors: * -EBADR: This unit type does not support starting. * -EALREADY: Unit is already started. @@ -3429,14 +3436,6 @@ int unit_patch_contexts(Unit *u) { ec->working_directory_missing_ok = true; } - if (MANAGER_IS_USER(u->manager) && - (ec->syscall_whitelist || - !set_isempty(ec->syscall_filter) || - !set_isempty(ec->syscall_archs) || - ec->address_families_whitelist || - !set_isempty(ec->address_families))) - ec->no_new_privileges = true; - if (ec->private_devices) ec->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) | (UINT64_C(1) << CAP_SYS_RAWIO)); diff --git a/src/core/unit.h b/src/core/unit.h index 991543664b..8052c234fd 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -441,6 +441,9 @@ struct UnitVTable { /* True if transient units of this type are OK */ bool can_transient:1; + + /* True if queued jobs of this type should be GC'ed if no other job needs them anymore */ + bool gc_jobs:1; }; extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; @@ -654,6 +657,8 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid); int unit_set_invocation_id(Unit *u, sd_id128_t id); int unit_acquire_invocation_id(Unit *u); +bool unit_shall_confirm_spawn(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 46507de937..f6a912ae06 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -141,13 +141,14 @@ static bool mount_in_initrd(struct mntent *me) { streq(me->mnt_dir, "/usr"); } -static int write_idle_timeout(FILE *f, const char *where, const char *opts) { +static int write_timeout(FILE *f, const char *where, const char *opts, + const char *filter, const char *variable) { _cleanup_free_ char *timeout = NULL; char timespan[FORMAT_TIMESPAN_MAX]; usec_t u; int r; - r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, NULL); + r = fstab_filter_options(opts, filter, NULL, &timeout, NULL); if (r < 0) return log_warning_errno(r, "Failed to parse options: %m"); if (r == 0) @@ -159,11 +160,21 @@ static int write_idle_timeout(FILE *f, const char *where, const char *opts) { return 0; } - fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0)); + fprintf(f, "%s=%s\n", variable, format_timespan(timespan, sizeof(timespan), u, 0)); return 0; } +static int write_idle_timeout(FILE *f, const char *where, const char *opts) { + return write_timeout(f, where, opts, + "x-systemd.idle-timeout\0", "TimeoutIdleSec"); +} + +static int write_mount_timeout(FILE *f, const char *where, const char *opts) { + return write_timeout(f, where, opts, + "x-systemd.mount-timeout\0", "TimeoutSec"); +} + static int write_requires_after(FILE *f, const char *opts) { _cleanup_strv_free_ char **names = NULL, **units = NULL; _cleanup_free_ char *res = NULL; @@ -327,6 +338,10 @@ static int add_mount( if (r < 0) return r; + r = write_mount_timeout(f, where, opts); + if (r < 0) + return r; + if (!isempty(filtered) && !streq(filtered, "defaults")) fprintf(f, "Options=%s\n", filtered); diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 99f690897d..5aa8aca426 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -32,7 +32,8 @@ int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, uint32_t xid, const uint8_t *mac_addr, - size_t mac_addr_len, uint16_t arp_type); + size_t mac_addr_len, uint16_t arp_type, + uint16_t port); int dhcp_network_bind_udp_socket(be32_t address, uint16_t port); int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len); @@ -57,7 +58,7 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, uint16_t source, be32_t destination_addr, uint16_t destination, uint16_t len); -int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum); +int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port); /* If we are invoking callbacks of a dhcp-client, ensure unreffing the * client from the callback doesn't destroy the object we are working diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c index a9f5a0a5de..3c85bb0b54 100644 --- a/src/libsystemd-network/dhcp-network.c +++ b/src/libsystemd-network/dhcp-network.c @@ -36,7 +36,8 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, size_t mac_addr_len, const uint8_t *bcast_addr, const struct ether_addr *eth_mac, - uint16_t arp_type, uint8_t dhcp_hlen) { + uint16_t arp_type, uint8_t dhcp_hlen, + uint16_t port) { struct sock_filter filter[] = { BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */ @@ -53,7 +54,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ @@ -125,7 +126,8 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, const uint8_t *mac_addr, - size_t mac_addr_len, uint16_t arp_type) { + size_t mac_addr_len, uint16_t arp_type, + uint16_t port) { static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /* Default broadcast address for IPoIB */ static const uint8_t ib_bcast[] = { @@ -151,7 +153,7 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, return -EINVAL; return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len, - bcast_addr, ð_mac, arp_type, dhcp_hlen); + bcast_addr, ð_mac, arp_type, dhcp_hlen, port); } int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 8be774061d..40442b3636 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -114,7 +114,7 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE); } -int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) { +int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) { size_t hdrlen; assert(packet); @@ -160,10 +160,10 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) { return -EINVAL; } - if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) { + if (be16toh(packet->udp.dest) != port) { log_debug("ignoring packet: to port %u, which " "is not the DHCP client port (%u)", - be16toh(packet->udp.dest), DHCP_PORT_CLIENT); + be16toh(packet->udp.dest), port); return -EINVAL; } diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 5ccb23922c..6475da2c2a 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -55,6 +55,7 @@ struct sd_dhcp_client { sd_event_source *timeout_resend; int ifindex; int fd; + uint16_t port; union sockaddr_union link; sd_event_source *receive_message; bool request_broadcast; @@ -426,6 +427,17 @@ int sd_dhcp_client_set_vendor_class_identifier( return 0; } +int sd_dhcp_client_set_client_port( + sd_dhcp_client *client, + uint16_t port) { + + assert_return(client, -EINVAL); + + client->port = port; + + return 0; +} + int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { assert_return(client, -EINVAL); assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE); @@ -668,7 +680,7 @@ static int dhcp_client_send_raw( DHCPPacket *packet, size_t len) { - dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT, + dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port, INADDR_BROADCAST, DHCP_PORT_SERVER, len); return dhcp_network_send_raw_socket(client->fd, &client->link, @@ -1120,7 +1132,7 @@ static int client_start_delayed(sd_dhcp_client *client) { r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid, client->mac_addr, - client->mac_addr_len, client->arp_type); + client->mac_addr_len, client->arp_type, client->port); if (r < 0) { client_stop(client, r); return r; @@ -1170,7 +1182,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid, client->mac_addr, - client->mac_addr_len, client->arp_type); + client->mac_addr_len, client->arp_type, + client->port); if (r < 0) { client_stop(client, r); return 0; @@ -1555,8 +1568,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i goto error; } - r = dhcp_network_bind_udp_socket(client->lease->address, - DHCP_PORT_CLIENT); + r = dhcp_network_bind_udp_socket(client->lease->address, client->port); if (r < 0) { log_dhcp_client(client, "could not bind UDP socket"); goto error; @@ -1766,7 +1778,7 @@ static int client_receive_message_raw( } } - r = dhcp_packet_verify_headers(packet, len, checksum); + r = dhcp_packet_verify_headers(packet, len, checksum, client->port); if (r < 0) return 0; @@ -1891,6 +1903,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) { client->fd = -1; client->attempt = 1; client->mtu = DHCP_DEFAULT_MIN_SIZE; + client->port = DHCP_PORT_CLIENT; client->req_opts_size = ELEMENTSOF(default_req_opts); client->req_opts = memdup(default_req_opts, client->req_opts_size); diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 2a101cb1fe..c10ca74b86 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -195,7 +195,7 @@ int dhcp_network_bind_raw_socket( union sockaddr_union *link, uint32_t id, const uint8_t *addr, size_t addr_len, - uint16_t arp_type) { + uint16_t arp_type, uint16_t port) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0) return -errno; diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c index 09cbd9ab44..a05794941f 100644 --- a/src/libsystemd/sd-bus/busctl-introspect.c +++ b/src/libsystemd/sd-bus/busctl-introspect.c @@ -143,9 +143,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) { case STATE_NAME: if (t == XML_ATTRIBUTE_VALUE) { - free(field); - field = name; - name = NULL; + free_and_replace(field, name); state = STATE_ANNOTATION; } else { @@ -158,9 +156,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) { case STATE_VALUE: if (t == XML_ATTRIBUTE_VALUE) { - free(value); - value = name; - name = NULL; + free_and_replace(value, name); state = STATE_ANNOTATION; } else { @@ -194,6 +190,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth STATE_SIGNAL_ARG, STATE_SIGNAL_ARG_NAME, STATE_SIGNAL_ARG_TYPE, + STATE_SIGNAL_ARG_DIRECTION, STATE_PROPERTY, STATE_PROPERTY_NAME, STATE_PROPERTY_TYPE, @@ -350,11 +347,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_INTERFACE_NAME: if (t == XML_ATTRIBUTE_VALUE) { - if (n_depth == 0) { - free(context->interface_name); - context->interface_name = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->interface_name, name); state = STATE_INTERFACE; } else { @@ -409,12 +403,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_METHOD_NAME: if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->member_name, name); state = STATE_METHOD; } else { @@ -432,7 +422,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth else if (streq_ptr(name, "type")) state = STATE_METHOD_ARG_TYPE; else if (streq_ptr(name, "direction")) - state = STATE_METHOD_ARG_DIRECTION; + state = STATE_METHOD_ARG_DIRECTION; else { log_error("Unexpected method <arg> attribute %s.", name); return -EBADMSG; @@ -458,7 +448,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth } else if (streq(argument_direction, "out")) { if (!strextend(&context->member_result, argument_type, NULL)) return log_oom(); - } + } else + log_error("Unexpected method <arg> direction value '%s'.", argument_direction); } argument_type = mfree(argument_type); @@ -487,9 +478,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_METHOD_ARG_TYPE: if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; + free_and_replace(argument_type, name); state = STATE_METHOD_ARG; } else { @@ -502,9 +491,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_METHOD_ARG_DIRECTION: if (t == XML_ATTRIBUTE_VALUE) { - free(argument_direction); - argument_direction = name; - name = NULL; + free_and_replace(argument_direction, name); state = STATE_METHOD_ARG; } else { @@ -559,12 +546,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_SIGNAL_NAME: if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->member_name, name); state = STATE_SIGNAL; } else { @@ -582,6 +565,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth state = STATE_SIGNAL_ARG_NAME; else if (streq_ptr(name, "type")) state = STATE_SIGNAL_ARG_TYPE; + else if (streq_ptr(name, "direction")) + state = STATE_SIGNAL_ARG_DIRECTION; else { log_error("Unexpected signal <arg> attribute %s.", name); return -EBADMSG; @@ -599,8 +584,11 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { if (argument_type) { - if (!strextend(&context->member_signature, argument_type, NULL)) - return log_oom(); + if (!argument_direction || streq(argument_direction, "out")) { + if (!strextend(&context->member_signature, argument_type, NULL)) + return log_oom(); + } else + log_error("Unexpected signal <arg> direction value '%s'.", argument_direction); argument_type = mfree(argument_type); } @@ -627,9 +615,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_SIGNAL_ARG_TYPE: if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; + free_and_replace(argument_type, name); state = STATE_SIGNAL_ARG; } else { @@ -639,6 +625,19 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth break; + case STATE_SIGNAL_ARG_DIRECTION: + + if (t == XML_ATTRIBUTE_VALUE) { + free_and_replace(argument_direction, name); + + state = STATE_SIGNAL_ARG; + } else { + log_error("Unexpected token in signal <arg>. (4)"); + return -EINVAL; + } + + break; + case STATE_PROPERTY: if (t == XML_ATTRIBUTE_NAME) { @@ -688,12 +687,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_PROPERTY_NAME: if (t == XML_ATTRIBUTE_VALUE) { + if (n_depth == 0) + free_and_replace(context->member_name, name); - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } state = STATE_PROPERTY; } else { log_error("Unexpected token in <property>. (2)"); @@ -705,12 +701,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_PROPERTY_TYPE: if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_signature); - context->member_signature = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->member_signature, name); state = STATE_PROPERTY; } else { diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index f8e18f23fd..0d8d99c56d 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -224,13 +224,8 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { return -ENODATA; if (r < 0) return r; - if (isempty(s)) { - *ret = NULL; - return 0; - } - x = s; - for (;;) { + for (x = s;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&x, &word, NULL, 0); @@ -243,15 +238,14 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { if (r < 0) return r; - if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + if (!GREEDY_REALLOC(ifis, allocated, c + 2)) return -ENOMEM; ifis[c++] = ifindex; } - if (!GREEDY_REALLOC(ifis, allocated, c + 1)) - return -ENOMEM; - ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ + if (ifis) + ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ *ret = ifis; ifis = NULL; diff --git a/src/network/.gitignore b/src/network/.gitignore index aca55206b7..230671763d 100644 --- a/src/network/.gitignore +++ b/src/network/.gitignore @@ -1,3 +1,2 @@ /networkd-network-gperf.c -/networkd-netdev-gperf.c /networkd-gperf.c diff --git a/src/network/netdev/.gitignore b/src/network/netdev/.gitignore new file mode 100644 index 0000000000..0f1a65d2e6 --- /dev/null +++ b/src/network/netdev/.gitignore @@ -0,0 +1 @@ +/netdev-gperf.c diff --git a/src/network/networkd-netdev-bond.c b/src/network/netdev/bond.c index 46d1669337..19b0e8da40 100644 --- a/src/network/networkd-netdev-bond.c +++ b/src/network/netdev/bond.c @@ -27,7 +27,7 @@ #include "conf-parser.h" #include "extract-word.h" #include "missing.h" -#include "networkd-netdev-bond.h" +#include "netdev/bond.h" #include "string-table.h" #include "string-util.h" diff --git a/src/network/networkd-netdev-bond.h b/src/network/netdev/bond.h index b941edb344..fb88b538ed 100644 --- a/src/network/networkd-netdev-bond.h +++ b/src/network/netdev/bond.h @@ -22,7 +22,7 @@ #include "in-addr-util.h" #include "list.h" -#include "networkd-netdev.h" +#include "netdev/netdev.h" /* * Maximum number of targets supported by the kernel for a single diff --git a/src/network/networkd-netdev-bridge.c b/src/network/netdev/bridge.c index 002ad94210..08e31b974f 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/netdev/bridge.c @@ -22,8 +22,8 @@ #include "missing.h" #include "netlink-util.h" -#include "networkd.h" -#include "networkd-netdev-bridge.h" +#include "netdev/bridge.h" +#include "networkd-manager.h" /* callback for brige netdev's parameter set */ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { diff --git a/src/network/networkd-netdev-bridge.h b/src/network/netdev/bridge.h index 53f72f1ea5..093c60d5b5 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/netdev/bridge.h @@ -19,7 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "networkd-netdev.h" +#include "netdev/netdev.h" typedef struct Bridge { NetDev meta; diff --git a/src/network/networkd-netdev-dummy.c b/src/network/netdev/dummy.c index 6617a86c20..5e6e162931 100644 --- a/src/network/networkd-netdev-dummy.c +++ b/src/network/netdev/dummy.c @@ -19,7 +19,7 @@ ***/ -#include "networkd-netdev-dummy.h" +#include "netdev/dummy.h" const NetDevVTable dummy_vtable = { .object_size = sizeof(Dummy), diff --git a/src/network/networkd-netdev-dummy.h b/src/network/netdev/dummy.h index efe302267e..a908400459 100644 --- a/src/network/networkd-netdev-dummy.h +++ b/src/network/netdev/dummy.h @@ -19,7 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "networkd-netdev.h" +#include "netdev/netdev.h" typedef struct Dummy { NetDev meta; diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/netdev/ipvlan.c index af4177e43a..3b5c30fed8 100644 --- a/src/network/networkd-netdev-ipvlan.c +++ b/src/network/netdev/ipvlan.c @@ -20,7 +20,7 @@ #include <net/if.h> #include "conf-parser.h" -#include "networkd-netdev-ipvlan.h" +#include "netdev/ipvlan.h" #include "string-table.h" static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = { diff --git a/src/network/networkd-netdev-ipvlan.h b/src/network/netdev/ipvlan.h index 10d4079844..7d7d0184f1 100644 --- a/src/network/networkd-netdev-ipvlan.h +++ b/src/network/netdev/ipvlan.h @@ -20,7 +20,7 @@ ***/ #include "missing.h" -#include "networkd-netdev.h" +#include "netdev/netdev.h" typedef enum IPVlanMode { NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2, diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/netdev/macvlan.c index 48e98aa51b..93f650def5 100644 --- a/src/network/networkd-netdev-macvlan.c +++ b/src/network/netdev/macvlan.c @@ -20,7 +20,7 @@ #include <net/if.h> #include "conf-parser.h" -#include "networkd-netdev-macvlan.h" +#include "netdev/macvlan.h" #include "string-table.h" static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = { diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/netdev/macvlan.h index 3663f4f051..118d55658c 100644 --- a/src/network/networkd-netdev-macvlan.h +++ b/src/network/netdev/macvlan.h @@ -21,7 +21,7 @@ typedef struct MacVlan MacVlan; -#include "networkd-netdev.h" +#include "netdev/netdev.h" typedef enum MacVlanMode { NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE, diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 323eaa8032..b3461e39a9 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -2,17 +2,17 @@ #include <stddef.h> #include "conf-parser.h" #include "network-internal.h" -#include "networkd-netdev-bond.h" -#include "networkd-netdev-bridge.h" -#include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-macvlan.h" -#include "networkd-netdev-tunnel.h" -#include "networkd-netdev-tuntap.h" -#include "networkd-netdev-veth.h" -#include "networkd-netdev-vlan.h" -#include "networkd-netdev-vxlan.h" -#include "networkd-netdev-vrf.h" -#include "networkd-netdev.h" +#include "netdev/bond.h" +#include "netdev/bridge.h" +#include "netdev/ipvlan.h" +#include "netdev/macvlan.h" +#include "netdev/tunnel.h" +#include "netdev/tuntap.h" +#include "netdev/veth.h" +#include "netdev/vlan.h" +#include "netdev/vxlan.h" +#include "netdev/vrf.h" +#include "netdev/netdev.h" #include "vlan-util.h" %} struct ConfigPerfItem; diff --git a/src/network/networkd-netdev.c b/src/network/netdev/netdev.c index a210ba1242..9b9e83d9db 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/netdev/netdev.c @@ -26,13 +26,26 @@ #include "list.h" #include "netlink-util.h" #include "network-internal.h" -#include "networkd-netdev.h" -#include "networkd.h" +#include "netdev/netdev.h" +#include "networkd-manager.h" #include "siphash24.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "netdev/bridge.h" +#include "netdev/bond.h" +#include "netdev/vlan.h" +#include "netdev/macvlan.h" +#include "netdev/ipvlan.h" +#include "netdev/vxlan.h" +#include "netdev/tunnel.h" +#include "netdev/tuntap.h" +#include "netdev/veth.h" +#include "netdev/dummy.h" +#include "netdev/vrf.h" +#include "netdev/vcan.h" + const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_BRIDGE] = &bridge_vtable, [NETDEV_KIND_BOND] = &bond_vtable, diff --git a/src/network/networkd-netdev.h b/src/network/netdev/netdev.h index 70ff947b99..70ff947b99 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/netdev/netdev.h diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/netdev/tunnel.c index 9138ee4511..b03e770061 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/netdev/tunnel.c @@ -28,7 +28,7 @@ #include "conf-parser.h" #include "missing.h" #include "networkd-link.h" -#include "networkd-netdev-tunnel.h" +#include "netdev/tunnel.h" #include "parse-util.h" #include "string-table.h" #include "string-util.h" diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/netdev/tunnel.h index 32a46bd82f..d78c6135ee 100644 --- a/src/network/networkd-netdev-tunnel.h +++ b/src/network/netdev/tunnel.h @@ -21,7 +21,7 @@ #include "in-addr-util.h" -#include "networkd-netdev.h" +#include "netdev/netdev.h" typedef enum Ip6TnlMode { NETDEV_IP6_TNL_MODE_IP6IP6, diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/netdev/tuntap.c index 088a4d8d32..3d62808842 100644 --- a/src/network/networkd-netdev-tuntap.c +++ b/src/network/netdev/tuntap.c @@ -27,7 +27,7 @@ #include "alloc-util.h" #include "fd-util.h" -#include "networkd-netdev-tuntap.h" +#include "netdev/tuntap.h" #include "user-util.h" #define TUN_DEV "/dev/net/tun" diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/netdev/tuntap.h index 120f00a353..95d3fcf1e9 100644 --- a/src/network/networkd-netdev-tuntap.h +++ b/src/network/netdev/tuntap.h @@ -21,7 +21,7 @@ typedef struct TunTap TunTap; -#include "networkd-netdev.h" +#include "netdev/netdev.h" struct TunTap { NetDev meta; diff --git a/src/network/networkd-netdev-vcan.c b/src/network/netdev/vcan.c index bfce6e1962..7f56702938 100644 --- a/src/network/networkd-netdev-vcan.c +++ b/src/network/netdev/vcan.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "networkd-netdev-vcan.h" +#include "netdev/vcan.h" const NetDevVTable vcan_vtable = { .object_size = sizeof(VCan), diff --git a/src/network/networkd-netdev-vcan.h b/src/network/netdev/vcan.h index 6ba47fd70e..00838b7675 100644 --- a/src/network/networkd-netdev-vcan.h +++ b/src/network/netdev/vcan.h @@ -23,7 +23,7 @@ typedef struct VCan VCan; #include <linux/can/netlink.h> -#include "networkd-netdev.h" +#include "netdev/netdev.h" struct VCan { NetDev meta; diff --git a/src/network/networkd-netdev-veth.c b/src/network/netdev/veth.c index b122a06c25..350b59bf03 100644 --- a/src/network/networkd-netdev-veth.c +++ b/src/network/netdev/veth.c @@ -22,7 +22,7 @@ #include "sd-netlink.h" -#include "networkd-netdev-veth.h" +#include "netdev/veth.h" static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { Veth *v; diff --git a/src/network/networkd-netdev-veth.h b/src/network/netdev/veth.h index e69bfbc8f0..b00ce476e8 100644 --- a/src/network/networkd-netdev-veth.h +++ b/src/network/netdev/veth.h @@ -21,7 +21,7 @@ typedef struct Veth Veth; -#include "networkd-netdev.h" +#include "netdev/netdev.h" struct Veth { NetDev meta; diff --git a/src/network/networkd-netdev-vlan.c b/src/network/netdev/vlan.c index 3cc072388f..28c061fa4f 100644 --- a/src/network/networkd-netdev-vlan.c +++ b/src/network/netdev/vlan.c @@ -19,7 +19,7 @@ #include <net/if.h> -#include "networkd-netdev-vlan.h" +#include "netdev/vlan.h" #include "vlan-util.h" static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { diff --git a/src/network/networkd-netdev-vlan.h b/src/network/netdev/vlan.h index 2dfe314b6e..fade899997 100644 --- a/src/network/networkd-netdev-vlan.h +++ b/src/network/netdev/vlan.h @@ -21,7 +21,7 @@ typedef struct VLan VLan; -#include "networkd-netdev.h" +#include "netdev/netdev.h" struct VLan { NetDev meta; diff --git a/src/network/networkd-netdev-vrf.c b/src/network/netdev/vrf.c index 89bd142e8c..f48b413102 100644 --- a/src/network/networkd-netdev-vrf.c +++ b/src/network/netdev/vrf.c @@ -21,7 +21,7 @@ #include "sd-netlink.h" #include "missing.h" -#include "networkd-netdev-vrf.h" +#include "netdev/vrf.h" static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { Vrf *v; diff --git a/src/network/networkd-netdev-vrf.h b/src/network/netdev/vrf.h index 3d92a26a4d..00f54ed96d 100644 --- a/src/network/networkd-netdev-vrf.h +++ b/src/network/netdev/vrf.h @@ -21,7 +21,7 @@ typedef struct Vrf Vrf; -#include "networkd-netdev.h" +#include "netdev/netdev.h" struct Vrf { NetDev meta; diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/netdev/vxlan.c index 706e52b698..10c892b044 100644 --- a/src/network/networkd-netdev-vxlan.c +++ b/src/network/netdev/vxlan.c @@ -28,7 +28,7 @@ #include "missing.h" #include "networkd-link.h" -#include "networkd-netdev-vxlan.h" +#include "netdev/vxlan.h" static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { VxLan *v; diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/netdev/vxlan.h index 3906820afb..6c3081d5fc 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/netdev/vxlan.h @@ -22,7 +22,7 @@ typedef struct VxLan VxLan; #include "in-addr-util.h" -#include "networkd-netdev.h" +#include "netdev/netdev.h" #define VXLAN_VID_MAX (1u << 24) - 1 diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index ebc6c9eb9e..a63b925a4a 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -19,7 +19,7 @@ #include "alloc-util.h" #include "networkd-address-pool.h" -#include "networkd.h" +#include "networkd-manager.h" #include "set.h" #include "string-util.h" diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index ed52d5e42d..2b698d9531 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -24,7 +24,7 @@ #include "firewall-util.h" #include "netlink-util.h" #include "networkd-address.h" -#include "networkd.h" +#include "networkd-manager.h" #include "parse-util.h" #include "set.h" #include "socket-util.h" diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c index 18ecd86858..fa5d3ee7fa 100644 --- a/src/network/networkd-brvlan.c +++ b/src/network/networkd-brvlan.c @@ -25,7 +25,9 @@ #include "conf-parser.h" #include "netlink-util.h" #include "networkd-brvlan.h" -#include "networkd.h" +#include "networkd-link.h" +#include "networkd-manager.h" +#include "networkd-network.h" #include "parse-util.h" #include "vlan-util.h" diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c index 49bb8c18f6..aaa27f311d 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -22,8 +22,10 @@ #include "conf-parser.h" #include "def.h" #include "dhcp-identifier.h" +#include "extract-word.h" #include "hexdecoct.h" #include "networkd-conf.h" +#include "networkd-network.h" #include "string-table.h" int manager_parse_config_file(Manager *m) { diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h index c7bfb42a72..93819626ba 100644 --- a/src/network/networkd-conf.h +++ b/src/network/networkd-conf.h @@ -19,7 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "networkd.h" +typedef struct Manager Manager; int manager_parse_config_file(Manager *m); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 76d3d132ea..614bceefab 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -24,7 +24,9 @@ #include "dhcp-lease-internal.h" #include "hostname-util.h" #include "network-internal.h" -#include "networkd.h" +#include "networkd-link.h" +#include "networkd-manager.h" +#include "networkd-network.h" static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { @@ -630,6 +632,12 @@ int dhcp4_configure(Link *link) { return r; } + if (link->network->dhcp_client_port) { + r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port); + if (r < 0) + return r; + } + switch (link->network->dhcp_client_identifier) { case DHCP_CLIENT_ID_DUID: { /* If configured, apply user specified DUID and/or IAID */ diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 15acf56a5f..6ba2d170e7 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -23,7 +23,8 @@ #include "sd-dhcp6-client.h" #include "network-internal.h" -#include "networkd.h" +#include "networkd-link.h" +#include "networkd-manager.h" static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); @@ -125,7 +126,6 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { assert(link); assert(link->network); - assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index ed5a47589e..3d7f4d2b2d 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -22,9 +22,10 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "netdev/bridge.h" #include "netlink-util.h" #include "networkd-fdb.h" -#include "networkd.h" +#include "networkd-manager.h" #include "util.h" #include "vlan-util.h" diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index 3fdfe74955..eca436d9fd 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -2,6 +2,7 @@ #include <stddef.h> #include "conf-parser.h" #include "networkd-conf.h" +#include "networkd-manager.h" %} struct ConfigPerfItem; %null_strings diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 2d81311e81..7ba05dbec3 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -21,7 +21,9 @@ #include <linux/if.h> #include "network-internal.h" -#include "networkd.h" +#include "networkd-address.h" +#include "networkd-manager.h" +#include "networkd-link.h" static int ipv4ll_address_lost(Link *link) { _cleanup_address_free_ Address *address = NULL; @@ -171,7 +173,6 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { assert(link); assert(link->network); - assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 532557ed6c..c39c648334 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -20,7 +20,7 @@ #include "alloc-util.h" #include "bus-util.h" #include "networkd-link.h" -#include "networkd.h" +#include "networkd-manager.h" #include "parse-util.h" #include "strv.h" diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index aefe7335b9..0b634572a9 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -29,8 +29,8 @@ #include "netlink-util.h" #include "network-internal.h" #include "networkd-lldp-tx.h" +#include "networkd-manager.h" #include "networkd-ndisc.h" -#include "networkd.h" #include "set.h" #include "socket-util.h" #include "stdio-util.h" @@ -2632,7 +2632,7 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m, log_link_debug(link, "Ignoring DHCP server for loopback link"); } - r = network_apply(link->manager, network, link); + r = network_apply(network, link); if (r < 0) return r; } @@ -2728,7 +2728,7 @@ static int link_load(Link *link) { goto network_file_fail; } - r = network_apply(link->manager, network, link); + r = network_apply(network, link); if (r < 0) return log_link_error_errno(link, r, "Failed to apply network %s: %m", basename(network_file)); } diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index 3aa768388b..2de63ce746 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -26,7 +26,7 @@ #include "fileio.h" #include "hostname-util.h" #include "networkd-lldp-tx.h" -#include "networkd.h" +#include "networkd-manager.h" #include "parse-util.h" #include "random-util.h" #include "socket-util.h" diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c index 0c429b9471..cbb1b93031 100644 --- a/src/network/networkd-manager-bus.c +++ b/src/network/networkd-manager-bus.c @@ -19,7 +19,7 @@ #include "alloc-util.h" #include "bus-util.h" -#include "networkd.h" +#include "networkd-manager.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 9174dcc7f4..a1252c9b51 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -33,7 +33,7 @@ #include "libudev-private.h" #include "local-addresses.h" #include "netlink-util.h" -#include "networkd.h" +#include "networkd-manager.h" #include "ordered-set.h" #include "path-util.h" #include "set.h" diff --git a/src/network/networkd.h b/src/network/networkd-manager.h index cb1b73145e..a90d9a933f 100644 --- a/src/network/networkd.h +++ b/src/network/networkd-manager.h @@ -32,20 +32,7 @@ #include "networkd-address-pool.h" #include "networkd-link.h" -#include "networkd-netdev-bond.h" -#include "networkd-netdev-bridge.h" -#include "networkd-netdev-dummy.h" -#include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-macvlan.h" -#include "networkd-netdev-tunnel.h" -#include "networkd-netdev-tuntap.h" -#include "networkd-netdev-veth.h" -#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" extern const char* const network_dirs[]; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 4853791aa5..70283e5347 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -18,11 +18,12 @@ ***/ #include <netinet/icmp6.h> +#include <arpa/inet.h> #include "sd-ndisc.h" -#include "networkd.h" #include "networkd-ndisc.h" +#include "networkd-route.h" #define NDISC_DNSSL_MAX 64U #define NDISC_RDNSS_MAX 64U diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c index 6e21676d23..3b835b52f9 100644 --- a/src/network/networkd-network-bus.c +++ b/src/network/networkd-network-bus.c @@ -18,7 +18,7 @@ ***/ #include "alloc-util.h" -#include "networkd.h" +#include "networkd-manager.h" #include "string-util.h" #include "strv.h" diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index bcf8186c33..efd3176ac3 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -1,8 +1,8 @@ %{ #include <stddef.h> #include "conf-parser.h" -#include "networkd.h" #include "networkd-conf.h" +#include "networkd-network.h" #include "network-internal.h" #include "vlan-util.h" %} @@ -100,6 +100,7 @@ DHCP.RouteMetric, config_parse_unsigned, 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) +DHCP.ListenPort, config_parse_uint32, 0, offsetof(Network, dhcp_client_port) 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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 0dc00e874d..31e899eecd 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -27,8 +27,8 @@ #include "fd-util.h" #include "hostname-util.h" #include "network-internal.h" +#include "networkd-manager.h" #include "networkd-network.h" -#include "networkd.h" #include "parse-util.h" #include "set.h" #include "stat-util.h" @@ -368,10 +368,9 @@ int network_get(Manager *manager, struct udev_device *device, return -ENOENT; } -int network_apply(Manager *manager, Network *network, Link *link) { +int network_apply(Network *network, Link *link) { int r; - assert(manager); assert(network); assert(link); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 42fc82d392..e956a59fe3 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -31,9 +31,9 @@ #include "networkd-brvlan.h" #include "networkd-fdb.h" #include "networkd-lldp-tx.h" -#include "networkd-netdev.h" #include "networkd-route.h" #include "networkd-util.h" +#include "netdev/netdev.h" #define DHCP_ROUTE_METRIC 1024 #define IPV4LL_ROUTE_METRIC 2048 @@ -124,6 +124,7 @@ struct Network { bool dhcp_use_timezone; unsigned dhcp_route_metric; uint32_t dhcp_route_table; + uint32_t dhcp_client_port; /* DHCP Server Support */ bool dhcp_server; @@ -212,7 +213,7 @@ int network_load(Manager *manager); int network_get_by_name(Manager *manager, const char *name, Network **ret); int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); -int network_apply(Manager *manager, Network *network, Link *link); +int network_apply(Network *network, Link *link); bool network_has_static_ipv6_addresses(Network *network); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 6f60ee5e31..f78e106991 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -21,8 +21,8 @@ #include "conf-parser.h" #include "in-addr-util.h" #include "netlink-util.h" +#include "networkd-manager.h" #include "networkd-route.h" -#include "networkd.h" #include "parse-util.h" #include "set.h" #include "string-util.h" diff --git a/src/network/networkd.c b/src/network/networkd.c index c8f81a2ca6..2851432eff 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -20,8 +20,8 @@ #include "sd-daemon.h" #include "capability-util.h" -#include "networkd.h" #include "networkd-conf.h" +#include "networkd-manager.h" #include "signal-util.h" #include "user-util.h" diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index adbe09a5e1..eee91d11d2 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -2,9 +2,11 @@ #include "dhcp6-protocol.h" #include "ethtool-util.h" #include "netlink-internal.h" -#include "networkd-netdev-bond.h" -#include "networkd-netdev-macvlan.h" -#include "networkd.h" +#include "netdev/bond.h" +#include "netdev/ipvlan.h" +#include "netdev/macvlan.h" +#include "networkd-link.h" +#include "networkd-util.h" #include "test-tables.h" int main(int argc, char **argv) { diff --git a/src/network/test-network.c b/src/network/test-network.c index 855646173f..93184a7f88 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -20,7 +20,7 @@ #include "alloc-util.h" #include "dhcp-lease-internal.h" #include "network-internal.h" -#include "networkd.h" +#include "networkd-manager.h" static void test_deserialize_in_addr(void) { _cleanup_free_ struct in_addr *addresses = NULL; diff --git a/src/network/networkd-wait-online-link.c b/src/network/wait-online/link.c index e63ba07e90..bd8578cf93 100644 --- a/src/network/networkd-wait-online-link.c +++ b/src/network/wait-online/link.c @@ -21,7 +21,9 @@ #include "sd-network.h" #include "alloc-util.h" -#include "networkd-wait-online-link.h" +#include "hashmap.h" +#include "link.h" +#include "manager.h" #include "string-util.h" int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) { diff --git a/src/network/networkd-wait-online-link.h b/src/network/wait-online/link.h index dc35085c55..c846e60c45 100644 --- a/src/network/networkd-wait-online-link.h +++ b/src/network/wait-online/link.h @@ -20,9 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -typedef struct Link Link; +#include "sd-netlink.h" -#include "networkd-wait-online.h" +typedef struct Link Link; +typedef struct Manager Manager; struct Link { Manager *manager; diff --git a/src/network/networkd-wait-online-manager.c b/src/network/wait-online/manager.c index 725b3310dd..d51b0a59d6 100644 --- a/src/network/networkd-wait-online-manager.c +++ b/src/network/wait-online/manager.c @@ -22,10 +22,10 @@ #include <fnmatch.h> #include "alloc-util.h" +#include "link.h" +#include "manager.h" #include "netlink-util.h" #include "network-internal.h" -#include "networkd-wait-online-link.h" -#include "networkd-wait-online.h" #include "time-util.h" #include "util.h" diff --git a/src/network/networkd-wait-online.h b/src/network/wait-online/manager.h index f91995c306..052f6b9780 100644 --- a/src/network/networkd-wait-online.h +++ b/src/network/wait-online/manager.h @@ -26,8 +26,7 @@ #include "hashmap.h" typedef struct Manager Manager; - -#include "networkd-wait-online-link.h" +typedef struct Link Link; struct Manager { Hashmap *links; diff --git a/src/network/networkd-wait-online.c b/src/network/wait-online/wait-online.c index 3220c4b7ef..268cbdb629 100644 --- a/src/network/networkd-wait-online.c +++ b/src/network/wait-online/wait-online.c @@ -22,7 +22,7 @@ #include "sd-daemon.h" -#include "networkd-wait-online.h" +#include "manager.h" #include "signal-util.h" #include "strv.h" diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 69b9efe320..a6adbbe879 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -111,6 +111,8 @@ * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */ #define NSPAWN_NOTIFY_SOCKET_PATH "/run/systemd/nspawn/notify" +#define EXIT_FORCE_RESTART 133 + typedef enum ContainerStatus { CONTAINER_TERMINATED, CONTAINER_REBOOTED @@ -4041,7 +4043,7 @@ static int run(int master, * 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) + if (r == EXIT_FORCE_RESTART) r = EXIT_FAILURE; /* replace 133 with the general failure code */ *ret = r; return 0; /* finito */ @@ -4056,8 +4058,8 @@ static int run(int master, * 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; + *ret = EXIT_FORCE_RESTART; + return 0; /* finito */ } expose_port_flush(arg_expose_ports, exposed); @@ -4315,8 +4317,8 @@ int main(int argc, char *argv[]) { finish: sd_notify(false, - "STOPPING=1\n" - "STATUS=Terminating..."); + r == 0 && ret == EXIT_FORCE_RESTART ? "STOPPING=1\nSTATUS=Restarting..." : + "STOPPING=1\nSTATUS=Terminating..."); if (pid > 0) kill(pid, SIGKILL); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 13e1f91192..e7e5c5f5a7 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -997,6 +997,7 @@ int link_load_user(Link *l) { *ntas = NULL; ResolveSupport s; + const char *p; int r; assert(l); @@ -1037,48 +1038,40 @@ int link_load_user(Link *l) { /* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */ l->dnssec_mode = dnssec_mode_from_string(dnssec); - if (servers) { - const char *p = servers; + for (p = servers;;) { + _cleanup_free_ char *word = NULL; - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, 0); - if (r < 0) - goto fail; - if (r == 0) - break; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + goto fail; + if (r == 0) + break; - r = link_update_dns_server_one(l, word); - if (r < 0) { - log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word); - continue; - } + r = link_update_dns_server_one(l, word); + if (r < 0) { + log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word); + continue; } } - if (domains) { - const char *p = domains; + for (p = domains;;) { + _cleanup_free_ char *word = NULL; + const char *n; + bool is_route; - for (;;) { - _cleanup_free_ char *word = NULL; - const char *n; - bool is_route; - - r = extract_first_word(&p, &word, NULL, 0); - if (r < 0) - goto fail; - if (r == 0) - break; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + goto fail; + if (r == 0) + break; - is_route = word[0] == '~'; - n = is_route ? word + 1 : word; + is_route = word[0] == '~'; + n = is_route ? word + 1 : word; - r = link_update_search_domain_one(l, n, is_route); - if (r < 0) { - log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word); - continue; - } + r = link_update_search_domain_one(l, n, is_route); + if (r < 0) { + log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word); + continue; } } diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 801014caf5..13f08f8a6c 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -60,7 +60,7 @@ int manager_read_resolv_conf(Manager *m) { return 0; /* Is it symlinked to our own file? */ - if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 && + if (stat(PRIVATE_RESOLV_CONF, &own) >= 0 && st.st_dev == own.st_dev && st.st_ino == own.st_ino) return 0; diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index deb75f9ae5..8d5a5c6b79 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -112,6 +112,10 @@ int main(int argc, char *argv[]) { sd_event_get_exit_code(m->event, &r); finish: + /* systemd-nspawn checks for private resov.conf to decide whether + or not to mount it into the container. So just delete it. */ + (void) unlink(PRIVATE_RESOLV_CONF); + sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down..."); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 35e2c8f18e..388b391342 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -62,6 +62,7 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { const char *eq, *field; + UnitDependency dep; int r, rl; assert(m); @@ -398,9 +399,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return bus_log_create_error(r); - p = eq; - - for (;;) { + for (p = eq;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); @@ -482,9 +481,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return bus_log_create_error(r); - p = eq; - - for (;;) { + for (p = eq;;) { _cleanup_free_ char *word = NULL; int offset; @@ -531,9 +528,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return bus_log_create_error(r); - p = eq; - - for (;;) { + for (p = eq;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); @@ -578,7 +573,9 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen flags = (~flags) & NAMESPACE_FLAGS_ALL; r = sd_bus_message_append(m, "v", "t", flags); - } else { + } else if ((dep = unit_dependency_from_string(field)) >= 0) + r = sd_bus_message_append(m, "v", "as", 1, eq); + else { log_error("Unknown assignment %s.", assignment); return -EINVAL; } @@ -844,6 +841,8 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* log_error("Assertion failed on job for %s.", strna(d->name)); else if (streq(d->result, "unsupported")) log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); + else if (streq(d->result, "collected")) + log_error("Queued job for %s was garbage collected.", strna(d->name)); else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { if (d->name) { int q; @@ -859,7 +858,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* } } - if (streq(d->result, "canceled")) + if (STR_IN_SET(d->result, "canceled", "collected")) r = -ECANCELED; else if (streq(d->result, "timeout")) r = -ETIME; diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 3b8768b9a7..6aebe18fc0 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -43,6 +43,7 @@ #include "escape.h" #include "fd-util.h" #include "missing.h" +#include "nsflags.h" #include "parse-util.h" #include "proc-cmdline.h" #include "rlimit-util.h" @@ -769,6 +770,23 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b char timespan[FORMAT_TIMESPAN_MAX]; print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0)); + } else if (streq(name, "RestrictNamespaces")) { + _cleanup_free_ char *s = NULL; + const char *result = NULL; + + if ((u & NAMESPACE_FLAGS_ALL) == 0) + result = "yes"; + else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL) + result = "no"; + else { + r = namespace_flag_to_string_many(u, &s); + if (r < 0) + return r; + + result = s; + } + + print_property(name, "%s", result); } else print_property(name, "%"PRIu64, u); @@ -1565,3 +1583,22 @@ int bus_property_get_rlimit( return sd_bus_message_append(reply, "t", u); } + +int bus_track_add_name_many(sd_bus_track *t, char **l) { + int r = 0; + char **i; + + assert(t); + + /* Continues adding after failure, and returns the first failure. */ + + STRV_FOREACH(i, l) { + int k; + + k = sd_bus_track_add_name(t, *i); + if (k < 0 && r >= 0) + r = k; + } + + return r; +} diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 934e0b5b77..af5f133912 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -159,3 +159,5 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); + +int bus_track_add_name_many(sd_bus_track *t, char **l); diff --git a/src/shared/condition.c b/src/shared/condition.c index 8bd6a51a99..525e65aedf 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -111,9 +111,8 @@ static int condition_test_kernel_command_line(Condition *c) { return r; equal = !!strchr(c->parameter, '='); - p = line; - for (;;) { + for (p = line;;) { _cleanup_free_ char *word = NULL; bool found; diff --git a/src/shared/nsflags.c b/src/shared/nsflags.c index 8fcbe97ba7..aeb79b131e 100644 --- a/src/shared/nsflags.c +++ b/src/shared/nsflags.c @@ -22,7 +22,6 @@ #include "alloc-util.h" #include "extract-word.h" #include "nsflags.h" -#include "seccomp-util.h" #include "string-util.h" const struct namespace_flag_map namespace_flag_map[] = { @@ -69,11 +68,6 @@ int namespace_flag_from_string_many(const char *name, unsigned long *ret) { assert_se(ret); - if (!name) { - *ret = 0; - return 0; - } - for (;;) { _cleanup_free_ char *word = NULL; unsigned long f; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index af5b18c0ed..4fd8d7ba27 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -173,6 +173,8 @@ static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; static bool arg_firmware_setup = false; static bool arg_now = false; +static bool arg_jobs_before = false; +static bool arg_jobs_after = false; static int daemon_reload(int argc, char *argv[], void* userdata); static int trivial_method(int argc, char *argv[], void *userdata); @@ -204,6 +206,9 @@ static int acquire_bus(BusFocus focus, sd_bus **ret) { if (arg_transport != BUS_TRANSPORT_LOCAL) focus = BUS_FULL; + if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0) + focus = BUS_FULL; + if (!busses[focus]) { bool user; @@ -2192,12 +2197,49 @@ finish: return r; } +static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, const char *prefix) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *name, *type, *state, *job_path, *unit_path; + uint32_t other_id; + int r; + + assert(bus); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + &error, + &reply, + "u", id); + if (r < 0) + return log_debug_errno(r, "Failed to get waiting jobs for job %" PRIu32, id); + + r = sd_bus_message_enter_container(reply, 'a', "(usssoo)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, &state, &job_path, &unit_path)) > 0) + printf("%s %u (%s/%s)\n", prefix, other_id, name, type); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + return 0; +} + struct job_info { uint32_t id; const char *name, *type, *state; }; -static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipped) { +static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) { unsigned id_len, unit_len, type_len, state_len; const struct job_info *j; const char *on, *off; @@ -2259,6 +2301,11 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipp on, unit_len, e ? e : j->name, off, type_len, j->type, on, state_len, j->state, off); + + if (arg_jobs_after) + output_waiting_jobs(bus, j->id, "GetJobAfter", "\twaiting for job"); + if (arg_jobs_before) + output_waiting_jobs(bus, j->id, "GetJobBefore", "\tblocking job"); } if (!arg_no_legend) { @@ -2327,7 +2374,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) { pager_open(arg_no_pager, false); - output_jobs_list(jobs, c, skipped); + output_jobs_list(bus, jobs, c, skipped); return 0; } @@ -2432,17 +2479,24 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un assert(unit_path); STRV_FOREACH(p, lp->search_path) { - _cleanup_free_ char *path; + _cleanup_free_ char *path = NULL, *lpath = NULL; + int r; path = path_join(arg_root, *p, unit_name); if (!path) return log_oom(); - if (access(path, F_OK) == 0) { - *unit_path = path; - path = NULL; - return 1; - } + r = chase_symlinks(path, arg_root, &lpath); + if (r == -ENOENT) + continue; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to access path '%s': %m", path); + + *unit_path = lpath; + lpath = NULL; + return 1; } return 0; @@ -2509,10 +2563,6 @@ static int unit_find_paths( if (!names) return log_oom(); - r = set_put(names, unit_name); - if (r < 0) - return log_error_errno(r, "Failed to add unit name: %m"); - r = unit_file_find_path(lp, unit_name, &path); if (r < 0) return r; @@ -2530,6 +2580,10 @@ static int unit_find_paths( } } + r = set_put(names, basename(path)); + if (r < 0) + return log_error_errno(r, "Failed to add unit name: %m"); + if (dropin_paths) { r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins); if (r < 0) @@ -6735,9 +6789,9 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { if (path) { if (arg_full) - r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path); + r = unit_file_create_copy(&lp, basename(path), path, &new_path, &tmp_path); else - r = unit_file_create_new(&lp, *name, ".d/override.conf", &new_path, &tmp_path); + r = unit_file_create_new(&lp, basename(path), ".d/override.conf", &new_path, &tmp_path); } else r = unit_file_create_new(&lp, *name, NULL, &new_path, &tmp_path); if (r < 0) @@ -7214,14 +7268,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return -EINVAL; } - p = optarg; - for (;;) { + for (p = optarg;;) { _cleanup_free_ char *type = NULL; r = extract_first_word(&p, &type, ",", 0); if (r < 0) return log_error_errno(r, "Failed to parse type: %s", optarg); - if (r == 0) break; @@ -7263,15 +7315,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_properties = new0(char*, 1); if (!arg_properties) return log_oom(); - } else { - p = optarg; - for (;;) { + } else + for (p = optarg;;) { _cleanup_free_ char *prop = NULL; r = extract_first_word(&p, &prop, ",", 0); if (r < 0) return log_error_errno(r, "Failed to parse property: %s", optarg); - if (r == 0) break; @@ -7280,7 +7330,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { prop = NULL; } - } /* If the user asked for a particular * property, show it to him, even if it is @@ -7300,10 +7349,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) { case ARG_AFTER: arg_dependency = DEPENDENCY_AFTER; + arg_jobs_after = true; break; case ARG_BEFORE: arg_dependency = DEPENDENCY_BEFORE; + arg_jobs_before = true; break; case ARG_SHOW_TYPES: @@ -7457,14 +7508,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return -EINVAL; } - p = optarg; - for (;;) { + for (p = optarg;;) { _cleanup_free_ char *s = NULL; r = extract_first_word(&p, &s, ",", 0); if (r < 0) return log_error_errno(r, "Failed to parse signal: %s", optarg); - if (r == 0) break; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 9a90c2ed42..ffe7f836de 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -126,6 +126,9 @@ int sd_dhcp_client_get_client_id( int sd_dhcp_client_set_mtu( sd_dhcp_client *client, uint32_t mtu); +int sd_dhcp_client_set_client_port( + sd_dhcp_client *client, + uint16_t port); int sd_dhcp_client_set_hostname( sd_dhcp_client *client, const char *hostname); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 6029853e3e..b2ea358b8c 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -219,6 +219,18 @@ static void test_exec_systemcallerrornumber(Manager *m) { #endif } +static void test_exec_restrict_namespaces(Manager *m) { +#ifdef HAVE_SECCOMP + if (!is_seccomp_available()) + return; + + test(m, "exec-restrict-namespaces-no.service", 0, CLD_EXITED); + test(m, "exec-restrict-namespaces-yes.service", 1, CLD_EXITED); + test(m, "exec-restrict-namespaces-mnt.service", 0, CLD_EXITED); + test(m, "exec-restrict-namespaces-mnt-blacklist.service", 1, CLD_EXITED); +#endif +} + static void test_exec_systemcall_system_mode_with_user(Manager *m) { #ifdef HAVE_SECCOMP if (!is_seccomp_available()) @@ -435,6 +447,7 @@ int main(int argc, char *argv[]) { test_exec_privatenetwork, test_exec_systemcallfilter, test_exec_systemcallerrornumber, + test_exec_restrict_namespaces, test_exec_user, test_exec_group, test_exec_supplementary_groups, diff --git a/src/test/test-nss.c b/src/test/test-nss.c index c43bda5917..4ccfe75147 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -77,7 +77,8 @@ static void* open_handle(const char* dir, const char* module, int flags) { path = strjoina("libnss_", module, ".so.2"); handle = dlopen(path, flags); - assert_se(handle); + if (!handle) + log_error("Failed to load module %s: %s", module, dlerror()); return handle; } @@ -379,75 +380,158 @@ static void test_byaddr(void *handle, puts(""); } +static int make_addresses(struct local_address **addresses) { + int n; + size_t n_alloc; + _cleanup_free_ struct local_address *addrs = NULL; + + n = local_addresses(NULL, 0, AF_UNSPEC, &addrs); + if (n < 0) + log_info_errno(n, "Failed to query local addresses: %m"); + + n_alloc = n; /* we _can_ do that */ + if (!GREEDY_REALLOC(addrs, n_alloc, n + 3)) + return log_oom(); + + addrs[n++] = (struct local_address) { .family = AF_INET, + .address.in = { htobe32(0x7F000001) } }; + addrs[n++] = (struct local_address) { .family = AF_INET, + .address.in = { htobe32(0x7F000002) } }; + addrs[n++] = (struct local_address) { .family = AF_INET6, + .address.in6 = in6addr_loopback }; + return 0; +} + +static int test_one_module(const char* dir, + const char *module, + char **names, + struct local_address *addresses, + int n_addresses) { + void *handle; + char **name; + int i; + + + log_info("======== %s ========", module); + + handle = open_handle(streq(module, "dns") ? NULL : dir, + module, + RTLD_LAZY|RTLD_NODELETE); + if (!handle) + return -EINVAL; + + STRV_FOREACH(name, names) + test_byname(handle, module, *name); + + for (i = 0; i < n_addresses; i++) + test_byaddr(handle, module, + &addresses[i].address, + FAMILY_ADDRESS_SIZE(addresses[i].family), + addresses[i].family); + + log_info(" "); + dlclose(handle); + return 0; +} + +static int parse_argv(int argc, char **argv, + char ***the_modules, + char ***the_names, + struct local_address **the_addresses, int *n_addresses) { + + int r, n = 0; + _cleanup_strv_free_ char **modules = NULL, **names = NULL; + _cleanup_free_ struct local_address *addrs = NULL; + size_t n_allocated = 0; + + if (argc > 1) + modules = strv_new(argv[1], NULL); + else + modules = strv_new( #ifdef HAVE_MYHOSTNAME -# define MODULE1 "myhostname\0" -#else -# define MODULE1 + "myhostname", #endif #ifdef HAVE_RESOLVED -# define MODULE2 "resolve\0" -#else -# define MODULE2 + "resolve", #endif #ifdef HAVE_MACHINED -# define MODULE3 "mymachines\0" -#else -# define MODULE3 + "mymachines", #endif -#define MODULE4 "dns\0" + "dns", + NULL); + if (!modules) + return -ENOMEM; + + if (argc > 2) { + char **name; + int family; + union in_addr_union address; + + STRV_FOREACH(name, argv + 2) { + r = in_addr_from_string_auto(*name, &family, &address); + if (r < 0) { + /* assume this is a name */ + r = strv_extend(&names, *name); + if (r < 0) + return r; + } else { + if (!GREEDY_REALLOC0(addrs, n_allocated, n + 1)) + return -ENOMEM; + + addrs[n++] = (struct local_address) { .family = family, + .address = address }; + } + } + } else { + _cleanup_free_ char *hostname; -int main(int argc, char **argv) { - _cleanup_free_ char *dir = NULL, *hostname = NULL; - const char *module; + hostname = gethostname_malloc(); + if (!hostname) + return -ENOMEM; - const uint32_t local_address_ipv4 = htobe32(0x7F000001); - const uint32_t local_address_ipv4_2 = htobe32(0x7F000002); + names = strv_new("localhost", "gateway", "foo_no_such_host", hostname, NULL); + if (!names) + return -ENOMEM; + + n = make_addresses(&addrs); + if (n < 0) + return n; + } + + *the_modules = modules; + *the_names = names; + modules = names = NULL; + *the_addresses = addrs; + *n_addresses = n; + addrs = NULL; + return 0; +} + +int main(int argc, char **argv) { + _cleanup_free_ char *dir = NULL; + _cleanup_strv_free_ char **modules = NULL, **names = NULL; _cleanup_free_ struct local_address *addresses = NULL; int n_addresses; + char **module; + int r; log_set_max_level(LOG_INFO); log_parse_environment(); - dir = dirname_malloc(argv[0]); - assert_se(dir); - - hostname = gethostname_malloc(); - assert_se(hostname); - - n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); - if (n_addresses < 0) { - log_info_errno(n_addresses, "Failed to query local addresses: %m"); - n_addresses = 0; + r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses); + if (r < 0) { + log_error_errno(r, "Failed to parse arguments: %m"); + return EXIT_FAILURE; } - NULSTR_FOREACH(module, MODULE1 MODULE2 MODULE3 MODULE4) { - void *handle; - const char *name; - int i; - - log_info("======== %s ========", module); - - handle = open_handle(streq(module, "dns") ? NULL : dir, - module, - RTLD_LAZY|RTLD_NODELETE); - NULSTR_FOREACH(name, "localhost\0" "gateway\0" "foo_no_such_host\0") - test_byname(handle, module, name); - - test_byname(handle, module, hostname); - - test_byaddr(handle, module, &local_address_ipv4, sizeof local_address_ipv4, AF_INET); - test_byaddr(handle, module, &local_address_ipv4_2, sizeof local_address_ipv4_2, AF_INET); - test_byaddr(handle, module, &in6addr_loopback, sizeof in6addr_loopback, AF_INET6); - - for (i = 0; i < n_addresses; i++) - test_byaddr(handle, module, - &addresses[i].address, - FAMILY_ADDRESS_SIZE(addresses[i].family), - addresses[i].family); - - dlclose(handle); + dir = dirname_malloc(argv[0]); + if (!dir) + return EXIT_FAILURE; - log_info(" "); + STRV_FOREACH(module, modules) { + r = test_one_module(dir, *module, names, addresses, n_addresses); + if (r < 0) + return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index a455652a27..6a6c1577c6 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -330,11 +330,13 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { tmx.esterror = 0; log_debug(" adjust (slew): %+.3f sec", offset); } else { - tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET; + tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR; /* ADJ_NANO uses nanoseconds in the microseconds field */ tmx.time.tv_sec = (long)offset; tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; + tmx.maxerror = 0; + tmx.esterror = 0; /* the kernel expects -0.3s as {-1, 7000.000.000} */ if (tmx.time.tv_usec < 0) { diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 708a665576..d7edbb396b 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -29,6 +29,7 @@ #include "string-table.h" #include "strxcpyx.h" #include "util.h" +#include "missing.h" static const char* const duplex_table[_DUP_MAX] = { [DUP_FULL] = "full", @@ -323,3 +324,211 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { return 0; } + +static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **g) { + struct ecmd { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + } ecmd = { + .req.cmd = ETHTOOL_GLINKSETTINGS, + }; + struct ethtool_link_usettings *u; + unsigned offset; + int r; + + /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS + handshake first to agree on the length of the link mode bitmaps. If kernel doesn't + agree with user, it returns the bitmap length it is expecting from user as a negative + length (and cmd field is 0). When kernel and user agree, kernel returns valid info in + all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on + https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf + */ + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return -ENOTSUP; + + ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return -ENOTSUP; + + u = new0(struct ethtool_link_usettings , 1); + if (!u) + return -ENOMEM; + + memcpy(&u->base, &ecmd.req, sizeof(struct ethtool_link_settings)); + + offset = 0; + memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); + + *g = u; + + return 0; +} + +static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **u) { + struct ethtool_link_usettings *e; + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_GSET, + }; + int r; + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + e = new0(struct ethtool_link_usettings, 1); + if (!e) + return -ENOMEM; + + e->base.cmd = ETHTOOL_GSET; + + e->base.link_mode_masks_nwords = 1; + e->base.speed = ethtool_cmd_speed(&ecmd); + e->base.duplex = ecmd.duplex; + e->base.port = ecmd.port; + e->base.phy_address = ecmd.phy_address; + e->base.autoneg = ecmd.autoneg; + e->base.mdio_support = ecmd.mdio_support; + + e->link_modes.supported[0] = ecmd.supported; + e->link_modes.advertising[0] = ecmd.advertising; + e->link_modes.lp_advertising[0] = ecmd.lp_advertising; + + *u = e; + + return 0; +} + +static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { + struct { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + } ecmd = { + .req.cmd = ETHTOOL_SLINKSETTINGS, + }; + unsigned int offset; + int r; + + if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0) + return -EINVAL; + + offset = 0; + memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords); + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + return 0; +} + +static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_SSET, + }; + int r; + + if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0) + return -EINVAL; + + ecmd.supported = u->link_modes.supported[0]; + ecmd.advertising = u->link_modes.advertising[0]; + ecmd.lp_advertising = u->link_modes.lp_advertising[0]; + + ethtool_cmd_speed_set(&ecmd, u->base.speed); + + ecmd.duplex = u->base.duplex; + ecmd.port = u->base.port; + ecmd.phy_address = u->base.phy_address; + ecmd.autoneg = u->base.autoneg; + ecmd.mdio_support = u->base.mdio_support; + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + return 0; +} + +/* If autonegotiation is disabled, the speed and duplex represent the fixed link + * mode and are writable if the driver supports multiple link modes. If it is + * enabled then they are read-only. If the link is up they represent the negotiated + * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest + * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. + */ + +int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autonegotiation) { + _cleanup_free_ struct ethtool_link_usettings *u = NULL; + struct ifreq ifr = {}; + int r; + + if (autonegotiation != 0) { + log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable."); + return 0; + } + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = get_glinksettings(fd, &ifr, &u); + if (r < 0) { + + r = get_gset(fd, &ifr, &u); + if (r < 0) + return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname); + } + + if (speed) + u->base.speed = speed; + + if (duplex != _DUP_INVALID) + u->base.duplex = duplex; + + u->base.autoneg = autonegotiation; + + if (u->base.cmd == ETHTOOL_GLINKSETTINGS) + r = set_slinksettings(fd, &ifr, u); + else + r = set_sset(fd, &ifr, u); + + if (r < 0) + return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname); + + return r; +} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 0744164653..75d6af396b 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -20,6 +20,9 @@ ***/ #include <macro.h> +#include <linux/ethtool.h> + +#include "missing.h" /* we can't use DUPLEX_ prefix, as it * clashes with <linux/ethtool.h> */ @@ -48,12 +51,27 @@ typedef enum NetDevFeature { _NET_DEV_FEAT_INVALID = -1 } NetDevFeature; + +#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX) + +/* layout of the struct passed from/to userland */ +struct ethtool_link_usettings { + struct ethtool_link_settings base; + + struct { + uint32_t supported[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + uint32_t advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + uint32_t lp_advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + } link_modes; +}; + int ethtool_connect(int *ret); int ethtool_get_driver(int *fd, const char *ifname, char **ret); int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features); +int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autoneg); const char *duplex_to_string(Duplex d) _const_; Duplex duplex_from_string(const char *d) _pure_; diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index f8b85cbd13..78e551df22 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -34,6 +34,7 @@ Link.Alias, config_parse_ifalias, 0, Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu) Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) +Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation) Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index ece9248c2a..1dca375279 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -167,8 +167,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) { link->mac_policy = _MACPOLICY_INVALID; link->wol = _WOL_INVALID; link->duplex = _DUP_INVALID; + link->autonegotiation = -1; - memset(&link->features, -1, _NET_DEV_FEAT_MAX); + memset(&link->features, -1, sizeof(link->features)); r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", @@ -202,9 +203,9 @@ static bool enable_name_policy(void) { } int link_config_load(link_config_ctx *ctx) { - int r; _cleanup_strv_free_ char **files; char **f; + int r; link_configs_free(ctx); @@ -364,11 +365,12 @@ static int get_mac(struct udev_device *device, bool want_random, int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) { - const char *old_name; - const char *new_name = NULL; + bool respect_predictable = false; struct ether_addr generated_mac; struct ether_addr *mac = NULL; - bool respect_predictable = false; + const char *new_name = NULL; + const char *old_name; + unsigned speed; int r, ifindex; assert(ctx); @@ -380,11 +382,19 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, if (!old_name) return -EINVAL; - r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex); - if (r < 0) - log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m", - old_name, config->speed / 1024, - duplex_to_string(config->duplex)); + + speed = DIV_ROUND_UP(config->speed, 1000000); + + r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, speed, config->duplex, config->autonegotiation); + if (r < 0) { + + if (r == -EOPNOTSUPP) + r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex); + + if (r < 0) + log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m", + old_name, speed, duplex_to_string(config->duplex)); + } r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); if (r < 0) diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 91cc0357c4..a99060d943 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -69,6 +69,7 @@ struct link_config { size_t mtu; size_t speed; Duplex duplex; + int autonegotiation; WakeOnLan wol; NetDevFeature features[_NET_DEV_FEAT_MAX]; diff --git a/test/test-execute/exec-restrict-namespaces-mnt-blacklist.service b/test/test-execute/exec-restrict-namespaces-mnt-blacklist.service new file mode 100644 index 0000000000..ab909cbd94 --- /dev/null +++ b/test/test-execute/exec-restrict-namespaces-mnt-blacklist.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test RestrictNamespaces=~mnt + +[Service] +RestrictNamespaces=~mnt +ExecStart=/bin/sh -x -c 'unshare -m' +Type=oneshot diff --git a/test/test-execute/exec-restrict-namespaces-mnt.service b/test/test-execute/exec-restrict-namespaces-mnt.service new file mode 100644 index 0000000000..1aeed72717 --- /dev/null +++ b/test/test-execute/exec-restrict-namespaces-mnt.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test RestrictNamespaces=mnt + +[Service] +RestrictNamespaces=mnt +ExecStart=/bin/sh -x -c 'unshare -m' +Type=oneshot diff --git a/test/test-execute/exec-restrict-namespaces-no.service b/test/test-execute/exec-restrict-namespaces-no.service new file mode 100644 index 0000000000..33500302d2 --- /dev/null +++ b/test/test-execute/exec-restrict-namespaces-no.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test RestrictNamespaces=no + +[Service] +RestrictNamespaces=no +ExecStart=/bin/sh -x -c 'unshare -m -u -i -n -p -f' +Type=oneshot diff --git a/test/test-execute/exec-restrict-namespaces-yes.service b/test/test-execute/exec-restrict-namespaces-yes.service new file mode 100644 index 0000000000..3fe70e2bea --- /dev/null +++ b/test/test-execute/exec-restrict-namespaces-yes.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test RestrictNamespaces=yes + +[Service] +RestrictNamespaces=yes +ExecStart=/bin/sh -x -c 'unshare -m' +Type=oneshot diff --git a/test/test-functions b/test/test-functions index 2a21a64c5c..c0128b8b00 100644 --- a/test/test-functions +++ b/test/test-functions @@ -5,13 +5,14 @@ PATH=/sbin:/bin:/usr/sbin:/usr/bin export PATH LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || "$ID_LIKE" = "debian" ]] && echo yes) +LOOKS_LIKE_ARCH=$(source /etc/os-release && [[ "$ID" = "arch" ]] && echo yes) KERNEL_VER=${KERNEL_VER-$(uname -r)} KERNEL_MODS="/lib/modules/$KERNEL_VER/" QEMU_TIMEOUT="${QEMU_TIMEOUT:-infinity}" NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}" TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out FSTYPE="${FSTYPE:-ext3}" -UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-no}" +UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}" if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2 @@ -58,15 +59,43 @@ run_qemu() { && KERNEL_BIN="/boot/$MACHINE_ID/$KERNEL_VER/linux" fi + if [[ ! "$KERNEL_BIN" ]]; then + if [[ "$LOOKS_LIKE_ARCH" ]]; then + KERNEL_BIN=/boot/vmlinuz-linux + else + KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER + fi + fi + default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img default_debian_initrd=/boot/initrd.img-${KERNEL_VER} - [ "$KERNEL_BIN" ] || KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER - [ "$INITRD" ] || { [ -e "$default_fedora_initrd" ] && INITRD=$default_fedora_initrd; } - [ "$INITRD" ] || { [ "$LOOKS_LIKE_DEBIAN" ] && [ -e "$default_debian_initrd" ] && INITRD=$default_debian_initrd; } + default_arch_initrd=/boot/initramfs-linux.img + if [[ ! "$INITRD" ]]; then + if [[ -e "$default_fedora_initrd" ]]; then + INITRD="$default_fedora_initrd" + elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then + INITRD="$default_debian_initrd" + elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then + INITRD="$default_arch_initrd" + fi + fi + [ "$QEMU_SMP" ] || QEMU_SMP=1 find_qemu_bin || return 1 + local _cgroup_args + if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then + _cgroup_args="systemd.unified_cgroup_hierarchy=yes" + elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then + _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=yes" + elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then + _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=no" + elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then + dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]" + exit 1 + fi + KERNEL_APPEND="root=/dev/sda1 \ raid=noautodetect \ loglevel=2 \ @@ -74,7 +103,7 @@ init=$ROOTLIBDIR/systemd \ ro \ console=ttyS0 \ selinux=0 \ -systemd.unified_cgroup_hierarchy=$UNIFIED_CGROUP_HIERARCHY \ +$_cgroup_args \ $KERNEL_APPEND \ " @@ -118,7 +147,17 @@ run_nspawn() { _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd" fi - _nspawn_cmd="env UNIFIED_CGROUP_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd" + if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then + dwarn "nspawn doesn't support UNIFIED_CGROUP_HIERARCHY=hybrid, skipping" + exit + elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then + _nspawn_cmd="env UNIFIED_CGROUP_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd" + elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then + _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd" + else + dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]" + exit 1 + fi (set -x; $_nspawn_cmd) rc=$? diff --git a/units/sys-fs-fuse-connections.mount b/units/sys-fs-fuse-connections.mount index e940beb09f..336b5f6277 100644 --- a/units/sys-fs-fuse-connections.mount +++ b/units/sys-fs-fuse-connections.mount @@ -12,6 +12,7 @@ Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems DefaultDependencies=no ConditionPathExists=/sys/fs/fuse/connections ConditionCapability=CAP_SYS_ADMIN +ConditionVirtualization=!private-users After=systemd-modules-load.service Before=sysinit.target |