]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #10407 from yuwata/netlink-slot
authorLennart Poettering <lennart@poettering.net>
Thu, 18 Oct 2018 16:05:58 +0000 (18:05 +0200)
committerGitHub <noreply@github.com>
Thu, 18 Oct 2018 16:05:58 +0000 (18:05 +0200)
sd-netlink: introduce sd_netlink_slot object and relevant functions

185 files changed:
.mkosi/mkosi.fedora
NEWS
TODO
catalog/systemd.be.catalog.in
catalog/systemd.be@latin.catalog.in
catalog/systemd.bg.catalog.in
catalog/systemd.catalog.in
catalog/systemd.da.catalog.in
catalog/systemd.fr.catalog.in
catalog/systemd.hr.catalog.in
catalog/systemd.hu.catalog.in
catalog/systemd.it.catalog.in
catalog/systemd.ko.catalog.in
catalog/systemd.pl.catalog.in
catalog/systemd.pt_BR.catalog.in
catalog/systemd.ru.catalog.in
catalog/systemd.sr.catalog.in
catalog/systemd.zh_CN.catalog.in
catalog/systemd.zh_TW.catalog.in
coccinelle/cmp.cocci [new file with mode: 0644]
docs/ENVIRONMENT.md
docs/PORTABLE_SERVICES.md
docs/TRANSIENT-SETTINGS.md
docs/TRANSLATORS.md
docs/_config.yml
hwdb/60-evdev.hwdb
man/bootctl.xml
man/journald.conf.xml
man/kernel-install.xml
man/logind.conf.xml
man/rules/meson.build
man/sd_bus_error.xml
man/systemd-boot.xml
man/systemd-portabled.service.xml
man/systemd-socket-proxyd.xml
man/systemd-sysusers.xml
man/systemd.exec.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.offline-updates.xml
man/systemd.resource-control.xml
man/systemd.socket.xml
man/systemd.unit.xml
man/systemd.xml
meson.build
meson_options.txt
mkosi.build
rules/60-persistent-storage.rules
src/basic/alloc-util.h
src/basic/btrfs-util.c
src/basic/cgroup-util.c
src/basic/def.h
src/basic/exec-util.c
src/basic/format-table.c
src/basic/hashmap.c
src/basic/hashmap.h
src/basic/list.h
src/basic/path-util.c
src/basic/path-util.h
src/basic/prioq.c
src/basic/prioq.h
src/basic/rlimit-util.c
src/basic/rlimit-util.h
src/basic/set.h
src/basic/siphash24.h
src/basic/socket-label.c
src/basic/socket-util.c
src/basic/socket-util.h
src/basic/string-util.c
src/basic/strxcpyx.c
src/basic/util.c
src/basic/util.h
src/boot/efi/boot.c
src/boot/efi/stub.c
src/boot/efi/util.c
src/boot/efi/util.h
src/core/dbus-execute.c
src/core/dbus-socket.c
src/core/dbus-unit.c
src/core/dbus.c
src/core/emergency-action.c
src/core/emergency-action.h
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/main.c
src/core/manager.c
src/core/service.c
src/core/socket.c
src/core/system.conf.in
src/core/transaction.c
src/core/unit.c
src/core/unit.h
src/coredump/coredumpctl.c
src/fsck/fsck.c
src/import/importd.c
src/journal-remote/journal-remote-main.c
src/journal-remote/journal-upload.c
src/journal/journal-file.c
src/journal/journalctl.c
src/journal/journald-audit.c
src/journal/journald-context.c
src/journal/journald-context.h
src/journal/journald-native.c
src/journal/journald-rate-limit.c
src/journal/journald-rate-limit.h
src/journal/journald-server.c
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/journal/sd-journal.c
src/libsystemd-network/dhcp-network.c
src/libsystemd-network/dhcp6-network.c
src/libsystemd-network/icmp6-util.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-bus/bus-error.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-watch-bind.c
src/libsystemd/sd-daemon/sd-daemon.c
src/libsystemd/sd-device/device-enumerator.c
src/libsystemd/sd-device/test-sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libudev/libudev-monitor.c
src/login/loginctl.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-gperf.gperf
src/login/logind-seat.c
src/login/logind-seat.h
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user-dbus.c
src/login/logind-user.c
src/login/logind-user.h
src/login/logind.c
src/login/logind.h
src/login/meson.build
src/login/pam_systemd.c
src/login/user-runtime-dir.c
src/machine/machinectl.c
src/network/networkd-route.c
src/nspawn/nspawn.c
src/reply-password/reply-password.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-stub.c
src/resolve/resolved-llmnr.c
src/resolve/resolved-mdns.c
src/shared/ask-password-api.c
src/shared/bus-unit-util.c
src/socket-proxy/socket-proxyd.c
src/sulogin-shell/sulogin-shell.c
src/systemctl/systemctl.c
src/systemd/sd-bus.h
src/test/meson.build
src/test/test-emergency-action.c [new file with mode: 0644]
src/test/test-prioq.c
src/test/test-socket-util.c
src/timesync/timesyncd-manager.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/udev-ctrl.c
src/udev/udev-event.c
src/udev/udev-node.c
src/udev/udev-node.h
src/udev/udev-rules.c
src/udev/udev-watch.c
src/udev/udev.h
src/udev/udevadm-test.c
src/udev/udevd.c
test/fuzz/fuzz-unit-file/directives.service
units/meson.build
units/systemd-exit.service [moved from units/systemd-exit.service.in with 88% similarity]
units/systemd-journal-gatewayd.service.in
units/systemd-journal-remote.service.in
units/systemd-journal-upload.service.in
units/systemd-journald.service.in
units/systemd-logind.service.in
units/systemd-poweroff.service [moved from units/systemd-poweroff.service.in with 89% similarity]
units/systemd-reboot.service [moved from units/systemd-reboot.service.in with 89% similarity]
units/user-.slice.d/10-defaults.conf
units/user-runtime-dir@.service.in
units/user/meson.build
units/user/systemd-exit.service [moved from units/user/systemd-exit.service.in with 87% similarity]

index 63027d9fc79a8dc57b8e082bd4318b17ad3fac58..ff43ecdfe6b2785076b4f8695e2d89120f798b38 100644 (file)
@@ -5,11 +5,12 @@
 
 [Distribution]
 Distribution=fedora
-Release=27
+Release=29
 
 [Output]
 Format=raw_btrfs
 Bootable=yes
+KernelCommandLine=printk.devkmsg=on
 
 [Partitions]
 RootSize=3G
diff --git a/NEWS b/NEWS
index a8a30aaa07fa927ee1de7bfeceb6909cd9c3e30e..d378b08b701a4cac008df1d03866e75246bac985 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -27,6 +27,42 @@ CHANGES WITH 240 in spe:
           non-transient services (i.e. those defined with unit files on disk)
           we will continue to default to Type=simple.
 
+        * The Linux kernel's current default RLIMIT_NOFILE resource limit for
+          userspace processes is set to 1024 (soft) and 4096
+          (hard). Previously, systemd passed this on unmodified to all
+          processes it forked off. With this systemd release the hard limit
+          systemd passes on is increased to 256K, overriding the kernel's
+          defaults and substantially increasing the number of simultaneous file
+          descriptors unprivileged userspace processes can allocate. Note that
+          the soft limit remains at 1024 for compatibility reasons: the
+          traditional UNIX select() call cannot deal with file descriptors >=
+          1024 and increasing the soft limit globally might thus result in
+          programs unexpectedly allocating a high file descriptor and thus
+          failing abnormally when attempting to use it with select() (of
+          course, programs shouldn't use select() anymore, and prefer
+          poll()/epoll, but the call unfortunately remains undeservedly popular
+          at this time). This change reflects the fact that file descriptor
+          handling in the Linux kernel has been optimized in more recent
+          kernels and allocating large numbers of them should be much cheaper
+          both in memory and in performance than it used to be. Programs that
+          want to take benefit of the increased limit have to "opt-in" into
+          high file descriptors explicitly by setting their soft limit to the
+          hard limit during initialization. Of course, when doing that they
+          must do this acknowledging the fact that they cannot use select()
+          anymore (and neither can any shared library they use — or any shared
+          library used by any shared library they use and so on).
+
+        * The fs.nr_open and fs.file-max sysctls are now automatically bumped
+          to the highest possible values, as separate accounting of file
+          descriptors is no longer necessary, as memcg tracks them correctly as
+          part of the memory accounting anyway. Thus, from the four limits on
+          file descriptors currently enforced (fs.file-max, fs.nr_open,
+          RLIMIT_NOFILE hard, RLIMIT_NOFILE soft) we turn off the first two,
+          and keep only the latter two. A set of build-time options
+          (-Dbump-proc-sys-fs-file-max=no and -Dbump-proc-sys-fs-nr-open=no)
+          has been added to revert this change in behaviour, which might be
+          an option for systems that turn off memcg in the kernel.
+
 CHANGES WITH 239:
 
         * NETWORK INTERFACE DEVICE NAMING CHANGES: systemd-udevd's "net_id"
diff --git a/TODO b/TODO
index b78892a9168faec87084ee234c546effcf82daac..07d6f3500ec7bcc6d296fff4b6dcfa54e99866eb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,6 +2,10 @@ Bugfixes:
 
 * copy.c: set the right chattrs before copying files and others after
 
+* Many manager configuration settings that are only applicable to user
+  manager or system manager can be always set. It would be better to reject
+  them when parsing config.
+
 External:
 
 * Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
@@ -22,6 +26,12 @@ Features:
 
 * set memory.oom.group in cgroupsv2 for all leaf cgroups
 
+* drop umask() calls and suchlike from our generators, pid1 should set things up correctly anyway
+
+* paranoia: whenever we process passwords, call mlock() on the memory
+  first. i.e. look for all places we use string_erase()/string_free_erase() and
+  augment them with mlock()
+
 * whenever oom_kill memory.event event is triggered print a nice log message
 
 * Move RestrictAddressFamily= to the new cgroup create socket
@@ -31,6 +41,10 @@ Features:
 
 * chown() tty a service is attached to after the service goes down
 
+* optionally: turn on cgroup delegation for per-session scope units
+
+* introduce per-unit (i.e. per-slice, per-service) journal log size limits.
+
 * optionally, if a per-partition GPT flag is set for the root/home/… partitions
   format the partition on next boot and unset the flag, in order to implement
   factory reset. also, add a second flag that simply indicates whether such a
@@ -38,20 +52,6 @@ Features:
   show state of these flags, and optionally trigger such a factory reset on
   next boot by setting the flag.
 
-* logind: maybe watch utmp asynchronously using inotify, and populate our own
-  tracked session metadata from the fields available therein. Why bother? Right
-  now, all "ssh" sessions will be tracked without their TTY by logind (which is
-  not just unfriendly to users as this means "loginctl session-status" shows
-  less information than "who" in many cases, but also breaks the IdleAction
-  logic, as we never can detect such sessions as idle, as we have no TTY to
-  watch). ssh sets the PAM_TTY field on its PAM sessions to "ssh" rather than
-  the actual pty, because the PAM session is opened early on for new
-  connections, but the PTY only registered much later (if at all). ssh writes
-  the utmp record only after a TTY is actually registered, hence we could use
-  this data then, and use it if it is available. Using utmp for this is ugly of
-  course, and watching things asynchronously even more so, but it should be
-  good enough for the idle detection logic at least.
-
 * maybe extend .path units to expose fanotify() per-mount change events
 
 * Add a "systemctl list-units --by-slice" mode or so, which rearranges the
@@ -469,8 +469,6 @@ Features:
 
 * maybe add support for specifier expansion in user.conf, specifically DefaultEnvironment=
 
-* introduce systemd-timesync-wait.service or so to sync on an NTP fix?
-
 * consider showing the unit names during boot up in the status output, not just the unit descriptions
 
 * maybe allow timer units with an empty Units= setting, so that they
@@ -612,7 +610,6 @@ Features:
   - document chaining of signal handler for SIGCHLD and child handlers
   - define more intervals where we will shift wakeup intervals around in, 1h, 6h, 24h, ...
   - generate a failure of a default event loop is executed out-of-thread
-  - maybe add support for inotify events (which we can do safely now, with O_PATH)
 
 * investigate endianness issues of UUID vs. GUID
 
@@ -671,11 +668,9 @@ Features:
 
 * logind:
   - logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around
-  - When we update the kernel all kind of hibernation should be prohibited until shutdown/reboot
   - logind: wakelock/opportunistic suspend support
   - Add pretty name for seats in logind
   - logind: allow showing logout dialog from system?
-  - session scopes/user unit: add RequiresMountsFor for the home directory of the user
   - add Suspend() bus calls which take timestamps to fix double suspend issues when somebody hits suspend and closes laptop quickly.
   - if pam_systemd is invoked by su from a process that is outside of a
     any session we should probably just become a NOP, since that's
@@ -848,8 +843,6 @@ Features:
     "machinectl start" with a new --ephemeral switch
   - "machinectl status" should also show internal logs of the container in
     question
-  - "machinectl list-images" should show os-release data, as well as
-    machine-info data (including deployment level)
   - "machinectl history"
   - "machinectl diff"
   - "machinectl commit" that takes a writable snapshot of a tree, invokes a
@@ -1045,8 +1038,6 @@ External:
 
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
 
-* drop accountsservice's StandardOutput=syslog and Type=dbus fields
-
 * /usr/bin/service should actually show the new command line
 
 * fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus=
index f8a52469f76f31f4026fc9df7895c33d18dcaa9c..c5786c4012d4ae2c71cc7794f19b99f77dfdbc8a 100644 (file)
@@ -173,7 +173,7 @@ Support: %SUPPORT_URL%
 
 Працэс запуску юніта @UNIT@ завершаны.
 
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Юніт @UNIT@ спыняецца
@@ -196,7 +196,7 @@ Support: %SUPPORT_URL%
 
 Збой юніта @UNIT@.
 
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Юніт @UNIT@ перачытвае сваю канфігурацыю
@@ -212,7 +212,7 @@ Support: %SUPPORT_URL%
 
 Юніт @UNIT@ перачытаў сваю канфігурацыю.
 
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Працэс @EXECUTABLE@ не можа быць выкананы
index 035365b15aad42b5775fc657ad98e47fe6237926..bec5c6f0487a491331d15caa1fb2dad98cf26e73 100644 (file)
@@ -176,7 +176,7 @@ Support: %SUPPORT_URL%
 
 Praces zapusku junita @UNIT@ zavieršany.
 
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Junit @UNIT@ spyniajecca
@@ -199,7 +199,7 @@ Support: %SUPPORT_URL%
 
 Zboj junita @UNIT@.
 
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Junit @UNIT@ pieračytvaje svaju kanfihuracyju
@@ -215,7 +215,7 @@ Support: %SUPPORT_URL%
 
 Junit @UNIT@ pieračytaŭ svaju kanfihuracyju.
 
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Praces @EXECUTABLE@ nie moža być vykanany
index 64d616f38176b2eee0b91eed124fce5c94197a9e..41f7b21bce26a412ba75524610fd75b8f71c4b8e 100644 (file)
@@ -178,7 +178,7 @@ Support: %SUPPORT_URL%
 
 Стартирането на модул „@UNIT@“ завърши.
 
-Резултатът е: @RESULT@
+Резултатът е: @JOB_RESULT@
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Модул „@UNIT@“ се спира
@@ -201,7 +201,7 @@ Support: %SUPPORT_URL%
 
 Модулът „@UNIT@“ не успя да стартира.
 
-Резултатът е: @RESULT@
+Резултатът е: @JOB_RESULT@
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Модулът „@UNIT@“ започна презареждане на настройките си
@@ -217,7 +217,7 @@ Support: %SUPPORT_URL%
 
 Модулът „@UNIT@“ завърши презареждането на настройките си.
 
-Резултатът e: @RESULT@
+Резултатът e: @JOB_RESULT@
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Програмата „@EXECUTABLE@“ не успя да се стартира
index f1bddc6f7d9e8b74b402590b6c2b83f52bf85497..49a45890f6e8b8445f4262c8f19e1551828d274e 100644 (file)
@@ -52,7 +52,8 @@ dropped, other services' messages are unaffected.
 
 The limits controlling when messages are dropped may be configured
 with RateLimitIntervalSec= and RateLimitBurst= in
-/etc/systemd/journald.conf. See journald.conf(5) for details.
+/etc/systemd/journald.conf or LogRateLimitIntervalSec= and LogRateLimitBurst=
+in the unit file. See journald.conf(5) and systemd.exec(5) for details.
 
 -- e9bf28e6e834481bb6f48f548ad13606
 Subject: Journal messages have been missed
@@ -201,7 +202,7 @@ Support: %SUPPORT_URL%
 
 Unit @UNIT@ has finished starting up.
 
-The start-up result is @RESULT@.
+The start-up result is @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Unit @UNIT@ has begun shutting down
@@ -224,7 +225,7 @@ Support: %SUPPORT_URL%
 
 Unit @UNIT@ has failed.
 
-The result is @RESULT@.
+The result is @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Unit @UNIT@ has begun reloading its configuration
@@ -240,7 +241,7 @@ Support: %SUPPORT_URL%
 
 Unit @UNIT@ has finished reloading its configuration
 
-The result is @RESULT@.
+The result is @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Process @EXECUTABLE@ could not be executed
index 4e2bec8a0fe3fea578a45871625ba390707b67e9..aecfafa05f156a865241d2cf8af8038562dfed27 100644 (file)
@@ -159,7 +159,7 @@ Support: %SUPPORT_URL%
 
 Enhed @UNIT@ er færdig med at starte op.
 
-Resultat for opstart er @RESULT@.
+Resultat for opstart er @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Enhed @UNIT@ har påbegyndt nedlukning
@@ -182,7 +182,7 @@ Support: %SUPPORT_URL%
 
 Enhed @UNIT@ har fejlet.
 
-Resultatet er @RESULT@
+Resultatet er @JOB_RESULT@
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration
@@ -198,7 +198,7 @@ Support: %SUPPORT_URL%
 
 Enhed @UNIT@ er færdig med at genindlæse sin konfiguration
 
-Resultatet er: @RESULT@.
+Resultatet er: @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Process @EXECUTABLE@ kunne ikke eksekveres
index 156b1a37dc182e94968f646a52c8670c2ac7706f..13edd083cbacfb83c083826d4a6eed47aafaa8b6 100644 (file)
@@ -191,7 +191,7 @@ Subject: L'unité (unit) @UNIT@ a terminé son démarrage
 Defined-By: systemd
 Support: %SUPPORT_URL%
 
-L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @RESULT@.
+L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: L'unité (unit) @UNIT@ a commencé à s'arrêter
@@ -212,7 +212,7 @@ Subject: L'unité (unit) @UNIT@ a échoué
 Defined-By: systemd
 Support: %SUPPORT_URL%
 
-L'unité (unit) @UNIT@ a échoué, avec le résultat @RESULT@.
+L'unité (unit) @UNIT@ a échoué, avec le résultat @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: L'unité (unit) @UNIT@ a commencé à recharger sa configuration
@@ -227,7 +227,7 @@ Defined-By: systemd
 Support: %SUPPORT_URL%
 
 L'unité (unit) @UNIT@ a terminé de recharger configuration,
-avec le résultat @RESULT@.
+avec le résultat @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Le processus @EXECUTABLE@ n'a pas pu être exécuté
index c4808b4c7d71c5201ce75a7cd5c45ad68448d676..4526ae2a8ca52e6b3ab2b3fd29cc8aa72ba6c746 100644 (file)
@@ -173,7 +173,7 @@ Support: %SUPPORT_URL%
 
 Jedinica @UNIT@ je završila pokretanje.
 
-Rezultat pokretanja je @RESULT@.
+Rezultat pokretanja je @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Jedinica @UNIT@ je započela isključivanje
@@ -196,7 +196,7 @@ Support: %SUPPORT_URL%
 
 Jedinica @UNIT@ nije uspjela.
 
-Rezultat je @RESULT@.
+Rezultat je @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Jedinica @UNIT@ je započela ponovno učitavati podešavanja
@@ -212,7 +212,7 @@ Support: %SUPPORT_URL%
 
 Jedinica @UNIT@ je završila ponovno učitavati podešavanja
 
-Rezultat je @RESULT@.
+Rezultat je @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Proces @EXECUTABLE@ se ne može pokrenuti
index 6c6d7e793493860bdc1bd414daa2d05a4def3866..5565b80b2a10197a7dffd3be77cd1211d1b7b6f0 100644 (file)
@@ -161,7 +161,7 @@ Support: %SUPPORT_URL%
 
 A(z) @UNIT@ egység befejezte az indulást
 
-Az indítás eredménye: @RESULT@.
+Az indítás eredménye: @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: A(z) @UNIT@ egység megkezdte a leállást
@@ -184,7 +184,7 @@ Support: %SUPPORT_URL%
 
 A(z) @UNIT@ egység hibát jelzett.
 
-Az eredmény: @RESULT@.
+Az eredmény: @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: A(z) @UNIT@ egység megkezdte a beállításainak újratöltését
@@ -200,7 +200,7 @@ Support: %SUPPORT_URL%
 
 A(z) @UNIT@ egység befejezte a beállításainak újratöltését.
 
-Az eredmény: @RESULT@.
+Az eredmény: @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: A folyamat végrehajtása sikertelen: @EXECUTABLE@
index 4fd1f2a93366dd39c8051ab2646995b6eb0a7b53..8ce4fa5d9202328e7147c4ea8807a6e521101506 100644 (file)
@@ -191,7 +191,7 @@ Support: %SUPPORT_URL%
 
 L'unità @UNIT@ ha terminato la fase di avvio.
 
-La fase di avvio è @RESULT@.
+La fase di avvio è @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: L'unità @UNIT@ inizia la fase di spegnimento
@@ -214,7 +214,7 @@ Support: %SUPPORT_URL%
 
 L'unità @UNIT@ è fallita.
 
-Il risultato è @RESULT@.
+Il risultato è @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: L'unità @UNIT@ inizia a caricare la propria configurazione
@@ -230,7 +230,7 @@ Support: %SUPPORT_URL%
 
 L'unità @UNIT@ è terminata ricaricando la propria configurazione
 
-Il risultato è @RESULT@.
+Il risultato è @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Il processo @EXECUTABLE@ non può essere eseguito
index fc0faad02ce26a4364d15471941acd706c208ada..59fbde8b624f29aff0523b27e3d98feed446ee45 100644 (file)
@@ -182,7 +182,7 @@ Support: %SUPPORT_URL%
 
 @UNIT@ 유닛 시동을 마쳤습니다.
 
-시동 결과는 @RESULT@ 입니다.
+시동 결과는 @JOB_RESULT@ 입니다.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: @UNIT@ 유닛 끝내기 동작 시작
@@ -205,7 +205,7 @@ Support: %SUPPORT_URL%
 
 @UNIT@ 유닛 동작에 실패했습니다.
 
-결과는 @RESULT@ 입니다.
+결과는 @JOB_RESULT@ 입니다.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: @UNIT@ 유닛 설정 다시 읽기 시작
@@ -221,7 +221,7 @@ Support: %SUPPORT_URL%
 
 @UNIT@ 유닛의 설정 다시 읽기 동작을 끝냈습니다.
 
-결과는 @RESULT@ 입니다.
+결과는 @JOB_RESULT@ 입니다.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: @EXECUTABLE@ 프로세스 시작할 수 없음
index 998894bd0ae2bf05bc4dc1b4f59202abbcf6e71d..b73f56ca11966a34eb9012af8976886894b0a53c 100644 (file)
@@ -201,7 +201,7 @@ Support: %SUPPORT_URL%
 
 Jednostka @UNIT@ ukończyła uruchamianie.
 
-Wynik uruchamiania: @RESULT@.
+Wynik uruchamiania: @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Rozpoczęto wyłączanie jednostki @UNIT@
@@ -224,7 +224,7 @@ Support: %SUPPORT_URL%
 
 Jednostka @UNIT@ się nie powiodła.
 
-Wynik: @RESULT@.
+Wynik: @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Rozpoczęto ponowne wczytywanie konfiguracji jednostki @UNIT@
@@ -240,7 +240,7 @@ Support: %SUPPORT_URL%
 
 Jednostka @UNIT@ ukończyła ponowne wczytywanie swojej konfiguracji.
 
-Wynik: @RESULT@.
+Wynik: @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Nie można wykonać procesu @EXECUTABLE@
index db1cb03198ceb376d4463f730f9a7eefdcb20d06..edaefb71648142998e2d4f73c5679a7a32d970c5 100644 (file)
@@ -162,7 +162,7 @@ Support: %SUPPORT_URL%
 
 A unidade @UNIT@ concluiu a inicialização.
 
-The start-up result is @RESULT@.
+The start-up result is @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Unidade @UNIT@ sendo desligado
@@ -185,7 +185,7 @@ Support: %SUPPORT_URL%
 
 A unidade @UNIT@ falhou.
 
-O resultado é @RESULT@.
+O resultado é @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Unidade @UNIT@ iniciou recarregamento de sua configuração
@@ -201,7 +201,7 @@ Support: %SUPPORT_URL%
 
 A unidade @UNIT@ concluiu o recarregamento de sua configuração.
 
-O resultado é @RESULT@.
+O resultado é @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Processo @EXECUTABLE@ não pôde ser executado
index 645edaa922b76968fed070727ddd3973dbd95116..ccdc685037962e89a3414154d2e413bb58f20997 100644 (file)
@@ -227,7 +227,7 @@ Support: %SUPPORT_URL%
 
 Процесс запуска юнита @UNIT@ был завершен.
 
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
 
 # Subject: Unit @UNIT@ has begun shutting down
 -- de5b426a63be47a7b6ac3eaac82e2f6f
@@ -253,7 +253,7 @@ Support: %SUPPORT_URL%
 
 Произошел сбой юнита @UNIT@.
 
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
 
 # Subject: Unit @UNIT@ has begun with reloading its configuration
 -- d34d037fff1847e6ae669a370e694725
@@ -271,7 +271,7 @@ Support: %SUPPORT_URL%
 
 Юнит @UNIT@ завершил процесс перечитывания своей конфигурации.
 
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
 
 # Subject: Process @EXECUTABLE@ could not be executed
 -- 641257651c1b4ec9a8624d7a40a9e1e7
index f5746715a404b26f806da0c99deeca7d02e72d52..7cb6546d432316a06abb7169f02231217b2d9207 100644 (file)
@@ -158,7 +158,7 @@ Support: %SUPPORT_URL%
 
 Јединица @UNIT@ је завршила са покретањем.
 
-Исход покретања је @RESULT@.
+Исход покретања је @JOB_RESULT@.
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: Јединица @UNIT@ је почела са гашењем
@@ -181,7 +181,7 @@ Support: %SUPPORT_URL%
 
 Јединица @UNIT@ је пукла.
 
-Исход је @RESULT@.
+Исход је @JOB_RESULT@.
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: Јединица @UNIT@ је почела са поновним учитавањем свог подешавања
@@ -197,7 +197,7 @@ Support: %SUPPORT_URL%
 
 Јединица @UNIT@ је завршила са поновним учитавањем свог подешавања
 
-Исход је @RESULT@.
+Исход је @JOB_RESULT@.
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: Процес @EXECUTABLE@ није могао бити извршен
index fa58448acf96a2e1d44a52baa7a748cdb29d5512..d6ac2592b8d7116a738d3e70f8b6e13d705db277 100644 (file)
@@ -156,7 +156,7 @@ Support: %SUPPORT_URL%
 
 @UNIT@ 单元已结束启动。
 
-启动结果为“@RESULT@”。
+启动结果为“@JOB_RESULT@”。
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: @UNIT@ 单元已开始停止操作
@@ -179,7 +179,7 @@ Support: %SUPPORT_URL%
 
 @UNIT@ 单元已失败。
 
-结果为“@RESULT@”。
+结果为“@JOB_RESULT@”。
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: @UNIT@ 单元已开始重新载入其配置
@@ -195,7 +195,7 @@ Support: %SUPPORT_URL%
 
 @UNIT@ 单元已结束配置重载入操作。
 
-结果为“@RESULT@”。
+结果为“@JOB_RESULT@”。
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: 进程 @EXECUTABLE@ 无法执行
index 17bd2bc9af9fe535ac0ffc1245ea770142ac9e78..a468c2f6bf34852ab09862731ef8ac27b1830c04 100644 (file)
@@ -160,7 +160,7 @@ Support: %SUPPORT_URL%
 
 單位 @UNIT@ 啟動已結束。
 
-啟動結果為 @RESULT@。
+啟動結果為 @JOB_RESULT@。
 
 -- de5b426a63be47a7b6ac3eaac82e2f6f
 Subject: 單位 @UNIT@ 已開始關閉
@@ -183,7 +183,7 @@ Support: %SUPPORT_URL%
 
 單位 @UNIT@ 已失敗。
 
-結果為 @RESULT@。
+結果為 @JOB_RESULT@。
 
 -- d34d037fff1847e6ae669a370e694725
 Subject: 單位 @UNIT@ 已開始重新載入其設定
@@ -199,7 +199,7 @@ Support: %SUPPORT_URL%
 
 單位 @UNIT@ 已結束重新載入其設定
 
-結果為 @RESULT@。
+結果為 @JOB_RESULT@。
 
 -- 641257651c1b4ec9a8624d7a40a9e1e7
 Subject: 行程 @EXECUTABLE@ 無法執行
diff --git a/coccinelle/cmp.cocci b/coccinelle/cmp.cocci
new file mode 100644 (file)
index 0000000..a34cbe5
--- /dev/null
@@ -0,0 +1,28 @@
+@@
+expression x, y;
+@@
+- if (x < y)
+-         return -1;
+- if (x > y)
+-         return 1;
+- return 0;
++ return CMP(x, y);
+@@
+expression x, y;
+@@
+- if (x < y)
+-         return -1;
+- else if (x > y)
+-         return 1;
+- return 0;
++ return CMP(x, y);
+@@
+expression x, y;
+@@
+- if (x < y)
+-         return -1;
+- else if (x > y)
+-         return 1;
+- else
+-         return 0;
++ return CMP(x, y);
index 016a89787dd14503b107dac25720534e34762a74..654f7d25cff6e3a234b490f7008f19fe8b9e6269 100644 (file)
@@ -112,6 +112,12 @@ systemd-timedated:
   first existing unit listed in the environment variable, and
   `timedatectl set-ntp off` disables and stops all listed units.
 
+systemd-sulogin-shell:
+
+* `$SYSTEMD_SULOGIN_FORCE=1` — This skips asking for the root password if the
+  root password is not available (such as when the root account is locked).
+  See `sulogin(8)` for more details.
+
 bootctl and other tools that access the EFI System Partition (ESP):
 
 * `$SYSTEMD_RELAX_ESP_CHECKS=1` — if set, the ESP validation checks are
index 55397f46391b73a56c3fb088bbe4ec07039afecf..4b37a19455bf9d54182608cb9eca9ddc8799e6f8 100644 (file)
@@ -20,7 +20,7 @@ Portable services don't bring anything inherently new to the table. All they do
 is put together known concepts in a slightly nicer way to cover a specific set
 of use-cases in a nicer way.
 
-# So, what *is* a "Portable Service"?
+## So, what *is* a "Portable Service"?
 
 A portable service is ultimately just an OS tree, either inside of a directory
 tree, or inside a raw disk image containing a Linux file system. This tree is
@@ -43,7 +43,7 @@ do too.
 If you so will, "Portable Services" are a nicer way to manage chroot()
 environments, with better security, tooling and behavior.
 
-# Where's the difference to a "Container"?
+## Where's the difference to a "Container"?
 
 "Container" is a very vague term, after all it is used for
 systemd-nspawn/LXC-type OS containers, for Docker/rkt-like micro service
@@ -74,7 +74,7 @@ Note that portable services are only available for system services, not for
 user services. i.e. the functionality cannot be used for the stuff
 bubblewrap/flatpak is focusing on.
 
-# Mode of Operation
+## Mode of Operation
 
 If you have portable service image, maybe in a raw disk image called
 `foobar_0.7.23.raw`, then attaching the services to the host is as easy as:
@@ -141,7 +141,7 @@ Note that `portable attach` won't enable or start any of the units it copies
 out. This still has to take place in a second, separate step. (That said We
 might add options to do this automatically later on.).
 
-# Requirements on Images
+## Requirements on Images
 
 Note that portable services don't introduce any new image format, but most OS
 images should just work the way they are. Specifically, the following
@@ -208,14 +208,14 @@ image. To facility 3 and 4 you also need to include a boot loader in the
 image. As mentioned `mkosi -b` takes care of all of that for you, but any other
 image generator should work too.
 
-# Execution Environment
+## Execution Environment
 
 Note that the code in portable service images is run exactly like regular
 services. Hence there's no new execution environment to consider. Oh, unlike
 Docker would do it, as these are regular system services they aren't run as PID
 1 either, but with regular PID values.
 
-# Access to host resources
+## Access to host resources
 
 If services shipped with this mechanism shall be able to access host resources
 (such as files or AF_UNIX sockets for IPC), use the normal `BindPaths=` and
@@ -224,7 +224,7 @@ If services shipped with this mechanism shall be able to access host resources
 `/etc/resolv.conf`, the D-Bus system bus socket or write access to the logging
 subsystem are available to the service.
 
-# Instantiation
+## Instantiation
 
 Sometimes it makes sense to instantiate the same set of services multiple
 times. The portable service concept does not introduce a new logic for this. It
@@ -242,7 +242,7 @@ simple as:
 The benefit of this approach is that templating works exactly the same for
 units shipped with the OS itself as for attached portable services.
 
-# Immutable images with local data
+## Immutable images with local data
 
 It's a good idea to keep portable service images read-only during normal
 operation. In fact all but the `trusted` profile will default to this kind of
index 1b9a240e5c3ec8aa133c77f2e2c84d824d0cd4a0..efc8d039839e9ef267d9cc6337c2c0f216b8a2eb 100644 (file)
@@ -135,6 +135,8 @@ All execution-related settings are available for transient units.
 ✓ SyslogLevelPrefix=
 ✓ LogLevelMax=
 ✓ LogExtraFields=
+✓ LogRateLimitIntervalSec=
+✓ LogRateLimitBurst=
 ✓ SecureBits=
 ✓ CapabilityBoundingSet=
 ✓ AmbientCapabilities=
index 38c2487b90d665b4637906218d5205551be39293..9c4545308345fe2c40cd3d534eb13cda5a8db8ef 100644 (file)
@@ -14,7 +14,7 @@ Finally, it is able to compile the translations (to `*.gmo` files), so that
 they can be used by systemd software. (This step is also useful to confirm the
 syntax of the `*.po` files is correct.)
 
-# Creating a New Translation
+## Creating a New Translation
 
 To create a translation to a language not yet available, start by creating the
 initial template:
@@ -39,7 +39,7 @@ $ cp po/systemd.pot po/<i>${lang_code}</i>.po
 Then edit the new <code>po/<i>${lang_code}</i>.po</code> file (for example,
 using the `poedit` GUI editor.)
 
-# Updating an Existing Translation
+## Updating an Existing Translation
 
 Start by updating the `*.po` files from the latest template:
 
index 2f7efbeab578c8042531ea7908ee8ffd7589fe46..ee845eec80abdabf0500cabe3873c198ad78b487 100644 (file)
@@ -1 +1 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
+theme: jekyll-theme-primer
index 70e4ad62e9a3a43625f3736075d3d28759ad82be..dcabb18d3e6502606b7f8e840e386c96888b60c0 100644 (file)
 #
 # Sort by brand, model
 
+#########################################
+# AIPTEK
+#########################################
+
+# Hyperpen 12000U
+evdev:input:b0003v08CAp0010*
+ EVDEV_ABS_00=::20
+ EVDEV_ABS_01=::20
+
 #########################################
 # Apple
 #########################################
 
-#  Macbook2,1 (late 2006), single-button touchpad
+# Macbook2,1 (late 2006), single-button touchpad
 evdev:input:b0003v05ACp021B*
 # Macbook4,1
 evdev:input:b0003v05ACp0229*
@@ -257,7 +266,7 @@ evdev:name:SynPS/2 Synaptics TouchPad*:dmi:*svnHewlett-Packard:pnHPPaviliondm4*
  EVDEV_ABS_35=1360:5563:47
  EVDEV_ABS_36=1269:4618:61
 
-# HP Pavilion g6 
+# HP Pavilion g6
 evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnHewlett-Packard:pnHPPaviliong6*
  EVDEV_ABS_00=1284:5696:88
  EVDEV_ABS_01=1287:4838:39
index 816b618087425d8de4b6d5fd72b9687b8034ecef..3c9e604f7cc8ba61702b97f079286d075aa4c10b 100644 (file)
         <term><option>list</option></term>
 
         <listitem><para>Shows all available boot loader entries implementing the <ulink
-        url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+        url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
         Specification</ulink>, as well as any other entries discovered or automatically generated by the boot
         loader.</para></listitem>
       </varlistentry>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-      <ulink url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader Specification</ulink>,
+      <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
       <ulink url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader Interface</ulink>
     </para>
   </refsect1>
index 927cd6da829405db34232e78cfd4e8b4ac9799cc..ed874aace919592b896890b0fa186a92ed0006e9 100644 (file)
         following units: <literal>s</literal>, <literal>min</literal>,
         <literal>h</literal>, <literal>ms</literal>,
         <literal>us</literal>. To turn off any kind of rate limiting,
-        set either value to 0.</para></listitem>
+        set either value to 0.</para>
+
+        <para>If a service provides rate limits for itself through
+        <varname>LogRateLimitIntervalSec=</varname> and/or <varname>LogRateLimitBurst=</varname>
+        in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+        those values will override the settings specified here.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
index 522d5fb54d4bd7891fd8756724b302c3f66691c3..884b25da9b4bc5b135b368c36beaf61f207dd0ee 100644 (file)
     <para>
       <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <ulink url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader Specification</ulink>
+      <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>
     </para>
   </refsect1>
 
index d3551a1bc2548f956ba0b02f13fad6a4d756cc30..a40785895781501ca8743a4c511e25a2560e7d43 100644 (file)
         5.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>UserStopDelaySec=</varname></term>
+
+        <listitem><para>Specifies how long to keep the user record and per-user service
+        <filename>user@.service</filename> around for a user after they logged out fully. If set to zero, the per-user
+        service is terminated immediately when the last session of the user has ended. If this option is configured to
+        non-zero rapid logout/login cycles are sped up, as the user's service manager is not constantly restarted. If
+        set to <literal>infinity</literal> the per-user service for a user is never terminated again after first login,
+        and continues to run until system shutdown. Defaults to 10s.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>HandlePowerKey=</varname></term>
         <term><varname>HandleSuspendKey=</varname></term>
index 303b584654977d01609b089b2106081cfb0996cd..3602bbaa1a820a040b47e2f937fb2ebe7434c3fc 100644 (file)
@@ -180,6 +180,7 @@ manpages = [
    'sd_bus_error_get_errno',
    'sd_bus_error_has_name',
    'sd_bus_error_is_set',
+   'sd_bus_error_move',
    'sd_bus_error_set',
    'sd_bus_error_set_const',
    'sd_bus_error_set_errno',
index 807ca86302dce197d234aa9173b9c64a91d7e2a4..36cdf4c338de7a5b5ff7b823b8a7ca1ff3fc6c61 100644 (file)
@@ -31,6 +31,7 @@
     <refname>sd_bus_error_set_errnofv</refname>
     <refname>sd_bus_error_get_errno</refname>
     <refname>sd_bus_error_copy</refname>
+    <refname>sd_bus_error_move</refname>
     <refname>sd_bus_error_is_set</refname>
     <refname>sd_bus_error_has_name</refname>
 
         <paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_error_move</function></funcdef>
+        <paramdef>sd_bus_error *<parameter>dst</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_error_is_set</function></funcdef>
         <paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
     should have both fields initialized to NULL. Set an error
     structure to <constant>SD_BUS_ERROR_NULL</constant> in order to
     reset both fields to NULL. When no longer necessary, resources
-    held by the <structname>sd_bus_error</structname>structure should
+    held by the <structname>sd_bus_error</structname> structure should
     be destroyed with <function>sd_bus_error_free()</function>.</para>
 
     <para><function>sd_bus_error_set()</function> sets an error
     Otherwise, they will be copied. Returns a converted
     <varname>errno</varname>-like, negative error code.</para>
 
+    <para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>, but will
+    move any error information from <parameter>e</parameter> into <parameter>dst</parameter>, resetting the
+    former. This function cannot fail, as no new memory is allocated. Note that if <parameter>e</parameter> is not set
+    (or <constant>NULL</constant>) <parameter>dst</parameter> is initializated to
+    <constant>SD_BUS_ERROR_NULL</constant>. Moreover, if <parameter>dst</parameter> is <constant>NULL</constant> no
+    operation is executed on it and and resources held by <parameter>e</parameter> are freed and reset. Returns a
+    converted <varname>errno</varname>-like, negative error code.</para>
+
     <para><function>sd_bus_error_is_set()</function> will return a
     non-zero value if <parameter>e</parameter> is
     non-<constant>NULL</constant> and an error has been set,
     <constant>NULL</constant>, and a positive errno value mapped from
     <parameter>e-&gt;name</parameter> otherwise.</para>
 
-    <para><function>sd_bus_error_copy()</function> returns 0 or a
-    positive integer on success, and a negative error value converted
-    from the error name otherwise.</para>
+    <para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return 0 or a positive
+    integer on success, and a negative error value converted from the error name otherwise.</para>
 
     <para><function>sd_bus_error_is_set()</function> returns a
     non-zero value when <parameter>e</parameter> and the
index 6abed7693a8a932f2ce6d05f5e6022c779cdd6de..c3b34e54c937bb5e82cb9a8fdd4de5ce2ee864f6 100644 (file)
 
     <itemizedlist>
       <listitem><para>Boot entries defined with <ulink
-      url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+      url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
       Specification</ulink> description files located in <filename>/loader/entries/</filename> on the ESP. These
       usually describe Linux kernel images with associated initrd images, but alternatively may also describe
       arbitrary other EFI executables.</para></listitem>
 
       <listitem><para>Unified kernel images following the <ulink
-      url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+      url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
       Specification</ulink>, as executable EFI binaries in <filename>/EFI/Linux/</filename> on the ESP.
       </para></listitem>
 
     <filename>/loader/loader.conf</filename> on the ESP (in combination with data read from EFI variables). See
     <citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Boot entry
     description files following the <ulink
-    url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+    url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
     Specification</ulink> are read from <filename>/loader/entries/</filename> on the ESP. Unified kernel boot entries
-    following the <ulink url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot
+    following the <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
     Loader Specification</ulink> are read from <filename>/EFI/Linux/</filename> on the ESP.</para>
   </refsect1>
 
     <para>
       <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <ulink url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader Specification</ulink>,
+      <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
       <ulink url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader Interface</ulink>
     </para>
   </refsect1>
index 06e9ce7837d587ef8f01c0a0c76f9f0b1ba0909d..8c5e4cc5a1ad271f1c61fa29179c2c6c5b87ed09 100644 (file)
@@ -36,7 +36,7 @@
     <para>Most of <command>systemd-portabled</command>'s functionality is accessible through the
     <citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
 
-    <para>See the <ulink url="https://github.com/systemd/systemd/blob/master/docs/PORTABLE_SERVICES.md">Portable
+    <para>See the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable
     Services Documentation</ulink> for details about the concepts this service implements.</para>
   </refsect1>
 
index d48dbc02b60a936c9fcb2e7ae1c581228aeb50e0..1869465a3b8b158bd7ffdafb58c69c850c2f6a5c 100644 (file)
@@ -52,7 +52,7 @@
     <citerefentry project='die-net'><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
     The main differences for <command>systemd-socket-proxyd</command>
     are support for socket activation with
-    <literal>Accept=false</literal> and an event-driven
+    <literal>Accept=no</literal> and an event-driven
     design that scales better with the number of
     connections.</para>
   </refsect1>
index 7da2c55d2735a08a0549b66cc4ba79e47bf2a14d..6c9b921f05ad195a9772c8904534a3b38467f197 100644 (file)
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <ulink url="https://systemd.io/UIDS-GIDS">Users, Groups, UIDs and GIDs on systemd systems</ulink>
     </para>
   </refsect1>
 
index d763cb9e82f342e5fc0511c4d0e4b7637fdf545d..5c043497bbe1da7fb90867df9f4209330d3e1048 100644 (file)
@@ -1947,6 +1947,22 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         matching. Assign an empty string to reset the list.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>LogRateLimitIntervalSec=</varname></term>
+        <term><varname>LogRateLimitBurst=</varname></term>
+
+        <listitem><para>Configures the rate limiting that is applied to messages generated by this unit. If, in the
+        time interval defined by <varname>LogRateLimitIntervalSec=</varname>, more messages than specified in
+        <varname>LogRateLimitBurst=</varname> are logged by a service, all further messages within the interval are
+        dropped until the interval is over. A message about the number of dropped messages is generated. The time
+        specification for <varname>LogRateLimitIntervalSec=</varname> may be specified in the following units: "s",
+        "min", "h", "ms", "us" (see
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details).
+        The default settings are set by <varname>RateLimitIntervalSec=</varname> and <varname>RateLimitBurst=</varname>
+        configured in <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>SyslogIdentifier=</varname></term>
 
index 380d0088ce1c7e0bebcd28dec48dd3cd35a06fd4..971d81aa42b6cff71cbc785ed1e07f1ddfc48e5a 100644 (file)
@@ -1470,10 +1470,10 @@ Name=ipip-tun
 Kind=ipip
 
 [Tunnel]
-Independent=true
+Independent=yes
 Local=10.65.208.212
 Remote=10.65.208.211
-FooOverUDP=true
+FooOverUDP=yes
 FOUDestinationPort=5555
       </programlisting>
     </example>
@@ -1484,8 +1484,8 @@ Name=tap-test
 Kind=tap
 
 [Tap]
-MultiQueue=true
-PacketInfo=true</programlisting> </example>
+MultiQueue=yes
+PacketInfo=yes</programlisting> </example>
 
     <example>
       <title>/etc/systemd/network/25-sit.netdev</title>
index 500467b67e534ba5701b8c139b92c2cbf0463bb1..cc33f8331e97cb96bb227a65ae67e16f10378636 100644 (file)
           <listitem><para>An IPv6 address, for which Neighbour Advertisement messages will be
           proxied. This option may be specified more than once. systemd-networkd will add the
           <option>IPv6ProxyNDPAddress=</option> entries to the kernel's IPv6 neighbor proxy table.
-          This option implies <option>IPv6ProxyNDP=true</option> but has no effect if
+          This option implies <option>IPv6ProxyNDP=yes</option> but has no effect if
           <option>IPv6ProxyNDP</option> has been set to false. Defaults to unset.
         </para></listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>Protocol=</varname></term>
           <listitem>
-            <para>The Protocol identifier for the route. Takes a number between 0 and 255 or the special values
+            <para>The protocol identifier for the route. Takes a number between 0 and 255 or the special values
             <literal>kernel</literal>, <literal>boot</literal> and <literal>static</literal>. Defaults to
             <literal>static</literal>.
             </para>
         <varlistentry>
           <term><varname>Type=</varname></term>
           <listitem>
-            <para>The Type identifier for special route types, which can be
-            <literal>unicast</literal> route to a destination network address which describes the path to the destination,
-            <literal>blackhole</literal> packets are discarded silently,
-            <literal>unreachable</literal> packets are discarded and the ICMP message host unreachable is generated,
-            <literal>prohibit</literal> packets are discarded and the ICMP message communication administratively
-            prohibited is generated. Defaults to <literal>unicast</literal>.
+            <para>Specifies the type for the route. If <literal>unicast</literal>, a regular route is defined, i.e. a
+            route indicating the path to take to a destination network address. If <literal>blackhole</literal>, packets
+            to the defined route are discarded silently. If <literal>unreachable</literal>, packets to the defined route
+            are discarded and the ICMP message "Host Unreachable" is generated. If <literal>prohibit</literal>, packets
+            to the defined route are discarded and the ICMP message "Communication Administratively Prohibited" is
+            generated. If <literal>throw</literal>, route lookup in the current routing table will fail and the route
+            selection process will return to Routing Policy Database (RPDB). Defaults to <literal>unicast</literal>.
             </para>
           </listitem>
         </varlistentry>
index 113d74a220931ee91307379d5f079c65cbff195b..13fdfc28defac77e20d959b68a57f95a76f05a29 100644 (file)
       </listitem>
 
       <listitem>
-        <para>The update service should declare <varname>DefaultDependencies=false</varname>,
+        <para>The update service should declare <varname>DefaultDependencies=no</varname>,
         <varname>Requires=sysinit.target</varname>, <varname>After=sysinit.target</varname>,
         <varname>After=system-update-pre.target</varname>
         and explicitly pull in any other services it requires.</para>
index 6d5778aa72fd61c90607266462c818cde3ea74d9..08ae2d049ad79bf122f8efb7cd8ef56588dfb824 100644 (file)
           control group attribute, see <ulink
           url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
 
-          <para>Implies <literal>IOAccounting=true</literal>.</para>
+          <para>Implies <literal>IOAccounting=yes</literal>.</para>
 
           <para>These settings are supported only if the unified control group hierarchy is used.</para>
         </listitem>
           the startup phase. Using <varname>StartupCPUShares=</varname> allows prioritizing specific services at
           boot-up differently than during normal runtime.</para>
 
-          <para>Implies <literal>CPUAccounting=true</literal>.</para>
+          <para>Implies <literal>CPUAccounting=yes</literal>.</para>
 
           <para>These settings are deprecated. Use <varname>CPUWeight=</varname> and
           <varname>StartupCPUWeight=</varname> instead.</para>
           attribute, see <ulink
           url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
 
-          <para>Implies <literal>MemoryAccounting=true</literal>.</para>
+          <para>Implies <literal>MemoryAccounting=yes</literal>.</para>
 
           <para>This setting is deprecated. Use <varname>MemoryMax=</varname> instead.</para>
         </listitem>
         boot-up differently than during runtime.</para>
 
         <para>Implies
-        <literal>BlockIOAccounting=true</literal>.</para>
+        <literal>BlockIOAccounting=yes</literal>.</para>
 
         <para>These settings are deprecated. Use <varname>IOWeight=</varname> and <varname>StartupIOWeight=</varname>
         instead.</para>
           url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.</para>
 
           <para>Implies
-          <literal>BlockIOAccounting=true</literal>.</para>
+          <literal>BlockIOAccounting=yes</literal>.</para>
 
           <para>This setting is deprecated. Use <varname>IODeviceWeight=</varname> instead.</para>
         </listitem>
           </para>
 
           <para>Implies
-          <literal>BlockIOAccounting=true</literal>.</para>
+          <literal>BlockIOAccounting=yes</literal>.</para>
 
           <para>These settings are deprecated. Use <varname>IOReadBandwidthMax=</varname> and
           <varname>IOWriteBandwidthMax=</varname> instead.</para>
index 4671c71bc85c334b57360b0a67e91d0bcc36465f..72807be7b6904825f288eed13bbc8e4792f79d7d 100644 (file)
@@ -68,8 +68,8 @@
     or it must be a template unit named the same way. Example: a
     socket file <filename>foo.socket</filename> needs a matching
     service <filename>foo.service</filename> if
-    <option>Accept=false</option> is set. If
-    <option>Accept=true</option> is set, a service template file
+    <option>Accept=no</option> is set. If
+    <option>Accept=yes</option> is set, a service template file
     <filename>foo@.service</filename> must exist from which services
     are instantiated for each incoming connection.</para>
 
         incoming traffic. Defaults to <option>false</option>. For
         performance reasons, it is recommended to write new daemons
         only in a way that is suitable for
-        <option>Accept=false</option>. A daemon listening on an
+        <option>Accept=no</option>. A daemon listening on an
         <constant>AF_UNIX</constant> socket may, but does not need to,
         call
         <citerefentry><refentrytitle>close</refentrytitle><manvolnum>2</manvolnum></citerefentry>
         on the received socket before exiting. However, it must not
         unlink the socket from a file system. It should not invoke
         <citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        on sockets it got with <varname>Accept=false</varname>, but it
+        on sockets it got with <varname>Accept=no</varname>, but it
         may do so for sockets it got with
-        <varname>Accept=true</varname> set. Setting
-        <varname>Accept=true</varname> is mostly useful to allow
+        <varname>Accept=yes</varname> set. Setting
+        <varname>Accept=yes</varname> is mostly useful to allow
         daemons designed for usage with
         <citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         to work unmodified with systemd socket
         <term><varname>MaxConnections=</varname></term>
         <listitem><para>The maximum number of connections to
         simultaneously run services instances for, when
-        <option>Accept=true</option> is set. If more concurrent
+        <option>Accept=yes</option> is set. If more concurrent
         connections are coming in, they will be refused until at least
         one existing connection is terminated. This setting has no
         effect on sockets configured with
-        <option>Accept=false</option> or datagram sockets. Defaults to
+        <option>Accept=no</option> or datagram sockets. Defaults to
         64.</para></listitem>
       </varlistentry>
 
index f418f5b5eec4747deca5add05cd063f1f0d91e40..467b905f14e8ceb11966151de408208957258c8d 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>FailureAction=</varname></term>
+        <term><varname>SuccessAction=</varname></term>
+
+        <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive state.
+        Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
+        <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option>,
+        <option>poweroff-immediate</option>, <option>exit</option>, and <option>exit-force</option>. In system mode,
+        all options are allowed. In user mode, only <option>none</option>, <option>exit</option>, and
+        <option>exit-force</option> are allowed. Both options default to <option>none</option>.</para>
+
+        <para>If <option>none</option> is set, no action will be triggered. <option>reboot</option> causes a reboot
+        following the normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
+        <option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
+        cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
+        <option>reboot-immediate</option> causes immediate execution of the
+        <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
+        might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
+        <option>poweroff-immediate</option> have the effect of powering down the system with similar
+        semantics. <option>exit</option> causes the manager to exit following the normal shutdown procedure, and
+        <option>exit-force</option> causes it terminate without shutting down services.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>JobTimeoutSec=</varname></term>
         <term><varname>JobRunningTimeoutSec=</varname></term>
       <varlistentry>
         <term><varname>StartLimitAction=</varname></term>
 
-        <listitem><para>Configure the action to take if the rate limit configured with
-        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes one of
-        <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
-        <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option> or
-        <option>poweroff-immediate</option>. If <option>none</option> is set, hitting the rate limit will trigger no
-        action besides that the start will not be permitted. <option>reboot</option> causes a reboot following the
-        normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
-        <option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
-        cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
-        <option>reboot-immediate</option> causes immediate execution of the
-        <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
-        might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
-        <option>poweroff-immediate</option> have the effect of powering down the system with similar
-        semantics. Defaults to <option>none</option>.</para></listitem>
+        <listitem><para>Configure an additional action to take if the rate limit configured with
+        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit.  Takes the same
+        values as the setting <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings and executes
+        the same actions. If <option>none</option> is set, hitting the rate limit will trigger no action besides that
+        the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><varname>FailureAction=</varname></term>
-        <term><varname>SuccessAction=</varname></term>
-        <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive
-        state. Takes the same values as the setting <varname>StartLimitAction=</varname> setting and executes the same
-        actions. Both options default to <option>none</option>.</para></listitem>
-      </varlistentry>
 
       <varlistentry>
         <term><varname>RebootArgument=</varname></term>
index f12956a8a53fe27e38dbaae183841b154a1c7d6b..77aade81587cac5fe18aa27c6c7b728e4d62d19c 100644 (file)
         for more information.</para></listitem>
       </varlistentry>
     </variablelist>
+
+    <para>For further environment variables understood by systemd and its various components, see <ulink
+    url="https://systemd.io/ENVIRONMENT">Known Environment Variables</ulink>.</para>
   </refsect1>
 
   <refsect1>
         <term><varname>quiet</varname></term>
 
         <listitem><para>Turn off status output at boot, much like
-        <varname>systemd.show_status=false</varname> would. Note that
+        <varname>systemd.show_status=no</varname> would. Note that
         this option is also read by the kernel itself and disables
         kernel log output. Passing this option hence turns off the
         usual output from both the system manager and the kernel.
index 39c427cd26e2df2d8f6ab9e5145c87c9665dd99a..a47d7f9370a3c00c906bf1b6833d09b6668b7fe3 100644 (file)
@@ -73,6 +73,10 @@ sysvrcnd_path = get_option('sysvrcnd-path')
 conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '',
            description : 'SysV init scripts and rcN.d links are supported')
 
+conf.set10('BUMP_PROC_SYS_FS_FILE_MAX', get_option('bump-proc-sys-fs-file-max'))
+conf.set10('BUMP_PROC_SYS_FS_NR_OPEN',  get_option('bump-proc-sys-fs-nr-open'))
+conf.set('HIGH_RLIMIT_NOFILE',          256*1024)
+
 # join_paths ignore the preceding arguments if an absolute component is
 # encountered, so this should canonicalize various paths when they are
 # absolute or relative.
@@ -227,7 +231,7 @@ conf.set_quoted('SYSTEMD_EXPORT_PATH',                        join_paths(rootlib
 conf.set_quoted('VENDOR_KEYRING_PATH',                        join_paths(rootlibexecdir, 'import-pubring.gpg'))
 conf.set_quoted('USER_KEYRING_PATH',                          join_paths(pkgsysconfdir, 'import-pubring.gpg'))
 conf.set_quoted('DOCUMENT_ROOT',                              join_paths(pkgdatadir, 'gatewayd'))
-conf.set('MEMORY_ACCOUNTING_DEFAULT',                         memory_accounting_default ? 'true' : 'false')
+conf.set10('MEMORY_ACCOUNTING_DEFAULT',                       memory_accounting_default)
 conf.set_quoted('MEMORY_ACCOUNTING_DEFAULT_YES_NO',           memory_accounting_default ? 'yes' : 'no')
 
 substs.set('prefix',                                          prefixdir)
@@ -269,6 +273,7 @@ substs.set('SYSTEM_SYSVRCND_PATH',                            sysvrcnd_path)
 substs.set('RC_LOCAL_SCRIPT_PATH_START',                      get_option('rc-local'))
 substs.set('RC_LOCAL_SCRIPT_PATH_STOP',                       get_option('halt-local'))
 substs.set('MEMORY_ACCOUNTING_DEFAULT',                       memory_accounting_default ? 'yes' : 'no')
+substs.set('HIGH_RLIMIT_NOFILE',                              conf.get('HIGH_RLIMIT_NOFILE'))
 
 #####################################################################
 
@@ -1762,15 +1767,15 @@ if conf.get('ENABLE_LOGIND') == 1
                              args : pam_systemd.full_path())
                 endif
         endif
-endif
 
-executable('systemd-user-runtime-dir',
-           user_runtime_dir_sources,
-           include_directories : includes,
-           link_with : [libshared, liblogind_core],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+        executable('systemd-user-runtime-dir',
+                   user_runtime_dir_sources,
+                   include_directories : includes,
+                   link_with : [libshared],
+                   install_rpath : rootlibexecdir,
+                   install : true,
+                   install_dir : rootlibexecdir)
+endif
 
 if conf.get('HAVE_PAM') == 1
         executable('systemd-user-sessions',
index 83ade5bea41fc7484c1e79fda891f6b3335f460a..b5a20fb0e21a0592c1a9e60870e3f83f9cfa8d9b 100644 (file)
@@ -49,6 +49,10 @@ option('debug-extra', type : 'array', choices : ['hashmap', 'mmap-cache'], value
        description : 'enable extra debugging')
 option('memory-accounting-default', type : 'boolean',
        description : 'enable MemoryAccounting= by default')
+option('bump-proc-sys-fs-file-max', type : 'boolean',
+       description : 'bump /proc/sys/fs/file-max to ULONG_MAX')
+option('bump-proc-sys-fs-nr-open', type : 'boolean',
+       description : 'bump /proc/sys/fs/nr_open to INT_MAX')
 option('valgrind', type : 'boolean', value : false,
        description : 'do extra operations to avoid valgrind warnings')
 option('log-trace', type : 'boolean', value : false,
index b7b8c50a2b7425c90875c859063526129a74ce1d..114e617c8586e57d8a200d7a0f7ab63584af8c71 100755 (executable)
@@ -90,3 +90,8 @@ cat > "$DESTDIR"/etc/issue <<EOF
 Kernel \r on an \m (\l)
 
 EOF
+
+# Manually update the boot loader from the one we just built
+mkdir -p "$DESTDIR"/boot/efi/EFI/systemd "$DESTDIR"/boot/efi/EFI/BOOT
+cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/boot/efi/EFI/systemd/systemd-bootx64.efi
+cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/boot/efi/EFI/BOOT/bootx64.efi
index b6df6b9c2251f7c39a9c599da42f4797504c3b2d..1d8880ef0290137e29f9b416c347e8a08505ecf9 100644 (file)
@@ -22,11 +22,13 @@ KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*"
 KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
 KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{wwid}=="?*", ENV{ID_WWN}="$attr{wwid}"
 KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
 KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
   ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}"
 
 KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
 KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
 KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
   ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n"
 
index ebe42889ea9f6d1918ed12fc886d0433dfab6326..2a6deb12ca0936b3ef996b2855ff75b553135e64 100644 (file)
@@ -46,6 +46,21 @@ static inline void *mfree(void *memory) {
 void* memdup(const void *p, size_t l) _alloc_(2);
 void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
 
+#define memdupa(p, l)                           \
+        ({                                      \
+                void *_q_;                      \
+                _q_ = alloca(l);                \
+                memcpy(_q_, p, l);              \
+        })
+
+#define memdupa_suffix0(p, l)                   \
+        ({                                      \
+                void *_q_;                      \
+                _q_ = alloca(l + 1);            \
+                ((uint8_t*) _q_)[l] = 0;        \
+                memcpy(_q_, p, l);              \
+        })
+
 static inline void freep(void *p) {
         free(*(void**) p);
 }
index 1c9c5fd162b9ce890465a61fc5dd37add1889617..89800c5d6114ec81f0462eec1beb3dc4ab2a849d 100644 (file)
@@ -404,26 +404,21 @@ static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, co
 }
 
 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
+        int r;
+
         assert(args);
 
         /* Compare min and max */
 
-        if (args->key.min_objectid < args->key.max_objectid)
-                return -1;
-        if (args->key.min_objectid > args->key.max_objectid)
-                return 1;
-
-        if (args->key.min_type < args->key.max_type)
-                return -1;
-        if (args->key.min_type > args->key.max_type)
-                return 1;
+        r = CMP(args->key.min_objectid, args->key.max_objectid);
+        if (r != 0)
+                return r;
 
-        if (args->key.min_offset < args->key.max_offset)
-                return -1;
-        if (args->key.min_offset > args->key.max_offset)
-                return 1;
+        r = CMP(args->key.min_type, args->key.max_type);
+        if (r != 0)
+                return r;
 
-        return 0;
+        return CMP(args->key.min_offset, args->key.max_offset);
 }
 
 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
index 7728b63a5f2999250f49a5da3030a0ca0da926f3..b725a1776138909465208804bd1372267627279f 100644 (file)
@@ -1166,7 +1166,7 @@ int cg_is_empty(const char *controller, const char *path) {
 
         r = cg_enumerate_processes(controller, path, &f);
         if (r == -ENOENT)
-                return 1;
+                return true;
         if (r < 0)
                 return r;
 
@@ -1196,6 +1196,8 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
                  * via the "populated" attribute of "cgroup.events". */
 
                 r = cg_read_event(controller, path, "populated", &t);
+                if (r == -ENOENT)
+                        return true;
                 if (r < 0)
                         return r;
 
@@ -1210,7 +1212,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
 
                 r = cg_enumerate_subgroups(controller, path, &d);
                 if (r == -ENOENT)
-                        return 1;
+                        return true;
                 if (r < 0)
                         return r;
 
index 4d515c11b6ac12066c1b81c523b817c0d1e7a043..005cd8d090e6d71ca4ed84cff4c724b8d2e6b639 100644 (file)
@@ -75,3 +75,5 @@
                 _CONF_PATHS_SPLIT_USR(n))
 
 #define LONG_LINE_MAX (1U*1024U*1024U)
+
+#define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL)
index aa26902a20cb50d7ce6d5edb2586df2b50835c7a..32c6418482278f4ea9d6d74cb45f7f6d4c243d52 100644 (file)
@@ -268,8 +268,8 @@ static int gather_environment_generate(int fd, void *arg) {
 }
 
 static int gather_environment_collect(int fd, void *arg) {
-        char ***env = arg;
         _cleanup_fclose_ FILE *f = NULL;
+        char ***env = arg;
         int r;
 
         /* Write out a series of env=cescape(VAR=value) assignments to fd. */
@@ -286,8 +286,9 @@ static int gather_environment_collect(int fd, void *arg) {
         if (r < 0)
                 return r;
 
-        if (ferror(f))
-                return errno > 0 ? -errno : -EIO;
+        r = fflush_and_check(f);
+        if (r < 0)
+                return r;
 
         return 0;
 }
index 5c99c398c2d6ed8ed3a81834ce8ec33a5f0ec91c..10e15c9d709de2c90238f5fe1dc1516a9f0093e8 100644 (file)
@@ -688,32 +688,16 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                         return 0;
 
                 case TABLE_TIMESTAMP:
-                        if (a->timestamp < b->timestamp)
-                                return -1;
-                        if (a->timestamp > b->timestamp)
-                                return 1;
-                        return 0;
+                        return CMP(a->timestamp, b->timestamp);
 
                 case TABLE_TIMESPAN:
-                        if (a->timespan < b->timespan)
-                                return -1;
-                        if (a->timespan > b->timespan)
-                                return 1;
-                        return 0;
+                        return CMP(a->timespan, b->timespan);
 
                 case TABLE_SIZE:
-                        if (a->size < b->size)
-                                return -1;
-                        if (a->size > b->size)
-                                return 1;
-                        return 0;
+                        return CMP(a->size, b->size);
 
                 case TABLE_UINT32:
-                        if (a->uint32 < b->uint32)
-                                return -1;
-                        if (a->uint32 > b->uint32)
-                                return 1;
-                        return 0;
+                        return CMP(a->uint32, b->uint32);
 
                 default:
                         ;
@@ -721,12 +705,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
         }
 
         /* Generic fallback using the orginal order in which the cells where added. */
-        if (index_a < index_b)
-                return -1;
-        if (index_a > index_b)
-                return 1;
-
-        return 0;
+        return CMP(index_a, index_b);
 }
 
 static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
index 018ef5e7d809dd278284178974e07b477e6a47ef..eba56add1fc76a3e013f160a61609031c4006b1d 100644 (file)
@@ -1543,31 +1543,9 @@ static unsigned find_first_entry(HashmapBase *h) {
         return hashmap_iterate_entry(h, &i);
 }
 
-void *internal_hashmap_first(HashmapBase *h) {
-        unsigned idx;
-
-        idx = find_first_entry(h);
-        if (idx == IDX_NIL)
-                return NULL;
-
-        return entry_value(h, bucket_at(h, idx));
-}
-
-void *internal_hashmap_first_key(HashmapBase *h) {
-        struct hashmap_base_entry *e;
-        unsigned idx;
-
-        idx = find_first_entry(h);
-        if (idx == IDX_NIL)
-                return NULL;
-
-        e = bucket_at(h, idx);
-        return (void*) e->key;
-}
-
-void *internal_hashmap_steal_first(HashmapBase *h) {
+void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
         struct hashmap_base_entry *e;
-        void *data;
+        void *key, *data;
         unsigned idx;
 
         idx = find_first_entry(h);
@@ -1575,26 +1553,16 @@ void *internal_hashmap_steal_first(HashmapBase *h) {
                 return NULL;
 
         e = bucket_at(h, idx);
+        key = (void*) e->key;
         data = entry_value(h, e);
-        remove_entry(h, idx);
 
-        return data;
-}
+        if (remove)
+                remove_entry(h, idx);
 
-void *internal_hashmap_steal_first_key(HashmapBase *h) {
-        struct hashmap_base_entry *e;
-        void *key;
-        unsigned idx;
+        if (ret_key)
+                *ret_key = key;
 
-        idx = find_first_entry(h);
-        if (idx == IDX_NIL)
-                return NULL;
-
-        e = bucket_at(h, idx);
-        key = (void*) e->key;
-        remove_entry(h, idx);
-
-        return key;
+        return data;
 }
 
 unsigned internal_hashmap_size(HashmapBase *h) {
index b771ceccdc6cec9f70c7889bdf3fefb8d96c1d20..bb2a5c76ec3e60a6e63bba282f333556a298d77d 100644 (file)
@@ -290,36 +290,51 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
  * the first entry is O(1).
  */
 
-void *internal_hashmap_steal_first(HashmapBase *h);
+void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
+static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+}
+static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+}
+static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+}
+static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+}
+
+
 static inline void *hashmap_steal_first(Hashmap *h) {
-        return internal_hashmap_steal_first(HASHMAP_BASE(h));
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
 }
 static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
-        return internal_hashmap_steal_first(HASHMAP_BASE(h));
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
+}
+static inline void *hashmap_first(Hashmap *h) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
+}
+static inline void *ordered_hashmap_first(OrderedHashmap *h) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
 }
 
-void *internal_hashmap_steal_first_key(HashmapBase *h);
+static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) {
+        void *key = NULL;
+
+        (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
+        return key;
+}
 static inline void *hashmap_steal_first_key(Hashmap *h) {
-        return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+        return internal_hashmap_first_key(HASHMAP_BASE(h), true);
 }
 static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
-        return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+        return internal_hashmap_first_key(HASHMAP_BASE(h), true);
 }
-
-void *internal_hashmap_first_key(HashmapBase *h) _pure_;
 static inline void *hashmap_first_key(Hashmap *h) {
-        return internal_hashmap_first_key(HASHMAP_BASE(h));
+        return internal_hashmap_first_key(HASHMAP_BASE(h), false);
 }
 static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
-        return internal_hashmap_first_key(HASHMAP_BASE(h));
-}
-
-void *internal_hashmap_first(HashmapBase *h) _pure_;
-static inline void *hashmap_first(Hashmap *h) {
-        return internal_hashmap_first(HASHMAP_BASE(h));
-}
-static inline void *ordered_hashmap_first(OrderedHashmap *h) {
-        return internal_hashmap_first(HASHMAP_BASE(h));
+        return internal_hashmap_first_key(HASHMAP_BASE(h), false);
 }
 
 #define hashmap_clear_with_destructor(_s, _f)                   \
index 643e0bea8887dffe74dce0e0fce596f00a48b40b..040680c30ad907250d259f6a3ae0f148b3892e2f 100644 (file)
@@ -38,9 +38,9 @@
 /* Append an item to the list */
 #define LIST_APPEND(name,head,item)                                     \
         do {                                                            \
-                typeof(*(head)) *_tail;                                 \
-                LIST_FIND_TAIL(name,head,_tail);                        \
-                LIST_INSERT_AFTER(name,head,_tail,item);                \
+                typeof(*(head)) **_hhead = &(head), *_tail;             \
+                LIST_FIND_TAIL(name, *_hhead, _tail);                   \
+                LIST_INSERT_AFTER(name, *_hhead, _tail, item);          \
         } while (false)
 
 /* Remove an item from the list */
index f60cf003e75c2db9a9a62f028933484dbc373d9d..2deb176240e55d44d4dee3ef53f5a306e22a221a 100644 (file)
@@ -779,24 +779,32 @@ bool filename_is_valid(const char *p) {
         if (*e != 0)
                 return false;
 
-        if (e - p > FILENAME_MAX)
+        if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */
                 return false;
 
         return true;
 }
 
-bool path_is_normalized(const char *p) {
+bool path_is_valid(const char *p) {
 
         if (isempty(p))
                 return false;
 
-        if (dot_or_dot_dot(p))
+        if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */
                 return false;
 
-        if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+        return true;
+}
+
+bool path_is_normalized(const char *p) {
+
+        if (!path_is_valid(p))
+                return false;
+
+        if (dot_or_dot_dot(p))
                 return false;
 
-        if (strlen(p)+1 > PATH_MAX)
+        if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
                 return false;
 
         if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
index 49604eab800728358d3caa0437870eb3e1dbab6c..17d31bbd89b2d104ace8c5e529a3eeac23000139 100644 (file)
@@ -134,6 +134,7 @@ char* dirname_malloc(const char *path);
 const char *last_path_component(const char *path);
 
 bool filename_is_valid(const char *p) _pure_;
+bool path_is_valid(const char *p) _pure_;
 bool path_is_normalized(const char *p) _pure_;
 
 char *file_in_same_dir(const char *path, const char *filename);
index ef28a086d149ad48e090974e4435b91faec1d046..4ef4eaf0cdebb14fcf0d62fd7630aba92e8cbe51 100644 (file)
@@ -32,11 +32,14 @@ struct Prioq {
 Prioq *prioq_new(compare_func_t compare_func) {
         Prioq *q;
 
-        q = new0(Prioq, 1);
+        q = new(Prioq, 1);
         if (!q)
                 return q;
 
-        q->compare_func = compare_func;
+        *q = (Prioq) {
+                .compare_func = compare_func,
+        };
+
         return q;
 }
 
@@ -88,6 +91,7 @@ static void swap(Prioq *q, unsigned j, unsigned k) {
 
 static unsigned shuffle_up(Prioq *q, unsigned idx) {
         assert(q);
+        assert(idx < q->n_items);
 
         while (idx > 0) {
                 unsigned k;
@@ -211,9 +215,12 @@ _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx)
 
         assert(q);
 
+        if (q->n_items <= 0)
+                return NULL;
+
         if (idx) {
                 if (*idx == PRIOQ_IDX_NULL ||
-                    *idx > q->n_items)
+                    *idx >= q->n_items)
                         return NULL;
 
                 i = q->items + *idx;
index e0361752603f91ed38f75262dd99fac4e1d5a140..bba5c7caa49ca6d047b799f2c8c441e759757361 100644 (file)
@@ -12,6 +12,7 @@ typedef struct Prioq Prioq;
 
 Prioq *prioq_new(compare_func_t compare);
 Prioq *prioq_free(Prioq *q);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free);
 int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
 
 int prioq_put(Prioq *q, void *data, unsigned *idx);
index be1ba615ec5acdd2d2a6b1063a3bc42a820d0136..c133f84b7e90e24c7dd10149d09188b11beeb371 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "alloc-util.h"
 #include "extract-word.h"
+#include "fd-util.h"
 #include "format-util.h"
 #include "macro.h"
 #include "missing.h"
@@ -33,8 +34,15 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
         if (highest.rlim_max == RLIM_INFINITY)
                 return -EPERM;
 
-        fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
-        fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
+        fixed = (struct rlimit) {
+                .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
+                .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
+        };
+
+        /* Shortcut things if we wouldn't change anything. */
+        if (fixed.rlim_cur == highest.rlim_cur &&
+            fixed.rlim_max == highest.rlim_max)
+                return 0;
 
         if (setrlimit(resource, &fixed) < 0)
                 return -errno;
@@ -360,3 +368,24 @@ void rlimit_free_all(struct rlimit **rl) {
         for (i = 0; i < _RLIMIT_MAX; i++)
                 rl[i] = mfree(rl[i]);
 }
+
+int rlimit_nofile_bump(int limit) {
+        int r;
+
+        /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
+         * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
+         * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
+         * (i.e. do not use select() — which chokes on fds >= 1024) */
+
+        if (limit < 0)
+                limit = read_nr_open();
+
+        if (limit < 3)
+                limit = 3;
+
+        r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
+
+        return 0;
+}
index c2ea6f846ba49bf0f0c9bf70258da62f2da432c0..6139af3ff50ff5fec8e5cf56bdbfc2719cd676e4 100644 (file)
@@ -20,3 +20,5 @@ int rlimit_format(const struct rlimit *rl, char **ret);
 void rlimit_free_all(struct rlimit **rl);
 
 #define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
+
+int rlimit_nofile_bump(int limit);
index 664713810dab37e1d806eaf9364bde5c881c595e..0d99d5550d9f93b075219b60e5336a541db28164 100644 (file)
@@ -86,7 +86,7 @@ static inline void set_clear_free(Set *s) {
 /* no set_clear_free_free */
 
 static inline void *set_steal_first(Set *s) {
-        return internal_hashmap_steal_first(HASHMAP_BASE(s));
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
 }
 
 #define set_clear_with_destructor(_s, _f)               \
@@ -105,7 +105,7 @@ static inline void *set_steal_first(Set *s) {
 /* no set_first_key */
 
 static inline void *set_first(Set *s) {
-        return internal_hashmap_first(HASHMAP_BASE(s));
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL);
 }
 
 /* no set_next */
index 54e2420cc6e94beea881250c0050746387c1ea98..70a4a03f6a5902c1af286ce8f9d9e5474881754a 100644 (file)
@@ -3,6 +3,7 @@
 #include <inttypes.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <string.h>
 #include <sys/types.h>
 
 struct siphash {
@@ -21,3 +22,7 @@ void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
 uint64_t siphash24_finalize(struct siphash *state);
 
 uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
+
+static inline uint64_t siphash24_string(const char *s, const uint8_t k[16]) {
+        return siphash24(s, strlen(s) + 1, k);
+}
index 35e14e567c065a7533bf3f36b89663cf0af1f6f1..2698d59aeaa96f95c9a44785dbbd2cd9a9217fc1 100644 (file)
@@ -35,7 +35,7 @@ int socket_address_listen(
 
         _cleanup_close_ int fd = -1;
         const char *p;
-        int r, one;
+        int r;
 
         assert(a);
 
@@ -74,26 +74,22 @@ int socket_address_listen(
                                 return -errno;
 
                 if (reuse_port) {
-                        one = 1;
-                        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
+                        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &const_int_one, sizeof(const_int_one)) < 0)
                                 log_warning_errno(errno, "SO_REUSEPORT failed: %m");
                 }
 
                 if (free_bind) {
-                        one = 1;
-                        if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
+                        if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &const_int_one, sizeof(const_int_one)) < 0)
                                 log_warning_errno(errno, "IP_FREEBIND failed: %m");
                 }
 
                 if (transparent) {
-                        one = 1;
-                        if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
+                        if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &const_int_one, sizeof(const_int_one)) < 0)
                                 log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
                 }
         }
 
-        one = 1;
-        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one)) < 0)
                 return -errno;
 
         p = socket_address_get_path(a);
index 4e8b2baf82e962b085bbbad3fb35e3ca3addb377..dd22d30fca928cf91407af259973ea6c17aa2667 100644 (file)
@@ -57,8 +57,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
         assert(a);
         assert(s);
 
-        zero(*a);
-        a->type = SOCK_STREAM;
+        *a = (SocketAddress) {
+                .type = SOCK_STREAM,
+        };
 
         if (*s == '[') {
                 uint16_t port;
@@ -96,7 +97,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                 size_t l;
 
                 l = strlen(s);
-                if (l >= sizeof(a->sockaddr.un.sun_path))
+                if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
+                                                           * parsing (the kernel itself is less strict here in what it
+                                                           * accepts) */
                         return -EINVAL;
 
                 a->sockaddr.un.sun_family = AF_UNIX;
@@ -108,7 +111,11 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                 size_t l;
 
                 l = strlen(s+1);
-                if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+                if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here
+                                                               * when parsing, even though abstract namespace sockets
+                                                               * explicitly allow embedded NUL bytes and don't consider
+                                                               * them special. But it's simply annoying to debug such
+                                                               * sockets. */
                         return -EINVAL;
 
                 a->sockaddr.un.sun_family = AF_UNIX;
@@ -286,19 +293,28 @@ int socket_address_verify(const SocketAddress *a) {
         case AF_UNIX:
                 if (a->size < offsetof(struct sockaddr_un, sun_path))
                         return -EINVAL;
+                if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will
+                                                             * append a NUL byte if we have path sockets that are above
+                                                             * sun_path' full size */
+                        return -EINVAL;
 
-                if (a->size > offsetof(struct sockaddr_un, sun_path)) {
+                if (a->size > offsetof(struct sockaddr_un, sun_path) &&
+                    a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */
 
-                        if (a->sockaddr.un.sun_path[0] != 0) {
-                                char *e;
-
-                                /* path */
-                                e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
-                                if (!e)
-                                        return -EINVAL;
+                        const char *e;
 
+                        e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
+                        if (e) {
+                                /* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */
                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
                                         return -EINVAL;
+                        } else {
+                                /* If there's no embedded NUL byte, then then the size needs to match the whole
+                                 * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
+                                 * and considers both equivalent: getsockname() even extends sockaddr_un beyond its
+                                 * size if the path is non NUL terminated.)*/
+                                if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1))
+                                        return -EINVAL;
                         }
                 }
 
@@ -482,6 +498,10 @@ const char* socket_address_get_path(const SocketAddress *a) {
         if (a->sockaddr.un.sun_path[0] == 0)
                 return NULL;
 
+        /* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un
+         * structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the
+         * full sun_path space. */
+        assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1);
         return a->sockaddr.un.sun_path;
 }
 
@@ -747,21 +767,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
         return 0;
 }
 
-int socket_address_unlink(SocketAddress *a) {
-        assert(a);
-
-        if (socket_address_family(a) != AF_UNIX)
-                return 0;
-
-        if (a->sockaddr.un.sun_path[0] == 0)
-                return 0;
-
-        if (unlink(a->sockaddr.un.sun_path) < 0)
-                return -errno;
-
-        return 1;
-}
-
 static const char* const netlink_family_table[] = {
         [NETLINK_ROUTE] = "route",
         [NETLINK_FIREWALL] = "firewall",
@@ -1246,3 +1251,71 @@ int socket_ioctl_fd(void) {
 
         return fd;
 }
+
+int sockaddr_un_unlink(const struct sockaddr_un *sa) {
+        const char *p, * nul;
+
+        assert(sa);
+
+        if (sa->sun_family != AF_UNIX)
+                return -EPROTOTYPE;
+
+        if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */
+                return 0;
+
+        /* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */
+        nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path));
+        if (nul)
+                p = sa->sun_path;
+        else
+                p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path));
+
+        if (unlink(p) < 0)
+                return -errno;
+
+        return 1;
+}
+
+int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
+        size_t l;
+
+        assert(ret);
+        assert(path);
+
+        /* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as
+         * abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept
+         * anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to
+         * reference paths in the abstract namespace that include NUL bytes in the name. */
+
+        l = strlen(path);
+        if (l == 0)
+                return -EINVAL;
+        if (!IN_SET(path[0], '/', '@'))
+                return -EINVAL;
+        if (path[1] == 0)
+                return -EINVAL;
+
+        /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
+         * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket
+         * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
+         * do not expect non-NUL terminated file system path*/
+        if (l+1 > sizeof(ret->sun_path))
+                return -EINVAL;
+
+        *ret = (struct sockaddr_un) {
+                .sun_family = AF_UNIX,
+        };
+
+        if (path[0] == '@') {
+                /* Abstract namespace socket */
+                memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */
+                return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */
+
+        } else {
+                assert(path[0] == '/');
+
+                /* File system socket */
+                memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */
+                return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */
+        }
+}
index 82781a0de1c320af45888bd99482387cb721d60f..d2e00f2f647e534cf630b42913e21907f6317710 100644 (file)
@@ -71,7 +71,12 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s);
 int socket_address_parse_netlink(SocketAddress *a, const char *s);
 int socket_address_print(const SocketAddress *a, char **p);
 int socket_address_verify(const SocketAddress *a) _pure_;
-int socket_address_unlink(SocketAddress *a);
+
+int sockaddr_un_unlink(const struct sockaddr_un *sa);
+
+static inline int socket_address_unlink(const SocketAddress *a) {
+        return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0;
+}
 
 bool socket_address_can_accept(const SocketAddress *a) _pure_;
 
@@ -179,7 +184,9 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
                 offsetof(struct sockaddr_un, sun_path) +                \
                         (_sa->sun_path[0] == 0 ?                        \
                          1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
-                         strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
+                         strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
         })
 
 int socket_ioctl_fd(void);
+
+int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
index c6dad5275fe16b08e4fac292bb12315845479a52..a3be35847df4852525ba254dca79e5e1647abfe7 100644 (file)
@@ -398,12 +398,7 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
         if (r != 0)
                 return r;
 
-        if (n < m)
-                return -1;
-        else if (n > m)
-                return 1;
-        else
-                return 0;
+        return CMP(n, m);
 }
 
 bool chars_intersect(const char *a, const char *b) {
index 6277f56b80ff74e5c5cce5d27859e17f9d141a4a..9210277cde53dba8187215c15bf1af065af59934 100644 (file)
@@ -31,12 +31,11 @@ size_t strpcpy(char **dest, size_t size, const char *src) {
                 if (size > 1)
                         *dest = mempcpy(*dest, src, size-1);
                 size = 0;
-        } else {
-                if (len > 0) {
-                        *dest = mempcpy(*dest, src, len);
-                        size -= len;
-                }
+        } else if (len > 0) {
+                *dest = mempcpy(*dest, src, len);
+                size -= len;
         }
+
         *dest[0] = '\0';
         return size;
 }
@@ -56,9 +55,8 @@ size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
         if (i < (int)size) {
                 *dest += i;
                 size -= i;
-        } else {
+        } else
                 size = 0;
-        }
         va_end(va);
         return size;
 }
@@ -73,7 +71,7 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
         do {
                 size = strpcpy(dest, size, src);
                 src = va_arg(va, char *);
-        } while (src != NULL);
+        } while (src);
         va_end(va);
         return size;
 }
@@ -100,7 +98,7 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
         do {
                 size = strpcpy(&s, size, src);
                 src = va_arg(va, char *);
-        } while (src != NULL);
+        } while (src);
         va_end(va);
 
         return size;
index 0da963f4af876548846ebc02860aadcefdb07003..c4c66cca53bbbbfd61af8e172aed64be2421467e 100644 (file)
@@ -51,6 +51,9 @@ int saved_argc = 0;
 char **saved_argv = NULL;
 static int saved_in_initrd = -1;
 
+const int const_int_zero = 0;
+const int const_int_one = 1;
+
 size_t page_size(void) {
         static thread_local size_t pgsz = 0;
         long r;
index 8cba4ed7260e2380a9a46f7d098aa4445bee5eeb..32d9380bd967b599d508c2601c8fd2e0a6539186 100644 (file)
@@ -232,3 +232,6 @@ int version(void);
 int str_verscmp(const char *s1, const char *s2);
 
 void disable_coredumps(void);
+
+extern const int const_int_zero;
+extern const int const_int_one;
index 48c9db7c154e173a5d800b251ce333b511f51a76..4ff7594a9a61afea34e6450d422e63cfca3b22d5 100644 (file)
@@ -24,11 +24,11 @@ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
 enum loader_type {
         LOADER_UNDEFINED,
         LOADER_EFI,
-        LOADER_LINUX
+        LOADER_LINUX,
 };
 
 typedef struct {
-        CHAR16 *file;
+        CHAR16 *id; /* The identifier for this entry (note that this id is not necessarily unique though!) */
         CHAR16 *title_show;
         CHAR16 *title;
         CHAR16 *version;
@@ -41,6 +41,11 @@ typedef struct {
         EFI_STATUS (*call)(VOID);
         BOOLEAN no_autoselect;
         BOOLEAN non_unique;
+        UINTN tries_done;
+        UINTN tries_left;
+        CHAR16 *path;
+        CHAR16 *current_name;
+        CHAR16 *next_name;
 } ConfigEntry;
 
 typedef struct {
@@ -68,14 +73,24 @@ static VOID cursor_left(UINTN *cursor, UINTN *first) {
                 (*first)--;
 }
 
-static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) {
+static VOID cursor_right(
+                UINTN *cursor,
+                UINTN *first,
+                UINTN x_max,
+                UINTN len) {
+
         if ((*cursor)+1 < x_max)
                 (*cursor)++;
         else if ((*first) + (*cursor) < len)
                 (*first)++;
 }
 
-static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
+static BOOLEAN line_edit(
+                CHAR16 *line_in,
+                CHAR16 **line_out,
+                UINTN x_max,
+                UINTN y_pos) {
+
         _cleanup_freepool_ CHAR16 *line = NULL, *print = NULL;
         UINTN size, len, first, cursor, clear;
         BOOLEAN exit, enter;
@@ -407,8 +422,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
 
                 entry = config->entries[i];
                 Print(L"config entry:           %d/%d\n", i+1, config->entry_count);
-                if (entry->file)
-                        Print(L"file                    '%s'\n", entry->file);
+                if (entry->id)
+                        Print(L"id                      '%s'\n", entry->id);
                 Print(L"title show              '%s'\n", entry->title_show);
                 if (entry->title)
                         Print(L"title                   '%s'\n", entry->title);
@@ -435,6 +450,17 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
                 if (entry->call)
                         Print(L"internal call           yes\n");
 
+                if (entry->tries_left != (UINTN) -1)
+                        Print(L"counting boots          yes\n"
+                               "tries done              %u\n"
+                               "tries left              %u\n"
+                               "current path            %s\\%s\n"
+                               "next path               %s\\%s\n",
+                              entry->tries_done,
+                              entry->tries_left,
+                              entry->path, entry->current_name,
+                              entry->path, entry->next_name);
+
                 Print(L"\n--- press key ---\n\n");
                 console_key_read(&key, TRUE);
         }
@@ -442,7 +468,11 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
 }
 
-static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
+static BOOLEAN menu_run(
+                Config *config,
+                ConfigEntry **chosen_entry,
+                CHAR16 *loaded_image_path) {
+
         EFI_STATUS err;
         UINTN visible_max;
         UINTN idx_highlight;
@@ -713,7 +743,7 @@ static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *load
                 case KEYPRESS(0, 0, 'd'):
                         if (config->idx_default_efivar != (INTN)idx_highlight) {
                                 /* store the selected entry in a persistent EFI variable */
-                                efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
+                                efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->id, TRUE);
                                 config->idx_default_efivar = idx_highlight;
                                 status = StrDuplicate(L"Default boot entry selected.");
                         } else {
@@ -837,11 +867,20 @@ static VOID config_add_entry(Config *config, ConfigEntry *entry) {
 }
 
 static VOID config_entry_free(ConfigEntry *entry) {
+        if (!entry)
+                return;
+
+        FreePool(entry->id);
         FreePool(entry->title_show);
         FreePool(entry->title);
+        FreePool(entry->version);
         FreePool(entry->machine_id);
         FreePool(entry->loader);
         FreePool(entry->options);
+        FreePool(entry->path);
+        FreePool(entry->current_name);
+        FreePool(entry->next_name);
+        FreePool(entry);
 }
 
 static BOOLEAN is_digit(CHAR16 c) {
@@ -870,7 +909,7 @@ static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) {
                         INTN order;
 
                         order = c_order(*s1) - c_order(*s2);
-                        if (order)
+                        if (order != 0)
                                 return order;
                         s1++;
                         s2++;
@@ -894,14 +933,20 @@ static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) {
                 if (is_digit(*s2))
                         return -1;
 
-                if (first)
+                if (first != 0)
                         return first;
         }
 
         return StrCmp(os1, os2);
 }
 
-static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
+static CHAR8 *line_get_key_value(
+                CHAR8 *content,
+                CHAR8 *sep,
+                UINTN *pos,
+                CHAR8 **key_ret,
+                CHAR8 **value_ret) {
+
         CHAR8 *line;
         UINTN linelen;
         CHAR8 *value;
@@ -968,7 +1013,6 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
         UINTN pos = 0;
         CHAR8 *key, *value;
 
-        line = content;
         while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
                 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
                         _cleanup_freepool_ CHAR16 *s = NULL;
@@ -1030,7 +1074,199 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
         }
 }
 
-static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
+static VOID config_entry_parse_tries(
+                ConfigEntry *entry,
+                CHAR16 *path,
+                CHAR16 *file,
+                CHAR16 *suffix) {
+
+        UINTN left = (UINTN) -1, done = (UINTN) -1, factor = 1, i, next_left, next_done;
+        _cleanup_freepool_ CHAR16 *prefix = NULL;
+
+        /*
+         * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
+         * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
+         * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
+         *
+         * Names we grok, and the series they result in:
+         *
+         * foobar+3.efi   → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
+         * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
+         */
+
+        i = StrLen(file);
+
+        /* Chop off any suffix such as ".conf" or ".efi" */
+        if (suffix) {
+                UINTN suffix_length;
+
+                suffix_length = StrLen(suffix);
+                if (i < suffix_length)
+                        return;
+
+                i -= suffix_length;
+        }
+
+        /* Go backwards through the string and parse everything we encounter */
+        for (;;) {
+                if (i == 0)
+                        return;
+
+                i--;
+
+                switch (file[i]) {
+
+                case '+':
+                        if (left == (UINTN) -1) /* didn't read at least one digit for 'left'? */
+                                return;
+
+                        if (done == (UINTN) -1) /* no 'done' counter? If so, it's equivalent to 0 */
+                                done = 0;
+
+                        goto good;
+
+                case '-':
+                        if (left == (UINTN) -1) /* didn't parse any digit yet? */
+                                return;
+
+                        if (done != (UINTN) -1) /* already encountered a dash earlier? */
+                                return;
+
+                        /* So we encountered a dash. This means this counter is of the form +LEFT-DONE. Let's assign
+                         * what we already parsed to 'done', and start fresh for the 'left' part. */
+
+                        done = left;
+                        left = (UINTN) -1;
+                        factor = 1;
+                        break;
+
+                case '0'...'9': {
+                        UINTN new_factor;
+
+                        if (left == (UINTN) -1)
+                                left = file[i] - '0';
+                        else {
+                                UINTN new_left, digit;
+
+                                digit = file[i] - '0';
+                                if (digit > (UINTN) -1 / factor) /* overflow check */
+                                        return;
+
+                                new_left = left + digit * factor;
+                                if (new_left < left) /* overflow check */
+                                        return;
+
+                                if (new_left == (UINTN) -1) /* don't allow us to be confused */
+                                        return;
+                        }
+
+                        new_factor = factor * 10;
+                        if (new_factor < factor) /* overflow chck */
+                                return;
+
+                        factor = new_factor;
+                        break;
+                }
+
+                default:
+                        return;
+                }
+        }
+
+good:
+        entry->tries_left = left;
+        entry->tries_done = done;
+
+        entry->path = StrDuplicate(path);
+        entry->current_name = StrDuplicate(file);
+
+        next_left = left <= 0 ? 0 : left - 1;
+        next_done = done >= (UINTN) -2 ? (UINTN) -2 : done + 1;
+
+        prefix = StrDuplicate(file);
+        prefix[i] = 0;
+
+        entry->next_name = PoolPrint(L"%s+%u-%u%s", prefix, next_left, next_done, suffix ?: L"");
+}
+
+static VOID config_entry_bump_counters(
+                ConfigEntry *entry,
+                EFI_FILE_HANDLE root_dir) {
+
+        _cleanup_freepool_ CHAR16* old_path = NULL, *new_path = NULL;
+        _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
+        static EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID;
+        _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
+        UINTN file_info_size, a, b;
+        EFI_STATUS r;
+
+        if (entry->tries_left == (UINTN) -1)
+                return;
+
+        if (!entry->path || !entry->current_name || !entry->next_name)
+                return;
+
+        old_path = PoolPrint(L"%s\\%s", entry->path, entry->current_name);
+
+        r = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, old_path, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
+        if (EFI_ERROR(r))
+                return;
+
+        a = StrLen(entry->current_name);
+        b = StrLen(entry->next_name);
+
+        file_info_size = OFFSETOF(EFI_FILE_INFO, FileName) + (a > b ? a : b) + 1;
+
+        for (;;) {
+                file_info = AllocatePool(file_info_size);
+
+                r = uefi_call_wrapper(handle->GetInfo, 4, handle, &EfiFileInfoGuid, &file_info_size, file_info);
+                if (!EFI_ERROR(r))
+                        break;
+
+                if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
+                        Print(L"\nFailed to get file info for '%s': %r\n", old_path, r);
+                        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                        return;
+                }
+
+                file_info_size *= 2;
+                FreePool(file_info);
+        }
+
+        /* And rename the file */
+        StrCpy(file_info->FileName, entry->next_name);
+        r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
+        if (EFI_ERROR(r)) {
+                Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r);
+                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                return;
+        }
+
+        /* Flush everything to disk, just in case… */
+        (void) uefi_call_wrapper(handle->Flush, 1, handle);
+
+        /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
+         * success */
+        new_path = PoolPrint(L"%s\\%s", entry->path, entry->next_name);
+        efivar_set(L"LoaderBootCountPath", new_path, FALSE);
+
+        /* If the file we just renamed is the loader path, then let's update that. */
+        if (StrCmp(entry->loader, old_path) == 0) {
+                FreePool(entry->loader);
+                entry->loader = new_path;
+                new_path = NULL;
+        }
+}
+
+static VOID config_entry_add_from_file(
+                Config *config,
+                EFI_HANDLE *device,
+                CHAR16 *path,
+                CHAR16 *file,
+                CHAR8 *content,
+                CHAR16 *loaded_image_path) {
+
         ConfigEntry *entry;
         CHAR8 *line;
         UINTN pos = 0;
@@ -1038,9 +1274,13 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
         UINTN len;
         _cleanup_freepool_ CHAR16 *initrd = NULL;
 
-        entry = AllocateZeroPool(sizeof(ConfigEntry));
+        entry = AllocatePool(sizeof(ConfigEntry));
+
+        *entry = (ConfigEntry) {
+                .tries_done = (UINTN) -1,
+                .tries_left = (UINTN) -1,
+        };
 
-        line = content;
         while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
                 if (strcmpa((CHAR8 *)"title", key) == 0) {
                         FreePool(entry->title);
@@ -1127,7 +1367,6 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
 
         if (entry->type == LOADER_UNDEFINED) {
                 config_entry_free(entry);
-                FreePool(entry);
                 return;
         }
 
@@ -1146,14 +1385,16 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
         }
 
         entry->device = device;
-        entry->file = StrDuplicate(file);
-        len = StrLen(entry->file);
+        entry->id = StrDuplicate(file);
+        len = StrLen(entry->id);
         /* remove ".conf" */
         if (len > 5)
-                entry->file[len - 5] = '\0';
-        StrLwr(entry->file);
+                entry->id[len - 5] = '\0';
+        StrLwr(entry->id);
 
         config_add_entry(config, entry);
+
+        config_entry_parse_tries(entry, path, file, L".conf");
 }
 
 static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
@@ -1177,7 +1418,12 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
                 config->timeout_sec_efivar = -1;
 }
 
-static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+static VOID config_load_entries(
+                Config *config,
+                EFI_HANDLE *device,
+                EFI_FILE *root_dir,
+                CHAR16 *loaded_image_path) {
+
         EFI_FILE_HANDLE entries_dir;
         EFI_STATUS err;
 
@@ -1211,12 +1457,44 @@ static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *ro
 
                         err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL);
                         if (!EFI_ERROR(err))
-                                config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
+                                config_entry_add_from_file(config, device, L"\\loader\\entries", f->FileName, content, loaded_image_path);
                 }
                 uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
         }
 }
 
+static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
+        INTN r;
+
+        /* Order entries that have no tries left to the end of the list */
+        if (a->tries_left != 0 && b->tries_left == 0)
+                return -1;
+        if (a->tries_left == 0 && b->tries_left != 0)
+                return 1;
+
+        r = str_verscmp(a->id, b->id);
+        if (r != 0)
+                return r;
+
+        if (a->tries_left == (UINTN) -1 ||
+            b->tries_left == (UINTN) -1)
+                return 0;
+
+        /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
+        if (a->tries_left > b->tries_left)
+                return -1;
+        if (a->tries_left < b->tries_left)
+                return 1;
+
+        /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
+        if (a->tries_done < b->tries_done)
+                return -1;
+        if (a->tries_done > b->tries_done)
+                return 1;
+
+        return 0;
+}
+
 static VOID config_sort_entries(Config *config) {
         UINTN i;
 
@@ -1228,8 +1506,9 @@ static VOID config_sort_entries(Config *config) {
                 for (k = 0; k < config->entry_count - i; k++) {
                         ConfigEntry *entry;
 
-                        if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
+                        if (config_entry_compare(config->entries[k], config->entries[k+1]) <= 0)
                                 continue;
+
                         entry = config->entries[k];
                         config->entries[k] = config->entries[k+1];
                         config->entries[k+1] = entry;
@@ -1240,10 +1519,20 @@ static VOID config_sort_entries(Config *config) {
         }
 }
 
+static INTN config_entry_find(Config *config, CHAR16 *id) {
+        UINTN i;
+
+        for (i = 0; i < config->entry_count; i++)
+                if (StrCmp(config->entries[i]->id, id) == 0)
+                        return (INTN) i;
+
+        return -1;
+}
+
 static VOID config_default_entry_select(Config *config) {
         _cleanup_freepool_ CHAR16 *entry_oneshot = NULL, *entry_default = NULL;
         EFI_STATUS err;
-        UINTN i;
+        INTN i;
 
         /*
          * The EFI variable to specify a boot entry for the next, and only the
@@ -1251,19 +1540,15 @@ static VOID config_default_entry_select(Config *config) {
          */
         err = efivar_get(L"LoaderEntryOneShot", &entry_oneshot);
         if (!EFI_ERROR(err)) {
-                BOOLEAN found = FALSE;
-
-                for (i = 0; i < config->entry_count; i++)
-                        if (StrCmp(config->entries[i]->file, entry_oneshot) == 0) {
-                                config->idx_default = i;
-                                found = TRUE;
-                                break;
-                        }
 
                 config->entry_oneshot = StrDuplicate(entry_oneshot);
                 efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
-                if (found)
+
+                i = config_entry_find(config, entry_oneshot);
+                if (i >= 0) {
+                        config->idx_default = i;
                         return;
+                }
         }
 
         /*
@@ -1274,12 +1559,13 @@ static VOID config_default_entry_select(Config *config) {
          */
         err = efivar_get(L"LoaderEntryDefault", &entry_default);
         if (!EFI_ERROR(err)) {
-                for (i = 0; i < config->entry_count; i++)
-                        if (StrCmp(config->entries[i]->file, entry_default) == 0) {
-                                config->idx_default = i;
-                                config->idx_default_efivar = i;
-                                return;
-                        }
+
+                i = config_entry_find(config, entry_default);
+                if (i >= 0) {
+                        config->idx_default = i;
+                        config->idx_default_efivar = i;
+                        return;
+                }
         }
         config->idx_default_efivar = -1;
 
@@ -1295,7 +1581,7 @@ static VOID config_default_entry_select(Config *config) {
                 while (i--) {
                         if (config->entries[i]->no_autoselect)
                                 continue;
-                        if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
+                        if (MetaiMatch(config->entries[i]->id, config->entry_default_pattern)) {
                                 config->idx_default = i;
                                 return;
                         }
@@ -1346,7 +1632,7 @@ static VOID config_title_generate(Config *config) {
                 FreePool(config->entries[i]->title_show);
                 title = config->entries[i]->title;
                 if (!title)
-                        title = config->entries[i]->file;
+                        title = config->entries[i]->id;
                 config->entries[i]->title_show = StrDuplicate(title);
         }
 
@@ -1396,43 +1682,74 @@ static VOID config_title_generate(Config *config) {
 
                 if (!config->entries[i]->non_unique)
                         continue;
-                s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
+                s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->id);
                 FreePool(config->entries[i]->title_show);
                 config->entries[i]->title_show = s;
                 config->entries[i]->non_unique = FALSE;
         }
 }
 
-static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
+static BOOLEAN config_entry_add_call(
+                Config *config,
+                CHAR16 *id,
+                CHAR16 *title,
+                EFI_STATUS (*call)(VOID)) {
+
         ConfigEntry *entry;
 
-        entry = AllocateZeroPool(sizeof(ConfigEntry));
-        entry->title = StrDuplicate(title);
-        entry->call = call;
-        entry->no_autoselect = TRUE;
+        entry = AllocatePool(sizeof(ConfigEntry));
+        *entry = (ConfigEntry) {
+                .id = StrDuplicate(id),
+                .title = StrDuplicate(title),
+                .call = call,
+                .no_autoselect = TRUE,
+                .tries_done = (UINTN) -1,
+                .tries_left = (UINTN) -1,
+        };
+
         config_add_entry(config, entry);
         return TRUE;
 }
 
-static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device,
-                                            enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+static ConfigEntry *config_entry_add_loader(
+                Config *config,
+                EFI_HANDLE *device,
+                enum loader_type type,
+                CHAR16 *id,
+                CHAR16 key,
+                CHAR16 *title,
+                CHAR16 *loader) {
+
         ConfigEntry *entry;
 
-        entry = AllocateZeroPool(sizeof(ConfigEntry));
-        entry->type = type;
-        entry->title = StrDuplicate(title);
-        entry->device = device;
-        entry->loader = StrDuplicate(loader);
-        entry->file = StrDuplicate(file);
-        StrLwr(entry->file);
-        entry->key = key;
-        config_add_entry(config, entry);
+        entry = AllocatePool(sizeof(ConfigEntry));
+        *entry = (ConfigEntry) {
+                .type = type,
+                .title = StrDuplicate(title),
+                .device = device,
+                .loader = StrDuplicate(loader),
+                .id = StrDuplicate(id),
+                .key = key,
+                .tries_done = (UINTN) -1,
+                .tries_left = (UINTN) -1,
+        };
 
+        StrLwr(entry->id);
+
+        config_add_entry(config, entry);
         return entry;
 }
 
-static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
-                                         CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+static BOOLEAN config_entry_add_loader_auto(
+                Config *config,
+                EFI_HANDLE *device,
+                EFI_FILE *root_dir,
+                CHAR16 *loaded_image_path,
+                CHAR16 *id,
+                CHAR16 key,
+                CHAR16 *title,
+                CHAR16 *loader) {
+
         EFI_FILE_HANDLE handle;
         ConfigEntry *entry;
         EFI_STATUS err;
@@ -1466,7 +1783,7 @@ static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device,
                 return FALSE;
         uefi_call_wrapper(handle->Close, 1, handle);
 
-        entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader);
+        entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader);
         if (!entry)
                 return FALSE;
 
@@ -1504,7 +1821,11 @@ static VOID config_entry_add_osx(Config *config) {
         }
 }
 
-static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
+static VOID config_entry_add_linux(
+                Config *config,
+                EFI_LOADED_IMAGE *loaded_image,
+                EFI_FILE *root_dir) {
+
         EFI_FILE_HANDLE linux_dir;
         EFI_STATUS err;
         ConfigEntry *entry;
@@ -1560,7 +1881,6 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
                         continue;
 
                 /* read properties from the embedded os-release file */
-                line = content;
                 while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
                         if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
                                 FreePool(os_name);
@@ -1588,15 +1908,16 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
                 }
 
                 if (os_name && os_id && (os_version || os_build)) {
-                        CHAR16 *conf;
-                        CHAR16 *path;
+                        _cleanup_freepool_ CHAR16 *conf = NULL, *path = NULL;
 
                         conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
                         path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
+
                         entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
 
                         FreePool(content);
                         content = NULL;
+
                         /* read the embedded cmdline file */
                         err = file_read(linux_dir, f->FileName, offs[1], szs[1], &content, NULL);
                         if (!EFI_ERROR(err)) {
@@ -1608,8 +1929,7 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
                                 entry->options = stra_to_str(content);
                         }
 
-                        FreePool(conf);
-                        FreePool(path);
+                        config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
                 }
 
                 FreePool(os_name);
@@ -1622,7 +1942,11 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
         uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
 }
 
-static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
+static EFI_STATUS image_start(
+                EFI_HANDLE parent_image,
+                const Config *config,
+                const ConfigEntry *entry) {
+
         EFI_HANDLE image;
         _cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
         CHAR16 *options;
@@ -1692,7 +2016,7 @@ static EFI_STATUS reboot_into_firmware(VOID) {
         if (!EFI_ERROR(err))
                 osind |= (UINT64)*b;
 
-        err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
+        err = efivar_set_raw(&global_guid, L"OsIndications", &osind, sizeof(UINT64), TRUE);
         if (EFI_ERROR(err))
                 return err;
 
@@ -1713,6 +2037,29 @@ static VOID config_free(Config *config) {
         FreePool(config->entry_oneshot);
 }
 
+static VOID config_write_entries_to_variable(Config *config) {
+        _cleanup_freepool_ CHAR16 *buffer = NULL;
+        UINTN i, sz = 0;
+        CHAR16 *p;
+
+        for (i = 0; i < config->entry_count; i++)
+                sz += StrLen(config->entries[i]->id) + 1;
+
+        p = buffer = AllocatePool(sz * sizeof(CHAR16));
+
+        for (i = 0; i < config->entry_count; i++) {
+                UINTN l;
+
+                l = StrLen(config->entries[i]->id) + 1;
+                CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16));
+
+                p += l;
+        }
+
+        /* Store the full list of discovered entries. */
+        (void) efivar_set_raw(&loader_guid, L"LoaderEntries", buffer, (UINT8*) p - (UINT8*) buffer, FALSE);
+}
+
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         _cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
         CHAR8 *b;
@@ -1794,7 +2141,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 UINT64 osind = (UINT64)*b;
 
                 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
-                        config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
+                        config_entry_add_call(&config, L"auto-reboot-into-firmware-ui", L"Reboot Into Firmware Interface", reboot_into_firmware);
                 FreePool(b);
         }
 
@@ -1804,6 +2151,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 goto out;
         }
 
+        config_write_entries_to_variable(&config);
+
         config_title_generate(&config);
 
         /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
@@ -1851,8 +2200,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                         }
                 }
 
+                config_entry_bump_counters(entry, root_dir);
+
                 /* export the selected boot entry to the system */
-                efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
+                efivar_set(L"LoaderEntrySelected", entry->id, FALSE);
 
                 uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
                 err = image_start(image, &config, entry);
index 5c97b54691c3f6ef7b725e1064603d6d7a3382e9..d11e55574857e6214a248685d5bcffe435f29e69 100644 (file)
@@ -1,9 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- */
 
 #include <efi.h>
 #include <efilib.h>
index c75e970b03d616559155e2ae38fa8489e8f84cfd..fd4be681a538b45ea187758e5611a6f98f401078 100644 (file)
@@ -10,7 +10,7 @@
  * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
  * associated EFI variables.
  */
-static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
+const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
 
 #ifdef __x86_64__
 UINT64 ticks_read(VOID) {
@@ -79,7 +79,7 @@ EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) {
         return EFI_INVALID_PARAMETER;
 }
 
-EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, VOID *buf, UINTN size, BOOLEAN persistent) {
         UINT32 flags;
 
         flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
@@ -90,7 +90,7 @@ EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINT
 }
 
 EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
-        return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
+        return efivar_set_raw(&loader_guid, name, value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
 }
 
 EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
index 8a3e0520fcd958ab90df7aec1bfeb2cffea82c6c..40ede66d4a90264cb5ba4c9ccf6763bd17fbb957 100644 (file)
@@ -5,6 +5,7 @@
 #include <efilib.h>
 
 #define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+#define OFFSETOF(x,y) ((UINTN) &(((x*)0)->y))
 
 static inline const CHAR16 *yes_no(BOOLEAN b) {
         return b ? L"yes" : L"no";
@@ -17,7 +18,7 @@ UINT64 ticks_freq(void);
 UINT64 time_usec(void);
 
 EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent);
-EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent);
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, VOID *buf, UINTN size, BOOLEAN persistent);
 EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent);
 VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec);
 
@@ -32,8 +33,22 @@ CHAR16 *stra_to_str(CHAR8 *stra);
 EFI_STATUS file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size);
 
 static inline void FreePoolp(void *p) {
-        FreePool(*(void**) p);
+        void *q = *(void**) p;
+
+        if (!q)
+                return;
+
+        FreePool(q);
 }
 
 #define _cleanup_(x) __attribute__((cleanup(x)))
 #define _cleanup_freepool_ _cleanup_(FreePoolp)
+
+static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
+        if (!*handle)
+                return;
+
+        uefi_call_wrapper((*handle)->Close, 1, *handle);
+}
+
+const EFI_GUID loader_guid;
index 824362eef9b82fb6a05662af1d622e8a750a1540..dd1b116e569a7caf8449d6cf4b1b4e2df3932487 100644 (file)
@@ -718,6 +718,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_rate_limit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_rate_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1067,6 +1069,12 @@ int bus_exec_context_set_transient_property(
         if (streq(name, "LogLevelMax"))
                 return bus_set_transient_log_level(u, name, &c->log_level_max, message, flags, error);
 
+        if (streq(name, "LogRateLimitIntervalUSec"))
+                return bus_set_transient_usec(u, name, &c->log_rate_limit_interval_usec, message, flags, error);
+
+        if (streq(name, "LogRateLimitBurst"))
+                return bus_set_transient_unsigned(u, name, &c->log_rate_limit_burst, message, flags, error);
+
         if (streq(name, "Personality"))
                 return bus_set_transient_personality(u, name, &c->personality, message, flags, error);
 
index 913cc74918f2a8e0661562fdf1c2b8cf17961118..4ea5b6c6e599e9f907350d3a287f7ec0e47f59db 100644 (file)
@@ -351,16 +351,27 @@ static int bus_socket_set_transient_property(
                 while ((r = sd_bus_message_read(message, "(ss)", &t, &a)) > 0) {
                         _cleanup_free_ SocketPort *p = NULL;
 
-                        p = new0(SocketPort, 1);
+                        p = new(SocketPort, 1);
                         if (!p)
                                 return log_oom();
 
+                        *p = (SocketPort) {
+                                .fd = -1,
+                                .socket = s,
+                        };
+
                         p->type = socket_port_type_from_string(t);
                         if (p->type < 0)
                                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown Socket type: %s", t);
 
                         if (p->type != SOCKET_SOCKET) {
+                                if (!path_is_valid(p->path))
+                                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid socket path: %s", t);
+
                                 p->path = strdup(a);
+                                if (!p->path)
+                                        return log_oom();
+
                                 path_simplify(p->path, false);
 
                         } else if (streq(t, "Netlink")) {
@@ -381,21 +392,10 @@ static int bus_socket_set_transient_property(
                                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Address family not supported: %s", a);
                         }
 
-                        p->fd = -1;
-                        p->auxiliary_fds = NULL;
-                        p->n_auxiliary_fds = 0;
-                        p->socket = s;
-
                         empty = false;
 
                         if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                                SocketPort *tail;
-
-                                LIST_FIND_TAIL(port, s->ports, tail);
-                                LIST_INSERT_AFTER(port, s->ports, tail, p);
-
-                                p = NULL;
-
+                                LIST_APPEND(port, s->ports, TAKE_PTR(p));
                                 unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Listen%s=%s", t, a);
                         }
                 }
index c4530f0c8b7c26516aaaed2fd18425953fe3b471..daec85a73b74b642dff8fbc12e99bbf3fcf84d49 100644 (file)
@@ -1299,8 +1299,43 @@ static int bus_unit_set_live_property(
         return 0;
 }
 
+static int bus_set_transient_emergency_action(
+                Unit *u,
+                const char *name,
+                EmergencyAction *p,
+                sd_bus_message *message,
+                UnitWriteFlags flags,
+                sd_bus_error *error) {
+
+        const char *s;
+        EmergencyAction v;
+        int r;
+        bool system;
+
+        assert(p);
+
+        r = sd_bus_message_read(message, "s", &s);
+        if (r < 0)
+                return r;
+
+        system = MANAGER_IS_SYSTEM(u->manager);
+        r = parse_emergency_action(s, system, &v);
+        if (v < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         v == -EOPNOTSUPP ? "EmergencyAction setting invalid for manager type: %s"
+                                                          : "Invalid %s setting: %s",
+                                         name, s);
+
+        if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                *p = v;
+                unit_write_settingf(u, flags, name,
+                                    "%s=%s", name, s);
+        }
+
+        return 1;
+}
+
 static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string);
-static BUS_DEFINE_SET_TRANSIENT_PARSE(emergency_action, EmergencyAction, emergency_action_from_string);
 static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string);
 
 static int bus_set_transient_conditions(
index bf5917696e0b9c5732095ba27c583e9a0d2515eb..3ffb53cb6e6c6ebd64f728780a21b24825e420fe 100644 (file)
@@ -974,12 +974,9 @@ int bus_init_system(Manager *m) {
 
 int bus_init_private(Manager *m) {
         _cleanup_close_ int fd = -1;
-        union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX
-        };
+        union sockaddr_union sa = {};
         sd_event_source *s;
-        socklen_t salen;
-        int r;
+        int r, salen;
 
         assert(m);
 
@@ -992,27 +989,24 @@ int bus_init_private(Manager *m) {
                 if (getpid_cached() != 1)
                         return 0;
 
-                strcpy(sa.un.sun_path, "/run/systemd/private");
-                salen = SOCKADDR_UN_LEN(sa.un);
+                salen = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
         } else {
-                size_t left = sizeof(sa.un.sun_path);
-                char *p = sa.un.sun_path;
-                const char *e;
+                const char *e, *joined;
 
                 e = secure_getenv("XDG_RUNTIME_DIR");
                 if (!e) {
-                        log_error("Failed to determine XDG_RUNTIME_DIR");
+                        log_error("XDG_RUNTIME_DIR is not set, refusing.");
                         return -EHOSTDOWN;
                 }
 
-                left = strpcpy(&p, left, e);
-                left = strpcpy(&p, left, "/systemd/private");
-
-                salen = sizeof(sa.un) - left;
+                joined = strjoina(e, "/systemd/private");
+                salen = sockaddr_un_set_path(&sa.un, joined);
         }
+        if (salen < 0)
+                return log_error_errno(salen, "Can't set path for AF_UNIX socket to bind to: %m");
 
         (void) mkdir_parents_label(sa.un.sun_path, 0755);
-        (void) unlink(sa.un.sun_path);
+        (void) sockaddr_un_unlink(&sa.un);
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
         if (fd < 0)
@@ -1035,9 +1029,8 @@ int bus_init_private(Manager *m) {
 
         (void) sd_event_source_set_description(s, "bus-connection");
 
-        m->private_listen_fd = fd;
+        m->private_listen_fd = TAKE_FD(fd);
         m->private_listen_event_source = s;
-        fd = -1;
 
         log_debug("Successfully created private D-Bus server.");
 
index 6f0b0a8f338a050ff7528355bd609b5e4425d497..c91d2bf5ff5a2e770391ea3cd3727afddfb023a9 100644 (file)
 #include "special.h"
 #include "string-table.h"
 #include "terminal-util.h"
-
-static void log_and_status(Manager *m, const char *message, const char *reason) {
-        log_warning("%s: %s", message, reason);
-        manager_status_printf(m, STATUS_TYPE_EMERGENCY,
-                              ANSI_HIGHLIGHT_RED "  !!  " ANSI_NORMAL,
-                              "%s: %s", message, reason);
+#include "virt.h"
+
+static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
+        log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
+        if (warn)
+                manager_status_printf(m, STATUS_TYPE_EMERGENCY,
+                                      ANSI_HIGHLIGHT_RED "  !!  " ANSI_NORMAL,
+                                      "%s: %s", message, reason);
 }
 
 int emergency_action(
                 Manager *m,
                 EmergencyAction action,
+                EmergencyActionFlags options,
                 const char *reboot_arg,
                 const char *reason) {
 
@@ -31,24 +34,17 @@ int emergency_action(
         if (action == EMERGENCY_ACTION_NONE)
                 return -ECANCELED;
 
-        if (!m->service_watchdogs) {
+        if (FLAGS_SET(options, EMERGENCY_ACTION_IS_WATCHDOG) && !m->service_watchdogs) {
                 log_warning("Watchdog disabled! Not acting on: %s", reason);
                 return -ECANCELED;
         }
 
-        if (!MANAGER_IS_SYSTEM(m)) {
-                /* Downgrade all options to simply exiting if we run
-                 * in user mode */
-
-                log_warning("Exiting: %s", reason);
-                m->objective = MANAGER_EXIT;
-                return -ECANCELED;
-        }
+        bool warn = FLAGS_SET(options, EMERGENCY_ACTION_WARN);
 
         switch (action) {
 
         case EMERGENCY_ACTION_REBOOT:
-                log_and_status(m, "Rebooting", reason);
+                log_and_status(m, warn, "Rebooting", reason);
 
                 (void) update_reboot_parameter_and_warn(reboot_arg);
                 (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
@@ -56,7 +52,7 @@ int emergency_action(
                 break;
 
         case EMERGENCY_ACTION_REBOOT_FORCE:
-                log_and_status(m, "Forcibly rebooting", reason);
+                log_and_status(m, warn, "Forcibly rebooting", reason);
 
                 (void) update_reboot_parameter_and_warn(reboot_arg);
                 m->objective = MANAGER_REBOOT;
@@ -64,7 +60,7 @@ int emergency_action(
                 break;
 
         case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
-                log_and_status(m, "Rebooting immediately", reason);
+                log_and_status(m, warn, "Rebooting immediately", reason);
 
                 sync();
 
@@ -78,18 +74,38 @@ int emergency_action(
                 (void) reboot(RB_AUTOBOOT);
                 break;
 
+        case EMERGENCY_ACTION_EXIT:
+                if (MANAGER_IS_USER(m) || detect_container() > 0) {
+                        log_and_status(m, warn, "Exiting", reason);
+                        (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
+                        break;
+                }
+
+                log_notice("Doing \"poweroff\" action instead of an \"exit\" emergency action.");
+                _fallthrough_;
+
         case EMERGENCY_ACTION_POWEROFF:
-                log_and_status(m, "Powering off", reason);
+                log_and_status(m, warn, "Powering off", reason);
                 (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
                 break;
 
+        case EMERGENCY_ACTION_EXIT_FORCE:
+                if (MANAGER_IS_USER(m) || detect_container() > 0) {
+                        log_and_status(m, warn, "Exiting immediately", reason);
+                        m->objective = MANAGER_EXIT;
+                        break;
+                }
+
+                log_notice("Doing \"poweroff-force\" action instead of an \"exit-force\" emergency action.");
+                _fallthrough_;
+
         case EMERGENCY_ACTION_POWEROFF_FORCE:
-                log_and_status(m, "Forcibly powering off", reason);
+                log_and_status(m, warn, "Forcibly powering off", reason);
                 m->objective = MANAGER_POWEROFF;
                 break;
 
         case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
-                log_and_status(m, "Powering off immediately", reason);
+                log_and_status(m, warn, "Powering off immediately", reason);
 
                 sync();
 
@@ -111,6 +127,26 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
         [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
         [EMERGENCY_ACTION_POWEROFF] = "poweroff",
         [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
-        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate"
+        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
+        [EMERGENCY_ACTION_EXIT] = "exit",
+        [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
 };
 DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
+
+int parse_emergency_action(
+                const char *value,
+                bool system,
+                EmergencyAction *ret) {
+
+        EmergencyAction x;
+
+        x = emergency_action_from_string(value);
+        if (x < 0)
+                return -EINVAL;
+
+        if (!system && x != EMERGENCY_ACTION_NONE && x < _EMERGENCY_ACTION_FIRST_USER_ACTION)
+                return -EOPNOTSUPP;
+
+        *ret = x;
+        return 0;
+}
index ea6590e78a83beda7f1c39e17f68f51a3353faee..1fc4a11e306818bd4bdc4f3f7e067613837c0bdf 100644 (file)
@@ -9,14 +9,26 @@ typedef enum EmergencyAction {
         EMERGENCY_ACTION_POWEROFF,
         EMERGENCY_ACTION_POWEROFF_FORCE,
         EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
+        EMERGENCY_ACTION_EXIT,
+        _EMERGENCY_ACTION_FIRST_USER_ACTION = EMERGENCY_ACTION_EXIT,
+        EMERGENCY_ACTION_EXIT_FORCE,
         _EMERGENCY_ACTION_MAX,
         _EMERGENCY_ACTION_INVALID = -1
 } EmergencyAction;
 
+typedef enum EmergencyActionFlags {
+        EMERGENCY_ACTION_IS_WATCHDOG = 1 << 0,
+        EMERGENCY_ACTION_WARN        = 1 << 1,
+} EmergencyActionFlags;
+
 #include "macro.h"
 #include "manager.h"
 
-int emergency_action(Manager *m, EmergencyAction action, const char *reboot_arg, const char *reason);
+int emergency_action(Manager *m,
+                     EmergencyAction action, EmergencyActionFlags options,
+                     const char *reboot_arg, const char *reason);
 
 const char* emergency_action_to_string(EmergencyAction i) _const_;
 EmergencyAction emergency_action_from_string(const char *s) _pure_;
+
+int parse_emergency_action(const char *value, bool system, EmergencyAction *ret);
index 0ef5d2d212f50aa776b0374bba7647f6017a47c8..60c4c0375eb9afd8b79ea6a1136bae58e0aceeaa 100644 (file)
@@ -379,10 +379,9 @@ static int open_terminal_as(const char *path, int flags, int nfd) {
 }
 
 static int acquire_path(const char *path, int flags, mode_t mode) {
-        union sockaddr_union sa = {
-                .sa.sa_family = AF_UNIX,
-        };
-        int fd, r;
+        union sockaddr_union sa = {};
+        _cleanup_close_ int fd = -1;
+        int r, salen;
 
         assert(path);
 
@@ -391,11 +390,11 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
 
         fd = open(path, flags|O_NOCTTY, mode);
         if (fd >= 0)
-                return fd;
+                return TAKE_FD(fd);
 
         if (errno != ENXIO) /* ENXIO is returned when we try to open() an AF_UNIX file system socket on Linux */
                 return -errno;
-        if (strlen(path) > sizeof(sa.un.sun_path)) /* Too long, can't be a UNIX socket */
+        if (strlen(path) >= sizeof(sa.un.sun_path)) /* Too long, can't be a UNIX socket */
                 return -ENXIO;
 
         /* So, it appears the specified path could be an AF_UNIX socket. Let's see if we can connect to it. */
@@ -404,25 +403,24 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
         if (fd < 0)
                 return -errno;
 
-        strncpy(sa.un.sun_path, path, sizeof(sa.un.sun_path));
-        if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
-                safe_close(fd);
+        salen = sockaddr_un_set_path(&sa.un, path);
+        if (salen < 0)
+                return salen;
+
+        if (connect(fd, &sa.sa, salen) < 0)
                 return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have
                                                            * indication that his wasn't an AF_UNIX socket after all */
-        }
 
         if ((flags & O_ACCMODE) == O_RDONLY)
                 r = shutdown(fd, SHUT_WR);
         else if ((flags & O_ACCMODE) == O_WRONLY)
                 r = shutdown(fd, SHUT_RD);
         else
-                return fd;
-        if (r < 0) {
-                safe_close(fd);
+                return TAKE_FD(fd);
+        if (r < 0)
                 return -errno;
-        }
 
-        return fd;
+        return TAKE_FD(fd);
 }
 
 static int fixup_input(
@@ -3748,6 +3746,9 @@ void exec_context_done(ExecContext *c) {
 
         exec_context_free_log_extra_fields(c);
 
+        c->log_rate_limit_interval_usec = 0;
+        c->log_rate_limit_burst = 0;
+
         c->stdin_data = mfree(c->stdin_data);
         c->stdin_data_size = 0;
 }
@@ -4229,6 +4230,17 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
         }
 
+        if (c->log_rate_limit_interval_usec > 0) {
+                char buf_timespan[FORMAT_TIMESPAN_MAX];
+
+                fprintf(f,
+                        "%sLogRateLimitIntervalSec: %s\n",
+                        prefix, format_timespan(buf_timespan, sizeof(buf_timespan), c->log_rate_limit_interval_usec, USEC_PER_SEC));
+        }
+
+        if (c->log_rate_limit_burst > 0)
+                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_rate_limit_burst);
+
         if (c->n_log_extra_fields > 0) {
                 size_t j;
 
@@ -5075,10 +5087,8 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
 finalize:
 
         r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, (int[]) { fd0, fd1 }, NULL);
-        if (r < 0) {
+        if (r < 0)
                 log_debug_errno(r, "Failed to add exec-runtime: %m");
-                return;
-        }
 }
 
 void exec_runtime_vacuum(Manager *m) {
index 03a36e608cdbd537fc091fa208b87d096b07b70c..a2ed477ebf960312fe04f2b3c0bdb9ef54e1aedd 100644 (file)
@@ -225,6 +225,9 @@ struct ExecContext {
         struct iovec* log_extra_fields;
         size_t n_log_extra_fields;
 
+        usec_t log_rate_limit_interval_usec;
+        unsigned log_rate_limit_burst;
+
         bool cpu_sched_reset_on_fork;
         bool non_blocking;
         bool private_tmp;
index 6b17cc1d6f6da22014d720e601cfd9bf5b4f3539..342997ce00c12d707b198405033f98d67fb84817 100644 (file)
@@ -31,14 +31,15 @@ Job* job_new_raw(Unit *unit) {
 
         assert(unit);
 
-        j = new0(Job, 1);
+        j = new(Job, 1);
         if (!j)
                 return NULL;
 
-        j->manager = unit->manager;
-        j->unit = unit;
-        j->type = _JOB_TYPE_INVALID;
-        j->reloaded = false;
+        *j = (Job) {
+                .manager = unit->manager,
+                .unit = unit,
+                .type = _JOB_TYPE_INVALID,
+        };
 
         return j;
 }
@@ -973,7 +974,9 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
         u = j->unit;
         job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
 
-        emergency_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg, "job timed out");
+        emergency_action(u->manager, u->job_timeout_action,
+                         EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+                         u->job_timeout_reboot_arg, "job timed out");
 
         return 0;
 }
@@ -1122,16 +1125,16 @@ int job_deserialize(Job *j, FILE *f) {
                 if (streq(l, "job-id")) {
 
                         if (safe_atou32(v, &j->id) < 0)
-                                log_debug("Failed to parse job id value %s", v);
+                                log_debug("Failed to parse job id value: %s", v);
 
                 } else if (streq(l, "job-type")) {
                         JobType t;
 
                         t = job_type_from_string(v);
                         if (t < 0)
-                                log_debug("Failed to parse job type %s", v);
+                                log_debug("Failed to parse job type: %s", v);
                         else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
-                                log_debug("Cannot deserialize job of type %s", v);
+                                log_debug("Cannot deserialize job of type: %s", v);
                         else
                                 j->type = t;
 
@@ -1140,7 +1143,7 @@ int job_deserialize(Job *j, FILE *f) {
 
                         s = job_state_from_string(v);
                         if (s < 0)
-                                log_debug("Failed to parse job state %s", v);
+                                log_debug("Failed to parse job state: %s", v);
                         else
                                 job_set_state(j, s);
 
@@ -1149,7 +1152,7 @@ int job_deserialize(Job *j, FILE *f) {
 
                         b = parse_boolean(v);
                         if (b < 0)
-                                log_debug("Failed to parse job irreversible flag %s", v);
+                                log_debug("Failed to parse job irreversible flag: %s", v);
                         else
                                 j->irreversible = j->irreversible || b;
 
@@ -1158,7 +1161,7 @@ int job_deserialize(Job *j, FILE *f) {
 
                         b = parse_boolean(v);
                         if (b < 0)
-                                log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
+                                log_debug("Failed to parse job sent_dbus_new_signal flag: %s", v);
                         else
                                 j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
 
@@ -1167,7 +1170,7 @@ int job_deserialize(Job *j, FILE *f) {
 
                         b = parse_boolean(v);
                         if (b < 0)
-                                log_debug("Failed to parse job ignore_order flag %s", v);
+                                log_debug("Failed to parse job ignore_order flag: %s", v);
                         else
                                 j->ignore_order = j->ignore_order || b;
 
index 7410d4709cae512cefeae0c0d0fe7021388a7f24..869ea81a9e555e3bfd69c4634ecdaee4dfb950e0 100644 (file)
@@ -57,6 +57,8 @@ $1.SyslogFacility,               config_parse_log_facility,          0,
 $1.SyslogLevel,                  config_parse_log_level,             0,                             offsetof($1, exec_context.syslog_priority)
 $1.SyslogLevelPrefix,            config_parse_bool,                  0,                             offsetof($1, exec_context.syslog_level_prefix)
 $1.LogLevelMax,                  config_parse_log_level,             0,                             offsetof($1, exec_context.log_level_max)
+$1.LogRateLimitIntervalSec,      config_parse_sec,                   0,                             offsetof($1, exec_context.log_rate_limit_interval_usec)
+$1.LogRateLimitBurst,            config_parse_unsigned,              0,                             offsetof($1, exec_context.log_rate_limit_burst)
 $1.LogExtraFields,               config_parse_log_extra_fields,      0,                             offsetof($1, exec_context)
 $1.Capabilities,                 config_parse_warn_compat,           DISABLED_LEGACY,               offsetof($1, exec_context)
 $1.SecureBits,                   config_parse_exec_secure_bits,      0,                             offsetof($1, exec_context.secure_bits)
index f96775ee7195c739f8830983a88117236904e7e5..bfd1d5b15f09bd2d0865584c77c86866ba80905c 100644 (file)
@@ -76,7 +76,6 @@ DEFINE_CONFIG_PARSE(config_parse_socket_protocol, supported_socket_protocol_from
 DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
@@ -4185,6 +4184,57 @@ int config_parse_job_running_timeout_sec(
         return 0;
 }
 
+int config_parse_emergency_action(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Manager *m = NULL;
+        EmergencyAction *x = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (unit)
+                m = ((Unit*) userdata)->manager;
+        else
+                m = data;
+
+        r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
+        if (r < 0) {
+                if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
+                        /* Compat mode: remove for systemd 241. */
+
+                        log_syntax(unit, LOG_INFO, filename, line, r,
+                                   "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
+                                   lvalue, rvalue);
+                        *x = EMERGENCY_ACTION_EXIT_FORCE;
+                        return 0;
+                }
+
+                if (r == -EOPNOTSUPP)
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "%s= specified as %s mode action, ignoring: %s",
+                                   lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
+                else
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
 #define FOLLOW_MAX 8
 
 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
index 01f88827fe682283c95011b0c96fb582040ec757..1a95486c03c9cef9ac0ce45fe417c1570ab488a4 100644 (file)
@@ -73,6 +73,7 @@
 #include "stdio-util.h"
 #include "strv.h"
 #include "switch-root.h"
+#include "sysctl-util.h"
 #include "terminal-util.h"
 #include "umask-util.h"
 #include "user-util.h"
@@ -1162,18 +1163,102 @@ static int prepare_reexecute(
         return 0;
 }
 
+static void bump_file_max_and_nr_open(void) {
+
+        /* Let's bump fs.file-max and fs.nr_open to their respective maximums. On current kernels large numbers of file
+         * descriptors are no longer a performance problem and their memory is properly tracked by memcg, thus counting
+         * them and limiting them in another two layers of limits is unnecessary and just complicates things. This
+         * function hence turns off 2 of the 4 levels of limits on file descriptors, and makes RLIMIT_NOLIMIT (soft +
+         * hard) the only ones that really matter. */
+
+#if BUMP_PROC_SYS_FS_FILE_MAX || BUMP_PROC_SYS_FS_NR_OPEN
+        _cleanup_free_ char *t = NULL;
+        int r;
+#endif
+
+#if BUMP_PROC_SYS_FS_FILE_MAX
+        /* I so wanted to use STRINGIFY(ULONG_MAX) here, but alas we can't as glibc/gcc define that as
+         * "(0x7fffffffffffffffL * 2UL + 1UL)". Seriously. 😢 */
+        if (asprintf(&t, "%lu\n", ULONG_MAX) < 0) {
+                log_oom();
+                return;
+        }
+
+        r = sysctl_write("fs/file-max", t);
+        if (r < 0)
+                log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.file-max, ignoring: %m");
+#endif
+
+#if BUMP_PROC_SYS_FS_FILE_MAX && BUMP_PROC_SYS_FS_NR_OPEN
+        t = mfree(t);
+#endif
+
+#if BUMP_PROC_SYS_FS_NR_OPEN
+        int v = INT_MAX;
+
+        /* Arg! The kernel enforces maximum and minimum values on the fs.nr_open, but we don't really know what they
+         * are. The expression by which the maximum is determined is dependent on the architecture, and is something we
+         * don't really want to copy to userspace, as it is dependent on implementation details of the kernel. Since
+         * the kernel doesn't expose the maximum value to us, we can only try and hope. Hence, let's start with
+         * INT_MAX, and then keep halving the value until we find one that works. Ugly? Yes, absolutely, but kernel
+         * APIs are kernel APIs, so what do can we do... 🤯 */
+
+        for (;;) {
+                int k;
+
+                v &= ~(__SIZEOF_POINTER__ - 1); /* Round down to next multiple of the pointer size */
+                if (v < 1024) {
+                        log_warning("Can't bump fs.nr_open, value too small.");
+                        break;
+                }
+
+                k = read_nr_open();
+                if (k < 0) {
+                        log_error_errno(k, "Failed to read fs.nr_open: %m");
+                        break;
+                }
+                if (k >= v) { /* Already larger */
+                        log_debug("Skipping bump, value is already larger.");
+                        break;
+                }
+
+                if (asprintf(&t, "%i\n", v) < 0) {
+                        log_oom();
+                        return;
+                }
+
+                r = sysctl_write("fs/nr_open", t);
+                t = mfree(t);
+                if (r == -EINVAL) {
+                        log_debug("Couldn't write fs.nr_open as %i, halving it.", v);
+                        v /= 2;
+                        continue;
+                }
+                if (r < 0) {
+                        log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.nr_open, ignoring: %m");
+                        break;
+                }
+
+                log_debug("Successfully bumped fs.nr_open to %i", v);
+                break;
+        }
+#endif
+}
+
 static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
         int r, nr;
 
         assert(saved_rlimit);
 
-        /* Save the original RLIMIT_NOFILE so that we can reset it
-         * later when transitioning from the initrd to the main
+        /* Save the original RLIMIT_NOFILE so that we can reset it later when transitioning from the initrd to the main
          * systemd or suchlike. */
         if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0)
                 return log_warning_errno(errno, "Reading RLIMIT_NOFILE failed, ignoring: %m");
 
-        /* Make sure forked processes get the default kernel setting */
+        /* Get the underlying absolute limit the kernel enforces */
+        nr = read_nr_open();
+
+        /* Make sure forked processes get limits based on the original kernel setting */
         if (!arg_default_rlimit[RLIMIT_NOFILE]) {
                 struct rlimit *rl;
 
@@ -1181,11 +1266,25 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
                 if (!rl)
                         return log_oom();
 
+                /* Bump the hard limit for system services to a substantially higher value. The default hard limit
+                 * current kernels set is pretty low (4K), mostly for historical reasons. According to kernel
+                 * developers, the fd handling in recent kernels has been optimized substantially enough, so that we
+                 * can bump the limit now, without paying too high a price in memory or performance. Note however that
+                 * we only bump the hard limit, not the soft limit. That's because select() works the way it works, and
+                 * chokes on fds >= 1024. If we'd bump the soft limit globally, it might accidentally happen to
+                 * unexpecting programs that they get fds higher than what they can process using select(). By only
+                 * bumping the hard limit but leaving the low limit as it is we avoid this pitfall: programs that are
+                 * written by folks aware of the select() problem in mind (and thus use poll()/epoll instead of
+                 * select(), the way everybody should) can explicitly opt into high fds by bumping their soft limit
+                 * beyond 1024, to the hard limit we pass. */
+                if (arg_system)
+                        rl->rlim_max = MIN((rlim_t) nr, MAX(rl->rlim_max, (rlim_t) HIGH_RLIMIT_NOFILE));
+
                 arg_default_rlimit[RLIMIT_NOFILE] = rl;
         }
 
-        /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows */
-        nr = read_nr_open();
+        /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows, for
+         * both hard and soft. */
         r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(nr));
         if (r < 0)
                 return log_warning_errno(r, "Setting RLIMIT_NOFILE failed, ignoring: %m");
@@ -1197,16 +1296,15 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) {
         int r;
 
         assert(saved_rlimit);
-        assert(getuid() == 0);
 
-        /* BPF_MAP_TYPE_LPM_TRIE bpf maps are charged against RLIMIT_MEMLOCK, even though we have CAP_IPC_LOCK which
-         * should normally disable such checks. We need them to implement IPAccessAllow= and IPAccessDeny=, hence let's
-         * bump the value high enough for the root user. */
+        /* BPF_MAP_TYPE_LPM_TRIE bpf maps are charged against RLIMIT_MEMLOCK, even if we have CAP_IPC_LOCK which should
+         * normally disable such checks. We need them to implement IPAccessAllow= and IPAccessDeny=, hence let's bump
+         * the value high enough for our user. */
 
         if (getrlimit(RLIMIT_MEMLOCK, saved_rlimit) < 0)
                 return log_warning_errno(errno, "Reading RLIMIT_MEMLOCK failed, ignoring: %m");
 
-        r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(1024ULL*1024ULL*64ULL));
+        r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(HIGH_RLIMIT_MEMLOCK));
         if (r < 0)
                 return log_warning_errno(r, "Setting RLIMIT_MEMLOCK failed, ignoring: %m");
 
@@ -1868,6 +1966,7 @@ static int initialize_runtime(
                         machine_id_setup(NULL, arg_machine_id, NULL);
                         loopback_setup();
                         bump_unix_max_dgram_qlen();
+                        bump_file_max_and_nr_open();
                         test_usr();
                         write_container_id();
                 }
@@ -1920,11 +2019,9 @@ static int initialize_runtime(
                 if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
                         log_warning_errno(errno, "Failed to make us a subreaper: %m");
 
-        if (arg_system) {
-                /* Bump up RLIMIT_NOFILE for systemd itself */
-                (void) bump_rlimit_nofile(saved_rlimit_nofile);
-                (void) bump_rlimit_memlock(saved_rlimit_memlock);
-        }
+        /* Bump up RLIMIT_NOFILE for systemd itself */
+        (void) bump_rlimit_nofile(saved_rlimit_nofile);
+        (void) bump_rlimit_memlock(saved_rlimit_memlock);
 
         return 0;
 }
index 971faef90a74641243008f3b1340b4cd9ba0758a..62703e1d7f435c6b719752b87e95163121cb9611 100644 (file)
@@ -872,10 +872,8 @@ static int manager_setup_notify(Manager *m) {
 
         if (m->notify_fd < 0) {
                 _cleanup_close_ int fd = -1;
-                union sockaddr_union sa = {
-                        .sa.sa_family = AF_UNIX,
-                };
-                static const int one = 1;
+                union sockaddr_union sa = {};
+                int salen;
 
                 /* First free all secondary fields */
                 m->notify_socket = mfree(m->notify_socket);
@@ -891,15 +889,18 @@ static int manager_setup_notify(Manager *m) {
                 if (!m->notify_socket)
                         return log_oom();
 
+                salen = sockaddr_un_set_path(&sa.un, m->notify_socket);
+                if (salen < 0)
+                        return log_error_errno(salen, "Notify socket '%s' not valid for AF_UNIX socket address, refusing.", m->notify_socket);
+
                 (void) mkdir_parents_label(m->notify_socket, 0755);
-                (void) unlink(m->notify_socket);
+                (void) sockaddr_un_unlink(&sa.un);
 
-                strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path));
-                r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+                r = bind(fd, &sa.sa, salen);
                 if (r < 0)
-                        return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
+                        return log_error_errno(errno, "bind(%s) failed: %m", m->notify_socket);
 
-                r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+                r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         return log_error_errno(errno, "SO_PASSCRED failed: %m");
 
@@ -970,7 +971,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
 
                 fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
 
-                (void) unlink(sa.un.sun_path);
+                (void) sockaddr_un_unlink(&sa.un);
 
                 /* Only allow root to connect to this socket */
                 RUN_WITH_UMASK(0077)
@@ -978,8 +979,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
                 if (r < 0)
                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
 
-                m->cgroups_agent_fd = fd;
-                fd = -1;
+                m->cgroups_agent_fd = TAKE_FD(fd);
         }
 
         if (!m->cgroups_agent_event_source) {
@@ -2540,7 +2540,7 @@ static void manager_handle_ctrl_alt_del(Manager *m) {
         if (ratelimit_below(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE)
                 manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
         else
-                emergency_action(m, m->cad_burst_action, NULL,
+                emergency_action(m, m->cad_burst_action, EMERGENCY_ACTION_WARN, NULL,
                                 "Ctrl-Alt-Del was pressed more than 7 times within 2s");
 }
 
index 3d065bf487ce9821faf1618adf629d8323b138c0..660536837e4667b787e89aed1566cd597cd6e97b 100644 (file)
@@ -538,8 +538,13 @@ static int service_verify(Service *s) {
         if (UNIT(s)->load_state != UNIT_LOADED)
                 return 0;
 
-        if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
-                log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing.");
+        if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
+            && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+                /* FailureAction= only makes sense if one of the start or stop commands is specified.
+                 * SuccessAction= will be executed unconditionally if no commands are specified. Hence,
+                 * either a command or SuccessAction= are required. */
+
+                log_unit_error(UNIT(s), "Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing.");
                 return -ENOEXEC;
         }
 
@@ -548,8 +553,8 @@ static int service_verify(Service *s) {
                 return -ENOEXEC;
         }
 
-        if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
-                log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.");
+        if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START] && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+                log_unit_error(UNIT(s), "Service has no ExecStart= and no SuccessAction= settings and does not have RemainAfterExit=yes set. Refusing.");
                 return -ENOEXEC;
         }
 
@@ -2024,6 +2029,12 @@ static void service_enter_start(Service *s) {
                         goto fail;
                 }
 
+                /* We force a fake state transition here. Otherwise, the unit would go directly from
+                 * SERVICE_DEAD to SERVICE_DEAD without SERVICE_ACTIVATING or SERVICE_ACTIVE
+                 * inbetween. This way we can later trigger actions that depend on the state
+                 * transition, including SuccessAction=. */
+                service_set_state(s, SERVICE_START);
+
                 service_enter_start_post(s);
                 return;
         }
@@ -2601,10 +2612,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char
                 _cleanup_free_ char *arg = NULL;
 
                 r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
+                if (r < 0)
+                        return r;
                 if (r == 0)
                         break;
-                else if (r < 0)
-                        return r;
 
                 switch (state) {
                 case STATE_EXEC_COMMAND_TYPE:
@@ -2744,10 +2755,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                 r = cunescape(value, 0, &t);
                 if (r < 0)
                         log_unit_debug_errno(u, r, "Failed to unescape status text: %s", value);
-                else {
-                        free(s->status_text);
-                        s->status_text = t;
-                }
+                else
+                        free_and_replace(s->status_text, t);
 
         } else if (streq(key, "accept-socket")) {
                 Unit *socket;
@@ -3648,7 +3657,11 @@ static void service_notify_message(
                         }
                         if (r > 0) {
                                 service_set_main_pid(s, new_main_pid);
-                                unit_watch_pid(UNIT(s), new_main_pid);
+
+                                r = unit_watch_pid(UNIT(s), new_main_pid);
+                                if (r < 0)
+                                        log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", new_main_pid);
+
                                 notify_dbus = true;
                         }
                 }
index 649904d4062715ec7c69619867588d267622efc7..f0f6a83a19dcaad2bfcb11903a9b5acd21aecbbc 100644 (file)
@@ -496,11 +496,7 @@ static int peer_address_compare_func(const void *a, const void *b) {
         case AF_INET6:
                 return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr));
         case AF_VSOCK:
-                if (x->peer.vm.svm_cid < y->peer.vm.svm_cid)
-                        return -1;
-                if (x->peer.vm.svm_cid > y->peer.vm.svm_cid)
-                        return 1;
-                return 0;
+                return CMP(x->peer.vm.svm_cid, y->peer.vm.svm_cid);
         }
         assert_not_reached("Black sheep in the family!");
 }
@@ -1018,8 +1014,7 @@ static void socket_apply_socket_options(Socket *s, int fd) {
         assert(fd >= 0);
 
         if (s->keep_alive) {
-                int one = 1;
-                if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_one, sizeof(const_int_one)) < 0)
                         log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m");
         }
 
@@ -1048,32 +1043,27 @@ static void socket_apply_socket_options(Socket *s, int fd) {
         }
 
         if (s->no_delay) {
-                int one = 1;
-
                 if (s->socket_protocol == IPPROTO_SCTP) {
-                        if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &one, sizeof(one)) < 0)
+                        if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &const_int_one, sizeof(const_int_one)) < 0)
                                 log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m");
                 } else {
-                        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
+                        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &const_int_one, sizeof(const_int_one)) < 0)
                                 log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
                 }
         }
 
         if (s->broadcast) {
-                int one = 1;
-                if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_one, sizeof(const_int_one)) < 0)
                         log_unit_warning_errno(UNIT(s), errno, "SO_BROADCAST failed: %m");
         }
 
         if (s->pass_cred) {
-                int one = 1;
-                if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one)) < 0)
                         log_unit_warning_errno(UNIT(s), errno, "SO_PASSCRED failed: %m");
         }
 
         if (s->pass_sec) {
-                int one = 1;
-                if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &const_int_one, sizeof(const_int_one)) < 0)
                         log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m");
         }
 
index 639b5818ffb5e94f9e20790f32b96948351f028b..ef1bbbd948f652c7f53d49c6e34a7f07daf00632 100644 (file)
@@ -53,7 +53,7 @@
 #DefaultLimitSTACK=
 #DefaultLimitCORE=
 #DefaultLimitRSS=
-#DefaultLimitNOFILE=
+#DefaultLimitNOFILE=1024:@HIGH_RLIMIT_NOFILE@
 #DefaultLimitAS=
 #DefaultLimitNPROC=
 #DefaultLimitMEMLOCK=
index 045930838b19e7d901a440751e8a2c04dbc13a2f..486c6a4a05b35e8144961de523aa2f88ef203e38 100644 (file)
@@ -526,7 +526,9 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro
                 if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
                     job_type_is_conflicting(j->unit->job->type, j->type))
                         return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
-                                                 "Transaction is destructive.");
+                                                 "Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
+                                                 tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
+                                                 j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
         }
 
         return 0;
index 1b9bbf70577854cc3a63d930145f036b249df87f..97807aab4b72360179fbd6144dce83fcf0f2567f 100644 (file)
@@ -1724,7 +1724,9 @@ int unit_start_limit_test(Unit *u) {
         log_unit_warning(u, "Start request repeated too quickly.");
         u->start_limit_hit = true;
 
-        return emergency_action(u->manager, u->start_limit_action, u->reboot_arg, "unit failed");
+        return emergency_action(u->manager, u->start_limit_action,
+                                EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+                                u->reboot_arg, "unit failed");
 }
 
 bool unit_shall_confirm_spawn(Unit *u) {
@@ -2000,7 +2002,7 @@ bool unit_is_unneeded(Unit *u) {
                  * restart, then don't clean this one up. */
 
                 HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i) {
-                        if (u->job)
+                        if (other->job)
                                 return false;
 
                         if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
@@ -2498,9 +2500,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
                 unit_check_binds_to(u);
 
                 if (os != UNIT_FAILED && ns == UNIT_FAILED)
-                        (void) emergency_action(u->manager, u->failure_action, u->reboot_arg, "unit failed");
+                        (void) emergency_action(u->manager, u->failure_action, 0,
+                                                u->reboot_arg, "unit failed");
                 else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE)
-                        (void) emergency_action(u->manager, u->success_action, u->reboot_arg, "unit succeeded");
+                        (void) emergency_action(u->manager, u->success_action, 0,
+                                                u->reboot_arg, "unit succeeded");
         }
 
         unit_add_to_dbus_queue(u);
@@ -3245,6 +3249,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
         unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id));
         unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max));
         unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields));
+        unit_serialize_item(u, f, "exported-log-rate-limit-interval", yes_no(u->exported_log_rate_limit_interval));
+        unit_serialize_item(u, f, "exported-log-rate-limit-burst", yes_no(u->exported_log_rate_limit_burst));
 
         unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
         if (u->cpu_usage_last != NSEC_INFINITY)
@@ -3520,6 +3526,26 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
 
                         continue;
 
+                } else if (streq(l, "exported-log-rate-limit-interval")) {
+
+                        r = parse_boolean(v);
+                        if (r < 0)
+                                log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v);
+                        else
+                                u->exported_log_rate_limit_interval = r;
+
+                        continue;
+
+                } else if (streq(l, "exported-log-rate-limit-burst")) {
+
+                        r = parse_boolean(v);
+                        if (r < 0)
+                                log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v);
+                        else
+                                u->exported_log_rate_limit_burst = r;
+
+                        continue;
+
                 } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
 
                         r = safe_atou64(v, &u->cpu_usage_base);
@@ -5269,6 +5295,60 @@ fail:
         return r;
 }
 
+static int unit_export_log_rate_limit_interval(Unit *u, const ExecContext *c) {
+        _cleanup_free_ char *buf = NULL;
+        const char *p;
+        int r;
+
+        assert(u);
+        assert(c);
+
+        if (u->exported_log_rate_limit_interval)
+                return 0;
+
+        if (c->log_rate_limit_interval_usec == 0)
+                return 0;
+
+        p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
+
+        if (asprintf(&buf, "%" PRIu64, c->log_rate_limit_interval_usec) < 0)
+                return log_oom();
+
+        r = symlink_atomic(buf, p);
+        if (r < 0)
+                return log_unit_debug_errno(u, r, "Failed to create log rate limit interval symlink %s: %m", p);
+
+        u->exported_log_rate_limit_interval = true;
+        return 0;
+}
+
+static int unit_export_log_rate_limit_burst(Unit *u, const ExecContext *c) {
+        _cleanup_free_ char *buf = NULL;
+        const char *p;
+        int r;
+
+        assert(u);
+        assert(c);
+
+        if (u->exported_log_rate_limit_burst)
+                return 0;
+
+        if (c->log_rate_limit_burst == 0)
+                return 0;
+
+        p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
+
+        if (asprintf(&buf, "%u", c->log_rate_limit_burst) < 0)
+                return log_oom();
+
+        r = symlink_atomic(buf, p);
+        if (r < 0)
+                return log_unit_debug_errno(u, r, "Failed to create log rate limit burst symlink %s: %m", p);
+
+        u->exported_log_rate_limit_burst = true;
+        return 0;
+}
+
 void unit_export_state_files(Unit *u) {
         const ExecContext *c;
 
@@ -5302,6 +5382,8 @@ void unit_export_state_files(Unit *u) {
         if (c) {
                 (void) unit_export_log_level_max(u, c);
                 (void) unit_export_log_extra_fields(u, c);
+                (void) unit_export_log_rate_limit_interval(u, c);
+                (void) unit_export_log_rate_limit_burst(u, c);
         }
 }
 
@@ -5338,6 +5420,20 @@ void unit_unlink_state_files(Unit *u) {
 
                 u->exported_log_extra_fields = false;
         }
+
+        if (u->exported_log_rate_limit_interval) {
+                p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
+                (void) unlink(p);
+
+                u->exported_log_rate_limit_interval = false;
+        }
+
+        if (u->exported_log_rate_limit_burst) {
+                p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
+                (void) unlink(p);
+
+                u->exported_log_rate_limit_burst = false;
+        }
 }
 
 int unit_prepare_exec(Unit *u) {
index dbd33d1c5b23db28c80cde78e1111998b4e01cf5..cdd6d8bf6e2a93a2b811a89b3d9a1f60d264153b 100644 (file)
@@ -349,6 +349,8 @@ typedef struct Unit {
         bool exported_invocation_id:1;
         bool exported_log_level_max:1;
         bool exported_log_extra_fields:1;
+        bool exported_log_rate_limit_interval:1;
+        bool exported_log_rate_limit_burst:1;
 
         /* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If
          * == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */
index 78e279db8bc7fbab43cc9c785700813b34121608..8c08c64884cbba925309b6e5dce319fdeb24edff 100644 (file)
@@ -15,6 +15,7 @@
 #include "bus-error.h"
 #include "bus-util.h"
 #include "compress.h"
+#include "def.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -26,6 +27,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "rlimit-util.h"
 #include "sigbus.h"
 #include "signal-util.h"
 #include "string-util.h"
@@ -1067,6 +1069,9 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        /* The journal merging logic potentially needs a lot of fds. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 goto end;
index 132714fdddb78ef81cc3dfe9ecab216d00d72d95..9030708db095db11376ee32b443a5d4ceb80f5ad 100644 (file)
@@ -247,20 +247,17 @@ static int fsck_progress_socket(void) {
                 .un.sun_path = "/run/systemd/fsck.progress",
         };
 
-        int fd, r;
+        _cleanup_close_ int fd = -1;
 
         fd = socket(AF_UNIX, SOCK_STREAM, 0);
         if (fd < 0)
                 return log_warning_errno(errno, "socket(): %m");
 
-        if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
-                r = log_full_errno(IN_SET(errno, ECONNREFUSED, ENOENT) ? LOG_DEBUG : LOG_WARNING,
-                                   errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
-                safe_close(fd);
-                return r;
-        }
+        if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+                return log_full_errno(IN_SET(errno, ECONNREFUSED, ENOENT) ? LOG_DEBUG : LOG_WARNING,
+                                      errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
 
-        return fd;
+        return TAKE_FD(fd);
 }
 
 int main(int argc, char *argv[]) {
index 04563fb0981cd99462cfbff20acd5ae57cb401c1..7dd00b7545b012e3b2f0dc046cfb4c8a16e1c8d9 100644 (file)
@@ -584,7 +584,6 @@ static int manager_new(Manager **ret) {
                 .un.sun_family = AF_UNIX,
                 .un.sun_path = "/run/systemd/import/notify",
         };
-        static const int one = 1;
         int r;
 
         assert(ret);
@@ -608,12 +607,12 @@ static int manager_new(Manager **ret) {
                 return -errno;
 
         (void) mkdir_parents_label(sa.un.sun_path, 0755);
-        (void) unlink(sa.un.sun_path);
+        (void) sockaddr_un_unlink(&sa.un);
 
         if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
                 return -errno;
 
-        if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+        if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one)) < 0)
                 return -errno;
 
         r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
index 6d9b44e5152791838ed7bf5bd07eee3d174b5bf1..b52e9329ef2b984be90b4d43eb77e8a12b610e3b 100644 (file)
@@ -12,6 +12,7 @@
 #include "journal-remote-write.h"
 #include "journal-remote.h"
 #include "process-util.h"
+#include "rlimit-util.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "stat-util.h"
@@ -1096,6 +1097,9 @@ int main(int argc, char **argv) {
         log_show_color(true);
         log_parse_environment();
 
+        /* The journal merging logic potentially needs a lot of fds. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         r = parse_config();
         if (r < 0)
                 return EXIT_FAILURE;
index 621fd620ee254df4c10e30553c02596a6d900c5e..88fc51ec2674796086e47432b18969d3f1b00ea5 100644 (file)
@@ -20,6 +20,7 @@
 #include "mkdir.h"
 #include "parse-util.h"
 #include "process-util.h"
+#include "rlimit-util.h"
 #include "sigbus.h"
 #include "signal-util.h"
 #include "string-util.h"
@@ -780,6 +781,9 @@ int main(int argc, char **argv) {
         log_show_color(true);
         log_parse_environment();
 
+        /* The journal merging logic potentially needs a lot of fds. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         r = parse_config();
         if (r < 0)
                 goto finish;
index 9c92d6751658399915fee9cd653d1085907513af..3b19d3c4444515b9469ec2995d3ba885bb7d5811 100644 (file)
@@ -2616,6 +2616,8 @@ void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
 }
 
 int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
+        int r;
+
         assert(af);
         assert(af->header);
         assert(bf);
@@ -2635,10 +2637,9 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
 
                 /* If this is from the same seqnum source, compare
                  * seqnums */
-                if (af->current_seqnum < bf->current_seqnum)
-                        return -1;
-                if (af->current_seqnum > bf->current_seqnum)
-                        return 1;
+                r = CMP(af->current_seqnum, bf->current_seqnum);
+                if (r != 0)
+                        return r;
 
                 /* Wow! This is weird, different data but the same
                  * seqnums? Something is borked, but let's make the
@@ -2648,25 +2649,18 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
         if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) {
 
                 /* If the boot id matches, compare monotonic time */
-                if (af->current_monotonic < bf->current_monotonic)
-                        return -1;
-                if (af->current_monotonic > bf->current_monotonic)
-                        return 1;
+                r = CMP(af->current_monotonic, bf->current_monotonic);
+                if (r != 0)
+                        return r;
         }
 
         /* Otherwise, compare UTC time */
-        if (af->current_realtime < bf->current_realtime)
-                return -1;
-        if (af->current_realtime > bf->current_realtime)
-                return 1;
+        r = CMP(af->current_realtime, bf->current_realtime);
+        if (r != 0)
+                return r;
 
         /* Finally, compare by contents */
-        if (af->current_xor_hash < bf->current_xor_hash)
-                return -1;
-        if (af->current_xor_hash > bf->current_xor_hash)
-                return 1;
-
-        return 0;
+        return CMP(af->current_xor_hash, bf->current_xor_hash);
 }
 
 static int bump_array_index(uint64_t *i, direction_t direction, uint64_t n) {
index 4d186014edb5ec167aae4548e93334c7df40f550..9bd2d9a150200e9c7d03d8592e5eec9765e2a6d8 100644 (file)
@@ -31,6 +31,7 @@
 #include "bus-util.h"
 #include "catalog.h"
 #include "chattr-util.h"
+#include "def.h"
 #include "device-private.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -2049,6 +2050,10 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
+         * split up into many files. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 goto finish;
@@ -2056,11 +2061,6 @@ int main(int argc, char *argv[]) {
         signal(SIGWINCH, columns_lines_cache_reset);
         sigbus_install();
 
-        /* Increase max number of open files to 16K if we can, we
-         * might needs this when browsing journal files, which might
-         * be split up into many files. */
-        setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
-
         switch (arg_action) {
 
         case ACTION_NEW_ID128:
index 68d889b5fbb08051b380502c04c9bc7b1f2e90f1..0e0465d87986b57d4b4b06b7d08527372cf9102e 100644 (file)
@@ -497,7 +497,6 @@ static int enable_audit(int fd, bool b) {
 }
 
 int server_open_audit(Server *s) {
-        static const int one = 1;
         int r;
 
         if (s->audit_fd < 0) {
@@ -528,7 +527,7 @@ int server_open_audit(Server *s) {
         } else
                 (void) fd_nonblock(s->audit_fd, true);
 
-        r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+        r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m");
 
index 8037480304c980beadd7addb0dc01f13feeacb36..1f1603b3767f9b2cb8897ba7c34743cc8ae1611e 100644 (file)
@@ -13,6 +13,7 @@
 #include "io-util.h"
 #include "journal-util.h"
 #include "journald-context.h"
+#include "parse-util.h"
 #include "process-util.h"
 #include "string-util.h"
 #include "syslog-util.h"
@@ -102,6 +103,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
         c->timestamp = USEC_INFINITY;
         c->extra_fields_mtime = NSEC_INFINITY;
         c->log_level_max = -1;
+        c->log_rate_limit_interval = s->rate_limit_interval;
+        c->log_rate_limit_burst = s->rate_limit_burst;
 
         r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
         if (r < 0) {
@@ -113,7 +116,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
         return 0;
 }
 
-static void client_context_reset(ClientContext *c) {
+static void client_context_reset(Server *s, ClientContext *c) {
+        assert(s);
         assert(c);
 
         c->timestamp = USEC_INFINITY;
@@ -148,6 +152,9 @@ static void client_context_reset(ClientContext *c) {
         c->extra_fields_mtime = NSEC_INFINITY;
 
         c->log_level_max = -1;
+
+        c->log_rate_limit_interval = s->rate_limit_interval;
+        c->log_rate_limit_burst = s->rate_limit_burst;
 }
 
 static ClientContext* client_context_free(Server *s, ClientContext *c) {
@@ -161,7 +168,7 @@ static ClientContext* client_context_free(Server *s, ClientContext *c) {
         if (c->in_lru)
                 assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0);
 
-        client_context_reset(c);
+        client_context_reset(s, c);
 
         return mfree(c);
 }
@@ -424,6 +431,42 @@ static int client_context_read_extra_fields(
         return 0;
 }
 
+static int client_context_read_log_rate_limit_interval(ClientContext *c) {
+        _cleanup_free_ char *value = NULL;
+        const char *p;
+        int r;
+
+        assert(c);
+
+        if (!c->unit)
+                return 0;
+
+        p = strjoina("/run/systemd/units/log-rate-limit-interval:", c->unit);
+        r = readlink_malloc(p, &value);
+        if (r < 0)
+                return r;
+
+        return safe_atou64(value, &c->log_rate_limit_interval);
+}
+
+static int client_context_read_log_rate_limit_burst(ClientContext *c) {
+        _cleanup_free_ char *value = NULL;
+        const char *p;
+        int r;
+
+        assert(c);
+
+        if (!c->unit)
+                return 0;
+
+        p = strjoina("/run/systemd/units/log-rate-limit-burst:", c->unit);
+        r = readlink_malloc(p, &value);
+        if (r < 0)
+                return r;
+
+        return safe_atou(value, &c->log_rate_limit_burst);
+}
+
 static void client_context_really_refresh(
                 Server *s,
                 ClientContext *c,
@@ -450,6 +493,8 @@ static void client_context_really_refresh(
         (void) client_context_read_invocation_id(s, c);
         (void) client_context_read_log_level_max(s, c);
         (void) client_context_read_extra_fields(s, c);
+        (void) client_context_read_log_rate_limit_interval(c);
+        (void) client_context_read_log_rate_limit_burst(c);
 
         c->timestamp = timestamp;
 
@@ -480,7 +525,7 @@ void client_context_maybe_refresh(
         /* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out
          * entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */
         if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) {
-                client_context_reset(c);
+                client_context_reset(s, c);
                 goto refresh;
         }
 
index 9df3a38eff89080a0838d94632668349d8cdea1f..5e19c71f14f59bbfb176112b7096b616da943b51 100644 (file)
@@ -49,6 +49,9 @@ struct ClientContext {
         size_t extra_fields_n_iovec;
         void *extra_fields_data;
         nsec_t extra_fields_mtime;
+
+        usec_t log_rate_limit_interval;
+        unsigned log_rate_limit_burst;
 };
 
 int client_context_get(
index 44610a4b3b33cba62494759a68dd36702f8920e7..208758785d488d69e76064690fab56e87043a97a 100644 (file)
@@ -443,7 +443,6 @@ int server_open_native_socket(Server*s) {
                 .un.sun_family = AF_UNIX,
                 .un.sun_path = "/run/systemd/journal/socket",
         };
-        static const int one = 1;
         int r;
 
         assert(s);
@@ -453,7 +452,7 @@ int server_open_native_socket(Server*s) {
                 if (s->native_fd < 0)
                         return log_error_errno(errno, "socket() failed: %m");
 
-                (void) unlink(sa.un.sun_path);
+                (void) sockaddr_un_unlink(&sa.un);
 
                 r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
                 if (r < 0)
@@ -463,19 +462,19 @@ int server_open_native_socket(Server*s) {
         } else
                 (void) fd_nonblock(s->native_fd, true);
 
-        r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+        r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return log_error_errno(errno, "SO_PASSCRED failed: %m");
 
 #if HAVE_SELINUX
         if (mac_selinux_use()) {
-                r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+                r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         log_warning_errno(errno, "SO_PASSSEC failed: %m");
         }
 #endif
 
-        r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+        r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
 
index 6a8a36a73664f306c42ffd90551af309eed5e1ac..0b42d53760ace28734f15e9a0b658ea0f38563be 100644 (file)
@@ -39,6 +39,10 @@ struct JournalRateLimitGroup {
         JournalRateLimit *parent;
 
         char *id;
+
+        /* Interval is stored to keep track of when the group expires */
+        usec_t interval;
+
         JournalRateLimitPool pools[POOLS_MAX];
         uint64_t hash;
 
@@ -47,8 +51,6 @@ struct JournalRateLimitGroup {
 };
 
 struct JournalRateLimit {
-        usec_t interval;
-        unsigned burst;
 
         JournalRateLimitGroup* buckets[BUCKETS_MAX];
         JournalRateLimitGroup *lru, *lru_tail;
@@ -58,18 +60,13 @@ struct JournalRateLimit {
         uint8_t hash_key[16];
 };
 
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) {
+JournalRateLimit *journal_rate_limit_new(void) {
         JournalRateLimit *r;
 
-        assert(interval > 0 || burst == 0);
-
         r = new0(JournalRateLimit, 1);
         if (!r)
                 return NULL;
 
-        r->interval = interval;
-        r->burst = burst;
-
         random_bytes(r->hash_key, sizeof(r->hash_key));
 
         return r;
@@ -109,7 +106,7 @@ _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, us
         assert(g);
 
         for (i = 0; i < POOLS_MAX; i++)
-                if (g->pools[i].begin + g->parent->interval >= ts)
+                if (g->pools[i].begin + g->interval >= ts)
                         return false;
 
         return true;
@@ -126,9 +123,8 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
                 journal_rate_limit_group_free(r->lru_tail);
 }
 
-static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
+static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) {
         JournalRateLimitGroup *g;
-        struct siphash state;
 
         assert(r);
         assert(id);
@@ -141,9 +137,9 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r,
         if (!g->id)
                 goto fail;
 
-        siphash24_init(&state, r->hash_key);
-        string_hash_func(g->id, &state);
-        g->hash = siphash24_finalize(&state);
+        g->hash = siphash24_string(g->id, r->hash_key);
+
+        g->interval = interval;
 
         journal_rate_limit_vacuum(r, ts);
 
@@ -189,11 +185,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
         return burst;
 }
 
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) {
         uint64_t h;
         JournalRateLimitGroup *g;
         JournalRateLimitPool *p;
-        struct siphash state;
         unsigned burst;
         usec_t ts;
 
@@ -209,16 +204,9 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
         if (!r)
                 return 1;
 
-        if (r->interval == 0 || r->burst == 0)
-                return 1;
-
-        burst = burst_modulate(r->burst, available);
-
         ts = now(CLOCK_MONOTONIC);
 
-        siphash24_init(&state, r->hash_key);
-        string_hash_func(id, &state);
-        h = siphash24_finalize(&state);
+        h = siphash24_string(id, r->hash_key);
         g = r->buckets[h % BUCKETS_MAX];
 
         LIST_FOREACH(bucket, g, g)
@@ -226,10 +214,16 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
                         break;
 
         if (!g) {
-                g = journal_rate_limit_group_new(r, id, ts);
+                g = journal_rate_limit_group_new(r, id, rl_interval, ts);
                 if (!g)
                         return -ENOMEM;
-        }
+        } else
+                g->interval = rl_interval;
+
+        if (rl_interval == 0 || rl_burst == 0)
+                return 1;
+
+        burst = burst_modulate(rl_burst, available);
 
         p = &g->pools[priority_map[priority]];
 
@@ -240,7 +234,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
                 return 1;
         }
 
-        if (p->begin + r->interval < ts) {
+        if (p->begin + rl_interval < ts) {
                 unsigned s;
 
                 s = p->suppressed;
index 3a7f106de00aa7d7ac633a2d9993701e1fbdc79c..a2992800fe544e6258ce9819663592347332b4ce 100644 (file)
@@ -5,6 +5,6 @@
 
 typedef struct JournalRateLimit JournalRateLimit;
 
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst);
+JournalRateLimit *journal_rate_limit_new(void);
 void journal_rate_limit_free(JournalRateLimit *r);
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available);
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available);
index 4f63907ebff33daacf3ad70d61dbfc23c6c84b56..e0ec438b1e65956ed5a3f5c8ad211d16b8375c4b 100644 (file)
@@ -937,7 +937,7 @@ void server_dispatch_message(
         if (c && c->unit) {
                 (void) determine_space(s, &available, NULL);
 
-                rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available);
+                rl = journal_rate_limit_test(s->rate_limit, c->unit, c->log_rate_limit_interval, c->log_rate_limit_burst, priority & LOG_PRIMASK, available);
                 if (rl == 0)
                         return;
 
@@ -1595,11 +1595,9 @@ static int dispatch_watchdog(sd_event_source *es, uint64_t usec, void *userdata)
 }
 
 static int server_connect_notify(Server *s) {
-        union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-        };
+        union sockaddr_union sa = {};
         const char *e;
-        int r;
+        int r, salen;
 
         assert(s);
         assert(s->notify_fd < 0);
@@ -1628,15 +1626,9 @@ static int server_connect_notify(Server *s) {
         if (!e)
                 return 0;
 
-        if (!IN_SET(e[0], '@', '/') || e[1] == 0) {
-                log_error("NOTIFY_SOCKET set to an invalid value: %s", e);
-                return -EINVAL;
-        }
-
-        if (strlen(e) > sizeof(sa.un.sun_path)) {
-                log_error("NOTIFY_SOCKET path too long: %s", e);
-                return -EINVAL;
-        }
+        salen = sockaddr_un_set_path(&sa.un, e);
+        if (salen < 0)
+                return log_error_errno(salen, "NOTIFY_SOCKET set to invalid value '%s': %m", e);
 
         s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
         if (s->notify_fd < 0)
@@ -1644,11 +1636,7 @@ static int server_connect_notify(Server *s) {
 
         (void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE);
 
-        strncpy(sa.un.sun_path, e, sizeof(sa.un.sun_path));
-        if (sa.un.sun_path[0] == '@')
-                sa.un.sun_path[0] = 0;
-
-        r = connect(s->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+        r = connect(s->notify_fd, &sa.sa, salen);
         if (r < 0)
                 return log_error_errno(errno, "Failed to connect to notify socket: %m");
 
@@ -1845,7 +1833,7 @@ int server_init(Server *s) {
         if (r < 0)
                 return r;
 
-        s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst);
+        s->rate_limit = journal_rate_limit_new();
         if (!s->rate_limit)
                 return -ENOMEM;
 
index 267e63f4a25b1fc308a02401f830496d3febcdb8..502bd6f51bb9e8cf58da7699924ae18f733a5281 100644 (file)
@@ -793,7 +793,7 @@ int server_open_stdout_socket(Server *s) {
                 if (s->stdout_fd < 0)
                         return log_error_errno(errno, "socket() failed: %m");
 
-                (void) unlink(sa.un.sun_path);
+                (void) sockaddr_un_unlink(&sa.un);
 
                 r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
                 if (r < 0)
index 46cc110f1cc7d76434fd57ceb258cef16404dbba..e347f8bf5dbba0a4a4201522c0cded02d9f9d784 100644 (file)
@@ -447,7 +447,6 @@ int server_open_syslog_socket(Server *s) {
                 .un.sun_family = AF_UNIX,
                 .un.sun_path = "/run/systemd/journal/dev-log",
         };
-        static const int one = 1;
         int r;
 
         assert(s);
@@ -457,7 +456,7 @@ int server_open_syslog_socket(Server *s) {
                 if (s->syslog_fd < 0)
                         return log_error_errno(errno, "socket() failed: %m");
 
-                (void) unlink(sa.un.sun_path);
+                (void) sockaddr_un_unlink(&sa.un);
 
                 r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
                 if (r < 0)
@@ -467,19 +466,19 @@ int server_open_syslog_socket(Server *s) {
         } else
                 (void) fd_nonblock(s->syslog_fd, true);
 
-        r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+        r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return log_error_errno(errno, "SO_PASSCRED failed: %m");
 
 #if HAVE_SELINUX
         if (mac_selinux_use()) {
-                r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+                r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         log_warning_errno(errno, "SO_PASSSEC failed: %m");
         }
 #endif
 
-        r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+        r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
 
index 83abd82d1cf195b33e0552576b7e9307b7530a9e..023395d8de3a55b117dac839af9696d1d9040d16 100644 (file)
@@ -433,6 +433,8 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
 }
 
 _pure_ static int compare_with_location(JournalFile *f, Location *l) {
+        int r;
+
         assert(f);
         assert(l);
         assert(f->location_type == LOCATION_SEEK);
@@ -449,35 +451,31 @@ _pure_ static int compare_with_location(JournalFile *f, Location *l) {
         if (l->seqnum_set &&
             sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
 
-                if (f->current_seqnum < l->seqnum)
-                        return -1;
-                if (f->current_seqnum > l->seqnum)
-                        return 1;
+                r = CMP(f->current_seqnum, l->seqnum);
+                if (r != 0)
+                        return r;
         }
 
         if (l->monotonic_set &&
             sd_id128_equal(f->current_boot_id, l->boot_id)) {
 
-                if (f->current_monotonic < l->monotonic)
-                        return -1;
-                if (f->current_monotonic > l->monotonic)
-                        return 1;
+                r = CMP(f->current_monotonic, l->monotonic);
+                if (r != 0)
+                        return r;
         }
 
         if (l->realtime_set) {
 
-                if (f->current_realtime < l->realtime)
-                        return -1;
-                if (f->current_realtime > l->realtime)
-                        return 1;
+                r = CMP(f->current_realtime, l->realtime);
+                if (r != 0)
+                        return r;
         }
 
         if (l->xor_hash_set) {
 
-                if (f->current_xor_hash < l->xor_hash)
-                        return -1;
-                if (f->current_xor_hash > l->xor_hash)
-                        return 1;
+                r = CMP(f->current_xor_hash, l->xor_hash);
+                if (r != 0)
+                        return r;
         }
 
         return 0;
index cf59f14958e17943a1475c60e8c489f9c893b21b..aa92376ee88ed6f7176ebc09d56febfcad3faa1e 100644 (file)
@@ -78,7 +78,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
                 .filter = filter
         };
         _cleanup_close_ int s = -1;
-        int r, on = 1;
+        int r;
 
         assert(ifindex > 0);
         assert(link);
@@ -87,7 +87,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
         if (s < 0)
                 return -errno;
 
-        r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on));
+        r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
@@ -149,7 +149,7 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
         };
         _cleanup_close_ int s = -1;
         char ifname[IF_NAMESIZE] = "";
-        int r, on = 1, tos = IPTOS_CLASS_CS6;
+        int r, tos = IPTOS_CLASS_CS6;
 
         s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
         if (s < 0)
@@ -159,7 +159,7 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+        r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
@@ -173,16 +173,16 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
         }
 
         if (address == INADDR_ANY) {
-                r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
+                r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         return -errno;
 
-                r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
+                r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         return -errno;
 
         } else {
-                r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
+                r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         return -errno;
         }
index 78cd38366920c31db64c9a2260c0864e51fe3e23..406b1efca48dd36cdcb3944461050c6e9c627e8f 100644 (file)
@@ -25,7 +25,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
                 .in6.sin6_scope_id = index,
         };
         _cleanup_close_ int s = -1;
-        int r, off = 0, on = 1;
+        int r;
 
         assert(index > 0);
         assert(local_address);
@@ -36,15 +36,15 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
         if (s < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &const_int_zero, sizeof(const_int_zero));
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+        r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
index 8bee24e5689a685ac84cb2698bc54579495f50f8..c27a92c6ca61f38a857d60fd8472acb6786d4db6 100644 (file)
@@ -33,7 +33,7 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
         int index = mreq->ipv6mr_interface;
         _cleanup_close_ int s = -1;
         char ifname[IF_NAMESIZE] = "";
-        static const int zero = 0, one = 1, hops = 255;
+        static const int hops = 255;
         int r;
 
         s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
@@ -56,7 +56,7 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &const_int_zero, sizeof(const_int_zero));
         if (r < 0)
                 return -errno;
 
@@ -68,11 +68,11 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+        r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
index ba682b879aff9911b8c74b980ea7e3d21e4fcac1..8d0bebe2adf4e621f5c73189dc9b02c0c4840dc0 100644 (file)
@@ -577,6 +577,8 @@ global:
         sd_bus_set_method_call_timeout;
         sd_bus_get_method_call_timeout;
 
+        sd_bus_error_move;
+
         sd_device_ref;
         sd_device_unref;
 
index 0f79ddc4278be68fd8280e5f0a49d99086cacf20..e73f7057e1ee27a3f31ac6a7eb6b66d8d19f1ce5 100644 (file)
@@ -308,6 +308,28 @@ finish:
         return -bus_error_name_to_errno(e->name);
 }
 
+_public_ int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e) {
+        int r;
+
+        if (!sd_bus_error_is_set(e)) {
+
+                if (dest)
+                        *dest = SD_BUS_ERROR_NULL;
+
+                return 0;
+        }
+
+        r = -bus_error_name_to_errno(e->name);
+
+        if (dest) {
+                *dest = *e;
+                *e = SD_BUS_ERROR_NULL;
+        } else
+                sd_bus_error_free(e);
+
+        return r;
+}
+
 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
         if (!name)
                 return 0;
index 1437df6cf36ced4a8029343b40680875dcf7982e..85558d9c05620d2a27109fd23cf4537157d01092 100644 (file)
@@ -730,20 +730,28 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
 
         if (path) {
                 l = strlen(path);
-                if (l > sizeof(b->sockaddr.un.sun_path))
+                if (l >= sizeof(b->sockaddr.un.sun_path)) /* We insist on NUL termination */
                         return -E2BIG;
 
-                b->sockaddr.un.sun_family = AF_UNIX;
-                strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
-                b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
-        } else if (abstract) {
+                b->sockaddr.un = (struct sockaddr_un) {
+                        .sun_family = AF_UNIX,
+                };
+
+                memcpy(b->sockaddr.un.sun_path, path, l);
+                b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+
+        } else {
+                assert(abstract);
+
                 l = strlen(abstract);
-                if (l > sizeof(b->sockaddr.un.sun_path) - 1)
+                if (l >= sizeof(b->sockaddr.un.sun_path) - 1) /* We insist on NUL termination */
                         return -E2BIG;
 
-                b->sockaddr.un.sun_family = AF_UNIX;
-                b->sockaddr.un.sun_path[0] = 0;
-                strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
+                b->sockaddr.un = (struct sockaddr_un) {
+                        .sun_family = AF_UNIX,
+                };
+
+                memcpy(b->sockaddr.un.sun_path+1, abstract, l);
                 b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
         }
 
@@ -964,9 +972,11 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
         } else
                 b->nspid = 0;
 
-        b->sockaddr.un.sun_family = AF_UNIX;
-        /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */
-        strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
+        b->sockaddr.un = (struct sockaddr_un) {
+                .sun_family = AF_UNIX,
+                /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */
+                .sun_path = "/var/run/dbus/system_bus_socket",
+        };
         b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un);
         b->is_local = false;
 
index 1796feaa2f3ffa034cdf77beb176b8418dce2b5c..40879b85375a623ce8e522822724024f65a09d35 100644 (file)
@@ -40,10 +40,9 @@ static const sd_bus_vtable vtable[] = {
 static void* thread_server(void *p) {
         _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL;
         _cleanup_close_ int fd = -1;
-        union sockaddr_union u = {
-                .un.sun_family = AF_UNIX,
-        };
+        union sockaddr_union u = {};
         const char *path = p;
+        int salen;
 
         log_debug("Initializing server");
 
@@ -66,12 +65,13 @@ static void* thread_server(void *p) {
         assert_se(symlink(basename(suffixed), suffixed2) >= 0);
         (void) usleep(100 * USEC_PER_MSEC);
 
-        strncpy(u.un.sun_path, path, sizeof(u.un.sun_path));
+        salen = sockaddr_un_set_path(&u.un, path);
+        assert_se(salen >= 0);
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
         assert_se(fd >= 0);
 
-        assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0);
+        assert_se(bind(fd, &u.sa, salen) >= 0);
         usleep(100 * USEC_PER_MSEC);
 
         assert_se(listen(fd, SOMAXCONN) >= 0);
index c8b5c67d18ec6c7a489e8dc6bbf3e25a7e80c631..44c1d312e1c06d31713421c7a9e1a2504aec35f3 100644 (file)
@@ -444,9 +444,7 @@ _public_ int sd_pid_notify_with_fds(
                 const int *fds,
                 unsigned n_fds) {
 
-        union sockaddr_union sockaddr = {
-                .sa.sa_family = AF_UNIX,
-        };
+        union sockaddr_union sockaddr = {};
         struct iovec iovec = {
                 .iov_base = (char*) state,
         };
@@ -459,7 +457,7 @@ _public_ int sd_pid_notify_with_fds(
         struct cmsghdr *cmsg = NULL;
         const char *e;
         bool send_ucred;
-        int r;
+        int r, salen;
 
         if (!state) {
                 r = -EINVAL;
@@ -475,14 +473,9 @@ _public_ int sd_pid_notify_with_fds(
         if (!e)
                 return 0;
 
-        /* Must be an abstract socket, or an absolute path */
-        if (!IN_SET(e[0], '@', '/') || e[1] == 0) {
-                r = -EINVAL;
-                goto finish;
-        }
-
-        if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
-                r = -EINVAL;
+        salen = sockaddr_un_set_path(&sockaddr.un, e);
+        if (salen < 0) {
+                r = salen;
                 goto finish;
         }
 
@@ -495,12 +488,7 @@ _public_ int sd_pid_notify_with_fds(
         (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
 
         iovec.iov_len = strlen(state);
-
-        strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
-        if (sockaddr.un.sun_path[0] == '@')
-                sockaddr.un.sun_path[0] = 0;
-
-        msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un);
+        msghdr.msg_namelen = salen;
 
         send_ucred =
                 (pid != 0 && pid != getpid_cached()) ||
index c7e5bf4431f5ee0d303c7ce4853fa8b6d73e605c..3ae283e93cab49b8b489112fd3ac7871a600c98b 100644 (file)
@@ -792,6 +792,33 @@ static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
         return r;
 }
 
+static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) {
+        sd_device **a, **b, **end;
+
+        assert(enumerator);
+
+        if (enumerator->n_devices <= 1)
+                return;
+
+        a = enumerator->devices + 1;
+        b = enumerator->devices;
+        end = enumerator->devices + enumerator->n_devices;
+
+        for (; a < end; a++) {
+                const char *devpath_a, *devpath_b;
+
+                assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0);
+                assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0);
+
+                if (path_equal(devpath_a, devpath_b))
+                        sd_device_unref(*a);
+                else
+                        *(++b) = *a;
+        }
+
+        enumerator->n_devices = b - enumerator->devices + 1;
+}
+
 int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
         int r = 0, k;
         size_t i;
@@ -822,6 +849,7 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
         }
 
         typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
+        device_enumerator_dedup_devices(enumerator);
 
         enumerator->scan_uptodate = true;
         enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
@@ -906,6 +934,7 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
         }
 
         typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
+        device_enumerator_dedup_devices(enumerator);
 
         enumerator->scan_uptodate = true;
         enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
index fc2d02b4b35fa8dbe7492896775e9e4b49f03929..e2701e0e242b6325179752f0ffa00843b4459669 100644 (file)
@@ -3,6 +3,7 @@
 #include "device-enumerator-private.h"
 #include "device-private.h"
 #include "device-util.h"
+#include "hashmap.h"
 #include "string-util.h"
 #include "tests.h"
 #include "util.h"
@@ -63,9 +64,73 @@ static void test_sd_device_basic(void) {
         }
 }
 
+static void test_sd_device_enumerator_filter_subsystem_one(const char *subsystem, Hashmap *h) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *d, *t;
+
+        assert_se(sd_device_enumerator_new(&e) >= 0);
+        assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, true) >= 0);
+
+        FOREACH_DEVICE(e, d) {
+                const char *syspath;
+
+                assert_se(sd_device_get_syspath(d, &syspath) >= 0);
+                assert_se(t = hashmap_remove(h, syspath));
+                assert_se(!sd_device_unref(t));
+
+                log_debug("Removed subsystem:%s syspath:%s", subsystem, syspath);
+        }
+
+        assert_se(hashmap_isempty(h));
+}
+
+static void test_sd_device_enumerator_filter_subsystem(void) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        _cleanup_(hashmap_freep) Hashmap *subsystems;
+        sd_device *d;
+        Hashmap *h;
+        char *s;
+
+        assert_se(subsystems = hashmap_new(&string_hash_ops));
+        assert_se(sd_device_enumerator_new(&e) >= 0);
+
+        FOREACH_DEVICE(e, d) {
+                const char *syspath, *subsystem;
+                int r;
+
+                assert_se(sd_device_get_syspath(d, &syspath) >= 0);
+
+                r = sd_device_get_subsystem(d, &subsystem);
+                assert_se(r >= 0 || r == -ENOENT);
+                if (r < 0)
+                        continue;
+
+                h = hashmap_get(subsystems, subsystem);
+                if (!h) {
+                        char *str;
+                        assert_se(str = strdup(subsystem));
+                        assert_se(h = hashmap_new(&string_hash_ops));
+                        assert_se(hashmap_put(subsystems, str, h) >= 0);
+                }
+
+                assert_se(hashmap_put(h, syspath, d) >= 0);
+                assert_se(sd_device_ref(d));
+
+                log_debug("Added subsystem:%s syspath:%s", subsystem, syspath);
+        }
+
+        while ((h = hashmap_steal_first_key_and_value(subsystems, (void**) &s))) {
+                test_sd_device_enumerator_filter_subsystem_one(s, h);
+                hashmap_free(h);
+                free(s);
+        }
+}
+
 int main(int argc, char **argv) {
         test_setup_logging(LOG_INFO);
 
         test_sd_device_basic();
+        test_sd_device_enumerator_filter_subsystem();
+
         return 0;
 }
index 720acfb0c79892f864df75d17cefe9a7ecd05b77..f44e6b4cca7a6f2c0f91eab152189747fc0ad713 100644 (file)
@@ -419,12 +419,7 @@ static int exit_prioq_compare(const void *a, const void *b) {
                 return 1;
 
         /* Lower priority values first */
-        if (x->priority < y->priority)
-                return -1;
-        if (x->priority > y->priority)
-                return 1;
-
-        return 0;
+        return CMP(x->priority, y->priority);
 }
 
 static void free_clock_data(struct clock_data *d) {
@@ -1570,21 +1565,16 @@ static int event_make_inotify_data(
 
 static int inode_data_compare(const void *a, const void *b) {
         const struct inode_data *x = a, *y = b;
+        int r;
 
         assert(x);
         assert(y);
 
-        if (x->dev < y->dev)
-                return -1;
-        if (x->dev > y->dev)
-                return 1;
-
-        if (x->ino < y->ino)
-                return -1;
-        if (x->ino > y->ino)
-                return 1;
+        r = CMP(x->dev, y->dev);
+        if (r != 0)
+                return r;
 
-        return 0;
+        return CMP(x->ino, y->ino);
 }
 
 static void inode_data_hash_func(const void *p, struct siphash *state) {
index f103cbedeaa672ca97d431afcacbc22afcd271fa..c18fd5890eebc1f75aada3fe0263ce1a30d51e40 100644 (file)
@@ -88,9 +88,9 @@ static int broadcast_groups_get(sd_netlink *nl) {
 
 int socket_bind(sd_netlink *nl) {
         socklen_t addrlen;
-        int r, one = 1;
+        int r;
 
-        r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
+        r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
index ea443083510acdb6e5d45c7bebc362ddbbae9bec..50c3925058e72b33b1ba59ca63a0ec5232bfe467 100644 (file)
@@ -337,7 +337,6 @@ int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct
  * Returns: 0 on success, otherwise a negative error value.
  */
 _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) {
-        const int on = 1;
         int r;
 
         assert_return(udev_monitor, -EINVAL);
@@ -358,7 +357,7 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) {
                 return log_debug_errno(r, "Failed to set address: %m");
 
         /* enable receiving of sender credentials */
-        if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0)
+        if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one)) < 0)
                 return log_debug_errno(errno, "Failed to set socket option SO_PASSCRED: %m");
 
         return 0;
index c9c3166f0c84f4a0b6c16c6767e394635357137c..39c24f8c3aeb34789bd4a99f4d8bfb9094bea672 100644 (file)
@@ -21,6 +21,7 @@
 #include "pager.h"
 #include "parse-util.h"
 #include "process-util.h"
+#include "rlimit-util.h"
 #include "sigbus.h"
 #include "signal-util.h"
 #include "spawn-polkit-agent.h"
@@ -1522,6 +1523,10 @@ int main(int argc, char *argv[]) {
         setlocale(LC_ALL, "");
         log_parse_environment();
         log_open();
+
+        /* The journal merging logic potentially needs a lot of fds. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         sigbus_install();
 
         r = parse_argv(argc, argv);
index 87506088a1c4a52b16a1fc60f936a349d438a307..6354dfe6e648ee5ca1326feb6b5942a1585949bf 100644 (file)
@@ -5,6 +5,9 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <linux/vt.h>
+#if ENABLE_UTMP
+#include <utmpx.h>
+#endif
 
 #include "sd-device.h"
 
@@ -17,6 +20,7 @@
 #include "fd-util.h"
 #include "logind.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -29,6 +33,8 @@ void manager_reset_config(Manager *m) {
         m->reserve_vt = 6;
         m->remove_ipc = true;
         m->inhibit_delay_max = 5 * USEC_PER_SEC;
+        m->user_stop_delay = 10 * USEC_PER_SEC;
+
         m->handle_power_key = HANDLE_POWEROFF;
         m->handle_suspend_key = HANDLE_SUSPEND;
         m->handle_hibernate_key = HANDLE_HIBERNATE;
@@ -90,15 +96,16 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
 
 int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
         Seat *s;
+        int r;
 
         assert(m);
         assert(id);
 
         s = hashmap_get(m->seats, id);
         if (!s) {
-                s = seat_new(m, id);
-                if (!s)
-                        return -ENOMEM;
+                r = seat_new(&s, m, id);
+                if (r < 0)
+                        return r;
         }
 
         if (_seat)
@@ -109,15 +116,16 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
 
 int manager_add_session(Manager *m, const char *id, Session **_session) {
         Session *s;
+        int r;
 
         assert(m);
         assert(id);
 
         s = hashmap_get(m->sessions, id);
         if (!s) {
-                s = session_new(m, id);
-                if (!s)
-                        return -ENOMEM;
+                r = session_new(&s, m, id);
+                if (r < 0)
+                        return r;
         }
 
         if (_session)
@@ -126,7 +134,14 @@ int manager_add_session(Manager *m, const char *id, Session **_session) {
         return 0;
 }
 
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
+int manager_add_user(
+                Manager *m,
+                uid_t uid,
+                gid_t gid,
+                const char *name,
+                const char *home,
+                User **_user) {
+
         User *u;
         int r;
 
@@ -135,7 +150,7 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
 
         u = hashmap_get(m->users, UID_TO_PTR(uid));
         if (!u) {
-                r = user_new(&u, m, uid, gid, name);
+                r = user_new(&u, m, uid, gid, name, home);
                 if (r < 0)
                         return r;
         }
@@ -146,7 +161,12 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
         return 0;
 }
 
-int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
+int manager_add_user_by_name(
+                Manager *m,
+                const char *name,
+                User **_user) {
+
+        const char *home = NULL;
         uid_t uid;
         gid_t gid;
         int r;
@@ -154,11 +174,11 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
         assert(m);
         assert(name);
 
-        r = get_user_creds(&name, &uid, &gid, NULL, NULL, 0);
+        r = get_user_creds(&name, &uid, &gid, &home, NULL, 0);
         if (r < 0)
                 return r;
 
-        return manager_add_user(m, uid, gid, name, _user);
+        return manager_add_user(m, uid, gid, name, home, _user);
 }
 
 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
@@ -171,7 +191,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
         if (!p)
                 return errno > 0 ? -errno : -ENOENT;
 
-        return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
+        return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user);
 }
 
 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
@@ -335,13 +355,16 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) {
         if (!pid_is_valid(pid))
                 return -EINVAL;
 
-        r = cg_pid_get_unit(pid, &unit);
-        if (r < 0)
-                goto not_found;
+        s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid));
+        if (!s) {
+                r = cg_pid_get_unit(pid, &unit);
+                if (r < 0)
+                        goto not_found;
 
-        s = hashmap_get(m->session_units, unit);
-        if (!s)
-                goto not_found;
+                s = hashmap_get(m->session_units, unit);
+                if (!s)
+                        goto not_found;
+        }
 
         if (ret)
                 *ret = s;
@@ -677,3 +700,142 @@ bool manager_all_buttons_ignored(Manager *m) {
 
         return true;
 }
+
+int manager_read_utmp(Manager *m) {
+#if ENABLE_UTMP
+        int r;
+
+        assert(m);
+
+        if (utmpxname(_PATH_UTMPX) < 0)
+                return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m");
+
+        setutxent();
+
+        for (;;) {
+                _cleanup_free_ char *t = NULL;
+                struct utmpx *u;
+                const char *c;
+                Session *s;
+
+                errno = 0;
+                u = getutxent();
+                if (!u) {
+                        if (errno != 0)
+                                log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m");
+                        r = 0;
+                        break;
+                }
+
+                if (u->ut_type != USER_PROCESS)
+                        continue;
+
+                if (!pid_is_valid(u->ut_pid))
+                        continue;
+
+                t = strndup(u->ut_line, sizeof(u->ut_line));
+                if (!t) {
+                        r = log_oom();
+                        break;
+                }
+
+                c = path_startswith(t, "/dev/");
+                if (c) {
+                        r = free_and_strdup(&t, c);
+                        if (r < 0) {
+                                log_oom();
+                                break;
+                        }
+                }
+
+                if (isempty(t))
+                        continue;
+
+                s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(u->ut_pid));
+                if (!s)
+                        continue;
+
+                if (s->tty_validity == TTY_FROM_UTMP && !streq_ptr(s->tty, t)) {
+                        /* This may happen on multiplexed SSH connection (i.e. 'SSH connection sharing'). In
+                         * this case PAM and utmp sessions don't match. In such a case let's invalidate the TTY
+                         * information and never acquire it again. */
+
+                        s->tty = mfree(s->tty);
+                        s->tty_validity = TTY_UTMP_INCONSISTENT;
+                        log_debug("Session '%s' has inconsistent TTY information, dropping TTY information.", s->id);
+                        continue;
+                }
+
+                /* Never override what we figured out once */
+                if (s->tty || s->tty_validity >= 0)
+                        continue;
+
+                s->tty = TAKE_PTR(t);
+                s->tty_validity = TTY_FROM_UTMP;
+                log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id);
+        }
+
+        endutxent();
+        return r;
+#else
+        return 0
+#endif
+}
+
+#if ENABLE_UTMP
+static int manager_dispatch_utmp(sd_event_source *s, const struct inotify_event *event, void *userdata) {
+        Manager *m = userdata;
+
+        assert(m);
+
+        /* If there's indication the file itself might have been removed or became otherwise unavailable, then let's
+         * reestablish the watch on whatever there's now. */
+        if ((event->mask & (IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF|IN_Q_OVERFLOW|IN_UNMOUNT)) != 0)
+                manager_connect_utmp(m);
+
+        (void) manager_read_utmp(m);
+        return 0;
+}
+#endif
+
+void manager_connect_utmp(Manager *m) {
+#if ENABLE_UTMP
+        sd_event_source *s = NULL;
+        int r;
+
+        assert(m);
+
+        /* Watch utmp for changes via inotify. We do this to deal with tools such as ssh, which will register the PAM
+         * session early, and acquire a TTY only much later for the connection. Thus during PAM the TTY won't be known
+         * yet. ssh will register itself with utmp when it finally acquired the TTY. Hence, let's make use of this, and
+         * watch utmp for the TTY asynchronously. We use the PAM session's leader PID as key, to find the right entry.
+         *
+         * Yes, relying on utmp is pretty ugly, but it's good enough for informational purposes, as well as idle
+         * detection (which, for tty sessions, relies on the TTY used) */
+
+        r = sd_event_add_inotify(m->event, &s, _PATH_UTMPX, IN_MODIFY|IN_MOVE_SELF|IN_DELETE_SELF|IN_ATTRIB, manager_dispatch_utmp, m);
+        if (r < 0)
+                log_full_errno(r == -ENOENT ? LOG_DEBUG: LOG_WARNING, r, "Failed to create inotify watch on " _PATH_UTMPX ", ignoring: %m");
+        else {
+                r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to adjust utmp event source priority, ignoring: %m");
+
+                (void) sd_event_source_set_description(s, "utmp");
+        }
+
+        sd_event_source_unref(m->utmp_event_source);
+        m->utmp_event_source = s;
+#endif
+}
+
+void manager_reconnect_utmp(Manager *m) {
+#if ENABLE_UTMP
+        assert(m);
+
+        if (m->utmp_event_source)
+                return;
+
+        manager_connect_utmp(m);
+#endif
+}
index ed6da4445bae10913c5eac90a17f1b7d41c2c357..032504e63a9a734d43970472905018f0d0e30e14 100644 (file)
@@ -773,6 +773,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 } while (hashmap_get(m->sessions, id));
         }
 
+        /* If we are not watching utmp aleady, try again */
+        manager_reconnect_utmp(m);
+
         r = manager_add_user_by_uid(m, uid, &user);
         if (r < 0)
                 goto fail;
@@ -782,9 +785,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 goto fail;
 
         session_set_user(session, user);
+        session_set_leader(session, leader);
 
-        session->leader = leader;
-        session->audit_id = audit_id;
         session->type = t;
         session->class = c;
         session->remote = remote;
@@ -796,6 +798,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         r = -ENOMEM;
                         goto fail;
                 }
+
+                session->tty_validity = TTY_FROM_PAM;
         }
 
         if (!isempty(display)) {
@@ -846,9 +850,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
 
         r = sd_bus_message_enter_container(message, 'a', "(sv)");
         if (r < 0)
-                return r;
+                goto fail;
 
-        r = session_start(session, message);
+        r = session_start(session, message, error);
         if (r < 0)
                 goto fail;
 
@@ -2648,6 +2652,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2728,24 +2733,20 @@ const sd_bus_vtable manager_vtable[] = {
 };
 
 static int session_jobs_reply(Session *s, const char *unit, const char *result) {
-        int r = 0;
-
         assert(s);
         assert(unit);
 
         if (!s->started)
-                return r;
+                return 0;
 
-        if (streq(result, "done"))
-                r = session_send_create_reply(s, NULL);
-        else {
+        if (result && !streq(result, "done")) {
                 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
 
-                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
-                r = session_send_create_reply(s, &e);
+                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result);
+                return session_send_create_reply(s, &e);
         }
 
-        return r;
+        return session_send_create_reply(s, NULL);
 }
 
 int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2778,30 +2779,29 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
         }
 
         session = hashmap_get(m->session_units, unit);
-        if (session && streq_ptr(path, session->scope_job)) {
-                session->scope_job = mfree(session->scope_job);
-                session_jobs_reply(session, unit, result);
+        if (session) {
+                if (streq_ptr(path, session->scope_job)) {
+                        session->scope_job = mfree(session->scope_job);
+                        (void) session_jobs_reply(session, unit, result);
+
+                        session_save(session);
+                        user_save(session->user);
+                }
 
-                session_save(session);
-                user_save(session->user);
                 session_add_to_gc_queue(session);
         }
 
         user = hashmap_get(m->user_units, unit);
-        if (user &&
-            (streq_ptr(path, user->service_job) ||
-             streq_ptr(path, user->slice_job))) {
-
-                if (streq_ptr(path, user->service_job))
+        if (user) {
+                if (streq_ptr(path, user->service_job)) {
                         user->service_job = mfree(user->service_job);
 
-                if (streq_ptr(path, user->slice_job))
-                        user->slice_job = mfree(user->slice_job);
+                        LIST_FOREACH(sessions_by_user, session, user->sessions)
+                                (void) session_jobs_reply(session, unit, NULL /* don't propagate user service failures to the client */);
 
-                LIST_FOREACH(sessions_by_user, session, user->sessions)
-                        session_jobs_reply(session, unit, result);
+                        user_save(user);
+                }
 
-                user_save(user);
                 user_add_to_gc_queue(user);
         }
 
@@ -2933,13 +2933,15 @@ int manager_start_scope(
                 pid_t pid,
                 const char *slice,
                 const char *description,
-                const char *after,
-                const char *after2,
+                char **wants,
+                char **after,
+                const char *requires_mounts_for,
                 sd_bus_message *more_properties,
                 sd_bus_error *error,
                 char **job) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        char **i;
         int r;
 
         assert(manager);
@@ -2977,14 +2979,20 @@ int manager_start_scope(
                         return r;
         }
 
-        if (!isempty(after)) {
-                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
+        STRV_FOREACH(i, wants) {
+                r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i);
                 if (r < 0)
                         return r;
         }
 
-        if (!isempty(after2)) {
-                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
+        STRV_FOREACH(i, after) {
+                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!empty_or_root(requires_mounts_for)) {
+                r = sd_bus_message_append(m, "(sv)", "RequiresMountsFor", "as", 1, requires_mounts_for);
                 if (r < 0)
                         return r;
         }
@@ -3081,7 +3089,8 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
         return strdup_job(reply, job);
 }
 
-int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) {
+int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *ret_error) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_free_ char *path = NULL;
         int r;
 
@@ -3098,17 +3107,16 @@ int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *err
                         path,
                         "org.freedesktop.systemd1.Scope",
                         "Abandon",
-                        error,
+                        &error,
                         NULL,
                         NULL);
         if (r < 0) {
-                if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
-                    sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) ||
-                    sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) {
-                        sd_bus_error_free(error);
+                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
+                    sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED) ||
+                    sd_bus_error_has_name(&error, BUS_ERROR_SCOPE_NOT_RUNNING))
                         return 0;
-                }
 
+                sd_bus_error_move(ret_error, &error);
                 return r;
         }
 
@@ -3130,7 +3138,7 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo
                         "ssi", unit, who == KILL_LEADER ? "main" : "all", signo);
 }
 
-int manager_unit_is_active(Manager *manager, const char *unit) {
+int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *ret_error) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_free_ char *path = NULL;
@@ -3166,17 +3174,18 @@ int manager_unit_is_active(Manager *manager, const char *unit) {
                     sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
                         return false;
 
+                sd_bus_error_move(ret_error, &error);
                 return r;
         }
 
         r = sd_bus_message_read(reply, "s", &state);
         if (r < 0)
-                return -EINVAL;
+                return r;
 
-        return !streq(state, "inactive") && !streq(state, "failed");
+        return !STR_IN_SET(state, "inactive", "failed");
 }
 
-int manager_job_is_active(Manager *manager, const char *path) {
+int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *ret_error) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         int r;
@@ -3201,6 +3210,7 @@ int manager_job_is_active(Manager *manager, const char *path) {
                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
                         return false;
 
+                sd_bus_error_move(ret_error, &error);
                 return r;
         }
 
index c85339dcd3574d1c3677340115480eb74b75beb5..8829ce7d85a219cd3258b7757bb4a34a472f0046 100644 (file)
@@ -23,6 +23,7 @@ Login.KillUserProcesses,            config_parse_bool,                  0, offse
 Login.KillOnlyUsers,                config_parse_strv,                  0, offsetof(Manager, kill_only_users)
 Login.KillExcludeUsers,             config_parse_strv,                  0, offsetof(Manager, kill_exclude_users)
 Login.InhibitDelayMaxSec,           config_parse_sec,                   0, offsetof(Manager, inhibit_delay_max)
+Login.UserStopDelaySec,             config_parse_sec,                   0, offsetof(Manager, user_stop_delay)
 Login.HandlePowerKey,               config_parse_handle_action,         0, offsetof(Manager, handle_power_key)
 Login.HandleSuspendKey,             config_parse_handle_action,         0, offsetof(Manager, handle_suspend_key)
 Login.HandleHibernateKey,           config_parse_handle_action,         0, offsetof(Manager, handle_hibernate_key)
index f24fe96841eae0720a7203e3624e8eeacece5de3..cc33799230867eb066562effe9c6914314d614fa 100644 (file)
 #include "terminal-util.h"
 #include "util.h"
 
-Seat *seat_new(Manager *m, const char *id) {
-        Seat *s;
+int seat_new(Seat** ret, Manager *m, const char *id) {
+        _cleanup_(seat_freep) Seat *s = NULL;
+        int r;
 
+        assert(ret);
         assert(m);
         assert(id);
 
-        s = new0(Seat, 1);
+        if (!seat_name_is_valid(id))
+                return -EINVAL;
+
+        s = new(Seat, 1);
         if (!s)
-                return NULL;
+                return -ENOMEM;
+
+        *s = (Seat) {
+                .manager = m,
+        };
 
         s->state_file = strappend("/run/systemd/seats/", id);
         if (!s->state_file)
-                return mfree(s);
+                return -ENOMEM;
 
         s->id = basename(s->state_file);
-        s->manager = m;
 
-        if (hashmap_put(m->seats, s->id, s) < 0) {
-                free(s->state_file);
-                return mfree(s);
-        }
+        r = hashmap_put(m->seats, s->id, s);
+        if (r < 0)
+                return r;
 
-        return s;
+        *ret = TAKE_PTR(s);
+        return 0;
 }
 
-void seat_free(Seat *s) {
-        assert(s);
+Seat* seat_free(Seat *s) {
+        if (!s)
+                return NULL;
 
         if (s->in_gc_queue)
                 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
@@ -64,7 +73,8 @@ void seat_free(Seat *s) {
 
         free(s->positions);
         free(s->state_file);
-        free(s);
+
+        return mfree(s);
 }
 
 int seat_save(Seat *s) {
@@ -165,7 +175,7 @@ static int vt_allocate(unsigned int vtnr) {
         xsprintf(p, "/dev/tty%u", vtnr);
         fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
         if (fd < 0)
-                return -errno;
+                return fd;
 
         return 0;
 }
@@ -189,10 +199,8 @@ int seat_preallocate_vts(Seat *s) {
                 int q;
 
                 q = vt_allocate(i);
-                if (q < 0) {
-                        log_error_errno(q, "Failed to preallocate VT %u: %m", i);
-                        r = q;
-                }
+                if (q < 0)
+                        r = log_error_errno(q, "Failed to preallocate VT %u: %m", i);
         }
 
         return r;
@@ -209,9 +217,9 @@ int seat_apply_acls(Seat *s, Session *old_active) {
                             !!s->active, s->active ? s->active->user->uid : 0);
 
         if (r < 0)
-                log_error_errno(r, "Failed to apply ACLs: %m");
+                return log_error_errno(r, "Failed to apply ACLs: %m");
 
-        return r;
+        return 0;
 }
 
 int seat_set_active(Seat *s, Session *session) {
@@ -231,7 +239,7 @@ int seat_set_active(Seat *s, Session *session) {
                 session_send_changed(old_active, "Active", NULL);
         }
 
-        seat_apply_acls(s, old_active);
+        (void) seat_apply_acls(s, old_active);
 
         if (session && session->started) {
                 session_send_changed(session, "Active", NULL);
@@ -411,7 +419,7 @@ int seat_start(Seat *s) {
 }
 
 int seat_stop(Seat *s, bool force) {
-        int r = 0;
+        int r;
 
         assert(s);
 
@@ -421,9 +429,9 @@ int seat_stop(Seat *s, bool force) {
                            "SEAT_ID=%s", s->id,
                            LOG_MESSAGE("Removed seat %s.", s->id));
 
-        seat_stop_sessions(s, force);
+        r = seat_stop_sessions(s, force);
 
-        unlink(s->state_file);
+        (void) unlink(s->state_file);
         seat_add_to_gc_queue(s);
 
         if (s->started)
index 70878bbe5269c1cf644bfa8be3b9f33cd3c8c3bf..51cd468e26f649bce1d84be657bd13678717c7fd 100644 (file)
@@ -27,8 +27,10 @@ struct Seat {
         LIST_FIELDS(Seat, gc_queue);
 };
 
-Seat *seat_new(Manager *m, const char *id);
-void seat_free(Seat *s);
+int seat_new(Seat **ret, Manager *m, const char *id);
+Seat* seat_free(Seat *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Seat *, seat_free);
 
 int seat_save(Seat *s);
 int seat_load(Seat *s);
index 25c4981dc004062f104f8ff0975dd06ed444841c..5b09a07ffacbc472aaae231cc4e3c462607cf9c6 100644 (file)
@@ -689,6 +689,15 @@ int session_send_lock_all(Manager *m, bool lock) {
         return r;
 }
 
+static bool session_ready(Session *s) {
+        assert(s);
+
+        /* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */
+
+        return !s->scope_job &&
+                !s->user->service_job;
+}
+
 int session_send_create_reply(Session *s, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
         _cleanup_close_ int fifo_fd = -1;
@@ -696,19 +705,16 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
 
         assert(s);
 
-        /* This is called after the session scope and the user service
-         * were successfully created, and finishes where
+        /* This is called after the session scope and the user service were successfully created, and finishes where
          * bus_manager_create_session() left off. */
 
         if (!s->create_message)
                 return 0;
 
-        if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job))
+        if (!sd_bus_error_is_set(error) && !session_ready(s))
                 return 0;
 
-        c = s->create_message;
-        s->create_message = NULL;
-
+        c = TAKE_PTR(s->create_message);
         if (error)
                 return sd_bus_reply_method_error(c, error);
 
@@ -716,8 +722,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
         if (fifo_fd < 0)
                 return fifo_fd;
 
-        /* Update the session state file before we notify the client
-         * about the result. */
+        /* Update the session state file before we notify the client about the result. */
         session_save(s);
 
         p = session_bus_path(s);
index 226cc49b0108c7a061baf70e933974d6f43e236a..a1c0a08fda8900c8f236f08eacc2c029811b1688 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/kd.h>
 #include <linux/vt.h>
 #include <signal.h>
+#include <stdio_ext.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "string-table.h"
+#include "strv.h"
 #include "terminal-util.h"
 #include "user-util.h"
 #include "util.h"
-#include "process-util.h"
 
 #define RELEASE_USEC (20*USEC_PER_SEC)
 
 static void session_remove_fifo(Session *s);
 
-Session* session_new(Manager *m, const char *id) {
-        Session *s;
+int session_new(Session **ret, Manager *m, const char *id) {
+        _cleanup_(session_freep) Session *s = NULL;
+        int r;
 
+        assert(ret);
         assert(m);
         assert(id);
-        assert(session_id_valid(id));
 
-        s = new0(Session, 1);
+        if (!session_id_valid(id))
+                return -EINVAL;
+
+        s = new(Session, 1);
         if (!s)
-                return NULL;
+                return -ENOMEM;
+
+        *s = (Session) {
+                .manager = m,
+                .fifo_fd = -1,
+                .vtfd = -1,
+                .audit_id = AUDIT_SESSION_INVALID,
+                .tty_validity = _TTY_VALIDITY_INVALID,
+        };
 
         s->state_file = strappend("/run/systemd/sessions/", id);
         if (!s->state_file)
-                return mfree(s);
-
-        s->devices = hashmap_new(&devt_hash_ops);
-        if (!s->devices) {
-                free(s->state_file);
-                return mfree(s);
-        }
+                return -ENOMEM;
 
         s->id = basename(s->state_file);
 
-        if (hashmap_put(m->sessions, s->id, s) < 0) {
-                hashmap_free(s->devices);
-                free(s->state_file);
-                return mfree(s);
-        }
+        s->devices = hashmap_new(&devt_hash_ops);
+        if (!s->devices)
+                return -ENOMEM;
 
-        s->manager = m;
-        s->fifo_fd = -1;
-        s->vtfd = -1;
-        s->audit_id = AUDIT_SESSION_INVALID;
+        r = hashmap_put(m->sessions, s->id, s);
+        if (r < 0)
+                return r;
 
-        return s;
+        *ret = TAKE_PTR(s);
+        return 0;
 }
 
-void session_free(Session *s) {
+Session* session_free(Session *s) {
         SessionDevice *sd;
 
-        assert(s);
+        if (!s)
+                return NULL;
 
         if (s->in_gc_queue)
                 LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
@@ -95,6 +102,8 @@ void session_free(Session *s) {
 
                 if (s->user->display == s)
                         s->user->display = NULL;
+
+                user_update_last_session_timer(s->user);
         }
 
         if (s->seat) {
@@ -112,6 +121,9 @@ void session_free(Session *s) {
                 free(s->scope);
         }
 
+        if (pid_is_valid(s->leader))
+                (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
+
         free(s->scope_job);
 
         sd_bus_message_unref(s->create_message);
@@ -126,7 +138,8 @@ void session_free(Session *s) {
         hashmap_remove(s->manager->sessions, s->id);
 
         free(s->state_file);
-        free(s);
+
+        return mfree(s);
 }
 
 void session_set_user(Session *s, User *u) {
@@ -135,6 +148,32 @@ void session_set_user(Session *s, User *u) {
 
         s->user = u;
         LIST_PREPEND(sessions_by_user, u->sessions, s);
+
+        user_update_last_session_timer(u);
+}
+
+int session_set_leader(Session *s, pid_t pid) {
+        int r;
+
+        assert(s);
+
+        if (!pid_is_valid(pid))
+                return -EINVAL;
+
+        if (s->leader == pid)
+                return 0;
+
+        r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pid), s);
+        if (r < 0)
+                return r;
+
+        if (pid_is_valid(s->leader))
+                (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
+
+        s->leader = pid;
+        (void) audit_session_from_pid(pid, &s->audit_id);
+
+        return 1;
 }
 
 static void session_save_devices(Session *s, FILE *f) {
@@ -170,20 +209,21 @@ int session_save(Session *s) {
         if (r < 0)
                 goto fail;
 
-        assert(s->user);
-
-        fchmod(fileno(f), 0644);
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+        (void) fchmod(fileno(f), 0644);
 
         fprintf(f,
                 "# This is private data. Do not parse.\n"
                 "UID="UID_FMT"\n"
                 "USER=%s\n"
                 "ACTIVE=%i\n"
+                "IS_DISPLAY=%i\n"
                 "STATE=%s\n"
                 "REMOTE=%i\n",
                 s->user->uid,
                 s->user->name,
                 session_is_active(s),
+                s->user->display == s,
                 session_state_to_string(session_get_state(s)),
                 s->remote);
 
@@ -207,6 +247,9 @@ int session_save(Session *s) {
         if (s->tty)
                 fprintf(f, "TTY=%s\n", s->tty);
 
+        if (s->tty_validity >= 0)
+                fprintf(f, "TTY_VALIDITY=%s\n", tty_validity_to_string(s->tty_validity));
+
         if (s->display)
                 fprintf(f, "DISPLAY=%s\n", s->display);
 
@@ -343,6 +386,7 @@ static int session_load_devices(Session *s, const char *devices) {
 int session_load(Session *s) {
         _cleanup_free_ char *remote = NULL,
                 *seat = NULL,
+                *tty_validity = NULL,
                 *vtnr = NULL,
                 *state = NULL,
                 *position = NULL,
@@ -354,7 +398,8 @@ int session_load(Session *s) {
                 *monotonic = NULL,
                 *controller = NULL,
                 *active = NULL,
-                *devices = NULL;
+                *devices = NULL,
+                *is_display = NULL;
 
         int k, r;
 
@@ -367,6 +412,7 @@ int session_load(Session *s) {
                            "FIFO",           &s->fifo_path,
                            "SEAT",           &seat,
                            "TTY",            &s->tty,
+                           "TTY_VALIDITY",   &tty_validity,
                            "DISPLAY",        &s->display,
                            "REMOTE_HOST",    &s->remote_host,
                            "REMOTE_USER",    &s->remote_user,
@@ -384,6 +430,7 @@ int session_load(Session *s) {
                            "CONTROLLER",     &controller,
                            "ACTIVE",         &active,
                            "DEVICES",        &devices,
+                           "IS_DISPLAY",     &is_display,
                            NULL);
 
         if (r < 0)
@@ -442,9 +489,27 @@ int session_load(Session *s) {
                 seat_claim_position(s->seat, s, npos);
         }
 
+        if (tty_validity) {
+                TTYValidity v;
+
+                v = tty_validity_from_string(tty_validity);
+                if (v < 0)
+                        log_debug("Failed to parse TTY validity: %s", tty_validity);
+                else
+                        s->tty_validity = v;
+        }
+
         if (leader) {
-                if (parse_pid(leader, &s->leader) >= 0)
-                        (void) audit_session_from_pid(s->leader, &s->audit_id);
+                pid_t pid;
+
+                r = parse_pid(leader, &pid);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
+                else {
+                        r = session_set_leader(s, pid);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
+                }
         }
 
         if (type) {
@@ -491,6 +556,18 @@ int session_load(Session *s) {
                         s->was_active = k;
         }
 
+        if (is_display) {
+                /* Note that when enumerating users are loaded before sessions, hence the display session to use is
+                 * something we have to store along with the session and not the user, as in that case we couldn't
+                 * apply it at the time we load the user. */
+
+                k = parse_boolean(is_display);
+                if (k < 0)
+                        log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m");
+                else if (k > 0)
+                        s->user->display = s;
+        }
+
         if (controller) {
                 if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) {
                         session_set_controller(s, controller, false, false);
@@ -516,7 +593,7 @@ int session_activate(Session *s) {
 
         /* on seats with VTs, we let VTs manage session-switching */
         if (seat_has_vts(s->seat)) {
-                if (!s->vtnr)
+                if (s->vtnr == 0)
                         return -EOPNOTSUPP;
 
                 return chvt(s->vtnr);
@@ -539,18 +616,18 @@ int session_activate(Session *s) {
         return 0;
 }
 
-static int session_start_scope(Session *s, sd_bus_message *properties) {
+static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) {
         int r;
 
         assert(s);
         assert(s->user);
 
         if (!s->scope) {
-                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_free_ char *scope = NULL;
-                char *job = NULL;
                 const char *description;
 
+                s->scope_job = mfree(s->scope_job);
+
                 scope = strjoin("session-", s->id, ".scope");
                 if (!scope)
                         return log_oom();
@@ -563,17 +640,16 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
                                 s->leader,
                                 s->user->slice,
                                 description,
-                                "systemd-logind.service",
-                                "systemd-user-sessions.service",
+                                STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */
+                                STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */
+                                s->user->home,
                                 properties,
-                                &error,
-                                &job);
+                                error,
+                                &s->scope_job);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r));
-
+                        return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(error, r));
 
                 s->scope = TAKE_PTR(scope);
-                free_and_replace(s->scope_job, job);
         }
 
         if (s->scope)
@@ -582,7 +658,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
         return 0;
 }
 
-int session_start(Session *s, sd_bus_message *properties) {
+int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
         int r;
 
         assert(s);
@@ -590,6 +666,9 @@ int session_start(Session *s, sd_bus_message *properties) {
         if (!s->user)
                 return -ESTALE;
 
+        if (s->stopping)
+                return -EINVAL;
+
         if (s->started)
                 return 0;
 
@@ -597,8 +676,7 @@ int session_start(Session *s, sd_bus_message *properties) {
         if (r < 0)
                 return r;
 
-        /* Create cgroup */
-        r = session_start_scope(s, properties);
+        r = session_start_scope(s, properties, error);
         if (r < 0)
                 return r;
 
@@ -649,21 +727,24 @@ static int session_stop_scope(Session *s, bool force) {
          * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
          * when killing any processes left after this point. */
         r = manager_abandon_scope(s->manager, s->scope, &error);
-        if (r < 0)
+        if (r < 0) {
                 log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r));
+                sd_bus_error_free(&error);
+        }
+
+        s->scope_job = mfree(s->scope_job);
 
         /* Optionally, let's kill everything that's left now. */
         if (force || manager_shall_kill(s->manager, s->user->name)) {
-                char *job = NULL;
 
-                r = manager_stop_unit(s->manager, s->scope, &error, &job);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
+                r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
+                if (r < 0) {
+                        if (force)
+                                return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
 
-                free(s->scope_job);
-                s->scope_job = job;
+                        log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r));
+                }
         } else {
-                s->scope_job = mfree(s->scope_job);
 
                 /* With no killing, this session is allowed to persist in "closing" state indefinitely.
                  * Therefore session stop and session removal may be two distinct events.
@@ -683,8 +764,17 @@ int session_stop(Session *s, bool force) {
 
         assert(s);
 
+        /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API
+         * request via the bus (either directly for the session object or for the seat or user object this session
+         * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the
+         * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */
+
         if (!s->user)
                 return -ESTALE;
+        if (!s->started)
+                return 0;
+        if (s->stopping)
+                return 0;
 
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
 
@@ -776,7 +866,7 @@ int session_release(Session *s) {
         return sd_event_add_time(s->manager->event,
                                  &s->timer_event_source,
                                  CLOCK_MONOTONIC,
-                                 now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
+                                 usec_add(now(CLOCK_MONOTONIC), RELEASE_USEC), 0,
                                  release_timeout_callback, s);
 }
 
@@ -855,7 +945,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
 
         /* For sessions with a leader but no explicitly configured
          * tty, let's check the controlling tty of the leader */
-        if (s->leader > 0) {
+        if (pid_is_valid(s->leader)) {
                 r = get_process_ctty_atime(s->leader, &atime);
                 if (r >= 0)
                         goto found_atime;
@@ -939,7 +1029,8 @@ int session_create_fifo(Session *s) {
                 if (r < 0)
                         return r;
 
-                if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
+                s->fifo_path = strjoin("/run/systemd/sessions/", s->id, ".ref");
+                if (!s->fifo_path)
                         return -ENOMEM;
 
                 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
@@ -951,7 +1042,6 @@ int session_create_fifo(Session *s) {
                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
                 if (s->fifo_fd < 0)
                         return -errno;
-
         }
 
         if (!s->fifo_event_source) {
@@ -981,12 +1071,14 @@ static void session_remove_fifo(Session *s) {
         s->fifo_fd = safe_close(s->fifo_fd);
 
         if (s->fifo_path) {
-                unlink(s->fifo_path);
+                (void) unlink(s->fifo_path);
                 s->fifo_path = mfree(s->fifo_path);
         }
 }
 
 bool session_may_gc(Session *s, bool drop_not_started) {
+        int r;
+
         assert(s);
 
         if (drop_not_started && !s->started)
@@ -1000,11 +1092,25 @@ bool session_may_gc(Session *s, bool drop_not_started) {
                         return false;
         }
 
-        if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
-                return false;
+        if (s->scope_job) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                r = manager_job_is_active(s->manager, s->scope_job, &error);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", s->scope_job, bus_error_message(&error, r));
+                if (r != 0)
+                        return false;
+        }
+
+        if (s->scope) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
-        if (s->scope && manager_unit_is_active(s->manager, s->scope))
-                return false;
+                r = manager_unit_is_active(s->manager, s->scope, &error);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", s->scope, bus_error_message(&error, r));
+                if (r != 0)
+                        return false;
+        }
 
         return true;
 }
@@ -1309,3 +1415,11 @@ static const char* const kill_who_table[_KILL_WHO_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
+
+static const char* const tty_validity_table[_TTY_VALIDITY_MAX] = {
+        [TTY_FROM_PAM] = "from-pam",
+        [TTY_FROM_UTMP] = "from-utmp",
+        [TTY_UTMP_INCONSISTENT] = "utmp-inconsistent",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(tty_validity, TTYValidity);
index 29ca399dafac365c3eb110a303db7612edf970c6..4f9c967dd93f4048ff08ec9d6887973cccafdd96 100644 (file)
@@ -46,6 +46,14 @@ enum KillWho {
         _KILL_WHO_INVALID = -1
 };
 
+typedef enum TTYValidity {
+        TTY_FROM_PAM,
+        TTY_FROM_UTMP,
+        TTY_UTMP_INCONSISTENT, /* may happen on ssh sessions with multiplexed TTYs */
+        _TTY_VALIDITY_MAX,
+        _TTY_VALIDITY_INVALID = -1,
+} TTYValidity;
+
 struct Session {
         Manager *manager;
 
@@ -60,8 +68,9 @@ struct Session {
 
         dual_timestamp timestamp;
 
-        char *tty;
         char *display;
+        char *tty;
+        TTYValidity tty_validity;
 
         bool remote;
         char *remote_user;
@@ -97,6 +106,7 @@ struct Session {
 
         sd_bus_message *create_message;
 
+        /* Set up when a client requested to release the session via the bus */
         sd_event_source *timer_event_source;
 
         char *controller;
@@ -109,9 +119,13 @@ struct Session {
         LIST_FIELDS(Session, gc_queue);
 };
 
-Session *session_new(Manager *m, const char *id);
-void session_free(Session *s);
+int session_new(Session **ret, Manager *m, const char *id);
+Session* session_free(Session *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free);
+
 void session_set_user(Session *s, User *u);
+int session_set_leader(Session *s, pid_t pid);
 bool session_may_gc(Session *s, bool drop_not_started);
 void session_add_to_gc_queue(Session *s);
 int session_activate(Session *s);
@@ -121,7 +135,7 @@ void session_set_idle_hint(Session *s, bool b);
 int session_get_locked_hint(Session *s);
 void session_set_locked_hint(Session *s, bool b);
 int session_create_fifo(Session *s);
-int session_start(Session *s, sd_bus_message *properties);
+int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
 int session_stop(Session *s, bool force);
 int session_finalize(Session *s);
 int session_release(Session *s);
@@ -155,6 +169,9 @@ SessionClass session_class_from_string(const char *s) _pure_;
 const char *kill_who_to_string(KillWho k) _const_;
 KillWho kill_who_from_string(const char *s) _pure_;
 
+const char* tty_validity_to_string(TTYValidity t) _const_;
+TTYValidity tty_validity_from_string(const char *s) _pure_;
+
 int session_prepare_vt(Session *s);
 void session_restore_vt(Session *s);
 void session_leave_vt(Session *s);
index c662a26b9fa1c501ffdd94883f3f7b39dbf6ca24..9620fb0cfc2344c247531d5ff8f98e1bc96c5918 100644 (file)
@@ -109,7 +109,7 @@ static int property_get_idle_since_hint(
         assert(reply);
         assert(u);
 
-        user_get_idle_hint(u, &t);
+        (void) user_get_idle_hint(u, &t);
         k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
 
         return sd_bus_message_append(reply, "t", k);
index f2664c323ea86367717bfb8a6035b7a3a6af16a8..16a83ae5d5432746c5f19b1a8cfa21c948d08c3b 100644 (file)
 #include "special.h"
 #include "stdio-util.h"
 #include "string-table.h"
+#include "strv.h"
 #include "unit-name.h"
 #include "user-util.h"
 #include "util.h"
 
-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
+int user_new(User **ret,
+             Manager *m,
+             uid_t uid,
+             gid_t gid,
+             const char *name,
+             const char *home) {
+
         _cleanup_(user_freep) User *u = NULL;
         char lu[DECIMAL_STR_MAX(uid_t) + 1];
         int r;
 
-        assert(out);
+        assert(ret);
         assert(m);
         assert(name);
 
-        u = new0(User, 1);
+        u = new(User, 1);
         if (!u)
                 return -ENOMEM;
 
-        u->manager = m;
-        u->uid = uid;
-        u->gid = gid;
-        xsprintf(lu, UID_FMT, uid);
+        *u = (User) {
+                .manager = m,
+                .uid = uid,
+                .gid = gid,
+                .last_session_timestamp = USEC_INFINITY,
+        };
 
         u->name = strdup(name);
         if (!u->name)
                 return -ENOMEM;
 
+        u->home = strdup(home);
+        if (!u->home)
+                return -ENOMEM;
+
         if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
                 return -ENOMEM;
 
         if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
                 return -ENOMEM;
 
+        xsprintf(lu, UID_FMT, uid);
         r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
         if (r < 0)
                 return r;
@@ -66,6 +80,10 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
         if (r < 0)
                 return r;
 
+        r = unit_name_build("user-runtime-dir", lu, ".service", &u->runtime_dir_service);
+        if (r < 0)
+                return r;
+
         r = hashmap_put(m->users, UID_TO_PTR(uid), u);
         if (r < 0)
                 return r;
@@ -78,8 +96,11 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
         if (r < 0)
                 return r;
 
-        *out = TAKE_PTR(u);
+        r = hashmap_put(m->user_units, u->runtime_dir_service, u);
+        if (r < 0)
+                return r;
 
+        *ret = TAKE_PTR(u);
         return 0;
 }
 
@@ -96,19 +117,25 @@ User *user_free(User *u) {
         if (u->service)
                 hashmap_remove_value(u->manager->user_units, u->service, u);
 
+        if (u->runtime_dir_service)
+                hashmap_remove_value(u->manager->user_units, u->runtime_dir_service, u);
+
         if (u->slice)
                 hashmap_remove_value(u->manager->user_units, u->slice, u);
 
         hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
 
-        u->slice_job = mfree(u->slice_job);
+        (void) sd_event_source_unref(u->timer_event_source);
+
         u->service_job = mfree(u->service_job);
 
         u->service = mfree(u->service);
+        u->runtime_dir_service = mfree(u->runtime_dir_service);
         u->slice = mfree(u->slice);
         u->runtime_path = mfree(u->runtime_path);
         u->state_file = mfree(u->state_file);
         u->name = mfree(u->name);
+        u->home = mfree(u->home);
 
         return mfree(u);
 }
@@ -135,9 +162,11 @@ static int user_save_internal(User *u) {
         fprintf(f,
                 "# This is private data. Do not parse.\n"
                 "NAME=%s\n"
-                "STATE=%s\n",
+                "STATE=%s\n"         /* friendly user-facing state */
+                "STOPPING=%s\n",     /* low-level state */
                 u->name,
-                user_state_to_string(user_get_state(u)));
+                user_state_to_string(user_get_state(u)),
+                yes_no(u->stopping));
 
         /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
         if (u->runtime_path)
@@ -146,9 +175,6 @@ static int user_save_internal(User *u) {
         if (u->service_job)
                 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
 
-        if (u->slice_job)
-                fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
-
         if (u->display)
                 fprintf(f, "DISPLAY=%s\n", u->display->id);
 
@@ -159,6 +185,10 @@ static int user_save_internal(User *u) {
                         u->timestamp.realtime,
                         u->timestamp.monotonic);
 
+        if (u->last_session_timestamp != USEC_INFINITY)
+                fprintf(f, "LAST_SESSION_TIMESTAMP=" USEC_FMT "\n",
+                        u->last_session_timestamp);
+
         if (u->sessions) {
                 Session *i;
                 bool first;
@@ -272,103 +302,83 @@ int user_save(User *u) {
         if (!u->started)
                 return 0;
 
-        return user_save_internal (u);
+        return user_save_internal(u);
 }
 
 int user_load(User *u) {
-        _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
-        Session *s = NULL;
+        _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL;
         int r;
 
         assert(u);
 
         r = parse_env_file(NULL, u->state_file, NEWLINE,
-                           "SERVICE_JOB", &u->service_job,
-                           "SLICE_JOB",   &u->slice_job,
-                           "DISPLAY",     &display,
-                           "REALTIME",    &realtime,
-                           "MONOTONIC",   &monotonic,
+                           "SERVICE_JOB",            &u->service_job,
+                           "STOPPING",               &stopping,
+                           "REALTIME",               &realtime,
+                           "MONOTONIC",              &monotonic,
+                           "LAST_SESSION_TIMESTAMP", &last_session_timestamp,
                            NULL);
-        if (r < 0) {
-                if (r == -ENOENT)
-                        return 0;
-
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
                 return log_error_errno(r, "Failed to read %s: %m", u->state_file);
-        }
 
-        if (display)
-                s = hashmap_get(u->manager->sessions, display);
-
-        if (s && s->display && display_is_local(s->display))
-                u->display = s;
+        if (stopping) {
+                r = parse_boolean(stopping);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse 'STOPPING' boolean: %s", stopping);
+                else
+                        u->stopping = r;
+        }
 
         if (realtime)
-                timestamp_deserialize(realtime, &u->timestamp.realtime);
+                (void) timestamp_deserialize(realtime, &u->timestamp.realtime);
         if (monotonic)
-                timestamp_deserialize(monotonic, &u->timestamp.monotonic);
+                (void) timestamp_deserialize(monotonic, &u->timestamp.monotonic);
+        if (last_session_timestamp)
+                (void) timestamp_deserialize(last_session_timestamp, &u->last_session_timestamp);
 
-        return r;
+        return 0;
 }
 
-static int user_start_service(User *u) {
+static void user_start_service(User *u) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        char *job;
         int r;
 
         assert(u);
 
+        /* Start the service containing the "systemd --user" instance (user@.service). Note that we don't explicitly
+         * start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by
+         * user@.service and the session scopes as dependencies. */
+
         u->service_job = mfree(u->service_job);
 
-        r = manager_start_unit(
-                        u->manager,
-                        u->service,
-                        &error,
-                        &job);
+        r = manager_start_unit(u->manager, u->service, &error, &u->service_job);
         if (r < 0)
-                /* we don't fail due to this, let's try to continue */
-                log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
-        else
-                u->service_job = job;
-
-        return 0;
+                log_warning_errno(r, "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
 }
 
 int user_start(User *u) {
-        int r;
-
         assert(u);
 
         if (u->started && !u->stopping)
                 return 0;
 
-        /*
-         * If u->stopping is set, the user is marked for removal and the slice
-         * and service stop-jobs are queued. We have to clear that flag before
-         * queing the start-jobs again. If they succeed, the user object can be
-         * re-used just fine (pid1 takes care of job-ordering and proper
-         * restart), but if they fail, we want to force another user_stop() so
-         * possibly pending units are stopped.
-         * Note that we don't clear u->started, as we have no clue what state
-         * the user is in on failure here. Hence, we pretend the user is
-         * running so it will be properly taken down by GC. However, we clearly
-         * return an error from user_start() in that case, so no further
-         * reference to the user is taken.
-         */
+        /* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear
+         * that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine
+         * (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop()
+         * so possibly pending units are stopped. */
         u->stopping = false;
 
         if (!u->started)
                 log_debug("Starting services for new user %s.", u->name);
 
-        /* Save the user data so far, because pam_systemd will read the
-         * XDG_RUNTIME_DIR out of it while starting up systemd --user.
-         * We need to do user_save_internal() because we have not
-         * "officially" started yet. */
+        /* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up
+         * systemd --user.  We need to do user_save_internal() because we have not "officially" started yet. */
         user_save_internal(u);
 
-        /* Spawn user systemd */
-        r = user_start_service(u);
-        if (r < 0)
-                return r;
+        /* Start user@UID.service */
+        user_start_service(u);
 
         if (!u->started) {
                 if (!dual_timestamp_is_set(&u->timestamp))
@@ -383,60 +393,50 @@ int user_start(User *u) {
         return 0;
 }
 
-static int user_stop_slice(User *u) {
+static void user_stop_service(User *u) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        char *job;
         int r;
 
         assert(u);
+        assert(u->service);
 
-        r = manager_stop_unit(u->manager, u->slice, &error, &job);
-        if (r < 0)
-                return log_error_errno(r, "Failed to stop user slice: %s", bus_error_message(&error, r));
+        /* The reverse of user_start_service(). Note that we only stop user@UID.service here, and let StopWhenUnneeded=
+         * deal with the slice and the user-runtime-dir@.service instance. */
 
-        return free_and_replace(u->slice_job, job);
-}
-
-static int user_stop_service(User *u) {
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        char *job;
-        int r;
-
-        assert(u);
+        u->service_job = mfree(u->service_job);
 
-        r = manager_stop_unit(u->manager, u->service, &error, &job);
+        r = manager_stop_unit(u->manager, u->service, &error, &u->service_job);
         if (r < 0)
-                return log_error_errno(r, "Failed to stop user service: %s", bus_error_message(&error, r));
-
-        return free_and_replace(u->service_job, job);
+                log_warning_errno(r, "Failed to stop user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
 }
 
 int user_stop(User *u, bool force) {
         Session *s;
-        int r = 0, k;
+        int r = 0;
         assert(u);
 
-        /* Stop jobs have already been queued */
-        if (u->stopping) {
+        /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API
+         * request to do so via the bus (in which case 'force' is true) and automatically due to GC, if there's no
+         * session left pinning it (in which case 'force' is false). Note that this just initiates tearing down of the
+         * user, the User object will remain in memory until user_finalize() is called, see below. */
+
+        if (!u->started)
+                return 0;
+
+        if (u->stopping) { /* Stop jobs have already been queued */
                 user_save(u);
-                return r;
+                return 0;
         }
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
+                int k;
+
                 k = session_stop(s, force);
                 if (k < 0)
                         r = k;
         }
 
-        /* Kill systemd */
-        k = user_stop_service(u);
-        if (k < 0)
-                r = k;
-
-        /* Kill cgroup */
-        k = user_stop_slice(u);
-        if (k < 0)
-                r = k;
+        user_stop_service(u);
 
         u->stopping = true;
 
@@ -451,6 +451,9 @@ int user_finalize(User *u) {
 
         assert(u);
 
+        /* Called when the user is really ready to be freed, i.e. when all unit stop jobs and suchlike for it are
+         * done. This is called as a result of an earlier user_done() when all jobs are completed. */
+
         if (u->started)
                 log_debug("User %s logged out.", u->name);
 
@@ -472,7 +475,7 @@ int user_finalize(User *u) {
                         r = k;
         }
 
-        unlink(u->state_file);
+        (void) unlink(u->state_file);
         user_add_to_gc_queue(u);
 
         if (u->started) {
@@ -528,11 +531,40 @@ int user_check_linger_file(User *u) {
                 return -ENOMEM;
 
         p = strjoina("/var/lib/systemd/linger/", cc);
+        if (access(p, F_OK) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
 
-        return access(p, F_OK) >= 0;
+                return false;
+        }
+
+        return true;
+}
+
+static bool user_unit_active(User *u) {
+        const char *i;
+        int r;
+
+        assert(u->service);
+        assert(u->runtime_dir_service);
+        assert(u->slice);
+
+        FOREACH_STRING(i, u->service, u->runtime_dir_service, u->slice) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                r = manager_unit_is_active(u->manager, i, &error);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", u->service, bus_error_message(&error, r));
+                if (r != 0)
+                        return true;
+        }
+
+        return false;
 }
 
 bool user_may_gc(User *u, bool drop_not_started) {
+        int r;
+
         assert(u);
 
         if (drop_not_started && !u->started)
@@ -541,15 +573,36 @@ bool user_may_gc(User *u, bool drop_not_started) {
         if (u->sessions)
                 return false;
 
-        if (user_check_linger_file(u) > 0)
-                return false;
+        if (u->last_session_timestamp != USEC_INFINITY) {
+                /* All sessions have been closed. Let's see if we shall leave the user record around for a bit */
 
-        if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
-                return false;
+                if (u->manager->user_stop_delay == USEC_INFINITY)
+                        return false; /* Leave it around forever! */
+                if (u->manager->user_stop_delay > 0 &&
+                    now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay))
+                        return false; /* Leave it around for a bit longer. */
+        }
 
-        if (u->service_job && manager_job_is_active(u->manager, u->service_job))
+        /* Is this a user that shall stay around forever ("linger")? Before we say "no" to GC'ing for lingering users, let's check
+         * if any of the three units that we maintain for this user is still around. If none of them is,
+         * there's no need to keep this user around even if lingering is enabled. */
+        if (user_check_linger_file(u) > 0 && user_unit_active(u))
                 return false;
 
+        /* Check if our job is still pending */
+        if (u->service_job) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                r = manager_job_is_active(u->manager, u->service_job, &error);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", u->service_job, bus_error_message(&error, r));
+                if (r != 0)
+                        return false;
+        }
+
+        /* Note that we don't care if the three units we manage for each user object are up or not, as we are managing
+         * their state rather than tracking it. */
+
         return true;
 }
 
@@ -571,7 +624,7 @@ UserState user_get_state(User *u) {
         if (u->stopping)
                 return USER_CLOSING;
 
-        if (!u->started || u->slice_job || u->service_job)
+        if (!u->started || u->service_job)
                 return USER_OPENING;
 
         if (u->sessions) {
@@ -590,7 +643,7 @@ UserState user_get_state(User *u) {
                 return all_closing ? USER_CLOSING : USER_ONLINE;
         }
 
-        if (user_check_linger_file(u) > 0)
+        if (user_check_linger_file(u) > 0 && user_unit_active(u))
                 return USER_LINGERING;
 
         return USER_CLOSING;
@@ -603,11 +656,10 @@ int user_kill(User *u, int signo) {
 }
 
 static bool elect_display_filter(Session *s) {
-        /* Return true if the session is a candidate for the user’s ‘primary
-         * session’ or ‘display’. */
+        /* Return true if the session is a candidate for the user’s ‘primary session’ or ‘display’. */
         assert(s);
 
-        return (s->class == SESSION_USER && !s->stopping);
+        return s->class == SESSION_USER && s->started && !s->stopping;
 }
 
 static int elect_display_compare(Session *s1, Session *s2) {
@@ -653,9 +705,8 @@ void user_elect_display(User *u) {
 
         assert(u);
 
-        /* This elects a primary session for each user, which we call
-         * the "display". We try to keep the assignment stable, but we
-         * "upgrade" to better choices. */
+        /* This elects a primary session for each user, which we call the "display". We try to keep the assignment
+         * stable, but we "upgrade" to better choices. */
         log_debug("Electing new display for user %s", u->name);
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
@@ -671,6 +722,59 @@ void user_elect_display(User *u) {
         }
 }
 
+static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
+        User *u = userdata;
+
+        assert(u);
+        user_add_to_gc_queue(u);
+
+        return 0;
+}
+
+void user_update_last_session_timer(User *u) {
+        int r;
+
+        assert(u);
+
+        if (u->sessions) {
+                /* There are sessions, turn off the timer */
+                u->last_session_timestamp = USEC_INFINITY;
+                u->timer_event_source = sd_event_source_unref(u->timer_event_source);
+                return;
+        }
+
+        if (u->last_session_timestamp != USEC_INFINITY)
+                return; /* Timer already started */
+
+        u->last_session_timestamp = now(CLOCK_MONOTONIC);
+
+        assert(!u->timer_event_source);
+
+        if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY)
+                return;
+
+        if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) {
+                log_debug("Not allocating user stop timeout, since we are already exiting.");
+                return;
+        }
+
+        r = sd_event_add_time(u->manager->event,
+                              &u->timer_event_source,
+                              CLOCK_MONOTONIC,
+                              usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0,
+                              user_stop_timeout_callback, u);
+        if (r < 0)
+                log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m");
+
+        if (DEBUG_LOGGING) {
+                char s[FORMAT_TIMESPAN_MAX];
+
+                log_debug("Last session of user '%s' logged out, terminating user context in %s.",
+                          u->name,
+                          format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC));
+        }
+}
+
 static const char* const user_state_table[_USER_STATE_MAX] = {
         [USER_OFFLINE] = "offline",
         [USER_OPENING] = "opening",
index eba23252841149941992ce5cb9a2107d7471acf6..c41973e27dc01d8e64eed83991ff95b2fee24c1b 100644 (file)
@@ -23,27 +23,34 @@ struct User {
         uid_t uid;
         gid_t gid;
         char *name;
+        char *home;
         char *state_file;
         char *runtime_path;
-        char *slice;
-        char *service;
+
+        char *slice;                     /* user-UID.slice */
+        char *service;                   /* user@UID.service */
+        char *runtime_dir_service;       /* user-runtime-dir@UID.service */
 
         char *service_job;
-        char *slice_job;
 
         Session *display;
 
-        dual_timestamp timestamp;
+        dual_timestamp timestamp;      /* When this User object was 'started' the first time */
+        usec_t last_session_timestamp; /* When the number of sessions of this user went from 1 to 0 the last time */
+
+        /* Set up when the last session of the user logs out */
+        sd_event_source *timer_event_source;
 
         bool in_gc_queue:1;
-        bool started:1;
-        bool stopping:1;
+
+        bool started:1;       /* Whenever the user being started, has been started or is being stopped again. */
+        bool stopping:1;      /* Whenever the user is being stopped or has been stopped. */
 
         LIST_HEAD(Session, sessions);
         LIST_FIELDS(User, gc_queue);
 };
 
-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name);
+int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home);
 User *user_free(User *u);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
@@ -60,6 +67,7 @@ int user_load(User *u);
 int user_kill(User *u, int signo);
 int user_check_linger_file(User *u);
 void user_elect_display(User *u);
+void user_update_last_session_timer(User *u);
 
 extern const sd_bus_vtable user_vtable[];
 int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
index e90c8575dcd821b6edcf63b28b170d647a8017c0..44899064c59cdfb5dbc146d6ab6aace66a46076e 100644 (file)
@@ -35,18 +35,21 @@ static int manager_new(Manager **ret) {
 
         assert(ret);
 
-        m = new0(Manager, 1);
+        m = new(Manager, 1);
         if (!m)
                 return -ENOMEM;
 
-        m->console_active_fd = -1;
-        m->reserve_vt_fd = -1;
+        *m = (Manager) {
+                .console_active_fd = -1,
+                .reserve_vt_fd = -1,
+        };
 
         m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
 
         m->devices = hashmap_new(&string_hash_ops);
         m->seats = hashmap_new(&string_hash_ops);
         m->sessions = hashmap_new(&string_hash_ops);
+        m->sessions_by_leader = hashmap_new(NULL);
         m->users = hashmap_new(NULL);
         m->inhibitors = hashmap_new(&string_hash_ops);
         m->buttons = hashmap_new(&string_hash_ops);
@@ -54,7 +57,7 @@ static int manager_new(Manager **ret) {
         m->user_units = hashmap_new(&string_hash_ops);
         m->session_units = hashmap_new(&string_hash_ops);
 
-        if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
+        if (!m->devices || !m->seats || !m->sessions || !m->sessions_by_leader || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
                 return -ENOMEM;
 
         r = sd_event_default(&m->event);
@@ -109,6 +112,7 @@ static Manager* manager_unref(Manager *m) {
         hashmap_free(m->devices);
         hashmap_free(m->seats);
         hashmap_free(m->sessions);
+        hashmap_free(m->sessions_by_leader);
         hashmap_free(m->users);
         hashmap_free(m->inhibitors);
         hashmap_free(m->buttons);
@@ -129,6 +133,10 @@ static Manager* manager_unref(Manager *m) {
         sd_event_source_unref(m->udev_button_event_source);
         sd_event_source_unref(m->lid_switch_ignore_event_source);
 
+#if ENABLE_UTMP
+        sd_event_source_unref(m->utmp_event_source);
+#endif
+
         safe_close(m->console_active_fd);
 
         udev_monitor_unref(m->udev_seat_monitor);
@@ -787,28 +795,28 @@ static int manager_connect_console(Manager *m) {
         assert(m);
         assert(m->console_active_fd < 0);
 
-        /* On certain architectures (S390 and Xen, and containers),
-           /dev/tty0 does not exist, so don't fail if we can't open
-           it. */
+        /* On certain systems (such as S390, Xen, and containers) /dev/tty0 does not exist (as there is no VC), so
+         * don't fail if we can't open it. */
+
         if (access("/dev/tty0", F_OK) < 0)
                 return 0;
 
         m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC);
         if (m->console_active_fd < 0) {
 
-                /* On some systems the device node /dev/tty0 may exist
-                 * even though /sys/class/tty/tty0 does not. */
-                if (errno == ENOENT)
+                /* On some systems /dev/tty0 may exist even though /sys/class/tty/tty0 does not. These are broken, but
+                 * common. Let's complain but continue anyway. */
+                if (errno == ENOENT) {
+                        log_warning_errno(errno, "System has /dev/tty0 but not /sys/class/tty/tty0/active which is broken, ignoring: %m");
                         return 0;
+                }
 
                 return log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m");
         }
 
         r = sd_event_add_io(m->event, &m->console_active_event_source, m->console_active_fd, 0, manager_dispatch_console, m);
-        if (r < 0) {
-                log_error("Failed to watch foreground console");
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to watch foreground console: %m");
 
         /*
          * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used
@@ -827,7 +835,7 @@ static int manager_connect_console(Manager *m) {
 
         r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to subscribe to signal: %m");
 
         return 0;
 }
@@ -951,13 +959,13 @@ static void manager_gc(Manager *m, bool drop_not_started) {
                 /* First, if we are not closing yet, initiate stopping */
                 if (session_may_gc(session, drop_not_started) &&
                     session_get_state(session) != SESSION_CLOSING)
-                        session_stop(session, false);
+                        (void) session_stop(session, false);
 
                 /* Normally, this should make the session referenced
                  * again, if it doesn't then let's get rid of it
                  * immediately */
                 if (session_may_gc(session, drop_not_started)) {
-                        session_finalize(session);
+                        (void) session_finalize(session);
                         session_free(session);
                 }
         }
@@ -968,11 +976,11 @@ static void manager_gc(Manager *m, bool drop_not_started) {
 
                 /* First step: queue stop jobs */
                 if (user_may_gc(user, drop_not_started))
-                        user_stop(user, false);
+                        (void) user_stop(user, false);
 
                 /* Second step: finalize user */
                 if (user_may_gc(user, drop_not_started)) {
-                        user_finalize(user);
+                        (void) user_finalize(user);
                         user_free(user);
                 }
         }
@@ -1067,6 +1075,9 @@ static int manager_startup(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to register SIGHUP handler: %m");
 
+        /* Connect to utmp */
+        manager_connect_utmp(m);
+
         /* Connect to console */
         r = manager_connect_console(m);
         if (r < 0)
@@ -1122,15 +1133,18 @@ static int manager_startup(Manager *m) {
         /* Reserve the special reserved VT */
         manager_reserve_vt(m);
 
+        /* Read in utmp if it exists */
+        manager_read_utmp(m);
+
         /* And start everything */
         HASHMAP_FOREACH(seat, m->seats, i)
-                seat_start(seat);
+                (void) seat_start(seat);
 
         HASHMAP_FOREACH(user, m->users, i)
-                user_start(user);
+                (void) user_start(user);
 
         HASHMAP_FOREACH(session, m->sessions, i)
-                session_start(session, NULL);
+                (void) session_start(session, NULL, NULL);
 
         HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
                 inhibitor_start(inhibitor);
index e25bc1e13fbb128fa805bc4ab96dbea821a10c37..b1a7b932eb91f21e8aacdc01407479a7cc785986 100644 (file)
@@ -27,6 +27,7 @@ struct Manager {
         Hashmap *devices;
         Hashmap *seats;
         Hashmap *sessions;
+        Hashmap *sessions_by_leader;
         Hashmap *users;
         Hashmap *inhibitors;
         Hashmap *buttons;
@@ -43,6 +44,10 @@ struct Manager {
         sd_event_source *udev_vcsa_event_source;
         sd_event_source *udev_button_event_source;
 
+#if ENABLE_UTMP
+        sd_event_source *utmp_event_source;
+#endif
+
         int console_active_fd;
 
         unsigned n_autovts;
@@ -62,6 +67,7 @@ struct Manager {
         Hashmap *user_units;
 
         usec_t inhibit_delay_max;
+        usec_t user_stop_delay;
 
         /* If an action is currently being executed or is delayed,
          * this is != 0 and encodes what is being done */
@@ -128,7 +134,7 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
 int manager_add_button(Manager *m, const char *name, Button **_button);
 int manager_add_seat(Manager *m, const char *id, Seat **_seat);
 int manager_add_session(Manager *m, const char *id, Session **_session);
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
+int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **_user);
 int manager_add_user_by_name(Manager *m, const char *name, User **_user);
 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
@@ -149,6 +155,10 @@ bool manager_is_docked_or_external_displays(Manager *m);
 bool manager_is_on_external_power(void);
 bool manager_all_buttons_ignored(Manager *m);
 
+int manager_read_utmp(Manager *m);
+void manager_connect_utmp(Manager *m);
+void manager_reconnect_utmp(Manager *m);
+
 extern const sd_bus_vtable manager_vtable[];
 
 int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
@@ -161,13 +171,13 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
 
 int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
 
-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job);
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job);
 int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
 int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error);
-int manager_unit_is_active(Manager *manager, const char *unit);
-int manager_job_is_active(Manager *manager, const char *path);
+int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error);
+int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error);
 
 /* gperf lookup function */
 const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
index 0e1ed18f7a812581f86918be1b38b155ad3927a5..1cc75fd1cfeee3102795f2647c2fa5a9ee4a79c2 100644 (file)
@@ -58,7 +58,6 @@ loginctl_sources = files('''
 
 user_runtime_dir_sources = files('''
         user-runtime-dir.c
-        logind.h
 '''.split())
 
 if conf.get('ENABLE_LOGIND') == 1
index 90ccbd7a830477be7baeeec450fd15eb8561f1ae..e2b2281c6b4018ae930e220f37e91d0e77da9895 100644 (file)
@@ -140,13 +140,11 @@ static int socket_from_display(const char *display, char **path) {
 }
 
 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
-        union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-        };
+        union sockaddr_union sa = {};
         _cleanup_free_ char *p = NULL, *tty = NULL;
         _cleanup_close_ int fd = -1;
         struct ucred ucred;
-        int v, r;
+        int v, r, salen;
 
         assert(display);
         assert(vtnr);
@@ -160,13 +158,15 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
         r = socket_from_display(display, &p);
         if (r < 0)
                 return r;
-        strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path));
+        salen = sockaddr_un_set_path(&sa.un, p);
+        if (salen < 0)
+                return salen;
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
         if (fd < 0)
                 return -errno;
 
-        if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+        if (connect(fd, &sa.sa, salen) < 0)
                 return -errno;
 
         r = getpeercred(fd, &ucred);
@@ -308,6 +308,36 @@ static int update_environment(pam_handle_t *handle, const char *key, const char
         return r;
 }
 
+static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
+        struct stat st;
+
+        assert(path);
+
+        /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set
+         * up properly for us. */
+
+        if (lstat(path, &st) < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno));
+                goto fail;
+        }
+
+        if (!S_ISDIR(st.st_mode)) {
+                pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path);
+                goto fail;
+        }
+
+        if (st.st_uid != uid) {
+                pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid);
+                goto fail;
+        }
+
+        return true;
+
+fail:
+        pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
+        return false;
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
@@ -367,10 +397,12 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
                         return PAM_BUF_ERR;
 
-                r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
-                if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
-                        return r;
+                if (validate_runtime_directory(handle, rt, pw->pw_uid)) {
+                        r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
+                        if (r != PAM_SUCCESS) {
+                                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
+                                return r;
+                        }
                 }
 
                 return PAM_SUCCESS;
@@ -574,9 +606,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                  * in privileged apps clobbering the runtime directory
                  * unnecessarily. */
 
-                r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
-                if (r != PAM_SUCCESS)
-                        return r;
+                if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) {
+                        r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
+                        if (r != PAM_SUCCESS)
+                                return r;
+                }
         }
 
         /* Most likely we got the session/type/class from environment variables, but might have gotten the data
index 69f95a25ab70d7748fb49cc33c7c305eeec79d05..a149e96b2689b79b1aa5d8a0733ca0ac23245c08 100644 (file)
@@ -3,9 +3,11 @@
 #include <stdint.h>
 #include <sys/mount.h>
 
+#include "sd-bus.h"
+
+#include "bus-error.h"
 #include "fs-util.h"
 #include "label.h"
-#include "logind.h"
 #include "mkdir.h"
 #include "mount-util.h"
 #include "path-util.h"
 #include "strv.h"
 #include "user-util.h"
 
-static int gather_configuration(size_t *runtime_dir_size) {
-        Manager m = {};
+static int acquire_runtime_dir_size(uint64_t *ret) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
         int r;
 
-        manager_reset_config(&m);
+        r = sd_bus_default_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to system bus: %m");
 
-        r = manager_parse_config_file(&m);
+        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret);
         if (r < 0)
-                log_warning_errno(r, "Failed to parse logind.conf: %m");
+                return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r));
 
-        *runtime_dir_size = m.runtime_dir_size;
         return 0;
 }
 
-static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gid, size_t runtime_dir_size) {
+static int user_mkdir_runtime_path(
+                const char *runtime_path,
+                uid_t uid,
+                gid_t gid,
+                uint64_t runtime_dir_size) {
+
         int r;
 
         assert(runtime_path);
@@ -49,10 +58,10 @@ static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gi
                 char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
                              + DECIMAL_STR_MAX(uid_t)
                              + DECIMAL_STR_MAX(gid_t)
-                             + DECIMAL_STR_MAX(size_t)];
+                             + DECIMAL_STR_MAX(uint64_t)];
 
                 xsprintf(options,
-                         "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
+                         "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s",
                          uid, gid, runtime_dir_size,
                          mac_smack_use() ? ",smackfsroot=*" : "");
 
@@ -113,7 +122,7 @@ static int user_remove_runtime_path(const char *runtime_path) {
 
 static int do_mount(const char *user) {
         char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
-        size_t runtime_dir_size;
+        uint64_t runtime_dir_size;
         uid_t uid;
         gid_t gid;
         int r;
@@ -126,9 +135,11 @@ static int do_mount(const char *user) {
                                                     : "Failed to look up user \"%s\": %m",
                                        user);
 
-        xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
+        r = acquire_runtime_dir_size(&runtime_dir_size);
+        if (r < 0)
+                return r;
 
-        assert_se(gather_configuration(&runtime_dir_size) == 0);
+        xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
 
         log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
         return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
index 2f21f99957d74f45c44c24afcee9e6506e822479..d408d80c14f50524803ead68519865a028e014c6 100644 (file)
@@ -22,6 +22,7 @@
 #include "cgroup-show.h"
 #include "cgroup-util.h"
 #include "copy.h"
+#include "def.h"
 #include "env-util.h"
 #include "fd-util.h"
 #include "format-table.h"
@@ -37,6 +38,7 @@
 #include "path-util.h"
 #include "process-util.h"
 #include "ptyfwd.h"
+#include "rlimit-util.h"
 #include "sigbus.h"
 #include "signal-util.h"
 #include "spawn-polkit-agent.h"
@@ -3030,6 +3032,10 @@ int main(int argc, char*argv[]) {
         setlocale(LC_ALL, "");
         log_parse_environment();
         log_open();
+
+        /* The journal merging logic potentially needs a lot of fds. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         sigbus_install();
 
         r = parse_argv(argc, argv);
index 33389676cf195ad77dade35a59b6009f1bd3a789..529d942f1534b8e89fc8fb4a8f4147391b3fef3a 100644 (file)
@@ -433,7 +433,7 @@ int route_remove(Route *route, Link *link,
         if (r < 0)
                 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
 
-        if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) {
+        if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
                 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
                 if (r < 0)
                         return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
@@ -582,7 +582,7 @@ int route_configure(
         if (r < 0)
                 return log_error_errno(r, "Could not set route type: %m");
 
-        if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) {
+        if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
                 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
                 if (r < 0)
                         return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
@@ -1045,6 +1045,8 @@ int config_parse_route_type(
                 n->type = RTN_UNREACHABLE;
         else if (streq(rvalue, "prohibit"))
                 n->type = RTN_PROHIBIT;
+        else if (streq(rvalue, "throw"))
+                n->type = RTN_THROW;
         else {
                 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
index 134c4c3b5d7d83729dab8581aa34e541fa9efd4c..fb360a6f4f1590c04e608e8f403837c803db9e28 100644 (file)
@@ -2812,10 +2812,10 @@ static int inner_child(
 }
 
 static int setup_sd_notify_child(void) {
-        static const int one = 1;
-        int fd = -1;
+        _cleanup_close_ int fd = -1;
         union sockaddr_union sa = {
-                .sa.sa_family = AF_UNIX,
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = NSPAWN_NOTIFY_SOCKET_PATH,
         };
         int r;
 
@@ -2824,28 +2824,21 @@ static int setup_sd_notify_child(void) {
                 return log_error_errno(errno, "Failed to allocate notification socket: %m");
 
         (void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755);
-        (void) unlink(NSPAWN_NOTIFY_SOCKET_PATH);
+        (void) sockaddr_un_unlink(&sa.un);
 
-        strncpy(sa.un.sun_path, NSPAWN_NOTIFY_SOCKET_PATH, sizeof(sa.un.sun_path));
         r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
-        if (r < 0) {
-                safe_close(fd);
-                return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
-        }
+        if (r < 0)
+                return log_error_errno(errno, "bind(" NSPAWN_NOTIFY_SOCKET_PATH ") failed: %m");
 
         r = userns_lchown(NSPAWN_NOTIFY_SOCKET_PATH, 0, 0);
-        if (r < 0) {
-                safe_close(fd);
+        if (r < 0)
                 return log_error_errno(r, "Failed to chown " NSPAWN_NOTIFY_SOCKET_PATH ": %m");
-        }
 
-        r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
-        if (r < 0) {
-                safe_close(fd);
+        r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
+        if (r < 0)
                 return log_error_errno(errno, "SO_PASSCRED failed: %m");
-        }
 
-        return fd;
+        return TAKE_FD(fd);
 }
 
 static int outer_child(
index efb68a354f08be4f98560ffe2b30087e25e04357..84176184afb28cbfcca9d369332a5225d96379eb 100644 (file)
 #include "util.h"
 
 static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
-        union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-        };
+        union sockaddr_union sa = {};
+        int salen;
 
         assert(fd >= 0);
         assert(socket_name);
         assert(packet);
 
-        strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+        salen = sockaddr_un_set_path(&sa.un, socket_name);
+        if (salen < 0)
+                return log_error_errno(salen, "Specified socket path for AF_UNIX socket invalid, refusing: %s", socket_name);
 
-        if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+        if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, salen) < 0)
                 return log_error_errno(errno, "Failed to send: %m");
 
         return 0;
index 5ceb9544102b05a163642872033ef19458a91863..0c96332c75d075547dc3487d0df57b0617eaab6a 100644 (file)
@@ -313,7 +313,6 @@ static int dns_scope_socket(
         _cleanup_close_ int fd = -1;
         union sockaddr_union sa;
         socklen_t salen;
-        static const int one = 1;
         int r, ifindex;
 
         assert(s);
@@ -379,7 +378,7 @@ static int dns_scope_socket(
                 return -errno;
 
         if (type == SOCK_STREAM) {
-                r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+                r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &const_int_one, sizeof(const_int_one));
                 if (r < 0)
                         return -errno;
         }
@@ -402,16 +401,23 @@ static int dns_scope_socket(
                 /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
 
                 if (sa.sa.sa_family == AF_INET) {
-                        r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+                        r = setsockopt(fd, IPPROTO_IP, IP_TTL, &const_int_one, sizeof(const_int_one));
                         if (r < 0)
                                 return -errno;
                 } else if (sa.sa.sa_family == AF_INET6) {
-                        r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
+                        r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &const_int_one, sizeof(const_int_one));
                         if (r < 0)
                                 return -errno;
                 }
         }
 
+        if (type == SOCK_DGRAM) {
+                /* Set IP_RECVERR or IPV6_RECVERR to get ICMP error feedback. See discussion in #10345. */
+                r = setsockopt(fd, SOL_IP, sa.sa.sa_family == AF_INET ? IP_RECVERR : IPV6_RECVERR, &const_int_one, sizeof(const_int_one));
+                if (r < 0)
+                        return -errno;
+        }
+
         if (ret_socket_address)
                 *ret_socket_address = sa;
         else {
index 5ddf13081eed48d8d52882e24a8d14bc827e22eb..5f87a790c65e8112c0acb8794e0a8e61f6fd5d37 100644 (file)
@@ -392,7 +392,6 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
 }
 
 static int manager_dns_stub_udp_fd(Manager *m) {
-        static const int one = 1;
         union sockaddr_union sa = {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(53),
@@ -408,13 +407,13 @@ static int manager_dns_stub_udp_fd(Manager *m) {
         if (fd < 0)
                 return -errno;
 
-        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
-        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
-        if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+        if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
         /* Make sure no traffic from outside the local host can leak to onto this socket */
@@ -479,7 +478,6 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
 }
 
 static int manager_dns_stub_tcp_fd(Manager *m) {
-        static const int one = 1;
         union sockaddr_union sa = {
                 .in.sin_family = AF_INET,
                 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
@@ -495,16 +493,16 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
         if (fd < 0)
                 return -errno;
 
-        if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
+        if (setsockopt(fd, IPPROTO_IP, IP_TTL, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
-        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
-        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
-        if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+        if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &const_int_one, sizeof const_int_one) < 0)
                 return -errno;
 
         /* Make sure no traffic from outside the local host can leak to onto this socket */
index 2f2a1a15eca86300fd8c57ce4a04f3cd71f9a365..a6100c3b0095ee594bf595c5e202e5f33289f186 100644 (file)
@@ -115,7 +115,7 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(LLMNR_PORT),
         };
-        static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+        static const int pmtu = IP_PMTUDISC_DONT, ttl = 255;
         int r;
 
         assert(m);
@@ -140,19 +140,19 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_LOOP: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_PKTINFO: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_RECVTTL: %m");
                 goto fail;
@@ -176,7 +176,7 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
                 log_warning("LLMNR-IPv4(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
 
                 /* try again with SO_REUSEADDR */
-                r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -189,7 +189,7 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
                 }
         } else {
                 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
-                r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -214,7 +214,7 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
                 .in6.sin6_family = AF_INET6,
                 .in6.sin6_port = htobe16(LLMNR_PORT),
         };
-        static const int one = 1, ttl = 255;
+        static const int ttl = 255;
         int r;
 
         assert(m);
@@ -239,25 +239,25 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_LOOP: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_V6ONLY: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVPKTINFO: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVHOPLIMIT: %m");
                 goto fail;
@@ -274,7 +274,7 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
                 log_warning("LLMNR-IPv6(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
 
                 /* try again with SO_REUSEADDR */
-                r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -287,7 +287,7 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
                 }
         } else {
                 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
-                r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -355,7 +355,7 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(LLMNR_PORT),
         };
-        static const int one = 1, pmtu = IP_PMTUDISC_DONT;
+        static const int pmtu = IP_PMTUDISC_DONT;
         int r;
 
         assert(m);
@@ -368,19 +368,19 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
                 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m");
 
         /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m");
                 goto fail;
@@ -404,7 +404,7 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
                 log_warning("LLMNR-IPv4(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
 
                 /* try again with SO_REUSEADDR */
-                r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -417,7 +417,7 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
                 }
         } else {
                 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
-                r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -448,7 +448,6 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
                 .in6.sin6_family = AF_INET6,
                 .in6.sin6_port = htobe16(LLMNR_PORT),
         };
-        static const int one = 1;
         int r;
 
         assert(m);
@@ -461,25 +460,25 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
                 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m");
 
         /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m");
                 goto fail;
@@ -496,7 +495,7 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
                 log_warning("LLMNR-IPv6(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
 
                 /* try again with SO_REUSEADDR */
-                r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -509,7 +508,7 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
                 }
         } else {
                 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
-                r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
                         goto fail;
index e70138181a9b1d730c3e3643b0e564c77a938eae..feee66c85ce3e1d3619949418ee0a53966c55a80 100644 (file)
@@ -101,12 +101,7 @@ static int proposed_rrs_cmp(DnsResourceRecord **x, unsigned x_size, DnsResourceR
                         return r;
         }
 
-        if (x_size < y_size)
-                return -1;
-        if (x_size > y_size)
-                return 1;
-
-        return 0;
+        return CMP(x_size, y_size);
 }
 
 static int mdns_packet_extract_matching_rrs(DnsPacket *p, DnsResourceKey *key, DnsResourceRecord ***ret_rrs) {
@@ -344,7 +339,7 @@ int manager_mdns_ipv4_fd(Manager *m) {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(MDNS_PORT),
         };
-        static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+        static const int pmtu = IP_PMTUDISC_DONT, ttl = 255;
         int r;
 
         assert(m);
@@ -368,19 +363,19 @@ int manager_mdns_ipv4_fd(Manager *m) {
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_PKTINFO: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_RECVTTL: %m");
                 goto fail;
@@ -405,7 +400,7 @@ int manager_mdns_ipv4_fd(Manager *m) {
                 log_warning("mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
 
                 /* try again with SO_REUSEADDR */
-                r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -418,7 +413,7 @@ int manager_mdns_ipv4_fd(Manager *m) {
                 }
         } else {
                 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
-                r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -441,7 +436,7 @@ int manager_mdns_ipv6_fd(Manager *m) {
                 .in6.sin6_family = AF_INET6,
                 .in6.sin6_port = htobe16(MDNS_PORT),
         };
-        static const int one = 1, ttl = 255;
+        static const int ttl = 255;
         int r;
 
         assert(m);
@@ -466,25 +461,25 @@ int manager_mdns_ipv6_fd(Manager *m) {
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m");
                 goto fail;
         }
 
-        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &const_int_one, sizeof(const_int_one));
         if (r < 0) {
                 r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m");
                 goto fail;
@@ -502,7 +497,7 @@ int manager_mdns_ipv6_fd(Manager *m) {
                 log_warning("mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
 
                 /* try again with SO_REUSEADDR */
-                r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
                         goto fail;
@@ -515,7 +510,7 @@ int manager_mdns_ipv6_fd(Manager *m) {
                 }
         } else {
                 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
-                r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+                r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one));
                 if (r < 0) {
                         r = log_error_errno(errno, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
                         goto fail;
index 682dc754fc74310b93244c9dd3bf0e5881d556ce..681fd0d618c913c2632f1a9551a0e3afb3dbb488 100644 (file)
@@ -451,41 +451,35 @@ finish:
         return r;
 }
 
-static int create_socket(char **name) {
-        union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-        };
+static int create_socket(char **ret) {
+        _cleanup_free_ char *path = NULL;
+        union sockaddr_union sa = {};
         _cleanup_close_ int fd = -1;
-        static const int one = 1;
-        char *c;
-        int r;
+        int salen;
 
-        assert(name);
+        assert(ret);
 
         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
         if (fd < 0)
                 return -errno;
 
-        snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
+        if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0)
+                return -ENOMEM;
+
+        salen = sockaddr_un_set_path(&sa.un, path);
+        if (salen < 0)
+                return salen;
 
         RUN_WITH_UMASK(0177) {
-                if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+                if (bind(fd, &sa.sa, salen) < 0)
                         return -errno;
         }
 
-        if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+        if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one)) < 0)
                 return -errno;
 
-        c = strdup(sa.un.sun_path);
-        if (!c)
-                return -ENOMEM;
-
-        *name = c;
-
-        r = fd;
-        fd = -1;
-
-        return r;
+        *ret = TAKE_PTR(path);
+        return TAKE_FD(fd);
 }
 
 int ask_password_agent(
index 8eea70716c00080cb399a440740dd6f65998b03b..2cd30e3467524a5c84b2f81fcbac884b2744a43c 100644 (file)
@@ -800,6 +800,14 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
 
                 return bus_append_parse_nsec(m, field, eq);
 
+        if (streq(field, "LogRateLimitIntervalSec"))
+
+                return bus_append_parse_sec_rename(m, field, eq);
+
+        if (streq(field, "LogRateLimitBurst"))
+
+                return bus_append_safe_atou(m, field, eq);
+
         if (streq(field, "MountFlags"))
 
                 return bus_append_mount_propagation_flags_from_string(m, field, eq);
index 4c60d97c3921ba33b7d4268634ece53ac143b622..227378edaaa041b1f27e2f1a71bc3a0243ec1a20 100644 (file)
@@ -378,17 +378,16 @@ static int resolve_remote(Connection *c) {
         const char *node, *service;
         int r;
 
-        if (path_is_absolute(arg_remote_host)) {
-                sa.un.sun_family = AF_UNIX;
-                strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path));
-                return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un));
-        }
+        if (IN_SET(arg_remote_host[0], '/', '@')) {
+                int salen;
+
+                salen = sockaddr_un_set_path(&sa.un, arg_remote_host);
+                if (salen < 0) {
+                        log_error_errno(salen, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
+                        goto fail;
+                }
 
-        if (arg_remote_host[0] == '@') {
-                sa.un.sun_family = AF_UNIX;
-                sa.un.sun_path[0] = 0;
-                strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1);
-                return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+                return connection_start(c, &sa.sa, salen);
         }
 
         service = strrchr(arg_remote_host, ':');
index 5db3592d6fd225fbacf3c909a735fe3d581d2be4..a1ea2333de5c0cd82733276d22999c47db9921a8 100644 (file)
@@ -9,6 +9,7 @@
 #include "bus-util.h"
 #include "bus-error.h"
 #include "def.h"
+#include "env-util.h"
 #include "log.h"
 #include "process-util.h"
 #include "sd-bus.h"
@@ -89,7 +90,11 @@ static void print_mode(const char* mode) {
 }
 
 int main(int argc, char *argv[]) {
-        static const char* const sulogin_cmdline[] = {SULOGIN, NULL};
+        const char* sulogin_cmdline[] = {
+                SULOGIN,
+                NULL,             /* --force */
+                NULL
+        };
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
@@ -99,6 +104,10 @@ int main(int argc, char *argv[]) {
 
         print_mode(argc > 1 ? argv[1] : "");
 
+        if (getenv_bool("SYSTEMD_SULOGIN_FORCE") > 0)
+                /* allows passwordless logins if root account is locked. */
+                sulogin_cmdline[1] = "--force";
+
         (void) fork_wait(sulogin_cmdline);
 
         r = bus_connect_system_systemd(&bus);
index be1b7375afa17d2ac5af5c81244b0ed4440af766..c8914f8f85634262329cbca6d0c396d0488d76bc 100644 (file)
@@ -3018,12 +3018,12 @@ static enum action verb_to_action(const char *verb) {
 
 static int start_unit(int argc, char *argv[], void *userdata) {
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        _cleanup_(wait_context_free) WaitContext wait_context = {};
         const char *method, *mode, *one_name, *suffix = NULL;
         _cleanup_strv_free_ char **names = NULL;
+        int r, ret = EXIT_SUCCESS;
         sd_bus *bus;
-        _cleanup_(wait_context_free) WaitContext wait_context = {};
         char **name;
-        int r = 0;
 
         if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) {
                 log_error("--wait may only be used with the 'start' or 'restart' commands.");
@@ -3070,9 +3070,11 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                 one_name = action_table[arg_action].target;
         }
 
-        if (one_name)
+        if (one_name) {
                 names = strv_new(one_name, NULL);
-        else {
+                if (!names)
+                        return log_oom();
+        } else {
                 r = expand_names(bus, strv_skip(argv, 1), suffix, &names);
                 if (r < 0)
                         return log_error_errno(r, "Failed to expand names: %m");
@@ -3110,16 +3112,15 @@ static int start_unit(int argc, char *argv[], void *userdata) {
 
         STRV_FOREACH(name, names) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-                int q;
 
-                q = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
-                if (r >= 0 && q < 0)
-                        r = translate_bus_error_to_exit_status(q, &error);
+                r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
+                if (ret == EXIT_SUCCESS && r < 0)
+                        ret = translate_bus_error_to_exit_status(r, &error);
         }
 
         if (!arg_no_block) {
-                int q, arg_count = 0;
                 const char* extra_args[4] = {};
+                int arg_count = 0;
 
                 if (arg_scope != UNIT_FILE_SYSTEM)
                         extra_args[arg_count++] = "--user";
@@ -3133,9 +3134,9 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                         extra_args[arg_count++] = arg_host;
                 }
 
-                q = bus_wait_for_jobs(w, arg_quiet, extra_args);
-                if (q < 0)
-                        return q;
+                r = bus_wait_for_jobs(w, arg_quiet, extra_args);
+                if (r < 0)
+                        return r;
 
                 /* When stopping units, warn if they can still be triggered by
                  * another active unit (socket, path, timer) */
@@ -3144,16 +3145,15 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                                 (void) check_triggering_units(bus, *name);
         }
 
-        if (r >= 0 && arg_wait && !set_isempty(wait_context.unit_paths)) {
-                int q;
-                q = sd_event_loop(wait_context.event);
-                if (q < 0)
-                        return log_error_errno(q, "Failed to run event loop: %m");
+        if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) {
+                r = sd_event_loop(wait_context.event);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to run event loop: %m");
                 if (wait_context.any_failed)
-                        r = EXIT_FAILURE;
+                        ret = EXIT_FAILURE;
         }
 
-        return r;
+        return ret;
 }
 
 #if ENABLE_LOGIND
@@ -5264,12 +5264,6 @@ static int show(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_no_pager, false);
 
-        if (show_mode == SYSTEMCTL_SHOW_STATUS)
-                /* Increase max number of open files to 16K if we can, we
-                 * might needs this when browsing journal files, which might
-                 * be split up into many files. */
-                setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
-
         /* If no argument is specified inspect the manager itself */
         if (show_mode == SYSTEMCTL_SHOW_PROPERTIES && argc <= 1)
                 return show_one(bus, "/org/freedesktop/systemd1", NULL, show_mode, &new_line, &ellipsized);
@@ -8449,7 +8443,7 @@ static int start_with_fallback(void) {
 
 static int halt_now(enum action a) {
         /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
-         * synce'd explicitly in advance. */
+         * synced explicitly in advance. */
         if (!arg_no_sync && !arg_dry_run)
                 (void) sync();
 
@@ -8661,6 +8655,10 @@ int main(int argc, char*argv[]) {
         setlocale(LC_ALL, "");
         log_parse_environment();
         log_open();
+
+        /* The journal merging logic potentially needs a lot of fds. */
+        (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
         sigbus_install();
 
         /* Explicitly not on_tty() to avoid setting cached value.
index 15094f428abd228eb191acfc03cbc89e3e146dae..9c4bbed9dcfa6624b7814d927afd9660d161d735 100644 (file)
@@ -422,6 +422,7 @@ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...)
 int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0);
 int sd_bus_error_get_errno(const sd_bus_error *e);
 int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e);
+int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e);
 int sd_bus_error_is_set(const sd_bus_error *e);
 int sd_bus_error_has_name(const sd_bus_error *e, const char *name);
 
index 2e003f1fd2fa338afd632b16d741a65ee6e13be1..a1770213a801b633fcefe08bb8d6246592c6319f 100644 (file)
@@ -63,6 +63,11 @@ tests += [
           libmount,
           libblkid]],
 
+        [['src/test/test-emergency-action.c'],
+         [libcore,
+          libshared],
+         []],
+
         [['src/test/test-job-type.c'],
          [libcore,
           libshared],
diff --git a/src/test/test-emergency-action.c b/src/test/test-emergency-action.c
new file mode 100644 (file)
index 0000000..8ce28ed
--- /dev/null
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "emergency-action.h"
+#include "tests.h"
+
+static void test_parse_emergency_action(void) {
+        EmergencyAction x;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(parse_emergency_action("none", false, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_NONE);
+        assert_se(parse_emergency_action("reboot", false, &x) == -EOPNOTSUPP);
+        assert_se(parse_emergency_action("reboot-force", false, &x) == -EOPNOTSUPP);
+        assert_se(parse_emergency_action("reboot-immediate", false, &x) == -EOPNOTSUPP);
+        assert_se(parse_emergency_action("poweroff", false, &x) == -EOPNOTSUPP);
+        assert_se(parse_emergency_action("poweroff-force", false, &x) == -EOPNOTSUPP);
+        assert_se(parse_emergency_action("poweroff-immediate", false, &x) == -EOPNOTSUPP);
+        assert_se(x == EMERGENCY_ACTION_NONE);
+        assert_se(parse_emergency_action("exit", false, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_EXIT);
+        assert_se(parse_emergency_action("exit-force", false, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+        assert_se(parse_emergency_action("exit-forcee", false, &x) == -EINVAL);
+
+        assert_se(parse_emergency_action("none", true, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_NONE);
+        assert_se(parse_emergency_action("reboot", true, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_REBOOT);
+        assert_se(parse_emergency_action("reboot-force", true, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_REBOOT_FORCE);
+        assert_se(parse_emergency_action("reboot-immediate", true, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_REBOOT_IMMEDIATE);
+        assert_se(parse_emergency_action("poweroff", true, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_POWEROFF);
+        assert_se(parse_emergency_action("poweroff-force", true, &x) == 0);
+        assert_se(x == EMERGENCY_ACTION_POWEROFF_FORCE);
+        assert_se(parse_emergency_action("poweroff-immediate", true, &x) == 0);
+        assert_se(parse_emergency_action("exit", true, &x) == 0);
+        assert_se(parse_emergency_action("exit-force", true, &x) == 0);
+        assert_se(parse_emergency_action("exit-forcee", true, &x) == -EINVAL);
+        assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+}
+
+int main(int argc, char **argv) {
+        test_setup_logging(LOG_INFO);
+
+        test_parse_emergency_action();
+
+        return EXIT_SUCCESS;
+}
index f7fb5ce42262c87c9462071150b74dd8bf11a6e0..a93d37d46029d8c349d9d43930c66a795ff79de9 100644 (file)
@@ -15,27 +15,25 @@ static int unsigned_compare(const unsigned *a, const unsigned *b) {
 }
 
 static void test_unsigned(void) {
-        unsigned buffer[SET_SIZE], i;
-        Prioq *q;
+        _cleanup_(prioq_freep) Prioq *q = NULL;
+        unsigned buffer[SET_SIZE], i, u, n;
 
         srand(0);
 
-        q = prioq_new(trivial_compare_func);
-        assert_se(q);
+        assert_se(q = prioq_new(trivial_compare_func));
 
         for (i = 0; i < ELEMENTSOF(buffer); i++) {
-                unsigned u;
-
                 u = (unsigned) rand();
                 buffer[i] = u;
                 assert_se(prioq_put(q, UINT_TO_PTR(u), NULL) >= 0);
+
+                n = prioq_size(q);
+                assert_se(prioq_remove(q, UINT_TO_PTR(u), &n) == 0);
         }
 
         typesafe_qsort(buffer, ELEMENTSOF(buffer), unsigned_compare);
 
         for (i = 0; i < ELEMENTSOF(buffer); i++) {
-                unsigned u;
-
                 assert_se(prioq_size(q) == ELEMENTSOF(buffer) - i);
 
                 u = PTR_TO_UINT(prioq_pop(q));
@@ -43,7 +41,6 @@ static void test_unsigned(void) {
         }
 
         assert_se(prioq_isempty(q));
-        prioq_free(q);
 }
 
 struct test {
@@ -54,13 +51,7 @@ struct test {
 static int test_compare(const void *a, const void *b) {
         const struct test *x = a, *y = b;
 
-        if (x->value < y->value)
-                return -1;
-
-        if (x->value > y->value)
-                return 1;
-
-        return 0;
+        return CMP(x->value, y->value);
 }
 
 static void test_hash(const void *a, struct siphash *state) {
@@ -75,66 +66,48 @@ static const struct hash_ops test_hash_ops = {
 };
 
 static void test_struct(void) {
-        Prioq *q;
-        Set *s;
+        _cleanup_(prioq_freep) Prioq *q = NULL;
+        _cleanup_(set_freep) Set *s = NULL;
         unsigned previous = 0, i;
-        int r;
+        struct test *t;
 
         srand(0);
 
-        q = prioq_new(test_compare);
-        assert_se(q);
-
-        s = set_new(&test_hash_ops);
-        assert_se(s);
+        assert_se(q = prioq_new(test_compare));
+        assert_se(s = set_new(&test_hash_ops));
 
         for (i = 0; i < SET_SIZE; i++) {
-                struct test *t;
-
-                t = new0(struct test, 1);
-                assert_se(t);
+                assert_se(t = new0(struct test, 1));
                 t->value = (unsigned) rand();
 
-                r = prioq_put(q, t, &t->idx);
-                assert_se(r >= 0);
+                assert_se(prioq_put(q, t, &t->idx) >= 0);
 
-                if (i % 4 == 0) {
-                        r = set_consume(s, t);
-                        assert_se(r >= 0);
-                }
+                if (i % 4 == 0)
+                        assert_se(set_consume(s, t) >= 0);
         }
 
-        for (;;) {
-                struct test *t;
-
-                t = set_steal_first(s);
-                if (!t)
-                        break;
-
-                r = prioq_remove(q, t, &t->idx);
-                assert_se(r > 0);
+        while ((t = set_steal_first(s))) {
+                assert_se(prioq_remove(q, t, &t->idx) == 1);
+                assert_se(prioq_remove(q, t, &t->idx) == 0);
+                assert_se(prioq_remove(q, t, NULL) == 0);
 
                 free(t);
         }
 
         for (i = 0; i < SET_SIZE * 3 / 4; i++) {
-                struct test *t;
-
                 assert_se(prioq_size(q) == (SET_SIZE * 3 / 4) - i);
 
-                t = prioq_pop(q);
-                assert_se(t);
-
+                assert_se(t = prioq_pop(q));
+                assert_se(prioq_remove(q, t, &t->idx) == 0);
+                assert_se(prioq_remove(q, t, NULL) == 0);
                 assert_se(previous <= t->value);
+
                 previous = t->value;
                 free(t);
         }
 
         assert_se(prioq_isempty(q));
-        prioq_free(q);
-
         assert_se(set_isempty(s));
-        set_free(s);
 }
 
 int main(int argc, char* argv[]) {
index df18a2a83c48b5a2c3a8818d4d548c4627f9405b..0de29c22350c86b0230174a493aaa135af8c47d6 100644 (file)
@@ -405,7 +405,7 @@ static void test_sockaddr_un_len(void) {
                 .sun_path = "\0foobar",
         };
 
-        assert_se(SOCKADDR_UN_LEN(fs) == offsetof(struct sockaddr_un, sun_path) + strlen(fs.sun_path));
+        assert_se(SOCKADDR_UN_LEN(fs) == offsetof(struct sockaddr_un, sun_path) + strlen(fs.sun_path) + 1);
         assert_se(SOCKADDR_UN_LEN(abstract) == offsetof(struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1));
 }
 
index 5a3d478d73fa0fbb53092cf63ec145afdb6a5615..f33a32afc0d15d262d8343a3258657c257d53e9f 100644 (file)
@@ -630,7 +630,6 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
 static int manager_listen_setup(Manager *m) {
         union sockaddr_union addr = {};
         static const int tos = IPTOS_LOWDELAY;
-        static const int on = 1;
         int r;
 
         assert(m);
@@ -651,7 +650,7 @@ static int manager_listen_setup(Manager *m) {
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(m->server_socket, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(on));
+        r = setsockopt(m->server_socket, SOL_SOCKET, SO_TIMESTAMPNS, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return -errno;
 
index 690c7b0de918457593d786d49c1f5038643b6c88..6c6f3be08cc05f930797d6f874e63ba715c1e652 100644 (file)
@@ -235,14 +235,18 @@ finish:
 static int send_passwords(const char *socket_name, char **passwords) {
         _cleanup_free_ char *packet = NULL;
         _cleanup_close_ int socket_fd = -1;
-        union sockaddr_union sa = { .un.sun_family = AF_UNIX };
+        union sockaddr_union sa = {};
         size_t packet_length = 1;
         char **p, *d;
         ssize_t n;
-        int r;
+        int r, salen;
 
         assert(socket_name);
 
+        salen = sockaddr_un_set_path(&sa.un, socket_name);
+        if (salen < 0)
+                return salen;
+
         STRV_FOREACH(p, passwords)
                 packet_length += strlen(*p) + 1;
 
@@ -262,9 +266,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
                 goto finish;
         }
 
-        strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
-
-        n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+        n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
         if (n < 0) {
                 r = log_debug_errno(errno, "sendto(): %m");
                 goto finish;
index 64024100fe89e1defa1135cadd815d0fa6760872..bde8b63dde76f76e1ce1046306ddad4390647306 100644 (file)
@@ -73,7 +73,6 @@ struct udev_ctrl_connection {
 
 struct udev_ctrl *udev_ctrl_new_from_fd(int fd) {
         struct udev_ctrl *uctrl;
-        const int on = 1;
         int r;
 
         uctrl = new0(struct udev_ctrl, 1);
@@ -97,12 +96,15 @@ struct udev_ctrl *udev_ctrl_new_from_fd(int fd) {
          * FIXME: remove it as soon as we can depend on this:
          *   http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949
          */
-        r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+        r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 log_warning_errno(errno, "could not set SO_PASSCRED: %m");
 
-        uctrl->saddr.un.sun_family = AF_LOCAL;
-        strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control");
+        uctrl->saddr.un = (struct sockaddr_un) {
+                .sun_family = AF_UNIX,
+                .sun_path = "/run/udev/control",
+        };
+
         uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un);
         return uctrl;
 }
@@ -117,7 +119,7 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
         if (!uctrl->bound) {
                 err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
                 if (err < 0 && errno == EADDRINUSE) {
-                        unlink(uctrl->saddr.un.sun_path);
+                        (void) sockaddr_un_unlink(&uctrl->saddr.un);
                         err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
                 }
 
@@ -148,7 +150,7 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
         if (uctrl == NULL)
                 return 0;
         if (uctrl->cleanup_socket)
-                unlink(uctrl->saddr.un.sun_path);
+                sockaddr_un_unlink(&uctrl->saddr.un);
         return 0;
 }
 
@@ -161,7 +163,6 @@ int udev_ctrl_get_fd(struct udev_ctrl *uctrl) {
 struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
         struct udev_ctrl_connection *conn;
         struct ucred ucred = {};
-        const int on = 1;
         int r;
 
         conn = new(struct udev_ctrl_connection, 1);
@@ -189,15 +190,14 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
         }
 
         /* enable receiving of the sender credentials in the messages */
-        r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+        r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 log_warning_errno(errno, "could not set SO_PASSCRED: %m");
 
         udev_ctrl_ref(uctrl);
         return conn;
 err:
-        if (conn->sock >= 0)
-                close(conn->sock);
+        safe_close(conn->sock);
         return mfree(conn);
 }
 
index f732913d176c0f29d58a54f37347aaa42b19eadf..75e5866bb01efe90d85cbc1a5fa0f8ea26ecd06e 100644 (file)
@@ -44,18 +44,19 @@ struct udev_event *udev_event_new(struct udev_device *dev) {
         if (event == NULL)
                 return NULL;
         event->dev = dev;
-        udev_list_init(NULL, &event->run_list, false);
-        udev_list_init(NULL, &event->seclabel_list, false);
         event->birth_usec = now(CLOCK_MONOTONIC);
         return event;
 }
 
 void udev_event_unref(struct udev_event *event) {
+        void *p;
+
         if (event == NULL)
                 return;
         sd_netlink_unref(event->rtnl);
-        udev_list_cleanup(&event->run_list);
-        udev_list_cleanup(&event->seclabel_list);
+        while ((p = hashmap_steal_first_key(event->run_list)))
+                free(p);
+        hashmap_free_free_free(event->seclabel_list);
         free(event->program_result);
         free(event->name);
         free(event);
@@ -790,7 +791,7 @@ static int rename_netif(struct udev_event *event) {
 
 void udev_event_execute_rules(struct udev_event *event,
                               usec_t timeout_usec, usec_t timeout_warn_usec,
-                              struct udev_list *properties_list,
+                              Hashmap *properties_list,
                               struct udev_rules *rules) {
         struct udev_device *dev = event->dev;
 
@@ -873,7 +874,7 @@ void udev_event_execute_rules(struct udev_event *event,
                         }
 
                         apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
-                        udev_node_add(dev->device, apply, event->mode, event->uid, event->gid, &event->seclabel_list);
+                        udev_node_add(dev->device, apply, event->mode, event->uid, event->gid, event->seclabel_list);
                 }
 
                 /* preserve old, or get new initialization timestamp */
@@ -889,12 +890,13 @@ void udev_event_execute_rules(struct udev_event *event,
 }
 
 void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) {
-        struct udev_list_entry *list_entry;
+        const char *cmd;
+        void *val;
+        Iterator i;
 
-        udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) {
+        HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) {
+                enum udev_builtin_cmd builtin_cmd = PTR_TO_INT(val);
                 char command[UTIL_PATH_SIZE];
-                const char *cmd = udev_list_entry_get_name(list_entry);
-                enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry);
 
                 udev_event_apply_format(event, cmd, command, sizeof(command), false);
 
index 6b0659159d280b682d7612bf1f21973d5fbf276a..9cccef1b0f7a2ae8d2567708c33a0b4a2b4792a7 100644 (file)
@@ -17,6 +17,7 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "libudev-private.h"
 #include "path-util.h"
 #include "selinux-util.h"
 #include "smack-util.h"
@@ -271,9 +272,8 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
 
 static int node_permissions_apply(sd_device *dev, bool apply,
                                   mode_t mode, uid_t uid, gid_t gid,
-                                  struct udev_list *seclabel_list) {
+                                  Hashmap *seclabel_list) {
         const char *devnode, *subsystem, *id_filename = NULL;
-        struct udev_list_entry *entry;
         struct stat stats;
         dev_t devnum;
         int r = 0;
@@ -305,6 +305,8 @@ static int node_permissions_apply(sd_device *dev, bool apply,
 
         if (apply) {
                 bool selinux = false, smack = false;
+                const char *name, *label;
+                Iterator i;
 
                 if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) {
                         log_debug("Setting permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
@@ -316,13 +318,9 @@ static int node_permissions_apply(sd_device *dev, bool apply,
                         log_debug("Preserve permissions of %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
 
                 /* apply SECLABEL{$module}=$label */
-                udev_list_entry_foreach(entry, udev_list_get_entry(seclabel_list)) {
-                        const char *name, *label;
+                HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) {
                         int q;
 
-                        name = udev_list_entry_get_name(entry);
-                        label = udev_list_entry_get_value(entry);
-
                         if (streq(name, "selinux")) {
                                 selinux = true;
 
@@ -388,7 +386,7 @@ static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
 
 int udev_node_add(sd_device *dev, bool apply,
                   mode_t mode, uid_t uid, gid_t gid,
-                  struct udev_list *seclabel_list) {
+                  Hashmap *seclabel_list) {
         const char *devnode, *devlink;
         _cleanup_free_ char *filename = NULL;
         int r;
index 868549e4923226989abe8b87e4ba57bdcf076d85..223c8f0e4395654a47ee1546ea26cb4507026b54 100644 (file)
@@ -4,13 +4,12 @@
 #include <stdbool.h>
 #include <sys/types.h>
 
-#include "libudev.h"
 #include "sd-device.h"
 
-#include "libudev-private.h"
+#include "hashmap.h"
 
 int udev_node_add(sd_device *dev, bool apply,
                   mode_t mode, uid_t uid, gid_t gid,
-                  struct udev_list *seclabel_list);
+                  Hashmap *seclabel_list);
 int udev_node_remove(sd_device *dev);
 int udev_node_update_old_links(sd_device *dev, sd_device *dev_old);
index 4d87754042b88c7f945211bbb535f3b8ee17976e..752e17f2aed0b4d9eb77072fa0996b50a945398f 100644 (file)
@@ -1392,17 +1392,12 @@ static void add_rule(struct udev_rules *rules, char *line,
                                 rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
 
                         pos = strstr(value, "nowatch");
-                        if (pos != NULL) {
-                                const int off = 0;
-
-                                rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off);
-                        } else {
+                        if (pos)
+                                rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &const_int_zero);
+                        else {
                                 pos = strstr(value, "watch");
-                                if (pos != NULL) {
-                                        const int on = 1;
-
-                                        rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on);
-                                }
+                                if (pos)
+                                        rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &const_int_one);
                         }
 
                         pos = strstr(value, "static_node=");
@@ -1716,19 +1711,20 @@ enum escape_type {
         ESCAPE_REPLACE,
 };
 
-void udev_rules_apply_to_event(struct udev_rules *rules,
-                               struct udev_event *event,
-                               usec_t timeout_usec,
-                               usec_t timeout_warn_usec,
-                               struct udev_list *properties_list) {
+int udev_rules_apply_to_event(
+                struct udev_rules *rules,
+                struct udev_event *event,
+                usec_t timeout_usec,
+                usec_t timeout_warn_usec,
+                Hashmap *properties_list) {
         struct token *cur;
         struct token *rule;
         enum escape_type esc = ESCAPE_UNSET;
         bool can_set_name;
         int r;
 
-        if (rules->tokens == NULL)
-                return;
+        if (!rules->tokens)
+                return 0;
 
         can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) &&
                         (major(udev_device_get_devnum(event->dev)) > 0 ||
@@ -1788,18 +1784,10 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
                         value = udev_device_get_property_value(event->dev, key_name);
 
                         /* check global properties */
-                        if (!value && properties_list) {
-                                struct udev_list_entry *list_entry;
-
-                                list_entry = udev_list_get_entry(properties_list);
-                                list_entry = udev_list_entry_get_by_name(list_entry, key_name);
-                                if (list_entry != NULL)
-                                        value = udev_list_entry_get_value(list_entry);
-                        }
+                        if (!value && properties_list)
+                                value = hashmap_get(properties_list, key_name);
 
-                        if (!value)
-                                value = "";
-                        if (match_key(rules, cur, value))
+                        if (match_key(rules, cur, strempty(value)))
                                 goto nomatch;
                         break;
                 }
@@ -2193,19 +2181,34 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
                                   rule->rule.filename_line);
                         break;
                 case TK_A_SECLABEL: {
+                        _cleanup_free_ char *name = NULL, *label = NULL;
                         char label_str[UTIL_LINE_SIZE] = {};
-                        const char *name, *label;
 
-                        name = rules_str(rules, cur->key.attr_off);
+                        name = strdup(rules_str(rules, cur->key.attr_off));
+                        if (!name)
+                                return log_oom();
+
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str), false);
-                        if (label_str[0] != '\0')
-                                label = label_str;
+                        if (!isempty(label_str))
+                                label = strdup(label_str);
                         else
-                                label = rules_str(rules, cur->key.value_off);
+                                label = strdup(rules_str(rules, cur->key.value_off));
+                        if (!label)
+                                return log_oom();
 
                         if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
-                                udev_list_cleanup(&event->seclabel_list);
-                        udev_list_entry_add(&event->seclabel_list, name, label);
+                                hashmap_clear_free_free(event->seclabel_list);
+
+                        r = hashmap_ensure_allocated(&event->seclabel_list, NULL);
+                        if (r < 0)
+                                return log_oom();
+
+                        r = hashmap_put(event->seclabel_list, name, label);
+                        if (r < 0)
+                                return log_oom();
+
+                        name = label = NULL;
+
                         log_debug("SECLABEL{%s}='%s' %s:%u",
                                   name, label,
                                   rules_str(rules, rule->rule.filename_off),
@@ -2285,10 +2288,9 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
                                           rule->rule.filename_line);
                                 break;
                         }
-                        if (free_and_strdup(&event->name, name_str) < 0) {
-                                log_oom();
-                                return;
-                        }
+                        if (free_and_strdup(&event->name, name_str) < 0)
+                                return log_oom();
+
                         log_debug("NAME '%s' %s:%u",
                                   event->name,
                                   rules_str(rules, rule->rule.filename_off),
@@ -2378,16 +2380,33 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
                 }
                 case TK_A_RUN_BUILTIN:
                 case TK_A_RUN_PROGRAM: {
-                        struct udev_list_entry *entry;
+                        _cleanup_free_ char *cmd = NULL;
+
+                        if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL)) {
+                                void *p;
+
+                                while ((p = hashmap_steal_first_key(event->run_list)))
+                                        free(p);
+                        }
+
+                        r = hashmap_ensure_allocated(&event->run_list, NULL);
+                        if (r < 0)
+                                return log_oom();
+
+                        cmd = strdup(rules_str(rules, cur->key.value_off));
+                        if (!cmd)
+                                return log_oom();
+
+                        r = hashmap_put(event->run_list, cmd, INT_TO_PTR(cur->key.builtin_cmd));
+                        if (r < 0)
+                                return log_oom();
+
+                        cmd = NULL;
 
-                        if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
-                                udev_list_cleanup(&event->run_list);
                         log_debug("RUN '%s' %s:%u",
                                   rules_str(rules, cur->key.value_off),
                                   rules_str(rules, rule->rule.filename_off),
                                   rule->rule.filename_line);
-                        entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL);
-                        udev_list_entry_set_num(entry, cur->key.builtin_cmd);
                         break;
                 }
                 case TK_A_GOTO:
@@ -2396,7 +2415,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
                         cur = &rules->tokens[cur->key.rule_goto];
                         continue;
                 case TK_END:
-                        return;
+                        return 0;
 
                 case TK_M_PARENTS_MIN:
                 case TK_M_PARENTS_MAX:
@@ -2412,6 +2431,8 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
                 /* fast-forward to next rule */
                 cur = rule + rule->rule.token_count;
         }
+
+        return 0;
 }
 
 int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
index cfcb310d231cec957d301679fe12945cee58ce2d..3f5885c52e5b8059b74ce46958a815fa6fe1e471 100644 (file)
@@ -121,23 +121,22 @@ int udev_watch_begin(sd_device *dev) {
 
 int udev_watch_end(sd_device *dev) {
         char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
-        const char *devnode;
+        const char *name = NULL;
         int wd, r;
 
         if (inotify_fd < 0)
                 return log_error_errno(EINVAL, "Invalid inotify descriptor.");
 
+        if (sd_device_get_devname(dev, &name) < 0)
+                (void) sd_device_get_syspath(dev, &name);
+
         r = device_get_watch_handle(dev, &wd);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
-                return log_error_errno(r, "Failed to get watch handle for device '%s', ignoring: %m", devnode);
-
-        r = sd_device_get_devname(dev, &devnode);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device name: %m");
+                return log_error_errno(r, "Failed to get watch handle for device '%s', ignoring: %m", strnull(name));
 
-        log_debug("Removing watch on '%s'", devnode);
+        log_debug("Removing watch on '%s'", strnull(name));
         (void) inotify_rm_watch(inotify_fd, wd);
 
         xsprintf(filename, "/run/udev/watch/%d", wd);
index 09bd54e9659f3b8d24ca750693c91d08a72b7c26..99217545dc86f5b4949d30f39af7eab22ab18d80 100644 (file)
@@ -13,6 +13,7 @@
 #include "sd-device.h"
 #include "sd-netlink.h"
 
+#include "hashmap.h"
 #include "label.h"
 #include "libudev-private.h"
 #include "macro.h"
@@ -28,8 +29,8 @@ struct udev_event {
         mode_t mode;
         uid_t uid;
         gid_t gid;
-        struct udev_list seclabel_list;
-        struct udev_list run_list;
+        Hashmap *seclabel_list;
+        Hashmap *run_list;
         int exec_delay;
         usec_t birth_usec;
         sd_netlink *rtnl;
@@ -53,9 +54,9 @@ struct udev_rules;
 struct udev_rules *udev_rules_new(int resolve_names);
 struct udev_rules *udev_rules_unref(struct udev_rules *rules);
 bool udev_rules_check_timestamp(struct udev_rules *rules);
-void udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
-                               usec_t timeout_usec, usec_t timeout_warn_usec,
-                               struct udev_list *properties_list);
+int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
+                              usec_t timeout_usec, usec_t timeout_warn_usec,
+                              Hashmap *properties_list);
 int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
 
 /* udev-event.c */
@@ -73,7 +74,7 @@ int udev_event_spawn(struct udev_event *event,
                      const char *cmd, char *result, size_t ressize);
 void udev_event_execute_rules(struct udev_event *event,
                               usec_t timeout_usec, usec_t timeout_warn_usec,
-                              struct udev_list *properties_list,
+                              Hashmap *properties_list,
                               struct udev_rules *rules);
 void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec);
 
index d2868d114cd30ed95207eaf6fef073e21b3ba2c5..ea6471b837bbebfe2e89a8e6fa2b50ed40f65a87 100644 (file)
@@ -92,6 +92,9 @@ int test_main(int argc, char *argv[], void *userdata) {
         _cleanup_(udev_event_unrefp) struct udev_event *event = NULL;
         struct udev_list_entry *entry;
         sigset_t mask, sigmask_orig;
+        const char *cmd;
+        Iterator i;
+        void *val;
         int r;
 
         log_set_max_level(LOG_DEBUG);
@@ -138,10 +141,10 @@ int test_main(int argc, char *argv[], void *userdata) {
         udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
                 printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
 
-        udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
+        HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) {
                 char program[UTIL_PATH_SIZE];
 
-                udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program), false);
+                udev_event_apply_format(event, cmd, program, sizeof(program), false);
                 printf("run: '%s'\n", program);
         }
 
index ea1476e7dddd47c3119e34217abe5ca918fd656d..20fd23053a1fd8a55d6ac31725e8573bd7bd0aac 100644 (file)
@@ -75,7 +75,7 @@ typedef struct Manager {
         pid_t pid; /* the process that originally allocated the manager object */
 
         struct udev_rules *rules;
-        struct udev_list properties;
+        Hashmap *properties;
 
         struct udev_monitor *monitor;
         struct udev_ctrl *ctrl;
@@ -293,7 +293,7 @@ static void manager_free(Manager *manager) {
         udev_ctrl_unref(manager->ctrl);
         udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
 
-        udev_list_cleanup(&manager->properties);
+        hashmap_free_free_free(manager->properties);
         udev_rules_unref(manager->rules);
 
         safe_close(manager->fd_inotify);
@@ -444,7 +444,7 @@ static void worker_spawn(Manager *manager, struct event *event) {
                         /* apply rules, create node, symlinks */
                         udev_event_execute_rules(udev_event,
                                                  arg_event_timeout_usec, arg_event_timeout_warn_usec,
-                                                 &manager->properties,
+                                                 manager->properties,
                                                  manager->rules);
 
                         udev_event_execute_run(udev_event,
@@ -555,7 +555,7 @@ static void event_run(Manager *manager, struct event *event) {
                 if (count < 0) {
                         log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it",
                                         worker->pid, count);
-                        kill(worker->pid, SIGKILL);
+                        (void) kill(worker->pid, SIGKILL);
                         worker->state = WORKER_KILLED;
                         continue;
                 }
@@ -629,7 +629,7 @@ static void manager_kill_workers(Manager *manager) {
                         continue;
 
                 worker->state = WORKER_KILLED;
-                kill(worker->pid, SIGTERM);
+                (void) kill(worker->pid, SIGTERM);
         }
 }
 
@@ -918,7 +918,7 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
         _cleanup_(udev_ctrl_connection_unrefp) struct udev_ctrl_connection *ctrl_conn = NULL;
         _cleanup_(udev_ctrl_msg_unrefp) struct udev_ctrl_msg *ctrl_msg = NULL;
         const char *str;
-        int i;
+        int i, r;
 
         assert(manager);
 
@@ -954,27 +954,56 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
         }
 
         str = udev_ctrl_get_set_env(ctrl_msg);
-        if (str != NULL) {
-                _cleanup_free_ char *key = NULL;
-
-                key = strdup(str);
-                if (key) {
-                        char *val;
-
-                        val = strchr(key, '=');
-                        if (val != NULL) {
-                                val[0] = '\0';
-                                val = &val[1];
-                                if (val[0] == '\0') {
-                                        log_debug("udevd message (ENV) received, unset '%s'", key);
-                                        udev_list_entry_add(&manager->properties, key, NULL);
-                                } else {
-                                        log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
-                                        udev_list_entry_add(&manager->properties, key, val);
-                                }
-                        } else
-                                log_error("wrong key format '%s'", key);
+        if (str) {
+                _cleanup_free_ char *key = NULL, *val = NULL, *old_key = NULL, *old_val = NULL;
+                char *eq;
+
+                eq = strchr(str, '=');
+                if (!eq) {
+                        log_error("Invalid key format '%s'", str);
+                        return 1;
+                }
+
+                key = strndup(str, eq - str);
+                if (!key) {
+                        log_oom();
+                        return 1;
+                }
+
+                old_val = hashmap_remove2(manager->properties, key, (void **) &old_key);
+
+                r = hashmap_ensure_allocated(&manager->properties, &string_hash_ops);
+                if (r < 0) {
+                        log_oom();
+                        return 1;
                 }
+
+                eq++;
+                if (!isempty(eq)) {
+                        log_debug("udevd message (ENV) received, unset '%s'", key);
+
+                        r = hashmap_put(manager->properties, key, NULL);
+                        if (r < 0) {
+                                log_oom();
+                                return 1;
+                        }
+                } else {
+                        val = strdup(eq);
+                        if (!val) {
+                                log_oom();
+                                return 1;
+                        }
+
+                        log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
+
+                        r = hashmap_put(manager->properties, key, val);
+                        if (r < 0) {
+                                log_oom();
+                                return 1;
+                        }
+                }
+
+                key = val = NULL;
                 manager_kill_workers(manager);
         }
 
@@ -1508,7 +1537,7 @@ static int parse_argv(int argc, char *argv[]) {
 
 static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) {
         _cleanup_(manager_freep) Manager *manager = NULL;
-        int r, fd_worker, one = 1;
+        int r, fd_worker;
 
         assert(ret);
         assert(fd_ctrl >= 0);
@@ -1529,7 +1558,6 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
                 return log_error_errno(ENOMEM, "error reading rules");
 
         LIST_HEAD_INIT(manager->events);
-        udev_list_init(NULL, &manager->properties, true);
 
         manager->cgroup = cgroup;
 
@@ -1548,7 +1576,7 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
 
         fd_worker = manager->worker_watch[READ_END];
 
-        r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+        r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &const_int_one, sizeof(const_int_one));
         if (r < 0)
                 return log_error_errno(errno, "could not enable SO_PASSCRED: %m");
 
index b9c1b467b0d5cfd5fec2a5a2f5b99a6c2cb03bc7..fd830dde416ac79d50d2ab92aad1aac3c4d6e811 100644 (file)
@@ -793,6 +793,8 @@ LineMax=
 LockPersonality=
 LogExtraFields=
 LogLevelMax=
+LogRateLimitIntervalSec=
+LogRateLimitBurst=
 LogsDirectory=
 LogsDirectoryMode=
 MACVLAN=
index e4ac6ced64365c906c2b85cbfd1bca578e45892e..3cc86b3e92e6f494fbf382b2364d40bff31e07c8 100644 (file)
@@ -85,6 +85,7 @@ units = [
          'multi-user.target.wants/'],
         ['systemd-coredump.socket',             'ENABLE_COREDUMP',
          'sockets.target.wants/'],
+        ['systemd-exit.service',                 ''],
         ['systemd-initctl.socket',              '',
          'sockets.target.wants/'],
         ['systemd-journal-gatewayd.socket',     'ENABLE_REMOTE HAVE_MICROHTTPD'],
@@ -97,6 +98,8 @@ units = [
          'sockets.target.wants/'],
         ['systemd-networkd.socket',             'ENABLE_NETWORKD',
          join_paths(pkgsysconfdir, 'system/sockets.target.wants/')],
+        ['systemd-poweroff.service',             ''],
+        ['systemd-reboot.service',               ''],
         ['systemd-rfkill.socket',               'ENABLE_RFKILL'],
         ['systemd-tmpfiles-clean.timer',        '',
          'timers.target.wants/'],
@@ -133,7 +136,6 @@ in_units = [
         ['systemd-binfmt.service',               'ENABLE_BINFMT',
          'sysinit.target.wants/'],
         ['systemd-coredump@.service',            'ENABLE_COREDUMP'],
-        ['systemd-exit.service',                 ''],
         ['systemd-firstboot.service',            'ENABLE_FIRSTBOOT',
          'sysinit.target.wants/'],
         ['systemd-fsck-root.service',            ''],
@@ -178,11 +180,9 @@ in_units = [
         ['systemd-nspawn@.service',              ''],
         ['systemd-portabled.service',            'ENABLE_PORTABLED',
          'dbus-org.freedesktop.portable1.service'],
-        ['systemd-poweroff.service',             ''],
         ['systemd-quotacheck.service',           'ENABLE_QUOTACHECK'],
         ['systemd-random-seed.service',          'ENABLE_RANDOMSEED',
          'sysinit.target.wants/'],
-        ['systemd-reboot.service',               ''],
         ['systemd-remount-fs.service',           '',
          'local-fs.target.wants/'],
         ['systemd-resolved.service',             'ENABLE_RESOLVE',
similarity index 88%
rename from units/systemd-exit.service.in
rename to units/systemd-exit.service
index 2fb6ebd76792478454999f3b3b3bacb7fab30910..6029b13a0526e569ceb19966276a4021fac8a005 100644 (file)
@@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
 DefaultDependencies=no
 Requires=shutdown.target
 After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force exit
+SuccessAction=exit
index 9768928c579ad55d663013e622aa57e355504521..a51d59d10112ac90f7463ed1c27e4a6e6e3444e6 100644 (file)
@@ -30,9 +30,9 @@ RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
 SystemCallArchitectures=native
 LockPersonality=yes
 
-# If there are many split upjournal files we need a lot of fds to
-# access them all and combine
-LimitNOFILE=16384
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
 
 [Install]
 Also=systemd-journal-gatewayd.socket
index a94265f215b1ec34d72e9ec984edcf6a3b367e0a..fa8682cd2857308748bb2c030ecf0dd3af25e6a3 100644 (file)
@@ -32,5 +32,9 @@ SystemCallArchitectures=native
 LockPersonality=yes
 LogsDirectory=journal/remote
 
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
+
 [Install]
 Also=systemd-journal-remote.socket
index 42da70f473cda1d5a810b1122be48be20eadb6d4..1ded99087798e9f8cc07ce75a67128de782d1d5d 100644 (file)
@@ -32,9 +32,9 @@ SystemCallArchitectures=native
 LockPersonality=yes
 StateDirectory=systemd/journal-upload
 
-# If there are many split up journal files we need a lot of fds to
-# access them all and combine
-LimitNOFILE=16384
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
 
 [Install]
 WantedBy=multi-user.target
index 52939e6820b45576a1dfa93170bf88aa73ec1c3f..41cac8cf6569bb961deb85a96e5dc9023987604d 100644 (file)
@@ -35,8 +35,6 @@ SystemCallArchitectures=native
 LockPersonality=yes
 IPAddressDeny=any
 
-# Increase the default a bit in order to allow many simultaneous
-# services being run since we keep one fd open per service. Also, when
-# flushing journal files to disk, we might need a lot of fds when many
-# journal files are combined.
-LimitNOFILE=16384
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
index 5e090bcf238bd4d47fa579e07f827794528b840b..961263f6071495cb9843ed7042a8a55bdc6b8d57 100644 (file)
@@ -37,6 +37,6 @@ LockPersonality=yes
 IPAddressDeny=any
 FileDescriptorStoreMax=512
 
-# Increase the default a bit in order to allow many simultaneous
-# logins since we keep one fd open per session.
-LimitNOFILE=16384
+# Increase the default a bit in order to allow many simultaneous logins since
+# we keep one fd open per session.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
similarity index 89%
rename from units/systemd-poweroff.service.in
rename to units/systemd-poweroff.service
index e9fd6555088ba026611c96c88249d8d1acd0309a..8d1d54389b9984760e560c7452031b53646507c7 100644 (file)
@@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
 DefaultDependencies=no
 Requires=shutdown.target umount.target final.target
 After=shutdown.target umount.target final.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force poweroff
+SuccessAction=poweroff-force
similarity index 89%
rename from units/systemd-reboot.service.in
rename to units/systemd-reboot.service
index 4763ccfdca75231756a5ef3a06f00bd0715ea35c..505f60aabf026a1bccc1fb3c982d9fd4886de334 100644 (file)
@@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
 DefaultDependencies=no
 Requires=shutdown.target umount.target final.target
 After=shutdown.target umount.target final.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force reboot
+SuccessAction=reboot-force
index f1d118562c7a354dcd6292d3f2113fd7afee507f..c81a00e0502fc82e4eff1876d8a06be99933420c 100644 (file)
@@ -11,6 +11,7 @@
 Description=User Slice of UID %j
 Documentation=man:user@.service(5)
 After=systemd-user-sessions.service
+StopWhenUnneeded=yes
 
 [Slice]
 TasksMax=33%
index 8c5407b881f8770b4712380ce0feafc8d797e5cd..c168b89f9859247278a946b04d8fe4bcca264bc3 100644 (file)
@@ -8,9 +8,9 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=/run/user/%i mount wrapper
+Description=User Runtime Directory /run/user/%i
 Documentation=man:user@.service(5)
-After=systemd-user-sessions.service
+After=systemd-user-sessions.service dbus.service
 StopWhenUnneeded=yes
 IgnoreOnIsolate=yes
 
@@ -18,5 +18,5 @@ IgnoreOnIsolate=yes
 ExecStart=@rootlibexecdir@/systemd-user-runtime-dir start %i
 ExecStop=@rootlibexecdir@/systemd-user-runtime-dir stop %i
 Type=oneshot
-RemainAfterExit=true
+RemainAfterExit=yes
 Slice=user-%i.slice
index b1c2e95597682c537210b486bc7d85a73e0cc2a9..36341a42f5abf48a22fadd7b075a18fb49a761ed 100644 (file)
@@ -14,6 +14,7 @@ units = [
         'sockets.target',
         'sound.target',
         'timers.target',
+        'systemd-exit.service',
         'systemd-tmpfiles-clean.timer',
 ]
 
@@ -23,7 +24,6 @@ foreach file : units
 endforeach
 
 in_units = [
-        'systemd-exit.service',
         'systemd-tmpfiles-clean.service',
         'systemd-tmpfiles-setup.service',
 ]
similarity index 87%
rename from units/user/systemd-exit.service.in
rename to units/user/systemd-exit.service
index d69273f6b30d31ad8f6dce5c2482647eb3b707df..1d3b61e3ab1e74d7d42dbc73f1db1c2dc1b4d949 100644 (file)
@@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
 DefaultDependencies=no
 Requires=shutdown.target
 After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --user --force exit
+SuccessAction=exit-force