]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #3909 from poettering/mount-tool
authorEvgeny Vereshchagin <evvers@ya.ru>
Fri, 19 Aug 2016 20:33:49 +0000 (23:33 +0300)
committerGitHub <noreply@github.com>
Fri, 19 Aug 2016 20:33:49 +0000 (23:33 +0300)
add a new tool for creating transient mount and automount units

64 files changed:
Makefile.am
hwdb/60-evdev.hwdb
hwdb/70-mouse.hwdb
hwdb/parse_hwdb.py
man/hostnamectl.xml
man/logind.conf.xml
man/systemctl.xml
man/systemd-inhibit.xml
man/systemd-logind.service.xml
man/systemd.exec.xml
man/systemd.preset.xml
man/systemd.unit.xml
man/systemd.xml
src/basic/cgroup-util.h
src/basic/hostname-util.c
src/basic/terminal-util.c
src/basic/user-util.h
src/core/cgroup.c
src/core/dbus-execute.c
src/core/dbus-mount.c
src/core/dbus-service.c
src/core/dbus-socket.c
src/core/dbus-swap.c
src/core/dbus-unit.c
src/core/dynamic-user.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment.c
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/service.c
src/core/socket.c
src/core/swap.c
src/core/unit.c
src/core/unit.h
src/hostname/hostnamectl.c
src/journal/journald-server.c
src/libudev/libudev-device.c
src/login/logind-action.c
src/login/logind-user.c
src/login/logind.c
src/network/networkd-route.c
src/nss-mymachines/nss-mymachines.c
src/nss-systemd/nss-systemd.c
src/shared/bus-unit-util.c
src/shared/clean-ipc.c
src/shared/clean-ipc.h
src/shared/install.c
src/test/test-condition.c
src/test/test-hostname-util.c
src/test/test-ipcrm.c
system-preset/90-systemd.preset
test/udev-test.pl
units/user/bluetooth.target [new symlink]
units/user/busnames.target [new symlink]
units/user/paths.target [new symlink]
units/user/printer.target [new symlink]
units/user/shutdown.target [new symlink]
units/user/smartcard.target [new symlink]
units/user/sockets.target [new symlink]
units/user/sound.target [new symlink]
units/user/timers.target [new symlink]

index 336f12880c9c473ba2762caf4259385e9e6c12dc..431975de8b4b0f09869ccce31dc876bad196137d 100644 (file)
@@ -576,7 +576,16 @@ dist_userunit_DATA = \
        units/user/default.target \
        units/user/exit.target \
        units/user/graphical-session.target \
-       units/user/graphical-session-pre.target
+       units/user/graphical-session-pre.target \
+       units/user/bluetooth.target \
+       units/user/busnames.target \
+       units/user/paths.target \
+       units/user/printer.target \
+       units/user/shutdown.target \
+       units/user/smartcard.target \
+       units/user/sockets.target \
+       units/user/sound.target \
+       units/user/timers.target
 
 nodist_userunit_DATA = \
        units/user/systemd-exit.service
@@ -635,6 +644,10 @@ EXTRA_DIST += \
        units/rc-local.service.in \
        units/halt-local.service.in
 
+GENERAL_ALIASES += \
+       $(systemunitdir)/reboot.target $(pkgsysconfdir)/system/ctrl-alt-del.target \
+       $(systemunitdir)/machines.target $(pkgsysconfdir)/system/multi-user.target.wants/machines.target
+
 # automake is broken and can't handle files with a dash in front
 # http://debbugs.gnu.org/cgi/bugreport.cgi?bug=14728#8
 units-install-hook:
@@ -6300,19 +6313,6 @@ SYSTEM_UNIT_ALIASES += \
        reboot.target ctrl-alt-del.target \
        getty@.service autovt@.service
 
-USER_UNIT_ALIASES += \
-       $(systemunitdir)/shutdown.target shutdown.target \
-       $(systemunitdir)/sockets.target sockets.target \
-       $(systemunitdir)/timers.target timers.target \
-       $(systemunitdir)/paths.target paths.target \
-       $(systemunitdir)/bluetooth.target bluetooth.target \
-       $(systemunitdir)/printer.target printer.target \
-       $(systemunitdir)/sound.target sound.target \
-       $(systemunitdir)/smartcard.target smartcard.target
-
-USER_UNIT_ALIASES += \
-       $(systemunitdir)/busnames.target busnames.target
-
 GENERAL_ALIASES += \
        $(systemunitdir)/remote-fs.target $(pkgsysconfdir)/system/multi-user.target.wants/remote-fs.target \
        $(systemunitdir)/getty@.service $(pkgsysconfdir)/system/getty.target.wants/getty@tty1.service \
index 4d14a6a2f4bec4b4583248621a763a252f39b2cd..e738ff7be5c6c5606fa339cfe84fd36628285943 100644 (file)
@@ -134,6 +134,13 @@ evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLati
  EVDEV_ABS_35=76:1815:22
  EVDEV_ABS_36=131:1330:30
 
+# Dell Precision 5510
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnPrecision5510*
+ EVDEV_ABS_00=::42
+ EVDEV_ABS_01=::43
+ EVDEV_ABS_35=::42
+ EVDEV_ABS_36=::43
+
 # Dell Precision M4700
 evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:*svnDellInc.:pnPrecisionM4700*
  EVDEV_ABS_00=0:1960:24
index d8215a6179851ace83fb15c59c761404f12eafb5..56e36af0e52b72af388f78b9510c2236d8fbfbe8 100644 (file)
@@ -47,6 +47,7 @@
 #    ID_INPUT_TRACKBALL
 #    MOUSE_DPI
 #    MOUSE_WHEEL_CLICK_ANGLE
+#    MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL
 #
 #########################################
 #         ID_INPUT_TRACKBALL            #
 #
 # Most mice have a 15 degree click stop (24 clicks per full rotation).
 #
+#########################################
+#   MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL  #
+#########################################
+#
+# Identical to MOUSE_WHEEL_CLICK_ANGLE but for the horizontal scroll wheel.
+# This property may only be specified if the angle for the horizontal
+# scroll wheel differs from the vertical wheel. If so, *both* click angles
+# must be specified.
 
 #
 # Sort by brand, type (usb, bluetooth), DPI, frequency.
@@ -344,9 +353,14 @@ mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:402d:
 
 # Logitech Performance MX
 mouse:usb:v046dp101a:name:Logitech Performance MX:
+ MOUSE_DPI=1000@166
+
 # Logitech MX Master
+# Horiz wheel has 14 stops, angle is rounded up
 mouse:usb:v046dp4041:name:Logitech MX Master:
  MOUSE_DPI=1000@166
+ MOUSE_WHEEL_CLICK_ANGLE=15
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
 
 # Logitech MK260 Wireless Combo Receiver aka M-R0011
 mouse:usb:v046dpc52e:name:Logitech USB Receiver:
index 99d034b4e05539e25734d98cc62de3f0cb115325..f55562250d3e06a5de6d661b02da4b974fc7c42d 100755 (executable)
@@ -82,6 +82,7 @@ def property_grammar():
     setting = Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ')
     props = (('MOUSE_DPI', Group(OneOrMore(setting('SETTINGS*')))),
              ('MOUSE_WHEEL_CLICK_ANGLE', INTEGER),
+             ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER),
              ('ID_INPUT_TRACKBALL', Literal('1')),
              ('POINTINGSTICK_SENSITIVITY', INTEGER),
              ('POINTINGSTICK_CONST_ACCEL', REAL),
index 60004e9d041bacf452306ead8737353c664c8acd..9e1b593e6d9140b9e72805cecc7ee1e90fd4d950 100644 (file)
     set, and is valid (something other than localhost), then the
     transient hostname is not used.</para>
 
-    <para>Note that the pretty hostname has little restrictions on the
-    characters used, while the static and transient hostnames are
-    limited to the usually accepted characters of Internet domain
-    names.</para>
+    <para>Note that the pretty hostname has little restrictions on the characters and length used, while the static and
+    transient hostnames are limited to the usually accepted characters of Internet domain names, and 64 characters at
+    maximum (the latter being a Linux limitation).</para>
 
     <para>The static hostname is stored in
     <filename>/etc/hostname</filename>, see
         <term><option>--transient</option></term>
         <term><option>--pretty</option></term>
 
-        <listitem><para>If <command>status</command> is used (or no
-        explicit command is given) and one of those fields is given,
-        <command>hostnamectl</command> will print out just this
-        selected hostname.</para>
+        <listitem><para>If <command>status</command> is invoked (or no explicit command is given) and one of these
+        switches is specified, <command>hostnamectl</command> will print out just this selected hostname.</para>
 
-        <para>If used with <command>set-hostname</command>, only the
-        selected hostname(s) will be updated. When more than one of
-        those options is used, all the specified hostnames will be
-        updated. </para></listitem>
+        <para>If used with <command>set-hostname</command>, only the selected hostname(s) will be updated. When more
+        than one of these switches are specified, all the specified hostnames will be updated. </para></listitem>
       </varlistentry>
 
       <xi:include href="user-system-options.xml" xpointer="host" />
       <varlistentry>
         <term><command>set-hostname <replaceable>NAME</replaceable></command></term>
 
-        <listitem><para>Set the system hostname to
-        <replaceable>NAME</replaceable>. By default, this will alter
-        the pretty, the static, and the transient hostname alike;
-        however, if one or more of <option>--static</option>,
-        <option>--transient</option>, <option>--pretty</option> are
-        used, only the selected hostnames are changed. If the pretty
-        hostname is being set, and static or transient are being set
-        as well, the specified hostname will be simplified in regards
-        to the character set used before the latter are updated. This
-        is done by replacing spaces with <literal>-</literal> and
-        removing special characters. This ensures that the pretty and
-        the static hostname are always closely related while still
-        following the validity rules of the specific name. This
-        simplification of the hostname string is not done if only the
-        transient and/or static host names are set, and the pretty
-        host name is left untouched.</para>
+        <listitem><para>Set the system hostname to <replaceable>NAME</replaceable>. By default, this will alter the
+        pretty, the static, and the transient hostname alike; however, if one or more of <option>--static</option>,
+        <option>--transient</option>, <option>--pretty</option> are used, only the selected hostnames are changed. If
+        the pretty hostname is being set, and static or transient are being set as well, the specified hostname will be
+        simplified in regards to the character set used before the latter are updated. This is done by removing special
+        characters and spaces. This ensures that the pretty and the static hostname are always closely related while
+        still following the validity rules of the specific name. This simplification of the hostname string is not done
+        if only the transient and/or static host names are set, and the pretty host name is left untouched.</para>
 
         <para>Pass the empty string <literal></literal> as the
         hostname to reset the selected hostnames to their default
index adba5a41314c9412d6fae45f61e7310dbe1aa8ea..9b0e18184958705b6354c9b6ccd0a1ae8a4f620e 100644 (file)
         <term><varname>HandleLidSwitch=</varname></term>
         <term><varname>HandleLidSwitchDocked=</varname></term>
 
-        <listitem><para>Controls whether logind shall handle the
+        <listitem><para>Controls how logind shall handle the
         system power and sleep keys and the lid switch to trigger
         actions such as system power-off or suspend. Can be one of
         <literal>ignore</literal>,
         docking station, or if more than one display is connected, the
         action specified by <varname>HandleLidSwitchDocked=</varname>
         occurs; otherwise the <varname>HandleLidSwitch=</varname>
-        action occurs.</para></listitem>
+        action occurs.</para>
+
+        <para>A different application may disable logind's handling of system power and
+        sleep keys and the lid switch by taking a low-level inhibitor lock
+        ("handle-power-key", "handle-suspend-key", "handle-hibernate-key",
+        "handle-lid-switch"). This is most commonly used by graphical desktop environments
+        to take over suspend and hibernation handling, and to use their own configuration
+        mechanisms. If a low-level inhibitor lock is taken, logind will not take any
+        action when that key or switch is triggered and the <varname>Handle*=</varname>
+        settings are irrelevant.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>HibernateKeyIgnoreInhibited=</varname></term>
         <term><varname>LidSwitchIgnoreInhibited=</varname></term>
 
-        <listitem><para>Controls whether actions triggered by the
-        power and sleep keys and the lid switch are subject to
-        inhibitor locks. These settings take boolean arguments. If
-        <literal>no</literal>, the inhibitor locks taken by
-        applications in order to block the requested operation are
-        respected. If <literal>yes</literal>, the requested operation
-        is executed in any case.
+        <listitem><para>Controls whether actions that <command>systemd-logind</command>
+        takes when the power and sleep keys and the lid switch are triggered are subject
+        to high-level inhibitor locks ("shutdown", "sleep", "idle"). Low level inhibitor
+        locks ("handle-*-key"), are always honoured, irrespective of this setting.</para>
+
+        <para>These settings take boolean arguments. If <literal>no</literal>, the
+        inhibitor locks taken by applications are respected. If <literal>yes</literal>,
+        "shutdown", "sleep", and "idle" inhibitor locks are ignored.
         <varname>PowerKeyIgnoreInhibited=</varname>,
-        <varname>SuspendKeyIgnoreInhibited=</varname> and
-        <varname>HibernateKeyIgnoreInhibited=</varname> default to
-        <literal>no</literal>.
-        <varname>LidSwitchIgnoreInhibited=</varname> defaults to
-        <literal>yes</literal>. This means that the lid switch does
-        not respect suspend blockers by default, but the power and
-        sleep keys do. </para></listitem>
+        <varname>SuspendKeyIgnoreInhibited=</varname>, and
+        <varname>HibernateKeyIgnoreInhibited=</varname> default to <literal>no</literal>.
+        <varname>LidSwitchIgnoreInhibited=</varname> defaults to <literal>yes</literal>.
+        This means that when <command>systemd-logind</command> is handling events by
+        itself (no low level inhibitor locks are taken by another application), the lid
+        switch does not respect suspend blockers by default, but the power and sleep keys
+        do.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Sets the maximum number of OS tasks each user may run concurrently. This controls the
         <varname>TasksMax=</varname> setting of the per-user slice unit, see
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for details. Defaults to 33%, which equals 10813 with the kernel's defaults on the host, but might be smaller
-        in OS containers.</para></listitem>
+        for details. If assigned the special value <literal>infinity</literal>, no tasks limit is applied.
+        Defaults to 33%, which equals 10813 with the kernel's defaults on the host, but might be smaller in
+        OS containers.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 0ad0ad6d7e4f7902ac48e5291f8128886518a09d..fde4f4f3bbc8e7dbe89b9c168771cb8c244c044a 100644 (file)
@@ -1092,7 +1092,8 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
             enabled and disabled, or only enabled, or only disabled.</para>
 
             <para>If the unit carries no install information, it will be silently ignored
-            by this command.</para>
+            by this command. <replaceable>NAME</replaceable> must be the real unit name,
+            any alias names are ignored silently.</para>
 
             <para>For more information on the preset policy format, see
             <citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
@@ -1680,20 +1681,15 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
           <term><command>switch-root <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></command></term>
 
           <listitem>
-            <para>Switches to a different root directory and executes a
-            new system manager process below it. This is intended for
-            usage in initial RAM disks ("initrd"), and will transition
-            from the initrd's system manager process (a.k.a. "init"
-            process) to the main system manager process. This call takes two
-            arguments: the directory that is to become the new root directory, and
-            the path to the new system manager binary below it to
-            execute as PID 1. If the latter is omitted or the empty
-            string, a systemd binary will automatically be searched for
-            and used as init. If the system manager path is omitted or
-            equal to the empty string, the state of the initrd's system
-            manager process is passed to the main system manager, which
-            allows later introspection of the state of the services
-            involved in the initrd boot.</para>
+            <para>Switches to a different root directory and executes a new system manager process below it. This is
+            intended for usage in initial RAM disks ("initrd"), and will transition from the initrd's system manager
+            process (a.k.a. "init" process) to the main system manager process which is loaded from the actual host
+            volume. This call takes two arguments: the directory that is to become the new root directory, and the path
+            to the new system manager binary below it to execute as PID 1. If the latter is omitted or the empty
+            string, a systemd binary will automatically be searched for and used as init. If the system manager path is
+            omitted, equal to the empty string or identical to the path to the systemd binary, the state of the
+            initrd's system manager process is passed to the main system manager, which allows later introspection of
+            the state of the services involved in the initrd boot phase.</para>
           </listitem>
         </varlistentry>
 
index 9d85908f97cdaf5ccc0f06f94f68b65eb6897e0a..ce169960d887a0c170f480299c7d35d00913dab7 100644 (file)
@@ -61,7 +61,7 @@
     <title>Description</title>
 
     <para><command>systemd-inhibit</command> may be used to execute a
-    program with a shutdown, sleep or idle inhibitor lock taken. The
+    program with a shutdown, sleep, or idle inhibitor lock taken. The
     lock will be acquired before the specified command line is
     executed and released afterwards.</para>
 
index 5733e42cd1afd5f2a15368f88703460cc8038f99..f0bdb1c756d28a927384be57205b843afc95445a 100644 (file)
@@ -84,7 +84,7 @@
       management</para></listitem>
     </itemizedlist>
 
-    <para>User sessions are registered in logind via the
+    <para>User sessions are registered with logind via the
     <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     PAM module.</para>
 
index bf823260961fec27e30a8a42852c040f767709f1..bcedebd5bb5e304bbb4e806939e6444b1e3f250b 100644 (file)
         use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running
         as part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by these
         users/groups around, as a different unit might get the same UID/GID assigned later on, and thus gain access to
-        these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>PrivateTmp=</varname> is
-        implied. This ensures that the lifetime of temporary files created by the executed processes is bound to the
-        runtime of the service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp</filename> and
-        <filename>/var/tmp</filename> are usually the only world-writable directories on a system this ensures that a
-        unit making use of dynamic user/group allocation cannot leave files around after unit termination. Use
-        <varname>RuntimeDirectory=</varname> (see below) in order to assign a writable runtime directory to a service,
-        owned by the dynamic user/group and removed automatically when the unit is terminated. Defaults to
-        off.</para></listitem>
+        these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>RemoveIPC=</varname> and
+        <varname>PrivateTmp=</varname> are implied. This ensures that the lifetime of IPC objects and temporary files
+        created by the executed processes is bound to the runtime of the service, and hence the lifetime of the dynamic
+        user/group. Since <filename>/tmp</filename> and <filename>/var/tmp</filename> are usually the only
+        world-writable directories on a system this ensures that a unit making use of dynamic user/group allocation
+        cannot leave files around after unit termination. Use <varname>RuntimeDirectory=</varname> (see below) in order
+        to assign a writable runtime directory to a service, owned by the dynamic user/group and removed automatically
+        when the unit is terminated. Defaults to off.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         user. This does not affect commands prefixed with <literal>+</literal>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RemoveIPC=</varname></term>
+
+        <listitem><para>Takes a boolean parameter. If set, all System V and POSIX IPC objects owned by the user and
+        group the processes of this unit are run as are removed when the unit is stopped. This setting only has an
+        effect if at least one of <varname>User=</varname>, <varname>Group=</varname> and
+        <varname>DynamicUser=</varname> are used. It has no effect on IPC objects owned by the root user. Specifically,
+        this removes System V semaphores, as well as System V and POSIX shared memory segments and message queues. If
+        multiple units use the same user or group the IPC objects are removed when the last of these units is
+        stopped. This setting is implied if <varname>DynamicUser=</varname> is set.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Nice=</varname></term>
 
       <varlistentry>
         <term><varname>PrivateTmp=</varname></term>
 
-        <listitem><para>Takes a boolean argument. If true, sets up a
-        new file system namespace for the executed processes and
-        mounts private <filename>/tmp</filename> and
-        <filename>/var/tmp</filename> directories inside it that is
-        not shared by processes outside of the namespace. This is
-        useful to secure access to temporary files of the process, but
-        makes sharing between processes via <filename>/tmp</filename>
-        or <filename>/var/tmp</filename> impossible. If this is
-        enabled, all temporary files created by a service in these
-        directories will be removed after the service is stopped.
-        Defaults to false. It is possible to run two or more units
-        within the same private <filename>/tmp</filename> and
-        <filename>/var/tmp</filename> namespace by using the
+        <listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the executed
+        processes and mounts private <filename>/tmp</filename> and <filename>/var/tmp</filename> directories inside it
+        that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of
+        the process, but makes sharing between processes via <filename>/tmp</filename> or <filename>/var/tmp</filename>
+        impossible. If this is enabled, all temporary files created by a service in these directories will be removed
+        after the service is stopped.  Defaults to false. It is possible to run two or more units within the same
+        private <filename>/tmp</filename> and <filename>/var/tmp</filename> namespace by using the
         <varname>JoinsNamespaceOf=</varname> directive, see
-        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for details. Note that using this setting will disconnect
-        propagation of mounts from the service to the host
-        (propagation in the opposite direction continues to work).
-        This means that this setting may not be used for services
-        which shall be able to install mount points in the main mount
-        namespace.</para></listitem>
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details. Note that using this setting will disconnect propagation of mounts from the service to the host
+        (propagation in the opposite direction continues to work).  This means that this setting may not be used for
+        services which shall be able to install mount points in the main mount namespace. This setting is implied if
+        <varname>DynamicUser=</varname> is set.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index b7164014f05264bd1507664eaa1a47dccd28d9e1..d09167baafacf0bb57810a483228352423015f0d 100644 (file)
     Empty lines and lines whose first non-whitespace character is # or
     ; are ignored.</para>
 
+    <para>Presets must refer to the "real" unit file, and not to any aliases. See
+    <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for a description of unit aliasing.</para>
+
     <para>Two different directives are understood:
     <literal>enable</literal> may be used to enable units by default,
     <literal>disable</literal> to disable units by default.</para>
index 85a7b12d7601bf3564a1c388818cd85c9e8d10f0..f818e772a98bd514bef6448c61d7d3529d53ee79 100644 (file)
     <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: "50" refers to 50 seconds; "2min
-    200ms" refers to 2 minutes plus 200 milliseconds, i.e. 120200ms.
-    The following time units are understood: s, min, h, d, w, ms, us.
-    For details see
+    <para>Time span values encoded in unit files can be written in various formats. A stand-alone number specifies a
+    time in seconds.  If suffixed with a time unit, the unit is honored. A concatenation of multiple values with units
+    is supported, in which case the values are added up. Example: <literal>50</literal> refers to 50 seconds;
+    <literal>2min 200ms</literal> refers to 2 minutes and 200 milliseconds, i.e. 120200 ms.  The following time units
+    are understood: <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>,
+    <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>.  For details see
     <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
 
-    <para>Empty lines and lines starting with # or ; are
-    ignored. This may be used for commenting. Lines ending
-    in a backslash are concatenated with the following
-    line while reading and the backslash is replaced by a
-    space character. This may be used to wrap long lines.</para>
+    <para>Empty lines and lines starting with <literal>#</literal> or <literal>;</literal> are ignored. This may be
+    used for commenting. Lines ending in a backslash are concatenated with the following line while reading and the
+    backslash is replaced by a space character. This may be used to wrap long lines.</para>
+
+    <para>Units can be aliased (have an alternative name), by creating a symlink from the new name to the existing name
+    in one of the unit search paths. For example, <filename>systemd-networkd.service</filename> has the alias
+    <filename>dbus-org.freedesktop.network1.service</filename>, created during installation as the symlink
+    <filename>/usr/lib/systemd/system/dbus-org.freedesktop.network1.service</filename>. In addition, unit files may
+    specify aliases through the <varname>Alias=</varname> directive in the [Install] section; those aliases are only
+    effective when the unit is enabled. When the unit is enabled, symlinks will be created for those names, and removed
+    when the unit is disabled. For example, <filename>reboot.target</filename> specifies
+    <varname>Alias=ctrl-alt-del.target</varname>, so when enabled it will be invoked whenever CTRL+ALT+DEL is
+    pressed. Alias names may be used in commands like <command>enable</command>, <command>disable</command>,
+    <command>start</command>, <command>stop</command>, <command>status</command>, â€¦, and in unit dependency directives
+    <varname>Wants=</varname>, <varname>Requires=</varname>, <varname>Before=</varname>, <varname>After=</varname>, â€¦,
+    with the limitation that aliases specified through <varname>Alias=</varname> are only effective when the unit is
+    enabled. Aliases cannot be used with the <command>preset</command> command.</para>
 
     <para>Along with a unit file <filename>foo.service</filename>, the
     directory <filename>foo.service.wants/</filename> may exist. All
index 65f55199e2ace3882fd3248ef69beff398532045..e30333e2096cef0c256dd6f5a947921d61236e42 100644 (file)
       <varlistentry>
         <term><varname>$SYSTEMD_COLORS</varname></term>
 
-        <listitem><para>Controls whether colorized output should be generated.
-        </para></listitem>
+        <listitem><para>The value must be a boolean. Controls whether colorized output should be
+        generated. This can be specified to override the decision that <command>systemd</command>
+        makes based on <varname>$TERM</varname> and what the console is connected to.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Set by systemd for supervised processes during
         socket-based activation. See
         <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-        for more information. </para></listitem>
+        for more information.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Set by systemd for supervised processes for
         status and start-up completion notification. See
         <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-        for more information. </para></listitem>
+        for more information.</para></listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
index a509a1775cd62ef9ff1625b59dcbd2a3edb8e24b..f1617a16be9534bb0fb93f051515a307484ca008 100644 (file)
@@ -112,6 +112,10 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
             (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX);
 }
 
+/* Default resource limits */
+#define DEFAULT_TASKS_MAX_PERCENTAGE            15U /* 15% of PIDs, 4915 on default settings */
+#define DEFAULT_USER_TASKS_MAX_PERCENTAGE       33U /* 33% of PIDs, 10813 on default settings */
+
 /*
  * General rules:
  *
index 13c3bb644621dc863ce3f04eddcd5c3565aee973..e44a357287c6e9dfb0ccc4f82e380fe4ae43ef2e 100644 (file)
@@ -163,7 +163,6 @@ char* hostname_cleanup(char *s) {
                         *(d++) = *p;
                         dot = false;
                 }
-
         }
 
         if (dot && d > s)
index f0a46c48cfc0ef77a81b955be5770ffd71ec9ae4..bfa936bd4e6e591a6c29211d221828b8244bd4bc 100644 (file)
@@ -39,6 +39,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -1191,12 +1192,9 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
         return receive_one_fd(pair[0], 0);
 }
 
-bool terminal_is_dumb(void) {
+static bool getenv_terminal_is_dumb(void) {
         const char *e;
 
-        if (!on_tty())
-                return true;
-
         e = getenv("TERM");
         if (!e)
                 return true;
@@ -1204,15 +1202,25 @@ bool terminal_is_dumb(void) {
         return streq(e, "dumb");
 }
 
+bool terminal_is_dumb(void) {
+        if (!on_tty())
+                return true;
+
+        return getenv_terminal_is_dumb();
+}
+
 bool colors_enabled(void) {
         static int enabled = -1;
 
         if (_unlikely_(enabled < 0)) {
-                const char *colors;
-
-                colors = getenv("SYSTEMD_COLORS");
-                if (colors)
-                        enabled = parse_boolean(colors) != 0;
+                int val;
+
+                val = getenv_bool("SYSTEMD_COLORS");
+                if (val >= 0)
+                        enabled = val;
+                else if (getpid() == 1)
+                        /* PID1 outputs to the console without holding it open all the time */
+                        enabled = !getenv_terminal_is_dumb();
                 else
                         enabled = !terminal_is_dumb();
         }
index 36f71fb004f32721750728dc538f8a80f7edcdc7..f569363811dc00a0466942984c95b5749e3a5ffc 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include <stdbool.h>
+#include <stdint.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -57,8 +58,19 @@ int take_etc_passwd_lock(const char *root);
 #define UID_INVALID ((uid_t) -1)
 #define GID_INVALID ((gid_t) -1)
 
-/* The following macros add 1 when converting things, since UID 0 is a
- * valid UID, while the pointer NULL is special */
+/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit
+ * user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not
+ * allocate from below this. Also stay away from the upper end of the range as that is often used
+ * for overflow/nobody users. */
+#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00))
+#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF))
+
+static inline bool uid_is_dynamic(uid_t uid) {
+        return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
+}
+
+/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
+ * NULL is special */
 #define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
 #define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
 
index 910a64b4daa72f3e6302460317a1338070b247ce..ca3c3366f31e0f3a74e816c31176d67d08d66bc0 100644 (file)
@@ -952,7 +952,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
 
         if ((mask & CGROUP_MASK_PIDS) && !is_root) {
 
-                if (c->tasks_max != (uint64_t) -1) {
+                if (c->tasks_max != CGROUP_LIMIT_MAX) {
                         char buf[DECIMAL_STR_MAX(uint64_t) + 2];
 
                         sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
index e35d3ccd2ec1acda726a61f774572da750557d74..7e33a2d20189a1d7b725d6231bfb61f9a2d851bd 100644 (file)
@@ -695,6 +695,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
@@ -1071,7 +1072,7 @@ int bus_exec_context_set_transient_property(
                               "IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
                               "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
                               "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
-                              "RestrictRealtime", "DynamicUser")) {
+                              "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
                 int b;
 
                 r = sd_bus_message_read(message, "b", &b);
@@ -1103,6 +1104,8 @@ int bus_exec_context_set_transient_property(
                                 c->restrict_realtime = b;
                         else if (streq(name, "DynamicUser"))
                                 c->dynamic_user = b;
+                        else if (streq(name, "RemoveIPC"))
+                                c->remove_ipc = b;
 
                         unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
                 }
index b4bbee06482039198def7962379c039119c118e0..3c6bda40739a500757c01e1f2f00e87eaf85081f 100644 (file)
@@ -117,6 +117,8 @@ const sd_bus_vtable bus_mount_vtable[] = {
         SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
index fab3677a0129ff6e35f4d5f2d128d1fc3742bfef..3c55e0f7fe3e11f7d05937a1505978a8b02705e3 100644 (file)
@@ -50,11 +50,6 @@ const sd_bus_vtable bus_service_vtable[] = {
         SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
-        /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
-        SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
         SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -70,6 +65,9 @@ const sd_bus_vtable bus_service_vtable[] = {
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
         BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -77,6 +75,12 @@ const sd_bus_vtable bus_service_vtable[] = {
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+
+        /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
+        SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
         SD_BUS_VTABLE_END
 };
 
index 9a071a1355304783177ee646d51d9502dfdb3836..21adb64e1500b11a93c0107ebf6c448702851805 100644 (file)
@@ -152,6 +152,8 @@ const sd_bus_vtable bus_socket_vtable[] = {
         SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
index 292f8738c684a828c48d5556dc8ec60b7f2a8f15..85a2c26b98d640d3e7f2f4b07b8c909e55c3159e 100644 (file)
@@ -84,6 +84,8 @@ const sd_bus_vtable bus_swap_vtable[] = {
         SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         SD_BUS_VTABLE_END
index b55d2cf73515d0ab9437e0fe99653c88c3a9cc33..89e56a2e51f6949b6061d633da00d6251ba083b3 100644 (file)
@@ -660,10 +660,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
         SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -705,7 +701,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
         SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -721,6 +716,12 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
 
+        /* Obsolete properties or obsolete alias names */
+        SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
         SD_BUS_VTABLE_END
 };
 
index 8035bee231d36aef3b9658dbdc727282d8129cf8..310aaa94e1dd3c75677229c24297c5f397074023 100644 (file)
 #include "user-util.h"
 #include "fileio.h"
 
-/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit user namespacing. At
- * least on Fedora normal users are allocated until UID 60000, hence do not allocate from below this. Also stay away
- * from the upper end of the range as that is often used for overflow/nobody users. */
-#define UID_PICK_MIN ((uid_t) UINT32_C(0x0000EF00))
-#define UID_PICK_MAX ((uid_t) UINT32_C(0x0000FFEF))
-
 /* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
-#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (UID_PICK_MAX - UID_PICK_MIN + 1)) + UID_PICK_MIN)
+#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
 
 static DynamicUser* dynamic_user_free(DynamicUser *d) {
         if (!d)
@@ -150,6 +144,45 @@ int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
         return 1;
 }
 
+static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
+
+        char path1[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
+        const char *path2;
+        int r = 0, k;
+
+        /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
+         * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
+         * would be its own client then). We hence keep these world-readable symlinks in place, so that the
+         * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
+         * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
+         * on them and as those may be taken by any user with read access we can't make them world-readable. */
+
+        xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
+        if (unlink(path1) < 0 && errno != ENOENT)
+                r = -errno;
+
+        if (b && symlink(name, path1) < 0) {
+                k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1);
+                if (r == 0)
+                        r = k;
+        }
+
+        path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
+        if (unlink(path2) < 0 && errno != ENOENT) {
+                k = -errno;
+                if (r == 0)
+                        r = k;
+        }
+
+        if (b && symlink(path1 + strlen("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
+                k = log_warning_errno(errno,  "Failed to symlink \"%s\": %m", path2);
+                if (r == 0)
+                        r = k;
+        }
+
+        return r;
+}
+
 static int pick_uid(const char *name, uid_t *ret_uid) {
 
         static const uint8_t hash_key[] = {
@@ -175,7 +208,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) {
                 if (--n_tries <= 0) /* Give up retrying eventually */
                         return -EBUSY;
 
-                if (candidate < UID_PICK_MIN || candidate > UID_PICK_MAX)
+                if (!uid_is_dynamic(candidate))
                         goto next;
 
                 xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate);
@@ -223,6 +256,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) {
                 }
 
                 (void) ftruncate(lock_fd, l);
+                (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
 
                 *ret_uid = candidate;
                 r = lock_fd;
@@ -324,14 +358,16 @@ static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
         return 0;
 }
 
-static void unlink_uid_lock(int lock_fd, uid_t uid) {
+static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
         char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
 
         if (lock_fd < 0)
                 return;
 
         xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
-        (void) unlink_noerrno(lock_path);
+        (void) unlink(lock_path);
+
+        (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
 }
 
 int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
@@ -399,7 +435,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
 
                 /* So, we found a working UID/lock combination. Let's see if we actually still need it. */
                 if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) {
-                        unlink_uid_lock(uid_lock_fd, uid);
+                        unlink_uid_lock(uid_lock_fd, uid, d->name);
                         return -errno;
                 }
 
@@ -407,7 +443,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
                 if (r < 0) {
                         if (r != -EAGAIN) {
                                 /* OK, something bad happened, let's get rid of the bits we acquired. */
-                                unlink_uid_lock(uid_lock_fd, uid);
+                                unlink_uid_lock(uid_lock_fd, uid, d->name);
                                 goto finish;
                         }
 
@@ -416,7 +452,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
                         /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
                          * acquired, and use what's stored now. */
 
-                        unlink_uid_lock(uid_lock_fd, uid);
+                        unlink_uid_lock(uid_lock_fd, uid, d->name);
                         safe_close(uid_lock_fd);
 
                         uid = new_uid;
@@ -513,7 +549,7 @@ static int dynamic_user_close(DynamicUser *d) {
                 goto finish;
 
         /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
-        unlink_uid_lock(lock_fd, uid);
+        unlink_uid_lock(lock_fd, uid, d->name);
         r = 1;
 
 finish:
@@ -634,11 +670,8 @@ int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
         assert(m);
         assert(ret);
 
-        /* A friendly way to translate a dynamic user's UID into a his name. */
-
-        if (uid < UID_PICK_MIN)
-                return -ESRCH;
-        if (uid > UID_PICK_MAX)
+        /* A friendly way to translate a dynamic user's UID into a name. */
+        if (!uid_is_dynamic(uid))
                 return -ESRCH;
 
         xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
index 6019df7ea695a0307c003e62a28fef6baafe7c5d..0af8eb5a02fa42fe0b8a34d35eb84dc73e959822 100644 (file)
@@ -91,6 +91,7 @@
 #include "selinux-util.h"
 #include "signal-util.h"
 #include "smack-util.h"
+#include "special.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
@@ -1384,6 +1385,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
 }
 
 static int build_environment(
+                Unit *u,
                 const ExecContext *c,
                 const ExecParameters *p,
                 unsigned n_fds,
@@ -1401,7 +1403,7 @@ static int build_environment(
         assert(c);
         assert(ret);
 
-        our_env = new0(char*, 12);
+        our_env = new0(char*, 13);
         if (!our_env)
                 return -ENOMEM;
 
@@ -1436,6 +1438,16 @@ static int build_environment(
                 our_env[n_env++] = x;
         }
 
+        /* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use D-Bus look up dynamic
+         * users via PID 1, possibly dead-locking the dbus daemon. This way it will not use D-Bus to resolve names, but
+         * check the database directly. */
+        if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
+                x = strdup("SYSTEMD_NSS_BYPASS_BUS=1");
+                if (!x)
+                        return -ENOMEM;
+                our_env[n_env++] = x;
+        }
+
         if (home) {
                 x = strappend("HOME=", home);
                 if (!x)
@@ -1723,11 +1735,12 @@ static int close_remaining_fds(
                 const ExecParameters *params,
                 ExecRuntime *runtime,
                 DynamicCreds *dcreds,
+                int user_lookup_fd,
                 int socket_fd,
                 int *fds, unsigned n_fds) {
 
         unsigned n_dont_close = 0;
-        int dont_close[n_fds + 11];
+        int dont_close[n_fds + 12];
 
         assert(params);
 
@@ -1755,9 +1768,40 @@ static int close_remaining_fds(
                         append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
         }
 
+        if (user_lookup_fd >= 0)
+                dont_close[n_dont_close++] = user_lookup_fd;
+
         return close_all_fds(dont_close, n_dont_close);
 }
 
+static int send_user_lookup(
+                Unit *unit,
+                int user_lookup_fd,
+                uid_t uid,
+                gid_t gid) {
+
+        assert(unit);
+
+        /* Send the resolved UID/GID to PID 1 after we learnt it. We send a single datagram, containing the UID/GID
+         * data as well as the unit name. Note that we suppress sending this if no user/group to resolve was
+         * specified. */
+
+        if (user_lookup_fd < 0)
+                return 0;
+
+        if (!uid_is_valid(uid) && !gid_is_valid(gid))
+                return 0;
+
+        if (writev(user_lookup_fd,
+               (struct iovec[]) {
+                           { .iov_base = &uid, .iov_len = sizeof(uid) },
+                           { .iov_base = &gid, .iov_len = sizeof(gid) },
+                           { .iov_base = unit->id, .iov_len = strlen(unit->id) }}, 3) < 0)
+                return -errno;
+
+        return 0;
+}
+
 static int exec_child(
                 Unit *unit,
                 ExecCommand *command,
@@ -1769,6 +1813,7 @@ static int exec_child(
                 int socket_fd,
                 int *fds, unsigned n_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;
@@ -1815,7 +1860,7 @@ static int exec_child(
 
         log_forget_fds();
 
-        r = close_remaining_fds(params, runtime, dcreds, socket_fd, fds, n_fds);
+        r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
         if (r < 0) {
                 *exit_status = EXIT_FDS;
                 return r;
@@ -1862,7 +1907,7 @@ static int exec_child(
                         return r;
                 }
 
-                if (uid == UID_INVALID || gid == GID_INVALID) {
+                if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
                         *exit_status = EXIT_USER;
                         return -ESRCH;
                 }
@@ -1902,6 +1947,14 @@ static int exec_child(
                 }
         }
 
+        r = send_user_lookup(unit, user_lookup_fd, uid, gid);
+        if (r < 0) {
+                *exit_status = EXIT_USER;
+                return r;
+        }
+
+        user_lookup_fd = safe_close(user_lookup_fd);
+
         /* If a socket is connected to STDIN/STDOUT/STDERR, we
          * must sure to drop O_NONBLOCK */
         if (socket_fd >= 0)
@@ -2059,6 +2112,7 @@ static int exec_child(
         }
 
         r = build_environment(
+                        unit,
                         context,
                         params,
                         n_fds,
@@ -2501,6 +2555,7 @@ int exec_spawn(Unit *unit,
                                socket_fd,
                                fds, n_fds,
                                files_env,
+                               unit->manager->user_lookup_fds[1],
                                &exit_status);
                 if (r < 0) {
                         log_open();
index 106154f81a4b15973d15882a2ebfd66d8b5d8208..6082c42aba2c6af3df55ad418d9a5bb5b5b2c638 100644 (file)
@@ -178,6 +178,7 @@ struct ExecContext {
         bool no_new_privileges;
 
         bool dynamic_user;
+        bool remove_ipc;
 
         /* This is not exposed to the user but available
          * internally. We need it to make sure that whenever we spawn
index 4ad6c4d79be988de2ac08bdf6aaf7f185c72560f..d5185cf6a0ab105e1d4092cecfc8d9ff0361b228 100644 (file)
@@ -2999,30 +2999,36 @@ int config_parse_tasks_max(
                 void *data,
                 void *userdata) {
 
-        uint64_t *tasks_max = data, u;
+        uint64_t *tasks_max = data, v;
+        Unit *u = userdata;
         int r;
 
-        if (isempty(rvalue) || streq(rvalue, "infinity")) {
-                *tasks_max = (uint64_t) -1;
+        if (isempty(rvalue)) {
+                *tasks_max = u->manager->default_tasks_max;
+                return 0;
+        }
+
+        if (streq(rvalue, "infinity")) {
+                *tasks_max = CGROUP_LIMIT_MAX;
                 return 0;
         }
 
         r = parse_percent(rvalue);
         if (r < 0) {
-                r = safe_atou64(rvalue, &u);
+                r = safe_atou64(rvalue, &v);
                 if (r < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
                         return 0;
                 }
         } else
-                u = system_tasks_max_scale(r, 100U);
+                v = system_tasks_max_scale(r, 100U);
 
-        if (u <= 0 || u >= UINT64_MAX) {
+        if (v <= 0 || v >= UINT64_MAX) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
                 return 0;
         }
 
-        *tasks_max = u;
+        *tasks_max = v;
         return 0;
 }
 
index 02324d325ee44bf8ab52d1c061cdbab55f67f1ee..125cfb28f0f7b8cf54a4bd0bae9fd945412a37bb 100644 (file)
@@ -1505,7 +1505,8 @@ int main(int argc, char *argv[]) {
         if (getpid() == 1) {
                 /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit)
                  * will process core dumps for system services by default. */
-                (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY));
+                if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
+                        log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m");
 
                 /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
                  * until the systemd-coredump tool is enabled via sysctl. */
@@ -1559,7 +1560,7 @@ int main(int argc, char *argv[]) {
         (void) reset_all_signal_handlers();
         (void) ignore_signals(SIGNALS_IGNORE, -1);
 
-        arg_default_tasks_max = system_tasks_max_scale(15U, 100U); /* 15% the system PIDs equals 4915 by default. */
+        arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
 
         if (parse_config_file() < 0) {
                 error_message = "Failed to parse config file";
@@ -2013,9 +2014,6 @@ finish:
                                 log_error_errno(r, "Failed to switch root, trying to continue: %m");
                 }
 
-                /* Reopen the console */
-                (void) make_console_stdio();
-
                 args_size = MAX(6, argc+1);
                 args = newa(const char*, args_size);
 
@@ -2063,6 +2061,9 @@ finish:
                 arg_serialization = safe_fclose(arg_serialization);
                 fds = fdset_free(fds);
 
+                /* Reopen the console */
+                (void) make_console_stdio();
+
                 for (j = 1, i = 1; j < (unsigned) argc; j++)
                         args[i++] = argv[j];
                 args[i++] = NULL;
index c20e185d78769ff6f55c0698cd7b3a8ebbd30515..6f2477eef49f8c184b3eace4babda3f98c349af5 100644 (file)
@@ -45,6 +45,7 @@
 #include "bus-error.h"
 #include "bus-kernel.h"
 #include "bus-util.h"
+#include "clean-ipc.h"
 #include "dbus-job.h"
 #include "dbus-manager.h"
 #include "dbus-unit.h"
@@ -81,6 +82,7 @@
 #include "transaction.h"
 #include "umask-util.h"
 #include "unit-name.h"
+#include "user-util.h"
 #include "util.h"
 #include "virt.h"
 #include "watchdog.h"
@@ -98,6 +100,7 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
 static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
 static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
 static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
 static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
 static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
 static int manager_run_generators(Manager *m);
@@ -590,6 +593,8 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
                 m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd =
                 m->ask_password_inotify_fd = -1;
 
+        m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1;
+
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
         m->have_ask_password = -EINVAL; /* we don't know */
@@ -812,6 +817,59 @@ static int manager_setup_cgroups_agent(Manager *m) {
         return 0;
 }
 
+static int manager_setup_user_lookup_fd(Manager *m) {
+        int r;
+
+        assert(m);
+
+        /* Set up the socket pair used for passing UID/GID resolution results from forked off processes to PID
+         * 1. Background: we can't do name lookups (NSS) from PID 1, since it might involve IPC and thus activation,
+         * and we might hence deadlock on ourselves. Hence we do all user/group lookups asynchronously from the forked
+         * off processes right before executing the binaries to start. In order to be able to clean up any IPC objects
+         * created by a unit (see RemoveIPC=) we need to know in PID 1 the used UID/GID of the executed processes,
+         * hence we establish this communication channel so that forked off processes can pass their UID/GID
+         * information back to PID 1. The forked off processes send their resolved UID/GID to PID 1 in a simple
+         * datagram, along with their unit name, so that we can share one communication socket pair among all units for
+         * this purpose.
+         *
+         * You might wonder why we need a communication channel for this that is independent of the usual notification
+         * socket scheme (i.e. $NOTIFY_SOCKET). The primary difference is about trust: data sent via the $NOTIFY_SOCKET
+         * channel is only accepted if it originates from the right unit and if reception was enabled for it. The user
+         * lookup socket OTOH is only accessible by PID 1 and its children until they exec(), and always available.
+         *
+         * Note that this function is called under two circumstances: when we first initialize (in which case we
+         * allocate both the socket pair and the event source to listen on it), and when we deserialize after a reload
+         * (in which case the socket pair already exists but we still need to allocate the event source for it). */
+
+        if (m->user_lookup_fds[0] < 0) {
+
+                /* Free all secondary fields */
+                safe_close_pair(m->user_lookup_fds);
+                m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
+
+                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, m->user_lookup_fds) < 0)
+                        return log_error_errno(errno, "Failed to allocate user lookup socket: %m");
+
+                (void) fd_inc_rcvbuf(m->user_lookup_fds[0], NOTIFY_RCVBUF_SIZE);
+        }
+
+        if (!m->user_lookup_event_source) {
+                r = sd_event_add_io(m->event, &m->user_lookup_event_source, m->user_lookup_fds[0], EPOLLIN, manager_dispatch_user_lookup_fd, m);
+                if (r < 0)
+                        return log_error_errno(errno, "Failed to allocate user lookup event source: %m");
+
+                /* Process even earlier than the notify event source, so that we always know first about valid UID/GID
+                 * resolutions */
+                r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8);
+                if (r < 0)
+                        return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m");
+
+                (void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup");
+        }
+
+        return 0;
+}
+
 static int manager_connect_bus(Manager *m, bool reexecuting) {
         bool try_bus_connect;
 
@@ -853,8 +911,7 @@ enum {
         _GC_OFFSET_MAX
 };
 
-static void unit_gc_mark_good(Unit *u, unsigned gc_marker)
-{
+static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
         Iterator i;
         Unit *other;
 
@@ -1021,12 +1078,14 @@ Manager* manager_free(Manager *m) {
         sd_event_source_unref(m->time_change_event_source);
         sd_event_source_unref(m->jobs_in_progress_event_source);
         sd_event_source_unref(m->run_queue_event_source);
+        sd_event_source_unref(m->user_lookup_event_source);
 
         safe_close(m->signal_fd);
         safe_close(m->notify_fd);
         safe_close(m->cgroups_agent_fd);
         safe_close(m->time_change_fd);
         safe_close(m->kdbus_fd);
+        safe_close_pair(m->user_lookup_fds);
 
         manager_close_ask_password(m);
 
@@ -1052,6 +1111,9 @@ Manager* manager_free(Manager *m) {
         assert(hashmap_isempty(m->units_requiring_mounts_for));
         hashmap_free(m->units_requiring_mounts_for);
 
+        hashmap_free(m->uid_refs);
+        hashmap_free(m->gid_refs);
+
         free(m);
         return NULL;
 }
@@ -1221,6 +1283,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         if (q < 0 && r == 0)
                 r = q;
 
+        q = manager_setup_user_lookup_fd(m);
+        if (q < 0 && r == 0)
+                r = q;
+
         /* We might have deserialized the kdbus control fd, but if we
          * didn't, then let's create the bus now. */
         manager_connect_bus(m, !!serialization);
@@ -1232,6 +1298,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         /* Release any dynamic users no longer referenced */
         dynamic_user_vacuum(m, true);
 
+        /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+        manager_vacuum_uid_refs(m);
+        manager_vacuum_gid_refs(m);
+
         if (serialization) {
                 assert(m->n_reloading > 0);
                 m->n_reloading--;
@@ -2396,6 +2466,20 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
                 fprintf(f, "cgroups-agent-fd=%i\n", copy);
         }
 
+        if (m->user_lookup_fds[0] >= 0) {
+                int copy0, copy1;
+
+                copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
+                if (copy0 < 0)
+                        return copy0;
+
+                copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
+                if (copy1 < 0)
+                        return copy1;
+
+                fprintf(f, "user-lookup=%i %i\n", copy0, copy1);
+        }
+
         if (m->kdbus_fd >= 0) {
                 int copy;
 
@@ -2412,6 +2496,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
         if (r < 0)
                 return r;
 
+        manager_serialize_uid_refs(m, f);
+        manager_serialize_gid_refs(m, f);
+
         fputc('\n', f);
 
         HASHMAP_FOREACH_KEY(u, t, m->units, i) {
@@ -2578,6 +2665,18 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                                 m->cgroups_agent_fd = fdset_remove(fds, fd);
                         }
 
+                } else if (startswith(l, "user-lookup=")) {
+                        int fd0, fd1;
+
+                        if (sscanf(l + 12, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
+                                log_debug("Failed to parse user lookup fd: %s", l + 12);
+                        else {
+                                m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
+                                safe_close_pair(m->user_lookup_fds);
+                                m->user_lookup_fds[0] = fdset_remove(fds, fd0);
+                                m->user_lookup_fds[1] = fdset_remove(fds, fd1);
+                        }
+
                 } else if (startswith(l, "kdbus-fd=")) {
                         int fd;
 
@@ -2590,6 +2689,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
 
                 } else if (startswith(l, "dynamic-user="))
                         dynamic_user_deserialize_one(m, l + 13, fds);
+                else if (startswith(l, "destroy-ipc-uid="))
+                        manager_deserialize_uid_refs_one(m, l + 16);
+                else if (startswith(l, "destroy-ipc-gid="))
+                        manager_deserialize_gid_refs_one(m, l + 16);
                 else {
                         int k;
 
@@ -2672,6 +2775,8 @@ int manager_reload(Manager *m) {
         lookup_paths_flush_generator(&m->lookup_paths);
         lookup_paths_free(&m->lookup_paths);
         dynamic_user_vacuum(m, false);
+        m->uid_refs = hashmap_free(m->uid_refs);
+        m->gid_refs = hashmap_free(m->gid_refs);
 
         q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
         if (q < 0 && r >= 0)
@@ -2705,12 +2810,20 @@ int manager_reload(Manager *m) {
         if (q < 0 && r >= 0)
                 r = q;
 
+        q = manager_setup_user_lookup_fd(m);
+        if (q < 0 && r >= 0)
+                r = q;
+
         /* Third, fire things up! */
         manager_coldplug(m);
 
         /* Release any dynamic users no longer referenced */
         dynamic_user_vacuum(m, true);
 
+        /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+        manager_vacuum_uid_refs(m);
+        manager_vacuum_gid_refs(m);
+
         /* Sync current state of bus names with our set of listening units */
         if (m->api_bus)
                 manager_sync_bus_names(m, m->api_bus);
@@ -3144,6 +3257,300 @@ ManagerState manager_state(Manager *m) {
         return MANAGER_RUNNING;
 }
 
+#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
+
+static void manager_unref_uid_internal(
+                Manager *m,
+                Hashmap **uid_refs,
+                uid_t uid,
+                bool destroy_now,
+                int (*_clean_ipc)(uid_t uid)) {
+
+        uint32_t c, n;
+
+        assert(m);
+        assert(uid_refs);
+        assert(uid_is_valid(uid));
+        assert(_clean_ipc);
+
+        /* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
+         * that uid_t and gid_t are actually defined the same way, with the same validity rules.
+         *
+         * We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest
+         * bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
+         * is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
+         * on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
+
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+        assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+        if (uid == 0) /* We don't keep track of root, and will never destroy it */
+                return;
+
+        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+
+        n = c & ~DESTROY_IPC_FLAG;
+        assert(n > 0);
+        n--;
+
+        if (destroy_now && n == 0) {
+                hashmap_remove(*uid_refs, UID_TO_PTR(uid));
+
+                if (c & DESTROY_IPC_FLAG) {
+                        log_debug("%s " UID_FMT " is no longer referenced, cleaning up its IPC.",
+                                  _clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
+                                  uid);
+                        (void) _clean_ipc(uid);
+                }
+        } else {
+                c = n | (c & DESTROY_IPC_FLAG);
+                assert_se(hashmap_update(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)) >= 0);
+        }
+}
+
+void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now) {
+        manager_unref_uid_internal(m, &m->uid_refs, uid, destroy_now, clean_ipc_by_uid);
+}
+
+void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now) {
+        manager_unref_uid_internal(m, &m->gid_refs, (uid_t) gid, destroy_now, clean_ipc_by_gid);
+}
+
+static int manager_ref_uid_internal(
+                Manager *m,
+                Hashmap **uid_refs,
+                uid_t uid,
+                bool clean_ipc) {
+
+        uint32_t c, n;
+        int r;
+
+        assert(m);
+        assert(uid_refs);
+        assert(uid_is_valid(uid));
+
+        /* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the assumption
+         * that uid_t and gid_t are actually defined the same way, with the same validity rules. */
+
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+        assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+        if (uid == 0) /* We don't keep track of root, and will never destroy it */
+                return 0;
+
+        r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+        if (r < 0)
+                return r;
+
+        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+
+        n = c & ~DESTROY_IPC_FLAG;
+        n++;
+
+        if (n & DESTROY_IPC_FLAG) /* check for overflow */
+                return -EOVERFLOW;
+
+        c = n | (c & DESTROY_IPC_FLAG) | (clean_ipc ? DESTROY_IPC_FLAG : 0);
+
+        return hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+}
+
+int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc) {
+        return manager_ref_uid_internal(m, &m->uid_refs, uid, clean_ipc);
+}
+
+int manager_ref_gid(Manager *m, gid_t gid, bool clean_ipc) {
+        return manager_ref_uid_internal(m, &m->gid_refs, (uid_t) gid, clean_ipc);
+}
+
+static void manager_vacuum_uid_refs_internal(
+                Manager *m,
+                Hashmap **uid_refs,
+                int (*_clean_ipc)(uid_t uid)) {
+
+        Iterator i;
+        void *p, *k;
+
+        assert(m);
+        assert(uid_refs);
+        assert(_clean_ipc);
+
+        HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+                uint32_t c, n;
+                uid_t uid;
+
+                uid = PTR_TO_UID(k);
+                c = PTR_TO_UINT32(p);
+
+                n = c & ~DESTROY_IPC_FLAG;
+                if (n > 0)
+                        continue;
+
+                if (c & DESTROY_IPC_FLAG) {
+                        log_debug("Found unreferenced %s " UID_FMT " after reload/reexec. Cleaning up.",
+                                  _clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
+                                  uid);
+                        (void) _clean_ipc(uid);
+                }
+
+                assert_se(hashmap_remove(*uid_refs, k) == p);
+        }
+}
+
+void manager_vacuum_uid_refs(Manager *m) {
+        manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid);
+}
+
+void manager_vacuum_gid_refs(Manager *m) {
+        manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid);
+}
+
+static void manager_serialize_uid_refs_internal(
+                Manager *m,
+                FILE *f,
+                Hashmap **uid_refs,
+                const char *field_name) {
+
+        Iterator i;
+        void *p, *k;
+
+        assert(m);
+        assert(f);
+        assert(uid_refs);
+        assert(field_name);
+
+        /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter
+         * of it is better rebuild after a reload/reexec. */
+
+        HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+                uint32_t c;
+                uid_t uid;
+
+                uid = PTR_TO_UID(k);
+                c = PTR_TO_UINT32(p);
+
+                if (!(c & DESTROY_IPC_FLAG))
+                        continue;
+
+                fprintf(f, "%s=" UID_FMT "\n", field_name, uid);
+        }
+}
+
+void manager_serialize_uid_refs(Manager *m, FILE *f) {
+        manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
+}
+
+void manager_serialize_gid_refs(Manager *m, FILE *f) {
+        manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
+}
+
+static void manager_deserialize_uid_refs_one_internal(
+                Manager *m,
+                Hashmap** uid_refs,
+                const char *value) {
+
+        uid_t uid;
+        uint32_t c;
+        int r;
+
+        assert(m);
+        assert(uid_refs);
+        assert(value);
+
+        r = parse_uid(value, &uid);
+        if (r < 0 || uid == 0) {
+                log_debug("Unable to parse UID reference serialization");
+                return;
+        }
+
+        r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+        if (r < 0) {
+                log_oom();
+                return;
+        }
+
+        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+        if (c & DESTROY_IPC_FLAG)
+                return;
+
+        c |= DESTROY_IPC_FLAG;
+
+        r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+        if (r < 0) {
+                log_debug("Failed to add UID reference entry");
+                return;
+        }
+}
+
+void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
+        manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
+}
+
+void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
+        manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+}
+
+int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+        struct buffer {
+                uid_t uid;
+                gid_t gid;
+                char unit_name[UNIT_NAME_MAX+1];
+        } _packed_ buffer;
+
+        Manager *m = userdata;
+        ssize_t l;
+        size_t n;
+        Unit *u;
+
+        assert_se(source);
+        assert_se(m);
+
+        /* Invoked whenever a child process succeeded resolving its user/group to use and sent us the resulting UID/GID
+         * in a datagram. We parse the datagram here and pass it off to the unit, so that it can add a reference to the
+         * UID/GID so that it can destroy the UID/GID's IPC objects when the reference counter drops to 0. */
+
+        l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
+        if (l < 0) {
+                if (errno == EINTR || errno == EAGAIN)
+                        return 0;
+
+                return log_error_errno(errno, "Failed to read from user lookup fd: %m");
+        }
+
+        if ((size_t) l <= offsetof(struct buffer, unit_name)) {
+                log_warning("Received too short user lookup message, ignoring.");
+                return 0;
+        }
+
+        if ((size_t) l > offsetof(struct buffer, unit_name) + UNIT_NAME_MAX) {
+                log_warning("Received too long user lookup message, ignoring.");
+                return 0;
+        }
+
+        if (!uid_is_valid(buffer.uid) && !gid_is_valid(buffer.gid)) {
+                log_warning("Got user lookup message with invalid UID/GID pair, ignoring.");
+                return 0;
+        }
+
+        n = (size_t) l - offsetof(struct buffer, unit_name);
+        if (memchr(buffer.unit_name, 0, n)) {
+                log_warning("Received lookup message with embedded NUL character, ignoring.");
+                return 0;
+        }
+
+        buffer.unit_name[n] = 0;
+        u = manager_get_unit(m, buffer.unit_name);
+        if (!u) {
+                log_debug("Got user lookup message but unit doesn't exist, ignoring.");
+                return 0;
+        }
+
+        log_unit_debug(u, "User lookup succeeded: uid=" UID_FMT " gid=" GID_FMT, buffer.uid, buffer.gid);
+
+        unit_notify_user_lookup(u, buffer.uid, buffer.gid);
+        return 0;
+}
+
 static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
         [MANAGER_INITIALIZING] = "initializing",
         [MANAGER_STARTING] = "starting",
index c681d5dc46297d78f9e98ac8a3c8a1704514e9e6..b9f2e4b5a1b2c21ee3ca1f3d6986af6ba8f4580b 100644 (file)
@@ -143,6 +143,9 @@ struct Manager {
 
         sd_event_source *jobs_in_progress_event_source;
 
+        int user_lookup_fds[2];
+        sd_event_source *user_lookup_event_source;
+
         UnitFileScope unit_file_scope;
         LookupPaths lookup_paths;
         Set *unit_path_cache;
@@ -234,7 +237,6 @@ struct Manager {
         bool dispatching_dbus_queue:1;
 
         bool taint_usr:1;
-
         bool test_run:1;
 
         /* If non-zero, exit with the following value when the systemd
@@ -301,6 +303,10 @@ struct Manager {
         /* Dynamic users/groups, indexed by their name */
         Hashmap *dynamic_users;
 
+        /* Keep track of all UIDs and GIDs any of our services currently use. This is useful for the RemoveIPC= logic. */
+        Hashmap *uid_refs;
+        Hashmap *gid_refs;
+
         /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
         RateLimit ctrl_alt_del_ratelimit;
 
@@ -378,5 +384,20 @@ ManagerState manager_state(Manager *m);
 
 int manager_update_failed_units(Manager *m, Unit *u, bool failed);
 
+void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now);
+int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc);
+
+void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now);
+int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now);
+
+void manager_vacuum_uid_refs(Manager *m);
+void manager_vacuum_gid_refs(Manager *m);
+
+void manager_serialize_uid_refs(Manager *m, FILE *f);
+void manager_deserialize_uid_refs_one(Manager *m, const char *value);
+
+void manager_serialize_gid_refs(Manager *m, FILE *f);
+void manager_deserialize_gid_refs_one(Manager *m, const char *value);
+
 const char *manager_state_to_string(ManagerState m) _const_;
 ManagerState manager_state_from_string(const char *s) _pure_;
index f3ccf6d48a91171428e7d976796beba14cd97e3c..f2ac8d171f56d04fb1ec594948411c8d23116047 100644 (file)
@@ -769,6 +769,8 @@ static void mount_enter_dead(Mount *m, MountResult f) {
 
         exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager));
 
+        unit_unref_uid_gid(UNIT(m), true);
+
         dynamic_creds_destroy(&m->dynamic_creds);
 }
 
index 4a37702f52feb2326042a1054cac8bca3cceabdc..1951ba92228b04d6ddbfbe6c540fc6495ca6ba36 100644 (file)
@@ -1471,6 +1471,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
         /* Also, remove the runtime directory */
         exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
 
+        /* Get rid of the IPC bits of the user */
+        unit_unref_uid_gid(UNIT(s), true);
+
         /* Release the user, and destroy it if we are the only remaining owner */
         dynamic_creds_destroy(&s->dynamic_creds);
 
index 50872e83662d4925523b9bee6546b00eb89c7260..70d55dd9ed9c17a86629e16ce16a6fd0274004b2 100644 (file)
@@ -1905,6 +1905,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
 
         exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
 
+        unit_unref_uid_gid(UNIT(s), true);
+
         dynamic_creds_destroy(&s->dynamic_creds);
 }
 
index 2c802da3b5357af0da55165ab7e99fff54744018..fb222b6858ecc5b7ae8a5296b6bba4535f09af9b 100644 (file)
@@ -683,6 +683,8 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
 
         exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
 
+        unit_unref_uid_gid(UNIT(s), true);
+
         dynamic_creds_destroy(&s->dynamic_creds);
 }
 
index 952604e0db6747d0aedea067e155ec5d17d0a33e..4b8d81c3f1850c5c0c8675672bd889bba3c32f52 100644 (file)
@@ -100,7 +100,8 @@ Unit *unit_new(Manager *m, size_t size) {
         u->on_failure_job_mode = JOB_REPLACE;
         u->cgroup_inotify_wd = -1;
         u->job_timeout = USEC_INFINITY;
-        u->sigchldgen = 0;
+        u->ref_uid = UID_INVALID;
+        u->ref_gid = GID_INVALID;
 
         RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
         RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@@ -550,6 +551,8 @@ void unit_free(Unit *u) {
 
         unit_release_cgroup(u);
 
+        unit_unref_uid_gid(u, false);
+
         (void) manager_update_failed_units(u->manager, u, false);
         set_remove(u->manager->startup_units, u);
 
@@ -2614,6 +2617,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
                 unit_serialize_item(u, f, "cgroup", u->cgroup_path);
         unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
 
+        if (uid_is_valid(u->ref_uid))
+                unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid);
+        if (gid_is_valid(u->ref_gid))
+                unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+
         if (serialize_jobs) {
                 if (u->job) {
                         fprintf(f, "job\n");
@@ -2851,6 +2859,28 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                                 u->cgroup_realized = b;
 
                         continue;
+
+                } else if (streq(l, "ref-uid")) {
+                        uid_t uid;
+
+                        r = parse_uid(v, &uid);
+                        if (r < 0)
+                                log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v);
+                        else
+                                unit_ref_uid_gid(u, uid, GID_INVALID);
+
+                        continue;
+
+                } else if (streq(l, "ref-gid")) {
+                        gid_t gid;
+
+                        r = parse_gid(v, &gid);
+                        if (r < 0)
+                                log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v);
+                        else
+                                unit_ref_uid_gid(u, UID_INVALID, gid);
+
+                        continue;
                 }
 
                 if (unit_can_serialize(u)) {
@@ -3310,6 +3340,7 @@ int unit_patch_contexts(Unit *u) {
                         }
 
                         ec->private_tmp = true;
+                        ec->remove_ipc = true;
                 }
         }
 
@@ -3932,3 +3963,144 @@ pid_t unit_main_pid(Unit *u) {
 
         return 0;
 }
+
+static void unit_unref_uid_internal(
+                Unit *u,
+                uid_t *ref_uid,
+                bool destroy_now,
+                void (*_manager_unref_uid)(Manager *m, uid_t uid, bool destroy_now)) {
+
+        assert(u);
+        assert(ref_uid);
+        assert(_manager_unref_uid);
+
+        /* Generic implementation of both unit_unref_uid() and unit_unref_gid(), under the assumption that uid_t and
+         * gid_t are actually the same time, with the same validity rules.
+         *
+         * Drops a reference to UID/GID from a unit. */
+
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+        assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+        if (!uid_is_valid(*ref_uid))
+                return;
+
+        _manager_unref_uid(u->manager, *ref_uid, destroy_now);
+        *ref_uid = UID_INVALID;
+}
+
+void unit_unref_uid(Unit *u, bool destroy_now) {
+        unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
+}
+
+void unit_unref_gid(Unit *u, bool destroy_now) {
+        unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
+}
+
+static int unit_ref_uid_internal(
+                Unit *u,
+                uid_t *ref_uid,
+                uid_t uid,
+                bool clean_ipc,
+                int (*_manager_ref_uid)(Manager *m, uid_t uid, bool clean_ipc)) {
+
+        int r;
+
+        assert(u);
+        assert(ref_uid);
+        assert(uid_is_valid(uid));
+        assert(_manager_ref_uid);
+
+        /* Generic implementation of both unit_ref_uid() and unit_ref_guid(), under the assumption that uid_t and gid_t
+         * are actually the same type, and have the same validity rules.
+         *
+         * Adds a reference on a specific UID/GID to this unit. Each unit referencing the same UID/GID maintains a
+         * reference so that we can destroy the UID/GID's IPC resources as soon as this is requested and the counter
+         * drops to zero. */
+
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+        assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+        if (*ref_uid == uid)
+                return 0;
+
+        if (uid_is_valid(*ref_uid)) /* Already set? */
+                return -EBUSY;
+
+        r = _manager_ref_uid(u->manager, uid, clean_ipc);
+        if (r < 0)
+                return r;
+
+        *ref_uid = uid;
+        return 1;
+}
+
+int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
+        return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid);
+}
+
+int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
+        return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid);
+}
+
+static int unit_ref_uid_gid_internal(Unit *u, uid_t uid, gid_t gid, bool clean_ipc) {
+        int r = 0, q = 0;
+
+        assert(u);
+
+        /* Reference both a UID and a GID in one go. Either references both, or neither. */
+
+        if (uid_is_valid(uid)) {
+                r = unit_ref_uid(u, uid, clean_ipc);
+                if (r < 0)
+                        return r;
+        }
+
+        if (gid_is_valid(gid)) {
+                q = unit_ref_gid(u, gid, clean_ipc);
+                if (q < 0) {
+                        if (r > 0)
+                                unit_unref_uid(u, false);
+
+                        return q;
+                }
+        }
+
+        return r > 0 || q > 0;
+}
+
+int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) {
+        ExecContext *c;
+        int r;
+
+        assert(u);
+
+        c = unit_get_exec_context(u);
+
+        r = unit_ref_uid_gid_internal(u, uid, gid, c ? c->remove_ipc : false);
+        if (r < 0)
+                return log_unit_warning_errno(u, r, "Couldn't add UID/GID reference to unit, proceeding without: %m");
+
+        return r;
+}
+
+void unit_unref_uid_gid(Unit *u, bool destroy_now) {
+        assert(u);
+
+        unit_unref_uid(u, destroy_now);
+        unit_unref_gid(u, destroy_now);
+}
+
+void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
+        int r;
+
+        assert(u);
+
+        /* This is invoked whenever one of the forked off processes let's us know the UID/GID its user name/group names
+         * resolved to. We keep track of which UID/GID is currently assigned in order to be able to destroy its IPC
+         * objects when no service references the UID/GID anymore. */
+
+        r = unit_ref_uid_gid(u, uid, gid);
+        if (r > 0)
+                bus_unit_send_change_signal(u);
+}
index a6c69938dd59b0fff7710cf472e99d2e11f811a2..53875653d7b465c8f063e0e0451a29f55630fdc9 100644 (file)
@@ -180,6 +180,10 @@ struct Unit {
         /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
         RateLimit auto_stop_ratelimit;
 
+        /* Reference to a specific UID/GID */
+        uid_t ref_uid;
+        gid_t ref_gid;
+
         /* Cached unit file state and preset */
         UnitFileState unit_file_state;
         int unit_file_preset;
@@ -195,8 +199,6 @@ struct Unit {
         CGroupMask cgroup_members_mask;
         int cgroup_inotify_wd;
 
-        uint32_t cgroup_netclass_id;
-
         /* How to start OnFailure units */
         JobMode on_failure_job_mode;
 
@@ -373,8 +375,7 @@ struct UnitVTable {
         /* Called whenever a process of this unit sends us a message */
         void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds);
 
-        /* Called whenever a name this Unit registered for comes or
-         * goes away. */
+        /* Called whenever a name this Unit registered for comes or goes away. */
         void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
 
         /* Called for each property that is being set */
@@ -623,6 +624,17 @@ int unit_fail_if_symlink(Unit *u, const char* where);
 
 int unit_start_limit_test(Unit *u);
 
+void unit_unref_uid(Unit *u, bool destroy_now);
+int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc);
+
+void unit_unref_gid(Unit *u, bool destroy_now);
+int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc);
+
+int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid);
+void unit_unref_uid_gid(Unit *u, bool destroy_now);
+
+void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
+
 /* Macros which append UNIT= or USER_UNIT= to the message */
 
 #define log_unit_full(unit, level, error, ...)                          \
index c16a324232f00bc2f20f8f2ca20cc98ed303422a..47953246675ad77426e42d5ccfeba668125e8155 100644 (file)
@@ -251,7 +251,7 @@ static int set_simple_string(sd_bus *bus, const char *method, const char *value)
 
 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
         _cleanup_free_ char *h = NULL;
-        char *hostname = args[1];
+        const char *hostname = args[1];
         int r;
 
         assert(args);
@@ -263,27 +263,29 @@ static int set_hostname(sd_bus *bus, char **args, unsigned n) {
         if (arg_pretty) {
                 const char *p;
 
-                /* If the passed hostname is already valid, then
-                 * assume the user doesn't know anything about pretty
-                 * hostnames, so let's unset the pretty hostname, and
-                 * just set the passed hostname as static/dynamic
+                /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty
+                 * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic
                  * hostname. */
-
-                if (arg_static && hostname_is_valid(hostname, true)) {
-                        p = "";
-                        /* maybe get rid of trailing dot */
-                        hostname = hostname_cleanup(hostname);
-                } else {
-                        p = h = strdup(hostname);
-                        if (!p)
-                                return log_oom();
-
-                        hostname_cleanup(hostname);
-                }
+                if (arg_static && hostname_is_valid(hostname, true))
+                        p = ""; /* No pretty hostname (as it is redundant), just a static one */
+                else
+                        p = hostname; /* Use the passed name as pretty hostname */
 
                 r = set_simple_string(bus, "SetPrettyHostname", p);
                 if (r < 0)
                         return r;
+
+                /* Now that we set the pretty hostname, let's clean up the parameter and use that as static
+                 * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing
+                 * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
+                 * multiple dots, and and dropping weird chars. Note that we clean the name up only if we also are
+                 * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
+                 * he does and pass the name as-is. */
+                h = strdup(hostname);
+                if (!h)
+                        return log_oom();
+
+                hostname = hostname_cleanup(h); /* Use the cleaned up name as static hostname */
         }
 
         if (arg_static) {
index 443b2a4cd767b59a644ba41b1fb210520fe81729..2a043a95b1be04497b0c54eaa9441de88d723cb4 100644 (file)
@@ -370,7 +370,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         if (s->runtime_journal)
                 return s->runtime_journal;
 
-        if (uid <= SYSTEM_UID_MAX)
+        if (uid <= SYSTEM_UID_MAX || uid_is_dynamic(uid))
                 return s->system_journal;
 
         r = sd_id128_get_machine(&machine);
index 995bf56586b59a766b19f29d082e34ff48eb1bad..c5f36725dc6722a95a34af3dafc6901a8d39eb52 100644 (file)
@@ -495,7 +495,7 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc
                 return NULL;
         }
 
-        /* then walk the chain of udev_device parents until the correspanding
+        /* then walk the chain of udev_device parents until the corresponding
            one is found */
         while ((udev_device = udev_device_get_parent(udev_device))) {
                 if (udev_device->device == parent)
index 8ef48dbaa16538e0fa40f87b3c5b75894bc17cdf..a950409254e0ee4e03d254cdbf411ed085d3b3a6 100644 (file)
@@ -85,7 +85,7 @@ int manager_handle_action(
         }
 
         /* If the key handling is inhibited, don't do anything */
-        if (!ignore_inhibited && inhibit_key > 0) {
+        if (inhibit_key > 0) {
                 if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
                         log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
                         return 0;
index 63363035e715433cd8c4411e325c9c4dad73881b..e0e73b034df141dd902f331c5dd2d7dc052b2947 100644 (file)
@@ -26,6 +26,7 @@
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "cgroup-util.h"
 #include "clean-ipc.h"
 #include "conf-parser.h"
 #include "escape.h"
@@ -612,9 +613,14 @@ int user_finalize(User *u) {
         if (k < 0)
                 r = k;
 
-        /* Clean SysV + POSIX IPC objects */
-        if (u->manager->remove_ipc) {
-                k = clean_ipc(u->uid);
+        /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
+         * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
+         * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
+         * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
+         * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
+         * and do it only for normal users. */
+        if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) {
+                k = clean_ipc_by_uid(u->uid);
                 if (k < 0)
                         r = k;
         }
@@ -891,7 +897,17 @@ int config_parse_user_tasks_max(
         assert(rvalue);
         assert(data);
 
-        /* First, try to parse as percentage */
+        if (isempty(rvalue)) {
+                *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
+                return 0;
+        }
+
+        if (streq(rvalue, "infinity")) {
+                *m = CGROUP_LIMIT_MAX;
+                return 0;
+        }
+
+        /* Try to parse as percentage */
         r = parse_percent(rvalue);
         if (r >= 0)
                 k = system_tasks_max_scale(r, 100U);
index 5ce36d28c7d18b27e717faebd0b19b03ecd41208..bbbf4aef571538781d6a3dad2fda5242e8274c4b 100644 (file)
@@ -38,6 +38,7 @@
 #include "signal-util.h"
 #include "strv.h"
 #include "udev-util.h"
+#include "cgroup-util.h"
 
 static void manager_free(Manager *m);
 
@@ -62,7 +63,7 @@ static void manager_reset_config(Manager *m) {
         m->idle_action = HANDLE_IGNORE;
 
         m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
-        m->user_tasks_max = system_tasks_max_scale(33U, 100U); /* 33% */
+        m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */
         m->sessions_max = 8192;
         m->inhibitors_max = 8192;
 
index cedaf47cf8e028a0d158b78979f28b1cfb8daede..b197a5da6b390ac7f54220eae4790fafbed1991e 100644 (file)
@@ -41,7 +41,7 @@ int route_new(Route **ret) {
         route->family = AF_UNSPEC;
         route->scope = RT_SCOPE_UNIVERSE;
         route->protocol = RTPROT_UNSPEC;
-        route->table = RT_TABLE_DEFAULT;
+        route->table = RT_TABLE_MAIN;
         route->lifetime = USEC_INFINITY;
 
         *ret = route;
@@ -322,7 +322,8 @@ int route_add(
         } else
                 return r;
 
-        *ret = route;
+        if (ret)
+                *ret = route;
 
         return 0;
 }
@@ -440,20 +441,14 @@ static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *
         assert(m);
         assert(link);
         assert(link->ifname);
-        assert(link->link_messages > 0);
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
-        link->link_messages--;
-
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EEXIST)
                 log_link_warning_errno(link, r, "could not remove route: %m");
 
-        if (link->link_messages == 0)
-                log_link_debug(link, "route removed");
-
         return 1;
 }
 
@@ -466,11 +461,8 @@ int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
         r = route_remove(route, route->link, route_expire_callback);
         if (r < 0)
                 log_warning_errno(r, "Could not remove route: %m");
-        else {
-                /* route may not be exist in kernel. If we fail still remove it */
-                route->link->link_messages++;
+        else
                 route_free(route);
-        }
 
         return 1;
 }
@@ -557,14 +549,12 @@ int route_configure(
         if (r < 0)
                 return log_error_errno(r, "Could not set flags: %m");
 
-        if (route->table != RT_TABLE_DEFAULT) {
-
+        if (route->table != RT_TABLE_MAIN) {
                 if (route->table < 256) {
                         r = sd_rtnl_message_route_set_table(req, route->table);
                         if (r < 0)
                                 return log_error_errno(r, "Could not set route table: %m");
                 } else {
-
                         r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
                         if (r < 0)
                                 return log_error_errno(r, "Could not set route table: %m");
index 8d57b26cbc5ee8826c52da0fafed7b1fcf9ea498..895f61c46296344965a89d7cb34f4553d036917e 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "env-util.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
@@ -434,6 +435,12 @@ enum nss_status _nss_mymachines_getpwnam_r(
         if (!machine_name_is_valid(machine))
                 goto not_found;
 
+        if (getenv_bool("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;
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -514,6 +521,9 @@ enum nss_status _nss_mymachines_getpwuid_r(
         if (uid < HOST_UID_LIMIT)
                 goto not_found;
 
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+                goto not_found;
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -605,6 +615,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
         if (!machine_name_is_valid(machine))
                 goto not_found;
 
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+                goto not_found;
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
@@ -682,6 +695,9 @@ enum nss_status _nss_mymachines_getgrgid_r(
         if (gid < HOST_GID_LIMIT)
                 goto not_found;
 
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+                goto not_found;
+
         r = sd_bus_open_system(&bus);
         if (r < 0)
                 goto fail;
index 7078c0c50cc51eaa9c0828d73fa6c6953a708281..17d04e958d50a65dc33f5ba7259a36d0808aaf22 100644 (file)
 
 #include "sd-bus.h"
 
+#include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "env-util.h"
+#include "fs-util.h"
 #include "macro.h"
 #include "nss-util.h"
 #include "signal-util.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "user-util.h"
 #include "util.h"
@@ -75,15 +78,50 @@ static const struct group nobody_group = {
 NSS_GETPW_PROTOTYPES(systemd);
 NSS_GETGR_PROTOTYPES(systemd);
 
+static int direct_lookup_name(const char *name, uid_t *ret) {
+        _cleanup_free_ char *s = NULL;
+        const char *path;
+        int r;
+
+        assert(name);
+
+        /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
+         * namespace and subject to proper authentication. However, there's one problem: if our module is called from
+         * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
+         * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
+
+        path = strjoina("/run/systemd/dynamic-uid/direct:", name);
+        r = readlink_malloc(path, &s);
+        if (r < 0)
+                return r;
+
+        return parse_uid(s, ret);
+}
+
+static int direct_lookup_uid(uid_t uid, char **ret) {
+        char path[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
+        int r;
+
+        xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
+
+        r = readlink_malloc(path, &s);
+        if (r < 0)
+                return r;
+        if (!valid_user_group_name(s)) { /* extra safety check */
+                free(s);
+                return -EINVAL;
+        }
+
+        *ret = s;
+        return 0;
+}
+
 enum nss_status _nss_systemd_getpwnam_r(
                 const char *name,
                 struct passwd *pwd,
                 char *buffer, size_t buflen,
                 int *errnop) {
 
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         uint32_t translated;
         size_t l;
         int r;
@@ -114,30 +152,45 @@ enum nss_status _nss_systemd_getpwnam_r(
         if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
                 goto not_found;
 
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "LookupDynamicUserByName",
-                               &error,
-                               &reply,
-                               "s",
-                               name);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
+                r = direct_lookup_name(name, (uid_t*) &translated);
+                if (r == -ENOENT)
                         goto not_found;
-
-                goto fail;
+                if (r < 0)
+                        goto fail;
+
+        } else {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+                _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+
+                r = sd_bus_open_system(&bus);
+                if (r < 0)
+                        goto fail;
+
+                r = sd_bus_call_method(bus,
+                                       "org.freedesktop.systemd1",
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "LookupDynamicUserByName",
+                                       &error,
+                                       &reply,
+                                       "s",
+                                       name);
+                if (r < 0) {
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                                goto not_found;
+
+                        goto fail;
+                }
+
+                r = sd_bus_message_read(reply, "u", &translated);
+                if (r < 0)
+                        goto fail;
         }
 
-        r = sd_bus_message_read(reply, "u", &translated);
-        if (r < 0)
-                goto fail;
-
         l = strlen(name);
         if (buflen < l+1) {
                 *errnop = ENOMEM;
@@ -175,6 +228,7 @@ enum nss_status _nss_systemd_getpwuid_r(
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_free_ char *direct = NULL;
         const char *translated;
         size_t l;
         int r;
@@ -204,30 +258,42 @@ enum nss_status _nss_systemd_getpwuid_r(
         if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
                 goto not_found;
 
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "LookupDynamicUserByUID",
-                               &error,
-                               &reply,
-                               "u",
-                               (uint32_t) uid);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                r = direct_lookup_uid(uid, &direct);
+                if (r == -ENOENT)
                         goto not_found;
-
-                goto fail;
+                if (r < 0)
+                        goto fail;
+
+                translated = direct;
+
+        } else {
+                r = sd_bus_open_system(&bus);
+                if (r < 0)
+                        goto fail;
+
+                r = sd_bus_call_method(bus,
+                                       "org.freedesktop.systemd1",
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "LookupDynamicUserByUID",
+                                       &error,
+                                       &reply,
+                                       "u",
+                                       (uint32_t) uid);
+                if (r < 0) {
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                                goto not_found;
+
+                        goto fail;
+                }
+
+                r = sd_bus_message_read(reply, "s", &translated);
+                if (r < 0)
+                        goto fail;
         }
 
-        r = sd_bus_message_read(reply, "s", &translated);
-        if (r < 0)
-                goto fail;
-
         l = strlen(translated) + 1;
         if (buflen < l) {
                 *errnop = ENOMEM;
@@ -262,9 +328,6 @@ enum nss_status _nss_systemd_getgrnam_r(
                 char *buffer, size_t buflen,
                 int *errnop) {
 
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         uint32_t translated;
         size_t l;
         int r;
@@ -294,30 +357,45 @@ enum nss_status _nss_systemd_getgrnam_r(
         if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
                 goto not_found;
 
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "LookupDynamicUserByName",
-                               &error,
-                               &reply,
-                               "s",
-                               name);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
+                r = direct_lookup_name(name, (uid_t*) &translated);
+                if (r == -ENOENT)
                         goto not_found;
-
-                goto fail;
+                if (r < 0)
+                        goto fail;
+        } else {
+
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+                _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+
+                r = sd_bus_open_system(&bus);
+                if (r < 0)
+                        goto fail;
+
+                r = sd_bus_call_method(bus,
+                                       "org.freedesktop.systemd1",
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "LookupDynamicUserByName",
+                                       &error,
+                                       &reply,
+                                       "s",
+                                       name);
+                if (r < 0) {
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                                goto not_found;
+
+                        goto fail;
+                }
+
+                r = sd_bus_message_read(reply, "u", &translated);
+                if (r < 0)
+                        goto fail;
         }
 
-        r = sd_bus_message_read(reply, "u", &translated);
-        if (r < 0)
-                goto fail;
-
         l = sizeof(char*) + strlen(name) + 1;
         if (buflen < l) {
                 *errnop = ENOMEM;
@@ -353,6 +431,7 @@ enum nss_status _nss_systemd_getgrgid_r(
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_free_ char *direct = NULL;
         const char *translated;
         size_t l;
         int r;
@@ -382,30 +461,41 @@ enum nss_status _nss_systemd_getgrgid_r(
         if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
                 goto not_found;
 
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
+        if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "LookupDynamicUserByUID",
-                               &error,
-                               &reply,
-                               "u",
-                               (uint32_t) gid);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                r = direct_lookup_uid(gid, &direct);
+                if (r == -ENOENT)
                         goto not_found;
-
-                goto fail;
+                if (r < 0)
+                        goto fail;
+
+                translated = direct;
+        } else {
+                r = sd_bus_open_system(&bus);
+                if (r < 0)
+                        goto fail;
+
+                r = sd_bus_call_method(bus,
+                                       "org.freedesktop.systemd1",
+                                       "/org/freedesktop/systemd1",
+                                       "org.freedesktop.systemd1.Manager",
+                                       "LookupDynamicUserByUID",
+                                       &error,
+                                       &reply,
+                                       "u",
+                                       (uint32_t) gid);
+                if (r < 0) {
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                                goto not_found;
+
+                        goto fail;
+                }
+
+                r = sd_bus_message_read(reply, "s", &translated);
+                if (r < 0)
+                        goto fail;
         }
 
-        r = sd_bus_message_read(reply, "s", &translated);
-        if (r < 0)
-                goto fail;
-
         l = sizeof(char*) + strlen(translated) + 1;
         if (buflen < l) {
                 *errnop = ENOMEM;
index 28bfa8b52265e745bf6aa0e8bc75589edc86cac5..feb4a067376da44b20c80892c1733a4034fb18d1 100644 (file)
@@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
                               "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
                               "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
                               "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
-                              "RestrictRealtime", "DynamicUser")) {
+                              "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
 
                 r = parse_boolean(eq);
                 if (r < 0)
index a3ac7aeb822a738a50e4ef88c1d1977e98d9c1b1..d5db604f031a82096565d720050d6c347c633d27 100644 (file)
 #include "macro.h"
 #include "string-util.h"
 #include "strv.h"
+#include "user-util.h"
 
-static int clean_sysvipc_shm(uid_t delete_uid) {
+static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
+
+        if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
+                return true;
+
+        if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
+                return true;
+
+        return false;
+}
+
+static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -77,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
                 if (n_attached > 0)
                         continue;
 
-                if (uid != delete_uid)
+                if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
                 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
@@ -89,7 +101,8 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
                         ret = log_warning_errno(errno,
                                                 "Failed to remove SysV shared memory segment %i: %m",
                                                 shmid);
-                }
+                } else
+                        log_debug("Removed SysV shared memory segment %i.", shmid);
         }
 
         return ret;
@@ -98,7 +111,7 @@ fail:
         return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
 }
 
-static int clean_sysvipc_sem(uid_t delete_uid) {
+static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -128,7 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
                            &semid, &uid, &gid, &cuid, &cgid) != 5)
                         continue;
 
-                if (uid != delete_uid)
+                if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
                 if (semctl(semid, 0, IPC_RMID) < 0) {
@@ -140,7 +153,8 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
                         ret = log_warning_errno(errno,
                                                 "Failed to remove SysV semaphores object %i: %m",
                                                 semid);
-                }
+                } else
+                        log_debug("Removed SysV semaphore %i.", semid);
         }
 
         return ret;
@@ -149,7 +163,7 @@ fail:
         return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
 }
 
-static int clean_sysvipc_msg(uid_t delete_uid) {
+static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -180,7 +194,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
                            &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
                         continue;
 
-                if (uid != delete_uid)
+                if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
                 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
@@ -192,7 +206,8 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
                         ret = log_warning_errno(errno,
                                                 "Failed to remove SysV message queue %i: %m",
                                                 msgid);
-                }
+                } else
+                        log_debug("Removed SysV message queue %i.", msgid);
         }
 
         return ret;
@@ -201,13 +216,13 @@ fail:
         return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
 }
 
-static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
+static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
         struct dirent *de;
         int ret = 0, r;
 
         assert(dir);
 
-        FOREACH_DIRENT(de, dir, goto fail) {
+        FOREACH_DIRENT_ALL(de, dir, goto fail) {
                 struct stat st;
 
                 if (STR_IN_SET(de->d_name, "..", "."))
@@ -217,12 +232,11 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
                         if (errno == ENOENT)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
-                        ret = -errno;
+                        ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
                         continue;
                 }
 
-                if (st.st_uid != uid)
+                if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
                         continue;
 
                 if (S_ISDIR(st.st_mode)) {
@@ -230,12 +244,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
 
                         kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
                         if (!kid) {
-                                if (errno != ENOENT) {
-                                        log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
-                                        ret = -errno;
-                                }
+                                if (errno != ENOENT)
+                                        ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
                         } else {
-                                r = clean_posix_shm_internal(kid, uid);
+                                r = clean_posix_shm_internal(kid, uid, gid);
                                 if (r < 0)
                                         ret = r;
                         }
@@ -245,9 +257,9 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
                                 if (errno == ENOENT)
                                         continue;
 
-                                log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
-                                ret = -errno;
-                        }
+                                ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
+                        } else
+                                log_debug("Removed POSIX shared memory directory %s", de->d_name);
                 } else {
 
                         if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
@@ -255,20 +267,19 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
                                 if (errno == ENOENT)
                                         continue;
 
-                                log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
-                                ret = -errno;
-                        }
+                                ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
+                        } else
+                                log_debug("Removed POSIX shared memory segment %s", de->d_name);
                 }
         }
 
         return ret;
 
 fail:
-        log_warning_errno(errno, "Failed to read /dev/shm: %m");
-        return -errno;
+        return log_warning_errno(errno, "Failed to read /dev/shm: %m");
 }
 
-static int clean_posix_shm(uid_t uid) {
+static int clean_posix_shm(uid_t uid, gid_t gid) {
         _cleanup_closedir_ DIR *dir = NULL;
 
         dir = opendir("/dev/shm");
@@ -279,10 +290,10 @@ static int clean_posix_shm(uid_t uid) {
                 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
         }
 
-        return clean_posix_shm_internal(dir, uid);
+        return clean_posix_shm_internal(dir, uid, gid);
 }
 
-static int clean_posix_mq(uid_t uid) {
+static int clean_posix_mq(uid_t uid, gid_t gid) {
         _cleanup_closedir_ DIR *dir = NULL;
         struct dirent *de;
         int ret = 0;
@@ -295,7 +306,7 @@ static int clean_posix_mq(uid_t uid) {
                 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
         }
 
-        FOREACH_DIRENT(de, dir, goto fail) {
+        FOREACH_DIRENT_ALL(de, dir, goto fail) {
                 struct stat st;
                 char fn[1+strlen(de->d_name)+1];
 
@@ -312,7 +323,7 @@ static int clean_posix_mq(uid_t uid) {
                         continue;
                 }
 
-                if (st.st_uid != uid)
+                if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
                         continue;
 
                 fn[0] = '/';
@@ -325,7 +336,8 @@ static int clean_posix_mq(uid_t uid) {
                         ret = log_warning_errno(errno,
                                                 "Failed to unlink POSIX message queue %s: %m",
                                                 fn);
-                }
+                } else
+                        log_debug("Removed POSIX message queue %s", fn);
         }
 
         return ret;
@@ -334,32 +346,44 @@ fail:
         return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
 }
 
-int clean_ipc(uid_t uid) {
+int clean_ipc(uid_t uid, gid_t gid) {
         int ret = 0, r;
 
-        /* Refuse to clean IPC of the root and system users */
-        if (uid <= SYSTEM_UID_MAX)
+        /* Anything to do? */
+        if (!uid_is_valid(uid) && !gid_is_valid(gid))
+                return 0;
+
+        /* Refuse to clean IPC of the root user */
+        if (uid == 0 && gid == 0)
                 return 0;
 
-        r = clean_sysvipc_shm(uid);
+        r = clean_sysvipc_shm(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_sysvipc_sem(uid);
+        r = clean_sysvipc_sem(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_sysvipc_msg(uid);
+        r = clean_sysvipc_msg(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_posix_shm(uid);
+        r = clean_posix_shm(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_posix_mq(uid);
+        r = clean_posix_mq(uid, gid);
         if (r < 0)
                 ret = r;
 
         return ret;
 }
+
+int clean_ipc_by_uid(uid_t uid) {
+        return clean_ipc(uid, GID_INVALID);
+}
+
+int clean_ipc_by_gid(gid_t gid) {
+        return clean_ipc(UID_INVALID, gid);
+}
index 44a83afcf703dc1a1f960aba6dd51ef12abfa014..6ca57f44fdd0e53e15363f11957a63dbd787ae10 100644 (file)
@@ -21,4 +21,6 @@
 
 #include <sys/types.h>
 
-int clean_ipc(uid_t uid);
+int clean_ipc(uid_t uid, gid_t gid);
+int clean_ipc_by_uid(uid_t uid);
+int clean_ipc_by_gid(gid_t gid);
index e740ef3910549c3262f2a6aa64a69a2e62735e34..6a16f8985b897a727e1a7ba943c8a4536afc4439 100644 (file)
@@ -393,19 +393,40 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
                 log_error_errno(r, "Failed to %s: %m.", verb);
 }
 
+/**
+ * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
+ * wc should be the full path in the host file system.
+ */
+static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
+        assert(path_is_absolute(wd));
+
+        /* This will give incorrect results if the paths are relative and go outside
+         * of the chroot. False negatives are possible. */
+
+        a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
+        b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
+        return path_equal_or_files_same(a, b);
+}
+
 static int create_symlink(
+                const LookupPaths *paths,
                 const char *old_path,
                 const char *new_path,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        _cleanup_free_ char *dest = NULL;
+        _cleanup_free_ char *dest = NULL, *dirname = NULL;
+        const char *rp;
         int r;
 
         assert(old_path);
         assert(new_path);
 
+        rp = skip_root(paths, old_path);
+        if (rp)
+                old_path = rp;
+
         /* Actually create a symlink, and remember that we did. Is
          * smart enough to check if there's already a valid symlink in
          * place.
@@ -436,7 +457,11 @@ static int create_symlink(
                 return r;
         }
 
-        if (path_equal(dest, old_path))
+        dirname = dirname_malloc(new_path);
+        if (!dirname)
+                return -ENOMEM;
+
+        if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
                 return 1;
 
         if (!force) {
@@ -620,7 +645,7 @@ static int remove_marked_symlinks(
 
         fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
         if (fd < 0)
-                return -errno;
+                return errno == ENOENT ? 0 : -errno;
 
         do {
                 int q, cfd;
@@ -903,6 +928,10 @@ static int install_info_may_process(
         return 0;
 }
 
+/**
+ * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
+ * hashmap, or retrieves the existing one if already present.
+ */
 static int install_info_add(
                 InstallContext *c,
                 const char *name,
@@ -1334,9 +1363,8 @@ static int install_info_follow(
 }
 
 /**
- * Search for the unit file. If the unit name is a symlink,
- * follow the symlink to the target, maybe more than once.
- * Propagate the instance name if present.
+ * Search for the unit file. If the unit name is a symlink, follow the symlink to the
+ * target, maybe more than once. Propagate the instance name if present.
  */
 static int install_info_traverse(
                 UnitFileScope scope,
@@ -1421,6 +1449,10 @@ static int install_info_traverse(
         return 0;
 }
 
+/**
+ * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
+ * or the name (otherwise). root_dir is prepended to the path.
+ */
 static int install_info_add_auto(
                 InstallContext *c,
                 const LookupPaths *paths,
@@ -1479,7 +1511,6 @@ static int install_info_symlink_alias(
 
         STRV_FOREACH(s, i->aliases) {
                 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
-                const char *rp;
 
                 q = install_full_printf(i, *s, &dst);
                 if (q < 0)
@@ -1489,9 +1520,7 @@ static int install_info_symlink_alias(
                 if (!alias_path)
                         return -ENOMEM;
 
-                rp = skip_root(paths, i->path);
-
-                q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes);
+                q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
                 if (r == 0)
                         r = q;
         }
@@ -1535,7 +1564,6 @@ static int install_info_symlink_wants(
 
         STRV_FOREACH(s, list) {
                 _cleanup_free_ char *path = NULL, *dst = NULL;
-                const char *rp;
 
                 q = install_full_printf(i, *s, &dst);
                 if (q < 0)
@@ -1550,9 +1578,7 @@ static int install_info_symlink_wants(
                 if (!path)
                         return -ENOMEM;
 
-                rp = skip_root(paths, i->path);
-
-                q = create_symlink(rp ?: i->path, path, true, changes, n_changes);
+                q = create_symlink(paths, i->path, path, true, changes, n_changes);
                 if (r == 0)
                         r = q;
         }
@@ -1569,7 +1595,6 @@ static int install_info_symlink_link(
                 unsigned *n_changes) {
 
         _cleanup_free_ char *path = NULL;
-        const char *rp;
         int r;
 
         assert(i);
@@ -1587,9 +1612,7 @@ static int install_info_symlink_link(
         if (!path)
                 return -ENOMEM;
 
-        rp = skip_root(paths, i->path);
-
-        return create_symlink(rp ?: i->path, path, force, changes, n_changes);
+        return create_symlink(paths, i->path, path, force, changes, n_changes);
 }
 
 static int install_info_apply(
@@ -1663,6 +1686,17 @@ static int install_context_apply(
                 if (r < 0)
                         return r;
 
+                /* We can attempt to process a masked unit when a different unit
+                 * that we were processing specifies it in DefaultInstance= or Also=. */
+                if (i->type == UNIT_FILE_TYPE_MASKED) {
+                        unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
+                        if (r >= 0)
+                                /* Assume that some *could* have been enabled here, avoid
+                                 * "empty [Install] section" warning. */
+                                r += 1;
+                        continue;
+                }
+
                 if (i->type != UNIT_FILE_TYPE_REGULAR)
                         continue;
 
@@ -1765,7 +1799,7 @@ int unit_file_mask(
                 if (!path)
                         return -ENOMEM;
 
-                q = create_symlink("/dev/null", path, force, changes, n_changes);
+                q = create_symlink(&paths, "/dev/null", path, force, changes, n_changes);
                 if (q < 0 && r >= 0)
                         r = q;
         }
@@ -1925,14 +1959,12 @@ int unit_file_link(
         r = 0;
         STRV_FOREACH(i, todo) {
                 _cleanup_free_ char *new_path = NULL;
-                const char *old_path;
 
-                old_path = skip_root(&paths, *i);
                 new_path = path_make_absolute(basename(*i), config_path);
                 if (!new_path)
                         return -ENOMEM;
 
-                q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes);
+                q = create_symlink(&paths, *i, new_path, force, changes, n_changes);
                 if (q < 0 && r >= 0)
                         r = q;
         }
@@ -1967,7 +1999,6 @@ int unit_file_revert(
                 unsigned *n_changes) {
 
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
-        /* _cleanup_(install_context_done) InstallContext c = {}; */
         _cleanup_lookup_paths_free_ LookupPaths paths = {};
         _cleanup_strv_free_ char **todo = NULL;
         size_t n_todo = 0, n_allocated = 0;
@@ -2312,7 +2343,7 @@ int unit_file_set_default(
         _cleanup_lookup_paths_free_ LookupPaths paths = {};
         _cleanup_(install_context_done) InstallContext c = {};
         UnitFileInstallInfo *i;
-        const char *new_path, *old_path;
+        const char *new_path;
         int r;
 
         assert(scope >= 0);
@@ -2335,10 +2366,8 @@ int unit_file_set_default(
         if (r < 0)
                 return r;
 
-        old_path = skip_root(&paths, i->path);
         new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
-
-        return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes);
+        return create_symlink(&paths, i->path, new_path, force, changes, n_changes);
 }
 
 int unit_file_get_default(
@@ -2685,18 +2714,25 @@ static int preset_prepare_one(
                 InstallContext *plus,
                 InstallContext *minus,
                 LookupPaths *paths,
-                UnitFilePresetMode mode,
                 const char *name,
                 Presets presets,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
+        _cleanup_(install_context_done) InstallContext tmp = {};
         UnitFileInstallInfo *i;
         int r;
 
-        if (install_info_find(plus, name) ||
-            install_info_find(minus, name))
+        if (install_info_find(plus, name) || install_info_find(minus, name))
+                return 0;
+
+        r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+        if (r < 0)
+                return r;
+        if (!streq(name, i->name)) {
+                log_debug("Skipping %s because is an alias for %s", name, i->name);
                 return 0;
+        }
 
         r = query_presets(name, presets);
         if (r < 0)
@@ -2748,7 +2784,7 @@ int unit_file_preset(
                 return r;
 
         STRV_FOREACH(i, files) {
-                r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes);
+                r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
                 if (r < 0)
                         return r;
         }
@@ -2809,7 +2845,7 @@ int unit_file_preset_all(
                                 continue;
 
                         /* we don't pass changes[] in, because we want to handle errors on our own */
-                        r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0);
+                        r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
                         if (r == -ERFKILL)
                                 r = unit_file_changes_add(changes, n_changes,
                                                           UNIT_FILE_IS_MASKED, de->d_name, NULL);
index 66003aa6bd05cc8a866d23d0e9dd437310830fdd..6f7d71ef9ab1cd630515257a3003e967a25f6056 100644 (file)
@@ -37,66 +37,82 @@ static void test_condition_test_path(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_READ_WRITE, "/tmp", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 }
@@ -105,38 +121,44 @@ static void test_condition_test_ac_power(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_AC_POWER, "true", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) == on_ac_power());
         condition_free(condition);
 
         condition = condition_new(CONDITION_AC_POWER, "false", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) != on_ac_power());
         condition_free(condition);
 
         condition = condition_new(CONDITION_AC_POWER, "false", false, true);
+        assert_se(condition);
         assert_se(condition_test(condition) == on_ac_power());
         condition_free(condition);
 }
 
 static void test_condition_test_host(void) {
+        _cleanup_free_ char *hostname = NULL;
+        char sid[SD_ID128_STRING_MAX];
         Condition *condition;
         sd_id128_t id;
         int r;
-        char sid[SD_ID128_STRING_MAX];
-        _cleanup_free_ char *hostname = NULL;
 
         r = sd_id128_get_machine(&id);
         assert_se(r >= 0);
         assert_se(sd_id128_to_string(id, sid));
 
         condition = condition_new(CONDITION_HOST, sid, false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_HOST, sid, false, true);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
@@ -148,6 +170,7 @@ static void test_condition_test_host(void) {
                 log_notice("hostname is an id128, skipping test");
         else {
                 condition = condition_new(CONDITION_HOST, hostname, false, false);
+                assert_se(condition);
                 assert_se(condition_test(condition));
                 condition_free(condition);
         }
@@ -165,14 +188,17 @@ static void test_condition_test_architecture(void) {
         assert_se(sa);
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);
+        assert_se(condition);
         assert_se(condition_test(condition) == 0);
         condition_free(condition);
 }
@@ -181,10 +207,12 @@ static void test_condition_test_kernel_command_line(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 }
@@ -193,10 +221,12 @@ static void test_condition_test_null(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_NULL, NULL, false, false);
+        assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_NULL, NULL, false, true);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 }
@@ -205,31 +235,36 @@ static void test_condition_test_security(void) {
         Condition *condition;
 
         condition = condition_new(CONDITION_SECURITY, "garbage oifdsjfoidsjoj", false, false);
+        assert_se(condition);
         assert_se(!condition_test(condition));
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "selinux", false, true);
+        assert_se(condition);
         assert_se(condition_test(condition) != mac_selinux_have());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "ima", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) == use_ima());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "apparmor", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) == mac_apparmor_use());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "smack", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) == mac_smack_use());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "audit", false, false);
+        assert_se(condition);
         assert_se(condition_test(condition) == use_audit());
         condition_free(condition);
 }
 
-
 int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
index 17fde9f27eecfba906b4cb4435ae099c17b0e08a..1c3d13ed1d79a07628744bcb3e3d53c10a27b2b6 100644 (file)
@@ -42,6 +42,7 @@ static void test_hostname_is_valid(void) {
         assert_se(!hostname_is_valid("foo..bar", false));
         assert_se(!hostname_is_valid("foo.bar..", false));
         assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", false));
+        assert_se(!hostname_is_valid("au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local", false));
 
         assert_se(hostname_is_valid("foobar", true));
         assert_se(hostname_is_valid("foobar.com", true));
index c5bcaf47bb1a2694555dea58ca5f5a648a924abf..551eba7215dd4883c86cd10b89d8dc2e16112283 100644 (file)
@@ -32,5 +32,5 @@ int main(int argc, char *argv[]) {
                 return EXIT_FAILURE;
         }
 
-        return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+        return clean_ipc_by_uid(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
index ee1b864bcfc1bffec39ff0470fd0cd6de21aa56c..0f494b75527ea137227a014d4f0f2d56a9113396 100644 (file)
@@ -15,6 +15,7 @@ enable getty@.service
 enable systemd-timesyncd.service
 enable systemd-networkd.service
 enable systemd-resolved.service
+enable systemd-networkd-wait-online.service
 
 disable console-getty.service
 disable console-shell.service
@@ -23,10 +24,12 @@ disable debug-shell.service
 disable halt.target
 disable kexec.target
 disable poweroff.target
-disable reboot.target
+enable reboot.target
 disable rescue.target
+disable exit.target
 
 disable syslog.socket
 
 disable systemd-journal-gatewayd.*
-disable systemd-networkd-wait-online.service
+disable systemd-journal-remote.*
+disable systemd-journal-upload.*
index 129af854f1feb7f1915aa5a43ff8eef7280bc4e1..9723386b233de99527ebd22c36ccddeb77c737de 100755 (executable)
@@ -37,7 +37,7 @@ my $EXIT_TEST_SKIP      = 77;
 
 my $rules_10k_tags      = "";
 for (my $i = 1; $i <= 10000; ++$i) {
-    $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
+        $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
 }
 
 my @tests = (
@@ -1596,6 +1596,6 @@ system("umount", "$udev_tmpfs");
 rmdir($udev_tmpfs);
 
 if ($error > 0) {
-    exit(1);
+        exit(1);
 }
 exit(0);
diff --git a/units/user/bluetooth.target b/units/user/bluetooth.target
new file mode 120000 (symlink)
index 0000000..72e74be
--- /dev/null
@@ -0,0 +1 @@
+../bluetooth.target
\ No newline at end of file
diff --git a/units/user/busnames.target b/units/user/busnames.target
new file mode 120000 (symlink)
index 0000000..04f4ba1
--- /dev/null
@@ -0,0 +1 @@
+../busnames.target
\ No newline at end of file
diff --git a/units/user/paths.target b/units/user/paths.target
new file mode 120000 (symlink)
index 0000000..33545d2
--- /dev/null
@@ -0,0 +1 @@
+../paths.target
\ No newline at end of file
diff --git a/units/user/printer.target b/units/user/printer.target
new file mode 120000 (symlink)
index 0000000..8b8d551
--- /dev/null
@@ -0,0 +1 @@
+../printer.target
\ No newline at end of file
diff --git a/units/user/shutdown.target b/units/user/shutdown.target
new file mode 120000 (symlink)
index 0000000..a9de837
--- /dev/null
@@ -0,0 +1 @@
+../shutdown.target
\ No newline at end of file
diff --git a/units/user/smartcard.target b/units/user/smartcard.target
new file mode 120000 (symlink)
index 0000000..f7a23b6
--- /dev/null
@@ -0,0 +1 @@
+../smartcard.target
\ No newline at end of file
diff --git a/units/user/sockets.target b/units/user/sockets.target
new file mode 120000 (symlink)
index 0000000..a9e4b97
--- /dev/null
@@ -0,0 +1 @@
+../sockets.target
\ No newline at end of file
diff --git a/units/user/sound.target b/units/user/sound.target
new file mode 120000 (symlink)
index 0000000..17c8e9d
--- /dev/null
@@ -0,0 +1 @@
+../sound.target
\ No newline at end of file
diff --git a/units/user/timers.target b/units/user/timers.target
new file mode 120000 (symlink)
index 0000000..f98b68a
--- /dev/null
@@ -0,0 +1 @@
+../timers.target
\ No newline at end of file