summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile-man.am18
-rw-r--r--Makefile.am22
-rw-r--r--NEWS81
-rw-r--r--TODO11
-rw-r--r--catalog/systemd.hr.catalog314
-rw-r--r--catalog/systemd.pl.catalog58
-rw-r--r--configure.ac215
-rw-r--r--hwdb/60-keyboard.hwdb5
-rw-r--r--hwdb/70-mouse.hwdb8
-rw-r--r--man/daemon.xml16
-rw-r--r--man/journalctl.xml17
-rw-r--r--man/kernel-command-line.xml9
-rw-r--r--man/loginctl.xml36
-rw-r--r--man/logind.conf.xml50
-rw-r--r--man/machinectl.xml18
-rw-r--r--man/sd_event_source_set_priority.xml2
-rw-r--r--man/sd_journal_add_match.xml18
-rw-r--r--man/sd_journal_get_data.xml2
-rw-r--r--man/sd_journal_open.xml81
-rw-r--r--man/sd_uid_get_state.xml13
-rw-r--r--man/sd_watchdog_enabled.xml12
-rw-r--r--man/systemctl.xml26
-rw-r--r--man/systemd-ask-password.xml2
-rw-r--r--man/systemd-journal-gatewayd.service.xml2
-rw-r--r--man/systemd-nspawn.xml106
-rw-r--r--man/systemd-run.xml101
-rw-r--r--man/systemd-tmpfiles.xml10
-rw-r--r--man/systemd.exec.xml13
-rw-r--r--man/systemd.network.xml11
-rw-r--r--man/systemd.nspawn.xml18
-rw-r--r--man/systemd.special.xml2
-rw-r--r--man/systemd.timer.xml3
-rw-r--r--man/tmpfiles.d.xml81
-rw-r--r--po/LINGUAS1
-rw-r--r--po/fr.po106
-rw-r--r--po/hr.po570
-rw-r--r--po/pl.po104
-rw-r--r--src/analyze/analyze.c1
-rw-r--r--src/basic/copy.c2
-rw-r--r--src/basic/dirent-util.c12
-rw-r--r--src/basic/dirent-util.h2
-rw-r--r--src/basic/fd-util.c12
-rw-r--r--src/basic/fd-util.h2
-rw-r--r--src/basic/fdset.c2
-rw-r--r--src/basic/fileio.c124
-rw-r--r--src/basic/fileio.h6
-rw-r--r--src/basic/fs-util.c15
-rw-r--r--src/basic/fs-util.h2
-rw-r--r--src/basic/missing.h8
-rw-r--r--src/basic/mount-util.c4
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/path-util.c49
-rw-r--r--src/basic/path-util.h3
-rw-r--r--src/basic/process-util.c12
-rw-r--r--src/basic/process-util.h2
-rw-r--r--src/basic/terminal-util.c19
-rw-r--r--src/basic/time-util.c37
-rw-r--r--src/basic/time-util.h1
-rw-r--r--src/basic/user-util.h5
-rw-r--r--src/basic/util.c2
-rw-r--r--src/cgls/cgls.c11
-rw-r--r--src/cgtop/cgtop.c2
-rw-r--r--src/core/busname.c10
-rw-r--r--src/core/dbus-manager.c41
-rw-r--r--src/core/dbus-unit.c147
-rw-r--r--src/core/dbus-unit.h1
-rw-r--r--src/core/ima-setup.c2
-rw-r--r--src/core/ima-setup.h2
-rw-r--r--src/core/machine-id-setup.c13
-rw-r--r--src/core/main.c11
-rw-r--r--src/core/manager.c2
-rw-r--r--src/core/mount.c10
-rw-r--r--src/core/org.freedesktop.systemd1.conf4
-rw-r--r--src/core/selinux-access.c5
-rw-r--r--src/core/service.c19
-rw-r--r--src/core/service.h2
-rw-r--r--src/core/socket.c10
-rw-r--r--src/core/swap.c10
-rw-r--r--src/core/timer.c2
-rw-r--r--src/core/unit.c20
-rw-r--r--src/core/unit.h9
-rw-r--r--src/coredump/coredump.c54
-rw-r--r--src/journal-remote/journal-gatewayd.c4
-rw-r--r--src/journal-remote/journal-remote-parse.c2
-rw-r--r--src/journal/journal-file.c120
-rw-r--r--src/journal/journal-file.h3
-rw-r--r--src/journal/journal-internal.h3
-rw-r--r--src/journal/journal-send.c2
-rw-r--r--src/journal/journal-verify.c6
-rw-r--r--src/journal/journalctl.c212
-rw-r--r--src/journal/journald-native.c2
-rw-r--r--src/journal/journald-server.c4
-rw-r--r--src/journal/sd-journal.c294
-rw-r--r--src/journal/test-journal-flush.c2
-rw-r--r--src/journal/test-journal-interleaving.c8
-rw-r--r--src/journal/test-journal-stream.c6
-rw-r--r--src/journal/test-journal-verify.c6
-rw-r--r--src/journal/test-journal.c10
-rw-r--r--src/libsystemd/libsystemd.sym6
-rw-r--r--src/libsystemd/sd-device/sd-device.c6
-rw-r--r--src/libsystemd/sd-event/sd-event.c10
-rw-r--r--src/libsystemd/sd-event/test-event.c12
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c1
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c2
-rw-r--r--src/login/.gitignore1
-rw-r--r--src/login/loginctl.c49
-rw-r--r--src/login/logind-core.c8
-rw-r--r--src/login/logind-dbus.c8
-rw-r--r--src/login/logind-session.c2
-rw-r--r--src/login/logind.c82
-rw-r--r--src/login/logind.conf.in (renamed from src/login/logind.conf)2
-rw-r--r--src/login/org.freedesktop.login1.policy.in8
-rw-r--r--src/login/systemd-user.m41
-rw-r--r--src/machine/image-dbus.c2
-rw-r--r--src/machine/machine-dbus.c92
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machinectl.c66
-rw-r--r--src/machine/machined-dbus.c21
-rw-r--r--src/network/networkd-address.c7
-rw-r--r--src/network/networkd-dhcp4.c4
-rw-r--r--src/network/networkd-link.c100
-rw-r--r--src/network/networkd-network.c13
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/network/networkd-route.c6
-rw-r--r--src/network/test-network-tables.c2
-rw-r--r--src/nspawn/nspawn-gperf.gperf4
-rw-r--r--src/nspawn/nspawn-network.c47
-rw-r--r--src/nspawn/nspawn-network.h2
-rw-r--r--src/nspawn/nspawn-patch-uid.c469
-rw-r--r--src/nspawn/nspawn-patch-uid.h23
-rw-r--r--src/nspawn/nspawn-register.c1
-rw-r--r--src/nspawn/nspawn-settings.c86
-rw-r--r--src/nspawn/nspawn-settings.h15
-rw-r--r--src/nspawn/nspawn.c367
-rw-r--r--src/nspawn/test-patch-uid.c61
-rw-r--r--src/resolve/RFCs2
-rw-r--r--src/resolve/resolved-link.c2
-rw-r--r--src/run/run.c12
-rw-r--r--src/shared/bus-unit-util.c1287
-rw-r--r--src/shared/bus-unit-util.h57
-rw-r--r--src/shared/bus-util.c859
-rw-r--r--src/shared/bus-util.h33
-rw-r--r--src/shared/cgroup-show.c92
-rw-r--r--src/shared/cgroup-show.h8
-rw-r--r--src/shared/dropin.c2
-rw-r--r--src/shared/install.c59
-rw-r--r--src/shared/logs-show.c49
-rw-r--r--src/shared/logs-show.h3
-rw-r--r--src/shared/output-mode.c37
-rw-r--r--src/shared/output-mode.h11
-rw-r--r--src/shared/path-lookup.c11
-rw-r--r--src/systemctl/systemctl.c58
-rw-r--r--src/systemd/sd-journal.h13
-rw-r--r--src/test/test-nss.c6
-rw-r--r--src/test/test-path-util.c22
-rw-r--r--src/test/test-tmpfiles.c26
-rw-r--r--src/tmpfiles/tmpfiles.c88
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c2
-rw-r--r--src/udev/udev-builtin-net_id.c28
l---------test/TEST-09-ISSUE-2691/Makefile1
-rwxr-xr-xtest/TEST-09-ISSUE-2691/test.sh76
-rwxr-xr-xtest/networkd-test.py7
-rwxr-xr-x[-rw-r--r--]test/sysv-generator-test.py2
-rw-r--r--units/emergency.service.in1
-rw-r--r--units/rc-local.service.in1
-rw-r--r--units/systemd-machined.service.in2
-rw-r--r--units/systemd-nspawn@.service.in2
-rw-r--r--units/systemd-user-sessions.service.in2
-rw-r--r--units/tmp.mount.m41
170 files changed, 5993 insertions, 2016 deletions
diff --git a/.gitignore b/.gitignore
index 02ba86ef6f..c7eb14452d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -240,6 +240,7 @@
/test-ns
/test-nss
/test-parse-util
+/test-patch-uid
/test-path
/test-path-lookup
/test-path-util
diff --git a/Makefile-man.am b/Makefile-man.am
index 7514e6a24f..d5b328d267 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -242,6 +242,7 @@ MANPAGES_ALIAS += \
man/SD_JOURNAL_INVALIDATE.3 \
man/SD_JOURNAL_LOCAL_ONLY.3 \
man/SD_JOURNAL_NOP.3 \
+ man/SD_JOURNAL_OS_ROOT.3 \
man/SD_JOURNAL_RUNTIME_ONLY.3 \
man/SD_JOURNAL_SUPPRESS_LOCATION.3 \
man/SD_JOURNAL_SYSTEM.3 \
@@ -388,9 +389,10 @@ MANPAGES_ALIAS += \
man/sd_journal_get_timeout.3 \
man/sd_journal_has_persistent_files.3 \
man/sd_journal_next_skip.3 \
- man/sd_journal_open_container.3 \
man/sd_journal_open_directory.3 \
+ man/sd_journal_open_directory_fd.3 \
man/sd_journal_open_files.3 \
+ man/sd_journal_open_files_fd.3 \
man/sd_journal_perror.3 \
man/sd_journal_previous.3 \
man/sd_journal_previous_skip.3 \
@@ -571,6 +573,7 @@ man/SD_JOURNAL_FOREACH_UNIQUE.3: man/sd_journal_query_unique.3
man/SD_JOURNAL_INVALIDATE.3: man/sd_journal_get_fd.3
man/SD_JOURNAL_LOCAL_ONLY.3: man/sd_journal_open.3
man/SD_JOURNAL_NOP.3: man/sd_journal_get_fd.3
+man/SD_JOURNAL_OS_ROOT.3: man/sd_journal_open.3
man/SD_JOURNAL_RUNTIME_ONLY.3: man/sd_journal_open.3
man/SD_JOURNAL_SUPPRESS_LOCATION.3: man/sd_journal_print.3
man/SD_JOURNAL_SYSTEM.3: man/sd_journal_open.3
@@ -717,9 +720,10 @@ man/sd_journal_get_monotonic_usec.3: man/sd_journal_get_realtime_usec.3
man/sd_journal_get_timeout.3: man/sd_journal_get_fd.3
man/sd_journal_has_persistent_files.3: man/sd_journal_has_runtime_files.3
man/sd_journal_next_skip.3: man/sd_journal_next.3
-man/sd_journal_open_container.3: man/sd_journal_open.3
man/sd_journal_open_directory.3: man/sd_journal_open.3
+man/sd_journal_open_directory_fd.3: man/sd_journal_open.3
man/sd_journal_open_files.3: man/sd_journal_open.3
+man/sd_journal_open_files_fd.3: man/sd_journal_open.3
man/sd_journal_perror.3: man/sd_journal_print.3
man/sd_journal_previous.3: man/sd_journal_next.3
man/sd_journal_previous_skip.3: man/sd_journal_next.3
@@ -1034,6 +1038,9 @@ man/SD_JOURNAL_LOCAL_ONLY.html: man/sd_journal_open.html
man/SD_JOURNAL_NOP.html: man/sd_journal_get_fd.html
$(html-alias)
+man/SD_JOURNAL_OS_ROOT.html: man/sd_journal_open.html
+ $(html-alias)
+
man/SD_JOURNAL_RUNTIME_ONLY.html: man/sd_journal_open.html
$(html-alias)
@@ -1472,15 +1479,18 @@ man/sd_journal_has_persistent_files.html: man/sd_journal_has_runtime_files.html
man/sd_journal_next_skip.html: man/sd_journal_next.html
$(html-alias)
-man/sd_journal_open_container.html: man/sd_journal_open.html
+man/sd_journal_open_directory.html: man/sd_journal_open.html
$(html-alias)
-man/sd_journal_open_directory.html: man/sd_journal_open.html
+man/sd_journal_open_directory_fd.html: man/sd_journal_open.html
$(html-alias)
man/sd_journal_open_files.html: man/sd_journal_open.html
$(html-alias)
+man/sd_journal_open_files_fd.html: man/sd_journal_open.html
+ $(html-alias)
+
man/sd_journal_perror.html: man/sd_journal_print.html
$(html-alias)
diff --git a/Makefile.am b/Makefile.am
index 198265f48d..b323de55c6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -962,6 +962,7 @@ noinst_LTLIBRARIES += \
libshared_la_SOURCES = \
src/shared/output-mode.h \
+ src/shared/output-mode.c \
src/shared/gpt.h \
src/shared/udev-util.h \
src/shared/linux/auto_dev-ioctl.h \
@@ -1037,6 +1038,8 @@ libshared_la_SOURCES = \
src/shared/machine-pool.h \
src/shared/resolve-util.c \
src/shared/resolve-util.h \
+ src/shared/bus-unit-util.c \
+ src/shared/bus-unit-util.h \
src/shared/tests.h \
src/shared/tests.c
@@ -3018,6 +3021,8 @@ systemd_nspawn_SOURCES = \
src/nspawn/nspawn-setuid.h \
src/nspawn/nspawn-stub-pid1.c \
src/nspawn/nspawn-stub-pid1.h \
+ src/nspawn/nspawn-patch-uid.c \
+ src/nspawn/nspawn-patch-uid.h \
src/core/mount-setup.c \
src/core/mount-setup.h \
src/core/loopback-setup.c \
@@ -3045,6 +3050,17 @@ systemd_nspawn_LDADD += \
libfirewall.la
endif
+test_patch_uid_SOURCES = \
+ src/nspawn/nspawn-patch-uid.c \
+ src/nspawn/nspawn-patch-uid.h \
+ src/nspawn/test-patch-uid.c
+
+test_patch_uid_LDADD = \
+ libshared.la
+
+manual_tests += \
+ test-patch-uid
+
# ------------------------------------------------------------------------------
systemd_run_SOURCES = \
src/run/run.c
@@ -5720,7 +5736,7 @@ dist_dbussystemservice_DATA += \
dist_dbuspolicy_DATA += \
src/login/org.freedesktop.login1.conf
-dist_pkgsysconf_DATA += \
+nodist_pkgsysconf_DATA += \
src/login/logind.conf
polkitpolicy_files += \
@@ -5757,7 +5773,8 @@ gperf_gperf_sources += \
EXTRA_DIST += \
src/login/71-seat.rules.in \
src/login/73-seat-late.rules.in \
- units/systemd-logind.service.in
+ units/systemd-logind.service.in \
+ src/login/logind.conf.in
# ------------------------------------------------------------------------------
if HAVE_PAM
@@ -5874,6 +5891,7 @@ substitutions = \
'|NTP_SERVERS=$(NTP_SERVERS)|' \
'|DNS_SERVERS=$(DNS_SERVERS)|' \
'|DEFAULT_DNSSEC_MODE=$(DEFAULT_DNSSEC_MODE)|' \
+ '|KILL_USER_PROCESSES=$(KILL_USER_PROCESSES)|' \
'|systemuidmax=$(SYSTEM_UID_MAX)|' \
'|systemgidmax=$(SYSTEM_GID_MAX)|' \
'|TTY_GID=$(TTY_GID)|' \
diff --git a/NEWS b/NEWS
index 58a0f032a8..16ea7b7290 100644
--- a/NEWS
+++ b/NEWS
@@ -22,9 +22,30 @@ CHANGES WITH 230 in spe:
* systemd-resolve conveniently resolves DANE records with the --tlsa
option and OPENPGPKEY records with the --openpgp option.
- * Testing tool /usr/lib/systemd/systemd-activate is renamed to
- systemd-socket-activate and installed into /usr/bin. It is now fully
- supported.
+ * systemd-logind will now by default terminate user processes that are
+ part of the user session scope unit (session-XX.scope) when the user
+ logs out. This behaviour is controlled by the
+ KillUserProcesses=yes|no setting in logind.conf, and previous default
+ of "no" is now changed to "yes". This means that user sessions will
+ be properly cleaned up after, but additional steps are necessary to
+ allow intentionally long-running processes to survive logout.
+
+ While the user is logged in at least once, user@.service is running,
+ and any service that should survive the end of any individual login
+ session can be started at a user service or scope using systemd-run.
+ systemd-run(1) man page has been extended with an example which
+ shows how to run screen in a scope unit underneath user@.service.
+ The same command works for tmux.
+
+ After the user logs out of all sessions, user@.service will be
+ terminated too, by default, unless the user has "lingering" enabled.
+ To effectively allow users to run long-term tasks even if they are
+ logged out, lingering must be enabled for them. See loginctl(1) for
+ details. The default polkit policy was modified to allow users to
+ set lingering for themselves without authentication.
+
+ Previous defaults can be restored at compile time by the
+ --without-kill-user-processes option.
* The unified cgroup hierarchy added in Linux 4.5 is now supported.
Use systemd.unified_cgroup_hierarchy=1 on the kernel command line
@@ -45,6 +66,10 @@ CHANGES WITH 230 in spe:
* The Unique Identifier sent in DHCP requests can be configured.
+ * Testing tool /usr/lib/systemd/systemd-activate is renamed to
+ systemd-socket-activate and installed into /usr/bin. It is now fully
+ supported.
+
* systemd-journald now uses separate threads to flush changes to
disk when closing journal files.
@@ -286,7 +311,7 @@ CHANGES WITH 229:
Andersen, Tom Gundersen, Torstein Husebø, Umut Tezduyar Lindskog, Vito
Caputo, WaLyong Cho, Yu Watanabe, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2016-02-11
+ — Berlin, 2016-02-11
CHANGES WITH 228:
@@ -487,7 +512,7 @@ CHANGES WITH 228:
Tom Gundersen, Torstein Husebø, Vito Caputo, Zbigniew
Jędrzejewski-Szmek
- -- Berlin, 2015-11-18
+ — Berlin, 2015-11-18
CHANGES WITH 227:
@@ -691,7 +716,7 @@ CHANGES WITH 227:
Andersen, Tom Gundersen, Tom Lyon, Viktar Vauchkevich,
Zbigniew Jędrzejewski-Szmek, Марко М. Костић
- -- Berlin, 2015-10-07
+ — Berlin, 2015-10-07
CHANGES WITH 226:
@@ -811,7 +836,7 @@ CHANGES WITH 226:
Hack, Susant Sahani, Sylvain Pasche, Thomas Hindoe Paaboel
Andersen, Tom Gundersen, Torstein Husebø
- -- Berlin, 2015-09-08
+ — Berlin, 2015-09-08
CHANGES WITH 225:
@@ -884,7 +909,7 @@ CHANGES WITH 225:
Paaboel Andersen, Thomas Meyer, Tom Gundersen, Vincent Batts,
WaLyong Cho, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2015-08-27
+ — Berlin, 2015-08-27
CHANGES WITH 224:
@@ -899,7 +924,7 @@ CHANGES WITH 224:
Herrmann, Herman Fries, Johannes Nixdorf, Kay Sievers, Lennart
Poettering, Peter Hutterer, Susant Sahani, Tom Gundersen
- -- Berlin, 2015-07-31
+ — Berlin, 2015-07-31
CHANGES WITH 223:
@@ -964,7 +989,7 @@ CHANGES WITH 223:
Gundersen, Torstein Husebø, Umut Tezduyar Lindskog, Vito Caputo,
Vivenzio Pagliari, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2015-07-29
+ — Berlin, 2015-07-29
CHANGES WITH 222:
@@ -1004,7 +1029,7 @@ CHANGES WITH 222:
Susant Sahani, Thomas Hindoe Paaboel Andersen, Tom Gundersen, Torstein
Husebø, Vedran Miletić, WaLyong Cho, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2015-07-07
+ — Berlin, 2015-07-07
CHANGES WITH 221:
@@ -1082,7 +1107,7 @@ CHANGES WITH 221:
Husebø, Umut Tezduyar Lindskog, Viktar Vauchkevich, Werner
Fink, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2015-06-19
+ — Berlin, 2015-06-19
CHANGES WITH 220:
@@ -1311,7 +1336,7 @@ CHANGES WITH 220:
Gundersen, Torstein Husebø, Umut Tezduyar Lindskog, Will
Woods, Zachary Cook, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2015-05-22
+ — Berlin, 2015-05-22
CHANGES WITH 219:
@@ -1635,7 +1660,7 @@ CHANGES WITH 219:
Lindskog, Veres Lajos, Vincent Batts, WaLyong Cho, Wieland
Hoffmann, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2015-02-16
+ — Berlin, 2015-02-16
CHANGES WITH 218:
@@ -1837,7 +1862,7 @@ CHANGES WITH 218:
Torstein Husebø, Umut Tezduyar Lindskog, Vicente Olivert
Riera, WaLyong Cho, Wesley Dawson, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2014-12-10
+ — Berlin, 2014-12-10
CHANGES WITH 217:
@@ -2049,7 +2074,7 @@ CHANGES WITH 217:
Husebø, Umut Tezduyar Lindskog, WaLyong Cho, Zbigniew
Jędrzejewski-Szmek
- -- Berlin, 2014-10-28
+ — Berlin, 2014-10-28
CHANGES WITH 216:
@@ -2251,7 +2276,7 @@ CHANGES WITH 216:
Tobias Geerinckx-Rice, Tomasz Torcz, Tom Gundersen, Umut
Tezduyar Lindskog, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2014-08-19
+ — Berlin, 2014-08-19
CHANGES WITH 215:
@@ -2485,7 +2510,7 @@ CHANGES WITH 215:
Paaboel Andersen, Tom Gundersen, Tom Hirst, Umut Tezduyar
Lindskog, Uoti Urpala, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2014-07-03
+ — Berlin, 2014-07-03
CHANGES WITH 214:
@@ -2679,7 +2704,7 @@ CHANGES WITH 214:
Andersen, Tom Gundersen, Umut Tezduyar Lindskog, Zbigniew
Jędrzejewski-Szmek
- -- Berlin, 2014-06-11
+ — Berlin, 2014-06-11
CHANGES WITH 213:
@@ -2811,7 +2836,7 @@ CHANGES WITH 213:
Lindskog, WaLyong Cho, Will Woods, Zbigniew
Jędrzejewski-Szmek
- -- Beijing, 2014-05-28
+ — Beijing, 2014-05-28
CHANGES WITH 212:
@@ -2960,7 +2985,7 @@ CHANGES WITH 212:
Umut Tezduyar Lindskog, Wieland Hoffmann, Zbigniew
Jędrzejewski-Szmek
- -- Berlin, 2014-03-25
+ — Berlin, 2014-03-25
CHANGES WITH 211:
@@ -3084,7 +3109,7 @@ CHANGES WITH 211:
Gundersen, Umut Tezduyar Lindskog, Uoti Urpala, Zachary Cook,
Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2014-03-12
+ — Berlin, 2014-03-12
CHANGES WITH 210:
@@ -3189,7 +3214,7 @@ CHANGES WITH 210:
Paaboel Andersen, Tom Gundersen, Umut Tezduyar Lindskog,
Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2014-02-24
+ — Berlin, 2014-02-24
CHANGES WITH 209:
@@ -3645,7 +3670,7 @@ CHANGES WITH 209:
Pavlín, Vincent Batts, WaLyong Cho, William Giokas, Yang
Zhiyong, Yin Kangkai, Yuxuan Shui, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2014-02-20
+ — Berlin, 2014-02-20
CHANGES WITH 208:
@@ -3732,7 +3757,7 @@ CHANGES WITH 208:
Michael Scherer, Michał Górny, Mike Gilbert, Patrick McCarty,
Sebastian Ott, Tom Gundersen, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2013-10-02
+ — Berlin, 2013-10-02
CHANGES WITH 207:
@@ -3832,7 +3857,7 @@ CHANGES WITH 207:
Paaboel Andersen, Tom Gundersen, Umut Tezduyar, WANG Chao,
William Giokas, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2013-09-13
+ — Berlin, 2013-09-13
CHANGES WITH 206:
@@ -3931,14 +3956,14 @@ CHANGES WITH 206:
Thomas H.P. Andersen, Tom Gundersen, Tomasz Torcz, William
Giokas, Zbigniew Jędrzejewski-Szmek
- -- Berlin, 2013-07-23
+ — Berlin, 2013-07-23
CHANGES WITH 205:
* Two new unit types have been introduced:
Scope units are very similar to service units, however, are
- created out of pre-existing processes -- instead of PID 1
+ created out of pre-existing processes — instead of PID 1
forking off the processes. By using scope units it is
possible for system services and applications to group their
own child processes (worker processes) in a powerful way
diff --git a/TODO b/TODO
index 4d8161cadf..ad72ba6076 100644
--- a/TODO
+++ b/TODO
@@ -33,11 +33,16 @@ Janitorial Clean-ups:
Features:
+* rework fopen_temporary() to make use of open_tmpfile_linkable() (problem: the
+ kernel doesn't support linkat() that replaces existing files, currently)
+
+* journalctl -f --no-tail fails for Lennart
+
+* check if DeviceAllow= should split first, resolve specifiers later
+
* transient units: don't bother with actually setting unit properties, we
reload the unit file anyway
-* https://github.com/systemd/systemd/pull/2886 is fucked
-
* make sure resolved can be restarted without losing pushed-in dns config
* fix https://github.com/systemd/systemd/pull/2890, this shouldn't be exported
@@ -384,7 +389,7 @@ Features:
* systemd-inhibit: make taking delay locks useful: support sending SIGINT or SIGTERM on PrepareForSleep()
-* remove any syslog support from log.c -- we probably cannot do this before split-off udev is gone for good
+* remove any syslog support from log.c — we probably cannot do this before split-off udev is gone for good
* shutdown logging: store to EFI var, and store to USB stick?
diff --git a/catalog/systemd.hr.catalog b/catalog/systemd.hr.catalog
new file mode 100644
index 0000000000..4a6c5ac1fc
--- /dev/null
+++ b/catalog/systemd.hr.catalog
@@ -0,0 +1,314 @@
+# This file is part of systemd.
+#
+# Copyright 2012 Lennart Poettering
+#
+# systemd 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.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+# Message catalog for systemd's own messages
+# Croatian translation
+
+# Format kataloga je dokumentiran na
+# http://www.freedesktop.org/wiki/Software/systemd/catalog
+
+# Za pojašnjenje zašto ovo radimo, posjetite https://xkcd.com/1024/
+
+-- f77379a8490b408bbe5f6940505a777b
+Subject: journal je pokrenut
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Journal proces sustava se pokrenuo, otvorio je journal
+ datoteke za upis i spreman je za obradu zahtjeva.
+
+-- d93fb3c9c24d451a97cea615ce59c00b
+Subject: journal je zaustavljen
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Journal proces sustava je isključio i zatvorio sve trenutno
+aktivne journal datoteke.
+
+-- ec387f577b844b8fa948f33cad9a75e6
+Subject: Diskovni prostor koji koristi journal
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+@JOURNAL_NAME@ (@JOURNAL_PATH@) trenutno koristi @CURRENT_USE_PRETTY@.
+Najveća dopuštena upotreba je postavljena na @MAX_USE_PRETTY@.
+Ostavljam najmanje @DISK_KEEP_FREE_PRETTY@ slobodno (trenutno dostupno @DISK_AVAILABLE_PRETTY@ diskovnog prostora).
+Prisilno ograničenje upotrebe je @LIMIT_PRETTY@, od kojeg je @AVAILABLE_PRETTY@ još dostupno.
+
+Ograničenja kontroliraju koliko diskovnog prostora koristi journal mogu
+se podesiti sa SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=,
+RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= settings u
+/etc/systemd/journald.conf. Pogledajte journald.conf(5) za više pojedinosti.
+
+-- a596d6fe7bfa4994828e72309e95d61e
+Subject: Poruka iz usluge je potisnuta
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:journald.conf(5)
+
+Usluga je prijavila previše poruka u određenom vremenskom razdoblju. Poruke
+iz usluge su odbačene.
+
+Zapamtite da samo poruke iz usluge u upitu su
+odbačene, ostale poruke usluga nisu zahvaćene.
+
+Ograničenja koja kontroliraju kada je poruka odbačena mogu se podesiti
+sa RateLimitInterval= i RateLimitBurst= u
+/etc/systemd/journald.conf. Pogledajte journald.conf(5) za više pojedinosti.
+
+-- e9bf28e6e834481bb6f48f548ad13606
+Subject: Journal poruka je propuštena
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Kernel poruka je izgubljena zato jer ih journal sustav nije mogao
+dovoljno brzo obraditi.
+
+-- fc2e22bc6ee647b6b90729ab34a250b1
+Subject: Proces @COREDUMP_PID@ (@COREDUMP_COMM@) je izbacio jezgru
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:core(5)
+
+Proces @COREDUMP_PID@ (@COREDUMP_COMM@) se srušio i izbacio jezgru.
+
+Rušenje programa je uobičajeno uzrokovano greškom u programiranju i
+trebalo bi se prijaviti razvijatelju kao greška.
+
+-- 8d45620c1a4348dbb17410da57c60c66
+Subject: Nova sesija @SESSION_ID@ je stvorena za korisnika @USER_ID@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Nova sesija sa ID @SESSION_ID@ je stvorena za korisnika @USER_ID@.
+
+Glavni proces sesije je @LEADER@.
+
+-- 3354939424b4456d9802ca8333ed424a
+Subject: Sesija @SESSION_ID@ je prekinuta
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Sesija sa ID @SESSION_ID@ je prekinuta.
+
+-- fcbefc5da23d428093f97c82a9290f7b
+Subject: Novo sjedište @SEAT_ID@ je sada dostupno
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Novo sjedište @SEAT_ID@ je podešeno i sada je dostupno.
+
+-- e7852bfe46784ed0accde04bc864c2d5
+Subject: Sjedište @SEAT_ID@ je sada uklonjeno
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Sjedište @SEAT_ID@ je uklonjeno i više nije dostupno.
+
+-- c7a787079b354eaaa9e77b371893cd27
+Subject: Vrijeme promjene
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Sat sustava je promijenjen na @REALTIME@ microsekundi nakon 1. Siječnja, 1970 godine.
+
+-- 45f82f4aef7a4bbf942ce861d1f20990
+Subject: Vremenska zona je promijenjena u @TIMEZONE@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Vremenska zona je promijenjena u @TIMEZONE@.
+
+-- b07a249cd024414a82dd00cd181378ff
+Subject: Pokretanje sustava je sada završeno
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Sve usluge sustava koje su zadane za pokretanje pri pokretanju sustava
+su uspješno pokrenute. Zapamtite da ovo ne znači da sada računalo
+miruje zato jer se neke usluge još uvijek mogu pokretati.
+
+Pokretanje kernela zahtijeva @KERNEL_USEC@ mikrosekundi.
+
+Pokretanje početnog RAM diska zahtijeva @INITRD_USEC@ mikrosekundi.
+
+Pokretanje prostora korisnika zahtijeva @USERSPACE_USEC@ mikrosekundi.
+
+-- 6bbd95ee977941e497c48be27c254128
+Subject: Pokrenuto je stanje spavanja @SLEEP@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Sustav je sada pokrenuo stanje spavanja @SLEEP@
+
+-- 8811e6df2a8e40f58a94cea26f8ebf14
+Subject: Završeno je stanje spavanja @SLEEP@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Sustav je sada završio stanje spavanja @SLEEP@
+
+-- 98268866d1d54a499c4e98921d93bc40
+Subject: Pokrenuto je isključivanje sustava
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Pokrenuto je isključivanje sustava. Isključivanje je sada pokrenuto,
+sve usluge sustava su prekinute i svi datotečni sustavi su odmontirani.
+
+-- 7d4958e842da4a758f6c1cdc7b36dcc5
+Subject: Jedinica @UNIT@ je započela pokretanje
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ je započela pokretanje.
+
+-- 39f53479d3a045ac8e11786248231fbf
+Subject: Jedinica @UNIT@ je završila pokretanje
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ je završila pokretanje.
+
+Rezultat pokretanja je @RESULT@.
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: Jedinica @UNIT@ je započela isključivanje
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ je započela isključivanje.
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: Jedinica @UNIT@ je završila isključivanje
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ je završila isključivanje.
+
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: Jedinica @UNIT@ nije uspjela
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ nije uspjela.
+
+Rezultat je @RESULT@.
+
+-- d34d037fff1847e6ae669a370e694725
+Subject: Jedinica @UNIT@ je započela ponovno učitavati podešavanja
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ je započela ponovno učitavati podešavanja
+
+-- 7b05ebc668384222baa8881179cfda54
+Subject: Jedinica @UNIT@ je završila ponovno učitavati podešavanja
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedinica @UNIT@ je završila ponovno učitavati podešavanja
+
+Rezultat je @RESULT@.
+
+-- 641257651c1b4ec9a8624d7a40a9e1e7
+Subject: Proces @EXECUTABLE@ se ne može pokrenuti
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Proces @EXECUTABLE@ se ne može pokrenuti i nije uspio.
+
+Broj greške vraćen ovim procesom je @ERRNO@.
+
+-- 0027229ca0644181a76c4e92458afa2e
+Subject: Jedna ili više poruka se ne mogu proslijediti u dnevnik sustava
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jedna ili više poruka se ne mogu proslijediti u dnevnik sustava, usluge
+su pokrenute istovremeno s journalom. Ovo uobičajeno označava da
+implementacija dnevnika sustava ne može slijediti brzinu
+zahtjeva poruka.
+
+-- 1dee0369c7fc4736b7099b38ecb46ee7
+Subject: Točka montiranja nije prazna
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Direktorij @WHERE@ je određen za točku montiranja (drugi redak u
+/etc/fstab ili Where= redak u datoteci systemd jedinice) i nije prazan.
+To ne utječe na montiranje, ali postojeće datoteke u ovom direktoriju
+postaju nedostupne. Kako bi vidjeli datoteke preko kojih je montirano,
+ručno montirajte osnovni datotečni sustav na drugu lokaciju.
+
+-- 24d8d4452573402496068381a6312df2
+Subject: Virtualni stroj ili spremnik su pokrenuti
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Virtualni stroj @NAME@ sa vodećim @LEADER@ PID-om je
+pokrenut i spreman je za korištenje.
+
+-- 58432bd3bace477cb514b56381b8a758
+Subject: Virtualni stroj ili spremnik su isključeni
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Virtualni stroj @NAME@ sa vodećim PID-om @LEADER@ je
+isključen.
+
+-- 36db2dfa5a9045e1bd4af5f93e1cf057
+Subject: DNSSEC način je isključen, jer ga poslužitelj ne podržava
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+
+Usluga razrješavanja (systemd-resolved.service) je otkrila da
+podešeni DNS poslužitelj ne podržava DNSSEC, i DNSSEC, kao rezultat
+provjera je isključena.
+
+Ovaj događaj će zauzeti mjesto ako je DNSSEC=allow-downgrade podešen u
+resolved.conf i podešeni DNS poslužitelj je nekompatibilan s DNSSEC. Zapamtite
+da korištenje ovog načina dopušta povećanje DNSSEC napada, napadač bi mogao
+isključiti DNSSEC provjeru na sustavu umetanjem DNS odgovora u
+komunikacijski kanal što rezultira povećanjem napada poput ovog.
+
+Ovaj događaj bi mogao označavati da je DNS poslužitelj uistinu nekompatibilan s
+DNSSEC ili da je napadač uspješno izvršio takav napad.
+
+-- 1675d7f172174098b1108bf8c7dc8f5d
+Subject: DNSSEC provjera neuspješna
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+DNS zahtjev ili snimak resursa nije prošao DNSSEC provjeru. To uobičajeno
+označava da je komunikacijski kanal mijenjan.
+
+-- 4d4408cfd0d144859184d1e65d7c8a65
+Subject: DNSSEC pouzdano sidro je opozvano
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+A DNSSEC trust anchor has been revoked. A new trust anchor has to be
+configured, or the operating system needs to be updated, to provide an updated
+DNSSEC trust anchor.
diff --git a/catalog/systemd.pl.catalog b/catalog/systemd.pl.catalog
index 6b8a31d8c4..0d2e3d22cf 100644
--- a/catalog/systemd.pl.catalog
+++ b/catalog/systemd.pl.catalog
@@ -1,7 +1,7 @@
# This file is part of systemd.
#
# Copyright 2012 Lennart Poettering
-# Copyright 2014, 2015 Piotr Drąg
+# Copyright 2014, 2015, 2016 Piotr Drąg
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
@@ -40,6 +40,22 @@ Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Systemowy proces dziennika został wyłączony i zamknął wszystkie obecnie
aktywne pliki dziennika.
+-- ec387f577b844b8fa948f33cad9a75e6
+Subject: Miejsce na dysku używane przez dziennik
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+@JOURNAL_NAME@ (@JOURNAL_PATH@) obecnie używa @CURRENT_USE_PRETTY@.
+Maksymalnie może używać @MAX_USE_PRETTY@.
+Zostawianie co najmniej @DISK_KEEP_FREE_PRETTY@ wolnego (z obecnie dostępnego @DISK_AVAILABLE_PRETTY@ miejsca na dysku).
+Wymuszone ograniczenie użycia wynosi więc @LIMIT_PRETTY@, z czego @AVAILABLE_PRETTY@ jest nadal dostępne.
+
+Ograniczenia kontrolujące ilość miejsca na dysku używanego przez dziennik
+można konfigurować za pomocą ustawień SystemMaxUse=, SystemKeepFree=,
+SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize=
+w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej
+informacji.
+
-- a596d6fe7bfa4994828e72309e95d61e
Subject: Ograniczono komunikaty z usługi
Defined-By: systemd
@@ -72,7 +88,8 @@ Documentation: man:core(5)
Proces @COREDUMP_PID@ (@COREDUMP_COMM@) uległ awarii i zrzucił plik core.
-Zwykle wskazuje to na błąd programistyczny w danym programie i powinno zostać zgłoszone jego producentowi jako błąd.
+Zwykle wskazuje to na błąd programistyczny w danym programie i powinno zostać
+zgłoszone jego producentowi jako błąd.
-- 8d45620c1a4348dbb17410da57c60c66
Subject: Utworzono nową sesję @SESSION_ID@ dla użytkownika @USER_ID@
@@ -259,3 +276,40 @@ Defined-By: systemd
Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została wyłączona.
+
+-- 36db2dfa5a9045e1bd4af5f93e1cf057
+Subject: Wyłączono tryb DNSSEC, ponieważ serwer go nie obsługuje
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+
+Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer
+DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona.
+
+To zdarzenie będzie miało miejsce, jeśli skonfigurowano DNSSEC=allow-downgrade
+w pliku resolved.conf, a skonfigurowany serwer DNS jest niezgodny z DNSSEC.
+Proszę zauważyć, że używanie tego trybu umożliwia ataki wyłączające DNSSEC,
+ponieważ atakujący będzie mógł wyłączyć walidację DNSSEC na komputerze przez
+umieszczenie odpowiednich odpowiedzi DNS w kanale komunikacji.
+
+To zdarzenie może wskazywać, że serwer DNS jest faktycznie niezgodny z DNSSEC,
+albo że atakującemu udało się upozorować atak tego typu.
+
+-- 1675d7f172174098b1108bf8c7dc8f5d
+Subject: Walidacja DNSSEC się nie powiodła
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+Zapytanie DNS lub ustawiony wpis zasobu nie przeszedł walidacji DNSSEC.
+Zwykle wskazuje to, że ktoś manipulował używanym kanałem komunikacji.
+
+-- 4d4408cfd0d144859184d1e65d7c8a65
+Subject: Unieważniono kotwicę zaufania DNSSEC
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+Kotwica zaufania DNSSEC została unieważniona. Należy skonfigurować nową, albo
+system operacyjny musi zostać zaktualizowany, aby dostarczyć zaktualizowaną
+kotwicę zaufania DNSSEC.
diff --git a/configure.ac b/configure.ac
index 7b9e64a0f6..d4e8ab6664 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1014,6 +1014,16 @@ fi
AM_CONDITIONAL(ENABLE_LOGIND, [test "$have_logind" = "yes"])
AS_IF([test "$have_logind" = "yes"], [ AC_DEFINE(HAVE_LOGIND, [1], [Logind support available]) ])
+AC_ARG_WITH([kill-user-processes],
+ [AS_HELP_STRING([--without-kill-user-processes], [Set logind's KillUserProcesses=no by default])])
+AS_IF([test "$with_kill_user_processes" != "no"],
+ [kill_user_processes=true
+ KILL_USER_PROCESSES=yes],
+ [kill_user_processes=false
+ KILL_USER_PROCESSES=no])
+AC_DEFINE_UNQUOTED(KILL_USER_PROCESSES, [$kill_user_processes], [Default KillUserProcesses setting])
+AC_SUBST(KILL_USER_PROCESSES)
+
# ------------------------------------------------------------------------------
have_machined=no
AC_ARG_ENABLE(machined, AS_HELP_STRING([--disable-machined], [disable machine daemon]))
@@ -1554,106 +1564,107 @@ AC_OUTPUT
AC_MSG_RESULT([
$PACKAGE_NAME $VERSION
- libcryptsetup: ${have_libcryptsetup}
- PAM: ${have_pam}
- AUDIT: ${have_audit}
- IMA: ${have_ima}
- AppArmor: ${have_apparmor}
- SELinux: ${have_selinux}
- SECCOMP: ${have_seccomp}
- SMACK: ${have_smack}
- ZLIB: ${have_zlib}
- XZ: ${have_xz}
- LZ4: ${have_lz4}
- BZIP2: ${have_bzip2}
- ACL: ${have_acl}
- GCRYPT: ${have_gcrypt}
- QRENCODE: ${have_qrencode}
- MICROHTTPD: ${have_microhttpd}
- GNUTLS: ${have_gnutls}
- libcurl: ${have_libcurl}
- libidn: ${have_libidn}
- libiptc: ${have_libiptc}
- ELFUTILS: ${have_elfutils}
- binfmt: ${have_binfmt}
- vconsole: ${have_vconsole}
- quotacheck: ${have_quotacheck}
- tmpfiles: ${have_tmpfiles}
- sysusers: ${have_sysusers}
- firstboot: ${have_firstboot}
- randomseed: ${have_randomseed}
- backlight: ${have_backlight}
- rfkill: ${have_rfkill}
- logind: ${have_logind}
- machined: ${have_machined}
- importd: ${have_importd}
- hostnamed: ${have_hostnamed}
- timedated: ${have_timedated}
- timesyncd: ${have_timesyncd}
- Default NTP servers: ${NTP_SERVERS}
- time epoch: ${TIME_EPOCH}
- localed: ${have_localed}
- networkd: ${have_networkd}
- resolved: ${have_resolved}
- Default DNS servers: ${DNS_SERVERS}
- Default DNSSEC mode: ${DEFAULT_DNSSEC_MODE}
- coredump: ${have_coredump}
- polkit: ${have_polkit}
- efi: ${have_efi}
- gnuefi: ${have_gnuefi}
- efi arch: ${EFI_ARCH}
- EFI machine type: ${EFI_MACHINE_TYPE_NAME}
- EFI CC ${EFI_CC}
- EFI libdir: ${EFI_LIB_DIR}
- EFI ldsdir: ${EFI_LDS_DIR}
- EFI includedir: ${EFI_INC_DIR}
- kmod: ${have_kmod}
- xkbcommon: ${have_xkbcommon}
- blkid: ${have_blkid}
- libmount: ${have_libmount}
- dbus: ${have_dbus}
- nss-myhostname: ${have_myhostname}
- hwdb: ${enable_hwdb}
- tpm: ${have_tpm}
- kdbus: ${have_kdbus}
- Python: ${have_python}
- man pages: ${have_manpages}
- test coverage: ${have_coverage}
- Split /usr: ${enable_split_usr}
- SysV compatibility: ${SYSTEM_SYSV_COMPAT}
- utmp/wtmp support: ${have_utmp}
- ldconfig support: ${enable_ldconfig}
- hibernate support: ${enable_hibernate}
- extra debugging: ${enable_debug}
- tests: ${enable_tests}
-
- prefix: ${prefix}
- rootprefix: ${with_rootprefix}
- sysconf dir: ${sysconfdir}
- datarootdir: ${datarootdir}
- includedir: ${includedir}
- lib dir: ${libdir}
- rootlib dir: ${with_rootlibdir}
- SysV init scripts: ${SYSTEM_SYSVINIT_PATH}
- SysV rc?.d directories: ${SYSTEM_SYSVRCND_PATH}
- Build Python: ${PYTHON}
- PAM modules dir: ${with_pamlibdir}
- PAM configuration dir: ${with_pamconfdir}
- D-Bus policy dir: ${with_dbuspolicydir}
- D-Bus session dir: ${with_dbussessionservicedir}
- D-Bus system dir: ${with_dbussystemservicedir}
- Bash completions dir: ${with_bashcompletiondir}
- Zsh completions dir: ${with_zshcompletiondir}
- Extra start script: ${RC_LOCAL_SCRIPT_PATH_START}
- Extra stop script: ${RC_LOCAL_SCRIPT_PATH_STOP}
- Wheel group: ${have_wheel_group}
- Debug shell: ${SUSHELL} @ ${DEBUGTTY}
- TTY GID: ${TTY_GID}
- Maximum System UID: ${SYSTEM_UID_MAX}
- Maximum System GID: ${SYSTEM_GID_MAX}
- Certificate root: ${CERTIFICATEROOT}
-
- CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
- CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
- LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
+ libcryptsetup: ${have_libcryptsetup}
+ PAM: ${have_pam}
+ AUDIT: ${have_audit}
+ IMA: ${have_ima}
+ AppArmor: ${have_apparmor}
+ SELinux: ${have_selinux}
+ SECCOMP: ${have_seccomp}
+ SMACK: ${have_smack}
+ ZLIB: ${have_zlib}
+ XZ: ${have_xz}
+ LZ4: ${have_lz4}
+ BZIP2: ${have_bzip2}
+ ACL: ${have_acl}
+ GCRYPT: ${have_gcrypt}
+ QRENCODE: ${have_qrencode}
+ MICROHTTPD: ${have_microhttpd}
+ GNUTLS: ${have_gnutls}
+ libcurl: ${have_libcurl}
+ libidn: ${have_libidn}
+ libiptc: ${have_libiptc}
+ ELFUTILS: ${have_elfutils}
+ binfmt: ${have_binfmt}
+ vconsole: ${have_vconsole}
+ quotacheck: ${have_quotacheck}
+ tmpfiles: ${have_tmpfiles}
+ sysusers: ${have_sysusers}
+ firstboot: ${have_firstboot}
+ randomseed: ${have_randomseed}
+ backlight: ${have_backlight}
+ rfkill: ${have_rfkill}
+ logind: ${have_logind}
+ Default KillUserProcesses setting: ${KILL_USER_PROCESSES}
+ machined: ${have_machined}
+ importd: ${have_importd}
+ hostnamed: ${have_hostnamed}
+ timedated: ${have_timedated}
+ timesyncd: ${have_timesyncd}
+ Default NTP servers: ${NTP_SERVERS}
+ time epoch: ${TIME_EPOCH}
+ localed: ${have_localed}
+ networkd: ${have_networkd}
+ resolved: ${have_resolved}
+ Default DNS servers: ${DNS_SERVERS}
+ Default DNSSEC mode: ${DEFAULT_DNSSEC_MODE}
+ coredump: ${have_coredump}
+ polkit: ${have_polkit}
+ efi: ${have_efi}
+ gnuefi: ${have_gnuefi}
+ efi arch: ${EFI_ARCH}
+ EFI machine type: ${EFI_MACHINE_TYPE_NAME}
+ EFI CC ${EFI_CC}
+ EFI libdir: ${EFI_LIB_DIR}
+ EFI ldsdir: ${EFI_LDS_DIR}
+ EFI includedir: ${EFI_INC_DIR}
+ kmod: ${have_kmod}
+ xkbcommon: ${have_xkbcommon}
+ blkid: ${have_blkid}
+ libmount: ${have_libmount}
+ dbus: ${have_dbus}
+ nss-myhostname: ${have_myhostname}
+ hwdb: ${enable_hwdb}
+ tpm: ${have_tpm}
+ kdbus: ${have_kdbus}
+ Python: ${have_python}
+ man pages: ${have_manpages}
+ test coverage: ${have_coverage}
+ Split /usr: ${enable_split_usr}
+ SysV compatibility: ${SYSTEM_SYSV_COMPAT}
+ utmp/wtmp support: ${have_utmp}
+ ldconfig support: ${enable_ldconfig}
+ hibernate support: ${enable_hibernate}
+ extra debugging: ${enable_debug}
+ tests: ${enable_tests}
+
+ prefix: ${prefix}
+ rootprefix: ${with_rootprefix}
+ sysconf dir: ${sysconfdir}
+ datarootdir: ${datarootdir}
+ includedir: ${includedir}
+ lib dir: ${libdir}
+ rootlib dir: ${with_rootlibdir}
+ SysV init scripts: ${SYSTEM_SYSVINIT_PATH}
+ SysV rc?.d directories: ${SYSTEM_SYSVRCND_PATH}
+ Build Python: ${PYTHON}
+ PAM modules dir: ${with_pamlibdir}
+ PAM configuration dir: ${with_pamconfdir}
+ D-Bus policy dir: ${with_dbuspolicydir}
+ D-Bus session dir: ${with_dbussessionservicedir}
+ D-Bus system dir: ${with_dbussystemservicedir}
+ Bash completions dir: ${with_bashcompletiondir}
+ Zsh completions dir: ${with_zshcompletiondir}
+ Extra start script: ${RC_LOCAL_SCRIPT_PATH_START}
+ Extra stop script: ${RC_LOCAL_SCRIPT_PATH_STOP}
+ Wheel group: ${have_wheel_group}
+ Debug shell: ${SUSHELL} @ ${DEBUGTTY}
+ TTY GID: ${TTY_GID}
+ Maximum System UID: ${SYSTEM_UID_MAX}
+ Maximum System GID: ${SYSTEM_GID_MAX}
+ Certificate root: ${CERTIFICATEROOT}
+
+ CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
+ CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
+ LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
])
diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb
index ffd06f040c..48665ca744 100644
--- a/hwdb/60-keyboard.hwdb
+++ b/hwdb/60-keyboard.hwdb
@@ -495,6 +495,11 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:pvr*
# HP ZBook
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:pvr*
KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute
+# HP Folio 1040g2
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBookFolio1040G2:pvr*
+ KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute
+ KEYBOARD_KEY_d8=!f23 # touchpad off
+ KEYBOARD_KEY_d9=!f22 # touchpad on
# HP ProBook 6555b
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard:pnHPProBook6555b:*
diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb
index bea0464a11..a5b39dc41e 100644
--- a/hwdb/70-mouse.hwdb
+++ b/hwdb/70-mouse.hwdb
@@ -475,3 +475,11 @@ mouse:usb:v093ap2521:name:USB OPTICAL MOUSE:
# SteelSeries Sensei Raw
mouse:usb:v1038p1369:name:SteelSeries Sensei Raw Gaming Mouse:
MOUSE_DPI=1000@1022
+
+##########################################
+# Trust
+##########################################
+
+# Trust illuminated mouse gxt 152
+mouse:usb:v145fp01ac:name:HID-compliant Mouse Trust Gaming Mouse:
+ MOUSE_DPI=*800@528 1200@537 1600@536 2400@521
diff --git a/man/daemon.xml b/man/daemon.xml
index b6125cb5c7..485c66225e 100644
--- a/man/daemon.xml
+++ b/man/daemon.xml
@@ -180,14 +180,12 @@
functionality of the init system, it is recommended not to
execute them when run as new-style service.</para>
- <para>Note that new-style init systems guarantee execution of
- daemon processes in a clean process context: it is guaranteed
- that the environment block is sanitized, that the signal
- handlers and mask is reset and that no left-over file
- descriptors are passed. Daemons will be executed in their own
- session, with standard input/output/error connected to
- <filename>/dev/null</filename> unless otherwise configured. The
- umask is reset.
+ <para>Note that new-style init systems guarantee execution of daemon processes in a clean process context: it is
+ guaranteed that the environment block is sanitized, that the signal handlers and mask is reset and that no
+ left-over file descriptors are passed. Daemons will be executed in their own session, with standard input
+ connected to <filename>/dev/null</filename> and standard output/error connected to the
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ logging service, unless otherwise configured. The umask is reset.
</para>
<para>It is recommended for new-style daemons to implement the
@@ -234,7 +232,7 @@
bus-activatable by supplying a D-Bus service activation
configuration file. This has multiple advantages: your daemon
may be started lazily on-demand; it may be started in parallel
- to other daemons requiring it -- which maximizes
+ to other daemons requiring it — which maximizes
parallelization and boot-up speed; your daemon can be
restarted on failure without losing any bus requests, as the
bus queues requests for activatable services. See below for
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 7a634879cc..3efe6ef62a 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -272,6 +272,16 @@
<varlistentry>
<term>
+ <option>short-unix</option>
+ </term>
+ <listitem>
+ <para>is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock
+ timestamps ("UNIX time"). The time is shown with microsecond accuracy.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
<option>verbose</option>
</term>
<listitem>
@@ -350,6 +360,13 @@
</varlistentry>
<varlistentry>
+ <term><option>--no-hostname</option></term>
+
+ <listitem><para>Don't show the hostname field of log messages originating from the local host. This switch only
+ has an effect on the <option>short</option> family of output modes (see above).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-x</option></term>
<term><option>--catalog</option></term>
diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml
index 42d5e006bb..9c04849f66 100644
--- a/man/kernel-command-line.xml
+++ b/man/kernel-command-line.xml
@@ -322,6 +322,15 @@
</varlistentry>
<varlistentry>
+ <term><varname>systemd.default_timeout_start_sec=</varname></term>
+
+ <listitem>
+ <para>Overwrites the default start job timeout <varname>DefaultTimeoutStartSec=</varname> at boot. For details,
+ see <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>modules-load=</varname></term>
<term><varname>rd.modules-load=</varname></term>
diff --git a/man/loginctl.xml b/man/loginctl.xml
index 7f7252a5d9..fb51740503 100644
--- a/man/loginctl.xml
+++ b/man/loginctl.xml
@@ -312,7 +312,10 @@
This allows users who are not logged in to run long-running
services. Takes one or more user names or numeric UIDs as
argument. If no argument is specified, enables/disables
- lingering for the user of the session of the caller.
+ lingering for the user of the session of the caller.</para>
+
+ <para>See also <varname>KillUserProcesses=</varname> setting in
+ <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
@@ -410,6 +413,37 @@
otherwise.</para>
</refsect1>
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Querying user status</title>
+
+ <programlisting>$ loginctl user-status
+fatima (1005)
+ Since: Sat 2016-04-09 14:23:31 EDT; 54min ago
+ State: active
+ Sessions: 5 *3
+ Unit: user-1005.slice
+ ├─user@1005.service
+ ...
+ ├─session-3.scope
+ ...
+ └─session-5.scope
+ ├─3473 login -- fatima
+ └─3515 -zsh
+
+Apr 09 14:40:30 laptop login[2325]: pam_unix(login:session):
+ session opened for user fatima by LOGIN(uid=0)
+Apr 09 14:40:30 laptop login[2325]: LOGIN ON tty3 BY fatima
+</programlisting>
+
+ <para>There are two sessions, 3 and 5. Session 3 is a graphical session,
+ marked with a star. The tree of processing including the two corresponding
+ scope units and the user manager unit are shown.</para>
+ </example>
+ </refsect1>
+
<xi:include href="less-variables.xml" />
<refsect1>
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
index 597759e33a..6ba35414be 100644
--- a/man/logind.conf.xml
+++ b/man/logind.conf.xml
@@ -119,30 +119,46 @@
<varlistentry>
<term><varname>KillUserProcesses=</varname></term>
- <listitem><para>Takes a boolean argument. Configures whether
- the processes of a user should be killed when the user
- completely logs out (i.e. after the user's last session
- ended). Defaults to <literal>no</literal>.</para>
-
- <para>Note that setting <varname>KillUserProcesses=1</varname>
+ <listitem><para>Takes a boolean argument. Configures whether the processes of a
+ user should be killed when the user logs out. If true, the scope unit
+ corresponding to the session and all processes inside that scope will be
+ terminated. If false, the scope is "abandoned", see
+ <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ and processes are not killed. Defaults to <literal>yes</literal>,
+ but see the options <varname>KillOnlyUsers=</varname> and
+ <varname>KillExcludeUsers=</varname> below.</para>
+
+ <para>In addition to session processes, user process may run under the user
+ manager unit <filename>user@.service</filename>. Depending on the linger
+ settings, this may allow users to run processes independent of their login
+ sessions. See the description of <command>enable-linger</command> in
+ <citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ </para>
+
+ <para>Note that setting <varname>KillUserProcesses=yes</varname>
will break tools like
- <citerefentry project='die-net'><refentrytitle>screen</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
+ <citerefentry project='die-net'><refentrytitle>screen</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ and
+ <citerefentry project='die-net'><refentrytitle>tmux</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ unless they are moved out of the session scope. See example in
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ </para></listitem>
</varlistentry>
<varlistentry>
<term><varname>KillOnlyUsers=</varname></term>
<term><varname>KillExcludeUsers=</varname></term>
- <listitem><para>These settings take space-separated lists of
- usernames that influence the effect of
- <varname>KillUserProcesses=</varname>. If not empty, only
- processes of users listed in <varname>KillOnlyUsers=</varname>
- will be killed when they log out entirely. Processes of users
- listed in <varname>KillExcludeUsers=</varname> are excluded
- from being killed. <varname>KillExcludeUsers=</varname>
- defaults to <literal>root</literal> and takes precedence over
- <varname>KillOnlyUsers=</varname>, which defaults to the empty
- list.</para></listitem>
+ <listitem><para>These settings take space-separated lists of usernames that override
+ the <varname>KillUserProcesses=</varname> setting. A user name may be added to
+ <varname>KillExcludeUsers=</varname> to exclude the processes in the session scopes of
+ that user from being killed even if <varname>KillUserProcesses=yes</varname> is set. If
+ <varname>KillExcludeUsers=</varname> is not set, the <literal>root</literal> user is
+ excluded by default. <varname>KillExcludeUsers=</varname> may be set to an empty value
+ to override this default. If a user is not excluded, <varname>KillOnlyUsers=</varname>
+ is checked next. If this setting is specified, only the session scopes of those users
+ will be killed. Otherwise, users are subject to the
+ <varname>KillUserProcesses=yes</varname> setting.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/machinectl.xml b/man/machinectl.xml
index a77d2419af..43a3b98840 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -195,16 +195,14 @@
</varlistentry>
<varlistentry>
- <term><option>--setenv=</option></term>
-
- <listitem><para>When used with the <command>shell</command>
- command, sets an environment variable to pass to the executed
- shell. Takes a pair of environment variable name and value,
- separated by <literal>=</literal> as argument. This switch
- may be used multiple times to set multiple environment
- variables. Note that this switch is not supported for the
- <command>login</command> command (see
- below).</para></listitem>
+ <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
+ <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
+
+ <listitem><para>When used with the <command>shell</command> command, sets an environment
+ variable to pass to the executed shell. Takes an environment variable name and value,
+ separated by <literal>=</literal>. This switch may be used multiple times to set multiple
+ environment variables. Note that this switch is not supported for the
+ <command>login</command> command (see below).</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/sd_event_source_set_priority.xml b/man/sd_event_source_set_priority.xml
index 9234f4233e..8c9b39fe5e 100644
--- a/man/sd_event_source_set_priority.xml
+++ b/man/sd_event_source_set_priority.xml
@@ -97,7 +97,7 @@
<constant>SD_EVENT_PRIORITY_IDLE</constant> (100) may be used to
indicate event sources that shall be dispatched early, normally or
late. It is recommended to specify priorities based on these
- definitions, and relative to them -- however, the full 64bit
+ definitions, and relative to them — however, the full 64bit
signed integer range is available for ordering event
sources.</para>
diff --git a/man/sd_journal_add_match.xml b/man/sd_journal_add_match.xml
index 3b27444f8d..98415d53fd 100644
--- a/man/sd_journal_add_match.xml
+++ b/man/sd_journal_add_match.xml
@@ -88,11 +88,19 @@
<citerefentry><refentrytitle>sd_journal_next</refentrytitle><manvolnum>3</manvolnum></citerefentry>
and
<citerefentry><refentrytitle>sd_journal_get_data</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
- Matches are of the form <literal>FIELD=value</literal>, where the
- field part is a short uppercase string consisting only of 0–9, A–Z
- and the underscore. It may not begin with two underscores or be
- the empty string. The value part may be any value, including
- binary. If a match is applied, only entries with this field set
+ Parameter <parameter>data</parameter> must be of the form
+ <literal><replaceable>FIELD</replaceable>=<replaceable>value</replaceable></literal>,
+ where the <replaceable>FIELD</replaceable> part is a short uppercase string consisting only
+ of 0–9, A–Z and the underscore; it may not begin with two underscores or be the empty
+ string. The <replaceable>value</replaceable> part may be anything, including binary. Parameter
+ <parameter>size</parameter> specifies the number of bytes in <parameter>data</parameter>
+ (i.e. the length of <replaceable>FIELD</replaceable>, plus one, plus the length of
+ <replaceable>value</replaceable>). Parameter <parameter>size</parameter> may also be
+ specified as <constant>0</constant>, in which case <parameter>data</parameter>
+ must be a <constant>NUL</constant>-terminated string, and the bytes before the terminating
+ zero are used as the match.</para>
+
+ <para>If a match is applied, only entries with this field set
will be iterated. Multiple matches may be active at the same time:
If they apply to different fields, only entries with both fields
set like this will be iterated. If they apply to the same fields,
diff --git a/man/sd_journal_get_data.xml b/man/sd_journal_get_data.xml
index 1f25d068d7..908ee7db16 100644
--- a/man/sd_journal_get_data.xml
+++ b/man/sd_journal_get_data.xml
@@ -148,7 +148,7 @@
<function>sd_journal_enumerate_unique()</function>. This threshold
is a hint only: it indicates that the client program is interested
only in the initial parts of the data fields, up to the threshold
- in size -- but the library might still return larger data objects.
+ in size — but the library might still return larger data objects.
That means applications should not rely exclusively on this
setting to limit the size of the data fields returned, but need to
apply a explicit size limit on the returned data as well. This
diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml
index fef453f8dc..153af2387f 100644
--- a/man/sd_journal_open.xml
+++ b/man/sd_journal_open.xml
@@ -45,14 +45,16 @@
<refnamediv>
<refname>sd_journal_open</refname>
<refname>sd_journal_open_directory</refname>
+ <refname>sd_journal_open_directory_fd</refname>
<refname>sd_journal_open_files</refname>
- <refname>sd_journal_open_container</refname>
+ <refname>sd_journal_open_files_fd</refname>
<refname>sd_journal_close</refname>
<refname>sd_journal</refname>
<refname>SD_JOURNAL_LOCAL_ONLY</refname>
<refname>SD_JOURNAL_RUNTIME_ONLY</refname>
<refname>SD_JOURNAL_SYSTEM</refname>
<refname>SD_JOURNAL_CURRENT_USER</refname>
+ <refname>SD_JOURNAL_OS_ROOT</refname>
<refpurpose>Open the system journal for reading</refpurpose>
</refnamediv>
@@ -74,6 +76,13 @@
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_journal_open_directory_fd</function></funcdef>
+ <paramdef>sd_journal **<parameter>ret</parameter></paramdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_journal_open_files</function></funcdef>
<paramdef>sd_journal **<parameter>ret</parameter></paramdef>
<paramdef>const char **<parameter>paths</parameter></paramdef>
@@ -81,9 +90,10 @@
</funcprototype>
<funcprototype>
- <funcdef>int <function>sd_journal_open_container</function></funcdef>
+ <funcdef>int <function>sd_journal_open_files_fd</function></funcdef>
<paramdef>sd_journal **<parameter>ret</parameter></paramdef>
- <paramdef>const char *<parameter>machine</parameter></paramdef>
+ <paramdef>int <parameter>fds[]</parameter></paramdef>
+ <paramdef>unsigned <parameter>n_fds</parameter></paramdef>
<paramdef>int <parameter>flags</parameter></paramdef>
</funcprototype>
@@ -117,29 +127,28 @@
<constant>SD_JOURNAL_CURRENT_USER</constant> are specified, all
journal file types will be opened.</para>
- <para><function>sd_journal_open_directory()</function> is similar
- to <function>sd_journal_open()</function> but takes an absolute
- directory path as argument. All journal files in this directory
- will be opened and interleaved automatically. This call also takes
- a flags argument, but it must be passed as 0 as no flags are
- currently understood for this call.</para>
-
- <para><function>sd_journal_open_files()</function> is similar to
- <function>sd_journal_open()</function> but takes a
- <constant>NULL</constant>-terminated list of file paths to open.
- All files will be opened and interleaved automatically. This call
- also takes a flags argument, but it must be passed as 0 as no
- flags are currently understood for this call. Please note that in
- the case of a live journal, this function is only useful for
- debugging, because individual journal files can be rotated at any
- moment, and the opening of specific files is inherently
- racy.</para>
-
- <para><function>sd_journal_open_container()</function> is similar
- to <function>sd_journal_open()</function> but opens the journal
- files of a running OS container. The specified machine name refers
- to a container that is registered with
- <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+ <para><function>sd_journal_open_directory()</function> is similar to <function>sd_journal_open()</function> but
+ takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved
+ automatically. This call also takes a flags argument. The only flags parameter accepted by this call is
+ <constant>SD_JOURNAL_OS_ROOT</constant>. If specified, the journal files are searched below the usual
+ <filename>/var/log/journal</filename> and <filename>/run/log/journal</filename> relative to the specified path,
+ instead of directly beneath it.</para>
+
+ <para><function>sd_journal_open_directory_fd()</function> is similar to
+ <function>sd_journal_open_directory()</function>, but takes a file descriptor referencing a directory in the file
+ system instead of an absolute file system path.</para>
+
+ <para><function>sd_journal_open_files()</function> is similar to <function>sd_journal_open()</function> but takes a
+ <constant>NULL</constant>-terminated list of file paths to open. All files will be opened and interleaved
+ automatically. This call also takes a flags argument, but it must be passed as 0 as no flags are currently
+ understood for this call. Please note that in the case of a live journal, this function is only useful for
+ debugging, because individual journal files can be rotated at any moment, and the opening of specific files is
+ inherently racy.</para>
+
+ <para><function>sd_journal_open_files_fd()</function> is similar to <function>sd_journal_open_files()</function>
+ but takes an array of open file descriptors that must reference journal files, instead of an array of file system
+ paths. Pass the array of file descriptors as second argument, and the number of array entries in the third. The
+ flags parameter must be passed as 0.</para>
<para><varname>sd_journal</varname> objects cannot be used in the
child after a fork. Functions which take a journal object as an
@@ -205,26 +214,6 @@
</refsect1>
<refsect1>
- <title>History</title>
-
- <para><function>sd_journal_open()</function>,
- <function>sd_journal_close()</function>,
- <constant>SD_JOURNAL_LOCAL_ONLY</constant>,
- <constant>SD_JOURNAL_RUNTIME_ONLY</constant>,
- <constant>SD_JOURNAL_SYSTEM_ONLY</constant> were added in
- systemd-38.</para>
-
- <para><function>sd_journal_open_directory()</function> was added
- in systemd-187.</para>
-
- <para><constant>SD_JOURNAL_SYSTEM</constant>,
- <constant>SD_JOURNAL_CURRENT_USER</constant>, and
- <function>sd_journal_open_files()</function> were added in
- systemd-205. <constant>SD_JOURNAL_SYSTEM_ONLY</constant> was
- deprecated.</para>
- </refsect1>
-
- <refsect1>
<title>See Also</title>
<para>
diff --git a/man/sd_uid_get_state.xml b/man/sd_uid_get_state.xml
index 4cc7405dd6..130af761da 100644
--- a/man/sd_uid_get_state.xml
+++ b/man/sd_uid_get_state.xml
@@ -218,19 +218,6 @@
</refsect1>
<refsect1>
- <title>History</title>
-
- <para><function>sd_uid_get_state()</function>,
- <function>sd_uid_is_on_seat()</function>,
- <function>sd_uid_get_sessions()</function>, and
- <function>sd_uid_get_seats()</function> functions were added in
- systemd-31.</para>
-
- <para><function>sd_uid_get_display()</function> was added in
- systemd-213.</para>
- </refsect1>
-
- <refsect1>
<title>See Also</title>
<para>
diff --git a/man/sd_watchdog_enabled.xml b/man/sd_watchdog_enabled.xml
index 6e27528a71..3de9899453 100644
--- a/man/sd_watchdog_enabled.xml
+++ b/man/sd_watchdog_enabled.xml
@@ -155,18 +155,6 @@
</refsect1>
<refsect1>
- <title>History</title>
-
- <para>The watchdog functionality and the
- <varname>$WATCHDOG_USEC</varname> variable were added in
- systemd-41.</para>
-
- <para><function>sd_watchdog_enabled()</function> function was
- added in systemd-209. Since that version, the
- <varname>$WATCHDOG_PID</varname> variable is also set.</para>
- </refsect1>
-
- <refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 5f624243f7..991e9bafaf 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1084,22 +1084,22 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<term><command>preset <replaceable>NAME</replaceable>...</command></term>
<listitem>
- <para>Reset one or more unit files, as specified on the
- command line, to the defaults configured in the preset
- policy files. This has the same effect as
- <command>disable</command> or <command>enable</command>,
- depending how the unit is listed in the preset files.</para>
+ <para>Reset the enable/disable status one or more unit files, as specified on
+ the command line, to the defaults configured in the preset policy files. This
+ has the same effect as <command>disable</command> or
+ <command>enable</command>, depending how the unit is listed in the preset
+ files.</para>
- <para>Use <option>--preset-mode=</option> to control
- whether units shall be enabled and disabled, or only
- enabled, or only disabled.</para>
+ <para>Use <option>--preset-mode=</option> to control whether units shall be
+ enabled and disabled, or only enabled, or only disabled.</para>
+
+ <para>If the unit carries no install information, it will be silently ignored
+ by this command.</para>
- <para>For more information on the preset policy format,
- see
+ <para>For more information on the preset policy format, see
<citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- For more information on the concept of presets, please
- consult the <ulink
- url="http://freedesktop.org/wiki/Software/systemd/Preset">Preset</ulink>
+ For more information on the concept of presets, please consult the
+ <ulink url="http://freedesktop.org/wiki/Software/systemd/Preset">Preset</ulink>
document.</para>
</listitem>
</varlistentry>
diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml
index e84a15c554..2b6fb5a82f 100644
--- a/man/systemd-ask-password.xml
+++ b/man/systemd-ask-password.xml
@@ -67,7 +67,7 @@
processes.</para>
<para>The purpose of this tool is to query system-wide passwords
- -- that is passwords not attached to a specific user account.
+ — that is passwords not attached to a specific user account.
Examples include: unlocking encrypted hard disks when they are
plugged in or at boot, entering an SSL certificate passphrase for
web and VPN servers.</para>
diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml
index e32ac26850..9ed85c3950 100644
--- a/man/systemd-journal-gatewayd.service.xml
+++ b/man/systemd-journal-gatewayd.service.xml
@@ -262,7 +262,7 @@
<term><uri>boot</uri></term>
<listitem><para>Limit events to the current boot of the system
- (like <command>journalctl --this--boot</command>).</para></listitem>
+ (like <command>journalctl --this-boot</command>).</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 8a004af1a2..bd688a0ee1 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -387,38 +387,79 @@
<varlistentry>
<term><option>--private-users=</option></term>
- <listitem><para>Enables user namespacing. If enabled, the
- container will run with its own private set of Unix user and
- group ids (UIDs and GIDs). Takes none, one or two
- colon-separated parameters: the first parameter specifies the
- first host UID to assign to the container, the second
- parameter specifies the number of host UIDs to assign to the
- container. If the second parameter is omitted, 65536 UIDs are
- assigned. If the first parameter is also omitted (and hence
- no parameter passed at all), the first UID assigned to the
- container is read from the owner of the root directory of the
- container's directory tree. By default, no user namespacing is
- applied.</para>
-
- <para>Note that user namespacing currently requires OS trees
- that are prepared for the UID shift that is being applied:
- UIDs and GIDs used for file ownership or in file ACL entries
- must be shifted to the container UID base that is
- used during container runtime.</para>
-
- <para>It is recommended to assign at least 65536 UIDs to each
- container, so that the usable UID range in the container
- covers 16 bit. For best security, do not assign overlapping UID
- ranges to multiple containers. It is hence a good idea to use
- the upper 16 bit of the host 32-bit UIDs as container
- identifier, while the lower 16 bit encode the container UID
- used.</para>
-
- <para>When user namespaces are used, the GID range assigned to
- each container is always chosen identical to the UID
- range.</para></listitem>
+ <listitem><para>Controls user namespacing. If enabled, the container will run with its own private set of UNIX
+ user and group ids (UIDs and GIDs). This involves mapping the private UIDs/GIDs used in the container (starting
+ with the container's root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other
+ purposes (usually in the range beyond the host's UID/GID 65536). The parameter may be specified as follows:</para>
+
+ <orderedlist>
+ <listitem><para>The value <literal>no</literal> turns off user namespacing. This is the default.</para></listitem>
+
+ <listitem><para>The value <literal>yes</literal> (or the omission of a parameter) turns on user
+ namespacing. The UID/GID range to use is determined automatically from the file ownership of the root
+ directory of the container's directory tree. To use this option, make sure to prepare the directory tree in
+ advance, and ensure that all files and directories in it are owned by UIDs/GIDs in the range you'd like to
+ use. Also, make sure that used file ACLs exclusively reference UIDs/GIDs in the appropriate range. If this
+ mode is used the number of UIDs/GIDs assigned to the container for use is 65536, and the UID/GID of the
+ root directory must be a multiple of 65536.</para></listitem>
+
+ <listitem><para>The value "pick" turns on user namespacing. In this case the UID/GID range is automatically
+ chosen. As first step, the file owner of the root directory of the container's directory tree is read, and it
+ is checked that it is currently not used by the system otherwise (in particular, that no other container is
+ using it). If this check is successful, the UID/GID range determined this way is used, similar to the
+ behaviour if "yes" is specified. If the check is not successful (and thus the UID/GID range indicated in the
+ root directory's file owner is already used elsewhere) a new – currently unused – UID/GID range of 65536
+ UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and 1878982656, always starting at a
+ multiple of 65536. This setting implies <option>--private-users-chown</option> (see below), which has the
+ effect that the files and directories in the container's directory tree will be owned by the appropriate
+ users of the range picked. Using this option makes user namespace behaviour fully automatic. Note that the
+ first invocation of a previously unused container image might result in picking a new UID/GID range for it,
+ and thus in the (possibly expensive) file ownership adjustment operation. However, subsequent invocations of
+ the container will be cheap (unless of course the picked UID/GID range is assigned to a different use by
+ then).</para></listitem>
+
+ <listitem><para>Finally if one or two colon-separated numeric parameters are specified, user namespacing is
+ turned on, too. The first parameter specifies the first host UID/GID to assign to the container, the second
+ parameter specifies the number of host UIDs/GIDs to assign to the container. If the second parameter is
+ omitted, 65536 UIDs/GIDs are assigned.</para></listitem>
+ </orderedlist>
+
+ <para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the
+ container covers 16 bit. For best security, do not assign overlapping UID/GID ranges to multiple containers. It is
+ hence a good idea to use the upper 16 bit of the host 32-bit UIDs/GIDs as container identifier, while the lower 16
+ bit encode the container UID/GID used. This is in fact the behaviour enforced by the
+ <option>--private-users=pick</option> option.</para>
+
+ <para>When user namespaces are used, the GID range assigned to each container is always chosen identical to the
+ UID range.</para>
+
+ <para>In most cases, using <option>--private-users=pick</option> is the recommended option as it enhances
+ container security massively and operates fully automatically in most cases.</para>
+
+ <para>Note that the picked UID/GID range is not written to <filename>/etc/passwd</filename> or
+ <filename>/etc/group</filename>. In fact, the allocation of the range is not stored persistently anywhere,
+ except in the file ownership of the files and directories of the container.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-U</option></term>
+
+ <listitem><para>If the kernel supports the user namespaces feature, equivalent to
+ <option>--private-users=pick</option>, otherwise equivalent to
+ <option>--private-users=no</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--private-users-chown</option></term>
+
+ <listitem><para>If specified, all files and directories in the container's directory tree will adjusted so that
+ they are owned to the appropriate UIDs/GIDs selected for the container (see above). This operation is
+ potentially expensive, as it involves descending and iterating through the full directory tree of the
+ container. Besides actual file ownership, file ACLs are adjusted as well.</para>
+
+ <para>This option is implied if <option>--private-users=pick</option> is used. This option has no effect if
+ user namespacing is not used.</para></listitem>
+ </varlistentry>
<varlistentry>
<term><option>--private-network</option></term>
@@ -502,7 +543,7 @@
host interface name and container interface name. The latter
may be omitted in which case the container and host sides will
be assigned the same name. This switch is independent of
- <option>--network-veth</option>, and -- in contrast -- may be
+ <option>--network-veth</option>, and — in contrast — may be
used multiple times, and allows configuration of the network
interface names. Note that <option>--network-bridge=</option>
has no effect on interfaces created with
@@ -737,7 +778,8 @@
</varlistentry>
<varlistentry>
- <term><option>--setenv=</option></term>
+ <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
+ <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem><para>Specifies an environment variable assignment
to pass to the init process in the container, in the format
diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 473f83eac6..9c1a29218e 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -226,11 +226,11 @@
</varlistentry>
<varlistentry>
- <term><option>--setenv=</option></term>
+ <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
+ <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
- <listitem><para>Runs the service process with the specified
- environment variables set. Also see
- <varname>Environment=</varname> in
+ <listitem><para>Runs the service process with the specified environment variable set.
+ Also see <varname>Environment=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
@@ -341,10 +341,10 @@
<refsect1>
<title>Examples</title>
- <para>The following command will log the environment variables
- provided by systemd to services:</para>
+ <example>
+ <title>Logging environment variables provided by systemd to services</title>
- <programlisting># systemd-run env
+ <programlisting># systemd-run env
Running as unit: run-19945.service
# journalctl -u run-19945.service
Sep 08 07:37:21 bupkis systemd[1]: Starting /usr/bin/env...
@@ -352,19 +352,27 @@ Sep 08 07:37:21 bupkis systemd[1]: Started /usr/bin/env.
Sep 08 07:37:21 bupkis env[19948]: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Sep 08 07:37:21 bupkis env[19948]: LANG=en_US.UTF-8
Sep 08 07:37:21 bupkis env[19948]: BOOT_IMAGE=/vmlinuz-3.11.0-0.rc5.git6.2.fc20.x86_64</programlisting>
+ </example>
+
+ <example>
+ <title>Limiting resources available to a command</title>
+
+ <programlisting># systemd-run -p BlockIOWeight=10 updatedb</programlisting>
- <para>The following command invokes the
- <citerefentry project='man-pages'><refentrytitle>updatedb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- tool, but lowers the block I/O weight for it to 10. See
- <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for more information on the <varname>BlockIOWeight=</varname>
- property.</para>
+ <para>This command invokes the
+ <citerefentry project='man-pages'><refentrytitle>updatedb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ tool, but lowers the block I/O weight for it to 10. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for more information on the <varname>BlockIOWeight=</varname>
+ property.</para>
+ </example>
- <programlisting># systemd-run -p BlockIOWeight=10 updatedb</programlisting>
+ <example>
+ <title>Running commands at a specified time</title>
- <para>The following command will touch a file after 30 seconds.</para>
+ <para>The following command will touch a file after 30 seconds.</para>
- <programlisting># date; systemd-run --on-active=30 --timer-property=AccuracySec=100ms /bin/touch /tmp/foo
+ <programlisting># date; systemd-run --on-active=30 --timer-property=AccuracySec=100ms /bin/touch /tmp/foo
Mon Dec 8 20:44:24 KST 2014
Running as unit: run-71.timer
Will run service as unit: run-71.service
@@ -376,13 +384,60 @@ Dec 08 20:44:38 container systemd[1]: Started /bin/touch /tmp/foo.
-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
Dec 08 20:44:48 container systemd[1]: Starting /bin/touch /tmp/foo...
Dec 08 20:44:48 container systemd[1]: Started /bin/touch /tmp/foo.</programlisting>
-
- <para>The following command invokes <filename>/bin/bash</filename>
- as a service passing its standard input, output and error to
- the calling TTY.</para>
-
- <programlisting># systemd-run -t --send-sighup /bin/bash</programlisting>
-
+ </example>
+
+ <example>
+ <title>Allowing access to the tty</title>
+
+ <para>The following command invokes <filename>/bin/bash</filename> as a service
+ passing its standard input, output and error to the calling TTY.</para>
+
+ <programlisting># systemd-run -t --send-sighup /bin/bash</programlisting>
+ </example>
+
+ <example>
+ <title>Start <command>screen</command> as a user service</title>
+
+ <programlisting>$ systemd-run --scope --user screen
+Running scope as unit run-r14b0047ab6df45bfb45e7786cc839e76.scope.
+
+$ screen -ls
+There is a screen on:
+ 492..laptop (Detached)
+1 Socket in /var/run/screen/S-fatima.
+</programlisting>
+
+ <para>This starts the <command>screen</command> process as a child of the
+ <command>systemd --user</command> process that was started by
+ <filename>user@.service</filename>, in a scope unit. A
+ <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ unit is used instead of a
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ unit, because <command>screen</command> will exit when detaching from the terminal,
+ and a service unit would be terminated. Running <command>screen</command>
+ as a user unit has the advantage that it is not part of the session scope.
+ If <varname>KillUserProcesses=yes</varname> is configured in
+ <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ the default, the session scope will be terminated when the user logs
+ out of that session.</para>
+
+ <para>The <filename>user@.service</filename> is started automatically
+ when the user first logs in, and stays around as long as at least one
+ login session is open. After the user logs out of the last session,
+ <filename>user@.service</filename> and all services underneath it
+ are terminated. This behaviour is the default, when "lingering" is
+ not enabled for that user. Enabling lingering means that
+ <filename>user@.service</filename> is started automatically during
+ boot, even if the user is not logged in, and that the service is
+ not terminated when the user logs out.</para>
+
+ <para>Enabling lingering allows the user to run processes without being logged in,
+ for example to allow <command>screen</command> to persist after the user logs out,
+ even if the session scope is terminated. In the default configuration, users can
+ enable lingering for themselves:</para>
+
+ <programlisting>$ loginctl enable-linger</programlisting>
+ </example>
</refsect1>
<refsect1>
diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 447a7eaa17..c1aab51551 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -75,11 +75,11 @@
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
- <para>If invoked with no arguments, it applies all directives from
- all configuration files. If one or more absolute filenames are passed on
- the command line, only the directives in these files are applied.
- If only the basename of a configuration file is specified, all
- configuration directories as specified in
+ <para>If invoked with no arguments, it applies all directives from all configuration
+ files. If one or more absolute filenames are passed on the command line, only the
+ directives in these files are applied. If <literal>-</literal> is specified instead
+ of a filename, directives are read from standard input. If only the basename of a
+ configuration file is specified, all configuration directories as specified in
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
are searched for a matching file.</para>
</refsect1>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 4ed62dbada..fea42ebd31 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -94,11 +94,10 @@
required to access <filename>/tmp</filename> and
<filename>/var/tmp</filename>.</para>
- <para>Units whose output standard output or error output is
- connected to any other sink but <option>null</option>,
- <option>tty</option> and <option>socket</option> automatically
- acquire dependencies of type <varname>After=</varname> on
- <filename>journald.socket</filename>.</para>
+ <para>Units whose output standard output or error output is connected to <option>journal</option>,
+ <option>syslog</option> or <option>kmsg</option> (or their combinations with console output, see below)
+ automatically acquire dependencies of type <varname>After=</varname> on
+ <filename>systemd-journald.socket</filename>.</para>
</refsect1>
<refsect1>
@@ -470,6 +469,10 @@
similar to the same option of
<varname>StandardInput=</varname>.</para>
+ <para>If the standard output (or error output, see below) of a unit is connected with the journal, syslog or
+ the kernel log buffer the unit will implicitly gain a dependency of type <varname>After=</varname> on
+ <filename>systemd-journald.socket</filename> (also see the automatic dependencies section above).</para>
+
<para>This setting defaults to the value set with
<option>DefaultStandardOutput=</option> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index d7947836e9..9bf1b198ad 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -202,6 +202,8 @@
<para>The maximum transmission unit in bytes to set for the
device. The usual suffixes K, M, G, are supported and are
understood to the base of 1024.</para>
+ <para>Note that if IPv6 is enabled on the interface, and the MTU is chosen
+ below 1280 (the minimum MTU for IPv6) it will automatically be increased to this value.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -210,6 +212,15 @@
<para>Identity Association Identifier for the interface, a 32-bit unsigned integer.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <listitem>
+ <para>Note that an interface without any static IPv6 addresses configured, and neither
+ DHCPv6 nor IPv6LL enabled, shall be considered to have no IPv6 support. IPv6 will be
+ automatically disabled for that interface by writing "1" to
+ <filename>/proc/sys/net/ipv6/conf/<replaceable>ifname</replaceable>/disable_ipv6</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
index ce900a5db1..15360078ef 100644
--- a/man/systemd.nspawn.xml
+++ b/man/systemd.nspawn.xml
@@ -251,6 +251,14 @@
<option>--uuid=</option> command line switch. This option is
privileged (see above). </para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>PrivateUsers=</varname></term>
+
+ <listitem><para>Configures support for usernamespacing. This is equivalent to the
+ <option>--private-users=</option> command line switch, and takes the same options. This option is privileged
+ (see above). </para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -314,6 +322,16 @@
for details about the specific options supported. This setting
is privileged (see above).</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>PrivateUsersChown=</varname></term>
+
+ <listitem><para>Configures whether the ownership of the files and directories in the container tree shall be
+ adjusted to the UID/GID range used, if necessary and user namespacing is enabled. This is equivalent to the
+ <option>--private-users-chown</option> command line switch. This option is privileged (see
+ above). </para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index 80c15b700d..14998b9647 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -742,7 +742,7 @@
defined what that is supposed to mean, with one exception:
at shutdown, a unit that is ordered after
<filename>network.target</filename> will be stopped before
- the network -- to whatever level it might be set up then --
+ the network — to whatever level it might be set up then —
is shut down. It is hence useful when writing service files
that require network access on shutdown, which should order
themselves after this target, but not pull it in. Also see
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
index 22c83f790f..eb0afee141 100644
--- a/man/systemd.timer.xml
+++ b/man/systemd.timer.xml
@@ -253,7 +253,8 @@
during the time when the timer was inactive. This is useful to
catch up on missed runs of the service when the machine was
off. Note that this setting only has an effect on timers
- configured with <varname>OnCalendar=</varname>.
+ configured with <varname>OnCalendar=</varname>. Defaults
+ to <varname>false</varname>.
</para></listitem>
</varlistentry>
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 3b6b1e3f11..957475d2bd 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -157,13 +157,23 @@
<varlistentry>
<term><varname>d</varname></term>
- <listitem><para>Create a directory if it does not exist yet.
- </para></listitem>
+ <listitem><para>Create a directory. The mode and ownership will be adjusted if
+ specified and the directory already exists. Contents of this directory are subject
+ to time based cleanup if the time argument is specified.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>D</varname></term>
- <listitem><para>Create or empty a directory.</para></listitem>
+ <listitem><para>Similar to <varname>d</varname>, but in addition the contents
+ of the directory will be removed when <option>--remove</option> is used.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>e</varname></term>
+ <listitem><para>Similar to <varname>d</varname>, but the directory will not be
+ created if it does not exist. Lines of this type accept shell-style globs in
+ place of normal path names.</para></listitem>
</varlistentry>
<varlistentry>
@@ -577,7 +587,7 @@
unconditionally.</para>
<para>The age field only applies to lines starting with
- <varname>d</varname>, <varname>D</varname>,
+ <varname>d</varname>, <varname>D</varname>, <varname>e</varname>,
<varname>v</varname>, <varname>q</varname>,
<varname>Q</varname>, <varname>C</varname>, <varname>x</varname>
and <varname>X</varname>. If omitted or set to
@@ -612,22 +622,63 @@
</refsect1>
<refsect1>
- <title>Example</title>
+ <title>Examples</title>
<example>
- <title>/etc/tmpfiles.d/screen.conf example</title>
- <para><command>screen</command> needs two directories created at
- boot with specific modes and ownership.</para>
+ <title>Create directories with specific mode and ownership</title>
+ <para>
+ <citerefentry><refentrytitle>screen</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ needs two directories created at boot with specific modes and ownership:</para>
+
+ <programlisting># /usr/lib/tmpfiles.d/screen.conf
+d /run/screens 1777 root screen 10d
+d /run/uscreens 0755 root screen 10d12h
+</programlisting>
+
+ <para>Contents of <filename>/run/screens</filename> and /run/uscreens will
+ cleaned up after 10 and 10½ days, respectively.</para>
+ </example>
- <programlisting>d /run/screens 1777 root root 10d
-d /run/uscreens 0755 root root 10d12h
-t /run/screen - - - - user.name="John Smith" security.SMACK64=screen</programlisting>
+ <example>
+ <title>Create a directory with a SMACK attribute</title>
+ <programlisting>D /run/cups - - - -
+t /run/cups - - - - security.SMACK64=printing user.attr-with-spaces="foo bar"
+ </programlisting>
+
+ <para>The direcory will be owned by root and have default mode. It's contents are
+ not subject to time based cleanup, but will be obliterated when
+ <command>systemd-tmpfiles --remove</command> runs.</para>
</example>
+
<example>
- <title>/etc/tmpfiles.d/abrt.conf example</title>
- <para><command>abrt</command> needs a directory created at boot with specific mode and ownership and its content should be preserved.</para>
+ <title>Create a directory and prevent its contents from cleanup</title>
+ <para>
+ <citerefentry><refentrytitle>abrt</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ needs a directory created at boot with specific mode and ownership and its content
+ should be preserved from the automatic cleanup applied to the contents of
+ <filename>/var/tmp</filename>:</para>
+
+ <programlisting># /usr/lib/tmpfiles.d/tmp.conf
+d /var/tmp 1777 root root 30d
+</programlisting>
+
+ <programlisting># /usr/lib/tmpfiles.d/abrt.conf
+d /var/tmp/abrt 0755 abrt abrt -
+</programlisting>
+ </example>
- <programlisting>d /var/tmp/abrt 0755 abrt abrt
-x /var/tmp/abrt/*</programlisting>
+ <example>
+ <title>Apply clean up during boot and based on time</title>
+
+ <programlisting># /usr/lib/tmpfiles.d/dnf.conf
+r! /var/cache/dnf/*/*/download_lock.pid
+r! /var/cache/dnf/*/*/metadata_lock.pid
+r! /var/lib/dnf/rpmdb_lock.pid
+e /var/chache/dnf/ - - - 30d
+</programlisting>
+
+ <para>The lock files will be removed during boot. Any files and directories in
+ <filename>/var/chache/dnf/</filename> will be removed after they have not been
+ accessed in 30 days.</para>
</example>
</refsect1>
diff --git a/po/LINGUAS b/po/LINGUAS
index 2774a3228f..0916707be0 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -2,6 +2,7 @@ de
el
fr
gl
+hr
hu
it
ko
diff --git a/po/fr.po b/po/fr.po
index 96cdc7e774..17550c755e 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,13 +1,13 @@
# French translations for systemd package
# Traductions françaises du paquet systemd.
# This file is distributed under the same license as the systemd package.
-# Sylvain Plantefève <sylvain.plantefeve@gmail.com>, 2013-2015
+# Sylvain Plantefève <sylvain.plantefeve@gmail.com>, 2013-2016
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-09-21 20:11+0200\n"
+"POT-Creation-Date: 2016-04-24 21:13+0200\n"
"PO-Revision-Date: 2014-12-28 13:04+0100\n"
"Last-Translator: Sylvain Plantefève <sylvain.plantefeve@gmail.com>\n"
"Language-Team: French\n"
@@ -254,48 +254,58 @@ msgstr ""
"gestion par le système du rabat de l'écran."
#: ../src/login/org.freedesktop.login1.policy.in.h:19
+msgid "Allow non-logged-in user to run programs"
+msgstr "Permet à un utilisateur non connecté d'exécuter des programmes"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:20
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+"Requête explicite requise pour exécuter des programmes en tant "
+"qu'utilisateur non connecté."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:21
msgid "Allow non-logged-in users to run programs"
msgstr "Permet aux utilisateurs non connectés d'exécuter des programmes"
-#: ../src/login/org.freedesktop.login1.policy.in.h:20
+#: ../src/login/org.freedesktop.login1.policy.in.h:22
msgid "Authentication is required to run programs as a non-logged-in user."
msgstr ""
-"Authentification requise pour permettre aux utilisateurs non connectés "
-"d'exécuter des programmes."
+"Authentification requise pour exécuter des programmes en tant qu'utilisateur "
+"non connecté."
-#: ../src/login/org.freedesktop.login1.policy.in.h:21
+#: ../src/login/org.freedesktop.login1.policy.in.h:23
msgid "Allow attaching devices to seats"
msgstr "Permet d'associer des périphériques à des postes (seats)"
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
+#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid "Authentication is required for attaching a device to a seat."
msgstr ""
"Authentification requise pour associer un périphérique à un poste (seat)."
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: ../src/login/org.freedesktop.login1.policy.in.h:25
msgid "Flush device to seat attachments"
msgstr "Révoquer les associations de périphériques aux postes (seats)"
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
+#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
"Authentication is required for resetting how devices are attached to seats."
msgstr ""
"Authentification requise pour révoquer les associations de périphériques aux "
"postes (seats)."
-#: ../src/login/org.freedesktop.login1.policy.in.h:25
+#: ../src/login/org.freedesktop.login1.policy.in.h:27
msgid "Power off the system"
msgstr "Éteindre le système"
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
+#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid "Authentication is required for powering off the system."
msgstr "Authentification requise pour éteindre le système."
-#: ../src/login/org.freedesktop.login1.policy.in.h:27
+#: ../src/login/org.freedesktop.login1.policy.in.h:29
msgid "Power off the system while other users are logged in"
msgstr "Éteindre le système alors que d'autres utilisateurs sont connectés"
-#: ../src/login/org.freedesktop.login1.policy.in.h:28
+#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
"Authentication is required for powering off the system while other users are "
"logged in."
@@ -303,11 +313,11 @@ msgstr ""
"Authentification requise pour éteindre le système alors que d'autres "
"utilisateurs sont connectés."
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
+#: ../src/login/org.freedesktop.login1.policy.in.h:31
msgid "Power off the system while an application asked to inhibit it"
msgstr "Éteindre le système alors qu'une application a demandé de l'empêcher"
-#: ../src/login/org.freedesktop.login1.policy.in.h:30
+#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
"Authentication is required for powering off the system while an application "
"asked to inhibit it."
@@ -315,19 +325,19 @@ msgstr ""
"Authentification requise pour éteindre le système alors qu'une application a "
"demandé de l'empêcher."
-#: ../src/login/org.freedesktop.login1.policy.in.h:31
+#: ../src/login/org.freedesktop.login1.policy.in.h:33
msgid "Reboot the system"
msgstr "Redémarrer le système"
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
+#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid "Authentication is required for rebooting the system."
msgstr "Authentification requise pour redémarrer le système."
-#: ../src/login/org.freedesktop.login1.policy.in.h:33
+#: ../src/login/org.freedesktop.login1.policy.in.h:35
msgid "Reboot the system while other users are logged in"
msgstr "Redémarrer le système alors que d'autres utilisateurs sont connectés"
-#: ../src/login/org.freedesktop.login1.policy.in.h:34
+#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
"Authentication is required for rebooting the system while other users are "
"logged in."
@@ -335,11 +345,11 @@ msgstr ""
"Authentification requise pour redémarrer le système alors que d'autres "
"utilisateurs sont connectés."
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
+#: ../src/login/org.freedesktop.login1.policy.in.h:37
msgid "Reboot the system while an application asked to inhibit it"
msgstr "Redémarrer le système alors qu'une application a demandé de l'empêcher"
-#: ../src/login/org.freedesktop.login1.policy.in.h:36
+#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
"Authentication is required for rebooting the system while an application "
"asked to inhibit it."
@@ -347,20 +357,20 @@ msgstr ""
"Authentification requise pour redémarrer le système alors qu'une application "
"a demandé de l'empêcher."
-#: ../src/login/org.freedesktop.login1.policy.in.h:37
+#: ../src/login/org.freedesktop.login1.policy.in.h:39
msgid "Suspend the system"
msgstr "Mettre le système en veille"
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
+#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid "Authentication is required for suspending the system."
msgstr "Authentification requise pour mettre le système en veille."
-#: ../src/login/org.freedesktop.login1.policy.in.h:39
+#: ../src/login/org.freedesktop.login1.policy.in.h:41
msgid "Suspend the system while other users are logged in"
msgstr ""
"Mettre le système en veille alors que d'autres utilisateurs sont connectés"
-#: ../src/login/org.freedesktop.login1.policy.in.h:40
+#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
"Authentication is required for suspending the system while other users are "
"logged in."
@@ -368,12 +378,12 @@ msgstr ""
"Authentification requise pour mettre le système en veille alors que d'autres "
"utilisateurs sont connectés."
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
+#: ../src/login/org.freedesktop.login1.policy.in.h:43
msgid "Suspend the system while an application asked to inhibit it"
msgstr ""
"Mettre le système en veille alors qu'une application a demandé de l'empêcher"
-#: ../src/login/org.freedesktop.login1.policy.in.h:42
+#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
"Authentication is required for suspending the system while an application "
"asked to inhibit it."
@@ -381,21 +391,21 @@ msgstr ""
"Authentification requise pour mettre le système en veille alors qu'une "
"application a demandé de l'empêcher."
-#: ../src/login/org.freedesktop.login1.policy.in.h:43
+#: ../src/login/org.freedesktop.login1.policy.in.h:45
msgid "Hibernate the system"
msgstr "Mettre le système en hibernation"
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
+#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid "Authentication is required for hibernating the system."
msgstr "Authentification requise pour mettre le système en hibernation."
-#: ../src/login/org.freedesktop.login1.policy.in.h:45
+#: ../src/login/org.freedesktop.login1.policy.in.h:47
msgid "Hibernate the system while other users are logged in"
msgstr ""
"Mettre le système en hibernation alors que d'autres utilisateurs sont "
"connectés"
-#: ../src/login/org.freedesktop.login1.policy.in.h:46
+#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
"Authentication is required for hibernating the system while other users are "
"logged in."
@@ -403,13 +413,13 @@ msgstr ""
"Authentification requise pour mettre le système en hibernation alors que "
"d'autres utilisateurs sont connectés."
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
+#: ../src/login/org.freedesktop.login1.policy.in.h:49
msgid "Hibernate the system while an application asked to inhibit it"
msgstr ""
"Mettre le système en hibernation alors qu'une application a demandé de "
"l'empêcher"
-#: ../src/login/org.freedesktop.login1.policy.in.h:48
+#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
"Authentication is required for hibernating the system while an application "
"asked to inhibit it."
@@ -417,34 +427,34 @@ msgstr ""
"Authentification requise pour mettre le système en hibernation alors qu'une "
"application a demandé de l'empêcher."
-#: ../src/login/org.freedesktop.login1.policy.in.h:49
+#: ../src/login/org.freedesktop.login1.policy.in.h:51
msgid "Manage active sessions, users and seats"
msgstr "Gérer les sessions actives, les utilisateurs et les postes (seats)"
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
+#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
"Authentication is required for managing active sessions, users and seats."
msgstr ""
"Authentification requise pour gérer les sessions actives, les utilisateurs "
"et les postes (seats)."
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: ../src/login/org.freedesktop.login1.policy.in.h:53
msgid "Lock or unlock active sessions"
msgstr "Verrouiller ou déverrouiller des sessions actives"
-#: ../src/login/org.freedesktop.login1.policy.in.h:52
+#: ../src/login/org.freedesktop.login1.policy.in.h:54
msgid "Authentication is required to lock or unlock active sessions."
msgstr ""
"Authentification requise pour verrouiller ou déverrouiller des sessions "
"actives."
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
msgid "Allow indication to the firmware to boot to setup interface"
msgstr ""
"Permet d'indiquer au micrologiciel de démarrer sur l'interface de "
"configuration"
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
msgid ""
"Authentication is required to indicate to the firmware to boot to setup "
"interface."
@@ -452,11 +462,11 @@ msgstr ""
"Authentification requise pour indiquer au micrologiciel de démarrer sur "
"l'interface de configuration."
-#: ../src/login/org.freedesktop.login1.policy.in.h:55
+#: ../src/login/org.freedesktop.login1.policy.in.h:57
msgid "Set a wall message"
msgstr "Définir un message wall"
-#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#: ../src/login/org.freedesktop.login1.policy.in.h:58
msgid "Authentication is required to set a wall message"
msgstr "Authentification requise pour définir un message wall."
@@ -581,33 +591,33 @@ msgstr ""
"Authentification requise pour activer ou désactiver la synchronisation de "
"l'heure avec le réseau."
-#: ../src/core/dbus-unit.c:428
+#: ../src/core/dbus-unit.c:450
msgid "Authentication is required to start '$(unit)'."
msgstr "Authentification requise pour démarrer « $(unit) »."
-#: ../src/core/dbus-unit.c:429
+#: ../src/core/dbus-unit.c:451
msgid "Authentication is required to stop '$(unit)'."
msgstr "Authentification requise pour arrêter « $(unit) »."
-#: ../src/core/dbus-unit.c:430
+#: ../src/core/dbus-unit.c:452
msgid "Authentication is required to reload '$(unit)'."
msgstr "Authentification requise pour recharger « $(unit) »."
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#: ../src/core/dbus-unit.c:453 ../src/core/dbus-unit.c:454
msgid "Authentication is required to restart '$(unit)'."
msgstr "Authentification requise pour redémarrer « $(unit) »."
-#: ../src/core/dbus-unit.c:535
+#: ../src/core/dbus-unit.c:560
msgid "Authentication is required to kill '$(unit)'."
msgstr "Authentification requise pour tuer « $(unit) »."
-#: ../src/core/dbus-unit.c:565
+#: ../src/core/dbus-unit.c:590
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Authentification requise pour réinitialiser l'état d'« échec » de "
"« $(unit) »."
-#: ../src/core/dbus-unit.c:597
+#: ../src/core/dbus-unit.c:622
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Authentification requise pour définir des propriétés de « $(unit) »."
diff --git a/po/hr.po b/po/hr.po
new file mode 100644
index 0000000000..a0aff4331f
--- /dev/null
+++ b/po/hr.po
@@ -0,0 +1,570 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# gogo <trebelnik2@gmail.com>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: systemd master\n"
+"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
+"POT-Creation-Date: 2016-27-04 11:57+0100\n"
+"PO-Revision-Date: 2016-04-27 12:11+0200\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.7.1\n"
+"Last-Translator: gogo <trebelnik2@gmail.com>com>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Language: hr\n"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
+msgid "Send passphrase back to system"
+msgstr "Pošalji lozinku natrag u sustav"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
+msgstr "Potrebna je ovjera za slanje upisane lozinke natrag u sustav."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
+msgid "Manage system services or other units"
+msgstr "Upravljajte uslugama sustava ili drugim jedinicama"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
+msgid "Authentication is required to manage system services or other units."
+msgstr "Potrebna je ovjera za upravljanje uslugama sustava ili jedinicama."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
+msgid "Manage system service or unit files"
+msgstr "Upravljajte uslugama sustava ili datotekama jedinica"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6
+msgid "Authentication is required to manage system service or unit files."
+msgstr ""
+"Potrebna je ovjera za upravljanje uslugama sustava ili datotekama jedinica."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
+msgid "Set or unset system and service manager environment variables"
+msgstr "Postavite ili uklonite varijable okruženja sustava i usluga"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8
+msgid ""
+"Authentication is required to set or unset system and service manager "
+"environment variables."
+msgstr ""
+"Potrebna je ovjera za postavljanje ili uklanjanje varijabla okruženja "
+"sustava i usluga."
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
+msgid "Reload the systemd state"
+msgstr "Ponovno učitaj systemd stanje"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10
+msgid "Authentication is required to reload the systemd state."
+msgstr "Potrebna je ovjera za ponovno učitavanje systemd stanja."
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+msgid "Set host name"
+msgstr "Postavi naziv računala"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
+msgid "Authentication is required to set the local host name."
+msgstr "Potrebna je ovjera za postavljanje naziva lokalnog računala."
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+msgid "Set static host name"
+msgstr "Postavi nepromjenjivi naziv račumala"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
+msgid ""
+"Authentication is required to set the statically configured local host name, "
+"as well as the pretty host name."
+msgstr ""
+"Potrebna je ovjera za postavljenje nepromjenjivog naziva lokalnog računala, "
+"kao i prijatnog naziva računala."
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+msgid "Set machine information"
+msgstr "Postavi informacije računala"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6
+msgid "Authentication is required to set local machine information."
+msgstr "Potrebna je ovjera za postavljanje informacije lokalnog računala."
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:1
+msgid "Import a VM or container image"
+msgstr "Uvezi VM ili spremnik slike"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:2
+msgid "Authentication is required to import a VM or container image"
+msgstr "Potrebna je ovjera za uvoz WM ili spremnika slike"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:3
+msgid "Export a VM or container image"
+msgstr "Izvezi VM ili spremnik slike"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:4
+msgid "Authentication is required to export a VM or container image"
+msgstr "Potrebna je ovjera za izvoz WM ili spremnika slike"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:5
+msgid "Download a VM or container image"
+msgstr "Preuzmi VM ili spremnik slike"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:6
+msgid "Authentication is required to download a VM or container image"
+msgstr "Potrebna je ovjera za preuzimanje VM ili spremnika slike."
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:1
+msgid "Set system locale"
+msgstr "Postavi sustav lokalizacije"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:2
+msgid "Authentication is required to set the system locale."
+msgstr "Potrebna je ovjera za postavljanje sustava lokalizacije."
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+msgid "Set system keyboard settings"
+msgstr "Postavi postavke tipkovnice sustava"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:4
+msgid "Authentication is required to set the system keyboard settings."
+msgstr "Potrebna je ovjera za postavljanje postavki tipkovnice sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:1
+msgid "Allow applications to inhibit system shutdown"
+msgstr "Dopusti aplikacijama zaustavljanje isključivanja sustava"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:2
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje isključivanja "
+"sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:3
+msgid "Allow applications to delay system shutdown"
+msgstr "Dopusti aplikacijama odgodu isključivanja sustava"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:4
+msgid "Authentication is required for an application to delay system shutdown."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama odgode isključivanja sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:5
+msgid "Allow applications to inhibit system sleep"
+msgstr "Dopusti aplikacijama zaustavljanje spavanja sustava"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:6
+msgid "Authentication is required for an application to inhibit system sleep."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanja spavanja sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:7
+msgid "Allow applications to delay system sleep"
+msgstr "Dopusti aplikacijama odgodu spavanja sustava"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:8
+msgid "Authentication is required for an application to delay system sleep."
+msgstr "Potrebna je ovjera za odgodu spavanja sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:9
+msgid "Allow applications to inhibit automatic system suspend"
+msgstr "Dopusti aplikacijama zaustavljanje automatskog suspendiranja sustava"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:10
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje automatskog "
+"suspendiranja sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:11
+msgid "Allow applications to inhibit system handling of the power key"
+msgstr ""
+"Dopusti aplikacijama sprječavanje rukovanja sustava tipkom isključivanja"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:12
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the power key."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
+"tipkom isključivanja."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:13
+msgid "Allow applications to inhibit system handling of the suspend key"
+msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom suspenzije"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:14
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the suspend key."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
+"tipkom suspenzije."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:15
+msgid "Allow applications to inhibit system handling of the hibernate key"
+msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom hibernacije"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:16
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the hibernate key."
+msgstr ""
+"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
+"tipkom hibernacije."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:17
+msgid "Allow applications to inhibit system handling of the lid switch"
+msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava preklopnicama"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:18
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the lid switch."
+msgstr ""
+"Potrebna je ovjera za dopuštenje sprječavanja rukovanja sustava "
+"preklopnicama."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:19
+msgid "Allow non-logged-in users to run programs"
+msgstr "Dopusti neprijavljenim korisnicima pokretanje programa"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:20
+msgid "Authentication is required to run programs as a non-logged-in user."
+msgstr ""
+"Potrebna je ovjera za dopuštenje neprijavljenim korisnicima pokretanje "
+"programa."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:21
+msgid "Allow attaching devices to seats"
+msgstr "Dopusti povezivanje uređaja skupu sesija i hardvera"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:22
+msgid "Authentication is required for attaching a device to a seat."
+msgstr "Potrebna je ovjera za povezivanje uređaja sa skupom sesija i hardvera."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:23
+msgid "Flush device to seat attachments"
+msgstr "Ukloni povezani uređaj sa skupa sesija i hardvera"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:24
+msgid ""
+"Authentication is required for resetting how devices are attached to seats."
+msgstr ""
+"Potrebna je ovjera za obnovu povezivanja uređaja sa skupom sesija i hardvera."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:25
+msgid "Power off the system"
+msgstr "Isključi sustav"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:26
+msgid "Authentication is required for powering off the system."
+msgstr "Potrebna je ovjera za isključivanje sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:27
+msgid "Power off the system while other users are logged in"
+msgstr "Isključi sustav kada su ostali korisnici prijavljeni"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:28
+msgid ""
+"Authentication is required for powering off the system while other users are "
+"logged in."
+msgstr ""
+"Potrebna je ovjera za isključivanje sustava kada su ostali korisnici "
+"prijavljeni."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:29
+msgid "Power off the system while an application asked to inhibit it"
+msgstr ""
+"Isključi sustav kada je aplikacija zatražila zaustavljanje isključivanja"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:30
+msgid ""
+"Authentication is required for powering off the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Potrebna je ovjera za isključivanje sustava kada je aplikacija zatražila "
+"zaustavljanje isključivanja."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:31
+msgid "Reboot the system"
+msgstr "Ponovno pokreni sustav"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:32
+msgid "Authentication is required for rebooting the system."
+msgstr "Potrebna je ovjera za ponovno pokretanje sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:33
+msgid "Reboot the system while other users are logged in"
+msgstr "Ponovno pokreni sustav kada su ostali korisnici prijavljeni"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:34
+msgid ""
+"Authentication is required for rebooting the system while other users are "
+"logged in."
+msgstr ""
+"Potrebna je ovjera za ponovno pokretanje sustava kada su ostali korisnici "
+"prijavljeni."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:35
+msgid "Reboot the system while an application asked to inhibit it"
+msgstr ""
+"Ponovno pokreni sustav kada je aplikacija zatražila zaustavljanje ponovnog "
+"pokretanja"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:36
+msgid ""
+"Authentication is required for rebooting the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Potrebna je ovjera za ponovno pokretanje sustava kada je aplikacija "
+"zatražila zaustavljanje ponovnog pokretanja."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:37
+msgid "Suspend the system"
+msgstr "Suspendiraj sustav"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:38
+msgid "Authentication is required for suspending the system."
+msgstr "Potrebna je ovjera za suspendiranje sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:39
+msgid "Suspend the system while other users are logged in"
+msgstr "Suspendiraj sustav kada su drugi korisnici prijavljeni"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:40
+msgid ""
+"Authentication is required for suspending the system while other users are "
+"logged in."
+msgstr ""
+"Potrebna je ovjera za suspendiranje sustava kada su drugi korisnici "
+"prijavljeni."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:41
+msgid "Suspend the system while an application asked to inhibit it"
+msgstr ""
+"Suspendiraj sustav kada je aplikacija zatražila zaustavljanje suspendiranja"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:42
+msgid ""
+"Authentication is required for suspending the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Potrebna je ovjera za suspendiranje sustava kada je aplikacija zatražila "
+"zaustavljanje suspendiranja."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:43
+msgid "Hibernate the system"
+msgstr "Hiberniraj sustav"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:44
+msgid "Authentication is required for hibernating the system."
+msgstr "Potrebna je ovjera za hibernaciju sustava."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:45
+msgid "Hibernate the system while other users are logged in"
+msgstr "Hiberniraj sustav kada su ostali korisnici prijavljeni."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:46
+msgid ""
+"Authentication is required for hibernating the system while other users are "
+"logged in."
+msgstr ""
+"Potrebna je ovjera za hibernaciju sustava kada su drugi korisnici "
+"prijavljeni."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:47
+msgid "Hibernate the system while an application asked to inhibit it"
+msgstr ""
+"Hiberniraj sustav kada je aplikacija zatražila zaustavljanje hibernacije"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:48
+msgid ""
+"Authentication is required for hibernating the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Potrebna je ovjera za hibernaciju sustava kada je aplikacija zatražila "
+"zaustavljanje hibernacije."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:49
+msgid "Manage active sessions, users and seats"
+msgstr ""
+"Upravljanje aktivnim sesijama, korisnicima i skupovima sesija i hardvera"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:50
+msgid ""
+"Authentication is required for managing active sessions, users and seats."
+msgstr ""
+"Potrebna je ovjera za upravljanje aktivnim sesijama, korisnicima i skupovima "
+"sesija i hardvera."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:51
+msgid "Lock or unlock active sessions"
+msgstr "Zaključavanje ili otključavanje aktivne sesije"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:52
+msgid "Authentication is required to lock or unlock active sessions."
+msgstr "Potrebna je ovjera za zaključavanje ili otključavanje aktivne sesije."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:53
+msgid "Allow indication to the firmware to boot to setup interface"
+msgstr "Dopusti najavu frimveru za pokretanje sučelja postavljanja"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:54
+msgid ""
+"Authentication is required to indicate to the firmware to boot to setup "
+"interface."
+msgstr "Potrebna je ovjera najave frimvera za pokretanje sučelja postavljanja."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
+msgid "Set a wall message"
+msgstr "Postavljanje zaslonske pruke"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
+msgid "Authentication is required to set a wall message"
+msgstr "Potrebna je ovjera za postavljanje zaslonske pruke."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:1
+msgid "Log into a local container"
+msgstr "Prijavi se u lokalni spremnik"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:2
+msgid "Authentication is required to log into a local container."
+msgstr "Potrebna je ovjera za prijavu u lokalni spremnik."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+msgid "Log into the local host"
+msgstr "Prijava na lokalno računalo"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+msgid "Authentication is required to log into the local host."
+msgstr "Potrebna je ovjera za prijavu na lokalno račuanlo."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+msgid "Acquire a shell in a local container"
+msgstr "Pokretanje ljuske u lokalnom spremniku"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr "Potrebna je ovjera za pokretanje ljuske u lokalnom spremniku."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+msgid "Acquire a shell on the local host"
+msgstr "Pokretanje ljuske na lokalnom računalu"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr "Potrebna je ovjera za pokretanje ljuske na lokalnom računalu."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Pokretanje pseudo TTY na lokalnom spremniku"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom spremniku."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+msgid "Acquire a pseudo TTY on the local host"
+msgstr "Pokretanje pseudo TTY na lokalnom računalu"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom računalu."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+msgid "Manage local virtual machines and containers"
+msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
+msgid ""
+"Authentication is required to manage local virtual machines and containers."
+msgstr ""
+"Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i "
+"spremnicima."
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
+msgid "Manage local virtual machine and container images"
+msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima slika"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
+msgid ""
+"Authentication is required to manage local virtual machine and container "
+"images."
+msgstr ""
+"Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i "
+"spremnicima slika."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+msgid "Set system time"
+msgstr "Postavi vrijeme sustava"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2
+msgid "Authentication is required to set the system time."
+msgstr "Potrebna je ovjera za postavljanje vremena sustava."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+msgid "Set system timezone"
+msgstr "Postavi vremensku zonu sustava"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4
+msgid "Authentication is required to set the system timezone."
+msgstr "Potrebna je ovjera za postavljanje vremenske zone sustava."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5
+msgid "Set RTC to local timezone or UTC"
+msgstr "Postavi RTC u lokalnu vremensku zonu ili UTC"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6
+msgid ""
+"Authentication is required to control whether the RTC stores the local or "
+"UTC time."
+msgstr ""
+"Potrebna je ovjera za postavljanje RTC-a u lokalnu vremensku zonu ili UTC."
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7
+msgid "Turn network time synchronization on or off"
+msgstr "Uključi ili isključi mrežno uklađivanje vremena"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8
+msgid ""
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
+msgstr ""
+"Potrebna je ovjera za uključivanje ili isključivanje mrežnog usklađivanja "
+"vremena."
+
+#: ../src/core/dbus-unit.c:428
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Potrebna je ovjera za pokretanje '$(unit)'."
+
+#: ../src/core/dbus-unit.c:429
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Potrebna je ovjera za zaustavljanje '$(unit)'."
+
+#: ../src/core/dbus-unit.c:430
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Potrebna je ovjera za ponovno učitavnje '$(unit)'."
+
+#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Potrebna je ovjera za ponovno pokretanje'$(unit)'."
+
+#: ../src/core/dbus-unit.c:535
+msgid "Authentication is required to kill '$(unit)'."
+msgstr "Potrebna je ovjera za ubijanje '$(unit)'."
+
+#: ../src/core/dbus-unit.c:565
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr "Potrebna je ovjera za vraćanje \"neuspjelog\" stanja '$(unit)'."
+
+#: ../src/core/dbus-unit.c:597
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Potrebna je ovjera za postavljanje svojstava na '$(unit)'."
diff --git a/po/pl.po b/po/pl.po
index d3e2ae8418..92e9a209cc 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1,13 +1,13 @@
# translation of pl.po to Polish
-# Piotr Drąg <piotrdrag@gmail.com>, 2011, 2013, 2014, 2015.
+# Piotr Drąg <piotrdrag@gmail.com>, 2011, 2013, 2014, 2015, 2016.
# Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-09-06 20:39+0200\n"
-"PO-Revision-Date: 2015-09-06 20:40+0200\n"
+"POT-Creation-Date: 2016-04-23 14:24+0200\n"
+"PO-Revision-Date: 2016-04-23 14:25+0200\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
"Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
"Language: pl\n"
@@ -241,48 +241,58 @@ msgstr ""
"przez system."
#: ../src/login/org.freedesktop.login1.policy.in.h:19
+msgid "Allow non-logged-in user to run programs"
+msgstr "Zezwolenie niezalogowanemu użytkownikowi na uruchamianie programów"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:20
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+"Wymagane jest bezpośrednie żądanie, aby uruchamiać programy jako "
+"niezalogowany użytkownik."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:21
msgid "Allow non-logged-in users to run programs"
msgstr "Zezwolenie niezalogowanym użytkownikom na uruchamianie programów"
-#: ../src/login/org.freedesktop.login1.policy.in.h:20
+#: ../src/login/org.freedesktop.login1.policy.in.h:22
msgid "Authentication is required to run programs as a non-logged-in user."
msgstr ""
"Wymagane jest uwierzytelnienie, aby uruchamiać programy jako niezalogowany "
"użytkownik."
-#: ../src/login/org.freedesktop.login1.policy.in.h:21
+#: ../src/login/org.freedesktop.login1.policy.in.h:23
msgid "Allow attaching devices to seats"
msgstr "Zezwolenie na podłączanie urządzeń do stanowisk"
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
+#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid "Authentication is required for attaching a device to a seat."
msgstr ""
"Wymagane jest uwierzytelnienie, aby podłączyć urządzenie do stanowiska."
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: ../src/login/org.freedesktop.login1.policy.in.h:25
msgid "Flush device to seat attachments"
msgstr "Usunięcie podłączenia urządzeń do stanowisk"
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
+#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
"Authentication is required for resetting how devices are attached to seats."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie ustawić sposób podłączenia "
"urządzeń do stanowisk."
-#: ../src/login/org.freedesktop.login1.policy.in.h:25
+#: ../src/login/org.freedesktop.login1.policy.in.h:27
msgid "Power off the system"
msgstr "Wyłączenie systemu"
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
+#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid "Authentication is required for powering off the system."
msgstr "Wymagane jest uwierzytelnienie, aby wyłączyć system."
-#: ../src/login/org.freedesktop.login1.policy.in.h:27
+#: ../src/login/org.freedesktop.login1.policy.in.h:29
msgid "Power off the system while other users are logged in"
msgstr "Wyłączenie systemu, kiedy są zalogowani inni użytkownicy"
-#: ../src/login/org.freedesktop.login1.policy.in.h:28
+#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
"Authentication is required for powering off the system while other users are "
"logged in."
@@ -290,11 +300,11 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy są zalogowani "
"inni użytkownicy."
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
+#: ../src/login/org.freedesktop.login1.policy.in.h:31
msgid "Power off the system while an application asked to inhibit it"
msgstr "Wyłączenie systemu, kiedy program zażądał jego wstrzymania"
-#: ../src/login/org.freedesktop.login1.policy.in.h:30
+#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
"Authentication is required for powering off the system while an application "
"asked to inhibit it."
@@ -302,19 +312,19 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy program zażądał "
"jego wstrzymania."
-#: ../src/login/org.freedesktop.login1.policy.in.h:31
+#: ../src/login/org.freedesktop.login1.policy.in.h:33
msgid "Reboot the system"
msgstr "Ponowne uruchomienie systemu"
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
+#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid "Authentication is required for rebooting the system."
msgstr "Wymagane jest uwierzytelnienie, aby ponownie uruchomić system."
-#: ../src/login/org.freedesktop.login1.policy.in.h:33
+#: ../src/login/org.freedesktop.login1.policy.in.h:35
msgid "Reboot the system while other users are logged in"
msgstr "Ponowne uruchomienie systemu, kiedy są zalogowani inni użytkownicy"
-#: ../src/login/org.freedesktop.login1.policy.in.h:34
+#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
"Authentication is required for rebooting the system while other users are "
"logged in."
@@ -322,11 +332,11 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić system, kiedy są "
"zalogowani inni użytkownicy."
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
+#: ../src/login/org.freedesktop.login1.policy.in.h:37
msgid "Reboot the system while an application asked to inhibit it"
msgstr "Ponowne uruchomienie systemu, kiedy program zażądał jego wstrzymania"
-#: ../src/login/org.freedesktop.login1.policy.in.h:36
+#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
"Authentication is required for rebooting the system while an application "
"asked to inhibit it."
@@ -334,19 +344,19 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić system, kiedy program "
"zażądał jego wstrzymania."
-#: ../src/login/org.freedesktop.login1.policy.in.h:37
+#: ../src/login/org.freedesktop.login1.policy.in.h:39
msgid "Suspend the system"
msgstr "Uśpienie systemu"
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
+#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid "Authentication is required for suspending the system."
msgstr "Wymagane jest uwierzytelnienie, aby uśpić system."
-#: ../src/login/org.freedesktop.login1.policy.in.h:39
+#: ../src/login/org.freedesktop.login1.policy.in.h:41
msgid "Suspend the system while other users are logged in"
msgstr "Uśpienie systemu, kiedy są zalogowani inni użytkownicy"
-#: ../src/login/org.freedesktop.login1.policy.in.h:40
+#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
"Authentication is required for suspending the system while other users are "
"logged in."
@@ -354,11 +364,11 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy są zalogowani inni "
"użytkownicy."
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
+#: ../src/login/org.freedesktop.login1.policy.in.h:43
msgid "Suspend the system while an application asked to inhibit it"
msgstr "Uśpienie systemu, kiedy program zażądał jego wstrzymania"
-#: ../src/login/org.freedesktop.login1.policy.in.h:42
+#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
"Authentication is required for suspending the system while an application "
"asked to inhibit it."
@@ -366,19 +376,19 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy program zażądał jego "
"wstrzymania."
-#: ../src/login/org.freedesktop.login1.policy.in.h:43
+#: ../src/login/org.freedesktop.login1.policy.in.h:45
msgid "Hibernate the system"
msgstr "Hibernacja systemu"
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
+#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid "Authentication is required for hibernating the system."
msgstr "Wymagane jest uwierzytelnienie, aby zahibernować system."
-#: ../src/login/org.freedesktop.login1.policy.in.h:45
+#: ../src/login/org.freedesktop.login1.policy.in.h:47
msgid "Hibernate the system while other users are logged in"
msgstr "Hibernacja systemu, kiedy są zalogowani inni użytkownicy"
-#: ../src/login/org.freedesktop.login1.policy.in.h:46
+#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
"Authentication is required for hibernating the system while other users are "
"logged in."
@@ -386,11 +396,11 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy są zalogowani "
"inni użytkownicy."
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
+#: ../src/login/org.freedesktop.login1.policy.in.h:49
msgid "Hibernate the system while an application asked to inhibit it"
msgstr "Hibernacja systemu, kiedy program zażądał jej wstrzymania"
-#: ../src/login/org.freedesktop.login1.policy.in.h:48
+#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
"Authentication is required for hibernating the system while an application "
"asked to inhibit it."
@@ -398,31 +408,31 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy program "
"zażądał jej wstrzymania."
-#: ../src/login/org.freedesktop.login1.policy.in.h:49
+#: ../src/login/org.freedesktop.login1.policy.in.h:51
msgid "Manage active sessions, users and seats"
msgstr "Zarządzanie aktywnymi sesjami, użytkownikami i stanowiskami"
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
+#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
"Authentication is required for managing active sessions, users and seats."
msgstr ""
"Wymagane jest uwierzytelnienie, aby zarządzać aktywnymi sesjami, "
"użytkownikami i stanowiskami."
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: ../src/login/org.freedesktop.login1.policy.in.h:53
msgid "Lock or unlock active sessions"
msgstr "Zablokowanie lub odblokowanie aktywnych sesji"
-#: ../src/login/org.freedesktop.login1.policy.in.h:52
+#: ../src/login/org.freedesktop.login1.policy.in.h:54
msgid "Authentication is required to lock or unlock active sessions."
msgstr ""
"Wymagane jest uwierzytelnienie, aby zablokować lub odblokować aktywne sesje."
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
msgid "Allow indication to the firmware to boot to setup interface"
msgstr "Wskazanie oprogramowaniu sprzętowemu, aby uruchomić interfejs ustawień"
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
msgid ""
"Authentication is required to indicate to the firmware to boot to setup "
"interface."
@@ -430,11 +440,11 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby wskazać oprogramowaniu sprzętowemu, że "
"należy uruchomić interfejs ustawień."
-#: ../src/login/org.freedesktop.login1.policy.in.h:55
+#: ../src/login/org.freedesktop.login1.policy.in.h:57
msgid "Set a wall message"
msgstr "Ustawienie komunikatu wall"
-#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#: ../src/login/org.freedesktop.login1.policy.in.h:58
msgid "Authentication is required to set a wall message"
msgstr "Wymagane jest uwierzytelnienie, aby ustawić komunikat wall"
@@ -557,36 +567,36 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację "
"czasu przez sieć."
-#: ../src/core/dbus-unit.c:428
+#: ../src/core/dbus-unit.c:450
msgid "Authentication is required to start '$(unit)'."
msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”."
-#: ../src/core/dbus-unit.c:429
+#: ../src/core/dbus-unit.c:451
msgid "Authentication is required to stop '$(unit)'."
msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”."
-#: ../src/core/dbus-unit.c:430
+#: ../src/core/dbus-unit.c:452
msgid "Authentication is required to reload '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”."
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#: ../src/core/dbus-unit.c:453 ../src/core/dbus-unit.c:454
msgid "Authentication is required to restart '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”."
-#: ../src/core/dbus-unit.c:535
+#: ../src/core/dbus-unit.c:560
msgid "Authentication is required to kill '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby wymusić wyłączenie jednostki „$(unit)”."
-#: ../src/core/dbus-unit.c:565
+#: ../src/core/dbus-unit.c:590
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby przywrócić stan „failed” (niepowodzenia) "
"jednostki „$(unit)”."
-#: ../src/core/dbus-unit.c:597
+#: ../src/core/dbus-unit.c:622
msgid "Authentication is required to set properties on '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”."
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 5e03c0c5e0..a790ccd33e 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "analyze-verify.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "glob-util.h"
#include "hashmap.h"
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e2db4be9ff..03487a6878 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -102,7 +102,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
if (try_cfr) {
n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
if (n < 0) {
- if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV))
+ if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
return n;
try_cfr = false;
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
index 5fb535cb13..59067121b7 100644
--- a/src/basic/dirent-util.c
+++ b/src/basic/dirent-util.c
@@ -52,12 +52,10 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
bool dirent_is_file(const struct dirent *de) {
assert(de);
- if (hidden_file(de->d_name))
+ if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
+ if (hidden_or_backup_file(de->d_name))
return false;
return true;
@@ -66,12 +64,10 @@ bool dirent_is_file(const struct dirent *de) {
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
assert(de);
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
+ if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
- if (hidden_file_allow_backup(de->d_name))
+ if (de->d_name[0] == '.')
return false;
return endswith(de->d_name, suffix);
diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h
index 6bf099b46c..b91d04908f 100644
--- a/src/basic/dirent-util.h
+++ b/src/basic/dirent-util.h
@@ -38,7 +38,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu
on_error; \
} \
break; \
- } else if (hidden_file((de)->d_name)) \
+ } else if (hidden_or_backup_file((de)->d_name)) \
continue; \
else
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index ec9560cd07..9130d023d7 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -25,11 +25,13 @@
#include <unistd.h>
#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
+#include "stdio-util.h"
#include "util.h"
int close_nointr(int fd) {
@@ -229,7 +231,7 @@ int close_all_fds(const int except[], unsigned n_except) {
while ((de = readdir(d))) {
int fd = -1;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
if (safe_atoi(de->d_name, &fd) < 0)
@@ -356,3 +358,11 @@ bool fdname_is_valid(const char *s) {
return p - s < 256;
}
+
+int fd_get_path(int fd, char **ret) {
+ char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+
+ return readlink_malloc(procfs_path, ret);
+}
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 44528c6e35..b86e41698a 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -72,6 +72,8 @@ void cmsg_close_all(struct msghdr *mh);
bool fdname_is_valid(const char *s);
+int fd_get_path(int fd, char **ret);
+
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
diff --git a/src/basic/fdset.c b/src/basic/fdset.c
index 06f8ecbdbc..527f27bc67 100644
--- a/src/basic/fdset.c
+++ b/src/basic/fdset.c
@@ -151,7 +151,7 @@ int fdset_new_fill(FDSet **_s) {
while ((de = readdir(d))) {
int fd = -1;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
r = safe_atoi(de->d_name, &fd);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 69590941e5..2a9b6e46ad 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -1083,30 +1083,6 @@ int mkostemp_safe(char *pattern, int flags) {
return fd;
}
-int open_tmpfile(const char *path, int flags) {
- char *p;
- int fd;
-
- assert(path);
-
-#ifdef O_TMPFILE
- /* Try O_TMPFILE first, if it is supported */
- fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
- if (fd >= 0)
- return fd;
-#endif
-
- /* Fall back to unguessable name + unlinking */
- p = strjoina(path, "/systemd-tmp-XXXXXX");
-
- fd = mkostemp_safe(p, flags);
- if (fd < 0)
- return fd;
-
- unlink(p);
- return fd;
-}
-
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
const char *fn;
char *t;
@@ -1278,3 +1254,103 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
return fputs(s, f);
}
+
+int open_tmpfile_unlinkable(const char *directory, int flags) {
+ char *p;
+ int fd;
+
+ assert(directory);
+
+ /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
+
+#ifdef O_TMPFILE
+ /* Try O_TMPFILE first, if it is supported */
+ fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ return fd;
+#endif
+
+ /* Fall back to unguessable name + unlinking */
+ p = strjoina(directory, "/systemd-tmp-XXXXXX");
+
+ fd = mkostemp_safe(p, flags);
+ if (fd < 0)
+ return fd;
+
+ (void) unlink(p);
+
+ return fd;
+}
+
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
+ _cleanup_free_ char *tmp = NULL;
+ int r, fd;
+
+ assert(target);
+ assert(ret_path);
+
+ /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
+ assert((flags & O_EXCL) == 0);
+
+ /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
+ * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+ * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
+
+#ifdef O_TMPFILE
+ {
+ _cleanup_free_ char *dn = NULL;
+
+ dn = dirname_malloc(target);
+ if (!dn)
+ return -ENOMEM;
+
+ fd = open(dn, O_TMPFILE|flags, 0640);
+ if (fd >= 0) {
+ *ret_path = NULL;
+ return fd;
+ }
+
+ log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
+ }
+#endif
+
+ r = tempfn_random(target, NULL, &tmp);
+ if (r < 0)
+ return r;
+
+ fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
+ if (fd < 0)
+ return -errno;
+
+ *ret_path = tmp;
+ tmp = NULL;
+
+ return fd;
+}
+
+int link_tmpfile(int fd, const char *path, const char *target) {
+
+ assert(fd >= 0);
+ assert(target);
+
+ /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
+ * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
+ * on the directory, and renameat2() is used instead.
+ *
+ * Note that in both cases we will not replace existing files. This is because linkat() dos not support this
+ * operation currently (renameat2() does), and there is no nice way to emulate this. */
+
+ if (path) {
+ if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
+ return -errno;
+ } else {
+ char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
+
+ xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
+
+ if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 8084895ff3..58dbc80c24 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -72,7 +72,6 @@ int fflush_and_check(FILE *f);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
int mkostemp_safe(char *pattern, int flags);
-int open_tmpfile(const char *path, int flags);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
@@ -82,3 +81,8 @@ int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
+
+int open_tmpfile_unlinkable(const char *directory, int flags);
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+
+int link_tmpfile(int fd, const char *path, const char *target);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 51268828af..e24e7036f7 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -38,6 +38,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -493,3 +494,17 @@ int get_files_in_directory(const char *path, char ***list) {
return n;
}
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
+ char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ int r;
+
+ /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
+ xsprintf(path, "/proc/self/fd/%i", what);
+
+ r = inotify_add_watch(fd, path, mask);
+ if (r < 0)
+ return -errno;
+
+ return r;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 0d23f8635f..517b599d6f 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -72,3 +72,5 @@ union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask);
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 6a49ccd281..b389e94cf7 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -445,6 +445,10 @@ struct btrfs_ioctl_quota_ctl_args {
#define TMPFS_MAGIC 0x01021994
#endif
+#ifndef MQUEUE_MAGIC
+#define MQUEUE_MAGIC 0x19800202
+#endif
+
#ifndef MS_MOVE
#define MS_MOVE 8192
#endif
@@ -828,6 +832,10 @@ struct btrfs_ioctl_quota_ctl_args {
#define IPV6_UNICAST_IF 76
#endif
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif
+
#ifndef IFF_MULTI_QUEUE
#define IFF_MULTI_QUEUE 0x100
#endif
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 5faa2eba05..ba698959b7 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -498,7 +498,9 @@ bool fstype_is_network(const char *fstype) {
"nfs4\0"
"gfs\0"
"gfs2\0"
- "glusterfs\0";
+ "glusterfs\0"
+ "pvfs2\0" /* OrangeFS */
+ ;
const char *x;
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index c407263e16..7dc579a159 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -93,7 +93,7 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
#if SIZE_MAX == UINT_MAX
static inline int safe_atozu(const char *s, size_t *ret_u) {
assert_cc(sizeof(size_t) == sizeof(unsigned));
- return safe_atou(s, ret_u);
+ return safe_atou(s, (unsigned *) ret_u);
}
#else
static inline int safe_atozu(const char *s, size_t *ret_u) {
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 044a12889d..100e3f5af2 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -756,34 +756,37 @@ char *file_in_same_dir(const char *path, const char *filename) {
return ret;
}
-bool hidden_file_allow_backup(const char *filename) {
- assert(filename);
-
- return
- filename[0] == '.' ||
- streq(filename, "lost+found") ||
- streq(filename, "aquota.user") ||
- streq(filename, "aquota.group") ||
- endswith(filename, ".rpmnew") ||
- endswith(filename, ".rpmsave") ||
- endswith(filename, ".rpmorig") ||
- endswith(filename, ".dpkg-old") ||
- endswith(filename, ".dpkg-new") ||
- endswith(filename, ".dpkg-tmp") ||
- endswith(filename, ".dpkg-dist") ||
- endswith(filename, ".dpkg-bak") ||
- endswith(filename, ".dpkg-backup") ||
- endswith(filename, ".dpkg-remove") ||
- endswith(filename, ".swp");
-}
+bool hidden_or_backup_file(const char *filename) {
+ const char *p;
-bool hidden_file(const char *filename) {
assert(filename);
- if (endswith(filename, "~"))
+ if (filename[0] == '.' ||
+ streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
+ endswith(filename, "~"))
return true;
- return hidden_file_allow_backup(filename);
+ p = strrchr(filename, '.');
+ if (!p)
+ return false;
+
+ return STR_IN_SET(p + 1,
+ "rpmnew",
+ "rpmsave",
+ "rpmorig",
+ "dpkg-old",
+ "dpkg-new",
+ "dpkg-tmp",
+ "dpkg-dist",
+ "dpkg-bak",
+ "dpkg-backup",
+ "dpkg-remove",
+ "ucf-new",
+ "ucf-old",
+ "ucf-dist",
+ "swp");
}
bool is_device_path(const char *path) {
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 34d5cd1570..a27c13fcc3 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -122,7 +122,6 @@ bool path_is_safe(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
-bool hidden_file_allow_backup(const char *filename);
-bool hidden_file(const char *filename) _pure_;
+bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index ae3f6109ad..f2cea01979 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -731,6 +731,18 @@ void valgrind_summary_hack(void) {
#endif
}
+int pid_compare_func(const void *a, const void *b) {
+ const pid_t *p = a, *q = b;
+
+ /* Suitable for usage in qsort() */
+
+ if (*p < *q)
+ return -1;
+ if (*p > *q)
+ return 1;
+ return 0;
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index f5d193e762..ffd4bcb0ff 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -101,3 +101,5 @@ int sched_policy_from_string(const char *s);
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
void valgrind_summary_hack(void);
+
+int pid_compare_func(const void *a, const void *b);
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 0a9d2bbdef..9521b79daa 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -1135,14 +1135,19 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
}
bool colors_enabled(void) {
- const char *colors;
+ static int enabled = -1;
- colors = getenv("SYSTEMD_COLORS");
- if (!colors) {
- if (streq_ptr(getenv("TERM"), "dumb"))
- return false;
- return on_tty();
+ if (_unlikely_(enabled < 0)) {
+ const char *colors;
+
+ colors = getenv("SYSTEMD_COLORS");
+ if (colors)
+ enabled = parse_boolean(colors) != 0;
+ else if (streq_ptr(getenv("TERM"), "dumb"))
+ enabled = false;
+ else
+ enabled = on_tty();
}
- return parse_boolean(colors) != 0;
+ return enabled;
}
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 7ca764abeb..edd9179cb8 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -1080,22 +1080,31 @@ bool timezone_is_valid(const char *name) {
return true;
}
-clockid_t clock_boottime_or_monotonic(void) {
- static clockid_t clock = -1;
- int fd;
-
- if (clock != -1)
- return clock;
-
- fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
- if (fd < 0)
- clock = CLOCK_MONOTONIC;
- else {
- safe_close(fd);
- clock = CLOCK_BOOTTIME;
+bool clock_boottime_supported(void) {
+ static int supported = -1;
+
+ /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
+
+ if (supported < 0) {
+ int fd;
+
+ fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (fd < 0)
+ supported = false;
+ else {
+ safe_close(fd);
+ supported = true;
+ }
}
- return clock;
+ return supported;
+}
+
+clockid_t clock_boottime_or_monotonic(void) {
+ if (clock_boottime_supported())
+ return CLOCK_BOOTTIME;
+ else
+ return CLOCK_MONOTONIC;
}
int get_timezone(char **tz) {
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 77e3cd08d4..a5e3f567ec 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -112,6 +112,7 @@ bool ntp_synced(void);
int get_timezones(char ***l);
bool timezone_is_valid(const char *name);
+bool clock_boottime_supported(void);
clockid_t clock_boottime_or_monotonic(void);
#define xstrftime(buf, fmt, tm) \
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index c23f1d485d..8026eca3f4 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -21,6 +21,7 @@
#include <stdbool.h>
#include <sys/types.h>
+#include <unistd.h>
bool uid_is_valid(uid_t uid);
@@ -63,3 +64,7 @@ int take_etc_passwd_lock(const char *root);
#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+static inline bool userns_supported(void) {
+ return access("/proc/self/uid_map", F_OK) >= 0;
+}
diff --git a/src/basic/util.c b/src/basic/util.c
index b70c50047f..756c663be4 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -522,7 +522,7 @@ int on_ac_power(void) {
if (!de)
break;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index d6fb10cac5..dcb5912b83 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -191,7 +191,8 @@ int main(int argc, char *argv[]) {
output_flags =
arg_all * OUTPUT_SHOW_ALL |
- (arg_full > 0) * OUTPUT_FULL_WIDTH;
+ (arg_full > 0) * OUTPUT_FULL_WIDTH |
+ arg_kernel_threads * OUTPUT_KERNEL_THREADS;
if (optind < argc) {
_cleanup_free_ char *root = NULL;
@@ -209,7 +210,7 @@ int main(int argc, char *argv[]) {
printf("Directory %s:\n", argv[i]);
fflush(stdout);
- q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags);
+ q = show_cgroup_by_path(argv[i], NULL, 0, output_flags);
} else {
_cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
const char *controller, *path;
@@ -235,7 +236,7 @@ int main(int argc, char *argv[]) {
show_cg_info(controller, path);
- q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags);
+ q = show_cgroup(controller, path, NULL, 0, output_flags);
}
if (q < 0)
@@ -258,7 +259,7 @@ int main(int argc, char *argv[]) {
printf("Working directory %s:\n", cwd);
fflush(stdout);
- r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags);
+ r = show_cgroup_by_path(cwd, NULL, 0, output_flags);
done = true;
}
}
@@ -273,7 +274,7 @@ int main(int argc, char *argv[]) {
show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
printf("-.slice\n");
- r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags);
+ r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
}
}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 9c0e82ebb3..14eb46c8db 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -362,7 +362,7 @@ static int refresh_one(
Group **ret) {
_cleanup_closedir_ DIR *d = NULL;
- Group *ours;
+ Group *ours = NULL;
int r;
assert(controller);
diff --git a/src/core/busname.c b/src/core/busname.c
index bbe61af4f0..f4f433340c 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -999,6 +999,14 @@ static bool busname_supported(void) {
return supported;
}
+static int busname_control_pid(Unit *u) {
+ BusName *n = BUSNAME(u);
+
+ assert(n);
+
+ return n->control_pid;
+}
+
static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_SUCCESS] = "success",
[BUSNAME_FAILURE_RESOURCES] = "resources",
@@ -1052,6 +1060,8 @@ const UnitVTable busname_vtable = {
.supported = busname_supported,
+ .control_pid = busname_control_pid,
+
.bus_vtable = bus_busname_vtable,
.status_message_formats = {
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index d48b0ca69d..d2eb388f7c 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -642,6 +642,30 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
return bus_unit_method_set_properties(message, u, error);
}
+static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_get_processes(message, u, error);
+}
+
static int transient_unit_from_message(
Manager *m,
sd_bus_message *message,
@@ -1666,7 +1690,6 @@ static int install_error(
static int method_enable_unit_files_generic(
sd_bus_message *message,
Manager *m,
- const char *verb,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes),
bool carries_install_info,
sd_bus_error *error) {
@@ -1701,15 +1724,15 @@ static int method_enable_unit_files_generic(
}
static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_enable, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error);
}
static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_reenable, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_reenable, true, error);
}
static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_link, false, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error);
}
static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) {
@@ -1717,11 +1740,11 @@ static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, cons
}
static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_preset_without_mode, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_preset_without_mode, true, error);
}
static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "disable", unit_file_mask, false, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_mask, false, error);
}
static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1769,7 +1792,6 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
static int method_disable_unit_files_generic(
sd_bus_message *message,
Manager *m,
- const char *verb,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes),
sd_bus_error *error) {
@@ -1803,11 +1825,11 @@ static int method_disable_unit_files_generic(
}
static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, "disable", unit_file_disable, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
}
static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error);
}
static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2044,6 +2066,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index c507265070..abe30413c3 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -24,8 +24,10 @@
#include "cgroup-util.h"
#include "dbus-unit.h"
#include "dbus.h"
+#include "fd-util.h"
#include "locale-util.h"
#include "log.h"
+#include "process-util.h"
#include "selinux-access.h"
#include "signal-util.h"
#include "special.h"
@@ -841,6 +843,145 @@ static int property_get_cgroup(
return sd_bus_message_append(reply, "s", t);
}
+static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) {
+ _cleanup_free_ char *buf = NULL, *cmdline = NULL;
+ int r;
+
+ assert(reply);
+ assert(pid > 0);
+
+ r = set_put(pids, PID_TO_PTR(pid));
+ if (r == -EEXIST || r == 0)
+ return 0;
+ if (r < 0)
+ return r;
+
+ if (!p) {
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf);
+ if (r == -ESRCH)
+ return 0;
+ if (r < 0)
+ return r;
+
+ p = buf;
+ }
+
+ (void) get_process_cmdline(pid, 0, true, &cmdline);
+
+ return sd_bus_message_append(reply,
+ "(sus)",
+ p,
+ (uint32_t) pid,
+ cmdline);
+}
+
+static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(reply);
+ assert(p);
+
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f);
+ if (r == ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ pid_t pid;
+
+ r = cg_read_pid(f, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (is_kernel_thread(pid) > 0)
+ continue;
+
+ r = append_process(reply, p, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *g = NULL, *j = NULL;
+
+ r = cg_read_subgroup(d, &g);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ j = strjoin(p, "/", g, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ r = append_cgroup(reply, j, pids);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(set_freep) Set *pids = NULL;
+ Unit *u = userdata;
+ pid_t pid;
+ int r;
+
+ assert(message);
+
+ pids = set_new(NULL);
+ if (!pids)
+ return -ENOMEM;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(sus)");
+ if (r < 0)
+ return r;
+
+ if (u->cgroup_path) {
+ r = append_cgroup(reply, u->cgroup_path, pids);
+ if (r < 0)
+ return r;
+ }
+
+ /* The main and control pids might live outside of the cgroup, hence fetch them separately */
+ pid = unit_main_pid(u);
+ if (pid > 0) {
+ r = append_process(reply, NULL, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ pid = unit_control_pid(u);
+ if (pid > 0) {
+ r = append_process(reply, NULL, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
@@ -848,6 +989,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
+ SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
@@ -1099,7 +1241,10 @@ static int bus_unit_set_transient_property(
if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s);
- r = manager_load_unit(u->manager, s, NULL, error, &slice);
+ /* Note that we do not dispatch the load queue here yet, as we don't want our own transient unit to be
+ * loaded while we are still setting it up. Or in other words, we use manager_load_unit_prepare()
+ * instead of manager_load_unit() on purpose, here. */
+ r = manager_load_unit_prepare(u->manager, s, NULL, error, &slice);
if (r < 0)
return r;
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 07948b9cd0..4db88dbebc 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -36,5 +36,6 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index ff7558d500..d1b0ce76ef 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -3,7 +3,7 @@
Copyright 2010 Lennart Poettering
Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
+ TORSEC group — http://security.polito.it
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h
index 3bad74b246..472b58cb00 100644
--- a/src/core/ima-setup.h
+++ b/src/core/ima-setup.h
@@ -5,7 +5,7 @@
Copyright 2010 Lennart Poettering
Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
+ TORSEC group — http://security.polito.it
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 9de528b6cf..0145fe2894 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -101,14 +101,23 @@ static int read_machine_id(int fd, char id[34]) {
return 0;
}
-static int write_machine_id(int fd, char id[34]) {
+static int write_machine_id(int fd, const char id[34]) {
+ int r;
+
assert(fd >= 0);
assert(id);
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
- return loop_write(fd, id, 33, false);
+ r = loop_write(fd, id, 33, false);
+ if (r < 0)
+ return r;
+
+ if (fsync(fd) < 0)
+ return -errno;
+
+ return 0;
}
static int generate_machine_id(char id[34], const char *root) {
diff --git a/src/core/main.c b/src/core/main.c
index 2912608435..75c5ff81f2 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -413,6 +413,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
target = runlevel_to_target(key);
if (target)
return free_and_strdup(&arg_default_unit, target);
+
+ } else if (streq(key, "systemd.default_timeout_start_sec") && value) {
+
+ r = parse_sec(value, &arg_default_timeout_start_usec);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
+
+ if (arg_default_timeout_start_usec <= 0)
+ arg_default_timeout_start_usec = USEC_INFINITY;
}
return 0;
@@ -1338,7 +1347,7 @@ int main(int argc, char *argv[]) {
saved_argv = argv;
saved_argc = argc;
- log_show_color(isatty(STDERR_FILENO) > 0);
+ log_show_color(colors_enabled());
log_set_upgrade_syslog_to_journal(true);
/* Disable the umask logic */
diff --git a/src/core/manager.c b/src/core/manager.c
index 5601770670..bd00c224f4 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2191,7 +2191,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
assert(_f);
path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
- fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
diff --git a/src/core/mount.c b/src/core/mount.c
index 6cfca1a4a9..cc07873b24 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1799,6 +1799,14 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}
+static int mount_control_pid(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ return m->control_pid;
+}
+
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
@@ -1860,6 +1868,8 @@ const UnitVTable mount_vtable = {
.reset_failed = mount_reset_failed,
+ .control_pid = mount_control_pid,
+
.bus_vtable = bus_mount_vtable,
.bus_set_property = bus_mount_set_property,
.bus_commit_properties = bus_mount_commit_properties,
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index f78eedbd6e..b732501364 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -78,6 +78,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitProcesses"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListJobs"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 2cdfcf7b5d..cc287d602d 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -110,6 +110,7 @@ static int callback_type_to_priority(int type) {
*/
_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_list ap;
+ const char *fmt2;
#ifdef HAVE_AUDIT
int fd;
@@ -131,8 +132,10 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
}
#endif
+ fmt2 = strjoina("selinux: ", fmt);
+
va_start(ap, fmt);
- log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
+ log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, ap);
va_end(ap);
return 0;
diff --git a/src/core/service.c b/src/core/service.c
index 58084e2f82..b46dd8bcdd 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -3195,6 +3195,22 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
+static int service_main_pid(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return s->main_pid;
+}
+
+static int service_control_pid(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -3303,6 +3319,9 @@ const UnitVTable service_vtable = {
.notify_cgroup_empty = service_notify_cgroup_empty_event,
.notify_message = service_notify_message,
+ .main_pid = service_main_pid,
+ .control_pid = service_control_pid,
+
.bus_name_owner_change = service_bus_name_owner_change,
.bus_vtable = bus_service_vtable,
diff --git a/src/core/service.h b/src/core/service.h
index a5ced215e4..cd9e41646e 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -80,7 +80,7 @@ typedef enum NotifyState {
typedef enum ServiceResult {
SERVICE_SUCCESS,
- SERVICE_FAILURE_RESOURCES,
+ SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */
SERVICE_FAILURE_TIMEOUT,
SERVICE_FAILURE_EXIT_CODE,
SERVICE_FAILURE_SIGNAL,
diff --git a/src/core/socket.c b/src/core/socket.c
index 65da0e3c5e..a9fff9c259 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2781,6 +2781,14 @@ char *socket_fdname(Socket *s) {
return UNIT(s)->id;
}
+static int socket_control_pid(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
+
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
[SOCKET_EXEC_START_CHOWN] = "StartChown",
@@ -2846,6 +2854,8 @@ const UnitVTable socket_vtable = {
.reset_failed = socket_reset_failed,
+ .control_pid = socket_control_pid,
+
.bus_vtable = bus_socket_vtable,
.bus_set_property = bus_socket_set_property,
.bus_commit_properties = bus_socket_commit_properties,
diff --git a/src/core/swap.c b/src/core/swap.c
index c6502eb821..d8802470d2 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -1426,6 +1426,14 @@ static bool swap_supported(void) {
return supported;
}
+static int swap_control_pid(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
+
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
@@ -1487,6 +1495,8 @@ const UnitVTable swap_vtable = {
.reset_failed = swap_reset_failed,
+ .control_pid = swap_control_pid,
+
.bus_vtable = bus_swap_vtable,
.bus_set_property = bus_swap_set_property,
.bus_commit_properties = bus_swap_commit_properties,
diff --git a/src/core/timer.c b/src/core/timer.c
index b286b714fa..f8f5f4b2e4 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -373,7 +373,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
* rather than the monotonic clock. */
ts_realtime = now(CLOCK_REALTIME);
- ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
+ ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC);
t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
LIST_FOREACH(value, v, t->values) {
diff --git a/src/core/unit.c b/src/core/unit.c
index c60ae2be9d..cb79c7c6b1 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3600,7 +3600,7 @@ int unit_kill_context(
* cases. It doesn't work at all in
* containers, and outside of containers it
* can be confused easily by left-over
- * directories in the cgroup -- which however
+ * directories in the cgroup — which however
* should not exist in non-delegated units. On
* the unified hierarchy that's different,
* there we get proper events. Hence rely on
@@ -3801,3 +3801,21 @@ bool unit_is_pristine(Unit *u) {
u->job ||
u->merged_into);
}
+
+pid_t unit_control_pid(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->control_pid)
+ return UNIT_VTABLE(u)->control_pid(u);
+
+ return 0;
+}
+
+pid_t unit_main_pid(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->main_pid)
+ return UNIT_VTABLE(u)->main_pid(u);
+
+ return 0;
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index cfdac852a5..5909652976 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -390,6 +390,12 @@ struct UnitVTable {
/* Returns the next timeout of a unit */
int (*get_timeout)(Unit *u, usec_t *timeout);
+ /* Returns the main PID if there is any defined, or 0. */
+ pid_t (*main_pid)(Unit *u);
+
+ /* Returns the main PID if there is any defined, or 0. */
+ pid_t (*control_pid)(Unit *u);
+
/* This is called for each unit type and should be used to
* enumerate existing devices and load them. However,
* everything that is loaded here should still stay in
@@ -601,6 +607,9 @@ bool unit_type_supported(UnitType t);
bool unit_is_pristine(Unit *u);
+pid_t unit_control_pid(Unit *u);
+pid_t unit_main_pid(Unit *u);
+
static inline bool unit_supported(Unit *u) {
return unit_type_supported(u->type);
}
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 2bbb958861..41fc1993d5 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -224,6 +224,8 @@ static int fix_permissions(
const char *context[_CONTEXT_MAX],
uid_t uid) {
+ int r;
+
assert(fd >= 0);
assert(target);
assert(context);
@@ -236,18 +238,9 @@ static int fix_permissions(
if (fsync(fd) < 0)
return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename));
- if (filename) {
- if (rename(filename, target) < 0)
- return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
- } else {
- _cleanup_free_ char *proc_fd_path = NULL;
-
- if (asprintf(&proc_fd_path, "/proc/self/fd/%d", fd) < 0)
- return log_oom();
-
- if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
- return log_error_errno(errno, "Failed to create coredump %s: %m", target);
- }
+ r = link_tmpfile(fd, filename, target);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move coredump %s into place: %m", target);
return 0;
}
@@ -308,33 +301,6 @@ static int make_filename(const char *context[_CONTEXT_MAX], char **ret) {
return 0;
}
-static int open_coredump_tmpfile(const char *target, char **ret_filename) {
- _cleanup_free_ char *tmp = NULL;
- int fd;
- int r;
-
- assert(target);
- assert(ret_filename);
-
- fd = open("/var/lib/systemd/coredump", O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0640);
- if (fd < 0) {
- log_debug_errno(errno, "Failed to use O_TMPFILE: %m");
-
- r = tempfn_random(target, NULL, &tmp);
- if (r < 0)
- return log_error_errno(r, "Failed to determine temporary file name: %m");
-
- fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
- if (fd < 0)
- return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
- }
-
- *ret_filename = tmp;
- tmp = NULL;
-
- return fd;
-}
-
static int save_external_coredump(
const char *context[_CONTEXT_MAX],
int input_fd,
@@ -378,9 +344,9 @@ static int save_external_coredump(
mkdir_p_label("/var/lib/systemd/coredump", 0755);
- fd = open_coredump_tmpfile(fn, &tmp);
+ fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
if (fd < 0)
- return fd;
+ return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
r = copy_bytes(input_fd, fd, max_size, false);
if (r == -EFBIG) {
@@ -418,9 +384,11 @@ static int save_external_coredump(
goto uncompressed;
}
- fd_compressed = open_coredump_tmpfile(fn_compressed, &tmp_compressed);
- if (fd_compressed < 0)
+ fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
+ if (fd_compressed < 0) {
+ log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
goto uncompressed;
+ }
r = compress_stream(fd, fd_compressed, -1);
if (r < 0) {
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 60d897758b..4ad9184993 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -122,12 +122,14 @@ static int open_journal(RequestMeta *m) {
}
static int request_meta_ensure_tmp(RequestMeta *m) {
+ assert(m);
+
if (m->tmp)
rewind(m->tmp);
else {
int fd;
- fd = open_tmpfile("/tmp", O_RDWR|O_CLOEXEC);
+ fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC);
if (fd < 0)
return fd;
diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c
index 3864647eb7..9ba9ee3fc0 100644
--- a/src/journal-remote/journal-remote-parse.c
+++ b/src/journal-remote/journal-remote-parse.c
@@ -485,7 +485,7 @@ int process_source(RemoteSource *source, bool compress, bool seal) {
}
target = source->size;
- while (target > 16 * LINE_CHUNK && remain < target / 2)
+ while (target > 16 * LINE_CHUNK && source->filled < target / 2)
target /= 2;
if (target < source->size) {
char *tmp;
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index bed825cdc3..35f4abab1d 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -37,6 +37,7 @@
#include "journal-file.h"
#include "lookup3.h"
#include "parse-util.h"
+#include "path-util.h"
#include "random-util.h"
#include "sd-event.h"
#include "set.h"
@@ -119,7 +120,7 @@ static void journal_file_set_offline_internal(JournalFile *f) {
if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING))
continue;
- f->header->state = STATE_OFFLINE;
+ f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE;
(void) fsync(f->fd);
break;
@@ -217,8 +218,10 @@ int journal_file_set_offline(JournalFile *f, bool wait) {
if (!(f->fd >= 0 && f->header))
return -EINVAL;
- if (f->header->state != STATE_ONLINE)
- return 0;
+ /* An offlining journal is implicitly online and may modify f->header->state,
+ * we must also join any potentially lingering offline thread when not online. */
+ if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE)
+ return journal_file_set_offline_thread_join(f);
/* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */
restarted = journal_file_set_offline_try_restart(f);
@@ -362,7 +365,8 @@ JournalFile* journal_file_close(JournalFile *f) {
(void) btrfs_defrag_fd(f->fd);
}
- safe_close(f->fd);
+ if (f->close_fd)
+ safe_close(f->fd);
free(f->path);
mmap_cache_unref(f->mmap);
@@ -703,7 +707,11 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
/* Objects may only be located at multiple of 64 bit */
if (!VALID64(offset))
- return -EFAULT;
+ return -EBADMSG;
+
+ /* Object may not be located in the file header */
+ if (offset < le64toh(f->header->header_size))
+ return -EBADMSG;
r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t);
if (r < 0)
@@ -1976,9 +1984,14 @@ static int generic_array_bisect(
i = right - 1;
lp = p = le64toh(array->entry_array.items[i]);
if (p <= 0)
- return -EBADMSG;
-
- r = test_object(f, p, needle);
+ r = -EBADMSG;
+ else
+ r = test_object(f, p, needle);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)");
+ n = i;
+ continue;
+ }
if (r < 0)
return r;
@@ -2054,9 +2067,14 @@ static int generic_array_bisect(
p = le64toh(array->entry_array.items[i]);
if (p <= 0)
- return -EBADMSG;
-
- r = test_object(f, p, needle);
+ r = -EBADMSG;
+ else
+ r = test_object(f, p, needle);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)");
+ right = n = i;
+ continue;
+ }
if (r < 0)
return r;
@@ -2461,13 +2479,18 @@ int journal_file_next_entry(
le64toh(f->header->entry_array_offset),
i,
ret, &ofs);
+ if (r == -EBADMSG && direction == DIRECTION_DOWN) {
+ /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read,
+ * consider this the end of the journal file. */
+ log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file.");
+ return 0;
+ }
if (r <= 0)
return r;
if (p > 0 &&
(direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) {
- log_debug("%s: entry array corrupted at entry %"PRIu64,
- f->path, i);
+ log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i);
return -EBADMSG;
}
@@ -2806,11 +2829,11 @@ void journal_file_print_header(JournalFile *f) {
"Data Hash Table Size: %"PRIu64"\n"
"Field Hash Table Size: %"PRIu64"\n"
"Rotate Suggested: %s\n"
- "Head Sequential Number: %"PRIu64"\n"
- "Tail Sequential Number: %"PRIu64"\n"
- "Head Realtime Timestamp: %s\n"
- "Tail Realtime Timestamp: %s\n"
- "Tail Monotonic Timestamp: %s\n"
+ "Head Sequential Number: %"PRIu64" (%"PRIx64")\n"
+ "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n"
+ "Head Realtime Timestamp: %s (%"PRIx64")\n"
+ "Tail Realtime Timestamp: %s (%"PRIx64")\n"
+ "Tail Monotonic Timestamp: %s (%"PRIx64")\n"
"Objects: %"PRIu64"\n"
"Entry Objects: %"PRIu64"\n",
f->path,
@@ -2831,11 +2854,11 @@ void journal_file_print_header(JournalFile *f) {
le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
yes_no(journal_file_rotate_suggested(f, 0)),
- le64toh(f->header->head_entry_seqnum),
- le64toh(f->header->tail_entry_seqnum),
- format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
- format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
- format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC),
+ le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum),
+ le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum),
+ format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime),
+ format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime),
+ format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic),
le64toh(f->header->n_objects),
le64toh(f->header->n_entries));
@@ -2898,6 +2921,7 @@ static int journal_file_warn_btrfs(JournalFile *f) {
}
int journal_file_open(
+ int fd,
const char *fname,
int flags,
mode_t mode,
@@ -2914,22 +2938,24 @@ int journal_file_open(
void *h;
int r;
- assert(fname);
assert(ret);
+ assert(fd >= 0 || fname);
if ((flags & O_ACCMODE) != O_RDONLY &&
(flags & O_ACCMODE) != O_RDWR)
return -EINVAL;
- if (!endswith(fname, ".journal") &&
- !endswith(fname, ".journal~"))
- return -EINVAL;
+ if (fname) {
+ if (!endswith(fname, ".journal") &&
+ !endswith(fname, ".journal~"))
+ return -EINVAL;
+ }
f = new0(JournalFile, 1);
if (!f)
return -ENOMEM;
- f->fd = -1;
+ f->fd = fd;
f->mode = mode;
f->flags = flags;
@@ -2954,7 +2980,10 @@ int journal_file_open(
}
}
- f->path = strdup(fname);
+ if (fname)
+ f->path = strdup(fname);
+ else /* If we don't know the path, fill in something explanatory and vaguely useful */
+ asprintf(&f->path, "/proc/self/%i", fd);
if (!f->path) {
r = -ENOMEM;
goto fail;
@@ -2966,10 +2995,15 @@ int journal_file_open(
goto fail;
}
- f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
if (f->fd < 0) {
- r = -errno;
- goto fail;
+ f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
+ if (f->fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ /* fds we opened here by us should also be closed by us. */
+ f->close_fd = true;
}
r = journal_file_fstat(f);
@@ -3090,6 +3124,9 @@ int journal_file_open(
goto fail;
}
+ /* The file is opened now successfully, thus we take possesion of any passed in fd. */
+ f->close_fd = true;
+
*ret = f;
return 0;
@@ -3116,6 +3153,11 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred
if (!old_file->writable)
return -EINVAL;
+ /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
+ * rotation, since we don't know the actual path, and couldn't rename the file hence.*/
+ if (path_startswith(old_file->path, "/proc/self/fd"))
+ return -EINVAL;
+
if (!endswith(old_file->path, ".journal"))
return -EINVAL;
@@ -3135,14 +3177,20 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred
if (r < 0 && errno != ENOENT)
return -errno;
- old_file->header->state = STATE_ARCHIVED;
+ /* Set as archive so offlining commits w/state=STATE_ARCHIVED.
+ * Previously we would set old_file->header->state to STATE_ARCHIVED directly here,
+ * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which
+ * would result in the rotated journal never getting fsync() called before closing.
+ * Now we simply queue the archive state by setting an archive bit, leaving the state
+ * as STATE_ONLINE so proper offlining occurs. */
+ old_file->archive = true;
/* Currently, btrfs is not very good with out write patterns
* and fragments heavily. Let's defrag our journal files when
* we archive them */
old_file->defrag_on_close = true;
- r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file);
+ r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file);
if (deferred_closes &&
set_put(deferred_closes, old_file) >= 0)
@@ -3170,7 +3218,7 @@ int journal_file_open_reliably(
size_t l;
_cleanup_free_ char *p = NULL;
- r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
+ r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
if (!IN_SET(r,
-EBADMSG, /* corrupted */
-ENODATA, /* truncated */
@@ -3211,7 +3259,7 @@ int journal_file_open_reliably(
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
- return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
+ return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
}
int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 9ad6013359..564e1a8179 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -85,6 +85,8 @@ typedef struct JournalFile {
bool compress_lz4:1;
bool seal:1;
bool defrag_on_close:1;
+ bool close_fd:1;
+ bool archive:1;
bool tail_entry_monotonic_valid:1;
@@ -142,6 +144,7 @@ typedef struct JournalFile {
} JournalFile;
int journal_file_open(
+ int fd,
const char *fname,
int flags,
mode_t mode,
diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
index 7639325acf..34a48141f5 100644
--- a/src/journal/journal-internal.h
+++ b/src/journal/journal-internal.h
@@ -82,6 +82,8 @@ struct Directory {
};
struct sd_journal {
+ int toplevel_fd;
+
char *path;
char *prefix;
@@ -117,6 +119,7 @@ struct sd_journal {
bool on_network:1;
bool no_new_files:1;
+ bool no_inotify:1;
bool unique_file_lost:1; /* File we were iterating over got
removed, and there were no more
files, so sd_j_enumerate_unique
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index a79846146a..f0959b6237 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -316,7 +316,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
buffer_fd = memfd_new(NULL);
if (buffer_fd < 0) {
if (buffer_fd == -ENOSYS) {
- buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC);
+ buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
if (buffer_fd < 0)
return buffer_fd;
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index a1241c9bcf..26572ddd76 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -838,19 +838,19 @@ int journal_file_verify(
} else if (f->seal)
return -ENOKEY;
- data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+ data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
if (data_fd < 0) {
r = log_error_errno(data_fd, "Failed to create data file: %m");
goto fail;
}
- entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+ entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_fd < 0) {
r = log_error_errno(entry_fd, "Failed to create entry file: %m");
goto fail;
}
- entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+ entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_array_fd < 0) {
r = log_error_errno(entry_array_fd,
"Failed to create entry array file: %m");
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index fd2cb99410..f67c556783 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -95,11 +95,13 @@ static bool arg_boot = false;
static sd_id128_t arg_boot_id = {};
static int arg_boot_offset = 0;
static bool arg_dmesg = false;
+static bool arg_no_hostname = false;
static const char *arg_cursor = NULL;
static const char *arg_after_cursor = NULL;
static bool arg_show_cursor = false;
static const char *arg_directory = NULL;
static char **arg_file = NULL;
+static bool arg_file_stdin = false;
static int arg_priorities = 0xFF;
static const char *arg_verify_key = NULL;
#ifdef HAVE_GCRYPT
@@ -304,6 +306,7 @@ static void help(void) {
" -a --all Show all fields, including long and unprintable\n"
" -q --quiet Do not show info messages and privilege warning\n"
" --no-pager Do not pipe output into a pager\n"
+ " --no-hostname Suppress output of hostname field\n"
" -m --merge Show entries from all available journals\n"
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
@@ -370,6 +373,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VACUUM_SIZE,
ARG_VACUUM_FILES,
ARG_VACUUM_TIME,
+ ARG_NO_HOSTNAME,
};
static const struct option options[] = {
@@ -427,6 +431,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE },
{ "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
+ { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
{}
};
@@ -588,9 +593,17 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_FILE:
- r = glob_extend(&arg_file, optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to add paths: %m");
+ if (streq(optarg, "-"))
+ /* An undocumented feature: we can read journal files from STDIN. We don't document
+ * this though, since after all we only support this for mmap-able, seekable files, and
+ * not for example pipes which are probably the primary usecase for reading things from
+ * STDIN. To avoid confusion we hence don't document this feature. */
+ arg_file_stdin = true;
+ else {
+ r = glob_extend(&arg_file, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add paths: %m");
+ }
break;
case ARG_ROOT:
@@ -780,6 +793,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_LIST_FIELD_NAMES;
break;
+ case ARG_NO_HOSTNAME:
+ arg_no_hostname = true;
+ break;
+
case 'x':
arg_catalog = true;
break;
@@ -856,6 +873,18 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) {
+
+ /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user
+ * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice
+ * to users, and automatically turn --unit= into --user-unit= if combined with --user. */
+ r = strv_extend_strv(&arg_user_units, arg_system_units, true);
+ if (r < 0)
+ return -ENOMEM;
+
+ arg_system_units = strv_free(arg_system_units);
+ }
+
return 1;
}
@@ -980,18 +1009,18 @@ static void boot_id_free_all(BootId *l) {
}
}
-static int discover_next_boot(
- sd_journal *j,
- BootId **boot,
+static int discover_next_boot(sd_journal *j,
+ sd_id128_t previous_boot_id,
bool advance_older,
- bool read_realtime) {
+ BootId **ret) {
- int r;
- char match[9+32+1] = "_BOOT_ID=";
_cleanup_free_ BootId *next_boot = NULL;
+ char match[9+32+1] = "_BOOT_ID=";
+ sd_id128_t boot_id;
+ int r;
assert(j);
- assert(boot);
+ assert(ret);
/* We expect the journal to be on the last position of a boot
* (in relation to the direction we are going), so that the next
@@ -1004,29 +1033,40 @@ static int discover_next_boot(
* we can actually advance to a *different* boot. */
sd_journal_flush_matches(j);
- if (advance_older)
- r = sd_journal_previous(j);
- else
- r = sd_journal_next(j);
- if (r < 0)
- return r;
- else if (r == 0)
- return 0; /* End of journal, yay. */
+ do {
+ if (advance_older)
+ r = sd_journal_previous(j);
+ else
+ r = sd_journal_next(j);
+ if (r < 0)
+ return r;
+ else if (r == 0)
+ return 0; /* End of journal, yay. */
+
+ r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
+ if (r < 0)
+ return r;
+
+ /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
+ * normally, this will only require a single iteration, as we seeked to the last entry of the previous
+ * boot entry already. However, it might happen that the per-journal-field entry arrays are less
+ * complete than the main entry array, and hence might reference an entry that's not actually the last
+ * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
+ * speed things up, but let's not trust that it is complete, and hence, manually advance as
+ * necessary. */
+
+ } while (sd_id128_equal(boot_id, previous_boot_id));
next_boot = new0(BootId, 1);
if (!next_boot)
return -ENOMEM;
- r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id);
+ next_boot->id = boot_id;
+
+ r = sd_journal_get_realtime_usec(j, &next_boot->first);
if (r < 0)
return r;
- if (read_realtime) {
- r = sd_journal_get_realtime_usec(j, &next_boot->first);
- if (r < 0)
- return r;
- }
-
/* Now seek to the last occurrence of this boot ID. */
sd_id128_to_string(next_boot->id, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
@@ -1049,13 +1089,11 @@ static int discover_next_boot(
else if (r == 0)
return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
- if (read_realtime) {
- r = sd_journal_get_realtime_usec(j, &next_boot->last);
- if (r < 0)
- return r;
- }
+ r = sd_journal_get_realtime_usec(j, &next_boot->last);
+ if (r < 0)
+ return r;
- *boot = next_boot;
+ *ret = next_boot;
next_boot = NULL;
return 0;
@@ -1064,47 +1102,48 @@ static int discover_next_boot(
static int get_boots(
sd_journal *j,
BootId **boots,
- BootId *query_ref_boot,
+ sd_id128_t *query_ref_boot,
int ref_boot_offset) {
bool skip_once;
int r, count = 0;
BootId *head = NULL, *tail = NULL;
const bool advance_older = query_ref_boot && ref_boot_offset <= 0;
+ sd_id128_t previous_boot_id;
assert(j);
/* Adjust for the asymmetry that offset 0 is
* the last (and current) boot, while 1 is considered the
* (chronological) first boot in the journal. */
- skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0;
+ skip_once = query_ref_boot && sd_id128_is_null(*query_ref_boot) && ref_boot_offset < 0;
/* Advance to the earliest/latest occurrence of our reference
* boot ID (taking our lookup direction into account), so that
* discover_next_boot() can do its job.
* If no reference is given, the journal head/tail will do,
* they're "virtual" boots after all. */
- if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) {
+ if (query_ref_boot && !sd_id128_is_null(*query_ref_boot)) {
char match[9+32+1] = "_BOOT_ID=";
sd_journal_flush_matches(j);
- sd_id128_to_string(query_ref_boot->id, match + 9);
+ sd_id128_to_string(*query_ref_boot, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0)
return r;
if (advance_older)
- r = sd_journal_seek_head(j);
+ r = sd_journal_seek_head(j); /* seek to oldest */
else
- r = sd_journal_seek_tail(j);
+ r = sd_journal_seek_tail(j); /* seek to newest */
if (r < 0)
return r;
if (advance_older)
- r = sd_journal_next(j);
+ r = sd_journal_next(j); /* read the oldest entry */
else
- r = sd_journal_previous(j);
+ r = sd_journal_previous(j); /* read the most recently added entry */
if (r < 0)
return r;
else if (r == 0)
@@ -1113,21 +1152,31 @@ static int get_boots(
count = 1;
goto finish;
}
+
+ /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot
+ * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at
+ * the following entry, which must then have an older/newer boot ID */
} else {
+
if (advance_older)
- r = sd_journal_seek_tail(j);
+ r = sd_journal_seek_tail(j); /* seek to newest */
else
- r = sd_journal_seek_head(j);
+ r = sd_journal_seek_head(j); /* seek to oldest */
if (r < 0)
return r;
- /* No sd_journal_next/previous here. */
+ /* No sd_journal_next()/_previous() here.
+ *
+ * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
+ * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
+ * entry we have. */
}
+ previous_boot_id = SD_ID128_NULL;
for (;;) {
_cleanup_free_ BootId *current = NULL;
- r = discover_next_boot(j, &current, advance_older, !query_ref_boot);
+ r = discover_next_boot(j, previous_boot_id, advance_older, &current);
if (r < 0) {
boot_id_free_all(head);
return r;
@@ -1136,6 +1185,8 @@ static int get_boots(
if (!current)
break;
+ previous_boot_id = current->id;
+
if (query_ref_boot) {
if (!skip_once)
ref_boot_offset += advance_older ? 1 : -1;
@@ -1143,7 +1194,7 @@ static int get_boots(
if (ref_boot_offset == 0) {
count = 1;
- query_ref_boot->id = current->id;
+ *query_ref_boot = current->id;
break;
}
} else {
@@ -1199,8 +1250,8 @@ static int list_boots(sd_journal *j) {
static int add_boot(sd_journal *j) {
char match[9+32+1] = "_BOOT_ID=";
+ sd_id128_t ref_boot_id;
int r;
- BootId ref_boot_id = {};
assert(j);
@@ -1210,7 +1261,7 @@ static int add_boot(sd_journal *j) {
if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL))
return add_match_this_boot(j, arg_machine);
- ref_boot_id.id = arg_boot_id;
+ ref_boot_id = arg_boot_id;
r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset);
assert(r <= 1);
if (r <= 0) {
@@ -1226,7 +1277,7 @@ static int add_boot(sd_journal *j) {
return r == 0 ? -ENODATA : r;
}
- sd_id128_to_string(ref_boot_id.id, match + 9);
+ sd_id128_to_string(ref_boot_id, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0)
@@ -1839,7 +1890,7 @@ static int access_check(sd_journal *j) {
break;
default:
- log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path);
+ log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path);
break;
}
}
@@ -2095,11 +2146,61 @@ int main(int argc, char *argv[]) {
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
- else if (arg_file)
+ else if (arg_file_stdin) {
+ int ifd = STDIN_FILENO;
+ r = sd_journal_open_files_fd(&j, &ifd, 1, 0);
+ } else if (arg_file)
r = sd_journal_open_files(&j, (const char**) arg_file, 0);
- else if (arg_machine)
- r = sd_journal_open_container(&j, arg_machine, 0);
- else
+ else if (arg_machine) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int fd;
+
+ if (geteuid() != 0) {
+ /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
+ * the container, thus we need root privileges to override them. */
+ log_error("Using the --machine= switch requires root privileges.");
+ r = -EPERM;
+ goto finish;
+ }
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open system bus: %m");
+ goto finish;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "OpenMachineRootDirectory",
+ &error,
+ &reply,
+ "s", arg_machine);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r));
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "h", &fd);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto finish;
+ }
+
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0) {
+ r = log_error_errno(errno, "Failed to duplicate file descriptor: %m");
+ goto finish;
+ }
+
+ r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT);
+ if (r < 0)
+ safe_close(fd);
+ } else
r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0) {
log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
@@ -2275,6 +2376,10 @@ int main(int argc, char *argv[]) {
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
r = sd_journal_get_fd(j);
+ if (r == -EMEDIUMTYPE) {
+ log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN.");
+ goto finish;
+ }
if (r < 0) {
log_error_errno(r, "Failed to get journal fd: %m");
goto finish;
@@ -2444,7 +2549,8 @@ int main(int argc, char *argv[]) {
arg_full * OUTPUT_FULL_WIDTH |
colors_enabled() * OUTPUT_COLOR |
arg_catalog * OUTPUT_CATALOG |
- arg_utc * OUTPUT_UTC;
+ arg_utc * OUTPUT_UTC |
+ arg_no_hostname * OUTPUT_NO_HOSTNAME;
r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
need_seek = true;
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 3d8f05996b..a445291a5e 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -206,7 +206,7 @@ void server_process_native_message(
allow_object_pid(ucred)) {
char buf[DECIMAL_STR_MAX(pid_t)];
memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
- char_array_0(buf);
+ buf[l-strlen("OBJECT_PID=")] = '\0';
/* ignore error */
parse_pid(buf, &object_pid);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index b1d1bf9e14..e14d0ad980 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -253,7 +253,7 @@ static int open_journal(
if (reliably)
r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
else
- r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
+ r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f);
if (r < 0)
return r;
@@ -1660,7 +1660,7 @@ static int server_connect_notify(Server *s) {
it. Specifically: given that PID 1 might block on
dbus-daemon during IPC, and dbus-daemon is logging to us,
and might hence block on us, we might end up in a deadlock
- if we block on sending PID 1 notification messages -- by
+ if we block on sending PID 1 notification messages — by
generating a full blocking circle. To avoid this, let's
create a non-blocking socket, and connect it to the
notification socket, and then wait for POLLOUT before we
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 3c21d4129e..27c1dd346f 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -1233,14 +1233,37 @@ static bool file_type_wanted(int flags, const char *filename) {
return false;
}
-static int add_any_file(sd_journal *j, const char *path) {
+static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) {
+ assert(j);
+ assert(path);
+ assert(prefix);
+
+ if (j->toplevel_fd >= 0)
+ return false;
+
+ return path_startswith(path, prefix);
+}
+
+static const char *skip_slash(const char *p) {
+
+ if (!p)
+ return NULL;
+
+ while (*p == '/')
+ p++;
+
+ return p;
+}
+
+static int add_any_file(sd_journal *j, int fd, const char *path) {
JournalFile *f = NULL;
+ bool close_fd = false;
int r, k;
assert(j);
- assert(path);
+ assert(fd >= 0 || path);
- if (ordered_hashmap_get(j->files, path))
+ if (path && ordered_hashmap_get(j->files, path))
return 0;
if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
@@ -1249,8 +1272,24 @@ static int add_any_file(sd_journal *j, const char *path) {
goto fail;
}
- r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
+ if (fd < 0 && j->toplevel_fd >= 0) {
+
+ /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative,
+ * explicitly, since otherwise openat() ignores the first argument.) */
+
+ fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
+ goto fail;
+ }
+
+ close_fd = true;
+ }
+
+ r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
if (r < 0) {
+ if (close_fd)
+ safe_close(fd);
log_debug_errno(r, "Failed to open journal file %s: %m", path);
goto fail;
}
@@ -1259,10 +1298,16 @@ static int add_any_file(sd_journal *j, const char *path) {
r = ordered_hashmap_put(j->files, f->path, f);
if (r < 0) {
+ f->close_fd = close_fd;
(void) journal_file_close(f);
goto fail;
}
+ if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
+ j->has_runtime_files = true;
+ else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
+ j->has_persistent_files = true;
+
log_debug("File %s added.", f->path);
check_network(j, f->fd);
@@ -1286,18 +1331,14 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
assert(prefix);
assert(filename);
- if (j->no_new_files ||
- !file_type_wanted(j->flags, filename))
+ if (j->no_new_files)
+ return 0;
+
+ if (!file_type_wanted(j->flags, filename))
return 0;
path = strjoina(prefix, "/", filename);
-
- if (!j->has_runtime_files && path_startswith(path, "/run/log/journal"))
- j->has_runtime_files = true;
- else if (!j->has_persistent_files && path_startswith(path, "/var/log/journal"))
- j->has_persistent_files = true;
-
- return add_any_file(j, path);
+ return add_any_file(j, -1, path);
}
static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
@@ -1373,21 +1414,33 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
assert(j);
assert(prefix);
- assert(dirname);
-
- log_debug("Considering %s/%s.", prefix, dirname);
- if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
- !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run")))
- return 0;
+ /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
+ * and reenumerates directory contents */
- path = strjoin(prefix, "/", dirname, NULL);
+ if (dirname)
+ path = strjoin(prefix, "/", dirname, NULL);
+ else
+ path = strdup(prefix);
if (!path) {
r = -ENOMEM;
goto fail;
}
- d = opendir(path);
+ log_debug("Considering directory %s.", path);
+
+ /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
+ if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
+ !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
+ return 0;
+
+
+ if (j->toplevel_fd < 0)
+ d = opendir(path);
+ else
+ /* Open the specified directory relative to the the toplevel fd. Enforce that the path specified is
+ * relative, by dropping the initial slash */
+ d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
if (!d) {
r = log_debug_errno(errno, "Failed to open directory %s: %m", path);
goto fail;
@@ -1419,17 +1472,18 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
return 0;
if (m->wd <= 0 && j->inotify_fd >= 0) {
+ /* Watch this directory, if it not being watched yet. */
- m->wd = inotify_add_watch(j->inotify_fd, m->path,
- IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
- IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
- IN_ONLYDIR);
+ m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d),
+ IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
+ IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
+ IN_ONLYDIR);
if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
inotify_rm_watch(j->inotify_fd, m->wd);
}
- FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
+ FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) {
if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~"))
@@ -1441,7 +1495,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
return 0;
fail:
- k = journal_put_error(j, r, path ?: dirname);
+ k = journal_put_error(j, r, path ?: prefix);
if (k < 0)
return k;
@@ -1449,28 +1503,62 @@ fail:
}
static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
+
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
Directory *m;
int r, k;
assert(j);
- assert(p);
- if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
- !path_startswith(p, "/run"))
- return -EINVAL;
+ /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
+ * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
+ * populate the set, as well as to update it later. */
- if (j->prefix)
- p = strjoina(j->prefix, p);
+ if (p) {
+ /* If there's a path specified, use it. */
- d = opendir(p);
- if (!d) {
- if (errno == ENOENT && missing_ok)
- return 0;
+ if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
+ !path_has_prefix(j, p, "/run"))
+ return -EINVAL;
- r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
- goto fail;
+ if (j->prefix)
+ p = strjoina(j->prefix, p);
+
+ if (j->toplevel_fd < 0)
+ d = opendir(p);
+ else
+ d = xopendirat(j->toplevel_fd, skip_slash(p), 0);
+
+ if (!d) {
+ if (errno == ENOENT && missing_ok)
+ return 0;
+
+ r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
+ goto fail;
+ }
+ } else {
+ int dfd;
+
+ /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
+ * opendir() will take possession of the fd, and close it, which we don't want. */
+
+ p = "."; /* store this as "." in the directories hashmap */
+
+ dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3);
+ if (dfd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ d = fdopendir(dfd);
+ if (!d) {
+ r = -errno;
+ safe_close(dfd);
+ goto fail;
+ }
+
+ rewinddir(d);
}
m = hashmap_get(j->directories_by_path, p);
@@ -1482,6 +1570,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
}
m->is_root = true;
+
m->path = strdup(p);
if (!m->path) {
free(m);
@@ -1505,7 +1594,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
if (m->wd <= 0 && j->inotify_fd >= 0) {
- m->wd = inotify_add_watch(j->inotify_fd, m->path,
+ m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d),
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
IN_ONLYDIR);
@@ -1516,7 +1605,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
if (j->no_new_files)
return 0;
- FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
+ FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) {
sd_id128_t id;
if (dirent_is_file_with_suffix(de, ".journal") ||
@@ -1585,8 +1674,7 @@ static int add_current_paths(sd_journal *j) {
assert(j);
assert(j->no_new_files);
- /* Simply adds all directories for files we have open as
- * "root" directories. We don't expect errors here, so we
+ /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
* treat them as fatal. */
ORDERED_HASHMAP_FOREACH(f, j->files, i) {
@@ -1597,7 +1685,7 @@ static int add_current_paths(sd_journal *j) {
if (!dir)
return -ENOMEM;
- r = add_root_directory(j, dir, true);
+ r = add_directory(j, dir, NULL);
if (r < 0)
return r;
}
@@ -1614,13 +1702,7 @@ static int allocate_inotify(sd_journal *j) {
return -errno;
}
- if (!j->directories_by_wd) {
- j->directories_by_wd = hashmap_new(NULL);
- if (!j->directories_by_wd)
- return -ENOMEM;
- }
-
- return 0;
+ return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
}
static sd_journal *journal_new(int flags, const char *path) {
@@ -1631,6 +1713,7 @@ static sd_journal *journal_new(int flags, const char *path) {
return NULL;
j->original_pid = getpid();
+ j->toplevel_fd = -1;
j->inotify_fd = -1;
j->flags = flags;
j->data_threshold = DEFAULT_DATA_THRESHOLD;
@@ -1684,6 +1767,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
char *p;
int r;
+ /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
+ * combination with sd_journal_open_directory_fd(). */
+
assert_return(machine, -EINVAL);
assert_return(ret, -EINVAL);
assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
@@ -1726,13 +1812,16 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
assert_return(ret, -EINVAL);
assert_return(path, -EINVAL);
- assert_return(flags == 0, -EINVAL);
+ assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL);
j = journal_new(flags, path);
if (!j)
return -ENOMEM;
- r = add_root_directory(j, path, false);
+ if (flags & SD_JOURNAL_OS_ROOT)
+ r = add_search_paths(j);
+ else
+ r = add_root_directory(j, path, false);
if (r < 0)
goto fail;
@@ -1741,7 +1830,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
fail:
sd_journal_close(j);
-
return r;
}
@@ -1758,7 +1846,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
return -ENOMEM;
STRV_FOREACH(path, paths) {
- r = add_any_file(j, *path);
+ r = add_any_file(j, -1, *path);
if (r < 0)
goto fail;
}
@@ -1770,7 +1858,96 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
fail:
sd_journal_close(j);
+ return r;
+}
+
+_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
+ sd_journal *j;
+ struct stat st;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(fd >= 0, -EBADF);
+ assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -EBADFD;
+
+ j = journal_new(flags, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ j->toplevel_fd = fd;
+
+ if (flags & SD_JOURNAL_OS_ROOT)
+ r = add_search_paths(j);
+ else
+ r = add_root_directory(j, NULL, false);
+ if (r < 0)
+ goto fail;
+ *ret = j;
+ return 0;
+
+fail:
+ sd_journal_close(j);
+ return r;
+}
+
+_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
+ Iterator iterator;
+ JournalFile *f;
+ sd_journal *j;
+ unsigned i;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(n_fds > 0, -EBADF);
+ assert_return(flags == 0, -EINVAL);
+
+ j = journal_new(flags, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ for (i = 0; i < n_fds; i++) {
+ struct stat st;
+
+ if (fds[i] < 0) {
+ r = -EBADF;
+ goto fail;
+ }
+
+ if (fstat(fds[i], &st) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ r = -EBADFD;
+ goto fail;
+ }
+
+ r = add_any_file(j, fds[i], NULL);
+ if (r < 0)
+ goto fail;
+ }
+
+ j->no_new_files = true;
+ j->no_inotify = true;
+
+ *ret = j;
+ return 0;
+
+fail:
+ /* If we fail, make sure we don't take possession of the files we managed to make use of successfuly, and they
+ * remain open */
+ ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
+ f->close_fd = false;
+
+ sd_journal_close(j);
return r;
}
@@ -2097,6 +2274,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
+ if (j->no_inotify)
+ return -EMEDIUMTYPE;
+
if (j->inotify_fd >= 0)
return j->inotify_fd;
@@ -2104,10 +2284,14 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
if (r < 0)
return r;
+ log_debug("Reiterating files to get inotify watches established");
+
/* Iterate through all dirs again, to add them to the
* inotify */
if (j->no_new_files)
r = add_current_paths(j);
+ else if (j->toplevel_fd >= 0)
+ r = add_root_directory(j, NULL, false);
else if (j->path)
r = add_root_directory(j, j->path, true);
else
diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c
index 93dc0e0d81..ba8b20b228 100644
--- a/src/journal/test-journal-flush.c
+++ b/src/journal/test-journal-flush.c
@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(dn));
fn = strappend(dn, "/test.journal");
- r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal);
+ r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal);
assert_se(r >= 0);
r = sd_journal_open(&j, 0);
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index f887f43f0d..5e063f4d04 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -52,7 +52,7 @@ noreturn static void log_assert_errno(const char *text, int eno, const char *fil
static JournalFile *test_open(const char *name) {
JournalFile *f;
- assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f));
+ assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f));
return f;
}
@@ -216,7 +216,7 @@ static void test_sequence_numbers(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644,
+ assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644,
true, false, NULL, NULL, NULL, NULL, &one) == 0);
append_number(one, 1, &seqnum);
@@ -233,7 +233,7 @@ static void test_sequence_numbers(void) {
memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644,
+ assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644,
true, false, NULL, NULL, NULL, one, &two) == 0);
assert_se(two->header->state == STATE_ONLINE);
@@ -264,7 +264,7 @@ static void test_sequence_numbers(void) {
/* restart server */
seqnum = 0;
- assert_se(journal_file_open("two.journal", O_RDWR, 0,
+ assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0,
true, false, NULL, NULL, NULL, NULL, &two) == 0);
assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index 839ea5a9a5..7e5a980719 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -92,9 +92,9 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0);
- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0);
- assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0);
+ assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0);
+ assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0);
+ assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0);
for (i = 0; i < N_ENTRIES; i++) {
char *p, *q;
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index 6b4643cd25..3d2312fc55 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -55,7 +55,7 @@ static int raw_verify(const char *fn, const char *verification_key) {
JournalFile *f;
int r;
- r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f);
+ r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f);
if (r < 0)
return r;
@@ -88,7 +88,7 @@ int main(int argc, char *argv[]) {
log_info("Generating...");
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
for (n = 0; n < N_ENTRIES; n++) {
struct iovec iovec;
@@ -111,7 +111,7 @@ int main(int argc, char *argv[]) {
log_info("Verifying...");
- assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0);
/* journal_file_print_header(f); */
journal_file_dump(f);
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index ea685af782..2543d64b5b 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -42,7 +42,7 @@ static void test_non_empty(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0);
dual_timestamp_get(&ts);
@@ -131,13 +131,13 @@ static void test_empty(void) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0);
+ assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0);
- assert_se(journal_file_open("test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0);
+ assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0);
- assert_se(journal_file_open("test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0);
+ assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0);
- assert_se(journal_file_open("test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0);
+ assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0);
journal_file_print_header(f1);
puts("");
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 4ab637b686..0b3a1708dc 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -489,3 +489,9 @@ global:
sd_journal_enumerate_fields;
sd_journal_restart_fields;
} LIBSYSTEMD_227;
+
+LIBSYSTEMD_230 {
+global:
+ sd_journal_open_directory_fd;
+ sd_journal_open_files_fd;
+} LIBSYSTEMD_229;
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index e787cb69d3..b1c3d5f228 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -1212,19 +1212,19 @@ int device_get_id_filename(sd_device *device, const char **ret) {
if (major(devnum) > 0) {
assert(subsystem);
- /* use dev_t -- b259:131072, c254:0 */
+ /* use dev_t — b259:131072, c254:0 */
r = asprintf(&id, "%c%u:%u",
streq(subsystem, "block") ? 'b' : 'c',
major(devnum), minor(devnum));
if (r < 0)
return -ENOMEM;
} else if (ifindex > 0) {
- /* use netdev ifindex -- n3 */
+ /* use netdev ifindex — n3 */
r = asprintf(&id, "n%u", ifindex);
if (r < 0)
return -ENOMEM;
} else {
- /* use $subsys:$sysname -- pci:0000:00:1f.2
+ /* use $subsys:$sysname — pci:0000:00:1f.2
* sysname() has '!' translated, get it from devpath
*/
const char *sysname;
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 79b98c293c..7ba6527f63 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -1072,6 +1072,10 @@ _public_ int sd_event_add_time(
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
+ if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) &&
+ !clock_boottime_supported())
+ return -EOPNOTSUPP;
+
if (!callback)
callback = time_exit_callback;
@@ -2527,7 +2531,8 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
}
dual_timestamp_get(&e->timestamp);
- e->timestamp_boottime = now(clock_boottime_or_monotonic());
+ if (clock_boottime_supported())
+ e->timestamp_boottime = now(CLOCK_BOOTTIME);
for (i = 0; i < m; i++) {
@@ -2761,6 +2766,9 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
CLOCK_BOOTTIME,
CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
+ if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported())
+ return -EOPNOTSUPP;
+
if (!dual_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran
* before and thus have no cached time. */
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index fd31588b8f..289114490c 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -270,8 +270,10 @@ static void test_sd_event_now(void) {
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0);
assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+ if (clock_boottime_supported()) {
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+ }
assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
@@ -280,8 +282,10 @@ static void test_sd_event_now(void) {
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0);
assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
- assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+ if (clock_boottime_supported()) {
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+ }
assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
}
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index 255526bf32..f251536a89 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -402,7 +402,6 @@ int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret,
int r;
assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL);
- assert_return(nlmsg_type != RTM_DELLINK || index > 0, -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nlmsg_type);
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index 33ef6fc0f7..1be1a7f8a7 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -63,7 +63,7 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c
return 0;
}
- printf("Host: %s -- Serv: %s\n", strna(host), strna(serv));
+ printf("Host: %s — Serv: %s\n", strna(host), strna(serv));
return 0;
}
diff --git a/src/login/.gitignore b/src/login/.gitignore
index 39088ec252..3a8ba497c1 100644
--- a/src/login/.gitignore
+++ b/src/login/.gitignore
@@ -1,4 +1,5 @@
/logind-gperf.c
+/logind.conf
/org.freedesktop.login1.policy
/71-seat.rules
/73-seat-late.rules
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 01f6fa5db0..1c75565636 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -27,6 +27,7 @@
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -227,18 +228,15 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *cgroup;
- int r;
unsigned c;
+ int r;
assert(bus);
assert(unit);
- if (arg_transport != BUS_TRANSPORT_LOCAL)
- return 0;
-
path = unit_dbus_path_from_name(unit);
if (!path)
- return -ENOMEM;
+ return log_oom();
r = sd_bus_get_property(
bus,
@@ -246,27 +244,40 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
path,
interface,
"ControlGroup",
- &error, &reply, "s");
+ &error,
+ &reply,
+ "s");
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &cgroup);
if (r < 0)
- return r;
+ return bus_log_parse_error(r);
if (isempty(cgroup))
return 0;
- if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
- return 0;
-
c = columns();
if (c > 18)
c -= 18;
else
c = 0;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
+ r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
+ if (r == -EBADR) {
+
+ if (arg_transport == BUS_TRANSPORT_REMOTE)
+ return 0;
+
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
+
+ if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
+ return 0;
+
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
+
return 0;
}
@@ -293,6 +304,7 @@ typedef struct SessionStatusInfo {
typedef struct UserStatusInfo {
uid_t uid;
+ bool linger;
char *name;
struct dual_timestamp timestamp;
char *state;
@@ -551,6 +563,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(UserStatusInfo, name) },
+ { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) },
{ "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
{ "State", "s", NULL, offsetof(UserStatusInfo, state) },
{ "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
@@ -595,16 +608,16 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
char **l;
printf("\tSessions:");
- STRV_FOREACH(l, i.sessions) {
- if (streq_ptr(*l, i.display))
- printf(" *%s", *l);
- else
- printf(" %s", *l);
- }
+ STRV_FOREACH(l, i.sessions)
+ printf(" %s%s",
+ streq_ptr(*l, i.display) ? "*" : "",
+ *l);
printf("\n");
}
+ printf("\t Linger: %s\n", yes_no(i.linger));
+
if (i.slice) {
printf("\t Unit: %s\n", i.slice);
show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 8bdb3a9a38..cbf8d757fe 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -364,16 +364,16 @@ bool manager_shall_kill(Manager *m, const char *user) {
assert(m);
assert(user);
- if (!m->kill_user_processes)
+ if (!m->kill_exclude_users && streq(user, "root"))
return false;
if (strv_contains(m->kill_exclude_users, user))
return false;
- if (strv_isempty(m->kill_only_users))
- return true;
+ if (!strv_isempty(m->kill_only_users))
+ return strv_contains(m->kill_only_users, user);
- return strv_contains(m->kill_only_users, user);
+ return m->kill_user_processes;
}
static int vt_is_busy(unsigned int vtnr) {
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 1d3133ee25..a281f99a34 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1077,11 +1077,11 @@ static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus
static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *cc = NULL;
Manager *m = userdata;
- int b, r;
+ int r, b, interactive;
struct passwd *pw;
const char *path;
uint32_t uid;
- int interactive;
+ bool self = false;
assert(message);
assert(m);
@@ -1102,6 +1102,8 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
+ self = true;
+
} else if (!uid_is_valid(uid))
return -EINVAL;
@@ -1113,7 +1115,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-user-linger",
+ self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger",
NULL,
interactive,
UID_INVALID,
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 676fbc15a3..a8b1d5943d 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -797,7 +797,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
/* Graphical sessions should really implement a real
* idle hint logic */
- if (s->display)
+ if (SESSION_TYPE_IS_GRAPHICAL(s->type))
goto dont_know;
/* For sessions with an explicitly configured tty, let's check
diff --git a/src/login/logind.c b/src/login/logind.c
index d5f6757bd3..a48e2fc61e 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -41,17 +41,7 @@
static void manager_free(Manager *m);
-static Manager *manager_new(void) {
- Manager *m;
- int r;
-
- m = new0(Manager, 1);
- if (!m)
- return NULL;
-
- m->console_active_fd = -1;
- m->reserve_vt_fd = -1;
-
+static void manager_reset_config(Manager *m) {
m->n_autovts = 6;
m->reserve_vt = 6;
m->remove_ipc = true;
@@ -61,16 +51,38 @@ static Manager *manager_new(void) {
m->handle_hibernate_key = HANDLE_HIBERNATE;
m->handle_lid_switch = HANDLE_SUSPEND;
m->handle_lid_switch_docked = HANDLE_IGNORE;
+ m->power_key_ignore_inhibited = false;
+ m->suspend_key_ignore_inhibited = false;
+ m->hibernate_key_ignore_inhibited = false;
m->lid_switch_ignore_inhibited = true;
+
m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
m->idle_action_usec = 30 * USEC_PER_MINUTE;
m->idle_action = HANDLE_IGNORE;
- m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
m->user_tasks_max = UINT64_C(12288);
+ m->kill_user_processes = KILL_USER_PROCESSES;
+
+ m->kill_only_users = strv_free(m->kill_only_users);
+ m->kill_exclude_users = strv_free(m->kill_exclude_users);
+}
+
+static Manager *manager_new(void) {
+ Manager *m;
+ int r;
+
+ m = new0(Manager, 1);
+ if (!m)
+ return NULL;
+
+ m->console_active_fd = -1;
+ m->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);
@@ -84,10 +96,6 @@ static Manager *manager_new(void) {
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
goto fail;
- m->kill_exclude_users = strv_new("root", NULL);
- if (!m->kill_exclude_users)
- goto fail;
-
m->udev = udev_new();
if (!m->udev)
goto fail;
@@ -98,6 +106,8 @@ static Manager *manager_new(void) {
sd_event_set_watchdog(m->event, true);
+ manager_reset_config(m);
+
return m;
fail:
@@ -986,6 +996,30 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
return 0;
}
+static int manager_parse_config_file(Manager *m) {
+ assert(m);
+
+ return config_parse_many(PKGSYSCONFDIR "/logind.conf",
+ CONF_PATHS_NULSTR("systemd/logind.conf.d"),
+ "Login\0",
+ config_item_perf_lookup, logind_gperf_lookup,
+ false, m);
+}
+
+static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = userdata;
+ int r;
+
+ manager_reset_config(m);
+ r = manager_parse_config_file(m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse config file, using defaults: %m");
+ else
+ log_info("Config file reloaded.");
+
+ return 0;
+}
+
static int manager_startup(Manager *m) {
int r;
Seat *seat;
@@ -997,6 +1031,12 @@ static int manager_startup(Manager *m) {
assert(m);
+ assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGHUP, -1) >= 0);
+
+ r = sd_event_add_signal(m->event, NULL, SIGHUP, manager_dispatch_reload_signal, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register SIGHUP handler: %m");
+
/* Connect to console */
r = manager_connect_console(m);
if (r < 0)
@@ -1099,16 +1139,6 @@ static int manager_run(Manager *m) {
}
}
-static int manager_parse_config_file(Manager *m) {
- assert(m);
-
- return config_parse_many(PKGSYSCONFDIR "/logind.conf",
- CONF_PATHS_NULSTR("systemd/logind.conf.d"),
- "Login\0",
- config_item_perf_lookup, logind_gperf_lookup,
- false, m);
-}
-
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r;
diff --git a/src/login/logind.conf b/src/login/logind.conf.in
index 6095e482ac..3c96def45d 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf.in
@@ -14,7 +14,7 @@
[Login]
#NAutoVTs=6
#ReserveVT=6
-#KillUserProcesses=no
+#KillUserProcesses=@KILL_USER_PROCESSES@
#KillOnlyUsers=
#KillExcludeUsers=root
#InhibitDelayMaxSec=5
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index 23326bb79f..1fa6441629 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -111,6 +111,14 @@
</defaults>
</action>
+ <action id="org.freedesktop.login1.set-self-linger">
+ <_description>Allow non-logged-in user to run programs</_description>
+ <_message>Explicit request is required to run programs as a non-logged-in user.</_message>
+ <defaults>
+ <allow_any>yes</allow_any>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.login1.set-user-linger">
<_description>Allow non-logged-in users to run programs</_description>
<_message>Authentication is required to run programs as a non-logged-in user.</_message>
diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4
index 7933508f2b..f188a8e548 100644
--- a/src/login/systemd-user.m4
+++ b/src/login/systemd-user.m4
@@ -8,4 +8,5 @@ m4_ifdef(`HAVE_SELINUX',
session required pam_selinux.so close
session required pam_selinux.so nottys open
)m4_dnl
+session required pam_loginuid.so
session include system-auth
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 73f5112c4d..b764bc43a0 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -137,6 +137,8 @@ int bus_image_method_clone(
return 1; /* Will call us back */
r = image_clone(image, new_name, read_only);
+ if (r == -EOPNOTSUPP)
+ return sd_bus_reply_method_errnof(message, r, "Image cloning is currently only supported on btrfs file systems.");
if (r < 0)
return r;
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index ab54d9e934..5121bfdd18 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -1274,6 +1274,97 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
return 1;
}
+int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_close_ int fd = -1;
+ Machine *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.machine1.manage-machines",
+ NULL,
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ break;
+
+ case MACHINE_CONTAINER: {
+ _cleanup_close_ int mntns_fd = -1, root_fd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ siginfo_t si;
+ pid_t child;
+
+ r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ _cleanup_close_ int dfd = -1;
+
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dfd < 0)
+ _exit(EXIT_FAILURE);
+
+ r = send_one_fd(pair[1], dfd, 0);
+ dfd = safe_close(dfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+
+ fd = receive_one_fd(pair[0], MSG_DONTWAIT);
+ if (fd < 0)
+ return fd;
+
+ break;
+ }
+
+ default:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
+ }
+
+ return sd_bus_reply_method_return(message, "h", fd);
+}
+
const sd_bus_vtable machine_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1297,6 +1388,7 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index 3a8162b171..241b23c7ec 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -38,6 +38,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error);
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 35177aa29e..5a68c4ceb2 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -33,6 +33,7 @@
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -331,8 +332,8 @@ static int list_images(int argc, char *argv[], void *userdata) {
}
static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_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;
const char *cgroup;
int r;
@@ -341,9 +342,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
assert(bus);
assert(unit);
- if (arg_transport == BUS_TRANSPORT_REMOTE)
- return 0;
-
path = unit_dbus_path_from_name(unit);
if (!path)
return log_oom();
@@ -357,16 +355,14 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
&error,
&reply,
"s");
- if (r < 0) {
- log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &cgroup);
if (r < 0)
return bus_log_parse_error(r);
- if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
+ if (isempty(cgroup))
return 0;
c = columns();
@@ -375,7 +371,21 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
else
c = 0;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
+ r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
+ if (r == -EBADR) {
+
+ if (arg_transport == BUS_TRANSPORT_REMOTE)
+ return 0;
+
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
+
+ if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
+ return 0;
+
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
+
return 0;
}
@@ -2402,7 +2412,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n"
- " --setenv=VAR=VALUE Add an environment variable for shell\n"
+ " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
@@ -2439,8 +2449,8 @@ static int help(int argc, char *argv[], void *userdata) {
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
" remove NAME... Remove an image\n"
- " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
- " clean Remove hidden (or all) images\n"
+ " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
+ " clean Remove hidden (or all) images\n\n"
"Image Transfer Commands:\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
@@ -2470,7 +2480,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FORCE,
ARG_FORMAT,
ARG_UID,
- ARG_SETENV,
};
static const struct option options[] = {
@@ -2496,16 +2505,38 @@ static int parse_argv(int argc, char *argv[]) {
{ "force", no_argument, NULL, ARG_FORCE },
{ "format", required_argument, NULL, ARG_FORMAT },
{ "uid", required_argument, NULL, ARG_UID },
- { "setenv", required_argument, NULL, ARG_SETENV },
+ { "setenv", required_argument, NULL, 'E' },
{}
};
+ bool reorder = false;
int c, r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
+ for (;;) {
+ const char * const option_string = "+hp:als:H:M:qn:o:";
+
+ c = getopt_long(argc, argv, option_string + reorder, options, NULL);
+ if (c < 0) {
+ /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
+ * for switches after the main verb. However, for "shell" we really don't want that, since we
+ * want that switches passed after that are passed to the program to execute, and not processed
+ * by us. To make this possible, we'll first invoke getopt_long() with reordering disabled
+ * (i.e. with the "+" prefix in the option string), and as soon as we hit the end (i.e. the
+ * verb) we check if that's "shell". If it is, we exit the loop, since we don't want any
+ * further options processed. However, if it is anything else, we process the same argument
+ * again, but this time allow reordering. */
+
+ if (!reorder && optind < argc && !streq(argv[optind], "shell")) {
+ reorder = true;
+ optind--;
+ continue;
+ }
+
+ break;
+ }
switch (c) {
@@ -2624,7 +2655,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_uid = optarg;
break;
- case ARG_SETENV:
+ case 'E':
if (!env_assignment_is_valid(optarg)) {
log_error("Environment assignment invalid: %s", optarg);
return -EINVAL;
@@ -2641,6 +2672,7 @@ static int parse_argv(int argc, char *argv[]) {
default:
assert_not_reached("Unhandled option");
}
+ }
return 1;
}
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index c9639c3cf2..31efa3695b 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -706,6 +706,26 @@ static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_e
return bus_machine_method_copy(message, machine, error);
}
+static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_open_root_directory(message, machine, error);
+}
+
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
@@ -1225,6 +1245,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 7f9a7268cc..429319da6b 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -67,16 +67,15 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
if (r < 0)
return r;
- address->network = network;
-
- LIST_APPEND(addresses, network->static_addresses, address);
-
if (section) {
address->section = section;
hashmap_put(network->addresses_by_section,
UINT_TO_PTR(address->section), address);
}
+ address->network = network;
+ LIST_APPEND(addresses, network->static_addresses, address);
+
*ret = address;
address = NULL;
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 0589ebf227..c5b61abc9e 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -57,6 +57,10 @@ static int link_set_dhcp_routes(Link *link) {
assert(link);
assert(link->dhcp_lease);
+ assert(link->network);
+
+ if (!link->network->dhcp_use_routes)
+ return 0;
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
if (r < 0 && r != -ENODATA)
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 9059a68fe3..5fc513bfda 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -42,6 +42,9 @@
static bool link_dhcp6_enabled(Link *link) {
assert(link);
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
@@ -90,6 +93,9 @@ static bool link_ipv4ll_enabled(Link *link) {
static bool link_ipv6ll_enabled(Link *link) {
assert(link);
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
@@ -99,6 +105,15 @@ static bool link_ipv6ll_enabled(Link *link) {
return link->network->link_local & ADDRESS_FAMILY_IPV6;
}
+static bool link_ipv6_enabled(Link *link) {
+ assert(link);
+
+ if (!socket_ipv6_is_supported())
+ return false;
+
+ return link_dhcp6_enabled(link) || link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network);
+}
+
static bool link_lldp_rx_enabled(Link *link) {
assert(link);
@@ -183,6 +198,9 @@ static bool link_proxy_arp_enabled(Link *link) {
static bool link_ipv6_accept_ra_enabled(Link *link) {
assert(link);
+ if (!socket_ipv6_is_supported())
+ return false;
+
if (link->flags & IFF_LOOPBACK)
return false;
@@ -205,6 +223,7 @@ static bool link_ipv6_accept_ra_enabled(Link *link) {
}
static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
+ assert(link);
if (!socket_ipv6_is_supported())
return _IPV6_PRIVACY_EXTENSIONS_INVALID;
@@ -218,6 +237,31 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) {
return link->network->ipv6_privacy_extensions;
}
+static int link_enable_ipv6(Link *link) {
+ const char *p = NULL;
+ bool disabled;
+ int r;
+
+ if (link->flags & IFF_LOOPBACK)
+ return 0;
+
+ disabled = !link_ipv6_enabled(link);
+
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6");
+
+ r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname);
+ else {
+ if (disabled)
+ log_link_info(link, "IPv6 disabled for interface: %m");
+ else
+ log_link_info(link, "IPv6 enabled for interface: %m");
+ }
+
+ return 0;
+}
+
void link_update_operstate(Link *link) {
LinkOperationalState operstate;
assert(link);
@@ -265,7 +309,6 @@ void link_update_operstate(Link *link) {
link->operstate = operstate;
link_send_changed(link, "OperationalState", NULL);
link_dirty(link);
- manager_dirty(link->manager);
}
}
@@ -533,8 +576,6 @@ static void link_set_state(Link *link, LinkState state) {
link->state = state;
link_send_changed(link, "AdministrativeState", NULL);
-
- return;
}
static void link_enter_unmanaged(Link *link) {
@@ -1415,7 +1456,7 @@ static int link_acquire_ipv6_conf(Link *link) {
return 0;
}
-static int link_acquire_conf(Link *link) {
+static int link_acquire_ipv4_conf(Link *link) {
int r;
assert(link);
@@ -1443,6 +1484,24 @@ static int link_acquire_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
}
+ return 0;
+}
+
+static int link_acquire_conf(Link *link) {
+ int r;
+
+ assert(link);
+
+ r = link_acquire_ipv4_conf(link);
+ if (r < 0)
+ return r;
+
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
+ r = link_acquire_ipv6_conf(link);
+ if (r < 0)
+ return r;
+ }
+
if (link_lldp_tx_enabled(link)) {
r = link_lldp_tx_start(link);
if (r < 0)
@@ -1510,7 +1569,21 @@ static int link_up(Link *link) {
return log_link_error_errno(link, r, "Could not set MAC address: %m");
}
+ /* If IPv6 not configured (no static IPv6 address and neither DHCPv6 nor IPv6LL is enabled)
+ for this interface then disable IPv6 else enable it. */
+ (void) link_enable_ipv6(link);
+
if (link->network->mtu) {
+ /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
+ on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */
+ if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) {
+
+ log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as "
+ "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m");
+
+ link->network->mtu = IPV6_MIN_MTU;
+ }
+
r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu);
if (r < 0)
return log_link_error_errno(link, r, "Could not set MTU: %m");
@@ -1520,7 +1593,7 @@ static int link_up(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
- if (socket_ipv6_is_supported()) {
+ if (link_ipv6_enabled(link)) {
/* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */
r = sd_netlink_message_open_container(req, AF_INET6);
if (r < 0)
@@ -2294,12 +2367,6 @@ static int link_configure(Link *link) {
r = link_acquire_conf(link);
if (r < 0)
return r;
-
- if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
- r = link_acquire_ipv6_conf(link);
- if (r < 0)
- return r;
- }
}
return link_enter_join_netdev(link);
@@ -2682,6 +2749,10 @@ static int link_carrier_gained(Link *link) {
link_enter_failed(link);
return r;
}
+
+ r = link_enter_set_addresses(link);
+ if (r < 0)
+ return r;
}
r = link_handle_bound_by_list(link);
@@ -3180,14 +3251,17 @@ void link_dirty(Link *link) {
assert(link);
+ /* mark manager dirty as link is dirty */
+ manager_dirty(link->manager);
+
r = set_ensure_allocated(&link->manager->dirty_links, NULL);
if (r < 0)
/* allocation errors are ignored */
return;
r = set_put(link->manager->dirty_links, link);
- if (r < 0)
- /* allocation errors are ignored */
+ if (r <= 0)
+ /* don't take another ref if the link was already dirty */
return;
link_ref(link);
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 1c7adf5180..07f8fb028f 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -399,6 +399,19 @@ int network_apply(Manager *manager, Network *network, Link *link) {
return 0;
}
+bool network_has_static_ipv6_addresses(Network *network) {
+ Address *address;
+
+ assert(network);
+
+ LIST_FOREACH(addresses, address, network->static_addresses) {
+ if (address->family == AF_INET6)
+ return true;
+ }
+
+ return false;
+}
+
int config_parse_netdev(const char *unit,
const char *filename,
unsigned line,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 3d44113b05..15417f4828 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -186,6 +186,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret);
int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
int network_apply(Manager *manager, Network *network, Link *link);
+bool network_has_static_ipv6_addresses(Network *network);
+
int config_parse_netdev(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);
int config_parse_domains(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);
int config_parse_tunnel(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);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index ab9b777d9a..bda2707e6d 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -68,15 +68,15 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
route->protocol = RTPROT_STATIC;
if (section) {
+ route->section = section;
+
r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route);
if (r < 0)
return r;
-
- route->section = section;
}
- LIST_PREPEND(routes, network->static_routes, route);
route->network = network;
+ LIST_PREPEND(routes, network->static_routes, route);
*ret = route;
route = NULL;
diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c
index ecbbe6c3c9..adbe09a5e1 100644
--- a/src/network/test-network-tables.c
+++ b/src/network/test-network-tables.c
@@ -9,7 +9,7 @@
int main(int argc, char **argv) {
test_table(bond_mode, NETDEV_BOND_MODE);
- /* test_table(link_state, LINK_STATE); -- not a reversible mapping */
+ /* test_table(link_state, LINK_STATE); — not a reversible mapping */
test_table(link_operstate, LINK_OPERSTATE);
test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN);
test_table(netdev_kind, NETDEV_KIND);
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index 116655cdd2..34e1310e29 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -16,7 +16,7 @@ struct ConfigPerfItem;
%includes
%%
Exec.Boot, config_parse_boot, 0, 0
-Exec.ProcessTwo, config_parse_pid2, 0, 0,
+Exec.ProcessTwo, config_parse_pid2, 0, 0
Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
Exec.User, config_parse_string, 0, offsetof(Settings, user)
@@ -26,11 +26,13 @@ Exec.KillSignal, config_parse_signal, 0, offsetof(Settings,
Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory)
+Exec.PrivateUsers, config_parse_private_users, 0, 0
Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
Files.Bind, config_parse_bind, 0, 0
Files.BindReadOnly, config_parse_bind, 1, 0
Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
+Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown)
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
index 74a0ae865b..f2b7e4dd79 100644
--- a/src/nspawn/nspawn-network.c
+++ b/src/nspawn/nspawn-network.c
@@ -538,3 +538,50 @@ int veth_extra_parse(char ***l, const char *p) {
a = b = NULL;
return 0;
}
+
+static int remove_one_veth_link(sd_netlink *rtnl, const char *name) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ if (isempty(name))
+ return 0;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r == -ENODEV) /* Already gone */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to remove veth interface %s: %m", name);
+
+ return 1;
+}
+
+int remove_veth_links(const char *primary, char **pairs) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ char **a, **b;
+ int r;
+
+ /* In some cases the kernel might pin the veth links between host and container even after the namespace
+ * died. Hence, let's better remove them explicitly too. */
+
+ if (isempty(primary) && strv_isempty(pairs))
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ remove_one_veth_link(rtnl, primary);
+
+ STRV_FOREACH_PAIR(a, b, pairs)
+ remove_one_veth_link(rtnl, *a);
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h
index 9ab1606d1c..c5036ab470 100644
--- a/src/nspawn/nspawn-network.h
+++ b/src/nspawn/nspawn-network.h
@@ -34,3 +34,5 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
int move_network_interfaces(pid_t pid, char **ifaces);
int veth_extra_parse(char ***l, const char *p);
+
+int remove_veth_links(const char *primary, char **pairs);
diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c
new file mode 100644
index 0000000000..c7382d412d
--- /dev/null
+++ b/src/nspawn/nspawn-patch-uid.c
@@ -0,0 +1,469 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <linux/magic.h>
+#ifdef HAVE_ACL
+#include <sys/acl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include "acl-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "missing.h"
+#include "nspawn-patch-uid.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+#ifdef HAVE_ACL
+
+static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
+ char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ acl_t acl;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (name) {
+ _cleanup_close_ int child_fd = -1;
+
+ child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (child_fd < 0)
+ return -errno;
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
+ acl = acl_get_file(procfs_path, type);
+ } else if (type == ACL_TYPE_ACCESS)
+ acl = acl_get_fd(fd);
+ else {
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ acl = acl_get_file(procfs_path, type);
+ }
+ if (!acl)
+ return -errno;
+
+ *ret = acl;
+ return 0;
+}
+
+static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
+ char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ int r;
+
+ assert(fd >= 0);
+ assert(acl);
+
+ if (name) {
+ _cleanup_close_ int child_fd = -1;
+
+ child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (child_fd < 0)
+ return -errno;
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
+ r = acl_set_file(procfs_path, type, acl);
+ } else if (type == ACL_TYPE_ACCESS)
+ r = acl_set_fd(fd, acl);
+ else {
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ r = acl_set_file(procfs_path, type, acl);
+ }
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) {
+ _cleanup_(acl_freep) acl_t copy = NULL;
+ acl_entry_t i;
+ int r;
+
+ assert(acl);
+ assert(ret);
+
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ if (r < 0)
+ return -errno;
+ while (r > 0) {
+ uid_t *old_uid, new_uid;
+ bool modify = false;
+ acl_tag_t tag;
+
+ if (acl_get_tag_type(i, &tag) < 0)
+ return -errno;
+
+ if (IN_SET(tag, ACL_USER, ACL_GROUP)) {
+
+ /* We don't distuingish here between uid_t and gid_t, let's make sure the compiler checks that
+ * this is actually OK */
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+
+ old_uid = acl_get_qualifier(i);
+ if (!old_uid)
+ return -errno;
+
+ new_uid = shift | (*old_uid & UINT32_C(0xFFFF));
+ if (!uid_is_valid(new_uid))
+ return -EINVAL;
+
+ modify = new_uid != *old_uid;
+ if (modify && !copy) {
+ int n;
+
+ /* There's no copy of the ACL yet? if so, let's create one, and start the loop from the
+ * beginning, so that we copy all entries, starting from the first, this time. */
+
+ n = acl_entries(acl);
+ if (n < 0)
+ return -errno;
+
+ copy = acl_init(n);
+ if (!copy)
+ return -errno;
+
+ /* Seek back to the beginning */
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ if (r < 0)
+ return -errno;
+ continue;
+ }
+ }
+
+ if (copy) {
+ acl_entry_t new_entry;
+
+ if (acl_create_entry(&copy, &new_entry) < 0)
+ return -errno;
+
+ if (acl_copy_entry(new_entry, i) < 0)
+ return -errno;
+
+ if (modify)
+ if (acl_set_qualifier(new_entry, &new_uid) < 0)
+ return -errno;
+ }
+
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i);
+ if (r < 0)
+ return -errno;
+ }
+
+ *ret = copy;
+ copy = NULL;
+
+ return !!*ret;
+}
+
+static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) {
+ _cleanup_(acl_freep) acl_t acl = NULL, shifted = NULL;
+ bool changed = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(st);
+
+ /* ACLs are not supported on symlinks, there's no point in trying */
+ if (S_ISLNK(st->st_mode))
+ return 0;
+
+ r = get_acl(fd, name, ACL_TYPE_ACCESS, &acl);
+ if (r == -EOPNOTSUPP)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = shift_acl(acl, shift, &shifted);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = set_acl(fd, name, ACL_TYPE_ACCESS, shifted);
+ if (r < 0)
+ return r;
+
+ changed = true;
+ }
+
+ if (S_ISDIR(st->st_mode)) {
+ acl_free(acl);
+ acl_free(shifted);
+
+ acl = shifted = NULL;
+
+ r = get_acl(fd, name, ACL_TYPE_DEFAULT, &acl);
+ if (r < 0)
+ return r;
+
+ r = shift_acl(acl, shift, &shifted);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ r = set_acl(fd, name, ACL_TYPE_DEFAULT, shifted);
+ if (r < 0)
+ return r;
+
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+#else
+
+static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) {
+ return 0;
+}
+
+#endif
+
+static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift) {
+ uid_t new_uid;
+ gid_t new_gid;
+ bool changed = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(st);
+
+ new_uid = shift | (st->st_uid & UINT32_C(0xFFFF));
+ new_gid = (gid_t) shift | (st->st_gid & UINT32_C(0xFFFF));
+
+ if (!uid_is_valid(new_uid) || !gid_is_valid(new_gid))
+ return -EINVAL;
+
+ if (st->st_uid != new_uid || st->st_gid != new_gid) {
+ if (name)
+ r = fchownat(fd, name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW);
+ else
+ r = fchown(fd, new_uid, new_gid);
+ if (r < 0)
+ return -errno;
+
+ /* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */
+ if (name && !S_ISLNK(st->st_mode))
+ r = fchmodat(fd, name, st->st_mode, 0);
+ else
+ r = fchmod(fd, st->st_mode);
+ if (r < 0)
+ return -errno;
+
+ changed = true;
+ }
+
+ r = patch_acls(fd, name, st, shift);
+ if (r < 0)
+ return r;
+
+ return r > 0 || changed;
+}
+
+static int is_procfs_sysfs_or_suchlike(int fd) {
+ struct statfs sfs;
+
+ assert(fd >= 0);
+
+ if (fstatfs(fd, &sfs) < 0)
+ return -errno;
+
+ return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) ||
+ F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC);
+}
+
+static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) {
+ bool changed = false;
+ int r;
+
+ assert(fd >= 0);
+
+ /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we
+ * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's
+ * stop the recursion when we hit a procfs or sysfs file system. */
+ r = is_procfs_sysfs_or_suchlike(fd);
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ r = 0; /* don't recurse */
+ goto finish;
+ }
+
+ r = patch_fd(fd, NULL, st, shift);
+ if (r == -EROFS) {
+ _cleanup_free_ char *name = NULL;
+
+ if (!is_toplevel) {
+ /* When we hit a ready-only subtree we simply skip it, but log about it. */
+ (void) fd_get_path(fd, &name);
+ log_debug("Skippping read-only file or directory %s.", strna(name));
+ r = 0;
+ }
+
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+
+ if (S_ISDIR(st->st_mode)) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+
+ if (!donate_fd) {
+ int copy;
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ fd = copy;
+ donate_fd = true;
+ }
+
+ d = fdopendir(fd);
+ if (!d) {
+ r = -errno;
+ goto finish;
+ }
+ fd = -1;
+
+ FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
+ struct stat fst;
+
+ if (STR_IN_SET(de->d_name, ".", ".."))
+ continue;
+
+ if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (S_ISDIR(fst.st_mode)) {
+ int subdir_fd;
+
+ subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (subdir_fd < 0) {
+ r = -errno;
+ goto finish;
+
+ }
+
+ r = recurse_fd(subdir_fd, true, &fst, shift, false);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ changed = true;
+
+ } else {
+ r = patch_fd(dirfd(d), de->d_name, &fst, shift);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ changed = true;
+ }
+ }
+ }
+
+ r = changed;
+
+finish:
+ if (donate_fd)
+ safe_close(fd);
+
+ return r;
+}
+
+static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) {
+ struct stat st;
+ int r;
+
+ assert(fd >= 0);
+
+ /* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an
+ * OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges
+ * following the concept that the upper 16bit of a UID identify the container, and the lower 16bit are the actual
+ * UID within the container. */
+
+ if ((shift & 0xFFFF) != 0) {
+ /* We only support containers where the shift starts at a 2^16 boundary */
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ if (range != 0x10000) {
+ /* We only support containers with 16bit UID ranges for the patching logic */
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) {
+ /* We only support containers where the uid/gid container ID match */
+ r = -EBADE;
+ goto finish;
+ }
+
+ /* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume
+ * that if the top-level dir has the right upper 16bit assigned, then everything below will have too... */
+ if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0)
+ return 0;
+
+ return recurse_fd(fd, donate_fd, &st, shift, true);
+
+finish:
+ if (donate_fd)
+ safe_close(fd);
+
+ return r;
+}
+
+int fd_patch_uid(int fd, uid_t shift, uid_t range) {
+ return fd_patch_uid_internal(fd, false, shift, range);
+}
+
+int path_patch_uid(const char *path, uid_t shift, uid_t range) {
+ int fd;
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (fd < 0)
+ return -errno;
+
+ return fd_patch_uid_internal(fd, true, shift, range);
+}
diff --git a/src/nspawn/nspawn-patch-uid.h b/src/nspawn/nspawn-patch-uid.h
new file mode 100644
index 0000000000..55d0990016
--- /dev/null
+++ b/src/nspawn/nspawn-patch-uid.h
@@ -0,0 +1,23 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+int fd_patch_uid(int fd, uid_t shift, uid_t range);
+int path_patch_uid(const char *path, uid_t shift, uid_t range);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index 760861089d..20103c5e88 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -20,6 +20,7 @@
#include "sd-bus.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "nspawn-register.h"
#include "stat-util.h"
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 4fb0054698..b98a79fd09 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -25,7 +25,9 @@
#include "parse-util.h"
#include "process-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
+#include "string-util.h"
int settings_load(FILE *f, const char *path, Settings **ret) {
_cleanup_(settings_freep) Settings *s = NULL;
@@ -40,9 +42,13 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
s->start_mode = _START_MODE_INVALID;
s->personality = PERSONALITY_INVALID;
+ s->userns_mode = _USER_NAMESPACE_MODE_INVALID;
+ s->uid_shift = UID_INVALID;
+ s->uid_range = UID_INVALID;
s->read_only = -1;
s->volatile_mode = _VOLATILE_MODE_INVALID;
+ s->userns_chown = -1;
s->private_network = -1;
s->network_veth = -1;
@@ -59,6 +65,16 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
if (r < 0)
return r;
+ /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
+ * both fields shall be initialized or neither. */
+ if (s->userns_mode == USER_NAMESPACE_PICK)
+ s->userns_chown = true;
+ else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
+ s->userns_chown = false;
+
+ if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
+ s->userns_mode = USER_NAMESPACE_NO;
+
*ret = s;
s = NULL;
@@ -392,3 +408,73 @@ conflict:
log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
return 0;
}
+
+int config_parse_private_users(
+ 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) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = parse_boolean(rvalue);
+ if (r == 0) {
+ /* no: User namespacing off */
+ settings->userns_mode = USER_NAMESPACE_NO;
+ settings->uid_shift = UID_INVALID;
+ settings->uid_range = UINT32_C(0x10000);
+ } else if (r > 0) {
+ /* yes: User namespacing on, UID range is read from root dir */
+ settings->userns_mode = USER_NAMESPACE_FIXED;
+ settings->uid_shift = UID_INVALID;
+ settings->uid_range = UINT32_C(0x10000);
+ } else if (streq(rvalue, "pick")) {
+ /* pick: User namespacing on, UID range is picked randomly */
+ settings->userns_mode = USER_NAMESPACE_PICK;
+ settings->uid_shift = UID_INVALID;
+ settings->uid_range = UINT32_C(0x10000);
+ } else {
+ const char *range, *shift;
+ uid_t sh, rn;
+
+ /* anything else: User namespacing on, UID range is explicitly configured */
+
+ range = strchr(rvalue, ':');
+ if (range) {
+ shift = strndupa(rvalue, range - rvalue);
+ range++;
+
+ r = safe_atou32(range, &rn);
+ if (r < 0 || rn <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
+ return 0;
+ }
+ } else {
+ shift = rvalue;
+ rn = UINT32_C(0x10000);
+ }
+
+ r = parse_uid(shift, &sh);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
+ return 0;
+ }
+
+ settings->userns_mode = USER_NAMESPACE_FIXED;
+ settings->uid_shift = sh;
+ settings->uid_range = rn;
+ }
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index a017405cd9..e12e91b886 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -33,6 +33,14 @@ typedef enum StartMode {
_START_MODE_INVALID = -1
} StartMode;
+typedef enum UserNamespaceMode {
+ USER_NAMESPACE_NO,
+ USER_NAMESPACE_FIXED,
+ USER_NAMESPACE_PICK,
+ _USER_NAMESPACE_MODE_MAX,
+ _USER_NAMESPACE_MODE_INVALID = -1,
+} UserNamespaceMode;
+
typedef enum SettingsMask {
SETTING_START_MODE = 1 << 0,
SETTING_ENVIRONMENT = 1 << 1,
@@ -47,7 +55,8 @@ typedef enum SettingsMask {
SETTING_VOLATILE_MODE = 1 << 10,
SETTING_CUSTOM_MOUNTS = 1 << 11,
SETTING_WORKING_DIRECTORY = 1 << 12,
- _SETTINGS_MASK_ALL = (1 << 13) -1
+ SETTING_USERNS = 1 << 13,
+ _SETTINGS_MASK_ALL = (1 << 14) -1
} SettingsMask;
typedef struct Settings {
@@ -62,12 +71,15 @@ typedef struct Settings {
unsigned long personality;
sd_id128_t machine_id;
char *working_directory;
+ UserNamespaceMode userns_mode;
+ uid_t uid_shift, uid_range;
/* [Image] */
int read_only;
VolatileMode volatile_mode;
CustomMount *custom_mounts;
unsigned n_custom_mounts;
+ int userns_chown;
/* [Network] */
int private_network;
@@ -99,3 +111,4 @@ int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, co
int config_parse_veth_extra(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);
int config_parse_boot(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);
int config_parse_pid2(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);
+int config_parse_private_users(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);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 8c1672ba0c..3fc6cc955c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -22,7 +22,9 @@
#endif
#include <errno.h>
#include <getopt.h>
+#include <grp.h>
#include <linux/loop.h>
+#include <pwd.h>
#include <sched.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
@@ -75,6 +77,7 @@
#include "nspawn-expose-ports.h"
#include "nspawn-mount.h"
#include "nspawn-network.h"
+#include "nspawn-patch-uid.h"
#include "nspawn-register.h"
#include "nspawn-settings.h"
#include "nspawn-setuid.h"
@@ -101,6 +104,11 @@
#include "user-util.h"
#include "util.h"
+/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit
+ * UID range here */
+#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000))
+#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000))
+
typedef enum ContainerStatus {
CONTAINER_TERMINATED,
CONTAINER_REBOOTED
@@ -173,8 +181,9 @@ static char *arg_image = NULL;
static VolatileMode arg_volatile_mode = VOLATILE_NO;
static ExposePort *arg_expose_ports = NULL;
static char **arg_property = NULL;
+static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
-static bool arg_userns = false;
+static bool arg_userns_chown = false;
static int arg_kill_signal = 0;
static bool arg_unified_cgroup_hierarchy = false;
static SettingsMask arg_settings_mask = 0;
@@ -202,8 +211,10 @@ static void help(void) {
" --uuid=UUID Set a specific machine UUID for the container\n"
" -S --slice=SLICE Place the container in the specified slice\n"
" --property=NAME=VALUE Set scope unit property\n"
+ " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n"
" --private-users[=UIDBASE[:NUIDS]]\n"
- " Run within user namespace\n"
+ " Run within user namespace, user configured UID/GID range\n"
+ " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n"
" --private-network Disable network in container\n"
" --network-interface=INTERFACE\n"
" Assign an existing network interface to the\n"
@@ -250,7 +261,7 @@ static void help(void) {
" the container\n"
" --overlay-ro=PATH[:PATH...]:PATH\n"
" Similar, but creates a read-only overlay mount\n"
- " --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
+ " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
" --share-system Share system namespaces with host\n"
" --register=BOOLEAN Register container as machine\n"
" --keep-unit Do not register a scope for the machine, reuse\n"
@@ -272,9 +283,15 @@ static int custom_mounts_prepare(void) {
for (i = 0; i < arg_n_custom_mounts; i++) {
CustomMount *m = &arg_custom_mounts[i];
- if (arg_userns && arg_uid_shift == UID_INVALID && path_equal(m->destination, "/")) {
- log_error("--private-users with automatic UID shift may not be combined with custom root mounts.");
- return -EINVAL;
+ if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) {
+
+ if (arg_userns_chown) {
+ log_error("--private-users-chown may not be combined with custom root mounts.");
+ return -EINVAL;
+ } else if (arg_uid_shift == UID_INVALID) {
+ log_error("--private-users with automatic UID shift may not be combined with custom root mounts.");
+ return -EINVAL;
+ }
}
if (m->type != CUSTOM_MOUNT_OVERLAY)
@@ -333,7 +350,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TMPFS,
ARG_OVERLAY,
ARG_OVERLAY_RO,
- ARG_SETENV,
ARG_SHARE_SYSTEM,
ARG_REGISTER,
ARG_KEEP_UNIT,
@@ -350,6 +366,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_KILL_SIGNAL,
ARG_SETTINGS,
ARG_CHDIR,
+ ARG_PRIVATE_USERS_CHOWN,
};
static const struct option options[] = {
@@ -374,7 +391,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
{ "machine", required_argument, NULL, 'M' },
{ "slice", required_argument, NULL, 'S' },
- { "setenv", required_argument, NULL, ARG_SETENV },
+ { "setenv", required_argument, NULL, 'E' },
{ "selinux-context", required_argument, NULL, 'Z' },
{ "selinux-apifs-context", required_argument, NULL, 'L' },
{ "quiet", no_argument, NULL, 'q' },
@@ -393,6 +410,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "port", required_argument, NULL, 'p' },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
+ { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN},
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "chdir", required_argument, NULL, ARG_CHDIR },
@@ -407,7 +425,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:n", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0)
switch (c) {
@@ -711,7 +729,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
- case ARG_SETENV: {
+ case 'E': {
char **n;
if (!env_assignment_is_valid(optarg)) {
@@ -798,10 +816,29 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_PRIVATE_USERS:
- if (optarg) {
+
+ r = optarg ? parse_boolean(optarg) : 1;
+ if (r == 0) {
+ /* no: User namespacing off */
+ arg_userns_mode = USER_NAMESPACE_NO;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+ } else if (r > 0) {
+ /* yes: User namespacing on, UID range is read from root dir */
+ arg_userns_mode = USER_NAMESPACE_FIXED;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+ } else if (streq(optarg, "pick")) {
+ /* pick: User namespacing on, UID range is picked randomly */
+ arg_userns_mode = USER_NAMESPACE_PICK;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+ } else {
_cleanup_free_ char *buffer = NULL;
const char *range, *shift;
+ /* anything else: User namespacing on, UID range is explicitly configured */
+
range = strchr(optarg, ':');
if (range) {
buffer = strndup(optarg, range - optarg);
@@ -821,9 +858,28 @@ static int parse_argv(int argc, char *argv[]) {
log_error("Failed to parse UID: %s", optarg);
return -EINVAL;
}
+
+ arg_userns_mode = USER_NAMESPACE_FIXED;
+ }
+
+ arg_settings_mask |= SETTING_USERNS;
+ break;
+
+ case 'U':
+ if (userns_supported()) {
+ arg_userns_mode = USER_NAMESPACE_PICK;
+ arg_uid_shift = UID_INVALID;
+ arg_uid_range = UINT32_C(0x10000);
+
+ arg_settings_mask |= SETTING_USERNS;
}
- arg_userns = true;
+ break;
+
+ case ARG_PRIVATE_USERS_CHOWN:
+ arg_userns_chown = true;
+
+ arg_settings_mask |= SETTING_USERNS;
break;
case ARG_KILL_SIGNAL:
@@ -894,6 +950,9 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_share_system)
arg_register = false;
+ if (arg_userns_mode == USER_NAMESPACE_PICK)
+ arg_userns_chown = true;
+
if (arg_start_mode != START_PID1 && arg_share_system) {
log_error("--boot and --share-system may not be combined.");
return -EINVAL;
@@ -934,8 +993,15 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_userns && access("/proc/self/uid_map", F_OK) < 0)
- return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support.");
+ if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) {
+ log_error("--private-users= is not supported, kernel compiled without user namespace support.");
+ return -EOPNOTSUPP;
+ }
+
+ if (arg_userns_chown && arg_read_only) {
+ log_error("--read-only and --private-users-chown may not be combined.");
+ return -EINVAL;
+ }
if (argc > optind) {
arg_parameters = strv_copy(argv + optind);
@@ -994,7 +1060,7 @@ static int verify_arguments(void) {
static int userns_lchown(const char *p, uid_t uid, gid_t gid) {
assert(p);
- if (!arg_userns)
+ if (arg_userns_mode == USER_NAMESPACE_NO)
return 0;
if (uid == UID_INVALID && gid == GID_INVALID)
@@ -1377,7 +1443,7 @@ static int setup_hostname(void) {
static int setup_journal(const char *directory) {
sd_id128_t this_id;
- _cleanup_free_ char *b = NULL, *d = NULL;
+ _cleanup_free_ char *d = NULL;
const char *p, *q;
bool try;
char id[33];
@@ -1398,7 +1464,7 @@ static int setup_journal(const char *directory) {
if (sd_id128_equal(arg_uuid, this_id)) {
log_full(try ? LOG_WARNING : LOG_ERR,
- "Host and machine ids are equal (%s): refusing to link journals", id);
+ "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id));
if (try)
return 0;
return -EEXIST;
@@ -1482,7 +1548,7 @@ static int setup_journal(const char *directory) {
}
if (arg_link_journal == LINK_HOST) {
- /* don't create parents here -- if the host doesn't have
+ /* don't create parents here — if the host doesn't have
* permanent journal set up, don't force it here */
if (mkdir(p, 0755) < 0 && errno != EEXIST) {
@@ -2219,6 +2285,29 @@ static int setup_machine_id(const char *directory) {
return 0;
}
+static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
+ int r;
+
+ assert(directory);
+
+ if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown)
+ return 0;
+
+ r = path_patch_uid(directory, arg_uid_shift, arg_uid_range);
+ if (r == -EOPNOTSUPP)
+ return log_error_errno(r, "Automatic UID/GID adjusting is only supported for UID/GID ranges starting at multiples of 2^16 with a range of 2^16.");
+ if (r == -EBADE)
+ return log_error_errno(r, "Upper 16 bits of root directory UID and GID do not match.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to adjust UID/GID shift of OS tree: %m");
+ if (r == 0)
+ log_debug("Root directory of image is already owned by the right UID/GID range, skipping recursive chown operation.");
+ else
+ log_debug("Patched directory tree to match UID/GID range.");
+
+ return r;
+}
+
static int mount_devices(
const char *where,
const char *root_device, bool root_device_rw,
@@ -2436,7 +2525,7 @@ static int determine_names(void) {
static int determine_uid_shift(const char *directory) {
int r;
- if (!arg_userns) {
+ if (arg_userns_mode == USER_NAMESPACE_NO) {
arg_uid_shift = 0;
return 0;
}
@@ -2463,7 +2552,6 @@ static int determine_uid_shift(const char *directory) {
return -EINVAL;
}
- log_info("Using user namespaces with base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
return 0;
}
@@ -2500,7 +2588,7 @@ static int inner_child(
cg_unified_flush();
- if (arg_userns) {
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
/* Tell the parent, that it now can write the UID map. */
(void) barrier_place(barrier); /* #1 */
@@ -2511,7 +2599,14 @@ static int inner_child(
}
}
- r = mount_all(NULL, arg_userns, true, arg_uid_shift, arg_private_network, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_all(NULL,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ true,
+ arg_private_network,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
+
if (r < 0)
return r;
@@ -2750,7 +2845,8 @@ static int outer_child(
if (r < 0)
return r;
- if (arg_userns) {
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* Let the parent know which UID shift we read from the image */
l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send UID shift: %m");
@@ -2758,17 +2854,49 @@ static int outer_child(
log_error("Short write while sending UID shift.");
return -EIO;
}
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift
+ * we just read from the image is available. If yes, it will send the UID shift back to us, if
+ * not it will pick a different one, and send it back to us. */
+
+ l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to recv UID shift: %m");
+ if (l != sizeof(arg_uid_shift)) {
+ log_error("Short read while recieving UID shift.");
+ return -EIO;
+ }
+ }
+
+ log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
}
/* Turn directory into bind mount */
if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0)
return log_error_errno(errno, "Failed to make bind mount: %m");
- r = setup_volatile(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context);
+ r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
if (r < 0)
return r;
- r = setup_volatile_state(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context);
+ r = setup_volatile(
+ directory,
+ arg_volatile_mode,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_context);
+ if (r < 0)
+ return r;
+
+ r = setup_volatile_state(
+ directory,
+ arg_volatile_mode,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_context);
if (r < 0)
return r;
@@ -2782,7 +2910,13 @@ static int outer_child(
return log_error_errno(r, "Failed to make tree read-only: %m");
}
- r = mount_all(directory, arg_userns, false, arg_private_network, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_all(directory,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ false,
+ arg_private_network,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -2824,11 +2958,24 @@ static int outer_child(
if (r < 0)
return r;
- r = mount_custom(directory, arg_custom_mounts, arg_n_custom_mounts, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_custom(
+ directory,
+ arg_custom_mounts,
+ arg_n_custom_mounts,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
if (r < 0)
return r;
- r = mount_cgroups(directory, arg_unified_cgroup_hierarchy, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
+ r = mount_cgroups(
+ directory,
+ arg_unified_cgroup_hierarchy,
+ arg_userns_mode != USER_NAMESPACE_NO,
+ arg_uid_shift,
+ arg_uid_range,
+ arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -2839,7 +2986,7 @@ static int outer_child(
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
(arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
(arg_private_network ? CLONE_NEWNET : 0) |
- (arg_userns ? CLONE_NEWUSER : 0),
+ (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0),
NULL);
if (pid < 0)
return log_error_errno(errno, "Failed to fork inner child: %m");
@@ -2883,6 +3030,61 @@ static int outer_child(
return 0;
}
+static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) {
+ unsigned n_tries = 100;
+ uid_t candidate;
+ int r;
+
+ assert(shift);
+ assert(ret_lock_file);
+ assert(arg_userns_mode == USER_NAMESPACE_PICK);
+ assert(arg_uid_range == 0x10000U);
+
+ candidate = *shift;
+
+ (void) mkdir("/run/systemd/nspawn-uid", 0755);
+
+ for (;;) {
+ char lock_path[strlen("/run/systemd/nspawn-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
+ _cleanup_release_lock_file_ LockFile lf = LOCK_FILE_INIT;
+
+ if (--n_tries <= 0)
+ return -EBUSY;
+
+ if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX)
+ goto next;
+ if ((candidate & UINT32_C(0xFFFF)) != 0)
+ goto next;
+
+ xsprintf(lock_path, "/run/systemd/nspawn-uid/" UID_FMT, candidate);
+ r = make_lock_file(lock_path, LOCK_EX|LOCK_NB, &lf);
+ if (r == -EBUSY) /* Range already taken by another nspawn instance */
+ goto next;
+ if (r < 0)
+ return r;
+
+ /* Make some superficial checks whether the range is currently known in the user database */
+ if (getpwuid(candidate))
+ goto next;
+ if (getpwuid(candidate + UINT32_C(0xFFFE)))
+ goto next;
+ if (getgrgid(candidate))
+ goto next;
+ if (getgrgid(candidate + UINT32_C(0xFFFE)))
+ goto next;
+
+ *ret_lock_file = lf;
+ lf = (struct LockFile) LOCK_FILE_INIT;
+ *shift = candidate;
+ return 0;
+
+ next:
+ random_bytes(&candidate, sizeof(candidate));
+ candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN;
+ candidate &= (uid_t) UINT32_C(0xFFFF0000);
+ }
+}
+
static int setup_uid_map(pid_t pid) {
char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1];
int r;
@@ -3114,6 +3316,19 @@ static int load_settings(void) {
}
}
+ if ((arg_settings_mask & SETTING_USERNS) == 0 &&
+ settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p);
+ else {
+ arg_userns_mode = settings->userns_mode;
+ arg_uid_shift = settings->uid_shift;
+ arg_uid_range = settings->uid_range;
+ arg_userns_chown = settings->userns_chown;
+ }
+ }
+
return 0;
}
@@ -3124,7 +3339,7 @@ int main(int argc, char *argv[]) {
_cleanup_close_ int master = -1, image_fd = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
int r, n_fd_passed, loop_nr = -1;
- char veth_name[IFNAMSIZ];
+ char veth_name[IFNAMSIZ] = "";
bool secondary = false, remove_subvol = false;
sigset_t mask_chld;
pid_t pid = 0;
@@ -3352,20 +3567,42 @@ int main(int argc, char *argv[]) {
}
for (;;) {
- _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 },
- pid_socket_pair[2] = { -1, -1 }, uuid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 };
- ContainerStatus container_status;
- _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
static const struct sigaction sa = {
.sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP,
};
- int ifi = 0;
- ssize_t l;
+
+ _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
+ _cleanup_close_ int etc_passwd_lock = -1;
+ _cleanup_close_pair_ int
+ kmsg_socket_pair[2] = { -1, -1 },
+ rtnl_socket_pair[2] = { -1, -1 },
+ pid_socket_pair[2] = { -1, -1 },
+ uuid_socket_pair[2] = { -1, -1 },
+ uid_shift_socket_pair[2] = { -1, -1 };
+ _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ ContainerStatus container_status;
char last_char = 0;
+ int ifi = 0;
+ ssize_t l;
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
+ * check with getpwuid() if the specific user already exists. Note that /etc might be
+ * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we
+ * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are
+ * really just an extra safety net. We kinda assume that the UID range we allocate from is
+ * really ours. */
+
+ etc_passwd_lock = take_etc_passwd_lock(NULL);
+ if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) {
+ log_error_errno(r, "Failed to take /etc/passwd lock: %m");
+ goto finish;
+ }
+ }
r = barrier_create(&barrier);
if (r < 0) {
@@ -3393,7 +3630,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_userns)
+ if (arg_userns_mode != USER_NAMESPACE_NO)
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) {
r = log_error_errno(errno, "Failed to create uid shift socket pair: %m");
goto finish;
@@ -3469,6 +3706,43 @@ int main(int argc, char *argv[]) {
uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* The child just let us know the UID shift it might have read from the image. */
+ l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read UID shift: %m");
+ goto finish;
+ }
+ if (l != sizeof(arg_uid_shift)) {
+ log_error("Short read while reading UID shift.");
+ r = EIO;
+ goto finish;
+ }
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* If we are supposed to pick the UID shift, let's try to use the shift read from the
+ * image, but if that's already in use, pick a new one, and report back to the child,
+ * which one we now picked. */
+
+ r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock);
+ if (r < 0) {
+ log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
+ goto finish;
+ }
+
+ l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to send UID shift: %m");
+ goto finish;
+ }
+ if (l != sizeof(arg_uid_shift)) {
+ log_error("Short write while writing UID shift.");
+ r = -EIO;
+ goto finish;
+ }
+ }
+ }
+
/* Wait for the outer child. */
r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
if (r < 0)
@@ -3505,24 +3779,13 @@ int main(int argc, char *argv[]) {
log_debug("Init process invoked as PID " PID_FMT, pid);
- if (arg_userns) {
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
if (!barrier_place_and_sync(&barrier)) { /* #1 */
log_error("Child died too early.");
r = -ESRCH;
goto finish;
}
- l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read UID shift: %m");
- goto finish;
- }
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short read while reading UID shift.");
- r = EIO;
- goto finish;
- }
-
r = setup_uid_map(pid);
if (r < 0)
goto finish;
@@ -3620,6 +3883,10 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear
+ * in getpwuid(), thus we can release the /etc/passwd lock. */
+ etc_passwd_lock = safe_close(etc_passwd_lock);
+
sd_notifyf(false,
"READY=1\n"
"STATUS=Container running.\n"
@@ -3714,6 +3981,7 @@ int main(int argc, char *argv[]) {
}
expose_port_flush(arg_expose_ports, &exposed);
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
}
finish:
@@ -3746,6 +4014,7 @@ finish:
}
expose_port_flush(arg_expose_ports, &exposed);
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
free(arg_directory);
free(arg_template);
diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c
new file mode 100644
index 0000000000..11c5321788
--- /dev/null
+++ b/src/nspawn/test-patch-uid.c
@@ -0,0 +1,61 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "log.h"
+#include "nspawn-patch-uid.h"
+#include "user-util.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ uid_t shift, range;
+ int r;
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc != 4) {
+ log_error("Expected PATH SHIFT RANGE parameters.");
+ return EXIT_FAILURE;
+ }
+
+ r = parse_uid(argv[2], &shift);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse UID shift %s.", argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ r = parse_gid(argv[3], &range);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse UID range %s.", argv[3]);
+ return EXIT_FAILURE;
+ }
+
+ r = path_patch_uid(argv[1], shift, range);
+ if (r < 0) {
+ log_error_errno(r, "Failed to patch directory tree: %m");
+ return EXIT_FAILURE;
+ }
+
+ log_info("Changed: %s", yes_no(r));
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
index 22004a00cd..09c85f9518 100644
--- a/src/resolve/RFCs
+++ b/src/resolve/RFCs
@@ -8,7 +8,7 @@ D = Comprehensively Implemented, by a dependency of resolved
Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES
Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types
-Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support
+Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts — Application and Support
~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes
Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes
Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index c5863b3aa2..b0dc65036d 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -468,7 +468,7 @@ static void link_read_settings(Link *l) {
}
if (r > 0) {
- /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */
+ /* If this link used to be managed, but is now unmanaged, flush all our settings — but only once. */
if (l->is_managed)
link_flush_settings(l);
diff --git a/src/run/run.c b/src/run/run.c
index c0aa2c5924..1d0f74ad21 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "calendarspec.h"
#include "env-util.h"
@@ -103,7 +104,7 @@ static void help(void) {
" --uid=USER Run as system user\n"
" --gid=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
- " --setenv=NAME=VALUE Set environment\n"
+ " -E --setenv=NAME=VALUE Set environment\n"
" -t --pty Run service on pseudo tty\n"
" -q --quiet Suppress information messages during runtime\n\n"
"Timer options:\n\n"
@@ -136,7 +137,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_EXEC_USER,
ARG_EXEC_GROUP,
ARG_NICE,
- ARG_SETENV,
ARG_ON_ACTIVE,
ARG_ON_BOOT,
ARG_ON_STARTUP,
@@ -165,7 +165,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "uid", required_argument, NULL, ARG_EXEC_USER },
{ "gid", required_argument, NULL, ARG_EXEC_GROUP },
{ "nice", required_argument, NULL, ARG_NICE },
- { "setenv", required_argument, NULL, ARG_SETENV },
+ { "setenv", required_argument, NULL, 'E' },
{ "property", required_argument, NULL, 'p' },
{ "tty", no_argument, NULL, 't' }, /* deprecated */
{ "pty", no_argument, NULL, 't' },
@@ -266,7 +266,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_nice_set = true;
break;
- case ARG_SETENV:
+ case 'E':
if (strv_extend(&arg_environment, optarg) < 0)
return log_oom();
@@ -621,6 +621,10 @@ static int transient_scope_set_properties(sd_bus_message *m) {
if (r < 0)
return r;
+ r = transient_cgroup_set_properties(m);
+ if (r < 0)
+ return r;
+
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
if (r < 0)
return r;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
new file mode 100644
index 0000000000..2b755cea28
--- /dev/null
+++ b/src/shared/bus-unit-util.c
@@ -0,0 +1,1287 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "bus-internal.h"
+#include "bus-unit-util.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "env-util.h"
+#include "escape.h"
+#include "hashmap.h"
+#include "list.h"
+#include "locale-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "syslog-util.h"
+#include "terminal-util.h"
+#include "utf8.h"
+#include "util.h"
+
+int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
+ assert(message);
+ assert(u);
+
+ u->machine = NULL;
+
+ return sd_bus_message_read(
+ message,
+ "(ssssssouso)",
+ &u->id,
+ &u->description,
+ &u->load_state,
+ &u->active_state,
+ &u->sub_state,
+ &u->following,
+ &u->unit_path,
+ &u->job_id,
+ &u->job_type,
+ &u->job_path);
+}
+
+int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
+ const char *eq, *field;
+ int r, rl;
+
+ assert(m);
+ assert(assignment);
+
+ eq = strchr(assignment, '=');
+ if (!eq) {
+ log_error("Not an assignment: %s", assignment);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ field = strndupa(assignment, eq - assignment);
+ eq++;
+
+ if (streq(field, "CPUQuota")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
+ else if (endswith(eq, "%")) {
+ double percent;
+
+ if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
+ log_error("CPU quota '%s' invalid.", eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
+ } else {
+ log_error("CPU quota needs to be in percent.");
+ return -EINVAL;
+ }
+
+ goto finish;
+
+ } else if (streq(field, "EnvironmentFile")) {
+
+ r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
+ eq[0] == '-' ? eq + 1 : eq,
+ eq[0] == '-');
+ goto finish;
+
+ } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
+ char *n;
+ usec_t t;
+ size_t l;
+ r = parse_sec(eq, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
+
+ l = strlen(field);
+ n = newa(char, l + 2);
+ if (!n)
+ return log_oom();
+
+ /* Change suffix Sec → USec */
+ strcpy(mempcpy(n, field, l - 3), "USec");
+ r = sd_bus_message_append(m, "sv", n, "t", t);
+ goto finish;
+ }
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ rl = rlimit_from_string(field);
+ if (rl >= 0) {
+ const char *sn;
+ struct rlimit l;
+
+ r = rlimit_parse(rl, eq, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resource limit: %s", eq);
+
+ r = sd_bus_message_append(m, "v", "t", l.rlim_max);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ sn = strjoina(field, "Soft");
+ r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
+
+ } else if (STR_IN_SET(field,
+ "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
+ "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
+ "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
+
+ r = parse_boolean(eq);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
+
+ r = sd_bus_message_append(m, "v", "b", r);
+
+ } else if (streq(field, "MemoryLimit")) {
+ uint64_t bytes;
+
+ if (isempty(eq) || streq(eq, "infinity"))
+ bytes = (uint64_t) -1;
+ else {
+ r = parse_size(eq, 1024, &bytes);
+ if (r < 0) {
+ log_error("Failed to parse bytes specification %s", assignment);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", bytes);
+
+ } else if (streq(field, "TasksMax")) {
+ uint64_t n;
+
+ if (isempty(eq) || streq(eq, "infinity"))
+ n = (uint64_t) -1;
+ else {
+ r = safe_atou64(eq, &n);
+ if (r < 0) {
+ log_error("Failed to parse maximum tasks specification %s", assignment);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", n);
+
+ } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
+ uint64_t u;
+
+ r = cg_cpu_shares_parse(eq, &u);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", u);
+
+ } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
+ uint64_t u;
+
+ r = cg_cpu_shares_parse(eq, &u);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", u);
+
+ } else if (STR_IN_SET(field,
+ "User", "Group", "DevicePolicy", "KillMode",
+ "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
+ "StandardInput", "StandardOutput", "StandardError",
+ "Description", "Slice", "Type", "WorkingDirectory",
+ "RootDirectory", "SyslogIdentifier", "ProtectSystem",
+ "ProtectHome"))
+ r = sd_bus_message_append(m, "v", "s", eq);
+
+ else if (streq(field, "SyslogLevel")) {
+ int level;
+
+ level = log_level_from_string(eq);
+ if (level < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", level);
+
+ } else if (streq(field, "SyslogFacility")) {
+ int facility;
+
+ facility = log_facility_unshifted_from_string(eq);
+ if (facility < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", facility);
+
+ } else if (streq(field, "DeviceAllow")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "v", "a(ss)", 0);
+ else {
+ const char *path, *rwm, *e;
+
+ e = strchr(eq, ' ');
+ if (e) {
+ path = strndupa(eq, e - eq);
+ rwm = e+1;
+ } else {
+ path = eq;
+ rwm = "";
+ }
+
+ if (!path_startswith(path, "/dev")) {
+ log_error("%s is not a device file in /dev.", path);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
+ }
+
+ } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "v", "a(st)", 0);
+ else {
+ const char *path, *bandwidth, *e;
+ uint64_t bytes;
+
+ e = strchr(eq, ' ');
+ if (e) {
+ path = strndupa(eq, e - eq);
+ bandwidth = e+1;
+ } else {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ if (!path_startswith(path, "/dev")) {
+ log_error("%s is not a device file in /dev.", path);
+ return -EINVAL;
+ }
+
+ r = parse_size(bandwidth, 1000, &bytes);
+ if (r < 0) {
+ log_error("Failed to parse byte value %s.", bandwidth);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
+ }
+
+ } else if (streq(field, "BlockIODeviceWeight")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "v", "a(st)", 0);
+ else {
+ const char *path, *weight, *e;
+ uint64_t u;
+
+ e = strchr(eq, ' ');
+ if (e) {
+ path = strndupa(eq, e - eq);
+ weight = e+1;
+ } else {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ if (!path_startswith(path, "/dev")) {
+ log_error("%s is not a device file in /dev.", path);
+ return -EINVAL;
+ }
+
+ r = safe_atou64(weight, &u);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, weight);
+ return -EINVAL;
+ }
+ r = sd_bus_message_append(m, "v", "a(st)", path, u);
+ }
+
+ } else if (streq(field, "Nice")) {
+ int32_t i;
+
+ r = safe_atoi32(eq, &i);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", i);
+
+ } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ if (r < 0) {
+ log_error("Failed to parse Environment value %s", eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (streq(field, "Environment")) {
+ if (!env_assignment_is_valid(word)) {
+ log_error("Invalid environment assignment: %s", word);
+ return -EINVAL;
+ }
+ } else { /* PassEnvironment */
+ if (!env_name_is_valid(word)) {
+ log_error("Invalid environment variable name: %s", word);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else if (streq(field, "KillSignal")) {
+ int sig;
+
+ sig = signal_from_string_try_harder(eq);
+ if (sig < 0) {
+ log_error("Failed to parse %s value %s.", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", sig);
+
+ } else if (streq(field, "TimerSlackNSec")) {
+ nsec_t n;
+
+ r = parse_nsec(eq, &n);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "t", n);
+ } else if (streq(field, "OOMScoreAdjust")) {
+ int oa;
+
+ r = safe_atoi(eq, &oa);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ if (!oom_score_adjust_is_valid(oa)) {
+ log_error("OOM score adjust value out of range");
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "i", oa);
+ } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int offset;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+ if (r == 0)
+ break;
+
+ if (!utf8_is_valid(word)) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ offset = word[0] == '-';
+ if (!path_is_absolute(word + offset)) {
+ log_error("Failed to parse %s value %s", field, eq);
+ return -EINVAL;
+ }
+
+ path_kill_slashes(word + offset);
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else if (streq(field, "RuntimeDirectory")) {
+ const char *p;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ p = eq;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value %s", field, eq);
+
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_append_basic(m, 's', word);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+
+ } else {
+ log_error("Unknown assignment %s.", assignment);
+ return -EINVAL;
+ }
+
+finish:
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 0;
+}
+
+typedef struct BusWaitForJobs {
+ sd_bus *bus;
+ Set *jobs;
+
+ char *name;
+ char *result;
+
+ sd_bus_slot *slot_job_removed;
+ sd_bus_slot *slot_disconnected;
+} BusWaitForJobs;
+
+static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ assert(m);
+
+ log_error("Warning! D-Bus connection terminated.");
+ sd_bus_close(sd_bus_message_get_bus(m));
+
+ return 0;
+}
+
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ const char *path, *unit, *result;
+ BusWaitForJobs *d = userdata;
+ uint32_t id;
+ char *found;
+ int r;
+
+ assert(m);
+ assert(d);
+
+ r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ found = set_remove(d->jobs, (char*) path);
+ if (!found)
+ return 0;
+
+ free(found);
+
+ if (!isempty(result))
+ d->result = strdup(result);
+
+ if (!isempty(unit))
+ d->name = strdup(unit);
+
+ return 0;
+}
+
+void bus_wait_for_jobs_free(BusWaitForJobs *d) {
+ if (!d)
+ return;
+
+ set_free_free(d->jobs);
+
+ sd_bus_slot_unref(d->slot_disconnected);
+ sd_bus_slot_unref(d->slot_job_removed);
+
+ sd_bus_unref(d->bus);
+
+ free(d->name);
+ free(d->result);
+
+ free(d);
+}
+
+int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
+ int r;
+
+ assert(bus);
+ assert(ret);
+
+ d = new0(BusWaitForJobs, 1);
+ if (!d)
+ return -ENOMEM;
+
+ d->bus = sd_bus_ref(bus);
+
+ /* When we are a bus client we match by sender. Direct
+ * connections OTOH have no initialized sender field, and
+ * hence we ignore the sender then */
+ r = sd_bus_add_match(
+ bus,
+ &d->slot_job_removed,
+ bus->bus_client ?
+ "type='signal',"
+ "sender='org.freedesktop.systemd1',"
+ "interface='org.freedesktop.systemd1.Manager',"
+ "member='JobRemoved',"
+ "path='/org/freedesktop/systemd1'" :
+ "type='signal',"
+ "interface='org.freedesktop.systemd1.Manager',"
+ "member='JobRemoved',"
+ "path='/org/freedesktop/systemd1'",
+ match_job_removed, d);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_match(
+ bus,
+ &d->slot_disconnected,
+ "type='signal',"
+ "sender='org.freedesktop.DBus.Local',"
+ "interface='org.freedesktop.DBus.Local',"
+ "member='Disconnected'",
+ match_disconnected, d);
+ if (r < 0)
+ return r;
+
+ *ret = d;
+ d = NULL;
+
+ return 0;
+}
+
+static int bus_process_wait(sd_bus *bus) {
+ int r;
+
+ for (;;) {
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ r = sd_bus_wait(bus, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+}
+
+static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
+ _cleanup_free_ char *dbus_path = NULL;
+
+ assert(d);
+ assert(d->name);
+ assert(result);
+
+ dbus_path = unit_dbus_path_from_name(d->name);
+ if (!dbus_path)
+ return -ENOMEM;
+
+ return sd_bus_get_property_string(d->bus,
+ "org.freedesktop.systemd1",
+ dbus_path,
+ "org.freedesktop.systemd1.Service",
+ "Result",
+ NULL,
+ result);
+}
+
+static const struct {
+ const char *result, *explanation;
+} explanations [] = {
+ { "resources", "of unavailable resources or another system error" },
+ { "timeout", "a timeout was exceeded" },
+ { "exit-code", "the control process exited with error code" },
+ { "signal", "a fatal signal was delivered to the control process" },
+ { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
+ { "watchdog", "the service failed to send watchdog ping" },
+ { "start-limit", "start of the service was attempted too often" }
+};
+
+static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
+ _cleanup_free_ char *service_shell_quoted = NULL;
+ const char *systemctl = "systemctl", *journalctl = "journalctl";
+
+ assert(service);
+
+ service_shell_quoted = shell_maybe_quote(service);
+
+ if (extra_args && extra_args[1]) {
+ _cleanup_free_ char *t;
+
+ t = strv_join((char**) extra_args, " ");
+ systemctl = strjoina("systemctl ", t ? : "<args>");
+ journalctl = strjoina("journalctl ", t ? : "<args>");
+ }
+
+ if (!isempty(result)) {
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(explanations); ++i)
+ if (streq(result, explanations[i].result))
+ break;
+
+ if (i < ELEMENTSOF(explanations)) {
+ log_error("Job for %s failed because %s.\n"
+ "See \"%s status %s\" and \"%s -xe\" for details.\n",
+ service,
+ explanations[i].explanation,
+ systemctl,
+ service_shell_quoted ?: "<service>",
+ journalctl);
+ goto finish;
+ }
+ }
+
+ log_error("Job for %s failed.\n"
+ "See \"%s status %s\" and \"%s -xe\" for details.\n",
+ service,
+ systemctl,
+ service_shell_quoted ?: "<service>",
+ journalctl);
+
+finish:
+ /* For some results maybe additional explanation is required */
+ if (streq_ptr(result, "start-limit"))
+ log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
+ "followed by \"%1$s start %2$s\" again.",
+ systemctl,
+ service_shell_quoted ?: "<service>");
+}
+
+static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+ int r = 0;
+
+ assert(d->result);
+
+ if (!quiet) {
+ if (streq(d->result, "canceled"))
+ log_error("Job for %s canceled.", strna(d->name));
+ else if (streq(d->result, "timeout"))
+ log_error("Job for %s timed out.", strna(d->name));
+ else if (streq(d->result, "dependency"))
+ log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+ else if (streq(d->result, "invalid"))
+ log_error("%s is not active, cannot reload.", strna(d->name));
+ else if (streq(d->result, "assert"))
+ log_error("Assertion failed on job for %s.", strna(d->name));
+ else if (streq(d->result, "unsupported"))
+ log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
+ else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
+ if (d->name) {
+ int q;
+ _cleanup_free_ char *result = NULL;
+
+ q = bus_job_get_service_result(d, &result);
+ if (q < 0)
+ log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
+
+ log_job_error_with_service_result(d->name, result, extra_args);
+ } else
+ log_error("Job failed. See \"journalctl -xe\" for details.");
+ }
+ }
+
+ if (streq(d->result, "canceled"))
+ r = -ECANCELED;
+ else if (streq(d->result, "timeout"))
+ r = -ETIME;
+ else if (streq(d->result, "dependency"))
+ r = -EIO;
+ else if (streq(d->result, "invalid"))
+ r = -ENOEXEC;
+ else if (streq(d->result, "assert"))
+ r = -EPROTO;
+ else if (streq(d->result, "unsupported"))
+ r = -EOPNOTSUPP;
+ else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
+ r = -EIO;
+
+ return r;
+}
+
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+ int r = 0;
+
+ assert(d);
+
+ while (!set_isempty(d->jobs)) {
+ int q;
+
+ q = bus_process_wait(d->bus);
+ if (q < 0)
+ return log_error_errno(q, "Failed to wait for response: %m");
+
+ if (d->result) {
+ q = check_wait_response(d, quiet, extra_args);
+ /* Return the first error as it is most likely to be
+ * meaningful. */
+ if (q < 0 && r == 0)
+ r = q;
+
+ log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
+ }
+
+ d->name = mfree(d->name);
+ d->result = mfree(d->result);
+ }
+
+ return r;
+}
+
+int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
+ int r;
+
+ assert(d);
+
+ r = set_ensure_allocated(&d->jobs, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ return set_put_strdup(d->jobs, path);
+}
+
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
+ int r;
+
+ r = bus_wait_for_jobs_add(d, path);
+ if (r < 0)
+ return log_oom();
+
+ return bus_wait_for_jobs(d, quiet, NULL);
+}
+
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
+ const char *type, *path, *source;
+ int r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
+ /* We expect only "success" changes to be sent over the bus.
+ Hence, reject anything negative. */
+ UnitFileChangeType ch = unit_file_change_type_from_string(type);
+
+ if (ch < 0) {
+ log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
+ continue;
+ }
+
+ r = unit_file_changes_add(changes, n_changes, ch, path, source);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
+ return 0;
+}
+
+struct CGroupInfo {
+ char *cgroup_path;
+ bool is_const; /* If false, cgroup_path should be free()'d */
+
+ Hashmap *pids; /* PID → process name */
+ bool done;
+
+ struct CGroupInfo *parent;
+ LIST_FIELDS(struct CGroupInfo, siblings);
+ LIST_HEAD(struct CGroupInfo, children);
+ size_t n_children;
+};
+
+static bool IS_ROOT(const char *p) {
+ return isempty(p) || streq(p, "/");
+}
+
+static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
+ struct CGroupInfo *parent = NULL, *cg;
+ int r;
+
+ assert(cgroups);
+ assert(ret);
+
+ if (IS_ROOT(path))
+ path = "/";
+
+ cg = hashmap_get(cgroups, path);
+ if (cg) {
+ *ret = cg;
+ return 0;
+ }
+
+ if (!IS_ROOT(path)) {
+ const char *e, *pp;
+
+ e = strrchr(path, '/');
+ if (!e)
+ return -EINVAL;
+
+ pp = strndupa(path, e - path);
+ if (!pp)
+ return -ENOMEM;
+
+ r = add_cgroup(cgroups, pp, false, &parent);
+ if (r < 0)
+ return r;
+ }
+
+ cg = new0(struct CGroupInfo, 1);
+ if (!cg)
+ return -ENOMEM;
+
+ if (is_const)
+ cg->cgroup_path = (char*) path;
+ else {
+ cg->cgroup_path = strdup(path);
+ if (!cg->cgroup_path) {
+ free(cg);
+ return -ENOMEM;
+ }
+ }
+
+ cg->is_const = is_const;
+ cg->parent = parent;
+
+ r = hashmap_put(cgroups, cg->cgroup_path, cg);
+ if (r < 0) {
+ if (!is_const)
+ free(cg->cgroup_path);
+ free(cg);
+ return r;
+ }
+
+ if (parent) {
+ LIST_PREPEND(siblings, parent->children, cg);
+ parent->n_children++;
+ }
+
+ *ret = cg;
+ return 1;
+}
+
+static int add_process(
+ Hashmap *cgroups,
+ const char *path,
+ pid_t pid,
+ const char *name) {
+
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(cgroups);
+ assert(name);
+ assert(pid > 0);
+
+ r = add_cgroup(cgroups, path, true, &cg);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
+}
+
+static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
+ assert(cgroups);
+ assert(cg);
+
+ while (cg->children)
+ remove_cgroup(cgroups, cg->children);
+
+ hashmap_remove(cgroups, cg->cgroup_path);
+
+ if (!cg->is_const)
+ free(cg->cgroup_path);
+
+ hashmap_free(cg->pids);
+
+ if (cg->parent)
+ LIST_REMOVE(siblings, cg->parent->children, cg);
+
+ free(cg);
+}
+
+static int cgroup_info_compare_func(const void *a, const void *b) {
+ const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
+
+ assert(x);
+ assert(y);
+
+ return strcmp(x->cgroup_path, y->cgroup_path);
+}
+
+static int dump_processes(
+ Hashmap *cgroups,
+ const char *cgroup_path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(prefix);
+
+ if (IS_ROOT(cgroup_path))
+ cgroup_path = "/";
+
+ cg = hashmap_get(cgroups, cgroup_path);
+ if (!cg)
+ return 0;
+
+ if (!hashmap_isempty(cg->pids)) {
+ const char *name;
+ size_t n = 0, i;
+ pid_t *pids;
+ void *pidp;
+ Iterator j;
+ int width;
+
+ /* Order processes by their PID */
+ pids = newa(pid_t, hashmap_size(cg->pids));
+
+ HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
+ pids[n++] = PTR_TO_PID(pidp);
+
+ assert(n == hashmap_size(cg->pids));
+ qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+
+ width = DECIMAL_STR_WIDTH(pids[n-1]);
+
+ for (i = 0; i < n; i++) {
+ _cleanup_free_ char *e = NULL;
+ const char *special;
+ bool more;
+
+ name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
+ assert(name);
+
+ if (n_columns != 0) {
+ unsigned k;
+
+ k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
+
+ e = ellipsize(name, k, 100);
+ if (e)
+ name = e;
+ }
+
+ more = i+1 < n || cg->children;
+ special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
+
+ fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
+ prefix,
+ special,
+ width, pids[i],
+ name);
+ }
+ }
+
+ if (cg->children) {
+ struct CGroupInfo **children, *child;
+ size_t n = 0, i;
+
+ /* Order subcgroups by their name */
+ children = newa(struct CGroupInfo*, cg->n_children);
+ LIST_FOREACH(siblings, child, cg->children)
+ children[n++] = child;
+ assert(n == cg->n_children);
+ qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
+
+ n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
+
+ for (i = 0; i < n; i++) {
+ _cleanup_free_ char *pp = NULL;
+ const char *name, *special;
+ bool more;
+
+ child = children[i];
+
+ name = strrchr(child->cgroup_path, '/');
+ if (!name)
+ return -EINVAL;
+ name++;
+
+ more = i+1 < n;
+ special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
+
+ fputs(prefix, stdout);
+ fputs(special, stdout);
+ fputs(name, stdout);
+ fputc('\n', stdout);
+
+ special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE);
+
+ pp = strappend(prefix, special);
+ if (!pp)
+ return -ENOMEM;
+
+ r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ cg->done = true;
+ return 0;
+}
+
+static int dump_extra_processes(
+ Hashmap *cgroups,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
+ _cleanup_free_ pid_t *pids = NULL;
+ _cleanup_hashmap_free_ Hashmap *names = NULL;
+ struct CGroupInfo *cg;
+ size_t n_allocated = 0, n = 0, k;
+ Iterator i;
+ int width, r;
+
+ /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
+ * combined, sorted, linear list. */
+
+ HASHMAP_FOREACH(cg, cgroups, i) {
+ const char *name;
+ void *pidp;
+ Iterator j;
+
+ if (cg->done)
+ continue;
+
+ if (hashmap_isempty(cg->pids))
+ continue;
+
+ r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
+ return -ENOMEM;
+
+ HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
+ pids[n++] = PTR_TO_PID(pidp);
+
+ r = hashmap_put(names, pidp, (void*) name);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (n == 0)
+ return 0;
+
+ qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+ width = DECIMAL_STR_WIDTH(pids[n-1]);
+
+ for (k = 0; k < n; k++) {
+ _cleanup_free_ char *e = NULL;
+ const char *name;
+
+ name = hashmap_get(names, PID_TO_PTR(pids[k]));
+ assert(name);
+
+ if (n_columns != 0) {
+ unsigned z;
+
+ z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
+
+ e = ellipsize(name, z, 100);
+ if (e)
+ name = e;
+ }
+
+ fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
+ prefix,
+ draw_special_char(DRAW_TRIANGULAR_BULLET),
+ width, pids[k],
+ name);
+ }
+
+ return 0;
+}
+
+int unit_show_processes(
+ sd_bus *bus,
+ const char *unit,
+ const char *cgroup_path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags,
+ sd_bus_error *error) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Hashmap *cgroups = NULL;
+ struct CGroupInfo *cg;
+ int r;
+
+ assert(bus);
+ assert(unit);
+
+ if (flags & OUTPUT_FULL_WIDTH)
+ n_columns = 0;
+ else if (n_columns <= 0)
+ n_columns = columns();
+
+ prefix = strempty(prefix);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitProcesses",
+ error,
+ &reply,
+ "s",
+ unit);
+ if (r < 0)
+ return r;
+
+ cgroups = hashmap_new(&string_hash_ops);
+ if (!cgroups)
+ return -ENOMEM;
+
+ r = sd_bus_message_enter_container(reply, 'a', "(sus)");
+ if (r < 0)
+ goto finish;
+
+ for (;;) {
+ const char *path = NULL, *name = NULL;
+ uint32_t pid;
+
+ r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ break;
+
+ r = add_process(cgroups, path, pid, name);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ goto finish;
+
+ r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
+ if (r < 0)
+ goto finish;
+
+ r = dump_extra_processes(cgroups, prefix, n_columns, flags);
+
+finish:
+ while ((cg = hashmap_first(cgroups)))
+ remove_cgroup(cgroups, cg);
+
+ hashmap_free(cgroups);
+
+ return r;
+}
diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h
new file mode 100644
index 0000000000..c0c172f336
--- /dev/null
+++ b/src/shared/bus-unit-util.h
@@ -0,0 +1,57 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+#include "output-mode.h"
+#include "install.h"
+
+typedef struct UnitInfo {
+ const char *machine;
+ const char *id;
+ const char *description;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *following;
+ const char *unit_path;
+ uint32_t job_id;
+ const char *job_type;
+ const char *job_path;
+} UnitInfo;
+
+int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
+
+int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
+
+typedef struct BusWaitForJobs BusWaitForJobs;
+
+int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
+void bus_wait_for_jobs_free(BusWaitForJobs *d);
+int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args);
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
+
+int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes);
+
+int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error);
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 6a1877d8aa..4efbf3710f 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -39,34 +39,16 @@
#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
-#include "cgroup-util.h"
#include "def.h"
-#include "env-util.h"
#include "escape.h"
-#include "extract-word.h"
#include "fd-util.h"
-#include "hashmap.h"
-#include "install.h"
-#include "kdbus.h"
-#include "log.h"
-#include "macro.h"
#include "missing.h"
#include "parse-util.h"
-#include "path-util.h"
#include "proc-cmdline.h"
-#include "process-util.h"
#include "rlimit-util.h"
-#include "set.h"
-#include "signal-util.h"
#include "stdio-util.h"
-#include "string-util.h"
#include "strv.h"
-#include "syslog-util.h"
-#include "time-util.h"
-#include "unit-name.h"
#include "user-util.h"
-#include "utf8.h"
-#include "util.h"
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
@@ -1383,847 +1365,6 @@ int bus_log_create_error(int r) {
return log_error_errno(r, "Failed to create bus message: %m");
}
-int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
- assert(message);
- assert(u);
-
- u->machine = NULL;
-
- return sd_bus_message_read(
- message,
- "(ssssssouso)",
- &u->id,
- &u->description,
- &u->load_state,
- &u->active_state,
- &u->sub_state,
- &u->following,
- &u->unit_path,
- &u->job_id,
- &u->job_type,
- &u->job_path);
-}
-
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
- const char *eq, *field;
- int r, rl;
-
- assert(m);
- assert(assignment);
-
- eq = strchr(assignment, '=');
- if (!eq) {
- log_error("Not an assignment: %s", assignment);
- return -EINVAL;
- }
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
- field = strndupa(assignment, eq - assignment);
- eq++;
-
- if (streq(field, "CPUQuota")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
- else if (endswith(eq, "%")) {
- double percent;
-
- if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
- log_error("CPU quota '%s' invalid.", eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
- } else {
- log_error("CPU quota needs to be in percent.");
- return -EINVAL;
- }
-
- goto finish;
-
- } else if (streq(field, "EnvironmentFile")) {
-
- r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
- eq[0] == '-' ? eq + 1 : eq,
- eq[0] == '-');
- goto finish;
-
- } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
- char *n;
- usec_t t;
- size_t l;
- r = parse_sec(eq, &t);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
-
- l = strlen(field);
- n = newa(char, l + 2);
- if (!n)
- return log_oom();
-
- /* Change suffix Sec → USec */
- strcpy(mempcpy(n, field, l - 3), "USec");
- r = sd_bus_message_append(m, "sv", n, "t", t);
- goto finish;
- }
-
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
- if (r < 0)
- return bus_log_create_error(r);
-
- rl = rlimit_from_string(field);
- if (rl >= 0) {
- const char *sn;
- struct rlimit l;
-
- r = rlimit_parse(rl, eq, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resource limit: %s", eq);
-
- r = sd_bus_message_append(m, "v", "t", l.rlim_max);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
- sn = strjoina(field, "Soft");
- r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
-
- } else if (STR_IN_SET(field,
- "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
- "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
- "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
- "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
- "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
-
- r = parse_boolean(eq);
- if (r < 0)
- return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
-
- r = sd_bus_message_append(m, "v", "b", r);
-
- } else if (streq(field, "MemoryLimit")) {
- uint64_t bytes;
-
- if (isempty(eq) || streq(eq, "infinity"))
- bytes = (uint64_t) -1;
- else {
- r = parse_size(eq, 1024, &bytes);
- if (r < 0) {
- log_error("Failed to parse bytes specification %s", assignment);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", bytes);
-
- } else if (streq(field, "TasksMax")) {
- uint64_t n;
-
- if (isempty(eq) || streq(eq, "infinity"))
- n = (uint64_t) -1;
- else {
- r = safe_atou64(eq, &n);
- if (r < 0) {
- log_error("Failed to parse maximum tasks specification %s", assignment);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", n);
-
- } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
- uint64_t u;
-
- r = cg_cpu_shares_parse(eq, &u);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "t", u);
-
- } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
- uint64_t u;
-
- r = cg_cpu_shares_parse(eq, &u);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "t", u);
-
- } else if (STR_IN_SET(field,
- "User", "Group", "DevicePolicy", "KillMode",
- "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
- "StandardInput", "StandardOutput", "StandardError",
- "Description", "Slice", "Type", "WorkingDirectory",
- "RootDirectory", "SyslogIdentifier", "ProtectSystem",
- "ProtectHome"))
- r = sd_bus_message_append(m, "v", "s", eq);
-
- else if (streq(field, "SyslogLevel")) {
- int level;
-
- level = log_level_from_string(eq);
- if (level < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", level);
-
- } else if (streq(field, "SyslogFacility")) {
- int facility;
-
- facility = log_facility_unshifted_from_string(eq);
- if (facility < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", facility);
-
- } else if (streq(field, "DeviceAllow")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(ss)", 0);
- else {
- const char *path, *rwm, *e;
-
- e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- rwm = e+1;
- } else {
- path = eq;
- rwm = "";
- }
-
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
- }
-
- } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(st)", 0);
- else {
- const char *path, *bandwidth, *e;
- uint64_t bytes;
-
- e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- bandwidth = e+1;
- } else {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
-
- r = parse_size(bandwidth, 1000, &bytes);
- if (r < 0) {
- log_error("Failed to parse byte value %s.", bandwidth);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
- }
-
- } else if (streq(field, "BlockIODeviceWeight")) {
-
- if (isempty(eq))
- r = sd_bus_message_append(m, "v", "a(st)", 0);
- else {
- const char *path, *weight, *e;
- uint64_t u;
-
- e = strchr(eq, ' ');
- if (e) {
- path = strndupa(eq, e - eq);
- weight = e+1;
- } else {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- if (!path_startswith(path, "/dev")) {
- log_error("%s is not a device file in /dev.", path);
- return -EINVAL;
- }
-
- r = safe_atou64(weight, &u);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, weight);
- return -EINVAL;
- }
- r = sd_bus_message_append(m, "v", "a(st)", path, u);
- }
-
- } else if (streq(field, "Nice")) {
- int32_t i;
-
- r = safe_atoi32(eq, &i);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", i);
-
- } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
-
- p = eq;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
- if (r < 0) {
- log_error("Failed to parse Environment value %s", eq);
- return -EINVAL;
- }
- if (r == 0)
- break;
-
- if (streq(field, "Environment")) {
- if (!env_assignment_is_valid(word)) {
- log_error("Invalid environment assignment: %s", word);
- return -EINVAL;
- }
- } else { /* PassEnvironment */
- if (!env_name_is_valid(word)) {
- log_error("Invalid environment variable name: %s", word);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
-
- } else if (streq(field, "KillSignal")) {
- int sig;
-
- sig = signal_from_string_try_harder(eq);
- if (sig < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", sig);
-
- } else if (streq(field, "TimerSlackNSec")) {
- nsec_t n;
-
- r = parse_nsec(eq, &n);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "t", n);
- } else if (streq(field, "OOMScoreAdjust")) {
- int oa;
-
- r = safe_atoi(eq, &oa);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- if (!oom_score_adjust_is_valid(oa)) {
- log_error("OOM score adjust value out of range");
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "i", oa);
- } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
-
- p = eq;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
- int offset;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
- if (r == 0)
- break;
-
- if (!utf8_is_valid(word)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- offset = word[0] == '-';
- if (!path_is_absolute(word + offset)) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- path_kill_slashes(word + offset);
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
-
- } else if (streq(field, "RuntimeDirectory")) {
- const char *p;
-
- r = sd_bus_message_open_container(m, 'v', "as");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "s");
- if (r < 0)
- return bus_log_create_error(r);
-
- p = eq;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s value %s", field, eq);
-
- if (r == 0)
- break;
-
- r = sd_bus_message_append_basic(m, 's', word);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
-
- } else {
- log_error("Unknown assignment %s.", assignment);
- return -EINVAL;
- }
-
-finish:
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- return 0;
-}
-
-typedef struct BusWaitForJobs {
- sd_bus *bus;
- Set *jobs;
-
- char *name;
- char *result;
-
- sd_bus_slot *slot_job_removed;
- sd_bus_slot *slot_disconnected;
-} BusWaitForJobs;
-
-static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- assert(m);
-
- log_error("Warning! D-Bus connection terminated.");
- sd_bus_close(sd_bus_message_get_bus(m));
-
- return 0;
-}
-
-static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char *path, *unit, *result;
- BusWaitForJobs *d = userdata;
- uint32_t id;
- char *found;
- int r;
-
- assert(m);
- assert(d);
-
- r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
- if (r < 0) {
- bus_log_parse_error(r);
- return 0;
- }
-
- found = set_remove(d->jobs, (char*) path);
- if (!found)
- return 0;
-
- free(found);
-
- if (!isempty(result))
- d->result = strdup(result);
-
- if (!isempty(unit))
- d->name = strdup(unit);
-
- return 0;
-}
-
-void bus_wait_for_jobs_free(BusWaitForJobs *d) {
- if (!d)
- return;
-
- set_free_free(d->jobs);
-
- sd_bus_slot_unref(d->slot_disconnected);
- sd_bus_slot_unref(d->slot_job_removed);
-
- sd_bus_unref(d->bus);
-
- free(d->name);
- free(d->result);
-
- free(d);
-}
-
-int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
- _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
- int r;
-
- assert(bus);
- assert(ret);
-
- d = new0(BusWaitForJobs, 1);
- if (!d)
- return -ENOMEM;
-
- d->bus = sd_bus_ref(bus);
-
- /* When we are a bus client we match by sender. Direct
- * connections OTOH have no initialized sender field, and
- * hence we ignore the sender then */
- r = sd_bus_add_match(
- bus,
- &d->slot_job_removed,
- bus->bus_client ?
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'" :
- "type='signal',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'",
- match_job_removed, d);
- if (r < 0)
- return r;
-
- r = sd_bus_add_match(
- bus,
- &d->slot_disconnected,
- "type='signal',"
- "sender='org.freedesktop.DBus.Local',"
- "interface='org.freedesktop.DBus.Local',"
- "member='Disconnected'",
- match_disconnected, d);
- if (r < 0)
- return r;
-
- *ret = d;
- d = NULL;
-
- return 0;
-}
-
-static int bus_process_wait(sd_bus *bus) {
- int r;
-
- for (;;) {
- r = sd_bus_process(bus, NULL);
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
-
- r = sd_bus_wait(bus, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
- _cleanup_free_ char *dbus_path = NULL;
-
- assert(d);
- assert(d->name);
- assert(result);
-
- dbus_path = unit_dbus_path_from_name(d->name);
- if (!dbus_path)
- return -ENOMEM;
-
- return sd_bus_get_property_string(d->bus,
- "org.freedesktop.systemd1",
- dbus_path,
- "org.freedesktop.systemd1.Service",
- "Result",
- NULL,
- result);
-}
-
-static const struct {
- const char *result, *explanation;
-} explanations [] = {
- { "resources", "a configured resource limit was exceeded" },
- { "timeout", "a timeout was exceeded" },
- { "exit-code", "the control process exited with error code" },
- { "signal", "a fatal signal was delivered to the control process" },
- { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
- { "watchdog", "the service failed to send watchdog ping" },
- { "start-limit", "start of the service was attempted too often" }
-};
-
-static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
- _cleanup_free_ char *service_shell_quoted = NULL;
- const char *systemctl = "systemctl", *journalctl = "journalctl";
-
- assert(service);
-
- service_shell_quoted = shell_maybe_quote(service);
-
- if (extra_args && extra_args[1]) {
- _cleanup_free_ char *t;
-
- t = strv_join((char**) extra_args, " ");
- systemctl = strjoina("systemctl ", t ? : "<args>");
- journalctl = strjoina("journalctl ", t ? : "<args>");
- }
-
- if (!isempty(result)) {
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(explanations); ++i)
- if (streq(result, explanations[i].result))
- break;
-
- if (i < ELEMENTSOF(explanations)) {
- log_error("Job for %s failed because %s.\n"
- "See \"%s status %s\" and \"%s -xe\" for details.\n",
- service,
- explanations[i].explanation,
- systemctl,
- service_shell_quoted ?: "<service>",
- journalctl);
- goto finish;
- }
- }
-
- log_error("Job for %s failed.\n"
- "See \"%s status %s\" and \"%s -xe\" for details.\n",
- service,
- systemctl,
- service_shell_quoted ?: "<service>",
- journalctl);
-
-finish:
- /* For some results maybe additional explanation is required */
- if (streq_ptr(result, "start-limit"))
- log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
- "followed by \"%1$s start %2$s\" again.",
- systemctl,
- service_shell_quoted ?: "<service>");
-}
-
-static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
- int r = 0;
-
- assert(d->result);
-
- if (!quiet) {
- if (streq(d->result, "canceled"))
- log_error("Job for %s canceled.", strna(d->name));
- else if (streq(d->result, "timeout"))
- log_error("Job for %s timed out.", strna(d->name));
- else if (streq(d->result, "dependency"))
- log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
- else if (streq(d->result, "invalid"))
- log_error("%s is not active, cannot reload.", strna(d->name));
- else if (streq(d->result, "assert"))
- log_error("Assertion failed on job for %s.", strna(d->name));
- else if (streq(d->result, "unsupported"))
- log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
- else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
- if (d->name) {
- int q;
- _cleanup_free_ char *result = NULL;
-
- q = bus_job_get_service_result(d, &result);
- if (q < 0)
- log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
-
- log_job_error_with_service_result(d->name, result, extra_args);
- } else
- log_error("Job failed. See \"journalctl -xe\" for details.");
- }
- }
-
- if (streq(d->result, "canceled"))
- r = -ECANCELED;
- else if (streq(d->result, "timeout"))
- r = -ETIME;
- else if (streq(d->result, "dependency"))
- r = -EIO;
- else if (streq(d->result, "invalid"))
- r = -ENOEXEC;
- else if (streq(d->result, "assert"))
- r = -EPROTO;
- else if (streq(d->result, "unsupported"))
- r = -EOPNOTSUPP;
- else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
- r = -EIO;
-
- return r;
-}
-
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
- int r = 0;
-
- assert(d);
-
- while (!set_isempty(d->jobs)) {
- int q;
-
- q = bus_process_wait(d->bus);
- if (q < 0)
- return log_error_errno(q, "Failed to wait for response: %m");
-
- if (d->result) {
- q = check_wait_response(d, quiet, extra_args);
- /* Return the first error as it is most likely to be
- * meaningful. */
- if (q < 0 && r == 0)
- r = q;
-
- log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
- }
-
- d->name = mfree(d->name);
- d->result = mfree(d->result);
- }
-
- return r;
-}
-
-int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
- int r;
-
- assert(d);
-
- r = set_ensure_allocated(&d->jobs, &string_hash_ops);
- if (r < 0)
- return r;
-
- return set_put_strdup(d->jobs, path);
-}
-
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
- int r;
-
- r = bus_wait_for_jobs_add(d, path);
- if (r < 0)
- return log_oom();
-
- return bus_wait_for_jobs(d, quiet, NULL);
-}
-
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
- const char *type, *path, *source;
- int r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
- /* We expect only "success" changes to be sent over the bus.
- Hence, reject anything negative. */
- UnitFileChangeType ch = unit_file_change_type_from_string(type);
-
- if (ch < 0) {
- log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
- continue;
- }
-
- r = unit_file_changes_add(changes, n_changes, ch, path, source);
- if (r < 0)
- return r;
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return bus_log_parse_error(r);
-
- unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
- return 0;
-}
-
/**
* bus_path_encode_unique() - encode unique object path
* @b: bus connection or NULL
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index 1a0841ce81..d792258ecd 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -24,15 +24,12 @@
#include <stdint.h>
#include <sys/types.h>
-#include "sd-bus-vtable.h"
#include "sd-bus.h"
#include "sd-event.h"
#include "hashmap.h"
-#include "install.h"
#include "macro.h"
#include "string-util.h"
-#include "time-util.h"
typedef enum BusTransport {
BUS_TRANSPORT_LOCAL,
@@ -126,22 +123,6 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t));
int bus_log_parse_error(int r);
int bus_log_create_error(int r);
-typedef struct UnitInfo {
- const char *machine;
- const char *id;
- const char *description;
- const char *load_state;
- const char *active_state;
- const char *sub_state;
- const char *following;
- const char *unit_path;
- uint32_t job_id;
- const char *job_type;
- const char *job_path;
-} UnitInfo;
-
-int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
-
#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
int function(sd_bus *bus, \
const char *path, \
@@ -173,20 +154,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \
SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags))
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
-
-typedef struct BusWaitForJobs BusWaitForJobs;
-
-int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
-void bus_wait_for_jobs_free(BusWaitForJobs *d);
-int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args);
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
-
-int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes);
-
int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path);
int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index f3039b23f7..7539891bf2 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -37,23 +37,21 @@
#include "string-util.h"
#include "terminal-util.h"
-static int compare(const void *a, const void *b) {
- const pid_t *p = a, *q = b;
+static void show_pid_array(
+ pid_t pids[],
+ unsigned n_pids,
+ const char *prefix,
+ unsigned n_columns,
+ bool extra,
+ bool more,
+ OutputFlags flags) {
- if (*p < *q)
- return -1;
- if (*p > *q)
- return 1;
- return 0;
-}
-
-static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
unsigned i, j, pid_width;
if (n_pids == 0)
return;
- qsort(pids, n_pids, sizeof(pid_t), compare);
+ qsort(pids, n_pids, sizeof(pid_t), pid_compare_func);
/* Filter duplicates */
for (j = 0, i = 1; i < n_pids; i++) {
@@ -86,8 +84,13 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un
}
}
+static int show_cgroup_one_by_path(
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ bool more,
+ OutputFlags flags) {
-static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
char *fn;
_cleanup_fclose_ FILE *f = NULL;
size_t n = 0, n_allocated = 0;
@@ -107,7 +110,7 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
while ((r = cg_read_pid(f, &pid)) > 0) {
- if (!kernel_threads && is_kernel_thread(pid) > 0)
+ if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0)
continue;
if (!GREEDY_REALLOC(pids, n_allocated, n + 1))
@@ -120,12 +123,17 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne
if (r < 0)
return r;
- show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
+ show_pid_array(pids, n, prefix, n_columns, false, more, flags);
return 0;
}
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+int show_cgroup_by_path(
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
+
_cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
_cleanup_closedir_ DIR *d = NULL;
char *gn = NULL;
@@ -137,8 +145,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (n_columns <= 0)
n_columns = columns();
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
r = cg_mangle_path(path, &fn);
if (r < 0)
@@ -160,7 +167,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
continue;
if (!shown_pids) {
- show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
+ show_cgroup_one_by_path(path, prefix, n_columns, true, flags);
shown_pids = true;
}
@@ -173,7 +180,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return -ENOMEM;
}
- show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
+ show_cgroup_by_path(last, p1, n_columns-2, flags);
free(last);
}
@@ -185,7 +192,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return r;
if (!shown_pids)
- show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
+ show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
if (last) {
printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last)));
@@ -196,13 +203,17 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
return -ENOMEM;
}
- show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
+ show_cgroup_by_path(last, p2, n_columns-2, flags);
}
return 0;
}
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
+int show_cgroup(const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ OutputFlags flags) {
_cleanup_free_ char *p = NULL;
int r;
@@ -212,10 +223,18 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un
if (r < 0)
return r;
- return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
+ return show_cgroup_by_path(p, prefix, n_columns, flags);
}
-static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) {
+static int show_extra_pids(
+ const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t pids[],
+ unsigned n_pids,
+ OutputFlags flags) {
+
_cleanup_free_ pid_t *copy = NULL;
unsigned i, j;
int r;
@@ -247,24 +266,39 @@ static int show_extra_pids(const char *controller, const char *path, const char
copy[j++] = pids[i];
}
- show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
+ show_pid_array(copy, j, prefix, n_columns, true, false, flags);
return 0;
}
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
+int show_cgroup_and_extra(
+ const char *controller,
+ const char *path,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t extra_pids[],
+ unsigned n_extra_pids,
+ OutputFlags flags) {
+
int r;
assert(path);
- r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
+ r = show_cgroup(controller, path, prefix, n_columns, flags);
if (r < 0)
return r;
return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
+int show_cgroup_and_extra_by_spec(
+ const char *spec,
+ const char *prefix,
+ unsigned n_columns,
+ const pid_t extra_pids[],
+ unsigned n_extra_pids,
+ OutputFlags flags) {
+
_cleanup_free_ char *controller = NULL, *path = NULL;
int r;
@@ -274,5 +308,5 @@ int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned
if (r < 0)
return r;
- return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
+ return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index 3ab7dfb33c..5c1d6e6d98 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -25,8 +25,8 @@
#include "logs-show.h"
#include "output-mode.h"
-int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
-int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
+int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags);
+int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags);
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
-int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index cc1acd6f23..b9cd952ac8 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -160,7 +160,7 @@ static int iterate_dir(
if (!de)
break;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
f = strjoin(path, "/", de->d_name, NULL);
diff --git a/src/shared/install.c b/src/shared/install.c
index 71012eafb4..b74ff6de22 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -222,8 +222,8 @@ int unit_file_changes_add(
const char *path,
const char *source) {
+ _cleanup_free_ char *p = NULL, *s = NULL;
UnitFileChange *c;
- unsigned i;
assert(path);
assert(!changes == !n_changes);
@@ -234,29 +234,22 @@ int unit_file_changes_add(
c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
if (!c)
return -ENOMEM;
-
*changes = c;
- i = *n_changes;
- c[i].type = type;
- c[i].path = strdup(path);
- if (!c[i].path)
- return -ENOMEM;
+ p = strdup(path);
+ if (source)
+ s = strdup(source);
- path_kill_slashes(c[i].path);
-
- if (source) {
- c[i].source = strdup(source);
- if (!c[i].source) {
- free(c[i].path);
- return -ENOMEM;
- }
+ if (!p || (source && !s))
+ return -ENOMEM;
- path_kill_slashes(c[i].path);
- } else
- c[i].source = NULL;
+ path_kill_slashes(p);
+ if (s)
+ path_kill_slashes(s);
- *n_changes = i+1;
+ c[*n_changes] = (UnitFileChange) { type, p, s };
+ p = s = NULL;
+ (*n_changes) ++;
return 0;
}
@@ -265,9 +258,6 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
assert(changes || n_changes == 0);
- if (!changes)
- return;
-
for (i = 0; i < n_changes; i++) {
free(changes[i].path);
free(changes[i].source);
@@ -370,8 +360,14 @@ static int create_symlink(
}
r = readlink_malloc(new_path, &dest);
- if (r < 0)
+ if (r < 0) {
+ /* translate EINVAL (non-symlink exists) to EEXIST */
+ if (r == -EINVAL)
+ r = -EEXIST;
+
+ unit_file_changes_add(changes, n_changes, r, new_path, NULL);
return r;
+ }
if (path_equal(dest, old_path))
return 0;
@@ -382,8 +378,10 @@ static int create_symlink(
}
r = symlink_atomic(old_path, new_path);
- if (r < 0)
+ if (r < 0) {
+ unit_file_changes_add(changes, n_changes, r, new_path, NULL);
return r;
+ }
unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
@@ -521,8 +519,8 @@ static int remove_marked_symlinks_fd(
unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
- /* Now, remember the full path (but with the root prefix removed) of the symlink we just
- * removed, and remove any symlinks to it, too */
+ /* Now, remember the full path (but with the root prefix removed) of
+ * the symlink we just removed, and remove any symlinks to it, too. */
rp = skip_root(lp, p);
q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
@@ -1392,7 +1390,6 @@ static int install_info_symlink_wants(
const char *config_path,
char **list,
const char *suffix,
- bool force,
UnitFileChange **changes,
unsigned *n_changes) {
@@ -1440,7 +1437,7 @@ static int install_info_symlink_wants(
rp = skip_root(paths, i->path);
- q = create_symlink(rp ?: i->path, path, force, changes, n_changes);
+ q = create_symlink(rp ?: i->path, path, true, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1499,11 +1496,11 @@ static int install_info_apply(
r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
- q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
+ q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
if (r == 0)
r = q;
- q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", force, changes, n_changes);
+ q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
if (r == 0)
r = q;
@@ -1864,7 +1861,7 @@ int unit_file_revert(
* c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
* "config", but not in "transient" or "control" or even "generated").
*
- * We remove all that in both the runtime and the persistant directories, if that applies.
+ * We remove all that in both the runtime and the persistent directories, if that applies.
*/
r = lookup_paths_init(&paths, scope, 0, root_dir);
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index e2d2931c51..9351b85eed 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -287,7 +287,10 @@ static int output_short(
if (r < 0)
return r;
}
-
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
if (r < 0)
return log_error_errno(r, "Failed to get journal fields: %m");
@@ -344,16 +347,22 @@ static int output_short(
t = (time_t) (x / USEC_PER_SEC);
- switch(mode) {
+ switch (mode) {
+
+ case OUTPUT_SHORT_UNIX:
+ r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC));
+ break;
+
case OUTPUT_SHORT_ISO:
r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm));
break;
+
case OUTPUT_SHORT_PRECISE:
r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
if (r > 0)
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
break;
+
default:
r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
}
@@ -367,6 +376,12 @@ static int output_short(
n += strlen(buf);
}
+ if (hostname && (flags & OUTPUT_NO_HOSTNAME)) {
+ /* Suppress display of the hostname if this is requested. */
+ hostname = NULL;
+ hostname_len = 0;
+ }
+
if (hostname && shall_print(hostname, hostname_len, flags)) {
fprintf(f, " %.*s", (int) hostname_len, hostname);
n += hostname_len + 1;
@@ -894,6 +909,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
[OUTPUT_SHORT_ISO] = output_short,
[OUTPUT_SHORT_PRECISE] = output_short,
[OUTPUT_SHORT_MONOTONIC] = output_short,
+ [OUTPUT_SHORT_UNIX] = output_short,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
[OUTPUT_JSON] = output_json,
@@ -1040,8 +1056,8 @@ static int show_journal(FILE *f,
}
int add_matches_for_unit(sd_journal *j, const char *unit) {
+ const char *m1, *m2, *m3, *m4;
int r;
- char *m1, *m2, *m3, *m4;
assert(j);
assert(unit);
@@ -1073,7 +1089,9 @@ int add_matches_for_unit(sd_journal *j, const char *unit) {
);
if (r == 0 && endswith(unit, ".slice")) {
- char *m5 = strappend("_SYSTEMD_SLICE=", unit);
+ const char *m5;
+
+ m5 = strjoina("_SYSTEMD_SLICE=", unit);
/* Show all messages belonging to a slice */
(void)(
@@ -1123,7 +1141,9 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
);
if (r == 0 && endswith(unit, ".slice")) {
- char *m5 = strappend("_SYSTEMD_SLICE=", unit);
+ const char *m5;
+
+ m5 = strjoina("_SYSTEMD_SLICE=", unit);
/* Show all messages belonging to a slice */
(void)(
@@ -1288,18 +1308,3 @@ int show_journal_by_unit(
return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
}
-
-static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
- [OUTPUT_SHORT] = "short",
- [OUTPUT_SHORT_ISO] = "short-iso",
- [OUTPUT_SHORT_PRECISE] = "short-precise",
- [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
- [OUTPUT_VERBOSE] = "verbose",
- [OUTPUT_EXPORT] = "export",
- [OUTPUT_JSON] = "json",
- [OUTPUT_JSON_PRETTY] = "json-pretty",
- [OUTPUT_JSON_SSE] = "json-sse",
- [OUTPUT_CAT] = "cat"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 9765a24ff2..6643440881 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -68,6 +68,3 @@ void json_escape(
const char* p,
size_t l,
OutputFlags flags);
-
-const char* output_mode_to_string(OutputMode m) _const_;
-OutputMode output_mode_from_string(const char *s) _pure_;
diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c
new file mode 100644
index 0000000000..bec53ee0ae
--- /dev/null
+++ b/src/shared/output-mode.c
@@ -0,0 +1,37 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd 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.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "output-mode.h"
+#include "string-table.h"
+
+static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
+ [OUTPUT_SHORT] = "short",
+ [OUTPUT_SHORT_ISO] = "short-iso",
+ [OUTPUT_SHORT_PRECISE] = "short-precise",
+ [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
+ [OUTPUT_SHORT_UNIX] = "short-unix",
+ [OUTPUT_VERBOSE] = "verbose",
+ [OUTPUT_EXPORT] = "export",
+ [OUTPUT_JSON] = "json",
+ [OUTPUT_JSON_PRETTY] = "json-pretty",
+ [OUTPUT_JSON_SSE] = "json-sse",
+ [OUTPUT_CAT] = "cat"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);
diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h
index c5470e7c1b..f37189e57f 100644
--- a/src/shared/output-mode.h
+++ b/src/shared/output-mode.h
@@ -19,11 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "macro.h"
+
typedef enum OutputMode {
OUTPUT_SHORT,
OUTPUT_SHORT_ISO,
OUTPUT_SHORT_PRECISE,
OUTPUT_SHORT_MONOTONIC,
+ OUTPUT_SHORT_UNIX,
OUTPUT_VERBOSE,
OUTPUT_EXPORT,
OUTPUT_JSON,
@@ -34,6 +37,9 @@ typedef enum OutputMode {
_OUTPUT_MODE_INVALID = -1
} OutputMode;
+/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the
+ * logs output, others only to the process tree output. */
+
typedef enum OutputFlags {
OUTPUT_SHOW_ALL = 1 << 0,
OUTPUT_FOLLOW = 1 << 1,
@@ -43,4 +49,9 @@ typedef enum OutputFlags {
OUTPUT_CATALOG = 1 << 5,
OUTPUT_BEGIN_NEWLINE = 1 << 6,
OUTPUT_UTC = 1 << 7,
+ OUTPUT_KERNEL_THREADS = 1 << 8,
+ OUTPUT_NO_HOSTNAME = 1 << 9,
} OutputFlags;
+
+const char* output_mode_to_string(OutputMode m) _const_;
+OutputMode output_mode_from_string(const char *s) _pure_;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 80a2ea7940..ca593b6963 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -586,9 +586,16 @@ int lookup_paths_init(
if (!add)
return -ENOMEM;
- r = strv_extend_strv(&paths, add, true);
- if (r < 0)
+ if (paths) {
+ r = strv_extend_strv(&paths, add, true);
+ if (r < 0)
return r;
+ } else {
+ /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+ * and don't have to copy anything */
+ paths = add;
+ add = NULL;
+ }
}
r = patch_root_prefix(&persistent_config, root);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 115c00ea9c..595d6853c6 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -39,6 +39,7 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-message.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -324,6 +325,8 @@ static int compare_unit_info(const void *a, const void *b) {
}
static bool output_show_unit(const UnitInfo *u, char **patterns) {
+ assert(u);
+
if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
return false;
@@ -341,6 +344,12 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
if (arg_all)
return true;
+ /* Note that '--all' is not purely a state filter, but also a
+ * filter that hides units that "follow" other units (which is
+ * used for device units that appear under different names). */
+ if (!isempty(u->following))
+ return false;
+
if (!strv_isempty(arg_states))
return true;
@@ -349,7 +358,7 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
if (u->job_id > 0)
return true;
- if (streq(u->active_state, "inactive") || u->following[0])
+ if (streq(u->active_state, "inactive"))
return false;
return true;
@@ -1274,6 +1283,8 @@ static int compare_unit_file_list(const void *a, const void *b) {
}
static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
+ assert(u);
+
if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE))
return false;
@@ -3428,6 +3439,7 @@ typedef struct UnitStatusInfo {
} UnitStatusInfo;
static void print_status_info(
+ sd_bus *bus,
UnitStatusInfo *i,
bool *ellipsized) {
@@ -3438,6 +3450,7 @@ static void print_status_info(
char since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *path;
char **t, **t2;
+ int r;
assert(i);
@@ -3710,25 +3723,26 @@ static void print_status_info(
printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC));
}
- if (i->control_group &&
- (i->main_pid > 0 || i->control_pid > 0 ||
- (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) {
+ if (i->control_group)
+ printf(" CGroup: %s\n", i->control_group);
+
+ {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ static const char prefix[] = " ";
unsigned c;
- printf(" CGroup: %s\n", i->control_group);
+ c = columns();
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
+ else
+ c = 0;
- if (IN_SET(arg_transport,
- BUS_TRANSPORT_LOCAL,
- BUS_TRANSPORT_MACHINE)) {
+ r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error);
+ if (r == -EBADR) {
unsigned k = 0;
pid_t extra[2];
- static const char prefix[] = " ";
- c = columns();
- if (c > sizeof(prefix) - 1)
- c -= sizeof(prefix) - 1;
- else
- c = 0;
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
if (i->main_pid > 0)
extra[k++] = i->main_pid;
@@ -3736,8 +3750,9 @@ static void print_status_info(
if (i->control_pid > 0)
extra[k++] = i->control_pid;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags());
- }
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, extra, k, get_output_flags());
+ } else if (r < 0)
+ log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r));
}
if (i->id && arg_transport == BUS_TRANSPORT_LOCAL)
@@ -4498,7 +4513,7 @@ static int show_one(
if (streq(verb, "help"))
show_unit_help(&info);
else
- print_status_info(&info, ellipsized);
+ print_status_info(bus, &info, ellipsized);
}
strv_free(info.documentation);
@@ -4652,7 +4667,7 @@ static int show_system_status(sd_bus *bus) {
else
c = 0;
- show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, get_output_flags());
+ show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags());
}
return 0;
@@ -5384,6 +5399,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
int carries_install_info = -1;
+ bool ignore_carries_install_info = false;
int r;
if (!argv[1])
@@ -5414,7 +5430,6 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
else if (streq(verb, "preset")) {
r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes);
- carries_install_info = r;
} else if (streq(verb, "mask"))
r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
else if (streq(verb, "unmask"))
@@ -5431,7 +5446,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
} else {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int expect_carries_install_info = false;
+ bool expect_carries_install_info = false;
bool send_runtime = true, send_force = true, send_preset_mode = false;
const char *method;
sd_bus *bus;
@@ -5462,6 +5477,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
method = "PresetUnitFiles";
expect_carries_install_info = true;
+ ignore_carries_install_info = true;
} else if (streq(verb, "mask"))
method = "MaskUnitFiles";
else if (streq(verb, "unmask")) {
@@ -5526,7 +5542,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
r = 0;
}
- if (carries_install_info == 0)
+ if (carries_install_info == 0 && !ignore_carries_install_info)
log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n"
"settings in the [Install] section, and DefaultInstance for template units).\n"
"This means they are not meant to be enabled using systemctl.\n"
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index d4c6f409cd..9c36b27157 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -67,10 +67,11 @@ typedef struct sd_journal sd_journal;
/* Open flags */
enum {
- SD_JOURNAL_LOCAL_ONLY = 1,
- SD_JOURNAL_RUNTIME_ONLY = 2,
- SD_JOURNAL_SYSTEM = 4,
- SD_JOURNAL_CURRENT_USER = 8,
+ SD_JOURNAL_LOCAL_ONLY = 1 << 0,
+ SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
+ SD_JOURNAL_SYSTEM = 1 << 2,
+ SD_JOURNAL_CURRENT_USER = 1 << 3,
+ SD_JOURNAL_OS_ROOT = 1 << 4,
SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */
};
@@ -84,8 +85,10 @@ enum {
int sd_journal_open(sd_journal **ret, int flags);
int sd_journal_open_directory(sd_journal **ret, const char *path, int flags);
+int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags);
int sd_journal_open_files(sd_journal **ret, const char **paths, int flags);
-int sd_journal_open_container(sd_journal **ret, const char *machine, int flags);
+int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags);
+int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */
void sd_journal_close(sd_journal *j);
int sd_journal_previous(sd_journal *j);
diff --git a/src/test/test-nss.c b/src/test/test-nss.c
index 9c13288d2e..55af592287 100644
--- a/src/test/test-nss.c
+++ b/src/test/test-nss.c
@@ -381,12 +381,18 @@ static void test_byaddr(void *handle,
#ifdef HAVE_MYHOSTNAME
# define MODULE1 "myhostname\0"
+#else
+# define MODULE1
#endif
#ifdef HAVE_RESOLVED
# define MODULE2 "resolve\0"
+#else
+# define MODULE2
#endif
#ifdef HAVE_MACHINED
# define MODULE3 "mymachines\0"
+#else
+# define MODULE3
#endif
#define MODULE4 "dns\0"
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 5d77e2959c..b53324b5e6 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -489,6 +489,27 @@ static void test_filename_is_valid(void) {
assert_se(filename_is_valid("o.o"));
}
+static void test_hidden_or_backup_file(void) {
+ assert_se(hidden_or_backup_file(".hidden"));
+ assert_se(hidden_or_backup_file("..hidden"));
+ assert_se(!hidden_or_backup_file("hidden."));
+
+ assert_se(hidden_or_backup_file("backup~"));
+ assert_se(hidden_or_backup_file(".backup~"));
+
+ assert_se(hidden_or_backup_file("lost+found"));
+ assert_se(hidden_or_backup_file("aquota.user"));
+ assert_se(hidden_or_backup_file("aquota.group"));
+
+ assert_se(hidden_or_backup_file("test.rpmnew"));
+ assert_se(hidden_or_backup_file("test.dpkg-old"));
+ assert_se(hidden_or_backup_file("test.dpkg-remove"));
+ assert_se(hidden_or_backup_file("test.swp"));
+
+ assert_se(!hidden_or_backup_file("test.rpmnew."));
+ assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
+}
+
int main(int argc, char **argv) {
test_path();
test_find_binary(argv[0]);
@@ -502,6 +523,7 @@ int main(int argc, char **argv) {
test_path_is_mount_point();
test_file_in_same_dir();
test_filename_is_valid();
+ test_hidden_or_backup_file();
return 0;
}
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index d7223dd2bf..b34ebeefb2 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -32,15 +32,17 @@
#include "util.h"
int main(int argc, char** argv) {
+ _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL;
+ _cleanup_close_ int fd = -1, fd2 = -1;
const char *p = argv[1] ?: "/tmp";
- char *pattern = strjoina(p, "/systemd-test-XXXXXX");
- _cleanup_close_ int fd, fd2;
- _cleanup_free_ char *cmd, *cmd2, *ans, *ans2;
+ char *pattern;
log_set_max_level(LOG_DEBUG);
log_parse_environment();
- fd = open_tmpfile(p, O_RDWR|O_CLOEXEC);
+ pattern = strjoina(p, "/systemd-test-XXXXXX");
+
+ fd = open_tmpfile_unlinkable(p, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0);
@@ -59,5 +61,21 @@ int main(int argc, char** argv) {
log_debug("link2: %s", ans2);
assert_se(endswith(ans2, " (deleted)"));
+ pattern = strjoina(p, "/tmpfiles-test");
+ assert_se(tempfn_random(pattern, NULL, &d) >= 0);
+
+ fd = open_tmpfile_linkable(d, O_RDWR|O_CLOEXEC, &tmp);
+ assert_se(fd >= 0);
+ assert_se(write(fd, "foobar\n", 7) == 7);
+
+ assert_se(touch(d) >= 0);
+ assert_se(link_tmpfile(fd, tmp, d) == -EEXIST);
+ assert_se(unlink(d) >= 0);
+ assert_se(link_tmpfile(fd, tmp, d) >= 0);
+
+ assert_se(read_one_line_file(d, &line) >= 0);
+ assert_se(streq(line, "foobar"));
+ assert_se(unlink(d) >= 0);
+
return 0;
}
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index efd264b34d..2053d35a67 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -94,6 +94,7 @@ typedef enum ItemType {
/* These ones take globs */
WRITE_FILE = 'w',
+ EMPTY_DIRECTORY = 'e',
SET_XATTR = 't',
RECURSIVE_SET_XATTR = 'T',
SET_ACL = 'a',
@@ -179,6 +180,7 @@ static bool needs_glob(ItemType t) {
IGNORE_DIRECTORY_PATH,
REMOVE_PATH,
RECURSIVE_REMOVE_PATH,
+ EMPTY_DIRECTORY,
ADJUST_MODE,
RELABEL_PATH,
RECURSIVE_RELABEL_PATH,
@@ -195,6 +197,7 @@ static bool takes_ownership(ItemType t) {
CREATE_FILE,
TRUNCATE_FILE,
CREATE_DIRECTORY,
+ EMPTY_DIRECTORY,
TRUNCATE_DIRECTORY,
CREATE_SUBVOLUME,
CREATE_SUBVOLUME_INHERIT_QUOTA,
@@ -1217,7 +1220,6 @@ static int create_item(Item *i) {
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
-
RUN_WITH_UMASK(0000)
mkdir_parents_label(i->path, 0755);
@@ -1276,11 +1278,11 @@ static int create_item(Item *i) {
if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
if (r == -ENOTTY)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path);
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
else if (r == -EROFS)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path);
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
else if (r == -ENOPROTOOPT)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path);
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
else if (r < 0)
q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
else if (r > 0)
@@ -1289,6 +1291,9 @@ static int create_item(Item *i) {
log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
}
+ /* fall through */
+
+ case EMPTY_DIRECTORY:
r = path_set_perms(i, i->path);
if (q < 0)
return q;
@@ -1298,7 +1303,6 @@ static int create_item(Item *i) {
break;
case CREATE_FIFO:
-
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo(i->path, i->mode);
@@ -1535,47 +1539,20 @@ static int remove_item_instance(Item *i, const char *instance) {
}
static int remove_item(Item *i) {
- int r = 0;
-
assert(i);
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case CREATE_DIRECTORY:
- case CREATE_SUBVOLUME:
- case CREATE_SUBVOLUME_INHERIT_QUOTA:
- case CREATE_SUBVOLUME_NEW_QUOTA:
- case CREATE_FIFO:
- case CREATE_SYMLINK:
- case CREATE_CHAR_DEVICE:
- case CREATE_BLOCK_DEVICE:
- case IGNORE_PATH:
- case IGNORE_DIRECTORY_PATH:
- case ADJUST_MODE:
- case RELABEL_PATH:
- case RECURSIVE_RELABEL_PATH:
- case WRITE_FILE:
- case COPY_FILES:
- case SET_XATTR:
- case RECURSIVE_SET_XATTR:
- case SET_ACL:
- case RECURSIVE_SET_ACL:
- case SET_ATTRIBUTE:
- case RECURSIVE_SET_ATTRIBUTE:
- break;
-
case REMOVE_PATH:
case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
- r = glob_item(i, remove_item_instance, false);
- break;
- }
+ return glob_item(i, remove_item_instance, false);
- return r;
+ default:
+ return 0;
+ }
}
static int clean_item_instance(Item *i, const char* instance) {
@@ -1630,8 +1607,6 @@ static int clean_item_instance(Item *i, const char* instance) {
}
static int clean_item(Item *i) {
- int r = 0;
-
assert(i);
log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
@@ -1641,19 +1616,17 @@ static int clean_item(Item *i) {
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
+ case EMPTY_DIRECTORY:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
case COPY_FILES:
clean_item_instance(i, i->path);
- break;
+ return 0;
case IGNORE_DIRECTORY_PATH:
- r = glob_item(i, clean_item_instance, false);
- break;
+ return glob_item(i, clean_item_instance, false);
default:
- break;
+ return 0;
}
-
- return r;
}
static int process_item_array(ItemArray *array);
@@ -1879,6 +1852,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
+ case EMPTY_DIRECTORY:
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
@@ -2198,7 +2172,8 @@ static int parse_argv(int argc, char *argv[]) {
}
static int read_config_file(const char *fn, bool ignore_enoent) {
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_fclose_ FILE *_f = NULL;
+ FILE *f;
char line[LINE_MAX];
Iterator iterator;
unsigned v = 0;
@@ -2207,16 +2182,23 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
assert(fn);
- r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
- if (r < 0) {
- if (ignore_enoent && r == -ENOENT) {
- log_debug_errno(r, "Failed to open \"%s\": %m", fn);
- return 0;
- }
+ if (streq(fn, "-")) {
+ log_debug("Reading config from stdin.");
+ fn = "<stdin>";
+ f = stdin;
+ } else {
+ r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT) {
+ log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
+ return 0;
+ }
- return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
+ return log_error_errno(r, "Failed to open '%s': %m", fn);
+ }
+ log_debug("Reading config file \"%s\".", fn);
+ f = _f;
}
- log_debug("Reading config file \"%s\".", fn);
FOREACH_LINE(line, f, break) {
char *l;
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 7b67831e54..c7ded451a2 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -481,7 +481,7 @@ static int show_passwords(void) {
if (de->d_type != DT_REG)
continue;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
if (!startswith(de->d_name, "ask."))
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 8b1bcefe2d..a7be2a4eed 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -27,21 +27,21 @@
* http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames
*
* Two character prefixes based on the type of interface:
- * en -- Ethernet
- * sl -- serial line IP (slip)
- * wl -- wlan
- * ww -- wwan
+ * en — Ethernet
+ * sl — serial line IP (slip)
+ * wl — wlan
+ * ww — wwan
*
* Type of names:
- * b<number> -- BCMA bus core number
- * c<bus_id> -- CCW bus group name, without leading zeros [s390]
- * o<index>[d<dev_port>] -- on-board device index number
- * s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
- * x<MAC> -- MAC address
+ * b<number> — BCMA bus core number
+ * c<bus_id> — CCW bus group name, without leading zeros [s390]
+ * o<index>[d<dev_port>] — on-board device index number
+ * s<slot>[f<function>][d<dev_port>] — hotplug slot index number
+ * x<MAC> — MAC address
* [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
- * -- PCI geographical location
+ * — PCI geographical location
* [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
- * -- USB port number chain
+ * — USB port number chain
*
* All multi-function PCI devices will carry the [f<function>] number in the
* device name, including the function 0 device.
@@ -140,9 +140,9 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
const char *attr;
int idx;
- /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */
+ /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
- /* SMBIOS type 41 -- Onboard Devices Extended Information */
+ /* SMBIOS type 41 — Onboard Devices Extended Information */
if (!attr)
attr = udev_device_get_sysattr_value(names->pcidev, "index");
if (!attr)
@@ -230,7 +230,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (l == 0)
names->pci_path[0] = '\0';
- /* ACPI _SUN -- slot user number */
+ /* ACPI _SUN — slot user number */
pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
if (!pci) {
err = -ENOENT;
diff --git a/test/TEST-09-ISSUE-2691/Makefile b/test/TEST-09-ISSUE-2691/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-09-ISSUE-2691/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-09-ISSUE-2691/test.sh b/test/TEST-09-ISSUE-2691/test.sh
new file mode 100755
index 0000000000..e247694f01
--- /dev/null
+++ b/test/TEST-09-ISSUE-2691/test.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
+
+. $TEST_BASE_DIR/test-functions
+SKIP_INITRD=yes
+QEMU_TIMEOUT=90
+
+check_result_qemu() {
+ ret=1
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+ [[ -e $TESTDIR/root/testok ]] && ret=0
+ [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
+ cp -a $TESTDIR/root/var/log/journal $TESTDIR
+ umount $TESTDIR/root
+ [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
+ ls -l $TESTDIR/journal/*/*.journal
+ test -s $TESTDIR/failed && ret=$(($ret+1))
+ return $ret
+}
+
+test_run() {
+ run_qemu || return 1
+ check_result_qemu || return 1
+ return 0
+}
+
+test_setup() {
+ create_empty_image
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<'EOF'
+[Unit]
+Description=Testsuite service
+After=multi-user.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -c '>/testok'
+RemainAfterExit=yes
+ExecStop=/bin/sh -c 'kill -SEGV $$$$'
+TimeoutStopSec=180s
+EOF
+
+ setup_testsuite
+ ) || return 1
+
+ # mask some services that we do not want to run in these tests
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+
+ ddebug "umount $TESTDIR/root"
+ umount $TESTDIR/root
+}
+
+test_cleanup() {
+ umount $TESTDIR/root 2>/dev/null
+ [[ $LOOPDEV ]] && losetup -d $LOOPDEV
+ return 0
+}
+
+do_test "$@"
diff --git a/test/networkd-test.py b/test/networkd-test.py
index d76ab507d2..d4de5adf1a 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -5,7 +5,12 @@
# does not write anything on disk or change any system configuration;
# but it assumes (and checks at the beginning) that networkd is not currently
# running.
-# This can be run on a normal installation, in QEMU, nspawn, or LXC.
+#
+# This can be run on a normal installation, in QEMU, nspawn (with
+# --private-network), LXD (with "--config raw.lxc=lxc.aa_profile=unconfined"),
+# or LXC system containers. You need at least the "ip" tool from the iproute
+# package; it is recommended to install dnsmasq too to get full test coverage.
+#
# ATTENTION: This uses the *installed* networkd, not the one from the built
# source tree.
#
diff --git a/test/sysv-generator-test.py b/test/sysv-generator-test.py
index aca5f1eec6..aadc29ebeb 100644..100755
--- a/test/sysv-generator-test.py
+++ b/test/sysv-generator-test.py
@@ -1,3 +1,5 @@
+#!/usr/bin/python
+#
# systemd-sysv-generator integration test
#
# (C) 2015 Canonical Ltd.
diff --git a/units/emergency.service.in b/units/emergency.service.in
index fb390eacfe..0de16f24e8 100644
--- a/units/emergency.service.in
+++ b/units/emergency.service.in
@@ -11,6 +11,7 @@ Documentation=man:sulogin(8)
DefaultDependencies=no
Conflicts=shutdown.target
Conflicts=rescue.service
+Conflicts=syslog.socket
Before=shutdown.target
[Service]
diff --git a/units/rc-local.service.in b/units/rc-local.service.in
index d4db1747ed..480dddbe37 100644
--- a/units/rc-local.service.in
+++ b/units/rc-local.service.in
@@ -17,3 +17,4 @@ Type=forking
ExecStart=@RC_LOCAL_SCRIPT_PATH_START@ start
TimeoutSec=0
RemainAfterExit=yes
+GuessMainPID=no
diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in
index 3710c595ca..685baab21d 100644
--- a/units/systemd-machined.service.in
+++ b/units/systemd-machined.service.in
@@ -15,7 +15,7 @@ After=machine.slice
[Service]
ExecStart=@rootlibexecdir@/systemd-machined
BusName=org.freedesktop.machine1
-CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID
+CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD
WatchdogSec=3min
# Note that machined cannot be placed in a mount namespace, since it
diff --git a/units/systemd-nspawn@.service.in b/units/systemd-nspawn@.service.in
index 1927c4d485..ea28941507 100644
--- a/units/systemd-nspawn@.service.in
+++ b/units/systemd-nspawn@.service.in
@@ -13,7 +13,7 @@ Before=machines.target
After=network.target
[Service]
-ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth --settings=override --machine=%i
+ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i
KillMode=mixed
Type=notify
RestartForceExitStatus=133
diff --git a/units/systemd-user-sessions.service.in b/units/systemd-user-sessions.service.in
index c09c05d4d5..b4ea5a134b 100644
--- a/units/systemd-user-sessions.service.in
+++ b/units/systemd-user-sessions.service.in
@@ -8,7 +8,7 @@
[Unit]
Description=Permit User Sessions
Documentation=man:systemd-user-sessions.service(8)
-After=remote-fs.target nss-user-lookup.target
+After=remote-fs.target nss-user-lookup.target network.target
[Service]
Type=oneshot
diff --git a/units/tmp.mount.m4 b/units/tmp.mount.m4
index 00a0d28722..1448bd268a 100644
--- a/units/tmp.mount.m4
+++ b/units/tmp.mount.m4
@@ -13,6 +13,7 @@ ConditionPathIsSymbolicLink=!/tmp
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target
+After=swap.target
[Mount]
What=tmpfs