]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #9624 from poettering/service-state-flush
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 2 Aug 2018 07:50:39 +0000 (09:50 +0200)
committerGitHub <noreply@github.com>
Thu, 2 Aug 2018 07:50:39 +0000 (09:50 +0200)
flush out ExecStatus structures when a new service cycle begins

138 files changed:
.dir-locals.el
.lgtm.yml [new file with mode: 0644]
NEWS
README
TODO
doc/ENVIRONMENT.md
doc/TRANSIENT-SETTINGS.md
hwdb/60-evdev.hwdb
hwdb/60-sensor.hwdb
man/.dir-locals.el
man/coredumpctl.xml
man/meson.build
man/nss-myhostname.xml
man/nss-mymachines.xml
man/nss-resolve.xml
man/pam_systemd.xml
man/resolved.conf.xml
man/rules/meson.build
man/systemctl.xml
man/systemd-run.xml
man/systemd-system.conf.xml
man/systemd.kill.xml
man/systemd.service.xml
man/systemd.syntax.xml
man/systemd.unit.xml
meson.build
meson_options.txt
shell-completion/bash/systemd-run
shell-completion/zsh/_systemd-run
src/analyze/analyze.c
src/basic/btrfs-util.c
src/basic/cgroup-util.c
src/basic/env-util.h
src/basic/fileio.c
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/mount-util.c
src/basic/parse-util.c
src/basic/path-util.h
src/basic/sigbus.c
src/basic/util.c
src/basic/util.h
src/boot/efi/meson.build
src/core/dbus-kill.c
src/core/dbus-manager.c
src/core/dynamic-user.c
src/core/emergency-action.c
src/core/emergency-action.h
src/core/execute.c
src/core/execute.h
src/core/kill.c
src/core/kill.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/service.c
src/core/service.h
src/core/show-status.c
src/core/show-status.h
src/core/socket.c
src/core/swap.c
src/core/unit.c
src/coredump/coredumpctl.c
src/escape/escape.c
src/import/export-raw.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-device/device-internal.h
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-resolve/sd-resolve.c
src/locale/keymap-util.c
src/login/70-uaccess.rules.m4 [moved from src/login/70-uaccess.rules with 96% similarity]
src/login/logind-dbus.c
src/login/logind-user.c
src/login/meson.build
src/login/pam_systemd.c
src/machine/machine-dbus.c
src/network/netdev/vrf.c
src/network/netdev/vrf.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-manager.c
src/network/test-network-tables.c
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-cgroup.h
src/nspawn/nspawn-mount.c
src/nspawn/nspawn-mount.h
src/nspawn/nspawn.c
src/nss-myhostname/nss-myhostname.c
src/nss-mymachines/nss-mymachines.c
src/nss-resolve/nss-resolve.c
src/nss-systemd/nss-systemd.c
src/partition/growfs.c
src/resolve/meson.build
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-stream.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dnstls-gnutls.c [new file with mode: 0644]
src/resolve/resolved-dnstls-gnutls.h [new file with mode: 0644]
src/resolve/resolved-dnstls-openssl.c [new file with mode: 0644]
src/resolve/resolved-dnstls-openssl.h [new file with mode: 0644]
src/resolve/resolved-dnstls.h [new file with mode: 0644]
src/resolve/resolved-etc-hosts.c
src/resolve/resolved-etc-hosts.h
src/resolve/resolved-gperf.gperf
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved.conf.in
src/resolve/test-resolve-tables.c
src/resolve/test-resolved-etc-hosts.c [new file with mode: 0644]
src/shared/bus-unit-util.c
src/shared/meson.build
src/shared/sleep-config.c
src/systemctl/systemctl.c
src/test/meson.build
src/test/test-execute.c
src/test/test-nss.c
src/test/test-tables.c
src/timedate/timedated.c
src/udev/collect/collect.c
src/udev/udev-builtin-net_id.c
test/TEST-09-ISSUE-2691/test.sh
test/TEST-23-TYPE-EXEC/Makefile [new file with mode: 0644]
test/TEST-23-TYPE-EXEC/test.sh [new file with mode: 0755]
test/TEST-23-TYPE-EXEC/testsuite.sh [new file with mode: 0755]
test/meson.build
test/test-execute/exec-dynamicuser-fixeduser-adm.service [new file with mode: 0644]
test/test-execute/exec-dynamicuser-fixeduser-games.service [new file with mode: 0644]
tools/check-includes.pl
tools/meson-link-test.c [deleted file]
units/user@.service.in

index 5ef7e11634cd942602ba45b97e7e86a9e62d070e..e3d01b28a9790e22943f1f148efe701adc60459b 100644 (file)
@@ -9,10 +9,7 @@
 ; NOTE: If you update this file make sure to update .vimrc and .editorconfig,
 ; too.
 
-((nil . ((indent-tabs-mode . nil)
-         (tab-width . 8)
-         (fill-column . 79)))
- (c-mode . ((fill-column . 119)
+((c-mode . ((fill-column . 119)
             (c-basic-offset . 8)
             (eval . (c-set-offset 'substatement-open 0))
             (eval . (c-set-offset 'statement-case-open 0))
@@ -24,4 +21,7 @@
  (meson-mode . ((meson-indent-basic . 8)))
  (sh-mode . ((sh-basic-offset . 8)
              (sh-indentation . 8)))
- (awk-mode . ((c-basic-offset . 8))))
+ (awk-mode . ((c-basic-offset . 8)))
+ (nil . ((indent-tabs-mode . nil)
+         (tab-width . 8)
+         (fill-column . 79))) )
diff --git a/.lgtm.yml b/.lgtm.yml
new file mode 100644 (file)
index 0000000..37f9c43
--- /dev/null
+++ b/.lgtm.yml
@@ -0,0 +1,10 @@
+extraction:
+  cpp:
+    prepare:
+      packages:
+        - python3-pip
+        - python3-setuptools
+        - python3-wheel
+    after_prepare:
+      - pip3 install meson
+      - export PATH="$HOME/.local/bin/:$PATH"
diff --git a/NEWS b/NEWS
index 537c4b6131ad682fcb76bedfa6b2ce9a74b5453e..242f55af29d4c5303bb78a52274195c62800d3f4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,32 @@
 systemd System and Service Manager
 
+CHANGES WITH 240 in spe:
+
+        * A new service type has been added: Type=exec. It's very similar to
+          Type=simple and ensures the service manager will wait for both fork()
+          and execve() of the main service binary to complete before proceeding
+          with follow-up units. This is primarily useful so that the manager
+          propagates any errors in the preparation phase of service execution
+          back to the job that requested the unit to be started. For example,
+          consider a service that has ExecStart= set to a file system binary
+          that doesn't exist. With Type=simple starting the unit would
+          typically succeed instantly, as only fork() has to complete
+          successfully and execve() is not waited for, and hence its failure is
+          seen "too late". With the new Type=exec service type starting the
+          unit will fail, as the execve() will be waited for and will fail,
+          which is then propagated back to the start job.
+
+          NOTE: with the next release 241 of systemd we intend to change the
+          systemd-run tool to default to Type=exec for transient services
+          started by it. This should be mostly safe, but in specific corner
+          cases might result in problems, as the systemd-run tool will then
+          block on NSS calls (such as user name lookups due to User=) done
+          between the fork() and execve(), which under specific circumstances
+          might cause problems. It is recommended to specify "-p Type=simple"
+          explicitly in the few cases where this applies. For regular,
+          non-transient services (i.e. those defined with unit files on disk)
+          we will continue to default to Type=simple.
+
 CHANGES WITH 239:
 
         * NETWORK INTERFACE DEVICE NAMING CHANGES: systemd-udevd's "net_id"
@@ -82,7 +109,28 @@ CHANGES WITH 239:
 
         * systemd-resolved.service and systemd-networkd.service now set
           DynamicUser=yes. The users systemd-resolve and systemd-network are
-          not created by systemd-sysusers.
+          not created by systemd-sysusers anymore.
+
+          NOTE: This has a chance of breaking nss-ldap and similar NSS modules
+          that embedd a network facing module into any process using getpwuid()
+          or related call: the dynamic allocation of the user ID for
+          systemd-resolved.service means the service manager has to check NSS
+          if the user name is already taken when forking off the service. Since
+          the user in the common case won't be defined in /etc/passwd the
+          lookup is likely to trigger nss-ldap which in turn might use NSS to
+          ask systemd-resolved for hostname lookups. This will hence result in
+          a deadlock: a user name lookup in order to start
+          systemd-resolved.service will result in a host name lookup for which
+          systemd-resolved.service needs to be started already. There are
+          multiple ways to work around this problem: pre-allocate the
+          "systemd-resolve" user on such systems, so that nss-ldap won't be
+          triggered; or use a different NSS package that doesn't do networking
+          in-process but provides a local asynchronous name cache; or configure
+          the NSS package to avoid lookups for UIDs in the range `pkg-config
+          systemd --variable=dynamicuidmin` â€¦ `pkg-config systemd
+          --variable=dynamicuidmax`, so that it does not consider itself
+          authoritative for the same UID range systemd allocates dynamic users
+          from.
 
         * The systemd-resolve tool has been renamed to resolvectl (it also
           remains available under the old name, for compatibility), and its
diff --git a/README b/README
index 82f47a59ab6cfbcf40bbab58ca1e87568e3d4b04..a3cfcada778fb14ecce63e42bf1b507290635fce 100644 (file)
--- a/README
+++ b/README
@@ -154,7 +154,8 @@ REQUIREMENTS:
         libmicrohttpd (optional)
         libpython (optional)
         libidn2 or libidn (optional)
-        gnutls >= 3.1.4 (optional, >= 3.5.3 is necessary to support DNS-over-TLS)
+        gnutls >= 3.1.4 (optional, >= 3.5.3 is required to support DNS-over-TLS with gnutls)
+        openssl >= 1.1.0 (optional, required to support DNS-over-TLS with openssl)
         elfutils >= 158 (optional)
         polkit (optional)
         pkg-config
@@ -162,14 +163,14 @@ REQUIREMENTS:
         docbook-xsl (optional, required for documentation)
         xsltproc    (optional, required for documentation)
         python-lxml (optional, required to build the indices)
-        python >= 3.4, meson >= 0.44, ninja
+        python >= 3.5, meson >= 0.46, ninja
         gcc, awk, sed, grep, m4, and similar tools
 
         During runtime, you need the following additional
         dependencies:
 
         util-linux >= v2.27.1 required
-        dbus >= 1.4.0 (strictly speaking optional, but recommended)
+        dbus >= 1.9.14 (strictly speaking optional, but recommended)
                 NOTE: If using dbus < 1.9.18, you should override the default
                 policy directory (--with-dbuspolicydir=/etc/dbus-1/system.d).
         dracut (optional)
diff --git a/TODO b/TODO
index 40622aa048fba42294172a27273e7125f1cff5e4..3ca3c5ec35ab04e428c03403a5cae95de2b42d54 100644 (file)
--- a/TODO
+++ b/TODO
@@ -19,8 +19,26 @@ Janitorial Clean-ups:
 * rework mount.c and swap.c to follow proper state enumeration/deserialization
   semantics, like we do for device.c now
 
+* udev: drop "collect", it's nonsense
+
 Features:
 
+* logind: maybe watch utmp asynchronously using inotify, and populate our own
+  tracked session metadata from the fields available therein. Why bother? Right
+  now, all "ssh" sessions will be tracked without their TTY by logind (which is
+  not just unfriendly to users as this means "loginctl session-status" shows
+  less information than "who" in many cases, but also breaks the IdleAction
+  logic, as we never can detect such sessions as idle, as we have no TTY to
+  watch). ssh sets the PAM_TTY field on its PAM sessions to "ssh" rather than
+  the actual pty, because the PAM session is opened early on for new
+  connections, but the PTY only registered much later (if at all). ssh writes
+  the utmp record only after a TTY is actually registered, hence we could use
+  this data then, and use it if it is available. Using utmp for this is ugly of
+  course, and watching things asynchronously even more so, but it should be
+  good enough for the idle detection logic at least.
+
+* maybe extend .path units to expose fanotify() per-mount change events
+
 * Add a "systemctl list-units --by-slice" mode or so, which rearranges the
   output of "systemctl list-units" slightly by showing the tree structure of
   the slices, and the units attached to them.
@@ -29,6 +47,10 @@ Features:
   units, and we should only enqeueu stop jobs from a defer event that processes
   queue instead of right-away when we assume that a unit is now unneeded.
 
+* nspawn: make nspawn suitable for shell pipelines: instead of triggering a
+  hangup when input is finished, send ^D, which synthesizes an EOF. Then wait
+  for hangup or ^D before passing on the EOF.
+
 * When reloading configuration PID 1 should reset all its properties to the
   original defaults before calling parse_config()
 
@@ -55,10 +77,6 @@ Features:
   that our log messages could contain clickable links for example for unit
   files and suchlike we operate on.
 
-* introduce a new SystemCallFilters= group called "@system-service" with a
-  sensible default set for system services, then make use of them in portable
-  profiles
-
 * add support for "portablectl attach http://foobar.com/waaa.raw (i.e. importd integration)
 
 * add attach --enable and attach --now (for attach+enable+start)
index 641a03d5d722240c13872d0cae1b83d7b88dfa06..c69bf9b66467c234948f504792025ac34ec3c1b1 100644 (file)
@@ -101,3 +101,21 @@ systemd-timedated:
   NTP client services. If set, `timedatectl set-ntp on` enables and starts the
   first existing unit listed in the environment variable, and
   `timedatectl set-ntp off` disables and stops all listed units.
+
+systemd itself:
+
+* `$SYSTEMD_ACTIVATION_UNIT` â€” set for all NSS and PAM module invocations that
+  are done by the service manager on behalf of a specific unit, in child
+  processes that are later (after execve()) going to become unit
+  processes. Contains the full unit name (e.g. "foobar.service"). NSS and PAM
+  modules can use this information to determine in which context and on whose
+  behalf they are being called, which may be useful to avoid deadlocks, for
+  example to bypass IPC calls to the very service that is about to be
+  started. Note that NSS and PAM modules should be careful to only rely on this
+  data when invoked privileged, or possibly only when getppid() returns 1, as
+  setting environment variables is of course possible in any even unprivileged
+  contexts.
+
+* `$SYSTEMD_ACTIVATION_SCOPE` â€” closely related to `$SYSTEMD_ACTIVATION_UNIT`,
+  it is either set to `system` or `user` depending on whether the NSS/PAM
+  module is called by systemd in `--system` or `--user` mode.
index 2d73980f163f05d0fd23c55b22c2c5bea153ad86..bb13cfdbfe7885ca61e0f8f28c49bf8831774f17 100644 (file)
@@ -256,6 +256,7 @@ All process killing settings are available for transient units:
 âœ“ SendSIGHUP=
 âœ“ KillMode=
 âœ“ KillSignal=
+✓ FinalKillSignal=
 ```
 
 ## Service Unit Settings
index 7a9c4c6337bca993c9e8b09a6134e81b9ba835f2..bb75bfd9ad63e2ab185be81d5a6ea48e67e714a3 100644 (file)
@@ -424,7 +424,7 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*02173BG*:*pvrThinkPadEd
 #########################################
 
 # Razer Blade Stealth
-evdev:name:1A586753:00 06CB:8323 Touchpad:dmi:*svnRazer:pnBladeStealth:*
+evdev:name:1A58675*:00 06CB:8323 Touchpad:dmi:*svnRazer:pnBladeStealth:*
  EVDEV_ABS_00=::12:8
  EVDEV_ABS_01=::11:8
  EVDEV_ABS_35=::12:8
index 4b165537a7ae14757555adefd6fa27e458ef2d39..a1c015a09a2f1ab8181273be5902112e9ed5cbf6 100644 (file)
 # Match string formats:
 # sensor:modalias:<parent device modalias>:dmi:<dmi string>
 #
+# The device modalias can be seen in the `modalias` file
+# of the sensor parent, for example:
+# cat /sys/`udevadm info -q path -n /dev/iio:device0`/../modalias
+#
+# The full DMI string of the running machine can be read from
+#   /sys/class/dmi/id/modalias
+# That requires a kernel built with CONFIG_DMIID set, which is common.
+# The full DMI string is not needed here and the meaning of individual parts
+# can be seen in the source of the DMIID kernel module
+#   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/dmi-id.c
+#
 # To add local entries, create a new file
 #   /etc/udev/hwdb.d/61-sensor-local.hwdb
 # and add your rules there. To load the new rules execute (as root):
@@ -96,6 +107,9 @@ sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP500LB*
 sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP300LD*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
+sensor:modalias:acpi:BOSC0200*:dmi:*svn*ASUSTeK*:*pn*TP412UA*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
+
 #########################################
 # Axxo
 #########################################
@@ -259,6 +273,10 @@ sensor:modalias:acpi:BOSC0200:BOSC0200:dmi:*ThinkPadYoga11e3rdGen*
 sensor:modalias:acpi:BMA250E*:dmi:bvnLENOVO:*:pvrLenovoMIIX3-1030:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
+# Miix3-830
+sensor:modalias:acpi:SMO8500*:dmi:bvnLENOVO:*:pvrLenovoMIIX3-830:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
 # IdeaPad Miix 310 note this only is for BIOS version (bvr) 1HCN4?WW, which has
 # a portrait LCD panel, versions with bvr 1HCN3?WW have a landscape panel
 sensor:modalias:acpi:KIOX000A*:dmi:bvnLENOVO:bvr1HCN4?WW:*:svnLENOVO:pn80SG:*
index 1c2512052d2d3f02b3cd8ce9cd33aee62ba92cdc..6115b4e8cf9be0595c3f0a537b10c18ae65773fa 100644 (file)
@@ -1,8 +1,5 @@
 ; special .c mode with reduced indentation for man pages
-((nil . ((indent-tabs-mode . nil)
-         (tab-width . 8)
-         (fill-column . 79)))
- (c-mode . ((fill-column . 80)
+((c-mode . ((fill-column . 80)
             (c-basic-offset . 2)
             (eval . (c-set-offset 'substatement-open 0))
             (eval . (c-set-offset 'statement-case-open 0))
@@ -11,4 +8,7 @@
             (eval . (c-set-offset 'arglist-close 0))))
  (nxml-mode . ((nxml-child-indent . 2)
                (fill-column . 119)))
- (meson-mode . ((meson-indent-basic . 8))))
+ (meson-mode . ((meson-indent-basic . 8)))
+ (nil . ((indent-tabs-mode . nil)
+         (tab-width . 8)
+         (fill-column . 79))))
index caa1bb1c0f7274101e4c1aa93d2081017931e879..94d5626fb53ed355c31e4e9bb737d6866a1ff4b0 100644 (file)
       <varlistentry>
         <term><command>info</command></term>
 
-        <listitem><para>Show detailed information about core dumps
+        <listitem><para>Show detailed information about the last core dump
+        or core dumps matching specified characteristics
         captured in the journal.</para></listitem>
       </varlistentry>
 
index fe19268bcdc92229635075f853695c37c934a183..9cd93df8462408160531c9f00e89e9153e176f82 100644 (file)
@@ -47,8 +47,8 @@ foreach tuple : xsltproc.found() ? manpages : []
         manaliases = []
         htmlaliases = []
         foreach alias : aliases
-                manaliases += [alias + '.' + section]
-                htmlaliases += [alias + '.html']
+                manaliases += alias + '.' + section
+                htmlaliases += alias + '.html'
         endforeach
 
         mandirn = join_paths(get_option('mandir'), 'man' + section)
@@ -62,7 +62,7 @@ foreach tuple : xsltproc.found() ? manpages : []
                         depend_files : custom_entities_ent,
                         install : want_man,
                         install_dir : mandirn)
-                man_pages += [p1]
+                man_pages += p1
 
                 p2 = []
                 foreach htmlalias : htmlaliases
@@ -75,9 +75,9 @@ foreach tuple : xsltproc.found() ? manpages : []
                                 dst = join_paths(docdir, 'html', htmlalias)
                                 cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
                                 meson.add_install_script('sh', '-c', cmd)
-                                p2 += [link]
+                                p2 += link
                         endif
-                        html_pages += [link]
+                        html_pages += link
                 endforeach
 
                 p3 = custom_target(
@@ -89,7 +89,7 @@ foreach tuple : xsltproc.found() ? manpages : []
                         depends : p2,
                         install : want_html,
                         install_dir : join_paths(docdir, 'html'))
-                html_pages += [p3]
+                html_pages += p3
 
                 source_xml_files += files(tuple[0] + '.xml')
         else
@@ -135,7 +135,7 @@ foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directiv
                 command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
                 install : want_man and have_lxml,
                 install_dir : mandirn)
-        man_pages += [p1]
+        man_pages += p1
 
         p2 = []
         if html == 'systemd.index.html'
@@ -149,9 +149,9 @@ foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directiv
                         dst = join_paths(docdir, 'html', htmlalias)
                         cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
                         meson.add_install_script('sh', '-c', cmd)
-                        p2 += [link]
+                        p2 += link
                 endif
-                html_pages += [link]
+                html_pages += link
         endif
 
         p3 = custom_target(
@@ -163,7 +163,7 @@ foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directiv
                 depends : p2,
                 install : want_html and have_lxml,
                 install_dir : join_paths(docdir, 'html'))
-        html_pages += [p3]
+        html_pages += p3
 endforeach
 
 # cannot use run_target until https://github.com/mesonbuild/meson/issues/1644 is resolved
index e1aabacad29ff07d84ff7c3fbcc6370314c553c8..18a6f5f665a22ef863b1bd596b90f3c91128bdc1 100644 (file)
@@ -6,7 +6,7 @@
   SPDX-License-Identifier: LGPL-2.1+
 -->
 
-<refentry id="nss-myhostname" conditional='ENABLE_MYHOSTNAME'>
+<refentry id="nss-myhostname" conditional='ENABLE_NSS_MYHOSTNAME'>
 
   <refentryinfo>
     <title>nss-myhostname</title>
index 394a90566513355f895d49a46893a6a6ec26e42c..d9811b24cc5c8dab33c2636da005611ddabd32fe 100644 (file)
@@ -6,7 +6,7 @@
   SPDX-License-Identifier: LGPL-2.1+
 -->
 
-<refentry id="nss-mymachines" conditional='ENABLE_MACHINED'>
+<refentry id="nss-mymachines" conditional='ENABLE_NSS_MYMACHINES'>
 
   <refentryinfo>
     <title>nss-mymachines</title>
index b5dcbbeaca0295fc0bc8b6361aa2ef5b2313801e..d747e0b1e526a6e2aa7a20ed0c1877f26066c9f4 100644 (file)
@@ -6,7 +6,7 @@
   SPDX-License-Identifier: LGPL-2.1+
 -->
 
-<refentry id="nss-resolve" conditional='ENABLE_RESOLVE'>
+<refentry id="nss-resolve" conditional='ENABLE_NSS_RESOLVE'>
 
   <refentryinfo>
     <title>nss-resolve</title>
index 5eab995a5240148504f61b63c8bca900fb1eccff..3ce3b282bd976e9c6ee5bb84317a9cccf97ded63 100644 (file)
       <varlistentry>
         <term><option>class=</option></term>
 
-        <listitem><para>Takes a string argument which sets the session
-        class. The XDG_SESSION_CLASS environmental variable takes
-        precedence. One of
-        <literal>user</literal>,
-        <literal>greeter</literal>,
-        <literal>lock-screen</literal> or
-        <literal>background</literal>. See
-        <citerefentry><refentrytitle>sd_session_get_class</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-        for details about the session class.</para></listitem>
+        <listitem><para>Takes a string argument which sets the session class. The <varname>XDG_SESSION_CLASS</varname>
+        environment variable (see below) takes precedence. One of <literal>user</literal>, <literal>greeter</literal>,
+        <literal>lock-screen</literal> or <literal>background</literal>. See
+        <citerefentry><refentrytitle>sd_session_get_class</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+        details about the session class.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>type=</option></term>
 
-        <listitem><para>Takes a string argument which sets the session
-        type. The XDG_SESSION_TYPE environmental variable takes
-        precedence. One of
-        <literal>unspecified</literal>,
-        <literal>tty</literal>,
-        <literal>x11</literal>,
-        <literal>wayland</literal> or
-        <literal>mir</literal>. See
-        <citerefentry><refentrytitle>sd_session_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-        for details about the session type.</para></listitem>
+        <listitem><para>Takes a string argument which sets the session type. The <varname>XDG_SESSION_TYPE</varname>
+        environment variable (see below) takes precedence. One of <literal>unspecified</literal>,
+        <literal>tty</literal>, <literal>x11</literal>, <literal>wayland</literal> or <literal>mir</literal>. See
+        <citerefentry><refentrytitle>sd_session_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+        details about the session type.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>desktop=</option></term>
+
+        <listitem><para>Takes a single, short identifier string for the desktop environment. The
+        <varname>XDG_SESSION_DESKTOP</varname> environment variable (see below) takes precedence. This may be used to
+        indicate the session desktop used, where this applies and if this information is available. For example:
+        <literal>GNOME</literal>, or <literal>KDE</literal>. It is recommended to use the same identifiers and
+        capitalization as for <varname>$XDG_CURRENT_DESKTOP</varname>, as defined by the <ulink
+        url="http://standards.freedesktop.org/desktop-entry-spec/latest/">Desktop Entry
+        Specification</ulink>. (However, note that the option only takes a single item, and not a colon-separated list
+        like <varname>$XDG_CURRENT_DESKTOP</varname>.) See
+        <citerefentry><refentrytitle>sd_session_get_desktop</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+        further details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>debug<optional>=</optional></option></term>
 
-        <listitem><para>Takes an optional
-        boolean argument. If yes or without
-        the argument, the module will log
-        debugging information as it
-        operates.</para></listitem>
+        <listitem><para>Takes an optional boolean argument. If yes or without the argument, the module will log
+        debugging information as it operates.</para></listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
   <refsect1>
     <title>Environment</title>
 
-    <para>The following environment variables are set for the
-    processes of the user's session:</para>
+    <para>The following environment variables are initialized by the module and available to the processes of the
+    user's session:</para>
 
     <variablelist class='environment-variables'>
       <varlistentry>
         <term><varname>$XDG_SESSION_ID</varname></term>
 
-        <listitem><para>A session identifier, suitable to be used in
-        filenames. The string itself should be considered opaque,
-        although often it is just the audit session ID as reported by
-        <filename>/proc/self/sessionid</filename>. Each ID will be
-        assigned only once during machine uptime. It may hence be used
-        to uniquely label files or other resources of this
-        session.</para></listitem>
+        <listitem><para>A short session identifier, suitable to be used in filenames. The string itself should be
+        considered opaque, although often it is just the audit session ID as reported by
+        <filename>/proc/self/sessionid</filename>. Each ID will be assigned only once during machine uptime. It may
+        hence be used to uniquely label files or other resources of this session. Combine this ID with the boot
+        identifier, as returned by
+        <citerefentry><refentrytitle>sd_id128_get_boot</refentrytitle><manvolnum>3</manvolnum></citerefentry>, for a
+        globally unique identifier for the current session.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     </variablelist>
 
-    <para>The following environment variables are read by the module
-    and may be used by the PAM service to pass metadata to the
-    module:</para>
+    <para>The following environment variables are read by the module and may be used by the PAM service to pass
+    metadata to the module. If these variables are not set when the PAM module is invoked but can be determined
+    otherwise they are set by the module, so that these variables are initialized for the session and applications if
+    known at all.</para>
 
     <variablelist class='environment-variables'>
       <varlistentry>
         <term><varname>$XDG_SESSION_TYPE</varname></term>
 
-        <listitem><para>The session type. This may be used instead of
-        <option>session=</option> on the module parameter line, and is
-        usually preferred.</para></listitem>
+        <listitem><para>The session type. This may be used instead of <option>session=</option> on the module parameter
+        line, and is usually preferred.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>$XDG_SESSION_CLASS</varname></term>
 
-        <listitem><para>The session class. This may be used instead of
-        <option>class=</option> on the module parameter line, and is
-        usually preferred.</para></listitem>
+        <listitem><para>The session class. This may be used instead of <option>class=</option> on the module parameter
+        line, and is usually preferred.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>$XDG_SESSION_DESKTOP</varname></term>
 
-        <listitem><para>A single, short identifier string for the
-        desktop environment. This may be used to indicate the session
-        desktop used, where this applies and if this information is
-        available. For example: <literal>GNOME</literal>, or
-        <literal>KDE</literal>. It is recommended to use the same
-        identifiers and capitalization as for
-        <varname>$XDG_CURRENT_DESKTOP</varname>, as defined by the
-        <ulink
-        url="http://standards.freedesktop.org/desktop-entry-spec/latest/">Desktop
-        Entry Specification</ulink>. (However, note that
-        <varname>$XDG_SESSION_DESKTOP</varname> only takes a single
-        item, and not a colon-separated list like
-        <varname>$XDG_CURRENT_DESKTOP</varname>.) See
-        <citerefentry><refentrytitle>sd_session_get_desktop</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-        for more details.</para></listitem>
+        <listitem><para>The desktop identifier. This may be used instead of <option>desktop=</option> on the module
+        parameter line, and is usually preferred.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       </varlistentry>
     </variablelist>
 
-    <para>If not set, <command>pam_systemd</command> will determine the
-    values for <varname>$XDG_SEAT</varname> and <varname>$XDG_VTNR</varname>
-    based on the <varname>$DISPLAY</varname> variable.</para>
+    <para>If not set, <command>pam_systemd</command> will initialize
+    <varname>$XDG_SEAT</varname> and <varname>$XDG_VTNR</varname>
+    based on the <varname>$DISPLAY</varname> variable (if the latter is set).</para>
   </refsect1>
 
   <refsect1>
index dbd07e8151e2010a6184508f5d19b0fd3f547a83..8bbabff7b8662f42c7815662df7fb8de86a157ba 100644 (file)
 
       <varlistentry>
         <term><varname>Cache=</varname></term>
-        <listitem><para>Takes a boolean argument. If "yes" (the default), resolving a domain name which already got
-        queried earlier will return the previous result as long as it is still valid, and thus does not result in a new
-        network request. Be aware that turning off caching comes at a performance penalty, which is particularly
-        high when DNSSEC is used.</para>
+        <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), resolving a domain name
+        which already got queried earlier will return the previous result as long as it is still valid, and thus does
+        not result in a new network request. Be aware that turning off caching comes at a performance penalty, which
+        is particularly high when DNSSEC is used.</para>
 
         <para>Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address
         (such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem>
         in use.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ReadEtcHosts=</varname></term>
+        <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), the DNS stub resolver will read
+        <filename>/etc/hosts</filename>, and try to resolve hosts or address by using the entries in the file before
+        sending query to DNS servers.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index 35bc1743d9f1e6b3169f80838ac612fd18153413..305876f72b810eb3a8362642e9d1d936c041adb8 100644 (file)
@@ -37,9 +37,9 @@ manpages = [
  ['modules-load.d', '5', [], 'HAVE_KMOD'],
  ['networkctl', '1', [], 'ENABLE_NETWORKD'],
  ['networkd.conf', '5', ['networkd.conf.d'], 'ENABLE_NETWORKD'],
- ['nss-myhostname', '8', ['libnss_myhostname.so.2'], 'ENABLE_MYHOSTNAME'],
- ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_MACHINED'],
- ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_RESOLVE'],
+ ['nss-myhostname', '8', ['libnss_myhostname.so.2'], 'ENABLE_NSS_MYHOSTNAME'],
+ ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_NSS_MYMACHINES'],
+ ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_NSS_RESOLVE'],
  ['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'],
  ['os-release', '5', [], ''],
  ['pam_systemd', '8', [], 'HAVE_PAM'],
index 3cde402a1b6a31ae47555ee78556151c2ca58f1f..850135cbc00f1f3c6e38a95bf42451ea4aea4f96 100644 (file)
 
       <variablelist>
         <varlistentry>
-          <term><command>list-units <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+          <term><command>list-units</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
 
           <listitem>
             <para>List units that <command>systemd</command> currently has in memory. This includes units that are
@@ -698,7 +698,7 @@ To show all installed unit files use 'systemctl list-unit-files'.
         </varlistentry>
 
         <varlistentry>
-          <term><command>list-sockets <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+          <term><command>list-sockets</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
 
           <listitem>
             <para>List socket units currently in memory, ordered by listening address.  If one or more
@@ -721,7 +721,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
         </varlistentry>
 
         <varlistentry>
-          <term><command>list-timers <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+          <term><command>list-timers</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
 
           <listitem>
             <para>List timer units currently in memory, ordered by the time they elapse next. If one or more
@@ -1093,7 +1093,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
       <variablelist>
         <varlistentry>
-          <term><command>list-unit-files <optional><replaceable>PATTERN…</replaceable></optional></command></term>
+          <term><command>list-unit-files</command> <optional><replaceable>PATTERN…</replaceable></optional></term>
 
           <listitem>
             <para>List unit files installed on the system, in combination with their enablement state (as reported by
@@ -1472,7 +1472,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
       <variablelist>
         <varlistentry>
-          <term><command>list-machines <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+          <term><command>list-machines</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
 
           <listitem>
             <para>List the host and all running local containers with
@@ -1774,7 +1774,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
           </listitem>
         </varlistentry>
         <varlistentry>
-          <term><command>reboot <optional><replaceable>arg</replaceable></optional></command></term>
+          <term><command>reboot</command> <optional><replaceable>arg</replaceable></optional></term>
 
           <listitem>
             <para>Shut down and reboot the system. This is mostly equivalent to <command>systemctl start reboot.target
@@ -1814,7 +1814,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </varlistentry>
 
         <varlistentry>
-          <term><command>exit <optional><replaceable>EXIT_CODE</replaceable></optional></command></term>
+          <term><command>exit</command> <optional><replaceable>EXIT_CODE</replaceable></optional></term>
 
           <listitem>
             <para>Ask the service manager to quit. This is only supported for user service managers (i.e. in
@@ -1828,7 +1828,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </varlistentry>
 
         <varlistentry>
-          <term><command>switch-root <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></command></term>
+          <term><command>switch-root</command> <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></term>
 
           <listitem>
             <para>Switches to a different root directory and executes a new system manager process below it. This is
index 149228ee0214567ed0958bddf35cac614b2106ba..6c15d2d8375228f916ffd8cb8dcfdbf63a7b50fe 100644 (file)
     <replaceable>COMMAND</replaceable> may be omitted. In this case, <command>systemd-run</command> creates only a
     <filename>.path</filename>, <filename>.socket</filename>, or <filename>.timer</filename> unit that triggers the
     specified unit.</para>
+
+    <para>By default, services created with <command>systemd-run</command> default to the <option>simple</option> type,
+    see the description of <varname>Type=</varname> in
+    <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+    details. Note that when this type is used the service manager (and thus the <command>systemd-run</command> command)
+    considers service start-up successful as soon as the <function>fork()</function> for the main service process
+    succeeded, i.e. before the <function>execve()</function> is invoked, and thus even if the specified command cannot
+    be started. Consider using the <option>exec</option> service type (i.e. <option>--property=Type=exec</option>) to
+    ensure that <command>systemd-run</command> returns successfully only if the specified command line has been
+    successfully started.</para>
   </refsect1>
 
   <refsect1>
index 4cdd85f9fff9f251e1feabf6ccb83e6a8d380c7c..a9116ea08fd90b270b29f6b32edec4557be710bb 100644 (file)
       <varlistentry>
         <term><varname>CPUAffinity=</varname></term>
 
-        <listitem><para>Configures the initial CPU affinity for the
-        init process. Takes a list of CPU indices or ranges separated
-        by either whitespace or commas. CPU ranges are specified by
-        the lower and upper CPU indices separated by a
-        dash.</para></listitem>
+        <listitem><para>Configures the CPU affinity for the service manager as well as the default CPU affinity for all
+        forked off processes. Takes a list of CPU indices or ranges separated by either whitespace or commas. CPU
+        ranges are specified by the lower and upper CPU indices separated by a dash. Individual services may override
+        the CPU affinity for their processes with the <varname>CPUAffinity=</varname> setting in unit files, see
+        <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 2112dea31a4a4e27e1ad46f38dd8ab43d80ea824..1a429062406a84185f95a99c624025e428672e0f 100644 (file)
@@ -94,7 +94,8 @@
         enabled with <varname>SendSIGHUP=</varname>). If then, after a
         delay (configured via the <varname>TimeoutStopSec=</varname>
         option), processes still remain, the termination request is
-        repeated with the <constant>SIGKILL</constant> signal (unless
+        repeated with the <constant>SIGKILL</constant> signal or the
+        signal specified via <varname>FinalKillSignal=</varname> (unless
         this is disabled via the <varname>SendSIGKILL=</varname>
         option). See
         <citerefentry><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>
       <varlistentry>
         <term><varname>SendSIGKILL=</varname></term>
         <listitem><para>Specifies whether to send
-        <constant>SIGKILL</constant> to remaining processes after a
-        timeout, if the normal shutdown procedure left processes of
-        the service around. Takes a boolean value. Defaults to "yes".
+        <constant>SIGKILL</constant> (or the signal specified by
+        <varname>FinalKillSignal=</varname>) to remaining processes
+        after a timeout, if the normal shutdown procedure left
+        processes of the service around. Takes a boolean value.
+        Defaults to "yes".
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>FinalKillSignal=</varname></term>
+        <listitem><para>Specifies which signal to send to remaining
+        processes after a timeout if <varname>SendSIGKILL=</varname>
+        is enabled. The signal configured here should be one that is
+        not typically caught and processed by services (<constant>SIGTERM</constant>
+        is not suitable). Developers can find it useful to use this to
+        generate a coredump to troubleshoot why a service did not
+        terminate upon receiving the initial <constant>SIGTERM</constant>
+        signal. This can be achieved by configuring <varname>LimitCORE=</varname>
+        and setting <varname>FinalKillSignal=</varname> to either
+        <constant>SIGQUIT</constant> or <constant>SIGABRT</constant>
+        Defaults to <constant>SIGKILL</constant>.
         </para></listitem>
       </varlistentry>
 
index f14a0572803c6c67598fce54d454a5d4bf31b37d..0cd5385f9bdb9ba56d488604290705d5fdf9bea2 100644 (file)
       <varlistentry>
         <term><varname>Type=</varname></term>
 
-        <listitem><para>Configures the process start-up type for this
-        service unit. One of
-        <option>simple</option>,
-        <option>forking</option>,
-        <option>oneshot</option>,
-        <option>dbus</option>,
-        <option>notify</option> or
-        <option>idle</option>.</para>
-
-        <para>If set to <option>simple</option> (the default if
-        neither <varname>Type=</varname> nor
-        <varname>BusName=</varname>, but <varname>ExecStart=</varname>
-        are specified), it is expected that the process configured
-        with <varname>ExecStart=</varname> is the main process of the
-        service. In this mode, if the process offers functionality to
-        other processes on the system, its communication channels
-        should be installed before the daemon is started up (e.g.
-        sockets set up by systemd, via socket activation), as systemd
-        will immediately proceed starting follow-up units.</para>
-
-        <para>If set to <option>forking</option>, it is expected that
-        the process configured with <varname>ExecStart=</varname> will
-        call <function>fork()</function> as part of its start-up. The
-        parent process is expected to exit when start-up is complete
-        and all communication channels are set up. The child continues
-        to run as the main daemon process. This is the behavior of
-        traditional UNIX daemons. If this setting is used, it is
-        recommended to also use the <varname>PIDFile=</varname>
-        option, so that systemd can identify the main process of the
-        daemon. systemd will proceed with starting follow-up units as
-        soon as the parent process exits.</para>
-
-        <para>Behavior of <option>oneshot</option> is similar to
-        <option>simple</option>; however, it is expected that the
-        process has to exit before systemd starts follow-up units.
-        <varname>RemainAfterExit=</varname> is particularly useful for
-        this type of service. This is the implied default if neither
-        <varname>Type=</varname> nor <varname>ExecStart=</varname> are
-        specified.</para>
-
-        <para>Behavior of <option>dbus</option> is similar to
-        <option>simple</option>; however, it is expected that the
-        daemon acquires a name on the D-Bus bus, as configured by
-        <varname>BusName=</varname>. systemd will proceed with
-        starting follow-up units after the D-Bus bus name has been
-        acquired. Service units with this option configured implicitly
-        gain dependencies on the <filename>dbus.socket</filename>
-        unit. This type is the default if <varname>BusName=</varname>
-        is specified.</para>
-
-        <para>Behavior of <option>notify</option> is similar to
-        <option>simple</option>; however, it is expected that the
-        daemon sends a notification message via
-        <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-        or an equivalent call when it has finished starting up.
-        systemd will proceed with starting follow-up units after this
-        notification message has been sent. If this option is used,
-        <varname>NotifyAccess=</varname> (see below) should be set to
-        open access to the notification socket provided by systemd. If
-        <varname>NotifyAccess=</varname> is missing or set to
-        <option>none</option>, it will be forcibly set to
-        <option>main</option>. Note that currently
-        <varname>Type=</varname><option>notify</option> will not work
-        if used in combination with
-        <varname>PrivateNetwork=</varname><option>yes</option>.</para>
-
-        <para>Behavior of <option>idle</option> is very similar to <option>simple</option>; however, actual execution
-        of the service program is delayed until all active jobs are dispatched. This may be used to avoid interleaving
-        of output of shell services with the status output on the console. Note that this type is useful only to
-        improve console output, it is not useful as a general unit ordering tool, and the effect of this service type
-        is subject to a 5s time-out, after which the service program is invoked anyway.</para>
+        <listitem>
+          <para>Configures the process start-up type for this service unit. One of <option>simple</option>,
+          <option>exec</option>, <option>forking</option>, <option>oneshot</option>, <option>dbus</option>,
+          <option>notify</option> or <option>idle</option>:</para>
+
+          <itemizedlist>
+            <listitem><para>If set to <option>simple</option> (the default if <varname>ExecStart=</varname> is
+            specified but neither <varname>Type=</varname> nor <varname>BusName=</varname> are), the service manager
+            will consider the unit started immediately after the main service process has been forked off. It is
+            expected that the process configured with <varname>ExecStart=</varname> is the main process of the
+            service. In this mode, if the process offers functionality to other processes on the system, its
+            communication channels should be installed before the service is started up (e.g.  sockets set up by
+            systemd, via socket activation), as the service manager will immediately proceed starting follow-up units,
+            right after creating the main service process, and before executing the service's binary. Note that this
+            means <command>systemctl start</command> command lines for <option>simple</option> services will report
+            success even if the service's binary cannot be invoked successfully (for example because the selected
+            <varname>User=</varname> doesn't exist, or the service binary is missing).</para></listitem>
+
+            <listitem><para>The <option>exec</option> type is similar to <option>simple</option>, but the service
+            manager will consider the unit started immediately after the main service binary has been executed. The service
+            manager will delay starting of follow-up units until that point. (Or in other words:
+            <option>simple</option> proceeds with further jobs right after <function>fork()</function> returns, while
+            <option>exec</option> will not proceed before both <function>fork()</function> and
+            <function>execve()</function> in the service process succeeded.) Note that this means <command>systemctl
+            start</command> command lines for <option>exec</option> services will report failure when the service's
+            binary cannot be invoked successfully (for example because the selected <varname>User=</varname> doesn't
+            exist, or the service binary is missing).</para></listitem>
+
+            <listitem><para>If set to <option>forking</option>, it is expected that the process configured with
+            <varname>ExecStart=</varname> will call <function>fork()</function> as part of its start-up. The parent
+            process is expected to exit when start-up is complete and all communication channels are set up. The child
+            continues to run as the main service process, and the service manager will consider the unit started when
+            the parent process exits. This is the behavior of traditional UNIX services. If this setting is used, it is
+            recommended to also use the <varname>PIDFile=</varname> option, so that systemd can reliably identify the
+            main process of the service. systemd will proceed with starting follow-up units as soon as the parent
+            process exits.</para></listitem>
+
+            <listitem><para>Behavior of <option>oneshot</option> is similar to <option>simple</option>; however, the
+            service manager will consider the unit started after the main process exits. It will then start follow-up
+            units. <varname>RemainAfterExit=</varname> is particularly useful for this type of
+            service. <varname>Type=</varname><option>oneshot</option> is the implied default if neither
+            <varname>Type=</varname> nor <varname>ExecStart=</varname> are specified.</para></listitem>
+
+            <listitem><para>Behavior of <option>dbus</option> is similar to <option>simple</option>; however, it is
+            expected that the service acquires a name on the D-Bus bus, as configured by
+            <varname>BusName=</varname>. systemd will proceed with starting follow-up units after the D-Bus bus name
+            has been acquired. Service units with this option configured implicitly gain dependencies on the
+            <filename>dbus.socket</filename> unit. This type is the default if <varname>BusName=</varname> is
+            specified.</para></listitem>
+
+            <listitem><para>Behavior of <option>notify</option> is similar to <option>exec</option>; however, it is
+            expected that the service sends a notification message via
+            <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry> or an
+            equivalent call when it has finished starting up. systemd will proceed with starting follow-up units after
+            this notification message has been sent. If this option is used, <varname>NotifyAccess=</varname> (see
+            below) should be set to open access to the notification socket provided by systemd. If
+            <varname>NotifyAccess=</varname> is missing or set to <option>none</option>, it will be forcibly set to
+            <option>main</option>. Note that currently <varname>Type=</varname><option>notify</option> will not work if
+            used in combination with <varname>PrivateNetwork=</varname><option>yes</option>.</para></listitem>
+
+            <listitem><para>Behavior of <option>idle</option> is very similar to <option>simple</option>; however,
+            actual execution of the service program is delayed until all active jobs are dispatched. This may be used
+            to avoid interleaving of output of shell services with the status output on the console. Note that this
+            type is useful only to improve console output, it is not useful as a general unit ordering tool, and the
+            effect of this service type is subject to a 5s time-out, after which the service program is invoked
+            anyway.</para></listitem>
+          </itemizedlist>
+
+          <para>It is generally recommended to use <varname>Type=</varname><option>simple</option> for long-running
+          services whenever possible, as it is the simplest and fastest option. However, as this service type won't
+          propagate service start-up failures and doesn't allow ordering of other units against completion of
+          initialization of the service (which for example is useful if clients need to connect to the service through
+          some form of IPC, and the IPC channel is only established by the service itself â€” in contrast to doing this
+          ahead of time through socket or bus activation or similar), it might not be sufficient for many cases. If so,
+          <option>notify</option> or <option>dbus</option> (the latter only in case the service provides a D-Bus
+          interface) are the preferred options as they allow service program code to precisely schedule when to
+          consider the service started up successfully and when to proceed with follow-up units. The
+          <option>notify</option> service type requires explicit support in the service codebase (as
+          <function>sd_notify()</function> or an equivalent API needs to be invoked by the service at the appropriate
+          time) â€” if it's not supported, then <option>forking</option> is an alternative: it supports the traditional
+          UNIX service start-up protocol. Finally, <option>exec</option> might be an option for cases where it is
+          enough to ensure the service binary is invoked, and where the service binary itself executes no or little
+          initialization on its own (and its initialization is unlikely to fail). Note that using any type other than
+          <option>simple</option> possibly delays the boot process, as the service manager needs to wait for service
+          initialization to complete. It is hence recommended not to needlessly use any types other than
+          <option>simple</option>. (Also note it is generally not recommended to use <option>idle</option> or
+          <option>oneshot</option> for long-running services.)</para>
         </listitem>
       </varlistentry>
 
index 1bb8e6bbd4289eeac9c680c0898297be36ef2105..3c67e92dd58a1a76b9b89cd685672272f9c5e16c 100644 (file)
@@ -87,6 +87,23 @@ KeyTwo=value 2 \
        value 2 continued
 </programlisting></example>
 
+    <para>Boolean arguments used in configuration files can be written in
+    various formats. For positive settings the strings
+    <option>1</option>, <option>yes</option>, <option>true</option>
+    and <option>on</option> are equivalent. For negative settings, the
+    strings <option>0</option>, <option>no</option>,
+    <option>false</option> and <option>off</option> are
+    equivalent.</para>
+
+    <para>Time span values encoded in configuration files can be written in various formats. A stand-alone
+    number specifies a time in seconds.  If suffixed with a time unit, the unit is honored. A
+    concatenation of multiple values with units is supported, in which case the values are added
+    up. Example: <literal>50</literal> refers to 50 seconds; <literal>2min 200ms</literal> refers to
+    2 minutes and 200 milliseconds, i.e. 120200 ms.  The following time units are understood:
+    <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>,
+    <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>.  For details see
+    <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+
     <para>Various settings are allowed to be specified more than once, in which case the
     interpretation depends on the setting. Often, multiple settings form a list, and setting to an
     empty value "resets", which means that previous assignments are ignored. When this is allowed,
@@ -95,4 +112,11 @@ KeyTwo=value 2 \
     file format.</para>
   </refsect1>
 
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
 </refentry>
index f5d2f9fd7daa3fb13968149845629df7e684693d..c615739e3350e75ace1c5662abc5a2e8a1868395 100644 (file)
     do not need the prefix. Applications may use this to include
     additional information in the unit files.</para>
 
-    <para>Boolean arguments used in unit files can be written in
-    various formats. For positive settings the strings
-    <option>1</option>, <option>yes</option>, <option>true</option>
-    and <option>on</option> are equivalent. For negative settings, the
-    strings <option>0</option>, <option>no</option>,
-    <option>false</option> and <option>off</option> are
-    equivalent.</para>
-
-    <para>Time span values encoded in unit files can be written in various formats. A stand-alone
-    number specifies a time in seconds.  If suffixed with a time unit, the unit is honored. A
-    concatenation of multiple values with units is supported, in which case the values are added
-    up. Example: <literal>50</literal> refers to 50 seconds; <literal>2min 200ms</literal> refers to
-    2 minutes and 200 milliseconds, i.e. 120200 ms.  The following time units are understood:
-    <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>,
-    <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>.  For details see
-    <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
-
     <para>Units can be aliased (have an alternative name), by creating a symlink from the new name
     to the existing name in one of the unit search paths. For example,
     <filename>systemd-networkd.service</filename> has the alias
index a8433000d6fde3f03f30ef3149e95c3a846bdfa1..9bfe112d3034d80478141ed69ce9ba25c5f35c3c 100644 (file)
@@ -9,7 +9,7 @@ project('systemd', 'c',
                 'sysconfdir=/etc',
                 'localstatedir=/var',
         ],
-        meson_version : '>= 0.44',
+        meson_version : '>= 0.46',
        )
 
 libsystemd_version = '0.23.0'
@@ -362,6 +362,7 @@ if get_option('buildtype') != 'debug'
 endif
 
 add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'c')
+add_project_link_arguments(cc.get_supported_link_arguments(possible_link_flags), language : 'c')
 
 # "negative" arguments: gcc on purpose does not return an error for "-Wno-"
 # arguments, just emits a warning. So test for the "positive" version instead.
@@ -389,18 +390,6 @@ if cc.compiles('''
         add_project_arguments('-Werror=shadow', language : 'c')
 endif
 
-link_test_c = files('tools/meson-link-test.c')
-
-foreach arg : possible_link_flags
-        have = run_command(check_compilation_sh,
-                           cc.cmd_array(), '-x', 'c', arg,
-                           '-include', link_test_c).returncode() == 0
-        message('Linking with @0@ supported: @1@'.format(arg, have ? 'yes' : 'no'))
-        if have
-                add_project_link_arguments(arg, language : 'c')
-        endif
-endforeach
-
 cpp = ' '.join(cc.cmd_array()) + ' -E'
 
 #####################################################################
@@ -635,52 +624,51 @@ else
 endif
 
 time_epoch = get_option('time-epoch')
-if time_epoch == ''
+if time_epoch == -1
         NEWS = files('NEWS')
-        time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout()
+        time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout().to_int()
 endif
-time_epoch = time_epoch.to_int()
 conf.set('TIME_EPOCH', time_epoch)
 
 system_uid_max = get_option('system-uid-max')
-if system_uid_max == ''
+if system_uid_max == -1
         system_uid_max = run_command(
                 awk,
                 '/^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }',
                 '/etc/login.defs').stdout().strip()
         if system_uid_max == ''
-                system_uid_max = '999'
+                system_uid_max = 999
+        else
+                system_uid_max = system_uid_max.to_int()
         endif
 endif
-system_uid_max = system_uid_max.to_int()
 conf.set('SYSTEM_UID_MAX', system_uid_max)
 substs.set('systemuidmax', system_uid_max)
-message('maximum system UID is @0@'.format(system_uid_max))
 
 system_gid_max = get_option('system-gid-max')
-if system_gid_max == ''
+if system_gid_max == -1
         system_gid_max = run_command(
                 awk,
                 '/^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }',
                 '/etc/login.defs').stdout().strip()
         if system_gid_max == ''
-                system_gid_max = '999'
+                system_gid_max = 999
+        else
+                system_gid_max = system_gid_max.to_int()
         endif
 endif
-system_gid_max = system_gid_max.to_int()
 conf.set('SYSTEM_GID_MAX', system_gid_max)
 substs.set('systemgidmax', system_gid_max)
-message('maximum system GID is @0@'.format(system_gid_max))
 
-dynamic_uid_min = get_option('dynamic-uid-min').to_int()
-dynamic_uid_max = get_option('dynamic-uid-max').to_int()
+dynamic_uid_min = get_option('dynamic-uid-min')
+dynamic_uid_max = get_option('dynamic-uid-max')
 conf.set('DYNAMIC_UID_MIN', dynamic_uid_min)
 conf.set('DYNAMIC_UID_MAX', dynamic_uid_max)
 substs.set('dynamicuidmin', dynamic_uid_min)
 substs.set('dynamicuidmax', dynamic_uid_max)
 
-container_uid_base_min = get_option('container-uid-base-min').to_int()
-container_uid_base_max = get_option('container-uid-base-max').to_int()
+container_uid_base_min = get_option('container-uid-base-min')
+container_uid_base_max = get_option('container-uid-base-max')
 conf.set('CONTAINER_UID_BASE_MIN', container_uid_base_min)
 conf.set('CONTAINER_UID_BASE_MAX', container_uid_base_max)
 substs.set('containeruidbasemin', container_uid_base_min)
@@ -744,17 +732,15 @@ conf.set('TTY_GID', tty_gid)
 substs.set('TTY_GID', tty_gid)
 
 # Ensure provided GID argument is numeric, otherwise fallback to default assignment
-if get_option('users-gid') != ''
-        users_gid = get_option('users-gid').to_int()
-else
-        users_gid = '-'
-endif
-substs.set('USERS_GID', users_gid)
+users_gid = get_option('users-gid')
+substs.set('USERS_GID', users_gid < 0 ? '-' : users_gid)
 
 conf.set10('ENABLE_ADM_GROUP', get_option('adm-group'))
 conf.set10('ENABLE_WHEEL_GROUP', get_option('wheel-group'))
 
-substs.set('DEV_KVM_MODE', get_option('dev-kvm-mode'))
+dev_kvm_mode = get_option('dev-kvm-mode')
+substs.set('DEV_KVM_MODE', dev_kvm_mode)
+conf.set10('DEV_KVM_UACCESS', dev_kvm_mode != '0666')
 substs.set('GROUP_RENDER_MODE', get_option('group-render-mode'))
 
 kill_user_processes = get_option('default-kill-user-processes')
@@ -1027,6 +1013,18 @@ else
 endif
 conf.set10('HAVE_GNUTLS', have)
 
+want_openssl = get_option('openssl')
+if want_openssl != 'false' and not fuzzer_build
+        libopenssl = dependency('openssl',
+                                version : '>= 1.1.0',
+                                required : want_openssl == 'true')
+        have = libopenssl.found()
+else
+        have = false
+        libopenssl = []
+endif
+conf.set10('HAVE_OPENSSL', have)
+
 want_elfutils = get_option('elfutils')
 if want_elfutils != 'false' and not fuzzer_build
         libdw = dependency('libdw',
@@ -1150,15 +1148,30 @@ substs.set('DEFAULT_DNSSEC_MODE', default_dnssec)
 
 dns_over_tls = get_option('dns-over-tls')
 if dns_over_tls != 'false'
-        have = (conf.get('HAVE_GNUTLS') == 1 and
-                libgnutls.version().version_compare('>=3.5.3'))
-        if dns_over_tls == 'true' and not have
-                error('DNS-over-TLS support was requested, but dependencies are not available')
+        if dns_over_tls == 'openssl'
+                have_gnutls = false
+        else
+                have_gnutls = (conf.get('HAVE_GNUTLS') == 1 and libgnutls.version().version_compare('>= 3.5.3'))
+                if dns_over_tls == 'gnutls' and not have_gnutls
+                        error('DNS-over-TLS support was requested with gnutls, but dependencies are not available')
+                endif
         endif
+        if dns_over_tls == 'gnutls' or have_gnutls
+                have_openssl = false
+        else
+                have_openssl = conf.get('HAVE_OPENSSL') == 1
+                if dns_over_tls != 'auto' and not have_openssl
+                        str = dns_over_tls == 'openssl' ? ' with openssl' : ''
+                        error('DNS-over-TLS support was requested$0$, but dependencies are not available'.format(str))
+                endif
+        endif
+        have = have_gnutls or have_openssl
 else
-        have = false
+        have = have_gnutls = have_openssl = false
 endif
 conf.set10('ENABLE_DNS_OVER_TLS', have)
+conf.set10('DNS_OVER_TLS_USE_GNUTLS', have_gnutls)
+conf.set10('DNS_OVER_TLS_USE_OPENSSL', have_openssl)
 
 default_dns_over_tls = get_option('default-dns-over-tls')
 if fuzzer_build
@@ -1218,7 +1231,6 @@ foreach term : ['utmp',
                 'networkd',
                 'timedated',
                 'timesyncd',
-                'myhostname',
                 'firstboot',
                 'randomseed',
                 'backlight',
@@ -1235,12 +1247,39 @@ foreach term : ['utmp',
                 'smack',
                 'gshadow',
                 'idn',
+                'nss-myhostname',
                 'nss-systemd']
         have = get_option(term)
         name = 'ENABLE_' + term.underscorify().to_upper()
         conf.set10(name, have)
 endforeach
 
+foreach tuple : [['nss-mymachines', 'machined'],
+                 ['nss-resolve',    'resolve']]
+        want = get_option(tuple[0])
+        if want != 'false'
+                have = get_option(tuple[1])
+                if want == 'true' and not have
+                        error('@0@ is requested but @1@ is disabled'.format(tuple[0], tuple[1]))
+                endif
+        else
+                have = false
+        endif
+        name = 'ENABLE_' + tuple[0].underscorify().to_upper()
+        conf.set10(name, have)
+endforeach
+
+enable_nss = false
+foreach term : ['ENABLE_NSS_MYHOSTNAME',
+                'ENABLE_NSS_MYMACHINES',
+                'ENABLE_NSS_RESOLVE',
+                'ENABLE_NSS_SYSTEMD']
+        if conf.get(term) == 1
+                enable_nss = true
+        endif
+endforeach
+conf.set10('ENABLE_NSS', enable_nss)
+
 conf.set10('ENABLE_TIMEDATECTL', get_option('timedated') or get_option('timesyncd'))
 
 want_tests = get_option('tests')
@@ -1276,7 +1315,7 @@ if get_option('efi')
         have = true
         conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME)
 
-        conf.set('SD_TPM_PCR', get_option('tpm-pcrindex').to_int())
+        conf.set('SD_TPM_PCR', get_option('tpm-pcrindex'))
 else
         have = false
 endif
@@ -1419,10 +1458,10 @@ test_dlopen = executable(
         link_with : [libbasic],
         dependencies : [libdl])
 
-foreach tuple : [['myhostname', 'ENABLE_MYHOSTNAME'],
+foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
                  ['systemd',    'ENABLE_NSS_SYSTEMD'],
-                 ['mymachines', 'ENABLE_MACHINED'],
-                 ['resolve',    'ENABLE_RESOLVE']]
+                 ['mymachines', 'ENABLE_NSS_MYMACHINES'],
+                 ['resolve',    'ENABLE_NSS_RESOLVE']]
 
         condition = tuple[1] == '' or conf.get(tuple[1]) == 1
         if condition
@@ -1495,7 +1534,7 @@ exe = executable('systemd-analyze',
                                  libblkid],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-journald',
            systemd_journald_sources,
@@ -1518,7 +1557,7 @@ exe = executable('systemd-cat',
                  dependencies : [threads],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('journalctl',
                  journalctl_sources,
@@ -1532,7 +1571,7 @@ exe = executable('journalctl',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-getty-generator',
            'src/getty-generator/getty-generator.c',
@@ -1609,7 +1648,7 @@ if conf.get('HAVE_BLKID') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootlibexecdir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_RESOLVE') == 1
@@ -1636,7 +1675,7 @@ if conf.get('ENABLE_RESOLVE') == 1
                                          libidn],
                          install_rpath : rootlibexecdir,
                          install : true)
-        public_programs += [exe]
+        public_programs += exe
 
         meson.add_install_script(meson_make_symlink,
                          join_paths(bindir, 'resolvectl'),
@@ -1669,7 +1708,7 @@ if conf.get('ENABLE_LOGIND') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 
         exe = executable('systemd-inhibit',
                          'src/login/inhibit.c',
@@ -1678,7 +1717,7 @@ if conf.get('ENABLE_LOGIND') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 
         if conf.get('HAVE_PAM') == 1
                 version_script_arg = join_paths(meson.current_source_dir(), pam_systemd_sym)
@@ -1730,7 +1769,7 @@ if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_BLKID') == 1
                          dependencies : [libblkid],
                          install_rpath : rootlibexecdir,
                          install : true)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 exe = executable('systemd-socket-activate', 'src/activate/activate.c',
@@ -1739,7 +1778,7 @@ exe = executable('systemd-socket-activate', 'src/activate/activate.c',
                  dependencies : [threads],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 
 if get_option('link-systemctl-shared')
@@ -1762,7 +1801,7 @@ exe = executable('systemctl', 'src/systemctl/systemctl.c',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 if conf.get('ENABLE_PORTABLED') == 1
         executable('systemd-portabled',
@@ -1781,7 +1820,7 @@ if conf.get('ENABLE_PORTABLED') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootlibexecdir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
@@ -1889,7 +1928,7 @@ if conf.get('ENABLE_HOSTNAMED') == 1
                          link_with : [libshared],
                          install_rpath : rootlibexecdir,
                          install : true)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_LOCALED') == 1
@@ -1915,7 +1954,7 @@ if conf.get('ENABLE_LOCALED') == 1
                          link_with : [libshared],
                          install_rpath : rootlibexecdir,
                          install : true)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_TIMEDATED') == 1
@@ -1936,7 +1975,7 @@ if conf.get('ENABLE_TIMEDATECTL') == 1
                          link_with : [libshared],
                          dependencies : [libm],
                          install : true)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_TIMESYNCD') == 1
@@ -1979,7 +2018,7 @@ if conf.get('ENABLE_MACHINED') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_IMPORTD') == 1
@@ -2044,7 +2083,7 @@ if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootlibexecdir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1
@@ -2100,7 +2139,7 @@ if conf.get('ENABLE_COREDUMP') == 1
                                          liblz4],
                          install_rpath : rootlibexecdir,
                          install : true)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_BINFMT') == 1
@@ -2111,7 +2150,7 @@ if conf.get('ENABLE_BINFMT') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootlibexecdir)
-        public_programs += [exe]
+        public_programs += exe
 
         meson.add_install_script('sh', '-c',
                                  mkdir_p.format(binfmtdir))
@@ -2210,7 +2249,7 @@ exe = executable('systemd-sysctl',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootlibexecdir)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-ac-power',
            'src/ac-power/ac-power.c',
@@ -2226,7 +2265,7 @@ exe = executable('systemd-detect-virt',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-delta',
                  'src/delta/delta.c',
@@ -2234,7 +2273,7 @@ exe = executable('systemd-delta',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-escape',
                  'src/escape/escape.c',
@@ -2243,7 +2282,7 @@ exe = executable('systemd-escape',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-notify',
                  'src/notify/notify.c',
@@ -2252,7 +2291,7 @@ exe = executable('systemd-notify',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-volatile-root',
            'src/volatile-root/volatile-root.c',
@@ -2276,7 +2315,7 @@ exe = executable('systemd-path',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-ask-password',
                  'src/ask-password/ask-password.c',
@@ -2285,7 +2324,7 @@ exe = executable('systemd-ask-password',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-reply-password',
            'src/reply-password/reply-password.c',
@@ -2302,7 +2341,7 @@ exe = executable('systemd-tty-ask-password-agent',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-cgls',
                  'src/cgls/cgls.c',
@@ -2310,7 +2349,7 @@ exe = executable('systemd-cgls',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-cgtop',
                  'src/cgtop/cgtop.c',
@@ -2318,7 +2357,7 @@ exe = executable('systemd-cgtop',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-initctl',
            'src/initctl/initctl.c',
@@ -2334,7 +2373,7 @@ exe = executable('systemd-mount',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 meson.add_install_script(meson_make_symlink,
                          'systemd-mount', join_paths(bindir, 'systemd-umount'))
@@ -2345,7 +2384,7 @@ exe = executable('systemd-run',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-stdio-bridge',
                  'src/stdio-bridge/stdio-bridge.c',
@@ -2353,7 +2392,7 @@ exe = executable('systemd-stdio-bridge',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('busctl',
                  'src/busctl/busctl.c',
@@ -2363,7 +2402,7 @@ exe = executable('busctl',
                  link_with : [libshared],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 if conf.get('ENABLE_SYSUSERS') == 1
         exe = executable('systemd-sysusers',
@@ -2373,7 +2412,7 @@ if conf.get('ENABLE_SYSUSERS') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_TMPFILES') == 1
@@ -2385,7 +2424,7 @@ if conf.get('ENABLE_TMPFILES') == 1
                          install_rpath : rootlibexecdir,
                          install : true,
                          install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 
         test('test-systemd-tmpfiles',
              test_systemd_tmpfiles_py,
@@ -2402,7 +2441,7 @@ if conf.get('ENABLE_HWDB') == 1
                          install_rpath : udev_rpath,
                          install : true,
                          install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 if conf.get('ENABLE_QUOTACHECK') == 1
@@ -2423,7 +2462,7 @@ exe = executable('systemd-socket-proxyd',
                  install_rpath : rootlibexecdir,
                  install : true,
                  install_dir : rootlibexecdir)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('systemd-udevd',
                  systemd_udevd_sources,
@@ -2440,7 +2479,7 @@ exe = executable('systemd-udevd',
                  install_rpath : udev_rpath,
                  install : true,
                  install_dir : rootlibexecdir)
-public_programs += [exe]
+public_programs += exe
 
 exe = executable('udevadm',
                  udevadm_sources,
@@ -2457,7 +2496,7 @@ exe = executable('udevadm',
                  install_rpath : udev_rpath,
                  install : true,
                  install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
 
 executable('systemd-shutdown',
            systemd_shutdown_sources,
@@ -2513,7 +2552,7 @@ exe = executable('systemd-nspawn',
                  dependencies : [libblkid],
                  install_rpath : rootlibexecdir,
                  install : true)
-public_programs += [exe]
+public_programs += exe
 
 if conf.get('ENABLE_NETWORKD') == 1
         executable('systemd-networkd',
@@ -2545,7 +2584,7 @@ if conf.get('ENABLE_NETWORKD') == 1
                    install_rpath : rootlibexecdir,
                    install : true,
                    install_dir : rootbindir)
-        public_programs += [exe]
+        public_programs += exe
 endif
 
 executable('systemd-sulogin-shell',
@@ -2745,13 +2784,7 @@ foreach tuple : sanitizers
         sanitizer = tuple[0]
         build = tuple[1]
 
-        have = run_command(check_compilation_sh,
-                           cc.cmd_array(), '-x', 'c',
-                           '-fsanitize=@0@'.format(sanitizer),
-                           '-include', link_test_c).returncode() == 0
-        message('@0@ sanitizer supported: @1@'.format(sanitizer, have ? 'yes' : 'no'))
-
-        if have
+        if cc.has_link_argument('-fsanitize=@0@'.format(sanitizer))
                 prev = ''
                 foreach p : fuzz_regression_tests
                         b = p.split('/')[-2]
@@ -2872,7 +2905,7 @@ status = [
         'debug shell:                       @0@ @ @1@'.format(get_option('debug-shell'),
                                                               get_option('debug-tty')),
         'TTY GID:                           @0@'.format(tty_gid),
-        'users GID:                         @0@'.format(users_gid),
+        'users GID:                         @0@'.format(substs.get('USERS_GID')),
         'maximum system UID:                @0@'.format(system_uid_max),
         'maximum system GID:                @0@'.format(system_gid_max),
         'minimum dynamic UID:               @0@'.format(dynamic_uid_min),
@@ -2910,8 +2943,7 @@ status += [
 # LDFLAGS:  ${OUR_LDFLAGS} ${LDFLAGS}
 
 if conf.get('ENABLE_EFI') == 1
-        status += [
-                'efi arch:                          @0@'.format(efi_arch)]
+        status += 'efi arch:                          @0@'.format(efi_arch)
 
         if have_gnu_efi
                 status += [
@@ -2944,11 +2976,11 @@ foreach tuple : [
         ['qrencode'],
         ['microhttpd'],
         ['gnutls'],
+        ['openssl'],
         ['libcurl'],
         ['idn'],
         ['libidn2'],
         ['libidn'],
-        ['nss-systemd'],
         ['libiptc'],
         ['elfutils'],
         ['binfmt'],
@@ -2971,7 +3003,8 @@ foreach tuple : [
         ['localed'],
         ['networkd'],
         ['resolve'],
-        ['DNS-over-TLS'],
+        ['DNS-over-TLS(gnutls)',  conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1],
+        ['DNS-over-TLS(openssl)', conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1],
         ['coredump'],
         ['polkit'],
         ['legacy pkla',      install_polkit_pkla],
@@ -2983,7 +3016,10 @@ foreach tuple : [
         ['blkid'],
         ['dbus'],
         ['glib'],
-        ['nss-myhostname',   conf.get('ENABLE_MYHOSTNAME') == 1],
+        ['nss-myhostname',   conf.get('ENABLE_NSS_MYHOSTNAME') == 1],
+        ['nss-mymachines',   conf.get('ENABLE_NSS_MYMACHINES') == 1],
+        ['nss-resolve',      conf.get('ENABLE_NSS_RESOLVE') == 1],
+        ['nss-systemd',      conf.get('ENABLE_NSS_SYSTEMD') == 1],
         ['hwdb'],
         ['tpm'],
         ['man pages',        want_man],
@@ -3009,9 +3045,9 @@ foreach tuple : [
                 cond = conf.get(ident1, 0) == 1 or conf.get(ident2, 0) == 1
         endif
         if cond
-                found += [tuple[0]]
+                found += tuple[0]
         else
-                missing += [tuple[0]]
+                missing += tuple[0]
         endif
 endforeach
 
index 0b531d96caf8162603a0eb2b1da89afd5aa33c9d..a927473b9b235da049ff06fc119cd904c2f398d6 100644 (file)
@@ -88,8 +88,14 @@ option('timesyncd', type : 'boolean',
        description : 'install the systemd-timesyncd daemon')
 option('remote', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'support for "journal over the network"')
-option('myhostname', type : 'boolean',
-       description : 'nss-myhostname support')
+option('nss-myhostname', type : 'boolean',
+       description : 'install nss-myhostname module')
+option('nss-mymachines', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'install nss-mymachines module')
+option('nss-resolve', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'install nss-resolve module')
+option('nss-systemd', type : 'boolean',
+       description : 'install nss-systemd module')
 option('firstboot', type : 'boolean',
        description : 'support for firstboot mechanism')
 option('randomseed', type : 'boolean',
@@ -144,28 +150,23 @@ option('compat-gateway-hostname', type : 'boolean', value : 'false',
 option('default-hierarchy', type : 'combo',
        choices : ['legacy', 'hybrid', 'unified'], value : 'hybrid',
        description : 'default cgroup hierarchy')
-option('time-epoch', type : 'string',
+option('time-epoch', type : 'integer', value : '-1',
        description : 'time epoch for time clients')
-option('system-uid-max', type : 'string',
+option('system-uid-max', type : 'integer', value : '-1',
        description : 'maximum system UID')
-option('system-gid-max', type : 'string',
+option('system-gid-max', type : 'integer', value : '-1',
        description : 'maximum system GID')
-option('dynamic-uid-min', type : 'string',
-       description : 'minimum dynamic UID',
-       value : '61184') # That's â†’ 0x0000EF00 in hex
-option('dynamic-uid-max', type : 'string',
-       description : 'maximum dynamic UID',
-       value : '65519') # That's â†’ 0x0000FFEF in hex
-option('container-uid-base-min', type : 'string',
-       description : 'minimum container UID base',
-       value : '524288') # That's â†’ 0x00080000 in hex
-option('container-uid-base-max', type : 'string',
-       description : 'maximum container UID base',
-       value : '1878982656') # That's â†’ 0x6FFF0000 in hex
-option('tty-gid', type : 'string',
-       description : 'the numeric GID of the "tty" group',
-       value : '5')
-option('users-gid', type : 'string',
+option('dynamic-uid-min', type : 'integer', value : 0x0000EF00,
+       description : 'minimum dynamic UID')
+option('dynamic-uid-max', type : 'integer', value : 0x0000FFEF,
+       description : 'maximum dynamic UID')
+option('container-uid-base-min', type : 'integer', value : 0x00080000,
+       description : 'minimum container UID base')
+option('container-uid-base-max', type : 'integer', value : 0x6FFF0000,
+       description : 'maximum container UID base')
+option('tty-gid', type : 'integer', value : 5,
+       description : 'the numeric GID of the "tty" group')
+option('users-gid', type : 'integer', value : '-1',
        description : 'the numeric GID of the "users" group')
 option('adm-group', type : 'boolean',
        description : 'the ACL for adm group should be added')
@@ -194,7 +195,7 @@ option('default-dns-over-tls', type : 'combo',
        description : 'default DNS-over-TLS mode',
        choices : ['opportunistic', 'no'],
        value : 'no')
-option('dns-over-tls', type : 'combo', choices : ['auto', 'true', 'false'],
+option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
        description : 'DNS-over-TLS support')
 option('dns-servers', type : 'string',
        description : 'space-separated list of default DNS servers',
@@ -246,8 +247,6 @@ option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libidn2 support')
 option('libidn', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libidn support')
-option('nss-systemd', type : 'boolean',
-       description : 'enable nss-systemd')
 option('libiptc', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libiptc support')
 option('qrencode', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -256,6 +255,8 @@ option('gcrypt', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'gcrypt support')
 option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'gnutls support')
+option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'openssl support')
 option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'elfutils support')
 option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -287,7 +288,7 @@ option('efi-ldsdir', type : 'string',
        description : 'path to the EFI lds directory')
 option('efi-includedir', type : 'string', value : '/usr/include/efi',
        description : 'path to the EFI header directory')
-option('tpm-pcrindex', type : 'string', value : '8',
+option('tpm-pcrindex', type : 'integer', value : 8,
        description : 'TPM PCR register number to use')
 
 option('bashcompletiondir', type : 'string',
index d317466b26f1d09b1fdb5a7386db33864bc3515c..4c60130dfa2b88e5e0c663a99ba1ac67f878c0de 100644 (file)
@@ -79,8 +79,8 @@ _systemd_run() {
                          SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
                          DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth=
                          BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
-                         KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK=
-                         LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
+                         KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
+                         LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
                          LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE=
                          LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices=
                          PrivateNetwork= NoNewPrivileges= WorkingDirectory= RootDirectory=
index 0ad4b27a6fef32cb556c91232c7142df60b7af5b..a8a8e6fe34a9ac23d0734ff04b2606d0461fa669 100644 (file)
@@ -32,8 +32,8 @@ _arguments \
                 SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
                 DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \
                 BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
-                KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= \
-                LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
+                KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
+                LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
                 LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= \
                 LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= \
                 PrivateNetwork= NoNewPrivileges= WorkingDirectory= RootDirectory= \
index ac5c05dd871ea09986eebafb0fcc87f672705615..b723ebf9bdfdbd123d771b967e0c81799c3dbd27 100644 (file)
@@ -88,6 +88,12 @@ struct boot_times {
         usec_t generators_finish_time;
         usec_t unitsload_start_time;
         usec_t unitsload_finish_time;
+        usec_t initrd_security_start_time;
+        usec_t initrd_security_finish_time;
+        usec_t initrd_generators_start_time;
+        usec_t initrd_generators_finish_time;
+        usec_t initrd_unitsload_start_time;
+        usec_t initrd_unitsload_finish_time;
 
         /*
          * If we're analyzing the user instance, all timestamps will be offset
@@ -289,6 +295,37 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
                                     &times.unitsload_finish_time) < 0)
                 return -EIO;
 
+        (void) bus_get_uint64_property(bus,
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "InitRDSecurityStartTimestampMonotonic",
+                                       &times.initrd_security_start_time);
+        (void) bus_get_uint64_property(bus,
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "InitRDSecurityFinishTimestampMonotonic",
+                                       &times.initrd_security_finish_time);
+        (void) bus_get_uint64_property(bus,
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "InitRDGeneratorsStartTimestampMonotonic",
+                                       &times.initrd_generators_start_time);
+        (void) bus_get_uint64_property(bus,
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "InitRDGeneratorsFinishTimestampMonotonic",
+                                       &times.initrd_generators_finish_time);
+        (void) bus_get_uint64_property(bus,
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "InitRDUnitsLoadStartTimestampMonotonic",
+                                       &times.initrd_unitsload_start_time);
+        (void) bus_get_uint64_property(bus,
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "InitRDUnitsLoadFinishTimestampMonotonic",
+                                       &times.initrd_unitsload_finish_time);
+
         if (times.finish_time <= 0) {
                 log_error("Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
                           "Please try again later.\n"
@@ -453,6 +490,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
         };
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
         _cleanup_(free_host_infop) struct host_info *host;
         int r;
 
@@ -460,7 +498,15 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
         if (!host)
                 return log_oom();
 
-        r = bus_map_all_properties(bus,
+        if (arg_scope != UNIT_FILE_SYSTEM) {
+                r = bus_connect_transport(arg_transport, arg_host, false, &system_bus);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to connect to system bus, ignoring: %m");
+                        goto manager;
+                }
+        }
+
+        r = bus_map_all_properties(system_bus ?: bus,
                                    "org.freedesktop.hostname1",
                                    "/org/freedesktop/hostname1",
                                    hostname_map,
@@ -468,9 +514,12 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
                                    &error,
                                    NULL,
                                    host);
-        if (r < 0)
-                log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
+        if (r < 0) {
+                log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s", bus_error_message(&error, r));
+                sd_bus_error_free(&error);
+        }
 
+manager:
         r = bus_map_all_properties(bus,
                                    "org.freedesktop.systemd1",
                                    "/org/freedesktop/systemd1",
@@ -543,14 +592,14 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
 
         size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
         if (t->kernel_time > 0)
-                strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
+                strpcpyf(&ptr, size, "= %s ", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
 
         if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY)
                 size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
         else if (unit_id && activated_time == 0)
                 size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
         else if (unit_id && activated_time == USEC_INFINITY)
-                size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id);
+                size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id);
         else if (!unit_id)
                 size = strpcpyf(&ptr, size, "\ncould not find default.target");
 
@@ -585,16 +634,38 @@ static void svg_graph_box(double height, double begin, double end) {
         }
 }
 
+static int plot_unit_times(struct unit_times *u, double width, int y) {
+        char ts[FORMAT_TIMESPAN_MAX];
+        bool b;
+
+        if (!u->name)
+                return 0;
+
+        svg_bar("activating",   u->activating, u->activated, y);
+        svg_bar("active",       u->activated, u->deactivating, y);
+        svg_bar("deactivating", u->deactivating, u->deactivated, y);
+
+        /* place the text on the left if we have passed the half of the svg width */
+        b = u->activating * SCALE_X < width / 2;
+        if (u->time)
+                svg_text(b, u->activating, y, "%s (%s)",
+                         u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
+        else
+                svg_text(b, u->activating, y, "%s", u->name);
+
+        return 1;
+}
+
 static int analyze_plot(int argc, char *argv[], void *userdata) {
         _cleanup_(free_host_infop) struct host_info *host = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(unit_times_freep) struct unit_times *times = NULL;
+        _cleanup_free_ char *pretty_times = NULL;
+        bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM;
         struct boot_times *boot;
+        struct unit_times *u;
         int n, m = 1, y = 0, r;
-        bool use_full_bus = true;
         double width;
-        _cleanup_free_ char *pretty_times = NULL;
-        struct unit_times *u;
 
         r = acquire_bus(&bus, &use_full_bus);
         if (r < 0)
@@ -608,7 +679,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
         if (n < 0)
                 return n;
 
-        if (use_full_bus) {
+        if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) {
                 n = acquire_host_info(bus, &host);
                 if (n < 0)
                         return n;
@@ -639,8 +710,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
         for (u = times; u->has_data; u++) {
                 double text_start, text_width;
 
-                if (u->activating < boot->userspace_time ||
-                    u->activating > boot->finish_time) {
+                if (u->activating > boot->finish_time) {
                         u->name = mfree(u->name);
                         continue;
                 }
@@ -711,7 +781,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
 
         svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
         svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
-        if (use_full_bus)
+        if (host)
                 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
                     isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
                     strempty(host->hostname),
@@ -741,9 +811,23 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
         }
         if (boot->initrd_time > 0) {
                 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
+                if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
+                        svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
+                if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time)
+                        svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y);
+                if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time)
+                        svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y);
                 svg_text(true, boot->initrd_time, y, "initrd");
                 y++;
         }
+
+        for (u = times; u->has_data; u++) {
+                if (u->activating >= boot->userspace_time)
+                        break;
+
+                y += plot_unit_times(u, width, y);
+        }
+
         svg_bar("active", boot->userspace_time, boot->finish_time, y);
         svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
         svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
@@ -751,26 +835,8 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
         svg_text(true, boot->userspace_time, y, "systemd");
         y++;
 
-        for (u = times; u->has_data; u++) {
-                char ts[FORMAT_TIMESPAN_MAX];
-                bool b;
-
-                if (!u->name)
-                        continue;
-
-                svg_bar("activating",   u->activating, u->activated, y);
-                svg_bar("active",       u->activated, u->deactivating, y);
-                svg_bar("deactivating", u->deactivating, u->deactivated, y);
-
-                /* place the text on the left if we have passed the half of the svg width */
-                b = u->activating * SCALE_X < width / 2;
-                if (u->time)
-                        svg_text(b, u->activating, y, "%s (%s)",
-                                 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
-                else
-                        svg_text(b, u->activating, y, "%s", u->name);
-                y++;
-        }
+        for (; u->has_data; u++)
+                y += plot_unit_times(u, width, y);
 
         svg("</g>\n");
 
index 6d2490f3d7af18d5635704fd7278ad88a2a0b053..efac0b94207f677ac4a4613736ead775325bacaf 100644 (file)
@@ -28,6 +28,7 @@
 #include "device-nodes.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "io-util.h"
 #include "macro.h"
 #include "missing.h"
@@ -59,23 +60,6 @@ static int validate_subvolume_name(const char *name) {
         return 0;
 }
 
-static int open_parent(const char *path, int flags) {
-        _cleanup_free_ char *parent = NULL;
-        int fd;
-
-        assert(path);
-
-        parent = dirname_malloc(path);
-        if (!parent)
-                return -ENOMEM;
-
-        fd = open(parent, flags);
-        if (fd < 0)
-                return -errno;
-
-        return fd;
-}
-
 static int extract_subvolume_name(const char *path, const char **subvolume) {
         const char *fn;
         int r;
@@ -144,7 +128,7 @@ int btrfs_subvol_make(const char *path) {
         if (r < 0)
                 return r;
 
-        fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+        fd = open_parent(path, O_CLOEXEC, 0);
         if (fd < 0)
                 return fd;
 
@@ -1283,7 +1267,7 @@ int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
         if (r < 0)
                 return r;
 
-        fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+        fd = open_parent(path, O_CLOEXEC, 0);
         if (fd < 0)
                 return fd;
 
@@ -1723,7 +1707,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
         if (r < 0)
                 return r;
 
-        new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+        new_fd = open_parent(new_path, O_CLOEXEC, 0);
         if (new_fd < 0)
                 return new_fd;
 
index 038ece4b06b1cfc7613f4513800be0b644c76f90..daa15dbfcbe1a682c105d932d6a4361880ff61d2 100644 (file)
@@ -2384,10 +2384,9 @@ int cg_kernel_controllers(Set **ret) {
 
         assert(ret);
 
-        /* Determines the full list of kernel-known controllers. Might
-         * include controllers we don't actually support, arbitrary
-         * named hierarchies and controllers that aren't currently
-         * accessible (because not mounted). */
+        /* Determines the full list of kernel-known controllers. Might include controllers we don't actually support
+         * and controllers that aren't currently accessible (because not mounted). This does not include "name="
+         * pseudo-controllers. */
 
         controllers = set_new(&string_hash_ops);
         if (!controllers)
index ef9398e618942c200faf460b010a62bf9e7a092a..174433ea91e6e13f2b5d305c9cd94c9f51ea7c6c 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 
 #include "macro.h"
+#include "string.h"
 
 bool env_name_is_valid(const char *e);
 bool env_value_is_valid(const char *e);
index 6b0bad5b714ec6bbd3eb616f5802461b0b3c6d4f..20d3f567c9d3fef8dc8f8f6e5efb57091296c354 100644 (file)
@@ -1225,9 +1225,13 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
         const char *fn;
         char *t;
 
-        assert(p);
         assert(ret);
 
+        if (isempty(p))
+                return -EINVAL;
+        if (path_equal(p, "/"))
+                return -EINVAL;
+
         /*
          * Turns this:
          *         /foo/bar/waldo
@@ -1258,9 +1262,13 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
         uint64_t u;
         unsigned i;
 
-        assert(p);
         assert(ret);
 
+        if (isempty(p))
+                return -EINVAL;
+        if (path_equal(p, "/"))
+                return -EINVAL;
+
         /*
          * Turns this:
          *         /foo/bar/waldo
@@ -1311,7 +1319,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
                 r = tmp_dir(&p);
                 if (r < 0)
                         return r;
-        }
+        } else if (isempty(p))
+                return -EINVAL;
 
         extra = strempty(extra);
 
@@ -1404,7 +1413,8 @@ int open_tmpfile_unlinkable(const char *directory, int flags) {
                 r = tmp_dir(&directory);
                 if (r < 0)
                         return r;
-        }
+        } else if (isempty(directory))
+                return -EINVAL;
 
         /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
 
@@ -1439,22 +1449,14 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
          * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
          * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
 
-        {
-                _cleanup_free_ char *dn = NULL;
-
-                dn = dirname_malloc(target);
-                if (!dn)
-                        return -ENOMEM;
-
-                fd = open(dn, O_TMPFILE|flags, 0640);
-                if (fd >= 0) {
-                        *ret_path = NULL;
-                        return fd;
-                }
-
-                log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
+        fd = open_parent(target, O_TMPFILE|flags, 0640);
+        if (fd >= 0) {
+                *ret_path = NULL;
+                return fd;
         }
 
+        log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
+
         r = tempfn_random(target, NULL, &tmp);
         if (r < 0)
                 return r;
index 3a8b32d881100a14a34e2c1f5b6bad505cbcfacf..aca9921de7efce9cee1287a7c69ac8f3cf9fc2e0 100644 (file)
@@ -1156,7 +1156,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
 }
 
 int fsync_directory_of_file(int fd) {
-        _cleanup_free_ char *path = NULL, *dn = NULL;
+        _cleanup_free_ char *path = NULL;
         _cleanup_close_ int dfd = -1;
         int r;
 
@@ -1182,16 +1182,40 @@ int fsync_directory_of_file(int fd) {
         if (!path_is_absolute(path))
                 return -EINVAL;
 
-        dn = dirname_malloc(path);
-        if (!dn)
-                return -ENOMEM;
-
-        dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+        dfd = open_parent(path, O_CLOEXEC, 0);
         if (dfd < 0)
-                return -errno;
+                return dfd;
 
         if (fsync(dfd) < 0)
                 return -errno;
 
         return 0;
 }
+
+int open_parent(const char *path, int flags, mode_t mode) {
+        _cleanup_free_ char *parent = NULL;
+        int fd;
+
+        if (isempty(path))
+                return -EINVAL;
+        if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */
+                return -EINVAL;
+
+        parent = dirname_malloc(path);
+        if (!parent)
+                return -ENOMEM;
+
+        /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
+         * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */
+
+        if ((flags & O_PATH) == O_PATH)
+                flags |= O_DIRECTORY;
+        else if ((flags & O_TMPFILE) != O_TMPFILE)
+                flags |= O_DIRECTORY|O_RDONLY;
+
+        fd = open(parent, flags, mode);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
index 28566773c6032d94acd568fbbaf5cd4d29c35f51..754163defd09219618046cba9b4e4ce687aa22eb 100644 (file)
@@ -103,3 +103,5 @@ void unlink_tempfilep(char (*p)[]);
 int unlinkat_deallocate(int fd, const char *name, int flags);
 
 int fsync_directory_of_file(int fd);
+
+int open_parent(const char *path, int flags, mode_t mode);
index aed7601d50fbd0d44f2f77cc35ec1fad81e18223..b9d3572baf5ecb42011fe1dcb78612cf2b584b0f 100644 (file)
@@ -571,3 +571,34 @@ int in_addr_prefix_from_string_auto(
         return 0;
 
 }
+
+void in_addr_data_hash_func(const void *p, struct siphash *state) {
+        const struct in_addr_data *a = p;
+
+        siphash24_compress(&a->family, sizeof(a->family), state);
+
+        if (a->family == AF_INET)
+                siphash24_compress(&a->address.in, sizeof(a->address.in), state);
+        else if (a->family == AF_INET6)
+                siphash24_compress(&a->address.in6, sizeof(a->address.in6), state);
+}
+
+int in_addr_data_compare_func(const void *a, const void *b) {
+        const struct in_addr_data *x = a, *y = b;
+
+        if (x->family != y->family)
+                return x->family - y->family;
+
+        if (x->family == AF_INET)
+                return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
+
+        if (x->family == AF_INET6)
+                return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
+
+        return trivial_compare_func(a, b);
+}
+
+const struct hash_ops in_addr_data_hash_ops = {
+        .hash = in_addr_data_hash_func,
+        .compare = in_addr_data_compare_func,
+};
index 956c00a850a25dee93c704cbb4ab7fcde78e0906..e4be30dc0d3af57517cee12e108762db56845653 100644 (file)
@@ -5,6 +5,7 @@
 #include <stddef.h>
 #include <sys/socket.h>
 
+#include "hash-funcs.h"
 #include "macro.h"
 #include "util.h"
 
@@ -53,3 +54,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
 }
 
 #define IN_ADDR_NULL ((union in_addr_union) {})
+
+void in_addr_data_hash_func(const void *p, struct siphash *state);
+int in_addr_data_compare_func(const void *a, const void *b);
+extern const struct hash_ops in_addr_data_hash_ops;
index ebe41a4c6c706c7e2951ab43a766631bbd8777ce..54d911b095d54ea9850de65b8a85ded817ef8bd2 100644 (file)
@@ -261,7 +261,7 @@ fallback_fstat:
 
 /* flags can be AT_SYMLINK_FOLLOW or 0 */
 int path_is_mount_point(const char *t, const char *root, int flags) {
-        _cleanup_free_ char *canonical = NULL, *parent = NULL;
+        _cleanup_free_ char *canonical = NULL;
         _cleanup_close_ int fd = -1;
         int r;
 
@@ -283,11 +283,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) {
                 t = canonical;
         }
 
-        parent = dirname_malloc(t);
-        if (!parent)
-                return -ENOMEM;
-
-        fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
+        fd = open_parent(t, O_PATH|O_CLOEXEC, 0);
         if (fd < 0)
                 return -errno;
 
index 6becf85878b07bec3f6f9feffb714624769d87a6..db38f91c832ad31dc68322eb42e367983d8e77aa 100644 (file)
@@ -637,6 +637,8 @@ int parse_permille_unbounded(const char *p) {
                 r = safe_atoi(n, &v);
                 if (r < 0)
                         return r;
+                if (v < 0)
+                        return -ERANGE;
         } else {
                 pc = endswith(p, "%");
                 if (!pc)
@@ -657,15 +659,14 @@ int parse_permille_unbounded(const char *p) {
                 r = safe_atoi(n, &v);
                 if (r < 0)
                         return r;
+                if (v < 0)
+                        return -ERANGE;
                 if (v > (INT_MAX - q) / 10)
                         return -ERANGE;
 
                 v = v * 10 + q;
         }
 
-        if (v < 0)
-                return -ERANGE;
-
         return v;
 }
 
index 8277c6b9165a8f830164dc37e2ed1b415a862460..49604eab800728358d3caa0437870eb3e1dbab6c 100644 (file)
@@ -58,10 +58,10 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
 /* Note: the search terminates on the first NULL item. */
 #define PATH_IN_SET(p, ...)                                     \
         ({                                                      \
-                char **s;                                       \
+                char **_s;                                      \
                 bool _found = false;                            \
-                STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__))         \
-                        if (path_equal(p, *s)) {                \
+                STRV_FOREACH(_s, STRV_MAKE(__VA_ARGS__))        \
+                        if (path_equal(p, *_s)) {               \
                                _found = true;                   \
                                break;                           \
                         }                                       \
index 70afba6bcf83d4213f67c166d632c87acf2be069..d5254eab9d3854f2eb52ccf3b8c3caf1cd9b5d66 100644 (file)
@@ -113,6 +113,10 @@ void sigbus_install(void) {
                 .sa_flags = SA_SIGINFO,
         };
 
+        /* make sure that sysconf() is not called from a signal handler because
+        * it is not guaranteed to be async-signal-safe since POSIX.1-2008 */
+        (void) page_size();
+
         n_installed++;
 
         if (n_installed == 1)
index 8f2d6061da0b2b62ad70821450ac9ca712a9afa0..f951d641d70dc9eccc785d6e15df8cb1f2824e91 100644 (file)
@@ -77,31 +77,6 @@ bool display_is_local(const char *display) {
                 display[1] <= '9';
 }
 
-int socket_from_display(const char *display, char **path) {
-        size_t k;
-        char *f, *c;
-
-        assert(display);
-        assert(path);
-
-        if (!display_is_local(display))
-                return -EINVAL;
-
-        k = strspn(display+1, "0123456789");
-
-        f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
-        if (!f)
-                return -ENOMEM;
-
-        c = stpcpy(f, "/tmp/.X11-unix/X");
-        memcpy(c, display+1, k);
-        c[k] = 0;
-
-        *path = f;
-
-        return 0;
-}
-
 bool kexec_loaded(void) {
        _cleanup_free_ char *s = NULL;
 
@@ -255,6 +230,11 @@ int container_get_leader(const char *machine, pid_t *pid) {
         assert(machine);
         assert(pid);
 
+        if (streq(machine, ".host")) {
+                *pid = 1;
+                return 0;
+        }
+
         if (!machine_name_is_valid(machine))
                 return -EINVAL;
 
index 9699d228f9c8f1147bf551cd65cec607121a5f54..42c262f5983ecc3c32132dce9af3f26bbaf923a4 100644 (file)
@@ -50,7 +50,6 @@ static inline const char* enable_disable(bool b) {
 bool plymouth_running(void);
 
 bool display_is_local(const char *display) _pure_;
-int socket_from_display(const char *display, char **path);
 
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
index 8ec1fa7be4d55713e74e33f088743ea6ede321f7..1cec5505a0534b218254a1dd27e339064783e2b8 100644 (file)
@@ -94,10 +94,6 @@ if have_gnu_efi
                 endif
         endif
 
-        message('efi-libdir: "@0@"'.format(efi_libdir))
-        message('efi-ldsdir: "@0@"'.format(efi_ldsdir))
-        message('efi-includedir: "@0@"'.format(efi_incdir))
-
         compile_args = ['-Wall',
                         '-Wextra',
                         '-std=gnu90',
@@ -151,10 +147,10 @@ if have_gnu_efi
                                                  + compile_args,
                                        depend_files : efi_headers)
                 if (common_sources + systemd_boot_sources).contains(file)
-                        systemd_boot_objects += [o_file]
+                        systemd_boot_objects += o_file
                 endif
                 if (common_sources + stub_sources).contains(file)
-                        stub_objects += [o_file]
+                        stub_objects += o_file
                 endif
         endforeach
 
index 028e7ec1c16e95cc4e7ee006dfa339e4b2293038..3e2a7694a7b053f969ddf4c177b43c89c9b11089 100644 (file)
@@ -12,6 +12,7 @@ const sd_bus_vtable bus_kill_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("KillMode", "s", property_get_kill_mode, offsetof(KillContext, kill_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("KillSignal", "i", bus_property_get_int, offsetof(KillContext, kill_signal), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("FinalKillSignal", "i", bus_property_get_int, offsetof(KillContext, final_kill_signal), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SendSIGKILL", "b", bus_property_get_bool, offsetof(KillContext, send_sigkill), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SendSIGHUP", "b", bus_property_get_bool,  offsetof(KillContext, send_sighup), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_VTABLE_END
@@ -19,6 +20,7 @@ const sd_bus_vtable bus_kill_vtable[] = {
 
 static BUS_DEFINE_SET_TRANSIENT_PARSE(kill_mode, KillMode, kill_mode_from_string);
 static BUS_DEFINE_SET_TRANSIENT_TO_STRING(kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(final_kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
 
 int bus_kill_context_set_transient_property(
                 Unit *u,
@@ -47,5 +49,8 @@ int bus_kill_context_set_transient_property(
         if (streq(name, "KillSignal"))
                 return bus_set_transient_kill_signal(u, name, &c->kill_signal, message, flags, error);
 
+        if (streq(name, "FinalKillSignal"))
+                return bus_set_transient_final_kill_signal(u, name, &c->final_kill_signal, message, flags, error);
+
         return 0;
 }
index 4ed68af1e008cf34eafea080bdf5c05ffa1849bc..c2f82e19de6d5259e073d9eb9ffdcb1c7e1efaae 100644 (file)
@@ -232,7 +232,7 @@ static int property_get_show_status(
         assert(reply);
         assert(m);
 
-        b = m->show_status > 0;
+        b = IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
         return sd_bus_message_append_basic(reply, 'b', &b);
 }
 
@@ -2418,6 +2418,12 @@ const sd_bus_vtable bus_manager_vtable[] = {
         BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_GENERATORS_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_START]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InitRDSecurityStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_SECURITY_START]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InitRDSecurityFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InitRDGeneratorsStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_START]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InitRDGeneratorsFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START]), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0),
         SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0),
         SD_BUS_PROPERTY("NNames", "u", property_get_hashmap_size, offsetof(Manager, units), 0),
index 7c5111ddf640e486939a7e21e260051dad9b5655..f380db553ebe54e64d62995ede26140e8d3e9a55 100644 (file)
@@ -525,6 +525,16 @@ static int dynamic_user_realize(
                         num = new_uid;
                         uid_lock_fd = new_uid_lock_fd;
                 }
+        } else if (is_user && !uid_is_dynamic(num)) {
+                struct passwd *p;
+
+                /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
+                errno = 0;
+                p = getpwuid(num);
+                if (!p)
+                        return errno > 0 ? -errno : -ESRCH;
+
+                gid = p->pw_gid;
         }
 
         /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
index 76e1124cff3e802ea8208ff97ab305e126c38b42..5420db8a87eb0ceac03130117c70966fbba2fadf 100644 (file)
@@ -1,7 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-  Copyright Â© 2012 Michael Olbrich
-***/
 
 #include <sys/reboot.h>
 
index 61791f176ffa5c24b634048b761a95ea87c07ae1..ea6590e78a83beda7f1c39e17f68f51a3353faee 100644 (file)
@@ -1,10 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-/***
-  Copyright Â© 2012 Michael Olbrich
-***/
-
 typedef enum EmergencyAction {
         EMERGENCY_ACTION_NONE,
         EMERGENCY_ACTION_REBOOT,
index ccfe3e097d325575ade6bd425e998537ba874167..a35dbac9ef12319a4bc3f0d20f87dd57f145896b 100644 (file)
@@ -148,11 +148,11 @@ static int shift_fds(int fds[], size_t n_fds) {
         return 0;
 }
 
-static int flags_fds(const int fds[], size_t n_storage_fds, size_t n_socket_fds, bool nonblock) {
+static int flags_fds(const int fds[], size_t n_socket_fds, size_t n_storage_fds, bool nonblock) {
         size_t i, n_fds;
         int r;
 
-        n_fds = n_storage_fds + n_socket_fds;
+        n_fds = n_socket_fds + n_storage_fds;
         if (n_fds <= 0)
                 return 0;
 
@@ -2573,6 +2573,7 @@ static int close_remaining_fds(
                 const DynamicCreds *dcreds,
                 int user_lookup_fd,
                 int socket_fd,
+                int exec_fd,
                 int *fds, size_t n_fds) {
 
         size_t n_dont_close = 0;
@@ -2589,6 +2590,8 @@ static int close_remaining_fds(
 
         if (socket_fd >= 0)
                 dont_close[n_dont_close++] = socket_fd;
+        if (exec_fd >= 0)
+                dont_close[n_dont_close++] = exec_fd;
         if (n_fds > 0) {
                 memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
                 n_dont_close += n_fds;
@@ -2724,16 +2727,17 @@ static int exec_child(
                 int socket_fd,
                 int named_iofds[3],
                 int *fds,
-                size_t n_storage_fds,
                 size_t n_socket_fds,
+                size_t n_storage_fds,
                 char **files_env,
                 int user_lookup_fd,
                 int *exit_status) {
 
         _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
-        _cleanup_free_ char *home_buffer = NULL;
+        int *fds_with_exec_fd, n_fds_with_exec_fd, r, ngids = 0, exec_fd = -1;
         _cleanup_free_ gid_t *supplementary_gids = NULL;
         const char *username = NULL, *groupname = NULL;
+        _cleanup_free_ char *home_buffer = NULL;
         const char *home = NULL, *shell = NULL;
         dev_t journal_stream_dev = 0;
         ino_t journal_stream_ino = 0;
@@ -2753,7 +2757,6 @@ static int exec_child(
 #endif
         uid_t uid = UID_INVALID;
         gid_t gid = GID_INVALID;
-        int r, ngids = 0;
         size_t n_fds;
         ExecDirectoryType dt;
         int secure_bits;
@@ -2797,8 +2800,8 @@ static int exec_child(
         /* In case anything used libc syslog(), close this here, too */
         closelog();
 
-        n_fds = n_storage_fds + n_socket_fds;
-        r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
+        n_fds = n_socket_fds + n_storage_fds;
+        r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, params->exec_fd, fds, n_fds);
         if (r < 0) {
                 *exit_status = EXIT_FDS;
                 return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m");
@@ -2834,10 +2837,22 @@ static int exec_child(
                 }
         }
 
+        /* We are about to invoke NSS and PAM modules. Let's tell them what we are doing here, maybe they care. This is
+         * used by nss-resolve to disable itself when we are about to start systemd-resolved, to avoid deadlocks. Note
+         * that these env vars do not survive the execve(), which means they really only apply to the PAM and NSS
+         * invocations themselves. Also note that while we'll only invoke NSS modules involved in user management they
+         * might internally call into other NSS modules that are involved in hostname resolution, we never know. */
+        if (setenv("SYSTEMD_ACTIVATION_UNIT", unit->id, true) != 0 ||
+            setenv("SYSTEMD_ACTIVATION_SCOPE", MANAGER_IS_SYSTEM(unit->manager) ? "system" : "user", true) != 0) {
+                *exit_status = EXIT_MEMORY;
+                return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
+        }
+
         if (context->dynamic_user && dcreds) {
                 _cleanup_strv_free_ char **suggested_paths = NULL;
 
-                /* Make sure we bypass our own NSS module for any NSS checks */
+                /* On top of that, make sure we bypass our own NSS module nss-systemd comprehensively for any NSS
+                 * checks, if DynamicUser=1 is used, as we shouldn't create a feedback loop with ourselves here.*/
                 if (putenv((char*) "SYSTEMD_NSS_DYNAMIC_BYPASS=1") != 0) {
                         *exit_status = EXIT_USER;
                         return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
@@ -3171,18 +3186,59 @@ static int exec_child(
         }
 
         /* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are
-         * more aggressive this time since socket_fd and the netns fds we don't need anymore. The custom endpoint fd
-         * was needed to upload the policy and can now be closed as well. */
-        r = close_all_fds(fds, n_fds);
+         * more aggressive this time since socket_fd and the netns fds we don't need anymore. We do keep the exec_fd
+         * however if we have it as we want to keep it open until the final execve(). */
+
+        if (params->exec_fd >= 0) {
+                exec_fd = params->exec_fd;
+
+                if (exec_fd < 3 + (int) n_fds) {
+                        int moved_fd;
+
+                        /* Let's move the exec fd far up, so that it's outside of the fd range we want to pass to the
+                         * process we are about to execute. */
+
+                        moved_fd = fcntl(exec_fd, F_DUPFD_CLOEXEC, 3 + (int) n_fds);
+                        if (moved_fd < 0) {
+                                *exit_status = EXIT_FDS;
+                                return log_unit_error_errno(unit, errno, "Couldn't move exec fd up: %m");
+                        }
+
+                        safe_close(exec_fd);
+                        exec_fd = moved_fd;
+                } else {
+                        /* This fd should be FD_CLOEXEC already, but let's make sure. */
+                        r = fd_cloexec(exec_fd, true);
+                        if (r < 0) {
+                                *exit_status = EXIT_FDS;
+                                return log_unit_error_errno(unit, r, "Failed to make exec fd FD_CLOEXEC: %m");
+                        }
+                }
+
+                fds_with_exec_fd = newa(int, n_fds + 1);
+                memcpy(fds_with_exec_fd, fds, n_fds * sizeof(int));
+                fds_with_exec_fd[n_fds] = exec_fd;
+                n_fds_with_exec_fd = n_fds + 1;
+        } else {
+                fds_with_exec_fd = fds;
+                n_fds_with_exec_fd = n_fds;
+        }
+
+        r = close_all_fds(fds_with_exec_fd, n_fds_with_exec_fd);
         if (r >= 0)
                 r = shift_fds(fds, n_fds);
         if (r >= 0)
-                r = flags_fds(fds, n_storage_fds, n_socket_fds, context->non_blocking);
+                r = flags_fds(fds, n_socket_fds, n_storage_fds, context->non_blocking);
         if (r < 0) {
                 *exit_status = EXIT_FDS;
                 return log_unit_error_errno(unit, r, "Failed to adjust passed file descriptors: %m");
         }
 
+        /* At this point, the fds we want to pass to the program are all ready and set up, with O_CLOEXEC turned off
+         * and at the right fd numbers. The are no other fds open, with one exception: the exec_fd if it is defined,
+         * and it has O_CLOEXEC set, after all we want it to be closed by the execve(), so that our parent knows we
+         * came this far. */
+
         secure_bits = context->secure_bits;
 
         if (needs_sandboxing) {
@@ -3413,10 +3469,35 @@ static int exec_child(
                                    LOG_UNIT_INVOCATION_ID(unit));
         }
 
+        if (exec_fd >= 0) {
+                uint8_t hot = 1;
+
+                /* We have finished with all our initializations. Let's now let the manager know that. From this point
+                 * on, if the manager sees POLLHUP on the exec_fd, then execve() was successful. */
+
+                if (write(exec_fd, &hot, sizeof(hot)) < 0) {
+                        *exit_status = EXIT_EXEC;
+                        return log_unit_error_errno(unit, errno, "Failed to enable exec_fd: %m");
+                }
+        }
+
         execve(command->path, final_argv, accum_env);
+        r = -errno;
 
-        if (errno == ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
-                log_struct_errno(LOG_INFO, errno,
+        if (exec_fd >= 0) {
+                uint8_t hot = 0;
+
+                /* The execve() failed. This means the exec_fd is still open. Which means we need to tell the manager
+                 * that POLLHUP on it no longer means execve() succeeded. */
+
+                if (write(exec_fd, &hot, sizeof(hot)) < 0) {
+                        *exit_status = EXIT_EXEC;
+                        return log_unit_error_errno(unit, errno, "Failed to disable exec_fd: %m");
+                }
+        }
+
+        if (r == -ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
+                log_struct_errno(LOG_INFO, r,
                                  "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
                                  LOG_UNIT_ID(unit),
                                  LOG_UNIT_INVOCATION_ID(unit),
@@ -3427,7 +3508,7 @@ static int exec_child(
         }
 
         *exit_status = EXIT_EXEC;
-        return log_unit_error_errno(unit, errno, "Failed to execute command: %m");
+        return log_unit_error_errno(unit, r, "Failed to execute command: %m");
 }
 
 static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
@@ -3452,7 +3533,7 @@ int exec_spawn(Unit *unit,
         assert(context);
         assert(ret);
         assert(params);
-        assert(params->fds || (params->n_storage_fds + params->n_socket_fds <= 0));
+        assert(params->fds || (params->n_socket_fds + params->n_storage_fds <= 0));
 
         if (context->std_input == EXEC_INPUT_SOCKET ||
             context->std_output == EXEC_OUTPUT_SOCKET ||
@@ -3472,8 +3553,8 @@ int exec_spawn(Unit *unit,
         } else {
                 socket_fd = -1;
                 fds = params->fds;
-                n_storage_fds = params->n_storage_fds;
                 n_socket_fds = params->n_socket_fds;
+                n_storage_fds = params->n_storage_fds;
         }
 
         r = exec_context_named_iofds(context, params, named_iofds);
@@ -3510,8 +3591,8 @@ int exec_spawn(Unit *unit,
                                socket_fd,
                                named_iofds,
                                fds,
-                               n_storage_fds,
                                n_socket_fds,
+                               n_storage_fds,
                                files_env,
                                unit->manager->user_lookup_fds[1],
                                &exit_status);
index 678ee5b1893aabbc2642239c776fcbb44f63b52e..2e8b01f7695f97f9899e195a0187689cb62533b4 100644 (file)
@@ -87,10 +87,10 @@ struct ExecStatus {
 };
 
 typedef enum ExecCommandFlags {
-        EXEC_COMMAND_IGNORE_FAILURE = 1,
-        EXEC_COMMAND_FULLY_PRIVILEGED = 2,
-        EXEC_COMMAND_NO_SETUID = 4,
-        EXEC_COMMAND_AMBIENT_MAGIC = 8,
+        EXEC_COMMAND_IGNORE_FAILURE   = 1 << 0,
+        EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
+        EXEC_COMMAND_NO_SETUID        = 1 << 2,
+        EXEC_COMMAND_AMBIENT_MAGIC    = 1 << 3,
 } ExecCommandFlags;
 
 /* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */
@@ -306,8 +306,8 @@ struct ExecParameters {
 
         int *fds;
         char **fd_names;
-        size_t n_storage_fds;
         size_t n_socket_fds;
+        size_t n_storage_fds;
 
         ExecFlags flags;
         bool selinux_context_net:1;
@@ -326,6 +326,9 @@ struct ExecParameters {
         int stdin_fd;
         int stdout_fd;
         int stderr_fd;
+
+        /* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done */
+        int exec_fd;
 };
 
 #include "unit.h"
index 929eebfe37c7bfce6e520480fb4a7febe12f09dd..73fa556d135f1841a05554009e1f78fe61e44115 100644 (file)
@@ -9,6 +9,7 @@ void kill_context_init(KillContext *c) {
         assert(c);
 
         c->kill_signal = SIGTERM;
+        c->final_kill_signal = SIGKILL;
         c->send_sigkill = true;
         c->send_sighup = false;
 }
@@ -21,10 +22,12 @@ void kill_context_dump(KillContext *c, FILE *f, const char *prefix) {
         fprintf(f,
                 "%sKillMode: %s\n"
                 "%sKillSignal: SIG%s\n"
+                "%sFinalKillSignal: SIG%s\n"
                 "%sSendSIGKILL: %s\n"
                 "%sSendSIGHUP:  %s\n",
                 prefix, kill_mode_to_string(c->kill_mode),
                 prefix, signal_to_string(c->kill_signal),
+                prefix, signal_to_string(c->final_kill_signal),
                 prefix, yes_no(c->send_sigkill),
                 prefix, yes_no(c->send_sighup));
 }
index 2d6aa943a6188765a7ffa0b652b7724e0e761e91..f4e312d75a591f78aeac248b6e849f32bbb3e1ee 100644 (file)
@@ -21,6 +21,7 @@ typedef enum KillMode {
 struct KillContext {
         KillMode kill_mode;
         int kill_signal;
+        int final_kill_signal;
         bool send_sigkill;
         bool send_sighup;
 };
index 290e8001d8ce4b08d3eb8728ee7fa223ff3e82be..7a276ea3c8c13bd7e4e3f2c8881703c05356a3c4 100644 (file)
@@ -151,7 +151,8 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
 `$1.SendSIGKILL,                 config_parse_bool,                  0,                             offsetof($1, kill_context.send_sigkill)
 $1.SendSIGHUP,                   config_parse_bool,                  0,                             offsetof($1, kill_context.send_sighup)
 $1.KillMode,                     config_parse_kill_mode,             0,                             offsetof($1, kill_context.kill_mode)
-$1.KillSignal,                   config_parse_signal,                0,                             offsetof($1, kill_context.kill_signal)'
+$1.KillSignal,                   config_parse_signal,                0,                             offsetof($1, kill_context.kill_signal)
+$1.FinalKillSignal,              config_parse_signal,                0,                             offsetof($1, kill_context.final_kill_signal)'
 )m4_dnl
 m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS',
 `$1.Slice,                       config_parse_unit_slice,            0,                             0
index 89b7bd8d096fba1368cca50e6ced673087033432..908d16c9f26e1c7bfb7498b55bffc39f18fe90e5 100644 (file)
@@ -3013,13 +3013,13 @@ int config_parse_cpu_quota(
                 return 0;
         }
 
-        r = parse_percent_unbounded(rvalue);
+        r = parse_permille_unbounded(rvalue);
         if (r <= 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
                 return 0;
         }
 
-        c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
+        c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 1000U;
         return 0;
 }
 
@@ -3041,7 +3041,7 @@ int config_parse_memory_limit(
 
         if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
 
-                r = parse_percent(rvalue);
+                r = parse_permille(rvalue);
                 if (r < 0) {
                         r = parse_size(rvalue, 1024, &bytes);
                         if (r < 0) {
@@ -3049,7 +3049,7 @@ int config_parse_memory_limit(
                                 return 0;
                         }
                 } else
-                        bytes = physical_memory_scale(r, 100U);
+                        bytes = physical_memory_scale(r, 1000U);
 
                 if (bytes >= UINT64_MAX ||
                     (bytes <= 0 && !streq(lvalue, "MemorySwapMax"))) {
@@ -3102,7 +3102,7 @@ int config_parse_tasks_max(
                 return 0;
         }
 
-        r = parse_percent(rvalue);
+        r = parse_permille(rvalue);
         if (r < 0) {
                 r = safe_atou64(rvalue, &v);
                 if (r < 0) {
@@ -3110,7 +3110,7 @@ int config_parse_tasks_max(
                         return 0;
                 }
         } else
-                v = system_tasks_max_scale(r, 100U);
+                v = system_tasks_max_scale(r, 1000U);
 
         if (v <= 0 || v >= UINT64_MAX) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
index dad281ef72a8a71eaaf63f9ac13634701c2b7edf..1cb5ccadf60c55e777645f20dba1669ce4c11954 100644 (file)
@@ -39,6 +39,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
 CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
 CONFIG_PARSER_PROTOTYPE(config_parse_kill_signal);
+CONFIG_PARSER_PROTOTYPE(config_parse_final_kill_signal);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags);
 CONFIG_PARSER_PROTOTYPE(config_parse_timer);
 CONFIG_PARSER_PROTOTYPE(config_parse_trigger_unit);
index 6bffd6f362b37b190ffc2f62c10341564435efcf..c8788b8e8ae5eae8d74b4c5e444bdc94dfc728ca 100644 (file)
@@ -95,7 +95,7 @@ static int arg_crash_chvt = -1;
 static bool arg_crash_shell = false;
 static bool arg_crash_reboot = false;
 static char *arg_confirm_spawn = NULL;
-static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
+static ShowStatus arg_show_status = _SHOW_STATUS_INVALID;
 static bool arg_switched_root = false;
 static bool arg_no_pager = false;
 static bool arg_service_watchdogs = true;
@@ -470,7 +470,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
         } else if (streq(key, "quiet") && !value) {
 
-                if (arg_show_status == _SHOW_STATUS_UNSET)
+                if (arg_show_status == _SHOW_STATUS_INVALID)
                         arg_show_status = SHOW_STATUS_AUTO;
 
         } else if (streq(key, "debug") && !value) {
@@ -838,19 +838,15 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_LOG_LEVEL:
                         r = log_set_max_level_from_string(optarg);
-                        if (r < 0) {
-                                log_error("Failed to parse log level %s.", optarg);
-                                return r;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse log level \"%s\": %m", optarg);
 
                         break;
 
                 case ARG_LOG_TARGET:
                         r = log_set_target_from_string(optarg);
-                        if (r < 0) {
-                                log_error("Failed to parse log target %s.", optarg);
-                                return r;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse log target \"%s\": %m", optarg);
 
                         break;
 
@@ -858,10 +854,9 @@ static int parse_argv(int argc, char *argv[]) {
 
                         if (optarg) {
                                 r = log_show_color_from_string(optarg);
-                                if (r < 0) {
-                                        log_error("Failed to parse log color setting %s.", optarg);
-                                        return r;
-                                }
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse log color setting \"%s\": %m",
+                                                               optarg);
                         } else
                                 log_show_color(true);
 
@@ -870,10 +865,9 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_LOG_LOCATION:
                         if (optarg) {
                                 r = log_show_location_from_string(optarg);
-                                if (r < 0) {
-                                        log_error("Failed to parse log location setting %s.", optarg);
-                                        return r;
-                                }
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse log location setting \"%s\": %m",
+                                                               optarg);
                         } else
                                 log_show_location(true);
 
@@ -881,26 +875,24 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_DEFAULT_STD_OUTPUT:
                         r = exec_output_from_string(optarg);
-                        if (r < 0) {
-                                log_error("Failed to parse default standard output setting %s.", optarg);
-                                return r;
-                        } else
-                                arg_default_std_output = r;
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse default standard output setting \"%s\": %m",
+                                                       optarg);
+                        arg_default_std_output = r;
                         break;
 
                 case ARG_DEFAULT_STD_ERROR:
                         r = exec_output_from_string(optarg);
-                        if (r < 0) {
-                                log_error("Failed to parse default standard error output setting %s.", optarg);
-                                return r;
-                        } else
-                                arg_default_std_error = r;
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse default standard error output setting \"%s\": %m",
+                                                       optarg);
+                        arg_default_std_error = r;
                         break;
 
                 case ARG_UNIT:
                         r = free_and_strdup(&arg_default_unit, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to set default unit %s: %m", optarg);
+                                return log_error_errno(r, "Failed to set default unit \"%s\": %m", optarg);
 
                         break;
 
@@ -938,7 +930,8 @@ static int parse_argv(int argc, char *argv[]) {
                         else {
                                 r = parse_boolean(optarg);
                                 if (r < 0)
-                                        return log_error_errno(r, "Failed to parse dump core boolean: %s", optarg);
+                                        return log_error_errno(r, "Failed to parse dump core boolean: \"%s\": %m",
+                                                               optarg);
                                 arg_dump_core = r;
                         }
                         break;
@@ -946,7 +939,8 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_CRASH_CHVT:
                         r = parse_crash_chvt(optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse crash virtual terminal index: %s", optarg);
+                                return log_error_errno(r, "Failed to parse crash virtual terminal index: \"%s\": %m",
+                                                       optarg);
                         break;
 
                 case ARG_CRASH_SHELL:
@@ -955,7 +949,8 @@ static int parse_argv(int argc, char *argv[]) {
                         else {
                                 r = parse_boolean(optarg);
                                 if (r < 0)
-                                        return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+                                        return log_error_errno(r, "Failed to parse crash shell boolean: \"%s\": %m",
+                                                               optarg);
                                 arg_crash_shell = r;
                         }
                         break;
@@ -966,7 +961,8 @@ static int parse_argv(int argc, char *argv[]) {
                         else {
                                 r = parse_boolean(optarg);
                                 if (r < 0)
-                                        return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+                                        return log_error_errno(r, "Failed to parse crash shell boolean: \"%s\": %m",
+                                                               optarg);
                                 arg_crash_reboot = r;
                         }
                         break;
@@ -976,23 +972,24 @@ static int parse_argv(int argc, char *argv[]) {
 
                         r = parse_confirm_spawn(optarg, &arg_confirm_spawn);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse confirm spawn option: %m");
+                                return log_error_errno(r, "Failed to parse confirm spawn option: \"%s\": %m",
+                                                       optarg);
                         break;
 
                 case ARG_SERVICE_WATCHDOGS:
                         r = parse_boolean(optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse service watchdogs boolean: %s", optarg);
+                                return log_error_errno(r, "Failed to parse service watchdogs boolean: \"%s\": %m",
+                                                       optarg);
                         arg_service_watchdogs = r;
                         break;
 
                 case ARG_SHOW_STATUS:
                         if (optarg) {
                                 r = parse_show_status(optarg, &arg_show_status);
-                                if (r < 0) {
-                                        log_error("Failed to parse show status boolean %s.", optarg);
-                                        return r;
-                                }
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse show status boolean: \"%s\": %m",
+                                                               optarg);
                         } else
                                 arg_show_status = SHOW_STATUS_YES;
                         break;
@@ -1002,8 +999,10 @@ static int parse_argv(int argc, char *argv[]) {
                         FILE *f;
 
                         r = safe_atoi(optarg, &fd);
-                        if (r < 0 || fd < 0) {
-                                log_error("Failed to parse deserialize option %s.", optarg);
+                        if (r < 0)
+                                log_error_errno(r, "Failed to parse deserialize option \"%s\": %m", optarg);
+                        if (fd < 0) {
+                                log_error("Invalid deserialize fd: %d", fd);
                                 return -EINVAL;
                         }
 
@@ -1011,7 +1010,7 @@ static int parse_argv(int argc, char *argv[]) {
 
                         f = fdopen(fd, "r");
                         if (!f)
-                                return log_error_errno(errno, "Failed to open serialization fd: %m");
+                                return log_error_errno(errno, "Failed to open serialization fd %d: %m", fd);
 
                         safe_fclose(arg_serialization);
                         arg_serialization = f;
@@ -1026,7 +1025,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_MACHINE_ID:
                         r = set_machine_id(optarg);
                         if (r < 0)
-                                return log_error_errno(r, "MachineID '%s' is not valid.", optarg);
+                                return log_error_errno(r, "MachineID '%s' is not valid: %m", optarg);
                         break;
 
                 case 'h':
@@ -1219,7 +1218,7 @@ static int status_welcome(void) {
         _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
         int r;
 
-        if (arg_show_status <= 0)
+        if (IN_SET(arg_show_status, SHOW_STATUS_NO, SHOW_STATUS_AUTO))
                 return 0;
 
         r = parse_os_release(NULL,
@@ -1985,7 +1984,7 @@ static int load_configuration(int argc, char **argv, const char **ret_error_mess
         }
 
         /* Initialize the show status setting if it hasn't been set explicitly yet */
-        if (arg_show_status == _SHOW_STATUS_UNSET)
+        if (arg_show_status == _SHOW_STATUS_INVALID)
                 arg_show_status = SHOW_STATUS_YES;
 
         return 0;
@@ -2373,8 +2372,8 @@ int main(int argc, char *argv[]) {
         m->timestamps[MANAGER_TIMESTAMP_KERNEL] = kernel_timestamp;
         m->timestamps[MANAGER_TIMESTAMP_INITRD] = initrd_timestamp;
         m->timestamps[MANAGER_TIMESTAMP_USERSPACE] = userspace_timestamp;
-        m->timestamps[MANAGER_TIMESTAMP_SECURITY_START] = security_start_timestamp;
-        m->timestamps[MANAGER_TIMESTAMP_SECURITY_FINISH] = security_finish_timestamp;
+        m->timestamps[manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_SECURITY_START)] = security_start_timestamp;
+        m->timestamps[manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_SECURITY_FINISH)] = security_finish_timestamp;
 
         set_manager_defaults(m);
         set_manager_settings(m);
index ada8712fd504b664be575dc49a92fbc362f85f24..8dc3f5e55f1fdbf342ed98157baa9044d0fcccd3 100644 (file)
@@ -1555,9 +1555,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         if (r < 0)
                 return r;
 
-        dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_GENERATORS_START);
+        dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_GENERATORS_START));
         r = manager_run_generators(m);
-        dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_GENERATORS_FINISH);
+        dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_GENERATORS_FINISH));
         if (r < 0)
                 return r;
 
@@ -1572,10 +1572,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
                 m->n_reloading++;
 
         /* First, enumerate what we can from all config files */
-        dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_START);
+        dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_UNITS_LOAD_START));
         manager_enumerate_perpetual(m);
         manager_enumerate(m);
-        dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
+        dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_UNITS_LOAD_FINISH));
 
         /* Second, deserialize if there is something to deserialize */
         if (serialization) {
@@ -3016,14 +3016,22 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
         fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged));
         fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs));
 
+        t = show_status_to_string(m->show_status);
+        if (t)
+                fprintf(f, "show-status=%s\n", t);
+
         if (m->log_level_overridden)
                 fprintf(f, "log-level-override=%i\n", log_get_max_level());
         if (m->log_target_overridden)
                 fprintf(f, "log-target-override=%s\n", log_target_to_string(log_get_target()));
 
         for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
-                /* The userspace and finish timestamps only apply to the host system, hence only serialize them there */
-                if (in_initrd() && IN_SET(q, MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH))
+                /* The following timestamps only apply to the host system, hence only serialize them there */
+                if (in_initrd() &&
+                    IN_SET(q, MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
+                           MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
+                           MANAGER_TIMESTAMP_GENERATORS_START, MANAGER_TIMESTAMP_GENERATORS_FINISH,
+                           MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH))
                         continue;
 
                 t = manager_timestamp_to_string(q);
@@ -3205,6 +3213,15 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 m->service_watchdogs = b;
 
+                } else if ((val = startswith(l, "show-status="))) {
+                        ShowStatus s;
+
+                        s = show_status_from_string(val);
+                        if (s < 0)
+                                log_notice("Failed to parse show-status flag %s", val);
+                        else
+                                manager_set_show_status(m, s);
+
                 } else if ((val = startswith(l, "log-level-override="))) {
                         int level;
 
@@ -3912,7 +3929,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
                           mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
         m->show_status = mode;
 
-        if (mode > 0)
+        if (IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES))
                 (void) touch("/run/systemd/show-status");
         else
                 (void) unlink("/run/systemd/show-status");
@@ -3934,7 +3951,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
         if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
                 return false;
 
-        return m->show_status > 0;
+        return IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
 }
 
 const char *manager_get_confirm_spawn(Manager *m) {
@@ -4514,6 +4531,14 @@ void manager_restore_original_log_target(Manager *m) {
         m->log_target_overridden = false;
 }
 
+ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s) {
+        if (in_initrd() &&
+            s >= MANAGER_TIMESTAMP_SECURITY_START &&
+            s <= MANAGER_TIMESTAMP_UNITS_LOAD_FINISH)
+                return s - MANAGER_TIMESTAMP_SECURITY_START + MANAGER_TIMESTAMP_INITRD_SECURITY_START;
+        return s;
+}
+
 static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
         [MANAGER_INITIALIZING] = "initializing",
         [MANAGER_STARTING] = "starting",
@@ -4538,6 +4563,12 @@ static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = {
         [MANAGER_TIMESTAMP_GENERATORS_FINISH] = "generators-finish",
         [MANAGER_TIMESTAMP_UNITS_LOAD_START] = "units-load-start",
         [MANAGER_TIMESTAMP_UNITS_LOAD_FINISH] = "units-load-finish",
+        [MANAGER_TIMESTAMP_INITRD_SECURITY_START] = "initrd-security-start",
+        [MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH] = "initrd-security-finish",
+        [MANAGER_TIMESTAMP_INITRD_GENERATORS_START] = "initrd-generators-start",
+        [MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH] = "initrd-generators-finish",
+        [MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START] = "initrd-units-load-start",
+        [MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH] = "initrd-units-load-finish",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(manager_timestamp, ManagerTimestamp);
index ea5d4250304930e48c052109e397805ab731f0a9..342258fcf9ecfc44f06c3ce45f54dee612fd9fd1 100644 (file)
@@ -67,6 +67,13 @@ typedef enum ManagerTimestamp {
         MANAGER_TIMESTAMP_GENERATORS_FINISH,
         MANAGER_TIMESTAMP_UNITS_LOAD_START,
         MANAGER_TIMESTAMP_UNITS_LOAD_FINISH,
+
+        MANAGER_TIMESTAMP_INITRD_SECURITY_START,
+        MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH,
+        MANAGER_TIMESTAMP_INITRD_GENERATORS_START,
+        MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH,
+        MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START,
+        MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH,
         _MANAGER_TIMESTAMP_MAX,
         _MANAGER_TIMESTAMP_INVALID = -1,
 } ManagerTimestamp;
@@ -479,3 +486,4 @@ void manager_disable_confirm_spawn(void);
 
 const char *manager_timestamp_to_string(ManagerTimestamp m) _const_;
 ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_;
+ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s);
index 75e1b6ac53017059003085f49a5cf25a4dd9599b..55d851d8c0ba3aec2cae0538e7e5c7df30010e9d 100644 (file)
@@ -747,10 +747,11 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
 static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
 
         ExecParameters exec_params = {
-                .flags      = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
-                .stdin_fd   = -1,
-                .stdout_fd  = -1,
-                .stderr_fd  = -1,
+                .flags     = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+                .stdin_fd  = -1,
+                .stdout_fd = -1,
+                .stderr_fd = -1,
+                .exec_fd   = -1,
         };
         pid_t pid;
         int r;
index 7476ee533395071c979681013716fcaeb68c9043..722a9e9ae7792e103fe1af6bfd4501573f2aaf7a 100644 (file)
@@ -79,9 +79,10 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
         [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
 };
 
-static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
+static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
 static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
 static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
+static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
 
 static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
 static void service_enter_reload_by_notify(Service *s);
@@ -389,6 +390,7 @@ static void service_done(Unit *u) {
         service_stop_watchdog(s);
 
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+        s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
 
         service_release_resources(u);
 }
@@ -1066,6 +1068,9 @@ static void service_set_state(Service *s, ServiceState state) {
             !(state == SERVICE_DEAD && UNIT(s)->job))
                 service_close_socket_fd(s);
 
+        if (state != SERVICE_START)
+                s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
+
         if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
                 service_stop_watchdog(s);
 
@@ -1178,21 +1183,23 @@ static int service_coldplug(Unit *u) {
         return 0;
 }
 
-static int service_collect_fds(Service *s,
-                               int **fds,
-                               char ***fd_names,
-                               unsigned *n_storage_fds,
-                               unsigned *n_socket_fds) {
+static int service_collect_fds(
+                Service *s,
+                int **fds,
+                char ***fd_names,
+                size_t *n_socket_fds,
+                size_t *n_storage_fds) {
 
         _cleanup_strv_free_ char **rfd_names = NULL;
         _cleanup_free_ int *rfds = NULL;
-        unsigned rn_socket_fds = 0, rn_storage_fds = 0;
+        size_t rn_socket_fds = 0, rn_storage_fds = 0;
         int r;
 
         assert(s);
         assert(fds);
         assert(fd_names);
         assert(n_socket_fds);
+        assert(n_storage_fds);
 
         if (s->socket_fd >= 0) {
 
@@ -1256,7 +1263,7 @@ static int service_collect_fds(Service *s,
 
         if (s->n_fd_store > 0) {
                 ServiceFDStore *fs;
-                unsigned n_fds;
+                size_t n_fds;
                 char **nl;
                 int *t;
 
@@ -1294,6 +1301,63 @@ static int service_collect_fds(Service *s,
         return 0;
 }
 
+static int service_allocate_exec_fd_event_source(
+                Service *s,
+                int fd,
+                sd_event_source **ret_event_source) {
+
+        _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL;
+        int r;
+
+        assert(s);
+        assert(fd >= 0);
+        assert(ret_event_source);
+
+        r = sd_event_add_io(UNIT(s)->manager->event, &source, fd, 0, service_dispatch_exec_io, s);
+        if (r < 0)
+                return log_unit_error_errno(UNIT(s), r, "Failed to allocate exec_fd event source: %m");
+
+        /* This is a bit lower priority than SIGCHLD, as that carries a lot more interesting failure information */
+
+        r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_NORMAL-3);
+        if (r < 0)
+                return log_unit_error_errno(UNIT(s), r, "Failed to adjust priority of exec_fd event source: %m");
+
+        (void) sd_event_source_set_description(source, "service event_fd");
+
+        r = sd_event_source_set_io_fd_own(source, true);
+        if (r < 0)
+                return log_unit_error_errno(UNIT(s), r, "Failed to pass ownership of fd to event source: %m");
+
+        *ret_event_source = TAKE_PTR(source);
+        return 0;
+}
+
+static int service_allocate_exec_fd(
+                Service *s,
+                sd_event_source **ret_event_source,
+                int* ret_exec_fd) {
+
+        _cleanup_close_pair_ int p[2] = { -1, -1 };
+        int r;
+
+        assert(s);
+        assert(ret_event_source);
+        assert(ret_exec_fd);
+
+        if (pipe2(p, O_CLOEXEC|O_NONBLOCK) < 0)
+                return log_unit_error_errno(UNIT(s), errno, "Failed to allocate exec_fd pipe: %m");
+
+        r = service_allocate_exec_fd_event_source(s, p[0], ret_event_source);
+        if (r < 0)
+                return r;
+
+        p[0] = -1;
+        *ret_exec_fd = TAKE_FD(p[1]);
+
+        return 0;
+}
+
 static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
         assert(s);
 
@@ -1325,9 +1389,12 @@ static int service_spawn(
                 .stdin_fd   = -1,
                 .stdout_fd  = -1,
                 .stderr_fd  = -1,
+                .exec_fd    = -1,
         };
         _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL;
-        unsigned n_storage_fds = 0, n_socket_fds = 0, n_env = 0;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *exec_fd_source = NULL;
+        size_t n_socket_fds = 0, n_storage_fds = 0, n_env = 0;
+        _cleanup_close_ int exec_fd = -1;
         _cleanup_free_ int *fds = NULL;
         pid_t pid;
         int r;
@@ -1353,11 +1420,19 @@ static int service_spawn(
             s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
             s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
 
-                r = service_collect_fds(s, &fds, &fd_names, &n_storage_fds, &n_socket_fds);
+                r = service_collect_fds(s, &fds, &fd_names, &n_socket_fds, &n_storage_fds);
                 if (r < 0)
                         return r;
 
-                log_unit_debug(UNIT(s), "Passing %i fds to service", n_storage_fds + n_socket_fds);
+                log_unit_debug(UNIT(s), "Passing %zu fds to service", n_socket_fds + n_storage_fds);
+        }
+
+        if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
+                assert(!s->exec_fd_event_source);
+
+                r = service_allocate_exec_fd(s, &exec_fd_source, &exec_fd);
+                if (r < 0)
+                        return r;
         }
 
         r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout));
@@ -1449,8 +1524,8 @@ static int service_spawn(
         exec_params.environment = final_env;
         exec_params.fds = fds;
         exec_params.fd_names = fd_names;
-        exec_params.n_storage_fds = n_storage_fds;
         exec_params.n_socket_fds = n_socket_fds;
+        exec_params.n_storage_fds = n_storage_fds;
         exec_params.watchdog_usec = s->watchdog_usec;
         exec_params.selinux_context_net = s->socket_fd_selinux_context_net;
         if (s->type == SERVICE_IDLE)
@@ -1458,6 +1533,7 @@ static int service_spawn(
         exec_params.stdin_fd = s->stdin_fd;
         exec_params.stdout_fd = s->stdout_fd;
         exec_params.stderr_fd = s->stderr_fd;
+        exec_params.exec_fd = exec_fd;
 
         r = exec_spawn(UNIT(s),
                        c,
@@ -1469,6 +1545,9 @@ static int service_spawn(
         if (r < 0)
                 return r;
 
+        s->exec_fd_event_source = TAKE_PTR(exec_fd_source);
+        s->exec_fd_hot = false;
+
         r = unit_watch_pid(UNIT(s), pid);
         if (r < 0) /* FIXME: we need to do something here */
                 return r;
@@ -1979,14 +2058,12 @@ static void service_enter_start(Service *s) {
                 s->control_pid = pid;
                 service_set_state(s, SERVICE_START);
 
-        } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) {
+        } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_EXEC)) {
 
-                /* For oneshot services we wait until the start
-                 * process exited, too, but it is our main process. */
+                /* For oneshot services we wait until the start process exited, too, but it is our main process. */
 
-                /* For D-Bus services we know the main pid right away,
-                 * but wait for the bus name to appear on the
-                 * bus. Notify services are similar. */
+                /* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the
+                 * bus. 'notify' and 'exec' services are similar. */
 
                 service_set_main_pid(s, pid);
                 service_set_state(s, SERVICE_START);
@@ -2442,6 +2519,13 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         if (r < 0)
                 return r;
 
+        if (s->exec_fd_event_source) {
+                r = unit_serialize_item_fd(u, f, fds, "exec-fd", sd_event_source_get_io_fd(s->exec_fd_event_source));
+                if (r < 0)
+                        return r;
+                unit_serialize_item(u, f, "exec-fd-hot", yes_no(s->exec_fd_hot));
+        }
+
         if (UNIT_ISSET(s->accept_socket)) {
                 r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
                 if (r < 0)
@@ -2775,6 +2859,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                         s->stderr_fd = fdset_remove(fds, fd);
                         s->exec_context.stdio_as_fds = true;
                 }
+        } else if (streq(key, "exec-fd")) {
+                int fd;
+
+                if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                        log_unit_debug(u, "Failed to parse exec-fd value: %s", value);
+                else {
+                        s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
+
+                        fd = fdset_remove(fds, fd);
+                        if (service_allocate_exec_fd_event_source(s, fd, &s->exec_fd_event_source) < 0)
+                                safe_close(fd);
+                }
         } else if (streq(key, "watchdog-override-usec")) {
                 usec_t watchdog_override_usec;
                 if (timestamp_deserialize(value, &watchdog_override_usec) < 0)
@@ -2858,7 +2954,7 @@ static int service_watch_pid_file(Service *s) {
 
         log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path);
 
-        r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io);
+        r = path_spec_watch(s->pid_file_pathspec, service_dispatch_inotify_io);
         if (r < 0)
                 goto fail;
 
@@ -2902,7 +2998,7 @@ static int service_demand_pid_file(Service *s) {
         return service_watch_pid_file(s);
 }
 
-static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
+static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
         PathSpec *p = userdata;
         Service *s;
 
@@ -2935,6 +3031,59 @@ fail:
         return 0;
 }
 
+static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
+        Service *s = SERVICE(userdata);
+
+        assert(s);
+
+        log_unit_debug(UNIT(s), "got exec-fd event");
+
+        /* If Type=exec is set, we'll consider a service started successfully the instant we invoked execve()
+         * successfully for it. We implement this through a pipe() towards the child, which the kernel automatically
+         * closes for us due to O_CLOEXEC on execve() in the child, which then triggers EOF on the pipe in the
+         * parent. We need to be careful however, as there are other reasons that we might cause the child's side of
+         * the pipe to be closed (for example, a simple exit()). To deal with that we'll ignore EOFs on the pipe unless
+         * the child signalled us first that it is about to call the execve(). It does so by sending us a simple
+         * non-zero byte via the pipe. We also provide the child with a way to inform us in case execve() failed: if it
+         * sends a zero byte we'll ignore POLLHUP on the fd again. */
+
+        for (;;) {
+                uint8_t x;
+                ssize_t n;
+
+                n = read(fd, &x, sizeof(x));
+                if (n < 0) {
+                        if (errno == EAGAIN) /* O_NONBLOCK in effect â†’ everything queued has now been processed. */
+                                return 0;
+
+                        return log_unit_error_errno(UNIT(s), errno, "Failed to read from exec_fd: %m");
+                }
+                if (n == 0) { /* EOF â†’ the event we are waiting for */
+
+                        s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
+
+                        if (s->exec_fd_hot) { /* Did the child tell us to expect EOF now? */
+                                log_unit_debug(UNIT(s), "Got EOF on exec-fd");
+
+                                s->exec_fd_hot = false;
+
+                                /* Nice! This is what we have been waiting for. Transition to next state. */
+                                if (s->type == SERVICE_EXEC && s->state == SERVICE_START)
+                                        service_enter_start_post(s);
+                        } else
+                                log_unit_debug(UNIT(s), "Got EOF on exec-fd while it was disabled, ignoring.");
+
+                        return 0;
+                }
+
+                /* A byte was read â†’ this turns on/off the exec fd logic */
+                assert(n == sizeof(x));
+                s->exec_fd_hot = x;
+        }
+
+        return 0;
+}
+
 static void service_notify_cgroup_empty_event(Unit *u) {
         Service *s = SERVICE(u);
 
@@ -3844,7 +3993,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
         [SERVICE_ONESHOT] = "oneshot",
         [SERVICE_DBUS] = "dbus",
         [SERVICE_NOTIFY] = "notify",
-        [SERVICE_IDLE] = "idle"
+        [SERVICE_IDLE] = "idle",
+        [SERVICE_EXEC] = "exec",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
index 9c06e91883b72ca1cdce04fc07f58a0d35e1d312..b53e4204e75519ac33612019e51a34fcccf6ecb5 100644 (file)
@@ -30,6 +30,7 @@ typedef enum ServiceType {
         SERVICE_DBUS,     /* we fork and wait until a specific D-Bus name appears on the bus */
         SERVICE_NOTIFY,   /* we fork and wait until a daemon sends us a ready message with sd_notify() */
         SERVICE_IDLE,     /* much like simple, but delay exec() until all jobs are dispatched. */
+        SERVICE_EXEC,     /* we fork and wait until we execute exec() (this means our own setup is waited for) */
         _SERVICE_TYPE_MAX,
         _SERVICE_TYPE_INVALID = -1
 } ServiceType;
@@ -165,6 +166,8 @@ struct Service {
         NotifyAccess notify_access;
         NotifyState notify_state;
 
+        sd_event_source *exec_fd_event_source;
+
         ServiceFDStore *fd_store;
         size_t n_fd_store;
         unsigned n_fd_store_max;
@@ -179,6 +182,7 @@ struct Service {
 
         unsigned n_restarts;
         bool flush_n_restarts;
+        bool exec_fd_hot;
 };
 
 extern const UnitVTable service_vtable;
index fd9aeb9416f82a3dbe681fd40472da561fd99bbf..d8d2317d38ef1f1763c78da2457f8b9e94357224 100644 (file)
@@ -5,26 +5,30 @@
 #include "io-util.h"
 #include "parse-util.h"
 #include "show-status.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "terminal-util.h"
 #include "util.h"
 
+static const char* const show_status_table[_SHOW_STATUS_MAX] = {
+        [SHOW_STATUS_NO] = "no",
+        [SHOW_STATUS_AUTO] = "auto",
+        [SHOW_STATUS_TEMPORARY] = "temporary",
+        [SHOW_STATUS_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(show_status, ShowStatus, SHOW_STATUS_YES);
+
 int parse_show_status(const char *v, ShowStatus *ret) {
-        int r;
+        ShowStatus s;
 
-        assert(v);
         assert(ret);
 
-        if (streq(v, "auto")) {
-                *ret = SHOW_STATUS_AUTO;
-                return 0;
-        }
-
-        r = parse_boolean(v);
-        if (r < 0)
-                return r;
+        s = show_status_from_string(v);
+        if (s < 0 || s == SHOW_STATUS_TEMPORARY)
+                return -EINVAL;
 
-        *ret = r ? SHOW_STATUS_YES : SHOW_STATUS_NO;
+        *ret = s;
         return 0;
 }
 
index 1a80de33d9e2596c20dc72c31c4d1976a54e3a63..ac590303c112993de6970647442f808b0cafe6fa 100644 (file)
@@ -8,13 +8,16 @@
 /* Manager status */
 
 typedef enum ShowStatus {
-        _SHOW_STATUS_UNSET = -2,
-        SHOW_STATUS_AUTO = -1,
-        SHOW_STATUS_NO = 0,
-        SHOW_STATUS_YES = 1,
-        SHOW_STATUS_TEMPORARY = 2,
+        SHOW_STATUS_NO,
+        SHOW_STATUS_AUTO,
+        SHOW_STATUS_TEMPORARY,
+        SHOW_STATUS_YES,
+        _SHOW_STATUS_MAX,
+        _SHOW_STATUS_INVALID = -1,
 } ShowStatus;
 
+ShowStatus show_status_from_string(const char *v) _const_;
+const char* show_status_to_string(ShowStatus s) _pure_;
 int parse_show_status(const char *v, ShowStatus *ret);
 
 int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
index a6127654b5229f479771c526872e2016c1c0badf..aedbf51a4cea2d057fa5b56a7fba80e1662243d4 100644 (file)
@@ -1867,10 +1867,11 @@ static int socket_coldplug(Unit *u) {
 static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
 
         ExecParameters exec_params = {
-                .flags      = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
-                .stdin_fd   = -1,
-                .stdout_fd  = -1,
-                .stderr_fd  = -1,
+                .flags     = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+                .stdin_fd  = -1,
+                .stdout_fd = -1,
+                .stderr_fd = -1,
+                .exec_fd   = -1,
         };
         pid_t pid;
         int r;
index f8d6a4dd22f5fb555f9352f3e28ced9383d07c1a..000c028e77361ac8b7241f3f2df70c734b36cf54 100644 (file)
@@ -606,6 +606,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
                 .stdin_fd  = -1,
                 .stdout_fd = -1,
                 .stderr_fd = -1,
+                .exec_fd   = -1,
         };
         pid_t pid;
         int r;
index 113205bf254ca78eafa08e392cb853ee87cde0c2..23433be31c48ea6d0f4c520bb1a4435d86442c0a 100644 (file)
@@ -4479,7 +4479,7 @@ static int operation_to_signal(KillContext *c, KillOperation k) {
                 return c->kill_signal;
 
         case KILL_KILL:
-                return SIGKILL;
+                return c->final_kill_signal;
 
         case KILL_ABORT:
                 return SIGABRT;
index 99d07c14fb9f5a4a2696a30d18ea93fbf3168cc2..e7ba8d366442a85e868c92e1801fa23d915b47e5 100644 (file)
@@ -654,7 +654,8 @@ static int dump_list(int argc, char **argv, void *userdata) {
          * pick a fairly low data threshold here */
         sd_journal_set_data_threshold(j, 4096);
 
-        if (arg_one) {
+        /* "info" without pattern implies "-1" */
+        if (arg_one || (verb_is_info && argc == 1)) {
                 r = focus(j);
                 if (r < 0)
                         return r;
index eaf0a9a7af37e96e350bf2a567e9f02b9c67479a..cd99ca62f47157323771441d6977411f193c57d0 100644 (file)
@@ -1,7 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-  Copyright Â© 2014 Michael Biebl
-***/
 
 #include <getopt.h>
 #include <stdio.h>
index f97ae461c5a1040efb02b1e7e9749388e4064df8..f3a34d40c51b1d718237291c441c166c8165b7e0 100644 (file)
@@ -16,6 +16,7 @@
 #include "export-raw.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "import-common.h"
 #include "missing.h"
 #include "ratelimit.h"
@@ -244,13 +245,9 @@ static int raw_export_on_defer(sd_event_source *s, void *userdata) {
 }
 
 static int reflink_snapshot(int fd, const char *path) {
-        char *p, *d;
         int new_fd, r;
 
-        p = strdupa(path);
-        d = dirname(p);
-
-        new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600);
+        new_fd = open_parent(path, O_TMPFILE|O_CLOEXEC|O_RDWR, 0600);
         if (new_fd < 0) {
                 _cleanup_free_ char *t = NULL;
 
index a87f4625ecaf2ebe7c617b3ba8502572529b7374..4412fe152fdf7c2e270463239b91ebf49d37e380 100644 (file)
@@ -949,7 +949,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
                 return -EINVAL;
 
         if (machine) {
-                if (!machine_name_is_valid(machine))
+                if (!streq(machine, ".host") && !machine_name_is_valid(machine))
                         return -EINVAL;
 
                 free_and_replace(b->machine, machine);
@@ -1447,7 +1447,7 @@ _public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) {
 
         assert_return(machine, -EINVAL);
         assert_return(ret, -EINVAL);
-        assert_return(machine_name_is_valid(machine), -EINVAL);
+        assert_return(streq(machine, ".host") || machine_name_is_valid(machine), -EINVAL);
 
         r = sd_bus_new(&b);
         if (r < 0)
index f60d54af1bef1b7275a394694abfe863b058ebaf..996008bf6de8d5f3be28cafdbefd67e94a3636cb 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "sd-device.h"
+
 #include "hashmap.h"
 #include "set.h"
 
index 9dfba51ceaf81e37a1eced8ca013c2a93cb99450..5940eccead8c9e3a83e4318817654e294e71e899 100644 (file)
@@ -890,20 +890,27 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
         const char *p;
         int r;
 
-        assert_return(machine_name_is_valid(machine), -EINVAL);
         assert_return(class, -EINVAL);
 
-        p = strjoina("/run/systemd/machines/", machine);
-        r = parse_env_file(NULL, p, NEWLINE, "CLASS", &c, NULL);
-        if (r == -ENOENT)
-                return -ENXIO;
-        if (r < 0)
-                return r;
-        if (!c)
-                return -EIO;
+        if (streq(machine, ".host")) {
+                c = strdup("host");
+                if (!c)
+                        return -ENOMEM;
+        } else {
+                if (!machine_name_is_valid(machine))
+                        return -EINVAL;
 
-        *class = TAKE_PTR(c);
+                p = strjoina("/run/systemd/machines/", machine);
+                r = parse_env_file(NULL, p, NEWLINE, "CLASS", &c, NULL);
+                if (r == -ENOENT)
+                        return -ENXIO;
+                if (r < 0)
+                        return r;
+                if (!c)
+                        return -EIO;
+        }
 
+        *class = TAKE_PTR(c);
         return 0;
 }
 
index c4fefb1b9a5e7ceb35cdd7a1b338488b6eb81f0e..3eb42c59eba36578a9306cd8c0f1a44018f1bb81 100644 (file)
@@ -200,12 +200,14 @@ static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *leng
         if (*length + l > maxlength)
                 return NULL;
 
-        s.ai_flags = ai->ai_flags;
-        s.ai_family = ai->ai_family;
-        s.ai_socktype = ai->ai_socktype;
-        s.ai_protocol = ai->ai_protocol;
-        s.ai_addrlen = ai->ai_addrlen;
-        s.canonname_len = cnl;
+        s = (AddrInfoSerialization) {
+                .ai_flags = ai->ai_flags,
+                .ai_family = ai->ai_family,
+                .ai_socktype = ai->ai_socktype,
+                .ai_protocol = ai->ai_protocol,
+                .ai_addrlen = ai->ai_addrlen,
+                .canonname_len = cnl,
+        };
 
         memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization));
         memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen);
@@ -224,15 +226,7 @@ static int send_addrinfo_reply(
                 int _errno,
                 int _h_errno) {
 
-        AddrInfoResponse resp = {
-                .header.type = RESPONSE_ADDRINFO,
-                .header.id = id,
-                .header.length = sizeof(AddrInfoResponse),
-                .ret = ret,
-                ._errno = _errno,
-                ._h_errno = _h_errno,
-        };
-
+        AddrInfoResponse resp = {};
         union {
                 AddrInfoSerialization ais;
                 uint8_t space[BUFSIZE];
@@ -242,6 +236,15 @@ static int send_addrinfo_reply(
 
         assert(out_fd >= 0);
 
+        resp = (AddrInfoResponse) {
+                .header.type = RESPONSE_ADDRINFO,
+                .header.id = id,
+                .header.length = sizeof(AddrInfoResponse),
+                .ret = ret,
+                ._errno = _errno,
+                ._h_errno = _h_errno,
+        };
+
         if (ret == 0 && ai) {
                 void *p = &buffer;
                 struct addrinfo *k;
@@ -278,14 +281,7 @@ static int send_nameinfo_reply(
                 int _errno,
                 int _h_errno) {
 
-        NameInfoResponse resp = {
-                .header.type = RESPONSE_NAMEINFO,
-                .header.id = id,
-                .ret = ret,
-                ._errno = _errno,
-                ._h_errno = _h_errno,
-        };
-
+        NameInfoResponse resp = {};
         struct iovec iov[3];
         struct msghdr mh;
         size_t hl, sl;
@@ -295,9 +291,16 @@ static int send_nameinfo_reply(
         sl = serv ? strlen(serv)+1 : 0;
         hl = host ? strlen(host)+1 : 0;
 
-        resp.header.length = sizeof(NameInfoResponse) + hl + sl;
-        resp.hostlen = hl;
-        resp.servlen = sl;
+        resp = (NameInfoResponse) {
+                .header.type = RESPONSE_NAMEINFO,
+                .header.id = id,
+                .header.length = sizeof(NameInfoResponse) + hl + sl,
+                .hostlen = hl,
+                .servlen = sl,
+                .ret = ret,
+                ._errno = _errno,
+                ._h_errno = _h_errno,
+        };
 
         iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) };
         iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl };
@@ -500,7 +503,6 @@ _public_ int sd_resolve_new(sd_resolve **ret) {
 }
 
 _public_ int sd_resolve_default(sd_resolve **ret) {
-
         static thread_local sd_resolve *default_resolve = NULL;
         sd_resolve *e = NULL;
         int r;
@@ -681,8 +683,8 @@ static int complete_query(sd_resolve *resolve, sd_resolve_query *q) {
 
 static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) {
         AddrInfoSerialization s;
-        size_t l;
         struct addrinfo *ai;
+        size_t l;
 
         assert(p);
         assert(*p);
@@ -698,15 +700,17 @@ static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo
         if (*length < l)
                 return -EBADMSG;
 
-        ai = new0(struct addrinfo, 1);
+        ai = new(struct addrinfo, 1);
         if (!ai)
                 return -ENOMEM;
 
-        ai->ai_flags = s.ai_flags;
-        ai->ai_family = s.ai_family;
-        ai->ai_socktype = s.ai_socktype;
-        ai->ai_protocol = s.ai_protocol;
-        ai->ai_addrlen = s.ai_addrlen;
+        *ai = (struct addrinfo) {
+                .ai_flags = s.ai_flags,
+                .ai_family = s.ai_family,
+                .ai_socktype = s.ai_socktype,
+                .ai_protocol = s.ai_protocol,
+                .ai_addrlen = s.ai_addrlen,
+        };
 
         if (s.ai_addrlen > 0) {
                 ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen);
@@ -928,7 +932,7 @@ _public_ int sd_resolve_getaddrinfo(
                 sd_resolve_getaddrinfo_handler_t callback, void *userdata) {
 
         _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL;
-        AddrInfoRequest req;
+        AddrInfoRequest req = {};
         struct iovec iov[3];
         struct msghdr mh = {};
         int r;
@@ -1005,7 +1009,7 @@ _public_ int sd_resolve_getnameinfo(
                 void *userdata) {
 
         _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL;
-        NameInfoRequest req;
+        NameInfoRequest req = {};
         struct iovec iov[2];
         struct msghdr mh;
         int r;
index 598b931d03a4f108715c7c349e3638f63a6caee9..6538da3b6f423e066a7a1f1c30b913d32d76b26d 100644 (file)
@@ -72,6 +72,10 @@ void context_free(Context *c) {
         context_free_locale(c);
         context_free_x11(c);
         context_free_vconsole(c);
+
+        sd_bus_message_unref(c->locale_cache);
+        sd_bus_message_unref(c->x11_cache);
+        sd_bus_message_unref(c->vc_cache);
 };
 
 void locale_simplify(char *locale[_VARIABLE_LC_MAX]) {
@@ -87,11 +91,13 @@ int locale_read_data(Context *c, sd_bus_message *m) {
         int r;
 
         /* Do not try to re-read the file within single bus operation. */
-        if (m && m == c->locale_cache)
-                return 0;
+        if (m) {
+                if (m == c->locale_cache)
+                        return 0;
 
-        /* To suppress multiple call of stat(), store the message to cache here. */
-        c->locale_cache = m;
+                sd_bus_message_unref(c->locale_cache);
+                c->locale_cache = sd_bus_message_ref(m);
+        }
 
         r = stat("/etc/locale.conf", &st);
         if (r < 0 && errno != ENOENT)
@@ -155,11 +161,13 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
         int r;
 
         /* Do not try to re-read the file within single bus operation. */
-        if (m && m == c->vc_cache)
-                return 0;
+        if (m) {
+                if (m == c->vc_cache)
+                        return 0;
 
-        /* To suppress multiple call of stat(), store the message to cache here. */
-        c->vc_cache = m;
+                sd_bus_message_unref(c->vc_cache);
+                c->vc_cache = sd_bus_message_ref(m);
+        }
 
         if (stat("/etc/vconsole.conf", &st) < 0) {
                 if (errno != ENOENT)
@@ -197,11 +205,13 @@ int x11_read_data(Context *c, sd_bus_message *m) {
         int r;
 
         /* Do not try to re-read the file within single bus operation. */
-        if (m && m == c->x11_cache)
-                return 0;
+        if (m) {
+                if (m == c->x11_cache)
+                        return 0;
 
-        /* To suppress multiple call of stat(), store the message to cache here. */
-        c->x11_cache = m;
+                sd_bus_message_unref(c->x11_cache);
+                c->x11_cache = sd_bus_message_ref(m);
+        }
 
         if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
                 if (errno != ENOENT)
similarity index 96%
rename from src/login/70-uaccess.rules
rename to src/login/70-uaccess.rules.m4
index 3515d292ac523e6f59b7aa480d73ff8980728e65..d55e5bf5cef14be8e1b1f1bd17fe4acfbb57bfcc 100644 (file)
@@ -46,6 +46,10 @@ SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess"
 
 # DRI video devices
 SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess"
+m4_ifdef(`DEV_KVM_UACCESS',``
+# KVM
+SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess"''
+)m4_dnl
 
 # smart-card readers
 ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess"
index 13298cc85530bcd05d90ded32e9a3f7c0efe362a..38e36f20b32dc2bdba620a81345fdece83ed579b 100644 (file)
@@ -1770,11 +1770,11 @@ static int method_do_shutdown_or_sleep(
         if (sleep_verb) {
                 r = can_sleep(sleep_verb);
                 if (r == -ENOSPC)
-                        return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
-                                                "Not enough swap space for hibernation");
+                        return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough swap space for hibernation");
+                if (r == -ENOMEDIUM)
+                        return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Kernel image has been removed, can't hibernate");
                 if (r == 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
-                                                 "Sleep verb \"%s\" not supported", sleep_verb);
+                        return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb \"%s\" not supported", sleep_verb);
                 if (r < 0)
                         return r;
         }
@@ -2199,7 +2199,7 @@ static int method_can_shutdown_or_sleep(
 
         if (sleep_verb) {
                 r = can_sleep(sleep_verb);
-                if (IN_SET(r,  0, -ENOSPC))
+                if (IN_SET(r,  0, -ENOSPC, -ENOMEDIUM))
                         return sd_bus_reply_method_return(message, "s", "na");
                 if (r < 0)
                         return r;
index e18a6f0efeb42082bfe431d36613596dc0f237bc..346f792fb8c748bb451f17e84a119939dbe28e51 100644 (file)
@@ -711,9 +711,9 @@ int config_parse_tmpfs_size(
         assert(data);
 
         /* First, try to parse as percentage */
-        r = parse_percent(rvalue);
-        if (r > 0 && r < 100)
-                *sz = physical_memory_scale(r, 100U);
+        r = parse_permille(rvalue);
+        if (r > 0 && r < 1000)
+                *sz = physical_memory_scale(r, 1000U);
         else {
                 uint64_t k;
 
index 4326a452c6025cb471c7f1d9148c6dc1a14eae10..0e1ed18f7a812581f86918be1b38b155ad3927a5 100644 (file)
@@ -81,10 +81,6 @@ if conf.get('ENABLE_LOGIND') == 1
 
         install_data('70-power-switch.rules', install_dir : udevrulesdir)
 
-        if conf.get('HAVE_ACL') == 1
-                install_data('70-uaccess.rules', install_dir : udevrulesdir)
-        endif
-
         seat_rules = configure_file(
                 input : '71-seat.rules.in',
                 output : '71-seat.rules',
@@ -92,6 +88,15 @@ if conf.get('ENABLE_LOGIND') == 1
         install_data(seat_rules,
                      install_dir : udevrulesdir)
 
+        custom_target(
+                '70-uaccess.rules',
+                input : '70-uaccess.rules.m4',
+                output: '70-uaccess.rules',
+                command : [meson_apply_m4, config_h, '@INPUT@'],
+                capture : true,
+                install : conf.get('HAVE_ACL') == 1,
+                install_dir : udevrulesdir)
+
         custom_target(
                 '73-seat-late.rules',
                 input : '73-seat-late.rules.m4',
index 1fbf6ba5853380322e5e534a36731cf8b71351fd..9d42d1a3bd2fd12cce85894f405cc98f971fe743 100644 (file)
@@ -16,6 +16,7 @@
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "cgroup-util.h"
 #include "def.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "login-util.h"
 #include "macro.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "socket-util.h"
 #include "strv.h"
 #include "terminal-util.h"
 #include "util.h"
-#include "path-util.h"
-#include "cgroup-util.h"
 
 static int parse_argv(
                 pam_handle_t *handle,
                 int argc, const char **argv,
                 const char **class,
                 const char **type,
+                const char **desktop,
                 bool *debug) {
 
         unsigned i;
@@ -53,6 +54,10 @@ static int parse_argv(
                         if (type)
                                 *type = argv[i] + 5;
 
+                } else if (startswith(argv[i], "desktop=")) {
+                        if (desktop)
+                                *desktop = argv[i] + 8;
+
                 } else if (streq(argv[i], "debug")) {
                         if (debug)
                                 *debug = true;
@@ -109,6 +114,31 @@ static int get_user_data(
         return PAM_SUCCESS;
 }
 
+static int socket_from_display(const char *display, char **path) {
+        size_t k;
+        char *f, *c;
+
+        assert(display);
+        assert(path);
+
+        if (!display_is_local(display))
+                return -EINVAL;
+
+        k = strspn(display+1, "0123456789");
+
+        f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
+        if (!f)
+                return -ENOMEM;
+
+        c = stpcpy(f, "/tmp/.X11-unix/X");
+        memcpy(c, display+1, k);
+        c[k] = 0;
+
+        *path = f;
+
+        return 0;
+}
+
 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
         union sockaddr_union sa = {
                 .un.sun_family = AF_UNIX,
@@ -160,40 +190,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
         return 0;
 }
 
-static int export_legacy_dbus_address(
-                pam_handle_t *handle,
-                uid_t uid,
-                const char *runtime) {
-
-        _cleanup_free_ char *s = NULL;
-        int r = PAM_BUF_ERR;
-
-        /* FIXME: We *really* should move the access() check into the
-         * daemons that spawn dbus-daemon, instead of forcing
-         * DBUS_SESSION_BUS_ADDRESS= here. */
-
-        s = strjoin(runtime, "/bus");
-        if (!s)
-                goto error;
-
-        if (access(s, F_OK) < 0)
-                return PAM_SUCCESS;
-
-        s = mfree(s);
-        if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
-                goto error;
-
-        r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
-        if (r != PAM_SUCCESS)
-                goto error;
-
-        return PAM_SUCCESS;
-
-error:
-        pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
-        return r;
-}
-
 static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
         uint64_t val;
         int r;
@@ -208,9 +204,9 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
                         return r;
                 }
         } else {
-                r = parse_percent(limit);
+                r = parse_permille(limit);
                 if (r >= 0) {
-                        r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+                        r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
                         if (r < 0) {
                                 pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
                                 return r;
@@ -231,8 +227,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
         return 0;
 }
 
-static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit)
-{
+static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
         uint64_t val;
         int r;
 
@@ -257,23 +252,62 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con
         uint64_t val;
         int r;
 
-        if (!isempty(limit)) {
-                r = cg_weight_parse(limit, &val);
-                if (r >= 0) {
-                        r = sd_bus_message_append(m, "(sv)", field, "t", val);
-                        if (r < 0) {
-                                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
-                                return r;
-                        }
-                } else if (streq(field, "CPUWeight"))
-                        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
-                else
-                        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
-        }
+        if (isempty(limit))
+                return 0;
+
+        r = cg_weight_parse(limit, &val);
+        if (r >= 0) {
+                r = sd_bus_message_append(m, "(sv)", field, "t", val);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                        return r;
+                }
+        } else if (streq(field, "CPUWeight"))
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
+        else
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
 
         return 0;
 }
 
+static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
+        const char *v;
+
+        assert(handle);
+        assert(key);
+
+        /* Looks for an environment variable, preferrably in the environment block associated with the specified PAM
+         * handle, falling back to the process' block instead. */
+
+        v = pam_getenv(handle, key);
+        if (!isempty(v))
+                return v;
+
+        v = getenv(key);
+        if (!isempty(v))
+                return v;
+
+        return fallback;
+}
+
+static int update_environment(pam_handle_t *handle, const char *key, const char *value) {
+        int r;
+
+        assert(handle);
+        assert(key);
+
+        /* Updates the environment, but only if there's actually a value set. Also, log about errors */
+
+        if (isempty(value))
+                return PAM_SUCCESS;
+
+        r = pam_misc_setenv(handle, key, value, 0);
+        if (r != PAM_SUCCESS)
+                pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s.", key);
+
+        return r;
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
@@ -288,7 +322,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 *remote_user = NULL, *remote_host = NULL,
                 *seat = NULL,
                 *type = NULL, *class = NULL,
-                *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL,
+                *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
                 *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int session_fd = -1, existing, r;
@@ -307,6 +341,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                        argc, argv,
                        &class_pam,
                        &type_pam,
+                       &desktop_pam,
                        &debug) < 0)
                 return PAM_SESSION_ERR;
 
@@ -338,10 +373,6 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         return r;
                 }
 
-                r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
-                if (r != PAM_SUCCESS)
-                        return r;
-
                 return PAM_SUCCESS;
         }
 
@@ -352,55 +383,41 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
         pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
 
-        seat = pam_getenv(handle, "XDG_SEAT");
-        if (isempty(seat))
-                seat = getenv("XDG_SEAT");
-
-        cvtnr = pam_getenv(handle, "XDG_VTNR");
-        if (isempty(cvtnr))
-                cvtnr = getenv("XDG_VTNR");
-
-        type = pam_getenv(handle, "XDG_SESSION_TYPE");
-        if (isempty(type))
-                type = getenv("XDG_SESSION_TYPE");
-        if (isempty(type))
-                type = type_pam;
-
-        class = pam_getenv(handle, "XDG_SESSION_CLASS");
-        if (isempty(class))
-                class = getenv("XDG_SESSION_CLASS");
-        if (isempty(class))
-                class = class_pam;
-
-        desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
-        if (isempty(desktop))
-                desktop = getenv("XDG_SESSION_DESKTOP");
+        seat = getenv_harder(handle, "XDG_SEAT", NULL);
+        cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
+        type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
+        class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
+        desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
 
         tty = strempty(tty);
 
         if (strchr(tty, ':')) {
-                /* A tty with a colon is usually an X11 display,
-                 * placed there to show up in utmp. We rearrange
-                 * things and don't pretend that an X display was a
-                 * tty. */
-
+                /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
+                 * and don't pretend that an X display was a tty. */
                 if (isempty(display))
                         display = tty;
                 tty = NULL;
+
         } else if (streq(tty, "cron")) {
-                /* cron has been setting PAM_TTY to "cron" for a very
-                 * long time and it probably shouldn't stop doing that
-                 * for compatibility reasons. */
+                /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
+                 * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
+                 * (as they otherwise even try to update it!) â€” but cron doesn't actually allocate a TTY for its forked
+                 * off processes.) */
                 type = "unspecified";
                 class = "background";
                 tty = NULL;
+
         } else if (streq(tty, "ssh")) {
-                /* ssh has been setting PAM_TTY to "ssh" for a very
-                 * long time and probably shouldn't stop doing that
-                 * for compatibility reasons. */
+                /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
+                 * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
                 type ="tty";
                 class = "user";
-                tty = NULL;
+                tty = NULL; /* This one is particularly sad, as this means that ssh sessions â€” even though usually
+                             * associated with a pty â€” won't be tracked by their tty in logind. This is because ssh
+                             * does the PAM session registration early for new connections, and registers a pty only
+                             * much later (this is because it doesn't know yet if it needs one at all, as whether to
+                             * register a pty or not is negotiated much later in the protocol). */
+
         } else
                 /* Chop off leading /dev prefix that some clients specify, but others do not. */
                 tty = skip_dev_prefix(tty);
@@ -411,9 +428,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         if (!isempty(display) && !vtnr) {
                 if (isempty(seat))
-                        get_seat_from_display(display, &seat, &vtnr);
+                        (void) get_seat_from_display(display, &seat, &vtnr);
                 else if (streq(seat, "seat0"))
-                        get_seat_from_display(display, NULL, &vtnr);
+                        (void) get_seat_from_display(display, NULL, &vtnr);
         }
 
         if (seat && !streq(seat, "seat0") && vtnr != 0) {
@@ -546,11 +563,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                            "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
                            id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
 
-        r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
-        if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to set session id.");
+        r = update_environment(handle, "XDG_SESSION_ID", id);
+        if (r != PAM_SUCCESS)
                 return r;
-        }
 
         if (original_uid == pw->pw_uid) {
                 /* Don't set $XDG_RUNTIME_DIR if the user we now
@@ -559,34 +574,38 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                  * in privileged apps clobbering the runtime directory
                  * unnecessarily. */
 
-                r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
-                if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
-                        return r;
-                }
-
-                r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
+                r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
                 if (r != PAM_SUCCESS)
                         return r;
         }
 
-        if (!isempty(seat)) {
-                r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
-                if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set seat.");
-                        return r;
-                }
-        }
+        /* Most likely we got the session/type/class from environment variables, but might have gotten the data
+         * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
+         * data is inherited into the session processes, and programs can rely on them to be initialized. */
+
+        r = update_environment(handle, "XDG_SESSION_TYPE", type);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = update_environment(handle, "XDG_SESSION_CLASS", class);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = update_environment(handle, "XDG_SEAT", seat);
+        if (r != PAM_SUCCESS)
+                return r;
 
         if (vtnr > 0) {
                 char buf[DECIMAL_STR_MAX(vtnr)];
                 sprintf(buf, "%u", vtnr);
 
-                r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
-                if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
+                r = update_environment(handle, "XDG_VTNR", buf);
+                if (r != PAM_SUCCESS)
                         return r;
-                }
         }
 
         r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
@@ -628,7 +647,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
         /* Only release session if it wasn't pre-existing when we
          * tried to create it */
-        pam_get_data(handle, "systemd.existing", &existing);
+        (void) pam_get_data(handle, "systemd.existing", &existing);
 
         id = pam_getenv(handle, "XDG_SESSION_ID");
         if (id && !existing) {
index 7f41465ccde423b001b02bb628f05a0a2fc01df7..77e8b161b027eea1c7e9d6c1d3fd6640d938276e 100644 (file)
@@ -1058,7 +1058,7 @@ finish:
 }
 
 int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
+        const char *src, *dest, *host_path, *container_path, *host_basename, *container_basename, *container_dirname;
         _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
         CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE;
         _cleanup_close_ int hostfd = -1;
@@ -1119,16 +1119,14 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
         }
 
         host_basename = basename(host_path);
-        t = strdupa(host_path);
-        host_dirname = dirname(t);
 
         container_basename = basename(container_path);
         t = strdupa(container_path);
         container_dirname = dirname(t);
 
-        hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
+        hostfd = open_parent(host_path, O_CLOEXEC, 0);
         if (hostfd < 0)
-                return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname);
+                return sd_bus_error_set_errnof(error, hostfd, "Failed to open host directory %s: %m", host_path);
 
         if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
                 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
index f3410613445df24d72d50a0b5a0cbd778b7a0c01..b18090f7a133aafd4872575b0cd0265b04c71020 100644 (file)
@@ -1,7 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-  Copyright Â© 2016 Andreas Rammhold <andreas@rammhold.de>
-***/
 
 #include <net/if.h>
 
index d1ac6f6976fba60f1be9f1517c87899c2b28d238..05b3937856eb054d386b364464877c1cff28a870 100644 (file)
@@ -1,10 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-/***
-  Copyright Â© 2016 Andreas Rammhold <andreas@rammhold.de>
-***/
-
 typedef struct Vrf Vrf;
 
 #include "netdev/netdev.h"
index 1b2e9dafde5b2def07abeeceeaf6c5e24636cae1..5616046f4f62c350ae8bfa567981906291aab2d6 100644 (file)
@@ -664,44 +664,46 @@ int dhcp4_configure(Link *link) {
 
         if (!link->dhcp_client) {
                 r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to create DHCP4 client: %m");
         }
 
         r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m");
 
         r = sd_dhcp_client_set_mac(link->dhcp_client,
                                    (const uint8_t *) &link->mac,
                                    sizeof (link->mac), ARPHRD_ETHER);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MAC address: %m");
 
         r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ifindex: %m");
 
         r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set callback: %m");
 
         r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
                                                  link->network->dhcp_broadcast);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for broadcast: %m");
 
         if (link->mtu) {
                 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MTU: %m");
         }
 
         if (link->network->dhcp_use_mtu) {
                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
                                                       SD_DHCP_OPTION_INTERFACE_MTU);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for MTU: %m");
         }
 
         /* NOTE: even if this variable is called "use", it also "sends" PRL
@@ -713,46 +715,47 @@ int dhcp4_configure(Link *link) {
                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
                                                       SD_DHCP_OPTION_STATIC_ROUTE);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for static route: %m");
+
                 r = sd_dhcp_client_set_request_option(link->dhcp_client,
                                                       SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for classless static route: %m");
         }
 
         if (link->network->dhcp_use_ntp) {
                 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for NTP server: %m");
         }
 
         if (link->network->dhcp_use_timezone) {
                 r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for timezone: %m");
         }
 
         r = dhcp4_set_hostname(link);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set hostname: %m");
 
         if (link->network->dhcp_vendor_class_identifier) {
                 r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
                                                                link->network->dhcp_vendor_class_identifier);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
         }
 
         if (link->network->dhcp_user_class) {
                 r = sd_dhcp_client_set_user_class(link->dhcp_client, (const char **) link->network->dhcp_user_class);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m");
         }
 
         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;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m");
         }
 
         switch (link->network->dhcp_client_identifier) {
@@ -766,7 +769,7 @@ int dhcp4_configure(Link *link) {
                                                  duid->raw_data_len > 0 ? duid->raw_data : NULL,
                                                  duid->raw_data_len);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m");
                 break;
         }
         case DHCP_CLIENT_ID_DUID_ONLY: {
@@ -778,7 +781,7 @@ int dhcp4_configure(Link *link) {
                                             duid->raw_data_len > 0 ? duid->raw_data : NULL,
                                             duid->raw_data_len);
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m");
                 break;
         }
         case DHCP_CLIENT_ID_MAC:
@@ -787,7 +790,7 @@ int dhcp4_configure(Link *link) {
                                                  (const uint8_t *) &link->mac,
                                                  sizeof(link->mac));
                 if (r < 0)
-                        return r;
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m");
                 break;
         default:
                 assert_not_reached("Unknown client identifier type.");
index 0fcd2cab3ef0bdf4aff9e58154f4dd0571af211b..e954754c2069770f3352c188d8aad6766b4ab620 100644 (file)
@@ -119,7 +119,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
         Link *link;
         Manager *manager = dhcp6_link->manager;
         union in_addr_union prefix;
-        uint8_t n_prefixes, n_used = 0;
+        uint64_t n_prefixes, n_used = 0;
         _cleanup_free_ char *buf = NULL;
         int r;
 
@@ -132,17 +132,17 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
         if (r < 0)
                 return r;
 
-        n_prefixes = 1 << (64 - pd_prefix_len);
+        n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
 
         (void) in_addr_to_string(AF_INET6, &prefix, &buf);
-        log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u",
+        log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
                        n_prefixes, strnull(buf), pd_prefix_len);
 
         while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
                 Link *assigned_link;
 
                 if (n_used == n_prefixes) {
-                        log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u",
+                        log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
                                        n_used, n_prefixes, strnull(buf), pd_prefix_len);
 
                         return -EAGAIN;
@@ -169,7 +169,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
                                 continue;
 
                 } else
-                        log_link_debug(link, "Assigned prefix %u/%u %s/64 to link",
+                        log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 to link",
                                        n_used + 1, n_prefixes, strnull(buf));
 
                 n_used++;
@@ -181,7 +181,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
 
         if (n_used < n_prefixes) {
                 Route *route;
-                int n = n_used;
+                uint64_t n = n_used;
 
                 r = route_new(&route);
                 if (r < 0)
@@ -457,7 +457,7 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
 }
 
 int dhcp6_configure(Link *link) {
-        sd_dhcp6_client *client = NULL;
+        _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
         const DUID *duid;
         int r;
 
@@ -468,22 +468,24 @@ int dhcp6_configure(Link *link) {
                 return 0;
 
         r = sd_dhcp6_client_new(&client);
+        if (r == -ENOMEM)
+                return log_oom();
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
 
         r = sd_dhcp6_client_attach_event(client, NULL, 0);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
 
         r = sd_dhcp6_client_set_mac(client,
                                     (const uint8_t *) &link->mac,
                                     sizeof (link->mac), ARPHRD_ETHER);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
 
         r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
 
         duid = link_duid(link);
         r = sd_dhcp6_client_set_duid(client,
@@ -491,37 +493,33 @@ int dhcp6_configure(Link *link) {
                                      duid->raw_data_len > 0 ? duid->raw_data : NULL,
                                      duid->raw_data_len);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
 
         r = dhcp6_set_hostname(client, link);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
 
         r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
 
         if (link->network->rapid_commit) {
                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
                 if (r < 0)
-                        goto error;
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
         }
 
         r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
         if (r < 0)
-                goto error;
+                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
 
         if (dhcp6_enable_prefix_delegation(link)) {
                 r = sd_dhcp6_client_set_prefix_delegation(client, true);
                 if (r < 0)
-                        goto error;
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
         }
 
-        link->dhcp6_client = client;
+        link->dhcp6_client = TAKE_PTR(client);
 
         return 0;
-
-error:
-        sd_dhcp6_client_unref(client);
-        return r;
 }
index e22c2fe5784204a42484575dd7bcc5334f1fac54..3792f3d7002e5b9e86ce265d5230c169c201909c 100644 (file)
@@ -1762,7 +1762,7 @@ int manager_set_hostname(Manager *m, const char *hostname) {
                 return log_oom();
 
         if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
-                log_info("Not connected to system bus, not setting hostname.");
+                log_debug("Not connected to system bus, setting hostname later.");
                 return 0;
         }
 
@@ -1810,7 +1810,7 @@ int manager_set_timezone(Manager *m, const char *tz) {
                 return log_oom();
 
         if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
-                log_info("Not connected to system bus, not setting timezone.");
+                log_debug("Not connected to system bus, setting timezone later.");
                 return 0;
         }
 
index 11cd7faa8b6541ace89ec21f7091d2fba6cdd0ac..63a2ba8215e789a135e4d94a21ec7f64cd77da49 100644 (file)
@@ -22,6 +22,7 @@ int main(int argc, char **argv) {
         test_table(bond_primary_reselect, NETDEV_BOND_PRIMARY_RESELECT);
         test_table(bond_xmit_hash_policy, NETDEV_BOND_XMIT_HASH_POLICY);
         test_table(dhcp6_message_status, DHCP6_STATUS);
+        test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE); /* enum starts from 1 */
         test_table(dhcp_use_domains, DHCP_USE_DOMAINS);
         test_table(duplex, DUP);
         test_table(ip6tnl_mode, NETDEV_IP6_TNL_MODE);
index d8a39a69591ef15ff7e59f3a79ee015a7daba3f8..4a3cd29094a8a9695622c54eb1e6c1154680d39e 100644 (file)
@@ -5,12 +5,16 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "mkdir.h"
 #include "mount-util.h"
 #include "nspawn-cgroup.h"
+#include "nspawn-mount.h"
+#include "path-util.h"
 #include "rm-rf.h"
 #include "string-util.h"
 #include "strv.h"
+#include "user-util.h"
 #include "util.h"
 
 static int chown_cgroup_path(const char *path, uid_t uid_shift) {
@@ -71,7 +75,7 @@ int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
         return 0;
 }
 
-int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift) {
+int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
         _cleanup_free_ char *cgroup = NULL;
         char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
         bool undo_mount = false;
@@ -125,7 +129,7 @@ int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift)
         }
 
         fn = strjoina(tree, cgroup);
-        r = chown_cgroup_path(fn, arg_uid_shift);
+        r = chown_cgroup_path(fn, uid_shift);
         if (r < 0)
                 log_error_errno(r, "Failed to chown() cgroup %s: %m", fn);
 finish:
@@ -188,3 +192,416 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
         (void) cg_enable_everywhere(supported, supported, cgroup);
         return 0;
 }
+
+/* Retrieve existing subsystems. This function is called in a new cgroup
+ * namespace.
+ */
+static int get_process_controllers(Set **ret) {
+        _cleanup_set_free_free_ Set *controllers = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(ret);
+
+        controllers = set_new(&string_hash_ops);
+        if (!controllers)
+                return -ENOMEM;
+
+        f = fopen("/proc/self/cgroup", "re");
+        if (!f)
+                return errno == ENOENT ? -ESRCH : -errno;
+
+        for (;;) {
+                _cleanup_free_ char *line = NULL;
+                char *e, *l;
+
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                l = strchr(line, ':');
+                if (!l)
+                        continue;
+
+                l++;
+                e = strchr(l, ':');
+                if (!e)
+                        continue;
+
+                *e = 0;
+
+                if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
+                        continue;
+
+                r = set_put_strdup(controllers, l);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(controllers);
+
+        return 0;
+}
+
+static int mount_legacy_cgroup_hierarchy(
+                const char *dest,
+                const char *controller,
+                const char *hierarchy,
+                bool read_only) {
+
+        const char *to, *fstype, *opts;
+        int r;
+
+        to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
+
+        r = path_is_mount_point(to, dest, 0);
+        if (r < 0 && r != -ENOENT)
+                return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
+        if (r > 0)
+                return 0;
+
+        mkdir_p(to, 0755);
+
+        /* The superblock mount options of the mount point need to be
+         * identical to the hosts', and hence writable... */
+        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
+                fstype = "cgroup2";
+                opts = NULL;
+        } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
+                fstype = "cgroup";
+                opts = "none,name=systemd,xattr";
+        } else {
+                fstype = "cgroup";
+                opts = controller;
+        }
+
+        r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
+        if (r < 0)
+                return r;
+
+        /* ... hence let's only make the bind mount read-only, not the superblock. */
+        if (read_only) {
+                r = mount_verbose(LOG_ERR, NULL, to, NULL,
+                                  MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 1;
+}
+
+/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
+static int mount_legacy_cgns_supported(
+                const char *dest,
+                CGroupUnified unified_requested,
+                bool userns,
+                uid_t uid_shift,
+                uid_t uid_range,
+                const char *selinux_apifs_context) {
+
+        _cleanup_set_free_free_ Set *controllers = NULL;
+        const char *cgroup_root = "/sys/fs/cgroup", *c;
+        int r;
+
+        (void) mkdir_p(cgroup_root, 0755);
+
+        /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
+        r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
+        if (r == 0) {
+                _cleanup_free_ char *options = NULL;
+
+                /* When cgroup namespaces are enabled and user namespaces are
+                 * used then the mount of the cgroupfs is done *inside* the new
+                 * user namespace. We're root in the new user namespace and the
+                 * kernel will happily translate our uid/gid to the correct
+                 * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
+                 * pass uid 0 and not uid_shift to tmpfs_patch_options().
+                 */
+                r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
+                if (r < 0)
+                        return log_oom();
+
+                r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
+                                  MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
+                if (r < 0)
+                        return r;
+        }
+
+        r = cg_all_unified();
+        if (r < 0)
+                return r;
+        if (r > 0)
+                goto skip_controllers;
+
+        r = get_process_controllers(&controllers);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine cgroup controllers: %m");
+
+        for (;;) {
+                _cleanup_free_ const char *controller = NULL;
+
+                controller = set_steal_first(controllers);
+                if (!controller)
+                        break;
+
+                r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
+                if (r < 0)
+                        return r;
+
+                /* When multiple hierarchies are co-mounted, make their
+                 * constituting individual hierarchies a symlink to the
+                 * co-mount.
+                 */
+                c = controller;
+                for (;;) {
+                        _cleanup_free_ char *target = NULL, *tok = NULL;
+
+                        r = extract_first_word(&c, &tok, ",", 0);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
+                        if (r == 0)
+                                break;
+
+                        if (streq(controller, tok))
+                                break;
+
+                        target = prefix_root("/sys/fs/cgroup/", tok);
+                        if (!target)
+                                return log_oom();
+
+                        r = symlink_idempotent(controller, target);
+                        if (r == -EINVAL)
+                                return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
+                }
+        }
+
+skip_controllers:
+        if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+                r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
+                if (r < 0)
+                        return r;
+        }
+
+        r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
+        if (r < 0)
+                return r;
+
+        if (!userns)
+                return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
+                                     MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+
+        return 0;
+}
+
+/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
+static int mount_legacy_cgns_unsupported(
+                const char *dest,
+                CGroupUnified unified_requested,
+                bool userns,
+                uid_t uid_shift,
+                uid_t uid_range,
+                const char *selinux_apifs_context) {
+
+        _cleanup_set_free_free_ Set *controllers = NULL;
+        const char *cgroup_root;
+        int r;
+
+        cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
+
+        (void) mkdir_p(cgroup_root, 0755);
+
+        /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
+        r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
+        if (r == 0) {
+                _cleanup_free_ char *options = NULL;
+
+                r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
+                if (r < 0)
+                        return log_oom();
+
+                r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
+                                  MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
+                if (r < 0)
+                        return r;
+        }
+
+        r = cg_all_unified();
+        if (r < 0)
+                return r;
+        if (r > 0)
+                goto skip_controllers;
+
+        r = cg_kernel_controllers(&controllers);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine cgroup controllers: %m");
+
+        for (;;) {
+                _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
+
+                controller = set_steal_first(controllers);
+                if (!controller)
+                        break;
+
+                origin = prefix_root("/sys/fs/cgroup/", controller);
+                if (!origin)
+                        return log_oom();
+
+                r = readlink_malloc(origin, &combined);
+                if (r == -EINVAL) {
+                        /* Not a symbolic link, but directly a single cgroup hierarchy */
+
+                        r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
+                        if (r < 0)
+                                return r;
+
+                } else if (r < 0)
+                        return log_error_errno(r, "Failed to read link %s: %m", origin);
+                else {
+                        _cleanup_free_ char *target = NULL;
+
+                        target = prefix_root(dest, origin);
+                        if (!target)
+                                return log_oom();
+
+                        /* A symbolic link, a combination of controllers in one hierarchy */
+
+                        if (!filename_is_valid(combined)) {
+                                log_warning("Ignoring invalid combined hierarchy %s.", combined);
+                                continue;
+                        }
+
+                        r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
+                        if (r < 0)
+                                return r;
+
+                        r = symlink_idempotent(combined, target);
+                        if (r == -EINVAL)
+                                return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
+                }
+        }
+
+skip_controllers:
+        if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+                r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
+                if (r < 0)
+                        return r;
+        }
+
+        r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
+        if (r < 0)
+                return r;
+
+        return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
+                             MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+}
+
+static int mount_unified_cgroups(const char *dest) {
+        const char *p;
+        int r;
+
+        assert(dest);
+
+        p = prefix_roota(dest, "/sys/fs/cgroup");
+
+        (void) mkdir_p(p, 0755);
+
+        r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
+        if (r > 0) {
+                p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
+                if (access(p, F_OK) >= 0)
+                        return 0;
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
+
+                log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
+                return -EINVAL;
+        }
+
+        return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+}
+
+int mount_cgroups(
+                const char *dest,
+                CGroupUnified unified_requested,
+                bool userns,
+                uid_t uid_shift,
+                uid_t uid_range,
+                const char *selinux_apifs_context,
+                bool use_cgns) {
+
+        if (unified_requested >= CGROUP_UNIFIED_ALL)
+                return mount_unified_cgroups(dest);
+        if (use_cgns)
+                return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
+
+        return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
+}
+
+static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
+        int r;
+
+        assert(root);
+        assert(own);
+
+        /* Make our own cgroup a (writable) bind mount */
+        r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
+        if (r < 0)
+                return r;
+
+        /* And then remount the systemd cgroup root read-only */
+        return mount_verbose(LOG_ERR, NULL, root, NULL,
+                             MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
+}
+
+int mount_systemd_cgroup_writable(
+                const char *dest,
+                CGroupUnified unified_requested) {
+
+        _cleanup_free_ char *own_cgroup_path = NULL;
+        const char *root, *own;
+        int r;
+
+        assert(dest);
+
+        r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine our own cgroup path: %m");
+
+        /* If we are living in the top-level, then there's nothing to do... */
+        if (path_equal(own_cgroup_path, "/"))
+                return 0;
+
+        if (unified_requested >= CGROUP_UNIFIED_ALL) {
+
+                root = prefix_roota(dest, "/sys/fs/cgroup");
+                own = strjoina(root, own_cgroup_path);
+
+        } else {
+
+                if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+                        root = prefix_roota(dest, "/sys/fs/cgroup/unified");
+                        own = strjoina(root, own_cgroup_path);
+
+                        r = mount_systemd_cgroup_writable_one(root, own);
+                        if (r < 0)
+                                return r;
+                }
+
+                root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
+                own = strjoina(root, own_cgroup_path);
+        }
+
+        return mount_systemd_cgroup_writable_one(root, own);
+}
index 6783c3a39f7b6669307ff313ba9b2db1891537cf..035e8fbd0f53e2125a0f35e45fd12b366fc67683 100644 (file)
@@ -9,3 +9,6 @@
 int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
 int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
 int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested);
+
+int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
+int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
index b5df65e2a46e8ef0e60f8bc2c49ad9cfa699988f..1279b9bb3eacf11a9194b2f81bd2d85a16730fd3 100644 (file)
@@ -327,19 +327,15 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
         return 0;
 }
 
-static int tmpfs_patch_options(
+int tmpfs_patch_options(
                 const char *options,
-                bool userns,
-                uid_t uid_shift, uid_t uid_range,
-                bool patch_ids,
+                uid_t uid_shift,
                 const char *selinux_apifs_context,
                 char **ret) {
 
         char *buf = NULL;
 
-        if ((userns && uid_shift != 0) || patch_ids) {
-                assert(uid_shift != UID_INVALID);
-
+        if (uid_shift != UID_INVALID) {
                 if (asprintf(&buf, "%s%suid=" UID_FMT ",gid=" UID_FMT,
                              strempty(options), options ? "," : "",
                              uid_shift, uid_shift) < 0)
@@ -433,16 +429,14 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
         /* Create mountpoint for cgroups. Otherwise we are not allowed since we
          * remount /sys read-only.
          */
-        if (cg_ns_supported()) {
-                x = prefix_roota(top, "/fs/cgroup");
-                (void) mkdir_p(x, 0755);
-        }
+        x = prefix_roota(top, "/fs/cgroup");
+        (void) mkdir_p(x, 0755);
 
         return mount_verbose(LOG_ERR, NULL, top, NULL,
                              MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL);
 }
 
-static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) {
+static int mkdir_userns(const char *path, mode_t mode, uid_t uid_shift) {
         int r;
 
         assert(path);
@@ -451,10 +445,7 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u
         if (r < 0 && r != -EEXIST)
                 return r;
 
-        if ((mask & MOUNT_USE_USERNS) == 0)
-                return 0;
-
-        if (mask & MOUNT_IN_USERNS)
+        if (uid_shift == UID_INVALID)
                 return 0;
 
         if (lchown(path, uid_shift, uid_shift) < 0)
@@ -463,7 +454,7 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u
         return 0;
 }
 
-static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) {
+static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, uid_t uid_shift) {
         const char *p, *e;
         int r;
 
@@ -490,17 +481,17 @@ static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, Mou
                 if (prefix && path_startswith(prefix, t))
                         continue;
 
-                r = mkdir_userns(t, mode, mask, uid_shift);
+                r = mkdir_userns(t, mode, uid_shift);
                 if (r < 0)
                         return r;
         }
 
-        return mkdir_userns(path, mode, mask, uid_shift);
+        return mkdir_userns(path, mode, uid_shift);
 }
 
 int mount_all(const char *dest,
               MountSettingsMask mount_settings,
-              uid_t uid_shift, uid_t uid_range,
+              uid_t uid_shift,
               const char *selinux_apifs_context) {
 
 #define PROC_INACCESSIBLE(path)                                         \
@@ -634,7 +625,7 @@ int mount_all(const char *dest,
                 if (what && r > 0)
                         continue;
 
-                r = mkdir_userns_p(dest, where, 0755, mount_settings, uid_shift);
+                r = mkdir_userns_p(dest, where, 0755, (use_userns && !in_userns) ? uid_shift : UID_INVALID);
                 if (r < 0 && r != -EEXIST) {
                         if (fatal && r != -EROFS)
                                 return log_error_errno(r, "Failed to create directory %s: %m", where);
@@ -649,10 +640,7 @@ int mount_all(const char *dest,
 
                 o = mount_table[k].options;
                 if (streq_ptr(mount_table[k].type, "tmpfs")) {
-                        if (in_userns)
-                                r = tmpfs_patch_options(o, use_userns, 0, uid_range, true, selinux_apifs_context, &options);
-                        else
-                                r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, false, selinux_apifs_context, &options);
+                        r = tmpfs_patch_options(o, in_userns ? 0 : uid_shift, selinux_apifs_context, &options);
                         if (r < 0)
                                 return log_oom();
                         if (r > 0)
@@ -755,7 +743,7 @@ static int mount_tmpfs(
                         return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
         }
 
-        r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
+        r = tmpfs_patch_options(m->options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 return log_oom();
         options = r > 0 ? buf : m->options;
@@ -860,419 +848,6 @@ int mount_custom(
         return 0;
 }
 
-/* Retrieve existing subsystems. This function is called in a new cgroup
- * namespace.
- */
-static int get_process_controllers(Set **ret) {
-        _cleanup_set_free_free_ Set *controllers = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        int r;
-
-        assert(ret);
-
-        controllers = set_new(&string_hash_ops);
-        if (!controllers)
-                return -ENOMEM;
-
-        f = fopen("/proc/self/cgroup", "re");
-        if (!f)
-                return errno == ENOENT ? -ESRCH : -errno;
-
-        for (;;) {
-                _cleanup_free_ char *line = NULL;
-                char *e, *l;
-
-                r = read_line(f, LONG_LINE_MAX, &line);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                l = strchr(line, ':');
-                if (!l)
-                        continue;
-
-                l++;
-                e = strchr(l, ':');
-                if (!e)
-                        continue;
-
-                *e = 0;
-
-                if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
-                        continue;
-
-                r = set_put_strdup(controllers, l);
-                if (r < 0)
-                        return r;
-        }
-
-        *ret = TAKE_PTR(controllers);
-
-        return 0;
-}
-
-static int mount_legacy_cgroup_hierarchy(
-                const char *dest,
-                const char *controller,
-                const char *hierarchy,
-                bool read_only) {
-
-        const char *to, *fstype, *opts;
-        int r;
-
-        to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
-
-        r = path_is_mount_point(to, dest, 0);
-        if (r < 0 && r != -ENOENT)
-                return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
-        if (r > 0)
-                return 0;
-
-        mkdir_p(to, 0755);
-
-        /* The superblock mount options of the mount point need to be
-         * identical to the hosts', and hence writable... */
-        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
-                fstype = "cgroup2";
-                opts = NULL;
-        } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
-                fstype = "cgroup";
-                opts = "none,name=systemd,xattr";
-        } else {
-                fstype = "cgroup";
-                opts = controller;
-        }
-
-        r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
-        if (r < 0)
-                return r;
-
-        /* ... hence let's only make the bind mount read-only, not the superblock. */
-        if (read_only) {
-                r = mount_verbose(LOG_ERR, NULL, to, NULL,
-                                  MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
-                if (r < 0)
-                        return r;
-        }
-
-        return 1;
-}
-
-/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
-static int mount_legacy_cgns_supported(
-                const char *dest,
-                CGroupUnified unified_requested,
-                bool userns,
-                uid_t uid_shift,
-                uid_t uid_range,
-                const char *selinux_apifs_context) {
-
-        _cleanup_set_free_free_ Set *controllers = NULL;
-        const char *cgroup_root = "/sys/fs/cgroup", *c;
-        int r;
-
-        (void) mkdir_p(cgroup_root, 0755);
-
-        /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
-        r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
-        if (r == 0) {
-                _cleanup_free_ char *options = NULL;
-
-                /* When cgroup namespaces are enabled and user namespaces are
-                 * used then the mount of the cgroupfs is done *inside* the new
-                 * user namespace. We're root in the new user namespace and the
-                 * kernel will happily translate our uid/gid to the correct
-                 * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
-                 * pass uid 0 and not uid_shift to tmpfs_patch_options().
-                 */
-                r = tmpfs_patch_options("mode=755", userns, 0, uid_range, true, selinux_apifs_context, &options);
-                if (r < 0)
-                        return log_oom();
-
-                r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
-                                  MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
-                if (r < 0)
-                        return r;
-        }
-
-        r = cg_all_unified();
-        if (r < 0)
-                return r;
-        if (r > 0)
-                goto skip_controllers;
-
-        r = get_process_controllers(&controllers);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine cgroup controllers: %m");
-
-        for (;;) {
-                _cleanup_free_ const char *controller = NULL;
-
-                controller = set_steal_first(controllers);
-                if (!controller)
-                        break;
-
-                r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
-                if (r < 0)
-                        return r;
-
-                /* When multiple hierarchies are co-mounted, make their
-                 * constituting individual hierarchies a symlink to the
-                 * co-mount.
-                 */
-                c = controller;
-                for (;;) {
-                        _cleanup_free_ char *target = NULL, *tok = NULL;
-
-                        r = extract_first_word(&c, &tok, ",", 0);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
-                        if (r == 0)
-                                break;
-
-                        if (streq(controller, tok))
-                                break;
-
-                        target = prefix_root("/sys/fs/cgroup/", tok);
-                        if (!target)
-                                return log_oom();
-
-                        r = symlink_idempotent(controller, target);
-                        if (r == -EINVAL)
-                                return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
-                }
-        }
-
-skip_controllers:
-        if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
-                r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
-                if (r < 0)
-                        return r;
-        }
-
-        r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
-        if (r < 0)
-                return r;
-
-        if (!userns)
-                return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
-                                     MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
-
-        return 0;
-}
-
-/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
-static int mount_legacy_cgns_unsupported(
-                const char *dest,
-                CGroupUnified unified_requested,
-                bool userns,
-                uid_t uid_shift,
-                uid_t uid_range,
-                const char *selinux_apifs_context) {
-
-        _cleanup_set_free_free_ Set *controllers = NULL;
-        const char *cgroup_root;
-        int r;
-
-        cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
-
-        (void) mkdir_p(cgroup_root, 0755);
-
-        /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
-        r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
-        if (r == 0) {
-                _cleanup_free_ char *options = NULL;
-
-                r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, false, selinux_apifs_context, &options);
-                if (r < 0)
-                        return log_oom();
-
-                r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
-                                  MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
-                if (r < 0)
-                        return r;
-        }
-
-        r = cg_all_unified();
-        if (r < 0)
-                return r;
-        if (r > 0)
-                goto skip_controllers;
-
-        r = cg_kernel_controllers(&controllers);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine cgroup controllers: %m");
-
-        for (;;) {
-                _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
-
-                controller = set_steal_first(controllers);
-                if (!controller)
-                        break;
-
-                origin = prefix_root("/sys/fs/cgroup/", controller);
-                if (!origin)
-                        return log_oom();
-
-                r = readlink_malloc(origin, &combined);
-                if (r == -EINVAL) {
-                        /* Not a symbolic link, but directly a single cgroup hierarchy */
-
-                        r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
-                        if (r < 0)
-                                return r;
-
-                } else if (r < 0)
-                        return log_error_errno(r, "Failed to read link %s: %m", origin);
-                else {
-                        _cleanup_free_ char *target = NULL;
-
-                        target = prefix_root(dest, origin);
-                        if (!target)
-                                return log_oom();
-
-                        /* A symbolic link, a combination of controllers in one hierarchy */
-
-                        if (!filename_is_valid(combined)) {
-                                log_warning("Ignoring invalid combined hierarchy %s.", combined);
-                                continue;
-                        }
-
-                        r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
-                        if (r < 0)
-                                return r;
-
-                        r = symlink_idempotent(combined, target);
-                        if (r == -EINVAL)
-                                return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
-                }
-        }
-
-skip_controllers:
-        if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
-                r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
-                if (r < 0)
-                        return r;
-        }
-
-        r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
-        if (r < 0)
-                return r;
-
-        return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
-                             MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
-}
-
-static int mount_unified_cgroups(const char *dest) {
-        const char *p;
-        int r;
-
-        assert(dest);
-
-        p = prefix_roota(dest, "/sys/fs/cgroup");
-
-        (void) mkdir_p(p, 0755);
-
-        r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
-        if (r > 0) {
-                p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
-                if (access(p, F_OK) >= 0)
-                        return 0;
-                if (errno != ENOENT)
-                        return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
-
-                log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
-                return -EINVAL;
-        }
-
-        return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
-}
-
-int mount_cgroups(
-                const char *dest,
-                CGroupUnified unified_requested,
-                bool userns,
-                uid_t uid_shift,
-                uid_t uid_range,
-                const char *selinux_apifs_context,
-                bool use_cgns) {
-
-        if (unified_requested >= CGROUP_UNIFIED_ALL)
-                return mount_unified_cgroups(dest);
-        if (use_cgns)
-                return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
-
-        return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
-}
-
-static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
-        int r;
-
-        assert(root);
-        assert(own);
-
-        /* Make our own cgroup a (writable) bind mount */
-        r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
-        if (r < 0)
-                return r;
-
-        /* And then remount the systemd cgroup root read-only */
-        return mount_verbose(LOG_ERR, NULL, root, NULL,
-                             MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
-}
-
-int mount_systemd_cgroup_writable(
-                const char *dest,
-                CGroupUnified unified_requested) {
-
-        _cleanup_free_ char *own_cgroup_path = NULL;
-        const char *root, *own;
-        int r;
-
-        assert(dest);
-
-        r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine our own cgroup path: %m");
-
-        /* If we are living in the top-level, then there's nothing to do... */
-        if (path_equal(own_cgroup_path, "/"))
-                return 0;
-
-        if (unified_requested >= CGROUP_UNIFIED_ALL) {
-
-                root = prefix_roota(dest, "/sys/fs/cgroup");
-                own = strjoina(root, own_cgroup_path);
-
-        } else {
-
-                if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
-                        root = prefix_roota(dest, "/sys/fs/cgroup/unified");
-                        own = strjoina(root, own_cgroup_path);
-
-                        r = mount_systemd_cgroup_writable_one(root, own);
-                        if (r < 0)
-                                return r;
-                }
-
-                root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
-                own = strjoina(root, own_cgroup_path);
-        }
-
-        return mount_systemd_cgroup_writable_one(root, own);
-}
-
 int setup_volatile_state(
                 const char *directory,
                 VolatileMode mode,
@@ -1301,7 +876,7 @@ int setup_volatile_state(
                 return log_error_errno(errno, "Failed to create %s: %m", directory);
 
         options = "mode=755";
-        r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
+        r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 return log_oom();
         if (r > 0)
@@ -1334,7 +909,7 @@ int setup_volatile(
                 return log_error_errno(errno, "Failed to create temporary directory: %m");
 
         options = "mode=755";
-        r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
+        r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 return log_oom();
         if (r > 0)
index b823282cbd92542cb4c0143812c19b6b8ad5494e..db55759ec3764da70d8375359360bfe91174c93a 100644 (file)
@@ -43,12 +43,9 @@ int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only);
 int tmpfs_mount_parse(CustomMount **l, size_t *n, const char *s);
 int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only);
 
-int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, const char *selinux_apifs_context);
 int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
 
-int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
-int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
-
 int mount_custom(const char *dest, CustomMount *mounts, size_t n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
 
 int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
@@ -56,3 +53,5 @@ int setup_volatile_state(const char *directory, VolatileMode mode, bool userns,
 
 int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s);
 int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
+
+int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
index 9ea1c87590f55c8d4267dfc26f979912214ff037..9a2f72bf2936206af6cea40c57bbf31cfa8e4053 100644 (file)
@@ -2558,7 +2558,6 @@ static int inner_child(
         r = mount_all(NULL,
                       arg_mount_settings | MOUNT_IN_USERNS,
                       arg_uid_shift,
-                      arg_uid_range,
                       arg_selinux_apifs_context);
         if (r < 0)
                 return r;
@@ -2990,7 +2989,6 @@ static int outer_child(
         r = mount_all(directory,
                       arg_mount_settings,
                       arg_uid_shift,
-                      arg_uid_range,
                       arg_selinux_apifs_context);
         if (r < 0)
                 return r;
index f82ce59f2cd04b270c29c2df02d5e9e63cfe3ec5..5abc0c91bf29a5f568bcc4d8741af00428326f54 100644 (file)
@@ -45,6 +45,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
         char *r_name;
         unsigned n;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -64,7 +65,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
 
                 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
                 if (n_addresses <= 0) {
-                        *errnop = ENOENT;
                         *h_errnop = HOST_NOT_FOUND;
                         return NSS_STATUS_NOTFOUND;
                 }
@@ -81,7 +81,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
 
                 /* We respond to our local host name, our hostname suffixed with a single dot. */
                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
-                        *errnop = ENOENT;
                         *h_errnop = HOST_NOT_FOUND;
                         return NSS_STATUS_NOTFOUND;
                 }
@@ -157,8 +156,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
         if (ttlp)
                 *ttlp = 0;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
         *h_errnop = NETDB_SUCCESS;
         h_errno = 0;
 
@@ -286,8 +285,8 @@ static enum nss_status fill_in_hostent(
         if (canonp)
                 *canonp = r_name;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
         *h_errnop = NETDB_SUCCESS;
         h_errno = 0;
 
@@ -309,6 +308,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
         uint32_t local_address_ipv4 = 0;
         int n_addresses = 0;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -334,7 +334,6 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
 
                 n_addresses = local_gateways(NULL, 0, af, &addresses);
                 if (n_addresses <= 0) {
-                        *errnop = ENOENT;
                         *h_errnop = HOST_NOT_FOUND;
                         return NSS_STATUS_NOTFOUND;
                 }
@@ -350,7 +349,6 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
                 }
 
                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
-                        *errnop = ENOENT;
                         *h_errnop = HOST_NOT_FOUND;
                         return NSS_STATUS_NOTFOUND;
                 }
@@ -393,6 +391,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
         bool additional_from_hostname = false;
         unsigned n;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(addr);
@@ -455,7 +454,6 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
                 }
         }
 
-        *errnop = ENOENT;
         *h_errnop = HOST_NOT_FOUND;
         return NSS_STATUS_NOTFOUND;
 
index 8d6caa0ada97787aa94b0f78825583d8f48d2497..3d1fc2835331b65835cc56106246455905c52f59 100644 (file)
@@ -63,6 +63,20 @@ static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
         return 0;
 }
 
+static bool avoid_deadlock(void) {
+
+        /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
+         * code activating systemd-machined.service. After all, we shouldn't synchronously do lookups to
+         * systemd-machined if we are required to finish before it can be started. This of course won't detect all
+         * possible dead locks of this kind, but it should work for the most obvious cases. */
+
+        if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
+                return false;
+
+        return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-machined.service") &&
+               streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
+}
+
 enum nss_status _nss_mymachines_gethostbyname4_r(
                 const char *name,
                 struct gaih_addrtuple **pat,
@@ -80,6 +94,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
         char *r_name;
         int n_ifindices, r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -102,6 +117,11 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
                 goto fail;
         }
 
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -126,7 +146,6 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
                 goto fail;
 
         if (c <= 0) {
-                *errnop = ESRCH;
                 *h_errnop = HOST_NOT_FOUND;
                 return NSS_STATUS_NOTFOUND;
         }
@@ -200,8 +219,8 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
         if (ttlp)
                 *ttlp = 0;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
         *h_errnop = NETDB_SUCCESS;
         h_errno = 0;
 
@@ -230,6 +249,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
         size_t l, idx, ms, alen;
         int r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -254,6 +274,11 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
                 goto fail;
         }
 
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -278,7 +303,6 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
                 goto fail;
 
         if (c <= 0) {
-                *errnop = ENOENT;
                 *h_errnop = HOST_NOT_FOUND;
                 return NSS_STATUS_NOTFOUND;
         }
@@ -364,8 +388,8 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
         if (canonp)
                 *canonp = r_name;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
         *h_errnop = NETDB_SUCCESS;
         h_errno = 0;
 
@@ -394,6 +418,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
         size_t l;
         int r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -401,28 +426,33 @@ enum nss_status _nss_mymachines_getpwnam_r(
 
         p = startswith(name, "vu-");
         if (!p)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         e = strrchr(p, '-');
         if (!e || e == p)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         r = parse_uid(e + 1, &uid);
         if (r < 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         machine = strndupa(p, e - p);
         if (!machine_name_is_valid(machine))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
                 /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
                  * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
                  * running on the host. */
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
+
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
 
         r = sd_bus_open_system(&bus);
         if (r < 0)
@@ -439,7 +469,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
                                machine, (uint32_t) uid);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
 
                 goto fail;
         }
@@ -450,7 +480,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
 
         /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */
         if (mapped < HOST_UID_LIMIT || mapped == uid)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         l = strlen(name);
         if (buflen < l+1) {
@@ -468,13 +498,8 @@ enum nss_status _nss_mymachines_getpwnam_r(
         pwd->pw_dir = (char*) "/";
         pwd->pw_shell = (char*) "/sbin/nologin";
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -493,17 +518,23 @@ enum nss_status _nss_mymachines_getpwuid_r(
         uint32_t mapped;
         int r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         if (!uid_is_valid(uid))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         /* We consider all uids < 65536 host uids */
         if (uid < HOST_UID_LIMIT)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
+
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
 
         r = sd_bus_open_system(&bus);
         if (r < 0)
@@ -520,7 +551,7 @@ enum nss_status _nss_mymachines_getpwuid_r(
                                (uint32_t) uid);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
 
                 goto fail;
         }
@@ -530,7 +561,7 @@ enum nss_status _nss_mymachines_getpwuid_r(
                 goto fail;
 
         if (mapped == uid)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
                 *errnop = ERANGE;
@@ -545,13 +576,8 @@ enum nss_status _nss_mymachines_getpwuid_r(
         pwd->pw_dir = (char*) "/";
         pwd->pw_shell = (char*) "/sbin/nologin";
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -574,6 +600,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
         size_t l;
         int r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -581,25 +608,30 @@ enum nss_status _nss_mymachines_getgrnam_r(
 
         p = startswith(name, "vg-");
         if (!p)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         e = strrchr(p, '-');
         if (!e || e == p)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (e - p > HOST_NAME_MAX - 1)  /* -1 for the last dash */
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         r = parse_gid(e + 1, &gid);
         if (r < 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         machine = strndupa(p, e - p);
         if (!machine_name_is_valid(machine))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
+
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
 
         r = sd_bus_open_system(&bus);
         if (r < 0)
@@ -616,7 +648,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
                                machine, (uint32_t) gid);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
 
                 goto fail;
         }
@@ -626,7 +658,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
                 goto fail;
 
         if (mapped < HOST_GID_LIMIT || mapped == gid)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         l = sizeof(char*) + strlen(name) + 1;
         if (buflen < l) {
@@ -642,13 +674,8 @@ enum nss_status _nss_mymachines_getgrnam_r(
         gr->gr_passwd = (char*) "*"; /* locked */
         gr->gr_mem = (char**) buffer;
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -667,17 +694,23 @@ enum nss_status _nss_mymachines_getgrgid_r(
         uint32_t mapped;
         int r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         if (!gid_is_valid(gid))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         /* We consider all gids < 65536 host gids */
         if (gid < HOST_GID_LIMIT)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
+
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
 
         r = sd_bus_open_system(&bus);
         if (r < 0)
@@ -694,7 +727,7 @@ enum nss_status _nss_mymachines_getgrgid_r(
                                (uint32_t) gid);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
 
                 goto fail;
         }
@@ -704,7 +737,7 @@ enum nss_status _nss_mymachines_getgrgid_r(
                 goto fail;
 
         if (mapped == gid)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (buflen < sizeof(char*) + 1) {
                 *errnop = ERANGE;
@@ -722,13 +755,8 @@ enum nss_status _nss_mymachines_getgrgid_r(
         gr->gr_passwd = (char*) "*"; /* locked */
         gr->gr_mem = (char**) buffer;
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
index eb3d2d977f45f42c764389c85ee03e8a5c76e9bb..a28b5d8ba81db5a2bcf52e418649e17efe1d9b04 100644 (file)
@@ -91,6 +91,20 @@ static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
         return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
 }
 
+static bool avoid_deadlock(void) {
+
+        /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
+         * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
+         * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
+         * possible dead locks of this kind, but it should work for the most obvious cases. */
+
+        if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
+                return false;
+
+        return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
+               streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
+}
+
 enum nss_status _nss_resolve_gethostbyname4_r(
                 const char *name,
                 struct gaih_addrtuple **pat,
@@ -108,6 +122,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
         char *r_name;
         int c, r, i = 0;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -116,6 +131,11 @@ enum nss_status _nss_resolve_gethostbyname4_r(
         assert(errnop);
         assert(h_errnop);
 
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -140,20 +160,15 @@ enum nss_status _nss_resolve_gethostbyname4_r(
 
         r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
         if (r < 0) {
-                if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
-                        *errnop = ESRCH;
-                        *h_errnop = HOST_NOT_FOUND;
-                        return NSS_STATUS_NOTFOUND;
-                }
+                if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+                    !bus_error_shall_fallback(&error))
+                        goto not_found;
 
                 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
                    allowing falling back to other nss modules. Treat all other error conditions as
                    NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
                    case so that the nsswitch.conf configuration can distuingish such executed but
                    negative replies from complete failure to talk to resolved). */
-                if (!bus_error_shall_fallback(&error))
-                        ret = NSS_STATUS_NOTFOUND;
-
                 goto fail;
         }
 
@@ -162,11 +177,8 @@ enum nss_status _nss_resolve_gethostbyname4_r(
                 r = c;
                 goto fail;
         }
-        if (c == 0) {
-                *errnop = ESRCH;
-                *h_errnop = HOST_NOT_FOUND;
-                return NSS_STATUS_NOTFOUND;
-        }
+        if (c == 0)
+                goto not_found;
 
         if (isempty(canonical))
                 canonical = name;
@@ -247,8 +259,8 @@ enum nss_status _nss_resolve_gethostbyname4_r(
         if (ttlp)
                 *ttlp = 0;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
         *h_errnop = NETDB_SUCCESS;
         h_errno = 0;
 
@@ -258,6 +270,10 @@ fail:
         *errnop = -r;
         *h_errnop = NO_RECOVERY;
         return ret;
+
+not_found:
+        *h_errnop = HOST_NOT_FOUND;
+        return NSS_STATUS_NOTFOUND;
 }
 
 enum nss_status _nss_resolve_gethostbyname3_r(
@@ -278,6 +294,7 @@ enum nss_status _nss_resolve_gethostbyname3_r(
         const char *canonical;
         int c, r, i = 0;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -294,6 +311,11 @@ enum nss_status _nss_resolve_gethostbyname3_r(
                 goto fail;
         }
 
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -318,14 +340,9 @@ enum nss_status _nss_resolve_gethostbyname3_r(
 
         r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
         if (r < 0) {
-                if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
-                        *errnop = ESRCH;
-                        *h_errnop = HOST_NOT_FOUND;
-                        return NSS_STATUS_NOTFOUND;
-                }
-
-                if (!bus_error_shall_fallback(&error))
-                        ret = NSS_STATUS_NOTFOUND;
+                if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+                    !bus_error_shall_fallback(&error))
+                        goto not_found;
 
                 goto fail;
         }
@@ -335,11 +352,8 @@ enum nss_status _nss_resolve_gethostbyname3_r(
                 r = c;
                 goto fail;
         }
-        if (c == 0) {
-                *errnop = ESRCH;
-                *h_errnop = HOST_NOT_FOUND;
-                return NSS_STATUS_NOTFOUND;
-        }
+        if (c == 0)
+                goto not_found;
 
         if (isempty(canonical))
                 canonical = name;
@@ -427,23 +441,27 @@ enum nss_status _nss_resolve_gethostbyname3_r(
         result->h_length = alen;
         result->h_addr_list = (char**) r_addr_list;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
-        *h_errnop = NETDB_SUCCESS;
-        h_errno = 0;
-
         if (ttlp)
                 *ttlp = 0;
 
         if (canonp)
                 *canonp = r_name;
 
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
+        *h_errnop = NETDB_SUCCESS;
+        h_errno = 0;
+
         return NSS_STATUS_SUCCESS;
 
 fail:
         *errnop = -r;
         *h_errnop = NO_RECOVERY;
         return ret;
+
+not_found:
+        *h_errnop = HOST_NOT_FOUND;
+        return NSS_STATUS_NOTFOUND;
 }
 
 enum nss_status _nss_resolve_gethostbyaddr2_r(
@@ -464,6 +482,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
         const char *n;
         int r, ifindex;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(addr);
@@ -484,6 +503,11 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
                 return NSS_STATUS_UNAVAIL;
         }
 
+        if (avoid_deadlock()) {
+                r = -EDEADLK;
+                goto fail;
+        }
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -516,14 +540,9 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
 
         r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
         if (r < 0) {
-                if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
-                        *errnop = ESRCH;
-                        *h_errnop = HOST_NOT_FOUND;
-                        return NSS_STATUS_NOTFOUND;
-                }
-
-                if (!bus_error_shall_fallback(&error))
-                        ret = NSS_STATUS_NOTFOUND;
+                if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+                    !bus_error_shall_fallback(&error))
+                        goto not_found;
 
                 goto fail;
         }
@@ -549,11 +568,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
         if (r < 0)
                 return r;
 
-        if (c <= 0) {
-                *errnop = ESRCH;
-                *h_errnop = HOST_NOT_FOUND;
-                return NSS_STATUS_NOTFOUND;
-        }
+        if (c <= 0)
+                goto not_found;
 
         ms += ALIGN(len) +              /* the address */
               2 * sizeof(char*) +       /* pointers to the address, plus trailing NULL */
@@ -612,8 +628,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
         if (ttlp)
                 *ttlp = 0;
 
-        /* Explicitly reset all error variables */
-        *errnop = 0;
+        /* Explicitly reset both *h_errnop and h_errno to work around
+         * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
         *h_errnop = NETDB_SUCCESS;
         h_errno = 0;
 
@@ -623,6 +639,10 @@ fail:
         *errnop = -r;
         *h_errnop = NO_RECOVERY;
         return ret;
+
+not_found:
+        *h_errnop = HOST_NOT_FOUND;
+        return NSS_STATUS_NOTFOUND;
 }
 
 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
index f516b84c63891be7e4695bcb26603da643a74399..f554828d49a60963544a49b0db70696df983d713 100644 (file)
@@ -145,6 +145,7 @@ enum nss_status _nss_systemd_getpwnam_r(
         size_t l;
         int bypass, r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
@@ -153,26 +154,24 @@ enum nss_status _nss_systemd_getpwnam_r(
         /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
          * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
         if (!valid_user_group_name(name))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
                 if (streq(name, root_passwd.pw_name)) {
                         *pwd = root_passwd;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
                 if (synthesize_nobody() &&
                     streq(name, nobody_passwd.pw_name)) {
                         *pwd = nobody_passwd;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
         }
 
         /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
         if (bypass <= 0) {
@@ -184,7 +183,7 @@ enum nss_status _nss_systemd_getpwnam_r(
         if (bypass > 0) {
                 r = direct_lookup_name(name, (uid_t*) &translated);
                 if (r == -ENOENT)
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
                 if (r < 0)
                         goto fail;
         } else {
@@ -199,7 +198,7 @@ enum nss_status _nss_systemd_getpwnam_r(
                                        name);
                 if (r < 0) {
                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
-                                goto not_found;
+                                return NSS_STATUS_NOTFOUND;
 
                         goto fail;
                 }
@@ -225,13 +224,8 @@ enum nss_status _nss_systemd_getpwnam_r(
         pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
         pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -251,31 +245,30 @@ enum nss_status _nss_systemd_getpwuid_r(
         size_t l;
         int bypass, r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         if (!uid_is_valid(uid))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
                 if (uid == root_passwd.pw_uid) {
                         *pwd = root_passwd;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
                 if (synthesize_nobody() &&
                     uid == nobody_passwd.pw_uid) {
                         *pwd = nobody_passwd;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
         }
 
         if (!uid_is_dynamic(uid))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
         if (bypass <= 0) {
@@ -287,7 +280,7 @@ enum nss_status _nss_systemd_getpwuid_r(
         if (bypass > 0) {
                 r = direct_lookup_uid(uid, &direct);
                 if (r == -ENOENT)
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
                 if (r < 0)
                         goto fail;
 
@@ -305,7 +298,7 @@ enum nss_status _nss_systemd_getpwuid_r(
                                        (uint32_t) uid);
                 if (r < 0) {
                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
-                                goto not_found;
+                                return NSS_STATUS_NOTFOUND;
 
                         goto fail;
                 }
@@ -331,13 +324,8 @@ enum nss_status _nss_systemd_getpwuid_r(
         pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
         pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -358,31 +346,30 @@ enum nss_status _nss_systemd_getgrnam_r(
         size_t l;
         int bypass, r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
         assert(gr);
 
         if (!valid_user_group_name(name))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         /* Synthesize records for root and nobody, in case they are missing form /etc/group */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
                 if (streq(name, root_group.gr_name)) {
                         *gr = root_group;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
                 if (synthesize_nobody() &&
                     streq(name, nobody_group.gr_name)) {
                         *gr = nobody_group;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
         }
 
         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
         if (bypass <= 0) {
@@ -394,7 +381,7 @@ enum nss_status _nss_systemd_getgrnam_r(
         if (bypass > 0) {
                 r = direct_lookup_name(name, (uid_t*) &translated);
                 if (r == -ENOENT)
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
                 if (r < 0)
                         goto fail;
         } else {
@@ -409,7 +396,7 @@ enum nss_status _nss_systemd_getgrnam_r(
                                        name);
                 if (r < 0) {
                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
-                                goto not_found;
+                                return NSS_STATUS_NOTFOUND;
 
                         goto fail;
                 }
@@ -433,13 +420,8 @@ enum nss_status _nss_systemd_getgrnam_r(
         gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
         gr->gr_mem = (char**) buffer;
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -459,31 +441,30 @@ enum nss_status _nss_systemd_getgrgid_r(
         size_t l;
         int bypass, r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         if (!gid_is_valid(gid))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         /* Synthesize records for root and nobody, in case they are missing from /etc/group */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
                 if (gid == root_group.gr_gid) {
                         *gr = root_group;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
                 if (synthesize_nobody() &&
                     gid == nobody_group.gr_gid) {
                         *gr = nobody_group;
-                        *errnop = 0;
                         return NSS_STATUS_SUCCESS;
                 }
         }
 
         if (!gid_is_dynamic(gid))
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                goto not_found;
+                return NSS_STATUS_NOTFOUND;
 
         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
         if (bypass <= 0) {
@@ -495,7 +476,7 @@ enum nss_status _nss_systemd_getgrgid_r(
         if (bypass > 0) {
                 r = direct_lookup_uid(gid, &direct);
                 if (r == -ENOENT)
-                        goto not_found;
+                        return NSS_STATUS_NOTFOUND;
                 if (r < 0)
                         goto fail;
 
@@ -513,7 +494,7 @@ enum nss_status _nss_systemd_getgrgid_r(
                                        (uint32_t) gid);
                 if (r < 0) {
                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
-                                goto not_found;
+                                return NSS_STATUS_NOTFOUND;
 
                         goto fail;
                 }
@@ -537,13 +518,8 @@ enum nss_status _nss_systemd_getgrgid_r(
         gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
         gr->gr_mem = (char**) buffer;
 
-        *errnop = 0;
         return NSS_STATUS_SUCCESS;
 
-not_found:
-        *errnop = 0;
-        return NSS_STATUS_NOTFOUND;
-
 fail:
         *errnop = -r;
         return NSS_STATUS_UNAVAIL;
@@ -598,6 +574,7 @@ static void systemd_endent(GetentData *data) {
 }
 
 static enum nss_status nss_systemd_endent(GetentData *p) {
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert_se(pthread_mutex_lock(&p->mutex) == 0);
@@ -668,6 +645,7 @@ static enum nss_status systemd_setent(GetentData *p) {
         uid_t id;
         int bypass, r;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(p);
@@ -750,6 +728,7 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz
         UserEntry *p;
         size_t len;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(result);
@@ -778,7 +757,6 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz
                 break;
         }
         if (!p) {
-                *errnop = ENOENT;
                 ret = NSS_STATUS_NOTFOUND;
                 goto finalize;
         }
@@ -801,6 +779,7 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size
         UserEntry *p;
         size_t len;
 
+        PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(result);
@@ -827,7 +806,6 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size
                 break;
         }
         if (!p) {
-                *errnop = ENOENT;
                 ret = NSS_STATUS_NOTFOUND;
                 goto finalize;
         }
index d2053cd391d78fbf07170269da27ba41ef75b24f..6f1aa28933518ca5276cd68ce2ef1826bec6b594 100644 (file)
@@ -24,8 +24,8 @@
 #include "path-util.h"
 #include "strv.h"
 
-const char *arg_target = NULL;
-bool arg_dry_run = false;
+static const char *arg_target = NULL;
+static bool arg_dry_run = false;
 
 static int resize_ext4(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) {
         assert((uint64_t) (int) blocksize == blocksize);
index 15f3835d5570526b13c7fb5efdf357bcf081889d..92b67b6333d764ce474aeb05a7085fcfebd178ae 100644 (file)
@@ -63,6 +63,7 @@ systemd_resolved_sources = files('''
         resolved-dns-stub.c
         resolved-etc-hosts.h
         resolved-etc-hosts.c
+        resolved-dnstls.h
 '''.split())
 
 resolvectl_sources = files('''
@@ -141,7 +142,17 @@ systemd_resolved_sources += [resolved_gperf_c, resolved_dnssd_gperf_c]
 
 systemd_resolved_dependencies = [threads, libgpg_error, libm, libidn]
 if conf.get('ENABLE_DNS_OVER_TLS') == 1
-        systemd_resolved_dependencies += [libgnutls]
+        if conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1
+                systemd_resolved_sources += files('resolved-dnstls-gnutls.c',
+                                                  'resolved-dnstls-gnutls.h')
+                systemd_resolved_dependencies += libgnutls
+        elif conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1
+                systemd_resolved_sources += files('resolved-dnstls-openssl.c',
+                                                  'resolved-dnstls-openssl.h')
+                systemd_resolved_dependencies += libopenssl
+        else
+                error('unknown dependency for supporting DNS-over-TLS')
+        endif
 endif
 
 if conf.get('ENABLE_RESOLVE') == 1
@@ -183,6 +194,16 @@ tests += [
           libm],
          'ENABLE_RESOLVE'],
 
+        [['src/resolve/test-resolved-etc-hosts.c',
+          'src/resolve/resolved-etc-hosts.c',
+          'src/resolve/resolved-etc-hosts.h'],
+         [libsystemd_resolve_core,
+          libshared],
+         [libgcrypt,
+          libgpg_error,
+          libm],
+         'ENABLE_RESOLVE'],
+
         [['src/resolve/test-resolved-packet.c',
           dns_type_headers],
          [libsystemd_resolve_core,
index 5476ca2dbd5d3b52a9b9e90f9cca6b102d84cc6a..eff3dbb2752daab006c4544a34338ea79ee96077 100644 (file)
@@ -81,8 +81,7 @@ int dns_server_new(
         s->linked = true;
 
 #if ENABLE_DNS_OVER_TLS
-        /* Do not verify cerificate */
-        gnutls_certificate_allocate_credentials(&s->tls_cert_cred);
+        dnstls_server_init(s);
 #endif
 
         /* A new DNS server that isn't fallback is added and the one
@@ -122,11 +121,7 @@ DnsServer* dns_server_unref(DnsServer *s)  {
         dns_stream_unref(s->stream);
 
 #if ENABLE_DNS_OVER_TLS
-        if (s->tls_cert_cred)
-                gnutls_certificate_free_credentials(s->tls_cert_cred);
-
-        if (s->tls_session_data.data)
-                gnutls_free(s->tls_session_data.data);
+        dnstls_server_free(s);
 #endif
 
         free(s->server_string);
@@ -164,6 +159,8 @@ void dns_server_unlink(DnsServer *s) {
                 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
                 s->manager->n_dns_servers--;
                 break;
+        default:
+                assert_not_reached("Unknown server type");
         }
 
         s->linked = false;
index dffc4217d11ac4fe57421aca9ee84063d05d173b..fda12510495ad8f1a2335273a27a964a8df0d546 100644 (file)
@@ -3,18 +3,15 @@
 
 #include "in-addr-util.h"
 
-#if ENABLE_DNS_OVER_TLS
-#include <gnutls/gnutls.h>
-#endif
-
 typedef struct DnsServer DnsServer;
 
 typedef enum DnsServerType {
         DNS_SERVER_SYSTEM,
         DNS_SERVER_FALLBACK,
         DNS_SERVER_LINK,
+        _DNS_SERVER_TYPE_MAX,
+        _DNS_SERVER_TYPE_INVALID = -1
 } DnsServerType;
-#define _DNS_SERVER_TYPE_MAX (DNS_SERVER_LINK + 1)
 
 const char* dns_server_type_to_string(DnsServerType i) _const_;
 DnsServerType dns_server_type_from_string(const char *s) _pure_;
@@ -40,6 +37,9 @@ int dns_server_feature_level_from_string(const char *s) _pure_;
 
 #include "resolved-link.h"
 #include "resolved-manager.h"
+#if ENABLE_DNS_OVER_TLS
+#include "resolved-dnstls.h"
+#endif
 
 struct DnsServer {
         Manager *manager;
@@ -57,8 +57,7 @@ struct DnsServer {
         DnsStream *stream;
 
 #if ENABLE_DNS_OVER_TLS
-        gnutls_certificate_credentials_t tls_cert_cred;
-        gnutls_datum_t tls_session_data;
+        DnsTlsServerData dnstls_data;
 #endif
 
         DnsServerFeatureLevel verified_feature_level;
index 066daef96e8a9603851c4fb1648ad88d8e5dbeea..8c6f217ad96d23f80b8205f86a508d2380d313ba 100644 (file)
@@ -11,8 +11,6 @@
 #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
 #define DNS_STREAMS_MAX 128
 
-#define WRITE_TLS_DATA 1
-
 static void dns_stream_stop(DnsStream *s) {
         assert(s);
 
@@ -38,6 +36,12 @@ static int dns_stream_update_io(DnsStream *s) {
         if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
                 f |= EPOLLIN;
 
+#if ENABLE_DNS_OVER_TLS
+        /* For handshake and clean closing purposes, TLS can override requested events */
+        if (s->dnstls_events)
+                f = s->dnstls_events;
+#endif
+
         return sd_event_source_set_io_events(s->io_event_source, f);
 }
 
@@ -45,14 +49,11 @@ static int dns_stream_complete(DnsStream *s, int error) {
         assert(s);
 
 #if ENABLE_DNS_OVER_TLS
-        if (s->tls_session && IN_SET(error, ETIMEDOUT, 0)) {
+        if (s->encrypted) {
                 int r;
 
-                r = gnutls_bye(s->tls_session, GNUTLS_SHUT_RDWR);
-                if (r == GNUTLS_E_AGAIN && !s->tls_bye) {
-                        dns_stream_ref(s); /* keep reference for closing TLS session */
-                        s->tls_bye = true;
-                } else
+                r = dnstls_stream_shutdown(s, error);
+                if (r != -EAGAIN)
                         dns_stream_stop(s);
         } else
 #endif
@@ -191,32 +192,22 @@ static int dns_stream_identify(DnsStream *s) {
         return 0;
 }
 
-static ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
+ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
         ssize_t r;
 
         assert(s);
         assert(iov);
 
 #if ENABLE_DNS_OVER_TLS
-        if (s->tls_session && !(flags & WRITE_TLS_DATA)) {
+        if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) {
                 ssize_t ss;
                 size_t i;
 
                 r = 0;
                 for (i = 0; i < iovcnt; i++) {
-                        ss = gnutls_record_send(s->tls_session, iov[i].iov_base, iov[i].iov_len);
-                        if (ss < 0) {
-                                switch(ss) {
-
-                                case GNUTLS_E_INTERRUPTED:
-                                        return -EINTR;
-                                case GNUTLS_E_AGAIN:
-                                        return -EAGAIN;
-                                default:
-                                        log_debug("Failed to invoke gnutls_record_send: %s", gnutls_strerror(ss));
-                                        return -EIO;
-                                }
-                        }
+                        ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len);
+                        if (ss < 0)
+                                return ss;
 
                         r += ss;
                         if (ss != (ssize_t) iov[i].iov_len)
@@ -243,6 +234,8 @@ static ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t i
                                 r = -EAGAIN;
                         } else if (errno == EINPROGRESS)
                                 r = -EAGAIN;
+                        else
+                                r = -errno;
                 } else
                         s->tfo_salen = 0; /* connection is made */
         } else {
@@ -258,28 +251,9 @@ static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
         ssize_t ss;
 
 #if ENABLE_DNS_OVER_TLS
-        if (s->tls_session) {
-                ss = gnutls_record_recv(s->tls_session, buf, count);
-                if (ss < 0) {
-                        switch(ss) {
-
-                        case GNUTLS_E_INTERRUPTED:
-                                return -EINTR;
-                        case GNUTLS_E_AGAIN:
-                                return -EAGAIN;
-                        default:
-                                log_debug("Failed to invoke gnutls_record_send: %s", gnutls_strerror(ss));
-                                return -EIO;
-                        }
-                } else if (s->on_connection) {
-                        int r;
-
-                        r = s->on_connection(s);
-                        s->on_connection = NULL; /* only call once */
-                        if (r < 0)
-                                return r;
-                }
-        } else
+        if (s->encrypted)
+                ss = dnstls_stream_read(s, buf, count);
+        else
 #endif
         {
                 ss = read(s->fd, buf, count);
@@ -290,22 +264,6 @@ static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
         return ss;
 }
 
-#if ENABLE_DNS_OVER_TLS
-static ssize_t dns_stream_tls_writev(gnutls_transport_ptr_t p, const giovec_t * iov, int iovcnt) {
-        int r;
-
-        assert(p);
-
-        r = dns_stream_writev((DnsStream*) p, (struct iovec*) iov, iovcnt, WRITE_TLS_DATA);
-        if (r < 0) {
-                errno = -r;
-                return -1;
-        }
-
-        return r;
-}
-#endif
-
 static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
         DnsStream *s = userdata;
 
@@ -321,36 +279,20 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
         assert(s);
 
 #if ENABLE_DNS_OVER_TLS
-        if (s->tls_bye) {
-                assert(s->tls_session);
-
-                r = gnutls_bye(s->tls_session, GNUTLS_SHUT_RDWR);
-                if (r != GNUTLS_E_AGAIN) {
-                        s->tls_bye = false;
-                        dns_stream_unref(s);
-                }
-
-                return 0;
-        }
-
-        if (s->tls_handshake < 0) {
-                assert(s->tls_session);
-
-                s->tls_handshake = gnutls_handshake(s->tls_session);
-                if (s->tls_handshake >= 0) {
-                        if (s->on_connection && !(gnutls_session_get_flags(s->tls_session) & GNUTLS_SFLAGS_FALSE_START)) {
-                                r = s->on_connection(s);
-                                s->on_connection = NULL; /* only call once */
-                                if (r < 0)
-                                        return r;
-                        }
+        if (s->encrypted) {
+                r = dnstls_stream_on_io(s, revents);
+
+                if (r == DNSTLS_STREAM_CLOSED)
+                        return 0;
+                else if (r == -EAGAIN)
+                        return dns_stream_update_io(s);
+                else if (r < 0) {
+                        return dns_stream_complete(s, -r);
                 } else {
-                        if (gnutls_error_is_fatal(s->tls_handshake))
-                                return dns_stream_complete(s, ECONNREFUSED);
-                        else
-                                return 0;
+                        r = dns_stream_update_io(s);
+                        if (r < 0)
+                                return r;
                 }
-
         }
 #endif
 
@@ -506,8 +448,8 @@ DnsStream *dns_stream_unref(DnsStream *s) {
         }
 
 #if ENABLE_DNS_OVER_TLS
-        if (s->tls_session)
-                gnutls_deinit(s->tls_session);
+        if (s->encrypted)
+                dnstls_stream_free(s);
 #endif
 
         ORDERED_SET_FOREACH(p, s->write_queue, i)
@@ -586,21 +528,6 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, co
         return 0;
 }
 
-#if ENABLE_DNS_OVER_TLS
-int dns_stream_connect_tls(DnsStream *s, gnutls_session_t tls_session) {
-        gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr_t) (long) s->fd, s);
-        gnutls_transport_set_vec_push_function(tls_session, &dns_stream_tls_writev);
-
-        s->encrypted = true;
-        s->tls_session = tls_session;
-        s->tls_handshake = gnutls_handshake(tls_session);
-        if (s->tls_handshake < 0 && gnutls_error_is_fatal(s->tls_handshake))
-                return -ECONNREFUSED;
-
-        return 0;
-}
-#endif
-
 int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
         int r;
 
index 9a0da226d834e641631bd5960589236f90f16bc6..72db2a1a56ac6d1d4393a787b1b537661ac17279 100644 (file)
@@ -8,11 +8,12 @@ typedef struct DnsStream DnsStream;
 #include "resolved-dns-packet.h"
 #include "resolved-dns-transaction.h"
 #include "resolved-manager.h"
-
 #if ENABLE_DNS_OVER_TLS
-#include <gnutls/gnutls.h>
+#include "resolved-dnstls.h"
 #endif
 
+#define DNS_STREAM_WRITE_TLS_DATA 1
+
 /* Streams are used by three subsystems:
  *
  *   1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP
@@ -40,9 +41,8 @@ struct DnsStream {
         socklen_t tfo_salen;
 
 #if ENABLE_DNS_OVER_TLS
-        gnutls_session_t tls_session;
-        int tls_handshake;
-        bool tls_bye;
+        DnsTlsStreamData dnstls_data;
+        int dnstls_events;
 #endif
 
         sd_event_source *io_event_source;
@@ -69,7 +69,7 @@ struct DnsStream {
 
 int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address);
 #if ENABLE_DNS_OVER_TLS
-int dns_stream_connect_tls(DnsStream *s, gnutls_session_t tls_session);
+int dns_stream_connect_tls(DnsStream *s, void *tls_session);
 #endif
 DnsStream *dns_stream_unref(DnsStream *s);
 DnsStream *dns_stream_ref(DnsStream *s);
@@ -77,6 +77,7 @@ DnsStream *dns_stream_ref(DnsStream *s);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref);
 
 int dns_stream_write_packet(DnsStream *s, DnsPacket *p);
+ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags);
 
 static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
         assert(s);
index 89d311811a7dbb5271f0f114e6671d87e8535390..db30997d04e8ea30a71ebcfe7633e1fec6d6e0a2 100644 (file)
 #include "resolved-dns-cache.h"
 #include "resolved-dns-transaction.h"
 #include "resolved-llmnr.h"
-#include "string-table.h"
-
 #if ENABLE_DNS_OVER_TLS
-#include <gnutls/socket.h>
+#include "resolved-dnstls.h"
 #endif
+#include "string-table.h"
 
 #define TRANSACTIONS_MAX 4096
 #define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
@@ -503,20 +502,6 @@ static int dns_transaction_on_stream_packet(DnsTransaction *t, DnsPacket *p) {
         return 0;
 }
 
-static int on_stream_connection(DnsStream *s) {
-#if ENABLE_DNS_OVER_TLS
-        /* Store TLS Ticket for faster succesive TLS handshakes */
-        if (s->tls_session && s->server) {
-                if (s->server->tls_session_data.data)
-                        gnutls_free(s->server->tls_session_data.data);
-
-                gnutls_session_get_data2(s->tls_session, &s->server->tls_session_data);
-        }
-#endif
-
-        return 0;
-}
-
 static int on_stream_complete(DnsStream *s, int error) {
         _cleanup_(dns_stream_unrefp) DnsStream *p = NULL;
         DnsTransaction *t, *n;
@@ -578,9 +563,6 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
         _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
         union sockaddr_union sa;
         int r;
-#if ENABLE_DNS_OVER_TLS
-        gnutls_session_t gs;
-#endif
 
         assert(t);
 
@@ -655,32 +637,12 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
 #if ENABLE_DNS_OVER_TLS
                 if (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) {
                         assert(t->server);
-
-                        r = gnutls_init(&gs, GNUTLS_CLIENT | GNUTLS_ENABLE_FALSE_START | GNUTLS_NONBLOCK);
-                        if (r < 0)
-                                return r;
-
-                        /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
-                        r = gnutls_priority_set_direct(gs, "NORMAL:-VERS-ALL:+VERS-TLS1.2", NULL);
-                        if (r < 0)
-                                return r;
-
-                        r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, t->server->tls_cert_cred);
-                        if (r < 0)
-                                return r;
-
-                        if (t->server->tls_session_data.size > 0)
-                                gnutls_session_set_data(gs, t->server->tls_session_data.data, t->server->tls_session_data.size);
-
-                        gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
-
-                        r = dns_stream_connect_tls(s, gs);
+                        r = dnstls_stream_connect_tls(s, t->server);
                         if (r < 0)
                                 return r;
                 }
 #endif
 
-                s->on_connection = on_stream_connection;
                 s->complete = on_stream_complete;
                 s->on_packet = dns_stream_on_packet;
 
diff --git a/src/resolve/resolved-dnstls-gnutls.c b/src/resolve/resolved-dnstls-gnutls.c
new file mode 100644 (file)
index 0000000..820e192
--- /dev/null
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_GNUTLS
+#error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
+#endif
+
+#include "resolved-dnstls.h"
+#include "resolved-dns-stream.h"
+
+#include <gnutls/socket.h>
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t, gnutls_deinit);
+
+static ssize_t dnstls_stream_writev(gnutls_transport_ptr_t p, const giovec_t *iov, int iovcnt) {
+        int r;
+
+        assert(p);
+
+        r = dns_stream_writev((DnsStream*) p, (const struct iovec*) iov, iovcnt, DNS_STREAM_WRITE_TLS_DATA);
+        if (r < 0) {
+                errno = -r;
+                return -1;
+        }
+
+        return r;
+}
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
+        _cleanup_(gnutls_deinitp) gnutls_session_t gs;
+        int r;
+
+        assert(stream);
+        assert(server);
+
+        r = gnutls_init(&gs, GNUTLS_CLIENT | GNUTLS_ENABLE_FALSE_START | GNUTLS_NONBLOCK);
+        if (r < 0)
+                return r;
+
+        /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
+        r = gnutls_priority_set_direct(gs, "NORMAL:-VERS-ALL:+VERS-TLS1.2", NULL);
+        if (r < 0)
+                return r;
+
+        r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, server->dnstls_data.cert_cred);
+        if (r < 0)
+                return r;
+
+        if (server->dnstls_data.session_data.size > 0) {
+                gnutls_session_set_data(gs, server->dnstls_data.session_data.data, server->dnstls_data.session_data.size);
+
+                // Clear old session ticket
+                gnutls_free(server->dnstls_data.session_data.data);
+                server->dnstls_data.session_data.data = NULL;
+                server->dnstls_data.session_data.size = 0;
+        }
+
+        gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+        gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
+        gnutls_transport_set_vec_push_function(gs, &dnstls_stream_writev);
+
+        stream->encrypted = true;
+        stream->dnstls_data.handshake = gnutls_handshake(gs);
+        if (stream->dnstls_data.handshake < 0 && gnutls_error_is_fatal(stream->dnstls_data.handshake))
+                return -ECONNREFUSED;
+
+        stream->dnstls_data.session = TAKE_PTR(gs);
+
+        return 0;
+}
+
+void dnstls_stream_free(DnsStream *stream) {
+        assert(stream);
+        assert(stream->encrypted);
+
+        if (stream->dnstls_data.session)
+                gnutls_deinit(stream->dnstls_data.session);
+}
+
+int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
+        int r;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.session);
+
+        if (stream->dnstls_data.shutdown) {
+                r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
+                if (r == GNUTLS_E_AGAIN) {
+                        stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
+                        return -EAGAIN;
+                } else if (r < 0)
+                        log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
+
+                stream->dnstls_events = 0;
+                stream->dnstls_data.shutdown = false;
+                dns_stream_unref(stream);
+                return DNSTLS_STREAM_CLOSED;
+        } else if (stream->dnstls_data.handshake < 0) {
+                stream->dnstls_data.handshake = gnutls_handshake(stream->dnstls_data.session);
+                if (stream->dnstls_data.handshake == GNUTLS_E_AGAIN) {
+                        stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
+                        return -EAGAIN;
+                } else if (stream->dnstls_data.handshake < 0) {
+                        log_debug("Failed to invoke gnutls_handshake: %s", gnutls_strerror(stream->dnstls_data.handshake));
+                        if (gnutls_error_is_fatal(stream->dnstls_data.handshake))
+                                return -ECONNREFUSED;
+                }
+
+                stream->dnstls_events = 0;
+        }
+
+        return 0;
+}
+
+int dnstls_stream_shutdown(DnsStream *stream, int error) {
+        int r;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.session);
+
+        /* Store TLS Ticket for faster succesive TLS handshakes */
+        if (stream->server && stream->server->dnstls_data.session_data.size == 0 && stream->dnstls_data.handshake == GNUTLS_E_SUCCESS)
+                gnutls_session_get_data2(stream->dnstls_data.session, &stream->server->dnstls_data.session_data);
+
+        if (IN_SET(error, ETIMEDOUT, 0)) {
+                r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
+                if (r == GNUTLS_E_AGAIN) {
+                        if (!stream->dnstls_data.shutdown) {
+                                stream->dnstls_data.shutdown = true;
+                                dns_stream_ref(stream);
+                                return -EAGAIN;
+                        }
+                } else if (r < 0)
+                        log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
+        }
+
+        return 0;
+}
+
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
+        ssize_t ss;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.session);
+        assert(buf);
+
+        ss = gnutls_record_send(stream->dnstls_data.session, buf, count);
+        if (ss < 0)
+                switch(ss) {
+                case GNUTLS_E_INTERRUPTED:
+                        return -EINTR;
+                case GNUTLS_E_AGAIN:
+                        return -EAGAIN;
+                default:
+                        log_debug("Failed to invoke gnutls_record_send: %s", gnutls_strerror(ss));
+                        return -EPIPE;
+                }
+
+        return ss;
+}
+
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
+        ssize_t ss;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.session);
+        assert(buf);
+
+        ss = gnutls_record_recv(stream->dnstls_data.session, buf, count);
+        if (ss < 0)
+                switch(ss) {
+                case GNUTLS_E_INTERRUPTED:
+                        return -EINTR;
+                case GNUTLS_E_AGAIN:
+                        return -EAGAIN;
+                default:
+                        log_debug("Failed to invoke gnutls_record_recv: %s", gnutls_strerror(ss));
+                        return -EPIPE;
+                }
+
+        return ss;
+}
+
+void dnstls_server_init(DnsServer *server) {
+        assert(server);
+
+        /* Do not verify cerificate */
+        gnutls_certificate_allocate_credentials(&server->dnstls_data.cert_cred);
+}
+
+void dnstls_server_free(DnsServer *server) {
+        assert(server);
+
+        if (server->dnstls_data.cert_cred)
+                gnutls_certificate_free_credentials(server->dnstls_data.cert_cred);
+
+        if (server->dnstls_data.session_data.data)
+                gnutls_free(server->dnstls_data.session_data.data);
+}
diff --git a/src/resolve/resolved-dnstls-gnutls.h b/src/resolve/resolved-dnstls-gnutls.h
new file mode 100644 (file)
index 0000000..364eea1
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_GNUTLS
+#error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
+#endif
+
+#include <stdbool.h>
+
+#include <gnutls/gnutls.h>
+
+struct DnsTlsServerData {
+        gnutls_certificate_credentials_t cert_cred;
+        gnutls_datum_t session_data;
+};
+
+struct DnsTlsStreamData {
+        gnutls_session_t session;
+        int handshake;
+        bool shutdown;
+};
diff --git a/src/resolve/resolved-dnstls-openssl.c b/src/resolve/resolved-dnstls-openssl.c
new file mode 100644 (file)
index 0000000..5dd7737
--- /dev/null
@@ -0,0 +1,338 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_OPENSSL
+#error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
+#endif
+
+#include "resolved-dnstls.h"
+#include "resolved-dns-stream.h"
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
+
+static int dnstls_flush_write_buffer(DnsStream *stream) {
+        ssize_t ss;
+
+        assert(stream);
+        assert(stream->encrypted);
+
+        if (stream->dnstls_data.write_buffer->length > 0) {
+                assert(stream->dnstls_data.write_buffer->data);
+
+                struct iovec iov[1];
+                iov[0].iov_base = stream->dnstls_data.write_buffer->data;
+                iov[0].iov_len = stream->dnstls_data.write_buffer->length;
+                ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA);
+                if (ss < 0) {
+                        if (ss == -EAGAIN)
+                                stream->dnstls_events |= EPOLLOUT;
+
+                        return ss;
+                } else {
+                        stream->dnstls_data.write_buffer->length -= ss;
+                        stream->dnstls_data.write_buffer->data += ss;
+
+                        if (stream->dnstls_data.write_buffer->length > 0) {
+                                stream->dnstls_events |= EPOLLOUT;
+                                return -EAGAIN;
+                        }
+                }
+        }
+
+        return 0;
+}
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
+        _cleanup_(SSL_freep) SSL *s = NULL;
+        _cleanup_(BIO_freep) BIO *rb = NULL;
+        _cleanup_(BIO_freep) BIO *wb = NULL;
+        int r;
+        int error;
+
+        assert(stream);
+        assert(server);
+
+        rb = BIO_new_socket(stream->fd, 0);
+        if (!rb)
+                return -ENOMEM;
+
+        wb = BIO_new(BIO_s_mem());
+        if (!wb)
+                return -ENOMEM;
+
+        BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
+
+        s = SSL_new(server->dnstls_data.ctx);
+        if (!s)
+                return -ENOMEM;
+
+        SSL_set_connect_state(s);
+        SSL_set_session(s, server->dnstls_data.session);
+        SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
+
+        stream->dnstls_data.handshake = SSL_do_handshake(s);
+        if (stream->dnstls_data.handshake <= 0) {
+                error = SSL_get_error(s, stream->dnstls_data.handshake);
+                if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+                        char errbuf[256];
+
+                        ERR_error_string_n(error, errbuf, sizeof(errbuf));
+                        log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
+                        return -ECONNREFUSED;
+                }
+        }
+
+        stream->encrypted = true;
+
+        r = dnstls_flush_write_buffer(stream);
+        if (r < 0 && r != -EAGAIN)
+                return r;
+
+        stream->dnstls_data.ssl = TAKE_PTR(s);
+
+        return 0;
+}
+
+void dnstls_stream_free(DnsStream *stream) {
+        assert(stream);
+        assert(stream->encrypted);
+
+        if (stream->dnstls_data.ssl)
+                SSL_free(stream->dnstls_data.ssl);
+}
+
+int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
+        int r;
+        int error;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.ssl);
+
+        /* Flush write buffer when requested by OpenSSL ss*/
+        if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
+                r = dnstls_flush_write_buffer(stream);
+                if (r < 0)
+                        return r;
+        }
+
+        if (stream->dnstls_data.shutdown) {
+                r = SSL_shutdown(stream->dnstls_data.ssl);
+                if (r <= 0) {
+                        error = SSL_get_error(stream->dnstls_data.ssl, r);
+                        if (r == 0 || IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+                                if (r < 0)
+                                        stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+
+                                r = dnstls_flush_write_buffer(stream);
+                                if (r < 0)
+                                        return r;
+
+                                return -EAGAIN;
+                        } else {
+                                char errbuf[256];
+
+                                ERR_error_string_n(error, errbuf, sizeof(errbuf));
+                                log_debug("Failed to invoke SSL_shutdown: %s", errbuf);
+                        }
+                }
+
+                r = dnstls_flush_write_buffer(stream);
+                if (r < 0)
+                        return r;
+
+                stream->dnstls_events = 0;
+                stream->dnstls_data.shutdown = false;
+                dns_stream_unref(stream);
+                return DNSTLS_STREAM_CLOSED;
+        } else if (stream->dnstls_data.handshake <= 0) {
+                stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
+                if (stream->dnstls_data.handshake <= 0) {
+                        error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
+                        if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+                                stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+                                r = dnstls_flush_write_buffer(stream);
+                                if (r < 0)
+                                        return r;
+
+                                return -EAGAIN;
+                        } else {
+                                char errbuf[256];
+
+                                ERR_error_string_n(error, errbuf, sizeof(errbuf));
+                                log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
+                                return -ECONNREFUSED;
+                        }
+                }
+
+                stream->dnstls_events = 0;
+                r = dnstls_flush_write_buffer(stream);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int dnstls_stream_shutdown(DnsStream *stream, int error) {
+        int r;
+        int ssl_error;
+        SSL_SESSION *s;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.ssl);
+
+        if (stream->server) {
+                s = SSL_get1_session(stream->dnstls_data.ssl);
+                if (s) {
+                        if (stream->server->dnstls_data.session)
+                                SSL_SESSION_free(stream->server->dnstls_data.session);
+
+                        stream->server->dnstls_data.session = s;
+                }
+        }
+
+        if (error == ETIMEDOUT) {
+                r = SSL_shutdown(stream->dnstls_data.ssl);
+                if (r == 0) {
+                        if (!stream->dnstls_data.shutdown) {
+                                stream->dnstls_data.shutdown = true;
+                                dns_stream_ref(stream);
+                        }
+
+                        r = dnstls_flush_write_buffer(stream);
+                        if (r < 0)
+                                return r;
+
+                        return -EAGAIN;
+                } else if (r < 0) {
+                        ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
+                        if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+                                stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+                                r = dnstls_flush_write_buffer(stream);
+                                if (r < 0 && r != -EAGAIN)
+                                        return r;
+
+                                if (!stream->dnstls_data.shutdown) {
+                                        stream->dnstls_data.shutdown = true;
+                                        dns_stream_ref(stream);
+                                }
+                                return -EAGAIN;
+                        } else {
+                                char errbuf[256];
+
+                                ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
+                                log_debug("Failed to invoke SSL_shutdown: %s", errbuf);
+                        }
+                }
+
+                stream->dnstls_events = 0;
+                r = dnstls_flush_write_buffer(stream);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
+        int r;
+        int error;
+        ssize_t ss;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.ssl);
+        assert(buf);
+
+        ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
+        if (r <= 0) {
+                error = SSL_get_error(stream->dnstls_data.ssl, ss);
+                if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+                        stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+                        r = dnstls_flush_write_buffer(stream);
+                        if (r < 0)
+                                return r;
+
+                        ss = -EAGAIN;
+                } else {
+                        char errbuf[256];
+
+                        ERR_error_string_n(error, errbuf, sizeof(errbuf));
+                        log_debug("Failed to invoke SSL_read: %s", errbuf);
+                        ss = -EPIPE;
+                }
+        }
+
+        stream->dnstls_events = 0;
+        r = dnstls_flush_write_buffer(stream);
+        if (r < 0)
+                return r;
+
+        return ss;
+}
+
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
+        int r;
+        int error;
+        ssize_t ss;
+
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.ssl);
+        assert(buf);
+
+        ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
+        if (r <= 0) {
+                error = SSL_get_error(stream->dnstls_data.ssl, ss);
+                if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+                        stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+
+                        /* flush write buffer in cache of renegotiation */
+                        r = dnstls_flush_write_buffer(stream);
+                        if (r < 0)
+                                return r;
+
+                        ss = -EAGAIN;
+                } else {
+                        char errbuf[256];
+
+                        ERR_error_string_n(error, errbuf, sizeof(errbuf));
+                        log_debug("Failed to invoke SSL_read: %s", errbuf);
+                        ss = -EPIPE;
+                }
+        }
+
+        stream->dnstls_events = 0;
+
+        /* flush write buffer in cache of renegotiation */
+        r = dnstls_flush_write_buffer(stream);
+        if (r < 0)
+                return r;
+
+        return ss;
+}
+
+void dnstls_server_init(DnsServer *server) {
+        assert(server);
+
+        server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
+        if (server->dnstls_data.ctx) {
+                SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
+                SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+        }
+}
+
+void dnstls_server_free(DnsServer *server) {
+        assert(server);
+
+        if (server->dnstls_data.ctx)
+                SSL_CTX_free(server->dnstls_data.ctx);
+
+        if (server->dnstls_data.session)
+                SSL_SESSION_free(server->dnstls_data.session);
+}
diff --git a/src/resolve/resolved-dnstls-openssl.h b/src/resolve/resolved-dnstls-openssl.h
new file mode 100644 (file)
index 0000000..c57bc1c
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_OPENSSL
+#error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
+#endif
+
+#include <stdbool.h>
+
+#include <openssl/ssl.h>
+
+struct DnsTlsServerData {
+        SSL_CTX *ctx;
+        SSL_SESSION *session;
+};
+
+struct DnsTlsStreamData {
+        int handshake;
+        bool shutdown;
+        SSL *ssl;
+        BUF_MEM *write_buffer;
+};
diff --git a/src/resolve/resolved-dnstls.h b/src/resolve/resolved-dnstls.h
new file mode 100644 (file)
index 0000000..fdd85ee
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_DNS_OVER_TLS
+#error This source file requires DNS-over-TLS to be enabled
+#endif
+
+typedef struct DnsTlsServerData DnsTlsServerData;
+typedef struct DnsTlsStreamData DnsTlsStreamData;
+
+#if DNS_OVER_TLS_USE_GNUTLS
+#include "resolved-dnstls-gnutls.h"
+#elif DNS_OVER_TLS_USE_OPENSSL
+#include "resolved-dnstls-openssl.h"
+#else
+#error Unknown dependency for supporting DNS-over-TLS
+#endif
+
+#include "resolved-dns-stream.h"
+#include "resolved-dns-transaction.h"
+
+#define DNSTLS_STREAM_CLOSED 1
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server);
+void dnstls_stream_free(DnsStream *stream);
+int dnstls_stream_on_io(DnsStream *stream, uint32_t revents);
+int dnstls_stream_shutdown(DnsStream *stream, int error);
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count);
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count);
+
+void dnstls_server_init(DnsServer *server);
+void dnstls_server_free(DnsServer *server);
index 507f68b47fdeb16a0106baf5aa8ecca136fccd70..5be62cc3a84f2014dbf5a6b2468dab21d86a725c 100644 (file)
 /* Recheck /etc/hosts at most once every 2s */
 #define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
 
-typedef struct EtcHostsItem {
-        int family;
-        union in_addr_union address;
-
-        char **names;
-} EtcHostsItem;
-
-typedef struct EtcHostsItemByName {
-        char *name;
-
-        EtcHostsItem **items;
-        size_t n_items, n_allocated;
-} EtcHostsItemByName;
-
-void manager_etc_hosts_flush(Manager *m) {
-        EtcHostsItem *item;
-        EtcHostsItemByName *bn;
-
-        while ((item = set_steal_first(m->etc_hosts_by_address))) {
-                strv_free(item->names);
-                free(item);
-        }
-
-        while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) {
-                free(bn->name);
-                free(bn->items);
-                free(bn);
-        }
-
-        m->etc_hosts_by_address = set_free(m->etc_hosts_by_address);
-        m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name);
-
-        m->etc_hosts_mtime = USEC_INFINITY;
+static inline void etc_hosts_item_free(EtcHostsItem *item) {
+        strv_free(item->names);
+        free(item);
 }
 
-static void etc_hosts_item_hash_func(const void *p, struct siphash *state) {
-        const EtcHostsItem *item = p;
-
-        siphash24_compress(&item->family, sizeof(item->family), state);
-
-        if (item->family == AF_INET)
-                siphash24_compress(&item->address.in, sizeof(item->address.in), state);
-        else if (item->family == AF_INET6)
-                siphash24_compress(&item->address.in6, sizeof(item->address.in6), state);
+static inline void etc_hosts_item_by_name_free(EtcHostsItemByName *item) {
+        free(item->name);
+        free(item->addresses);
+        free(item);
 }
 
-static int etc_hosts_item_compare_func(const void *a, const void *b) {
-        const EtcHostsItem *x = a, *y = b;
-
-        if (x->family != y->family)
-                return x->family - y->family;
-
-        if (x->family == AF_INET)
-                return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
-
-        if (x->family == AF_INET6)
-                return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
-
-        return trivial_compare_func(a, b);
+void etc_hosts_free(EtcHosts *hosts) {
+        hosts->by_address = hashmap_free_with_destructor(hosts->by_address, etc_hosts_item_free);
+        hosts->by_name = hashmap_free_with_destructor(hosts->by_name, etc_hosts_item_by_name_free);
+        hosts->no_address = set_free_free(hosts->no_address);
 }
 
-static const struct hash_ops etc_hosts_item_ops = {
-        .hash = etc_hosts_item_hash_func,
-        .compare = etc_hosts_item_compare_func,
-};
-
-static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) {
+void manager_etc_hosts_flush(Manager *m) {
+        etc_hosts_free(&m->etc_hosts);
+        m->etc_hosts_mtime = USEC_INFINITY;
+}
 
-        EtcHostsItem key = {
-                .family = family,
-                .address = *address,
-        };
+static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
+        _cleanup_free_ char *address_str = NULL;
+        struct in_addr_data address = {};
+        bool found = false;
         EtcHostsItem *item;
-        char **n;
         int r;
 
-        assert(m);
-        assert(address);
+        assert(hosts);
+        assert(line);
+
+        r = extract_first_word(&line, &address_str, NULL, EXTRACT_RELAX);
+        if (r < 0)
+                return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
+        if (r == 0) {
+                log_error("Premature end of line, in line /etc/hosts:%u.", nr);
+                return -EINVAL;
+        }
+
+        r = in_addr_ifindex_from_string_auto(address_str, &address.family, &address.address, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address_str, nr);
 
-        r = in_addr_is_null(family, address);
+        r = in_addr_is_null(address.family, &address.address);
         if (r < 0)
                 return r;
         if (r > 0)
@@ -99,16 +64,11 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
                  * nothing. */
                 item = NULL;
         else {
-                /* If this is a normal address, then, simply add entry mapping it to the specified names */
+                /* If this is a normal address, then simply add entry mapping it to the specified names */
 
-                item = set_get(m->etc_hosts_by_address, &key);
-                if (item) {
-                        r = strv_extend_strv(&item->names, names, true);
-                        if (r < 0)
-                                return log_oom();
-                } else {
-
-                        r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops);
+                item = hashmap_get(hosts->by_address, &address);
+                if (!item) {
+                        r = hashmap_ensure_allocated(&hosts->by_address, &in_addr_data_hash_ops);
                         if (r < 0)
                                 return log_oom();
 
@@ -116,11 +76,9 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
                         if (!item)
                                 return log_oom();
 
-                        item->family = family;
-                        item->address = *address;
-                        item->names = names;
+                        item->address = address;
 
-                        r = set_put(m->etc_hosts_by_address, item);
+                        r = hashmap_put(hosts->by_address, &item->address, item);
                         if (r < 0) {
                                 free(item);
                                 return log_oom();
@@ -128,12 +86,49 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
                 }
         }
 
-        STRV_FOREACH(n, names) {
+        for (;;) {
+                _cleanup_free_ char *name = NULL;
                 EtcHostsItemByName *bn;
 
-                bn = hashmap_get(m->etc_hosts_by_name, *n);
+                r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
+                if (r < 0)
+                        return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
+                if (r == 0)
+                        break;
+
+                r = dns_name_is_valid(name);
+                if (r <= 0)
+                        return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
+
+                found = true;
+
+                if (is_localhost(name))
+                        /* Suppress the "localhost" line that is often seen */
+                        continue;
+
+                if (!item) {
+                        /* Optimize the case where we don't need to store any addresses, by storing
+                         * only the name in a dedicated Set instead of the hashmap */
+
+                        r = set_ensure_allocated(&hosts->no_address, &dns_name_hash_ops);
+                        if (r < 0)
+                                return log_oom();
+
+                        r = set_put(hosts->no_address, name);
+                        if (r < 0)
+                                return r;
+
+                        TAKE_PTR(name);
+                        continue;
+                }
+
+                r = strv_extend(&item->names, name);
+                if (r < 0)
+                        return log_oom();
+
+                bn = hashmap_get(hosts->by_name, name);
                 if (!bn) {
-                        r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops);
+                        r = hashmap_ensure_allocated(&hosts->by_name, &dns_name_hash_ops);
                         if (r < 0)
                                 return log_oom();
 
@@ -141,103 +136,61 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
                         if (!bn)
                                 return log_oom();
 
-                        bn->name = strdup(*n);
-                        if (!bn->name) {
-                                free(bn);
-                                return log_oom();
-                        }
-
-                        r = hashmap_put(m->etc_hosts_by_name, bn->name, bn);
+                        r = hashmap_put(hosts->by_name, name, bn);
                         if (r < 0) {
-                                free(bn->name);
                                 free(bn);
                                 return log_oom();
                         }
-                }
 
-                if (item) {
-                        if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1))
-                                return log_oom();
-
-                        bn->items[bn->n_items++] = item;
+                        bn->name = TAKE_PTR(name);
                 }
-        }
 
-        return 0;
-}
-
-static int parse_line(Manager *m, unsigned nr, const char *line) {
-        _cleanup_free_ char *address = NULL;
-        _cleanup_strv_free_ char **names = NULL;
-        union in_addr_union in;
-        bool suppressed = false;
-        int family, r;
+                if (!GREEDY_REALLOC(bn->addresses, bn->n_allocated, bn->n_addresses + 1))
+                        return log_oom();
 
-        assert(m);
-        assert(line);
+                bn->addresses[bn->n_addresses++] = &item->address;
+        }
 
-        r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX);
-        if (r < 0)
-                return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
-        if (r == 0) {
-                log_error("Premature end of line, in line /etc/hosts:%u.", nr);
+        if (!found) {
+                log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
                 return -EINVAL;
         }
 
-        r = in_addr_from_string_auto(address, &family, &in);
-        if (r < 0)
-                return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr);
+        return 0;
+}
 
-        for (;;) {
-                _cleanup_free_ char *name = NULL;
+int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
+        _cleanup_(etc_hosts_free) EtcHosts t = {};
+        char line[LINE_MAX];
+        unsigned nr = 0;
+        int r;
 
-                r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
-                if (r < 0)
-                        return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
-                if (r == 0)
-                        break;
+        FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
+                char *l;
 
-                r = dns_name_is_valid(name);
-                if (r <= 0)
-                        return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
+                nr++;
 
-                if (is_localhost(name)) {
-                        /* Suppress the "localhost" line that is often seen */
-                        suppressed = true;
+                l = strstrip(line);
+                if (isempty(l))
+                        continue;
+                if (l[0] == '#')
                         continue;
-                }
 
-                r = strv_push(&names, name);
+                r = parse_line(&t, nr, l);
                 if (r < 0)
-                        return log_oom();
-
-                name = NULL;
-        }
-
-        if (strv_isempty(names)) {
-
-                if (suppressed)
-                        return 0;
-
-                log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
-                return -EINVAL;
+                        return r;
         }
 
-        /* Takes possession of the names strv */
-        r = add_item(m, family, &in, names);
-        if (r < 0)
-                return r;
-
-        names = NULL;
-        return r;
+        etc_hosts_free(hosts);
+        *hosts = t;
+        t = (EtcHosts) {}; /* prevent cleanup */
+        return 0;
 }
 
-int manager_etc_hosts_read(Manager *m) {
+static int manager_etc_hosts_read(Manager *m) {
         _cleanup_fclose_ FILE *f = NULL;
-        char line[LINE_MAX];
         struct stat st;
         usec_t ts;
-        unsigned nr = 0;
         int r;
 
         assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0);
@@ -250,12 +203,11 @@ int manager_etc_hosts_read(Manager *m) {
 
         if (m->etc_hosts_mtime != USEC_INFINITY) {
                 if (stat("/etc/hosts", &st) < 0) {
-                        if (errno == ENOENT) {
-                                r = 0;
-                                goto clear;
-                        }
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
 
-                        return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
+                        manager_etc_hosts_flush(m);
+                        return 0;
                 }
 
                 /* Did the mtime change? If not, there's no point in re-reading the file. */
@@ -265,12 +217,11 @@ int manager_etc_hosts_read(Manager *m) {
 
         f = fopen("/etc/hosts", "re");
         if (!f) {
-                if (errno == ENOENT) {
-                        r = 0;
-                        goto clear;
-                }
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to open /etc/hosts: %m");
 
-                return log_error_errno(errno, "Failed to open /etc/hosts: %m");
+                manager_etc_hosts_flush(m);
+                return 0;
         }
 
         /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next
@@ -279,38 +230,20 @@ int manager_etc_hosts_read(Manager *m) {
         if (r < 0)
                 return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m");
 
-        manager_etc_hosts_flush(m);
-
-        FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
-                char *l;
-
-                nr++;
-
-                l = strstrip(line);
-                if (isempty(l))
-                        continue;
-                if (l[0] == '#')
-                        continue;
-
-                r = parse_line(m, nr, l);
-                if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */
-                        goto clear;
-        }
+        r = etc_hosts_parse(&m->etc_hosts, f);
+        if (r < 0)
+                return r;
 
         m->etc_hosts_mtime = timespec_load(&st.st_mtim);
         m->etc_hosts_last = ts;
 
         return 1;
-
-clear:
-        manager_etc_hosts_flush(m);
-        return r;
 }
 
 int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
         bool found_a = false, found_aaaa = false;
+        struct in_addr_data k = {};
         EtcHostsItemByName *bn;
-        EtcHostsItem k = {};
         DnsResourceKey *t;
         const char *name;
         unsigned i;
@@ -320,9 +253,10 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
         assert(q);
         assert(answer);
 
-        r = manager_etc_hosts_read(m);
-        if (r < 0)
-                return r;
+        if (!m->read_etc_hosts)
+                return 0;
+
+        (void) manager_etc_hosts_read(m);
 
         name = dns_question_first_name(q);
         if (!name)
@@ -333,7 +267,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
                 EtcHostsItem *item;
                 DnsResourceKey *found_ptr = NULL;
 
-                item = set_get(m->etc_hosts_by_address, &k);
+                item = hashmap_get(m->etc_hosts.by_address, &k);
                 if (!item)
                         return 0;
 
@@ -382,13 +316,16 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
                 return 1;
         }
 
-        bn = hashmap_get(m->etc_hosts_by_name, name);
-        if (!bn)
-                return 0;
-
-        r = dns_answer_reserve(answer, bn->n_items);
-        if (r < 0)
-                return r;
+        bn = hashmap_get(m->etc_hosts.by_name, name);
+        if (bn) {
+                r = dns_answer_reserve(answer, bn->n_addresses);
+                if (r < 0)
+                        return r;
+        } else {
+                /* Check if name was listed with no address. If yes, continue to return an answer. */
+                if (!set_contains(m->etc_hosts.no_address, name))
+                        return 0;
+        }
 
         DNS_QUESTION_FOREACH(t, q) {
                 if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
@@ -411,14 +348,14 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
                         break;
         }
 
-        for (i = 0; i < bn->n_items; i++) {
+        for (i = 0; bn && i < bn->n_addresses; i++) {
                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
 
-                if ((!found_a && bn->items[i]->family == AF_INET) ||
-                    (!found_aaaa && bn->items[i]->family == AF_INET6))
+                if ((!found_a && bn->addresses[i]->family == AF_INET) ||
+                    (!found_aaaa && bn->addresses[i]->family == AF_INET6))
                         continue;
 
-                r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
+                r = dns_resource_record_new_address(&rr, bn->addresses[i]->family, &bn->addresses[i]->address, bn->name);
                 if (r < 0)
                         return r;
 
index b8e04c393b0a5ad60a851042aee8ae9678664d24..caf32a53ddae230c869f3a2bc0e1cfbc5d530106 100644 (file)
@@ -5,6 +5,21 @@
 #include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
 
+typedef struct EtcHostsItem {
+        struct in_addr_data address;
+
+        char **names;
+} EtcHostsItem;
+
+typedef struct EtcHostsItemByName {
+        char *name;
+
+        struct in_addr_data **addresses;
+        size_t n_addresses, n_allocated;
+} EtcHostsItemByName;
+
+int etc_hosts_parse(EtcHosts *hosts, FILE *f);
+void etc_hosts_free(EtcHosts *hosts);
+
 void manager_etc_hosts_flush(Manager *m);
-int manager_etc_hosts_read(Manager *m);
 int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
index 648ca4d8a24b674dff1937ecd215a21ee550b0e1..9b9290b727eae87a7a5c30e870922898a1bd2c14 100644 (file)
@@ -26,3 +26,4 @@ Resolve.DNSSEC,          config_parse_dnssec_mode,            0,
 Resolve.DNSOverTLS,      config_parse_dns_over_tls_mode,      0,                   offsetof(Manager, dns_over_tls_mode)
 Resolve.Cache,           config_parse_bool,                   0,                   offsetof(Manager, enable_cache)
 Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0,                   offsetof(Manager, dns_stub_listener_mode)
+Resolve.ReadEtcHosts,    config_parse_bool,                   0,                   offsetof(Manager, read_etc_hosts)
index 44a53952d2450b7d3befd2ed43a74bae4a9eea6a..03ca5188cb8ca2d6cf5dd428f98955155c9f351e 100644 (file)
@@ -581,6 +581,7 @@ int manager_new(Manager **ret) {
         m->read_resolv_conf = true;
         m->need_builtin_fallbacks = true;
         m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
+        m->read_etc_hosts = true;
 
         r = dns_trust_anchor_load(&m->trust_anchor);
         if (r < 0)
index be7ad2d10ddb15b1e0cb22d0993df1bb6a5b00e7..79a473f57efb891affc20a8cf98a119038339a45 100644 (file)
@@ -23,6 +23,12 @@ typedef struct Manager Manager;
 #define MANAGER_SEARCH_DOMAINS_MAX 32
 #define MANAGER_DNS_SERVERS_MAX 32
 
+typedef struct EtcHosts {
+        Hashmap *by_address;
+        Hashmap *by_name;
+        Set *no_address;
+} EtcHosts;
+
 struct Manager {
         sd_event *event;
 
@@ -114,9 +120,9 @@ struct Manager {
         unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX];
 
         /* Data from /etc/hosts */
-        Set* etc_hosts_by_address;
-        Hashmap* etc_hosts_by_name;
+        EtcHosts etc_hosts;
         usec_t etc_hosts_last, etc_hosts_mtime;
+        bool read_etc_hosts;
 
         /* Local DNS stub on 127.0.0.53:53 */
         int dns_stub_udp_fd;
index 2528340f74ad8af1db3a34709a65cc4ec6053fee..e559291f66ca88187203b106064851bcb253b2ab 100644 (file)
@@ -21,3 +21,4 @@
 #DNSOverTLS=@DEFAULT_DNS_OVER_TLS_MODE@
 #Cache=yes
 #DNSStubListener=udp
+#ReadEtcHosts=yes
index 842d42b3110e9e7fa84b84a174c674848c200a0a..2230a66ef5e21397adda728afe8ef125e9bfd4f6 100644 (file)
@@ -1,11 +1,18 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "dns-type.h"
+#include "resolved-dns-dnssec.h"
+#include "resolved-dns-packet.h"
 #include "test-tables.h"
 
 int main(int argc, char **argv) {
         uint16_t i;
 
+        test_table(dns_protocol, DNS_PROTOCOL);
+        test_table(dnssec_result, DNSSEC_RESULT);
+        test_table(dnssec_verdict, DNSSEC_VERDICT);
+
+        test_table_sparse(dns_rcode, DNS_RCODE);
         test_table_sparse(dns_type, DNS_TYPE);
 
         log_info("/* DNS_TYPE */");
diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c
new file mode 100644 (file)
index 0000000..5b8f1e2
--- /dev/null
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "resolved-etc-hosts.h"
+
+static void test_parse_etc_hosts_system(void) {
+        _cleanup_fclose_ FILE *f = NULL;
+
+        f = fopen("/etc/hosts", "r");
+        if (!f) {
+                assert_se(errno == -ENOENT);
+                return;
+        }
+
+        _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+        assert_se(etc_hosts_parse(&hosts, f) == 0);
+}
+
+static void test_parse_etc_hosts(const char *fname) {
+        _cleanup_(unlink_tempfilep) char
+                t[] = "/tmp/test-resolved-etc-hosts.XXXXXX";
+
+        int fd;
+        _cleanup_fclose_ FILE *f;
+
+        if (fname) {
+                f = fopen(fname, "r");
+                assert_se(f);
+        } else {
+                fd = mkostemp_safe(t);
+                assert_se(fd >= 0);
+
+                f = fdopen(fd, "r+");
+                fputs("1.2.3.4 some.where\n", f);
+                fputs("1.2.3.5 some.where\n", f);
+                fputs("::0 some.where some.other\n", f);
+                fputs("0.0.0.0 black.listed\n", f);
+                fputs("::5 some.where some.other foobar.foo.foo\n", f);
+                fputs("        \n", f);
+                fflush(f);
+                rewind(f);
+        }
+
+        _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+        assert_se(etc_hosts_parse(&hosts, f) == 0);
+
+        if (fname)
+                return;
+                
+        EtcHostsItemByName *bn;
+        assert_se(bn = hashmap_get(hosts.by_name, "some.where"));
+        assert_se(bn->n_addresses == 3);
+        assert_se(bn->n_allocated >= 3);
+
+        assert_se(bn->addresses[0]->family == AF_INET);
+        assert_se(memcmp(&bn->addresses[0]->address.in,
+                         &(struct in_addr) { .s_addr = htobe32(0x01020304) }, 4) == 0);
+        assert_se(bn->addresses[1]->family == AF_INET);
+        assert_se(memcmp(&bn->addresses[1]->address.in,
+                         &(struct in_addr) { .s_addr = htobe32(0x01020305) }, 4) == 0);
+        assert_se(bn->addresses[2]->family == AF_INET6);
+        assert_se(memcmp(&bn->addresses[2]->address.in6,
+                         &(struct in6_addr) { .s6_addr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5} }, 16 ) == 0);
+
+        assert_se(bn = hashmap_get(hosts.by_name, "some.other"));
+        assert_se(bn->n_addresses == 1);
+        assert_se(bn->n_allocated >= 1);
+        assert_se(bn->addresses[0]->family == AF_INET6);
+        assert_se(memcmp(&bn->addresses[0]->address.in6,
+                         &(struct in6_addr) { .s6_addr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5} }, 16 ) == 0);
+
+        assert_se( set_contains(hosts.no_address, "some.where"));
+        assert_se( set_contains(hosts.no_address, "some.other"));
+        assert_se( set_contains(hosts.no_address, "black.listed"));
+        assert_se(!set_contains(hosts.no_address, "foobar.foo.foo"));
+}
+
+int main(int argc, char **argv) {
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        if (argc == 1)
+                test_parse_etc_hosts_system();
+        test_parse_etc_hosts(argv[1]);
+
+        return 0;
+}
index 0c713678e2da6639b750f5ef9e5e789165c433e1..28b830bd419a53d983a00dc47d5957fd186035ec 100644 (file)
@@ -422,16 +422,16 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                         return 1;
                 }
 
-                r = parse_percent(eq);
+                r = parse_permille(eq);
                 if (r >= 0) {
                         char *n;
 
-                        /* When this is a percentage we'll convert this into a relative value in the range
-                         * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
-                         * ones). This way the physical memory size can be determined server-side */
+                        /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
+                         * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
+                         * size can be determined server-side. */
 
                         n = strjoina(field, "Scale");
-                        r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+                        r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -449,13 +449,15 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
                 else {
-                        r = parse_percent_unbounded(eq);
-                        if (r <= 0) {
-                                log_error_errno(r, "CPU quota '%s' invalid.", eq);
-                                return -EINVAL;
+                        r = parse_permille_unbounded(eq);
+                        if (r == 0) {
+                                log_error("CPU quota too small.");
+                                return -ERANGE;
                         }
+                        if (r < 0)
+                                return log_error_errno(r, "CPU quota '%s' invalid.", eq);
 
-                        r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
+                        r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 1000U));
                 }
 
                 if (r < 0)
@@ -503,9 +505,9 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                         path = strndupa(eq, e - eq);
                         bandwidth = e+1;
 
-                        if (streq(bandwidth, "infinity")) {
+                        if (streq(bandwidth, "infinity"))
                                 bytes = CGROUP_LIMIT_MAX;
-                        else {
+                        else {
                                 r = parse_size(bandwidth, 1000, &bytes);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
@@ -1189,7 +1191,7 @@ static int bus_append_kill_property(sd_bus_message *m, const char *field, const
 
                 return bus_append_parse_boolean(m, field, eq);
 
-        if (streq(field, "KillSignal"))
+        if (STR_IN_SET(field, "KillSignal", "FinalKillSignal"))
 
                 return bus_append_signal_from_string(m, field, eq);
 
index cb1d43c828091bc783e0a91045e2b110ead52b2a..17370f404844c9bd6760284aaeff11f37fe424d8 100644 (file)
@@ -106,7 +106,7 @@ shared_sources = files('''
 '''.split())
 
 test_tables_h = files('test-tables.h')
-shared_sources += [test_tables_h]
+shared_sources += test_tables_h
 
 if conf.get('HAVE_ACL') == 1
         shared_sources += files('acl-util.c')
index 9e4ce183d3635f3b49d5890cf75c07f42e0a81e3..2ffd32bf999155bb83d861a6da23855a8c13cd4b 100644 (file)
@@ -9,9 +9,12 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/utsname.h>
 #include <syslog.h>
 #include <unistd.h>
 
+#include "sd-id128.h"
+
 #include "alloc-util.h"
 #include "conf-parser.h"
 #include "def.h"
@@ -21,6 +24,7 @@
 #include "log.h"
 #include "macro.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "sleep-config.h"
 #include "string-util.h"
 #include "strv.h"
@@ -138,12 +142,16 @@ int can_sleep_disk(char **types) {
                 return true;
 
         /* If /sys is read-only we cannot sleep */
-        if (access("/sys/power/disk", W_OK) < 0)
+        if (access("/sys/power/disk", W_OK) < 0) {
+                log_debug_errno(errno, "/sys/power/disk is not writable: %m");
                 return false;
+        }
 
         r = read_one_line_file("/sys/power/disk", &p);
-        if (r < 0)
+        if (r < 0) {
+                log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
                 return false;
+        }
 
         STRV_FOREACH(type, types) {
                 const char *word, *state;
@@ -193,18 +201,30 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us
                            "%zu "   /* used */
                            "%*i\n", /* priority */
                            &dev_field, &type_field, &size_field, &used_field);
+                if (k == EOF)
+                        break;
                 if (k != 4) {
-                        if (k == EOF)
-                                break;
-
                         log_warning("Failed to parse /proc/swaps:%u", i);
                         continue;
                 }
 
-                if (streq(type_field, "partition") && endswith(dev_field, "\\040(deleted)")) {
-                        log_warning("Ignoring deleted swapfile '%s'.", dev_field);
-                        continue;
+                if (streq(type_field, "file")) {
+
+                        if (endswith(dev_field, "\\040(deleted)")) {
+                                log_warning("Ignoring deleted swap file '%s'.", dev_field);
+                                continue;
+                        }
+
+                } else if (streq(type_field, "partition")) {
+                        const char *fn;
+
+                        fn = path_startswith(dev_field, "/dev/");
+                        if (fn && startswith(fn, "zram")) {
+                                log_debug("Ignoring compressed RAM swap device '%s'.", dev_field);
+                                continue;
+                        }
                 }
+
                 if (device)
                         *device = TAKE_PTR(dev_field);
                 if (type)
@@ -235,14 +255,13 @@ static bool enough_swap_for_hibernation(void) {
 
         r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
         if (r < 0) {
-                log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
+                log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
                 return false;
         }
 
         r = safe_atollu(active, &act);
         if (r < 0) {
-                log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
-                                active);
+                log_debug_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", active);
                 return false;
         }
 
@@ -253,6 +272,72 @@ static bool enough_swap_for_hibernation(void) {
         return r;
 }
 
+static int kernel_exists(void) {
+        struct utsname u;
+        sd_id128_t m;
+        int i, r;
+
+        /* Do some superficial checks whether the kernel we are currently running is still around. If it isn't we
+         * shouldn't offer hibernation as we couldn't possible resume from hibernation again. Of course, this check is
+         * very superficial, as the kernel's mere existance is hardly enough to know whether the hibernate/resume cycle
+         * will succeed. However, the common case of kernel updates can be caught this way, and it's definitely worth
+         * covering that. */
+
+        for (i = 0;; i++) {
+                _cleanup_free_ char *path = NULL;
+
+                switch (i) {
+
+                case 0:
+                        /* First, let's look in /lib/modules/`uname -r`/vmlinuz. This is where current Fedora places
+                         * its RPM-managed kernels. It's a good place, as it means compiled vendor code is monopolized
+                         * in /usr, and then the kernel image is stored along with its modules in the same
+                         * hierarchy. It's also what our 'kernel-install' script is written for. */
+                        if (uname(&u) < 0)
+                                return log_debug_errno(errno, "Failed to acquire kernel release: %m");
+
+                        path = strjoin("/lib/modules/", u.release, "/vmlinuz");
+                        break;
+
+                case 1:
+                        /* Secondly, let's look in /boot/vmlinuz-`uname -r`. This is where older Fedora and other
+                         * distributions tend to place the kernel. */
+                        path = strjoin("/boot/vmlinuz-", u.release);
+                        break;
+
+                case 2:
+                        /* For the other cases, we look in the EFI/boot partition, at the place where our
+                         * "kernel-install" script copies the kernel on install by default. */
+                        r = sd_id128_get_machine(&m);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to read machine ID: %m");
+
+                        (void) asprintf(&path, "/efi/" SD_ID128_FORMAT_STR "/%s/linux", SD_ID128_FORMAT_VAL(m), u.release);
+                        break;
+                case 3:
+                        (void) asprintf(&path, "/boot/" SD_ID128_FORMAT_STR "/%s/linux", SD_ID128_FORMAT_VAL(m), u.release);
+                        break;
+                case 4:
+                        (void) asprintf(&path, "/boot/efi/" SD_ID128_FORMAT_STR "/%s/linux", SD_ID128_FORMAT_VAL(m), u.release);
+                        break;
+
+                default:
+                        return false;
+                }
+
+                if (!path)
+                        return -ENOMEM;
+
+                log_debug("Testing whether %s exists.", path);
+
+                if (access(path, F_OK) >= 0)
+                        return true;
+
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Failed to determine whether '%s' exists, ignoring: %m", path);
+        }
+}
+
 int read_fiemap(int fd, struct fiemap **ret) {
         _cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL;
         struct stat statinfo;
@@ -350,7 +435,7 @@ static bool can_s2h(void) {
 
         FOREACH_STRING(p, "suspend", "hibernate") {
                 r = can_sleep(p);
-                if (IN_SET(r, 0, -ENOSPC)) {
+                if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM)) {
                         log_debug("Unable to %s system.", p);
                         return false;
                 }
@@ -380,6 +465,11 @@ int can_sleep(const char *verb) {
         if (streq(verb, "suspend"))
                 return true;
 
+        if (kernel_exists() <= 0) {
+                log_debug_errno(errno, "Couldn't find kernel, not offering hibernation.");
+                return -ENOMEDIUM;
+        }
+
         if (!enough_swap_for_hibernation())
                 return -ENOSPC;
 
index 93c1979d19abfcc32482f4e353e9a5a76f1a7e3b..abd4b92a24d9850c7212b7784f7be306e32a1ea8 100644 (file)
@@ -4931,7 +4931,7 @@ typedef enum SystemctlShowMode{
         _SYSTEMCTL_SHOW_MODE_INVALID = -1,
 } SystemctlShowMode;
 
-static const char* const systemctl_show_mode_table[] = {
+static const char* const systemctl_show_mode_table[_SYSTEMCTL_SHOW_MODE_MAX] = {
         [SYSTEMCTL_SHOW_PROPERTIES] = "show",
         [SYSTEMCTL_SHOW_STATUS] = "status",
         [SYSTEMCTL_SHOW_HELP] = "help",
index 7da7e3a22c7bac8e9ac0b82fbd00561a57df3a6b..18f05b2dc28059c1755fb6e133fc4e89c018df37 100644 (file)
@@ -645,7 +645,7 @@ tests += [
         [['src/test/test-nss.c'],
          [],
          [libdl],
-         '', 'manual'],
+         'ENABLE_NSS', 'manual'],
 
         [['src/test/test-umount.c',
           'src/core/mount-setup.c',
index acf28d82c4c533c1b068bb0f6ebeca0ce4002d4e..9ef18bb2114a04c6f9f9278c4f332167a9c7d6e4 100644 (file)
@@ -105,6 +105,25 @@ invalid:
         return false;
 }
 
+static bool check_user_has_group_with_same_name(const char *name) {
+        struct passwd *p;
+        struct group *g;
+
+        assert(name);
+
+        p = getpwnam(name);
+        if (!p ||
+            !streq(p->pw_name, name))
+                return false;
+
+        g = getgrgid(p->pw_gid);
+        if (!g ||
+            !streq(g->gr_name, name))
+                return false;
+
+        return true;
+}
+
 static bool is_inaccessible_available(void) {
         char *p;
 
@@ -427,6 +446,10 @@ static void test_exec_supplementarygroups(Manager *m) {
 
 static void test_exec_dynamicuser(Manager *m) {
         test(m, "exec-dynamicuser-fixeduser.service", 0, CLD_EXITED);
+        if (check_user_has_group_with_same_name("adm"))
+                test(m, "exec-dynamicuser-fixeduser-adm.service", 0, CLD_EXITED);
+        if (check_user_has_group_with_same_name("games"))
+                test(m, "exec-dynamicuser-fixeduser-games.service", 0, CLD_EXITED);
         test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", 0, CLD_EXITED);
         test(m, "exec-dynamicuser-supplementarygroups.service", 0, CLD_EXITED);
         test(m, "exec-dynamicuser-statedir.service", 0, CLD_EXITED);
index 9e543e75571584257c50897604a979715cc0db95..3c193f8ac2d05751680c2377fe681a84945873a8 100644 (file)
@@ -51,14 +51,12 @@ static const char* af_to_string(int family, char *buf, size_t buf_len) {
 }
 
 static void* open_handle(const char* dir, const char* module, int flags) {
-        const char *path;
+        const char *path = NULL;
         void *handle;
 
-        if (dir) {
+        if (dir)
                 path = strjoina(dir, "/libnss_", module, ".so.2");
-                if (access(path, F_OK) < 0)
-                        path = strjoina(dir, "/.libs/libnss_", module, ".so.2");
-        } else
+        if (!path || access(path, F_OK) < 0)
                 path = strjoina("libnss_", module, ".so.2");
 
         handle = dlopen(path, flags);
@@ -397,9 +395,7 @@ static int test_one_module(const char* dir,
 
         log_info("======== %s ========", module);
 
-        handle = open_handle(streq(module, "dns") ? NULL : dir,
-                             module,
-                             RTLD_LAZY|RTLD_NODELETE);
+        handle = open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE);
         if (!handle)
                 return -EINVAL;
 
@@ -431,13 +427,13 @@ static int parse_argv(int argc, char **argv,
                 modules = strv_new(argv[1], NULL);
         else
                 modules = strv_new(
-#if ENABLE_MYHOSTNAME
+#if ENABLE_NSS_MYHOSTNAME
                                 "myhostname",
 #endif
-#if ENABLE_RESOLVE
+#if ENABLE_NSS_RESOLVE
                                 "resolve",
 #endif
-#if ENABLE_MACHINED
+#if ENABLE_NSS_MYMACHINES
                                 "mymachines",
 #endif
                                 "dns",
index 944104a25a6712391b1666d77973c83c027a1ff3..49268eae22766eba1520595ea67d4488d7dac920 100644 (file)
@@ -5,6 +5,7 @@
 #include "cgroup.h"
 #include "compress.h"
 #include "condition.h"
+#include "device-internal.h"
 #include "device.h"
 #include "execute.h"
 #include "import-util.h"
@@ -24,6 +25,7 @@
 #include "rlimit-util.h"
 #include "scope.h"
 #include "service.h"
+#include "show-status.h"
 #include "slice.h"
 #include "socket-util.h"
 #include "socket.h"
@@ -47,7 +49,9 @@ int main(int argc, char **argv) {
         test_table(collect_mode, COLLECT_MODE);
         test_table(condition_result, CONDITION_RESULT);
         test_table(condition_type, CONDITION_TYPE);
+        test_table(device_action, DEVICE_ACTION);
         test_table(device_state, DEVICE_STATE);
+        test_table(dns_over_tls_mode, DNS_OVER_TLS_MODE);
         test_table(dnssec_mode, DNSSEC_MODE);
         test_table(emergency_action, EMERGENCY_ACTION);
         test_table(exec_directory_type, EXEC_DIRECTORY_TYPE);
@@ -75,6 +79,7 @@ int main(int argc, char **argv) {
         test_table(name_policy, NAMEPOLICY);
         test_table(namespace_type, NAMESPACE_TYPE);
         test_table(notify_access, NOTIFY_ACCESS);
+        test_table(notify_state, NOTIFY_STATE);
         test_table(output_mode, OUTPUT_MODE);
         test_table(partition_designator, PARTITION_DESIGNATOR);
         test_table(path_result, PATH_RESULT);
@@ -91,6 +96,7 @@ int main(int argc, char **argv) {
         test_table(service_result, SERVICE_RESULT);
         test_table(service_state, SERVICE_STATE);
         test_table(service_type, SERVICE_TYPE);
+        test_table(show_status, SHOW_STATUS);
         test_table(slice_state, SLICE_STATE);
         test_table(socket_address_bind_ipv6_only, SOCKET_ADDRESS_BIND_IPV6_ONLY);
         test_table(socket_exec_command, SOCKET_EXEC_COMMAND);
index 4beded5de015dbf8a51ec7ea42782113b9183eb5..012cbe002899e0387f9857a79b7bcef82c93bb77 100644 (file)
@@ -44,6 +44,10 @@ typedef struct Context {
         char *zone;
         bool local_rtc;
         Hashmap *polkit_registry;
+        sd_bus_message *cache;
+
+        sd_bus_slot *slot_job_removed;
+        char *path_ntp_unit;
 
         LIST_HEAD(UnitStatusInfo, units);
 } Context;
@@ -71,6 +75,10 @@ static void context_free(Context *c) {
 
         free(c->zone);
         bus_verify_polkit_async_registry_free(c->polkit_registry);
+        sd_bus_message_unref(c->cache);
+
+        sd_bus_slot_unref(c->slot_job_removed);
+        free(c->path_ntp_unit);
 
         while ((p = c->units)) {
                 LIST_REMOVE(units, c->units, p);
@@ -302,18 +310,20 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m)
                 { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
                 {}
         };
-        static sd_bus_message *_m = NULL;
         UnitStatusInfo *u;
         int r;
 
         assert(c);
         assert(bus);
 
-        /* Suppress multiple call of context_update_ntp_status() within single DBus transaction. */
-        if (m && m == _m)
-                return 0;
+        /* Suppress calling context_update_ntp_status() multiple times within single DBus transaction. */
+        if (m) {
+                if (m == c->cache)
+                        return 0;
 
-        _m = m;
+                sd_bus_message_unref(c->cache);
+                c->cache = sd_bus_message_ref(m);
+        }
 
         LIST_FOREACH(units, u, c->units) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -341,17 +351,55 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m)
         return 0;
 }
 
-static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        const char *path;
+        Context *c = userdata;
         int r;
 
+        assert(c);
+        assert(m);
+
+        r = sd_bus_message_read(m, "uoss", NULL, &path, NULL, NULL);
+        if (r < 0) {
+                bus_log_parse_error(r);
+                return 0;
+        }
+
+        if (!streq_ptr(path, c->path_ntp_unit))
+                return 0;
+
+        (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
+
+        c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
+        c->path_ntp_unit = mfree(c->path_ntp_unit);
+
+        return 0;
+}
+
+static int unit_start_or_stop(Context *c, UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
+        const char *path;
+        int r;
+
+        assert(c);
         assert(u);
         assert(bus);
         assert(error);
 
-        /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
+        /* This method may be called frequently. Forget the previous job if it has not completed yet. */
+        c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
 
-        if (streq(u->active_state, "active") == start)
-                return 0;
+        r = sd_bus_match_signal_async(
+                        bus,
+                        &slot,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "JobRemoved",
+                        match_job_removed, NULL, c);
+        if (r < 0)
+                return r;
 
         r = sd_bus_call_method(
                 bus,
@@ -360,13 +408,22 @@ static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *erro
                 "org.freedesktop.systemd1.Manager",
                 start ? "StartUnit" : "StopUnit",
                 error,
-                NULL,
+                &reply,
                 "ss",
                 u->name,
                 "replace");
         if (r < 0)
                 return r;
 
+        r = sd_bus_message_read(reply, "o", &path);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = free_and_strdup(&c->path_ntp_unit, path);
+        if (r < 0)
+                return log_oom();
+
+        c->slot_job_removed = TAKE_PTR(slot);
         return 0;
 }
 
@@ -418,8 +475,9 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *
                         error,
                         NULL,
                         NULL);
-         if (r < 0)
-                 return r;
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -809,7 +867,7 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
                         if (q < 0)
                                 r = q;
 
-                        q = unit_start_or_stop(u, bus, error, enable);
+                        q = unit_start_or_stop(c, u, bus, error, enable);
                         if (q < 0)
                                 r = q;
                 }
@@ -823,17 +881,17 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
                         if (r < 0)
                                 continue;
 
-                        r = unit_start_or_stop(u, bus, error, enable);
+                        r = unit_start_or_stop(c, u, bus, error, enable);
                         break;
                 }
 
-        else if (context_ntp_service_is_active(c) <= 0)
+        else
                 LIST_FOREACH(units, u, c->units) {
                         if (!streq(u->load_state, "loaded") ||
                             !streq(u->unit_file_state, "enabled"))
                                 continue;
 
-                        r = unit_start_or_stop(u, bus, error, enable);
+                        r = unit_start_or_stop(c, u, bus, error, enable);
                         break;
                 }
 
@@ -842,8 +900,6 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
 
         log_info("Set NTP to %sd", enable_disable(enable));
 
-        (void) sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
-
         return sd_bus_reply_method_return(m, NULL);
 }
 
index 82b83547633589ff1289e49dde6f38b7d47c1627..39fd03d152c433595bbee0b2d7a569090a04614f 100644 (file)
@@ -59,7 +59,7 @@ static inline struct _mate *node_to_mate(struct udev_list_node *node)
 
 _noreturn_ static void sig_alrm(int signo)
 {
-        exit(4);
+        _exit(4);
 }
 
 static void usage(void)
index 147e04ab8ca6736a5c44bd47c049af7b5c8e835f..4dddc8677387560c472d8dd4e6f43c306c4e12eb 100644 (file)
@@ -34,6 +34,9 @@
  * All multi-function PCI devices will carry the [f<function>] number in the
  * device name, including the function 0 device.
  *
+ * SR-IOV virtual devices are named based on the name of the parent interface,
+ * with a suffix of "v<N>", where <N> is the virtual device number.
+ *
  * When using PCI geography, The PCI domain is only prepended when it is not 0.
  *
  * For USB devices the full chain of port numbers of hubs is composed. If the
index 9b5990bc608efb00089e4780be4ac1c0f1f9c700..40eac300764e96c221fd0213c32f2bcff1a18736 100755 (executable)
@@ -6,7 +6,7 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
-QEMU_TIMEOUT=90
+QEMU_TIMEOUT=180
 
 test_setup() {
     create_empty_image
@@ -31,7 +31,7 @@ Type=oneshot
 ExecStart=/bin/sh -c '>/testok'
 RemainAfterExit=yes
 ExecStop=/bin/sh -c 'kill -SEGV $$$$'
-TimeoutStopSec=180s
+TimeoutStopSec=270s
 EOF
 
         setup_testsuite
diff --git a/test/TEST-23-TYPE-EXEC/Makefile b/test/TEST-23-TYPE-EXEC/Makefile
new file mode 100644 (file)
index 0000000..34d7cc6
--- /dev/null
@@ -0,0 +1,4 @@
+BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
+
+all setup clean run:
+       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
diff --git a/test/TEST-23-TYPE-EXEC/test.sh b/test/TEST-23-TYPE-EXEC/test.sh
new file mode 100755 (executable)
index 0000000..bdcea23
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test Type=exec"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+    create_empty_image
+    mkdir -p $TESTDIR/root
+    mount ${LOOPDEV}p1 $TESTDIR/root
+
+    (
+        LOG_LEVEL=5
+        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+        setup_basic_environment
+
+        # setup the testsuite service
+        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+NotifyAccess=all
+EOF
+        cp testsuite.sh $initdir/
+
+        setup_testsuite
+    ) || return 1
+    setup_nspawn_root
+
+    ddebug "umount $TESTDIR/root"
+    umount $TESTDIR/root
+}
+
+do_test "$@"
diff --git a/test/TEST-23-TYPE-EXEC/testsuite.sh b/test/TEST-23-TYPE-EXEC/testsuite.sh
new file mode 100755 (executable)
index 0000000..80734bb
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+systemd-analyze set-log-level debug
+systemd-analyze set-log-target console
+
+# Create a binary for which execve() will fail
+touch /tmp/brokenbinary
+chmod +x /tmp/brokenbinary
+
+# These three commands should succeed.
+systemd-run --unit=one -p Type=simple /bin/sleep infinity
+systemd-run --unit=two -p Type=simple -p User=idontexist /bin/sleep infinity
+systemd-run --unit=three -p Type=simple /tmp/brokenbinary
+
+# And now, do the same with Type=exec, where the latter two should fail
+systemd-run --unit=four -p Type=exec /bin/sleep infinity
+! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
+! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
+
+systemd-analyze set-log-level info
+
+echo OK > /testok
+
+exit 0
index 4aab7437e404e1b233e05ee9dede2217ec86a17e..616ffb972892a716c03fe9330d6c78f04a324068 100644 (file)
@@ -45,6 +45,8 @@ test_data_files = '''
         test-execute/exec-cpuaffinity1.service
         test-execute/exec-cpuaffinity2.service
         test-execute/exec-cpuaffinity3.service
+        test-execute/exec-dynamicuser-fixeduser-adm.service
+        test-execute/exec-dynamicuser-fixeduser-games.service
         test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
         test-execute/exec-dynamicuser-fixeduser.service
         test-execute/exec-dynamicuser-statedir-migrate-step1.service
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-adm.service b/test/test-execute/exec-dynamicuser-fixeduser-adm.service
new file mode 100644 (file)
index 0000000..90040ee
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Test DynamicUser with static User= whose uid and gid are different
+# On Fedora, user adm has uid==3 and gid==4.
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+# Multiple ExecStart= lines causes the issue #9702.
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+DynamicUser=yes
+User=adm
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-games.service b/test/test-execute/exec-dynamicuser-fixeduser-games.service
new file mode 100644 (file)
index 0000000..1cc9518
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Test DynamicUser with static User= whose uid and gid are different
+# On Ubuntu or Debian, user games has uid==5 and gid==60.
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+# Multiple ExecStart= lines causes the issue #9702.
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+DynamicUser=yes
+User=games
index 6aae7c15348be7e2d6c1458234757c24089e00d3..c8bfcba8c0f067fdd06eaaae69bffbbe55321146 100755 (executable)
@@ -1,7 +1,7 @@
+# SPDX-License-Identifier: CC0-1.0
 #!/usr/bin/env perl
 #
 # checkincludes: Find files included more than once in (other) files.
-# Copyright abandoned, 2000, Niels Kristian Bech Jensen <nkbj@image.dk>.
 
 foreach $file (@ARGV) {
        open(FILE, $file) or die "Cannot open $file: $!.\n";
diff --git a/tools/meson-link-test.c b/tools/meson-link-test.c
deleted file mode 100644 (file)
index 825bbff..0000000
+++ /dev/null
@@ -1 +0,0 @@
-int main(void) {return 0;}
index 07107a66ee28f4c1660bfdbaf0b9b0591993741a..f840c4114c02bbbaf820763509eb2c22134595fa 100644 (file)
@@ -10,8 +10,7 @@
 [Unit]
 Description=User Manager for UID %i
 Documentation=man:user@.service(5)
-After=systemd-user-sessions.service
-After=user-runtime-dir@%i.service
+After=systemd-user-sessions.service user-runtime-dir@%i.service dbus.service
 Requires=user-runtime-dir@%i.service
 
 [Service]