[Distribution]
Distribution=fedora
-Release=27
+Release=29
[Output]
Format=raw_btrfs
Bootable=yes
+KernelCommandLine=printk.devkmsg=on
[Partitions]
RootSize=3G
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"
* 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.
* 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
* 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
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
* 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
- 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
* 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
"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
* 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=
Працэс запуску юніта @UNIT@ завершаны.
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Юніт @UNIT@ спыняецца
Збой юніта @UNIT@.
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Юніт @UNIT@ перачытвае сваю канфігурацыю
Юніт @UNIT@ перачытаў сваю канфігурацыю.
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Працэс @EXECUTABLE@ не можа быць выкананы
Praces zapusku junita @UNIT@ zavieršany.
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Junit @UNIT@ spyniajecca
Zboj junita @UNIT@.
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Junit @UNIT@ pieračytvaje svaju kanfihuracyju
Junit @UNIT@ pieračytaŭ svaju kanfihuracyju.
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Praces @EXECUTABLE@ nie moža być vykanany
Стартирането на модул „@UNIT@“ завърши.
-Резултатът е: @RESULT@
+Резултатът е: @JOB_RESULT@
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Модул „@UNIT@“ се спира
Модулът „@UNIT@“ не успя да стартира.
-Резултатът е: @RESULT@
+Резултатът е: @JOB_RESULT@
-- d34d037fff1847e6ae669a370e694725
Subject: Модулът „@UNIT@“ започна презареждане на настройките си
Модулът „@UNIT@“ завърши презареждането на настройките си.
-Резултатът e: @RESULT@
+Резултатът e: @JOB_RESULT@
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Програмата „@EXECUTABLE@“ не успя да се стартира
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
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
Unit @UNIT@ has failed.
-The result is @RESULT@.
+The result is @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Unit @UNIT@ has begun reloading its configuration
Unit @UNIT@ has finished reloading its configuration
-The result is @RESULT@.
+The result is @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Process @EXECUTABLE@ could not be executed
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
Enhed @UNIT@ har fejlet.
-Resultatet er @RESULT@
+Resultatet er @JOB_RESULT@
-- d34d037fff1847e6ae669a370e694725
Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration
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
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
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
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é
Jedinica @UNIT@ je završila pokretanje.
-Rezultat pokretanja je @RESULT@.
+Rezultat pokretanja je @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Jedinica @UNIT@ je započela isključivanje
Jedinica @UNIT@ nije uspjela.
-Rezultat je @RESULT@.
+Rezultat je @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Jedinica @UNIT@ je započela ponovno učitavati podešavanja
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
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
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
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@
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
L'unità @UNIT@ è fallita.
-Il risultato è @RESULT@.
+Il risultato è @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: L'unità @UNIT@ inizia a caricare la propria configurazione
L'unità @UNIT@ è terminata ricaricando la propria configurazione
-Il risultato è @RESULT@.
+Il risultato è @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Il processo @EXECUTABLE@ non può essere eseguito
@UNIT@ 유닛 시동을 마쳤습니다.
-시동 결과는 @RESULT@ 입니다.
+시동 결과는 @JOB_RESULT@ 입니다.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: @UNIT@ 유닛 끝내기 동작 시작
@UNIT@ 유닛 동작에 실패했습니다.
-결과는 @RESULT@ 입니다.
+결과는 @JOB_RESULT@ 입니다.
-- d34d037fff1847e6ae669a370e694725
Subject: @UNIT@ 유닛 설정 다시 읽기 시작
@UNIT@ 유닛의 설정 다시 읽기 동작을 끝냈습니다.
-결과는 @RESULT@ 입니다.
+결과는 @JOB_RESULT@ 입니다.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: @EXECUTABLE@ 프로세스 시작할 수 없음
Jednostka @UNIT@ ukończyła uruchamianie.
-Wynik uruchamiania: @RESULT@.
+Wynik uruchamiania: @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Rozpoczęto wyłączanie jednostki @UNIT@
Jednostka @UNIT@ się nie powiodła.
-Wynik: @RESULT@.
+Wynik: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Rozpoczęto ponowne wczytywanie konfiguracji jednostki @UNIT@
Jednostka @UNIT@ ukończyła ponowne wczytywanie swojej konfiguracji.
-Wynik: @RESULT@.
+Wynik: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Nie można wykonać procesu @EXECUTABLE@
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
A unidade @UNIT@ falhou.
-O resultado é @RESULT@.
+O resultado é @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Unidade @UNIT@ iniciou recarregamento de sua configuração
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
Процесс запуска юнита @UNIT@ был завершен.
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
# Subject: Unit @UNIT@ has begun shutting down
-- de5b426a63be47a7b6ac3eaac82e2f6f
Произошел сбой юнита @UNIT@.
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
# Subject: Unit @UNIT@ has begun with reloading its configuration
-- d34d037fff1847e6ae669a370e694725
Юнит @UNIT@ завершил процесс перечитывания своей конфигурации.
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
# Subject: Process @EXECUTABLE@ could not be executed
-- 641257651c1b4ec9a8624d7a40a9e1e7
Јединица @UNIT@ је завршила са покретањем.
-Исход покретања је @RESULT@.
+Исход покретања је @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Јединица @UNIT@ је почела са гашењем
Јединица @UNIT@ је пукла.
-Исход је @RESULT@.
+Исход је @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Јединица @UNIT@ је почела са поновним учитавањем свог подешавања
Јединица @UNIT@ је завршила са поновним учитавањем свог подешавања
-Исход је @RESULT@.
+Исход је @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Процес @EXECUTABLE@ није могао бити извршен
@UNIT@ 单元已结束启动。
-启动结果为“@RESULT@”。
+启动结果为“@JOB_RESULT@”。
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: @UNIT@ 单元已开始停止操作
@UNIT@ 单元已失败。
-结果为“@RESULT@”。
+结果为“@JOB_RESULT@”。
-- d34d037fff1847e6ae669a370e694725
Subject: @UNIT@ 单元已开始重新载入其配置
@UNIT@ 单元已结束配置重载入操作。
-结果为“@RESULT@”。
+结果为“@JOB_RESULT@”。
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: 进程 @EXECUTABLE@ 无法执行
單位 @UNIT@ 啟動已結束。
-啟動結果為 @RESULT@。
+啟動結果為 @JOB_RESULT@。
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: 單位 @UNIT@ 已開始關閉
單位 @UNIT@ 已失敗。
-結果為 @RESULT@。
+結果為 @JOB_RESULT@。
-- d34d037fff1847e6ae669a370e694725
Subject: 單位 @UNIT@ 已開始重新載入其設定
單位 @UNIT@ 已結束重新載入其設定
-結果為 @RESULT@。
+結果為 @JOB_RESULT@。
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: 行程 @EXECUTABLE@ 無法執行
--- /dev/null
+@@
+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);
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
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
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
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:
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
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
`/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
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
✓ SyslogLevelPrefix=
✓ LogLevelMax=
✓ LogExtraFields=
+✓ LogRateLimitIntervalSec=
+✓ LogRateLimitBurst=
✓ SecureBits=
✓ CapabilityBoundingSet=
✓ AmbientCapabilities=
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:
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:
-theme: jekyll-theme-minimal
\ No newline at end of file
+theme: jekyll-theme-primer
#
# 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*
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
<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>
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>
<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>
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>
'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',
<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->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
<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>
<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>
<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>
<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>
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>
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>
Kind=tap
[Tap]
-MultiQueue=true
-PacketInfo=true</programlisting> </example>
+MultiQueue=yes
+PacketInfo=yes</programlisting> </example>
<example>
<title>/etc/systemd/network/25-sit.netdev</title>
<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>
</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>
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>
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>
</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>
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.
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.
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)
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'))
#####################################################################
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',
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,
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
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"
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);
}
}
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) \
r = cg_enumerate_processes(controller, path, &f);
if (r == -ENOENT)
- return 1;
+ return true;
if (r < 0)
return r;
* 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;
r = cg_enumerate_subgroups(controller, path, &d);
if (r == -ENOENT)
- return 1;
+ return true;
if (r < 0)
return r;
_CONF_PATHS_SPLIT_USR(n))
#define LONG_LINE_MAX (1U*1024U*1024U)
+
+#define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL)
}
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. */
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;
}
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:
;
}
/* 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) {
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);
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) {
* 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) \
/* 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 */
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, "/./"))
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);
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;
}
static unsigned shuffle_up(Prioq *q, unsigned idx) {
assert(q);
+ assert(idx < q->n_items);
while (idx > 0) {
unsigned k;
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;
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);
#include "alloc-util.h"
#include "extract-word.h"
+#include "fd-util.h"
#include "format-util.h"
#include "macro.h"
#include "missing.h"
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;
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;
+}
void rlimit_free_all(struct rlimit **rl);
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
+
+int rlimit_nofile_bump(int limit);
/* 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) \
/* 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 */
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
+#include <string.h>
#include <sys/types.h>
struct siphash {
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);
+}
_cleanup_close_ int fd = -1;
const char *p;
- int r, one;
+ int r;
assert(a);
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);
assert(a);
assert(s);
- zero(*a);
- a->type = SOCK_STREAM;
+ *a = (SocketAddress) {
+ .type = SOCK_STREAM,
+ };
if (*s == '[') {
uint16_t port;
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;
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;
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;
}
}
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;
}
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",
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 */
+ }
+}
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_;
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);
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) {
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;
}
if (i < (int)size) {
*dest += i;
size -= i;
- } else {
+ } else
size = 0;
- }
va_end(va);
return size;
}
do {
size = strpcpy(dest, size, src);
src = va_arg(va, char *);
- } while (src != NULL);
+ } while (src);
va_end(va);
return size;
}
do {
size = strpcpy(&s, size, src);
src = va_arg(va, char *);
- } while (src != NULL);
+ } while (src);
va_end(va);
return size;
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;
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;
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;
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 {
(*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;
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);
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);
}
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;
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 {
}
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) {
INTN order;
order = c_order(*s1) - c_order(*s2);
- if (order)
+ if (order != 0)
return order;
s1++;
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;
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;
}
}
-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;
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);
if (entry->type == LOADER_UNDEFINED) {
config_entry_free(entry);
- FreePool(entry);
return;
}
}
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) {
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;
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;
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;
}
}
+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
*/
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;
+ }
}
/*
*/
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;
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;
}
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);
}
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;
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;
}
}
-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;
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);
}
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)) {
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);
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;
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;
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;
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);
}
goto out;
}
+ config_write_entries_to_variable(&config);
+
config_title_generate(&config);
/* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
}
}
+ 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);
/* 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>
* 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) {
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;
}
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) {
#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";
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);
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;
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),
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);
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")) {
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);
}
}
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(
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);
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)
(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.");
#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) {
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);
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;
break;
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
- log_and_status(m, "Rebooting immediately", reason);
+ log_and_status(m, warn, "Rebooting immediately", reason);
sync();
(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();
[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;
+}
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);
}
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);
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. */
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(
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;
}
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;
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) {
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;
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;
}
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;
}
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;
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);
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;
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;
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;
$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)
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");
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) {
#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"
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;
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");
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");
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();
}
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;
}
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);
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");
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)
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) {
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");
}
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;
}
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;
}
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;
}
_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:
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;
}
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;
}
}
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!");
}
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");
}
}
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");
}
#DefaultLimitSTACK=
#DefaultLimitCORE=
#DefaultLimitRSS=
-#DefaultLimitNOFILE=
+#DefaultLimitNOFILE=1024:@HIGH_RLIMIT_NOFILE@
#DefaultLimitAS=
#DefaultLimitNPROC=
#DefaultLimitMEMLOCK=
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;
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) {
* 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)))
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);
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)
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);
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;
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);
}
}
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) {
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. */
#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"
#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"
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;
.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[]) {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/import/notify",
};
- static const int one = 1;
int r;
assert(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);
#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"
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;
#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"
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;
}
int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
+ int r;
+
assert(af);
assert(af->header);
assert(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
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) {
#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"
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;
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:
}
int server_open_audit(Server *s) {
- static const int one = 1;
int r;
if (s->audit_fd < 0) {
} 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");
#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"
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) {
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;
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) {
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);
}
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,
(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;
/* 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;
}
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(
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/socket",
};
- static const int one = 1;
int r;
assert(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)
} 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");
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;
};
struct JournalRateLimit {
- usec_t interval;
- unsigned burst;
JournalRateLimitGroup* buckets[BUCKETS_MAX];
JournalRateLimitGroup *lru, *lru_tail;
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;
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;
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);
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);
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;
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)
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]];
return 1;
}
- if (p->begin + r->interval < ts) {
+ if (p->begin + rl_interval < ts) {
unsigned s;
s = p->suppressed;
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);
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;
}
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);
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)
(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");
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;
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)
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/dev-log",
};
- static const int one = 1;
int r;
assert(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)
} 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");
}
_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+ int r;
+
assert(f);
assert(l);
assert(f->location_type == LOCATION_SEEK);
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;
.filter = filter
};
_cleanup_close_ int s = -1;
- int r, on = 1;
+ int r;
assert(ifindex > 0);
assert(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;
};
_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)
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;
}
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;
}
.in6.sin6_scope_id = index,
};
_cleanup_close_ int s = -1;
- int r, off = 0, on = 1;
+ int r;
assert(index > 0);
assert(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;
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);
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;
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;
sd_bus_set_method_call_timeout;
sd_bus_get_method_call_timeout;
+ sd_bus_error_move;
+
sd_device_ref;
sd_device_unref;
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;
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;
}
} 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;
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");
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);
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,
};
struct cmsghdr *cmsg = NULL;
const char *e;
bool send_ucred;
- int r;
+ int r, salen;
if (!state) {
r = -EINVAL;
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;
}
(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()) ||
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;
}
typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
+ device_enumerator_dedup_devices(enumerator);
enumerator->scan_uptodate = true;
enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
}
typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
+ device_enumerator_dedup_devices(enumerator);
enumerator->scan_uptodate = true;
enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
#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"
}
}
+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;
}
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) {
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) {
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;
* 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);
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;
#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"
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);
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/vt.h>
+#if ENABLE_UTMP
+#include <utmpx.h>
+#endif
#include "sd-device.h"
#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"
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;
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)
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)
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;
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;
}
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;
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) {
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) {
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;
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
+}
} 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;
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;
r = -ENOMEM;
goto fail;
}
+
+ session->tty_validity = TTY_FROM_PAM;
}
if (!isempty(display)) {
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;
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),
};
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) {
}
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);
}
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);
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;
}
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;
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;
}
"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;
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;
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
return false;
+ sd_bus_error_move(ret_error, &error);
return r;
}
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)
#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);
free(s->positions);
free(s->state_file);
- free(s);
+
+ return mfree(s);
}
int seat_save(Seat *s) {
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;
}
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;
!!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) {
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);
}
int seat_stop(Seat *s, bool force) {
- int r = 0;
+ int r;
assert(s);
"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)
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);
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;
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);
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);
#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);
if (s->user->display == s)
s->user->display = NULL;
+
+ user_update_last_session_timer(s->user);
}
if (s->seat) {
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);
hashmap_remove(s->manager->sessions, s->id);
free(s->state_file);
- free(s);
+
+ return mfree(s);
}
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) {
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);
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);
int session_load(Session *s) {
_cleanup_free_ char *remote = NULL,
*seat = NULL,
+ *tty_validity = NULL,
*vtnr = NULL,
*state = NULL,
*position = NULL,
*monotonic = NULL,
*controller = NULL,
*active = NULL,
- *devices = NULL;
+ *devices = NULL,
+ *is_display = NULL;
int k, r;
"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,
"CONTROLLER", &controller,
"ACTIVE", &active,
"DEVICES", &devices,
+ "IS_DISPLAY", &is_display,
NULL);
if (r < 0)
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) {
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);
/* 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);
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();
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)
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);
if (!s->user)
return -ESTALE;
+ if (s->stopping)
+ return -EINVAL;
+
if (s->started)
return 0;
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;
* 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.
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);
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);
}
/* 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;
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)
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) {
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)
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;
}
};
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);
_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;
dual_timestamp timestamp;
- char *tty;
char *display;
+ char *tty;
+ TTYValidity tty_validity;
bool remote;
char *remote_user;
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;
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);
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);
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);
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);
#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;
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;
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;
}
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);
}
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)
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);
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;
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))
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;
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);
r = k;
}
- unlink(u->state_file);
+ (void) unlink(u->state_file);
user_add_to_gc_queue(u);
if (u->started) {
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)
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;
}
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) {
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;
}
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) {
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) {
}
}
+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",
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);
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);
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);
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);
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);
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);
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
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;
}
/* 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);
}
}
/* 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);
}
}
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)
/* 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);
Hashmap *devices;
Hashmap *seats;
Hashmap *sessions;
+ Hashmap *sessions_by_leader;
Hashmap *users;
Hashmap *inhibitors;
Hashmap *buttons;
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;
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 */
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);
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);
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);
user_runtime_dir_sources = files('''
user-runtime-dir.c
- logind.h
'''.split())
if conf.get('ENABLE_LOGIND') == 1
}
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);
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);
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,
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;
* 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
#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);
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=*" : "");
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;
: "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);
#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"
#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"
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);
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");
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");
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;
}
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;
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(
#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;
_cleanup_close_ int fd = -1;
union sockaddr_union sa;
socklen_t salen;
- static const int one = 1;
int r, ifindex;
assert(s);
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;
}
/* 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 {
}
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),
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 */
}
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),
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 */
.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);
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;
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;
}
} 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;
.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);
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;
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;
}
} 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;
.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);
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;
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;
}
} 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;
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(LLMNR_PORT),
};
- static const int one = 1;
int r;
assert(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;
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;
}
} 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;
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) {
.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);
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;
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;
}
} 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;
.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);
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;
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;
}
} 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;
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(
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);
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, ':');
#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"
}
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;
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);
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.");
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");
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";
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) */
(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
(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);
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();
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.
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);
libmount,
libblkid]],
+ [['src/test/test-emergency-action.c'],
+ [libcore,
+ libshared],
+ []],
+
[['src/test/test-job-type.c'],
[libcore,
libshared],
--- /dev/null
+/* 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;
+}
}
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));
}
assert_se(prioq_isempty(q));
- prioq_free(q);
}
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) {
};
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[]) {
.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));
}
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);
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;
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;
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;
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);
* 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;
}
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);
}
if (uctrl == NULL)
return 0;
if (uctrl->cleanup_socket)
- unlink(uctrl->saddr.un.sun_path);
+ sockaddr_un_unlink(&uctrl->saddr.un);
return 0;
}
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);
}
/* 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);
}
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);
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;
}
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 */
}
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);
#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"
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;
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);
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;
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;
#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);
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=");
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 ||
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;
}
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),
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),
}
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:
cur = &rules->tokens[cur->key.rule_goto];
continue;
case TK_END:
- return;
+ return 0;
case TK_M_PARENTS_MIN:
case TK_M_PARENTS_MAX:
/* fast-forward to next rule */
cur = rule + rule->rule.token_count;
}
+
+ return 0;
}
int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
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);
#include "sd-device.h"
#include "sd-netlink.h"
+#include "hashmap.h"
#include "label.h"
#include "libudev-private.h"
#include "macro.h"
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;
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 */
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);
_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);
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);
}
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;
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);
/* 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,
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;
}
continue;
worker->state = WORKER_KILLED;
- kill(worker->pid, SIGTERM);
+ (void) kill(worker->pid, SIGTERM);
}
}
_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);
}
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);
}
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);
return log_error_errno(ENOMEM, "error reading rules");
LIST_HEAD_INIT(manager->events);
- udev_list_init(NULL, &manager->properties, true);
manager->cgroup = cgroup;
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");
LockPersonality=
LogExtraFields=
LogLevelMax=
+LogRateLimitIntervalSec=
+LogRateLimitBurst=
LogsDirectory=
LogsDirectoryMode=
MACVLAN=
'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'],
'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/'],
['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', ''],
['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',
DefaultDependencies=no
Requires=shutdown.target
After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force exit
+SuccessAction=exit
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
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
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
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@
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@
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
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
Description=User Slice of UID %j
Documentation=man:user@.service(5)
After=systemd-user-sessions.service
+StopWhenUnneeded=yes
[Slice]
TasksMax=33%
# (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
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
'sockets.target',
'sound.target',
'timers.target',
+ 'systemd-exit.service',
'systemd-tmpfiles-clean.timer',
]
endforeach
in_units = [
- 'systemd-exit.service',
'systemd-tmpfiles-clean.service',
'systemd-tmpfiles-setup.service',
]
DefaultDependencies=no
Requires=shutdown.target
After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --user --force exit
+SuccessAction=exit-force