[systemd-commits] 57 commits - catalog/systemd.zh_TW.catalog configure.ac Makefile.am man/systemd-cgtop.xml man/systemd.network.xml po/LINGUAS po/zh_TW.po README rules/60-persistent-storage.rules shell-completion/zsh src/basic src/cgtop src/core src/journal src/libsystemd src/libsystemd-network src/libsystemd-terminal src/login src/network src/nspawn src/resolve src/shared src/systemd src/test
David Herrmann
dvdhrm at kemper.freedesktop.org
Sun Jun 14 03:58:09 PDT 2015
Makefile.am | 1391 ++----
README | 4
catalog/systemd.zh_TW.catalog | 263 +
configure.ac | 7
man/systemd-cgtop.xml | 23
man/systemd.network.xml | 7
po/LINGUAS | 1
po/zh_TW.po | 477 ++
rules/60-persistent-storage.rules | 5
shell-completion/zsh/_systemctl.in | 15
src/basic/.gitignore | 16
src/basic/Makefile | 1
src/basic/MurmurHash2.c | 86
src/basic/MurmurHash2.h | 33
src/basic/af-list.c | 58
src/basic/af-list.h | 27
src/basic/arphrd-list.c | 58
src/basic/arphrd-list.h | 27
src/basic/async.c | 92
src/basic/async.h | 27
src/basic/audit.c | 94
src/basic/audit.h | 33
src/basic/barrier.c | 416 +
src/basic/barrier.h | 91
src/basic/blkid-util.h | 33
src/basic/btrfs-ctree.h | 98
src/basic/btrfs-util.c | 1152 +++++
src/basic/btrfs-util.h | 87
src/basic/build.h | 157
src/basic/bus-label.c | 100
src/basic/bus-label.h | 32
src/basic/calendarspec.c | 1006 ++++
src/basic/calendarspec.h | 57
src/basic/cap-list.c | 65
src/basic/cap-list.h | 26
src/basic/capability.c | 307 +
src/basic/capability.h | 45
src/basic/cgroup-util.c | 1917 +++++++++
src/basic/cgroup-util.h | 139
src/basic/clock-util.c | 142
src/basic/clock-util.h | 29
src/basic/conf-files.c | 174
src/basic/conf-files.h | 28
src/basic/copy.c | 507 ++
src/basic/copy.h | 35
src/basic/def.h | 86
src/basic/device-nodes.c | 80
src/basic/device-nodes.h | 25
src/basic/env-util.c | 594 ++
src/basic/env-util.h | 49
src/basic/errno-list.c | 58
src/basic/errno-list.h | 27
src/basic/ether-addr-util.h | 27
src/basic/exit-status.c | 241 +
src/basic/exit-status.h | 103
src/basic/fdset.c | 285 +
src/basic/fdset.h | 57
src/basic/fileio-label.c | 68
src/basic/fileio-label.h | 31
src/basic/fileio.c | 817 +++
src/basic/fileio.h | 45
src/basic/gunicode.c | 110
src/basic/gunicode.h | 30
src/basic/hashmap.c | 1854 ++++++++
src/basic/hashmap.h | 420 ++
src/basic/hostname-util.c | 193
src/basic/hostname-util.h | 39
src/basic/in-addr-util.c | 338 +
src/basic/in-addr-util.h | 53
src/basic/ioprio.h | 55
src/basic/json.c | 866 ++++
src/basic/json.h | 88
src/basic/label.c | 80
src/basic/label.h | 30
src/basic/linux/Makefile | 1
src/basic/list.h | 158
src/basic/locale-util.c | 224 +
src/basic/locale-util.h | 54
src/basic/lockfile-util.c | 154
src/basic/lockfile-util.h | 39
src/basic/log.c | 1138 +++++
src/basic/log.h | 229 +
src/basic/login-util.c | 31
src/basic/login-util.h | 26
src/basic/macro.h | 473 ++
src/basic/memfd-util.c | 171
src/basic/memfd-util.h | 35
src/basic/mempool.c | 103
src/basic/mempool.h | 49
src/basic/missing.h | 1001 ++++
src/basic/mkdir-label.c | 39
src/basic/mkdir.c | 125
src/basic/mkdir.h | 40
src/basic/ordered-set.h | 59
src/basic/path-util.c | 853 ++++
src/basic/path-util.h | 102
src/basic/prioq.c | 308 +
src/basic/prioq.h | 42
src/basic/process-util.c | 539 ++
src/basic/process-util.h | 65
src/basic/random-util.c | 129
src/basic/random-util.h | 38
src/basic/ratelimit.c | 55
src/basic/ratelimit.h | 57
src/basic/refcnt.h | 34
src/basic/replace-var.c | 111
src/basic/replace-var.h | 24
src/basic/ring.c | 209 +
src/basic/ring.h | 56
src/basic/rm-rf.c | 224 +
src/basic/rm-rf.h | 34
src/basic/securebits.h | 45
src/basic/selinux-util.c | 462 ++
src/basic/selinux-util.h | 47
src/basic/set.h | 134
src/basic/sigbus.c | 152
src/basic/sigbus.h | 27
src/basic/signal-util.c | 268 +
src/basic/signal-util.h | 41
src/basic/siphash24.c | 135
src/basic/siphash24.h | 6
src/basic/smack-util.c | 208
src/basic/smack-util.h | 36
src/basic/socket-label.c | 164
src/basic/socket-util.c | 769 +++
src/basic/socket-util.h | 120
src/basic/sparse-endian.h | 88
src/basic/special.h | 117
src/basic/strbuf.c | 201
src/basic/strbuf.h | 54
src/basic/strv.c | 704 +++
src/basic/strv.h | 155
src/basic/strxcpyx.c | 100
src/basic/strxcpyx.h | 31
src/basic/terminal-util.c | 1072 +++++
src/basic/terminal-util.h | 109
src/basic/time-util.c | 997 ++++
src/basic/time-util.h | 111
src/basic/unaligned.h | 66
src/basic/unit-name.c | 834 +++
src/basic/unit-name.h | 177
src/basic/utf8.c | 402 +
src/basic/utf8.h | 57
src/basic/util.c | 6048 +++++++++++++++++++++++++++++
src/basic/util.h | 903 ++++
src/basic/verbs.c | 90
src/basic/verbs.h | 34
src/basic/virt.c | 406 +
src/basic/virt.h | 35
src/basic/xml.c | 254 +
src/basic/xml.h | 34
src/cgtop/cgtop.c | 53
src/core/ima-setup.c | 39
src/core/kmod-setup.c | 9
src/core/loopback-setup.c | 2
src/core/selinux-access.c | 28
src/core/service.c | 9
src/core/umount.c | 2
src/core/unit.c | 11
src/journal/journald-server.c | 204
src/journal/journald-syslog.c | 3
src/libsystemd-network/sd-dhcp-lease.c | 12
src/libsystemd-network/test-pppoe.c | 2
src/libsystemd-terminal/grdev-drm.c | 2
src/libsystemd-terminal/grdev.c | 2
src/libsystemd-terminal/idev.c | 2
src/libsystemd/sd-bus/bus-internal.c | 26
src/libsystemd/sd-bus/bus-internal.h | 2
src/libsystemd/sd-bus/bus-util.c | 2085 ---------
src/libsystemd/sd-bus/bus-util.h | 211 -
src/libsystemd/sd-login/sd-login.c | 2
src/libsystemd/sd-network/sd-network.c | 67
src/libsystemd/sd-rtnl/local-addresses.c | 4
src/libsystemd/sd-rtnl/rtnl-message.c | 48
src/libsystemd/sd-rtnl/rtnl-util.c | 4
src/libsystemd/sd-rtnl/sd-rtnl.c | 109
src/libsystemd/sd-rtnl/test-rtnl.c | 14
src/login/logind-session.h | 2
src/login/test-login-shared.c | 2
src/network/networkctl.c | 6
src/network/networkd-link.c | 6
src/network/networkd-manager.c | 4
src/network/networkd-netdev-bond.c | 5
src/network/networkd-netdev-ipvlan.c | 10
src/network/networkd-netdev-macvlan.c | 10
src/network/networkd-network-gperf.gperf | 130
src/network/networkd-network.c | 50
src/network/networkd-wait-online-manager.c | 2
src/network/networkd.h | 3
src/nspawn/nspawn.c | 12
src/resolve/resolved-bus.c | 2
src/resolve/resolved-dns-answer.c | 2
src/resolve/resolved-dns-domain.c | 613 --
src/resolve/resolved-dns-domain.h | 50
src/resolve/resolved-dns-packet.c | 2
src/resolve/resolved-dns-question.c | 2
src/resolve/resolved-dns-rr.c | 2
src/resolve/resolved-dns-scope.c | 2
src/resolve/resolved-dns-zone.c | 2
src/resolve/resolved-manager.c | 4
src/resolve/test-dns-domain.c | 192
src/shared/.gitignore | 16
src/shared/Makefile | 1
src/shared/MurmurHash2.c | 86
src/shared/MurmurHash2.h | 33
src/shared/af-list.c | 58
src/shared/af-list.h | 27
src/shared/arphrd-list.c | 58
src/shared/arphrd-list.h | 27
src/shared/async.c | 92
src/shared/async.h | 27
src/shared/audit.c | 94
src/shared/audit.h | 33
src/shared/barrier.c | 416 -
src/shared/barrier.h | 91
src/shared/blkid-util.h | 33
src/shared/btrfs-ctree.h | 98
src/shared/btrfs-util.c | 1152 -----
src/shared/btrfs-util.h | 87
src/shared/build.h | 157
src/shared/bus-label.c | 100
src/shared/bus-label.h | 32
src/shared/bus-util.c | 2060 +++++++++
src/shared/bus-util.h | 209 +
src/shared/calendarspec.c | 1006 ----
src/shared/calendarspec.h | 57
src/shared/cap-list.c | 65
src/shared/cap-list.h | 26
src/shared/capability.c | 307 -
src/shared/capability.h | 45
src/shared/cgroup-util.c | 1921 ---------
src/shared/cgroup-util.h | 139
src/shared/clock-util.c | 142
src/shared/clock-util.h | 29
src/shared/conf-files.c | 174
src/shared/conf-files.h | 28
src/shared/copy.c | 507 --
src/shared/copy.h | 35
src/shared/def.h | 86
src/shared/device-nodes.c | 80
src/shared/device-nodes.h | 25
src/shared/dns-domain.c | 613 ++
src/shared/dns-domain.h | 59
src/shared/env-util.c | 594 --
src/shared/env-util.h | 49
src/shared/errno-list.c | 58
src/shared/errno-list.h | 27
src/shared/ether-addr-util.h | 27
src/shared/exit-status.c | 241 -
src/shared/exit-status.h | 103
src/shared/fdset.c | 285 -
src/shared/fdset.h | 57
src/shared/fileio-label.c | 68
src/shared/fileio-label.h | 31
src/shared/fileio.c | 817 ---
src/shared/fileio.h | 45
src/shared/gunicode.c | 110
src/shared/gunicode.h | 30
src/shared/hashmap.c | 1854 --------
src/shared/hashmap.h | 420 --
src/shared/hostname-util.c | 193
src/shared/hostname-util.h | 39
src/shared/in-addr-util.c | 338 -
src/shared/in-addr-util.h | 53
src/shared/ioprio.h | 55
src/shared/json.c | 866 ----
src/shared/json.h | 88
src/shared/label.c | 80
src/shared/label.h | 30
src/shared/linux/Makefile | 1
src/shared/list.h | 158
src/shared/locale-util.c | 224 -
src/shared/locale-util.h | 54
src/shared/lockfile-util.c | 154
src/shared/lockfile-util.h | 39
src/shared/log.c | 1138 -----
src/shared/log.h | 229 -
src/shared/login-shared.c | 31
src/shared/login-shared.h | 26
src/shared/macro.h | 473 --
src/shared/memfd-util.c | 171
src/shared/memfd-util.h | 35
src/shared/mempool.c | 103
src/shared/mempool.h | 49
src/shared/missing.h | 1001 ----
src/shared/mkdir-label.c | 39
src/shared/mkdir.c | 125
src/shared/mkdir.h | 40
src/shared/ordered-set.h | 59
src/shared/path-util.c | 853 ----
src/shared/path-util.h | 102
src/shared/prioq.c | 308 -
src/shared/prioq.h | 42
src/shared/process-util.c | 539 --
src/shared/process-util.h | 65
src/shared/random-util.c | 129
src/shared/random-util.h | 38
src/shared/ratelimit.c | 55
src/shared/ratelimit.h | 57
src/shared/refcnt.h | 34
src/shared/replace-var.c | 111
src/shared/replace-var.h | 24
src/shared/ring.c | 209 -
src/shared/ring.h | 56
src/shared/rm-rf.c | 224 -
src/shared/rm-rf.h | 34
src/shared/securebits.h | 45
src/shared/selinux-util.c | 462 --
src/shared/selinux-util.h | 47
src/shared/set.h | 134
src/shared/sigbus.c | 152
src/shared/sigbus.h | 27
src/shared/signal-util.c | 268 -
src/shared/signal-util.h | 41
src/shared/siphash24.c | 135
src/shared/siphash24.h | 6
src/shared/smack-util.c | 208
src/shared/smack-util.h | 36
src/shared/socket-label.c | 164
src/shared/socket-util.c | 769 ---
src/shared/socket-util.h | 120
src/shared/sparse-endian.h | 88
src/shared/special.h | 117
src/shared/strbuf.c | 201
src/shared/strbuf.h | 54
src/shared/strv.c | 704 ---
src/shared/strv.h | 155
src/shared/strxcpyx.c | 100
src/shared/strxcpyx.h | 31
src/shared/terminal-util.c | 1072 -----
src/shared/terminal-util.h | 109
src/shared/time-util.c | 997 ----
src/shared/time-util.h | 111
src/shared/unaligned.h | 66
src/shared/unit-name.c | 834 ---
src/shared/unit-name.h | 177
src/shared/utf8.c | 402 -
src/shared/utf8.h | 57
src/shared/util.c | 6048 -----------------------------
src/shared/util.h | 903 ----
src/shared/verbs.c | 90
src/shared/verbs.h | 34
src/shared/virt.c | 406 -
src/shared/virt.h | 35
src/shared/xml.c | 254 -
src/shared/xml.h | 34
src/systemd/sd-rtnl.h | 5
src/test/test-cgroup-util.c | 20
src/test/test-dns-domain.c | 192
src/test/test-rtnl-manual.c | 2
350 files changed, 41687 insertions(+), 41069 deletions(-)
New commits:
commit d11a6028e399ffb57a75bb3c45cab06d49167a55
Merge: b18d3e1 8c8302a
Author: Kay Sievers <kay at vrfy.org>
Date: Sun Jun 14 12:34:13 2015 +0200
Merge pull request #187 from kaysievers/libcleanup
build-sys: simplify lib dependencies
commit 8c8302a66e6278428e30f409d659fec8959d9e2d
Author: Kay Sievers <kay at vrfy.org>
Date: Sat Jun 13 20:35:37 2015 +0200
build-sys: simplify lib dependencies
diff --git a/Makefile.am b/Makefile.am
index eaa9a85..381701d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -905,6 +905,8 @@ libbasic_la_CFLAGS = \
libbasic_la_LIBADD = \
$(SELINUX_LIBS) \
$(CAP_LIBS) \
+ -ldl \
+ -lrt \
-lm
# -----------------------------------------------------------------------------
@@ -1012,21 +1014,16 @@ endif
libshared_la_CFLAGS = \
$(AM_CFLAGS) \
- $(SELINUX_CFLAGS) \
- $(CAP_CFLAGS) \
$(ACL_CFLAGS) \
- $(SECCOMP_CFLAGS) \
- -pthread
+ $(LIBIDN_CFLAGS) \
+ $(SECCOMP_CFLAGS)
libshared_la_LIBADD = \
libsystemd-internal.la \
libsystemd-journal-internal.la \
- $(SELINUX_LIBS) \
- $(CAP_LIBS) \
$(ACL_LIBS) \
$(LIBIDN_LIBS) \
- $(SECCOMP_LIBS) \
- -lm
+ $(SECCOMP_LIBS)
# -----------------------------------------------------------------------------
if HAVE_LIBIPTC
@@ -1190,8 +1187,7 @@ libcore_la_CFLAGS = \
$(AUDIT_CFLAGS) \
$(KMOD_CFLAGS) \
$(APPARMOR_CFLAGS) \
- $(MOUNT_CFLAGS) \
- -pthread
+ $(MOUNT_CFLAGS)
libcore_la_LIBADD = \
libudev-internal.la \
@@ -1321,8 +1317,7 @@ systemd_SOURCES = \
src/core/main.c
systemd_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
dist_pkgsysconf_DATA += \
src/core/system.conf \
@@ -1525,8 +1520,7 @@ test_engine_CFLAGS = \
$(SECCOMP_CFLAGS)
test_engine_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
test_job_type_SOURCES = \
src/test/test-job-type.c
@@ -1536,8 +1530,7 @@ test_job_type_CFLAGS = \
$(SECCOMP_CFLAGS)
test_job_type_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
test_ns_SOURCES = \
src/test/test-ns.c
@@ -1566,8 +1559,7 @@ test_dns_domain_SOURCES = \
test_dns_domain_LDADD = \
libsystemd-network.la \
- libshared.la \
- $(LIBIDN_LIBS)
+ libshared.la
if ENABLE_EFI
manual_tests += \
@@ -1588,8 +1580,7 @@ test_unit_name_CFLAGS = \
$(SECCOMP_CFLAGS)
test_unit_name_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
test_unit_file_SOURCES = \
src/test/test-unit-file.c
@@ -1599,8 +1590,7 @@ test_unit_file_CFLAGS = \
$(SECCOMP_CFLAGS)
test_unit_file_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
test_utf8_SOURCES = \
src/test/test-utf8.c
@@ -1684,7 +1674,6 @@ test_path_lookup_SOURCES = \
src/test/test-path-lookup.c
test_path_lookup_LDADD = \
- -lm \
libshared.la
test_uid_range_SOURCES = \
@@ -1813,8 +1802,7 @@ test_tables_LDADD = \
libsystemd-journal-internal.la \
libjournal-core.la \
libcore.la \
- libudev-core.la \
- $(RT_LIBS)
+ libudev-core.la
test_prioq_SOURCES = \
src/test/test-prioq.c
@@ -1850,8 +1838,7 @@ test_ipcrm_SOURCES = \
src/test/test-ipcrm.c
test_ipcrm_LDADD = \
- libshared.la \
- -lrt
+ libshared.la
test_btrfs_SOURCES = \
src/test/test-btrfs.c
@@ -1943,8 +1930,7 @@ test_cgroup_mask_CFLAGS = \
$(SECCOMP_CFLAGS)
test_cgroup_mask_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
test_cgroup_util_SOURCES = \
src/test/test-cgroup-util.c
@@ -2023,8 +2009,7 @@ test_sched_prio_CFLAGS = \
$(SECCOMP_CFLAGS)
test_sched_prio_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
test_conf_files_SOURCES = \
src/test/test-conf-files.c
@@ -2095,8 +2080,7 @@ systemd_analyze_CFLAGS = \
$(SECCOMP_CFLAGS)
systemd_analyze_LDADD = \
- libcore.la \
- $(RT_LIBS)
+ libcore.la
# ------------------------------------------------------------------------------
systemd_initctl_SOURCES = \
@@ -2792,20 +2776,12 @@ libbus_proxy_core_la_SOURCES = \
src/bus-proxyd/synthesize.c \
src/bus-proxyd/synthesize.h
-libbus_proxy_core_la_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
libbus_proxy_core_la_LIBADD = \
libshared.la
systemd_bus_proxyd_SOURCES = \
src/bus-proxyd/bus-proxyd.c
-systemd_bus_proxyd_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
systemd_bus_proxyd_LDADD = \
libbus-proxy-core.la \
libshared.la
@@ -2940,22 +2916,13 @@ libsystemd_internal_la_SOURCES = \
nodist_libsystemd_internal_la_SOURCES = \
src/libsystemd/libsystemd.sym
-libsystemd_internal_la_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
libsystemd_internal_la_LIBADD = \
- libbasic.la \
- $(RT_LIBS)
+ libbasic.la
libsystemd_resolve_la_SOURCES = \
src/libsystemd/sd-resolve/sd-resolve.c \
src/libsystemd/sd-resolve/resolve-util.h
-libsystemd_resolve_la_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
libsystemd_resolve_la_LIBADD = \
-lresolv
@@ -3073,10 +3040,6 @@ test_bus_signature_LDADD = \
test_bus_chat_SOURCES = \
src/libsystemd/sd-bus/test-bus-chat.c
-test_bus_chat_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
test_bus_chat_LDADD = \
libshared.la
@@ -3093,20 +3056,12 @@ test_bus_cleanup_LDADD = \
test_bus_server_SOURCES = \
src/libsystemd/sd-bus/test-bus-server.c
-test_bus_server_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
test_bus_server_LDADD = \
libshared.la
test_bus_objects_SOURCES = \
src/libsystemd/sd-bus/test-bus-objects.c
-test_bus_objects_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
test_bus_objects_LDADD = \
libshared.la
@@ -3194,10 +3149,6 @@ test_resolve_LDADD = \
libsystemd-resolve.la \
libshared.la
-test_resolve_CFLAGS = \
- $(AM_CFLAGS) \
- -pthread
-
busctl_SOURCES = \
src/libsystemd/sd-bus/busctl.c \
src/libsystemd/sd-bus/busctl-introspect.c \
@@ -3284,8 +3235,7 @@ test_dhcp_client_SOURCES = \
test_dhcp_client_LDADD = \
libsystemd-network.la \
libudev-internal.la \
- libshared.la \
- $(LIBIDN_LIBS)
+ libshared.la
test_dhcp_server_SOURCES = \
src/libsystemd-network/test-dhcp-server.c
@@ -3800,8 +3750,7 @@ test_libudev_SOURCES = \
test_libudev_LDADD = \
libudev-internal.la \
- libshared.la \
- $(ACL_LIBS)
+ libshared.la
test_udev_SOURCES = \
src/test/test-udev.c
@@ -3809,9 +3758,7 @@ test_udev_SOURCES = \
test_udev_LDADD = \
libudev-core.la \
$(BLKID_LIBS) \
- $(KMOD_LIBS) \
- $(SELINUX_LIBS) \
- $(ACL_LIBS)
+ $(KMOD_LIBS)
check_DATA += \
test/sys
@@ -4938,7 +4885,6 @@ libnss_myhostname_la_LDFLAGS = \
libnss_myhostname_la_LIBADD = \
libshared.la
-#libsystemd-network.la
lib_LTLIBRARIES += \
libnss_myhostname.la
@@ -5269,8 +5215,7 @@ gperf_txt_sources += \
systemd_resolved_LDADD = \
libsystemd-network.la \
- libshared.la \
- $(LIBIDN_LIBS)
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-resolved
@@ -5339,8 +5284,7 @@ nodist_systemd_resolve_host_SOURCES = \
src/resolve/dns_type-to-name.h
systemd_resolve_host_LDADD = \
- libshared.la \
- $(LIBIDN_LIBS)
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-resolve-host
@@ -5359,8 +5303,7 @@ systemd_networkd_SOURCES = \
src/network/networkd.c
systemd_networkd_LDADD = \
- libnetworkd-core.la \
- $(LIBIDN_LIBS)
+ libnetworkd-core.la
if HAVE_LIBIPTC
systemd_networkd_LDADD += \
@@ -5456,8 +5399,7 @@ test_network_SOURCES = \
src/network/test-network.c
test_network_LDADD = \
- libnetworkd-core.la \
- $(LIBIDN_LIBS)
+ libnetworkd-core.la
if HAVE_LIBIPTC
test_network_LDADD += \
diff --git a/configure.ac b/configure.ac
index 344bfe1..fcdccb1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -301,12 +301,6 @@ AC_SEARCH_LIBS([cap_init], [cap], [], [AC_MSG_ERROR([*** POSIX caps library not
CAP_LIBS="$LIBS"
AC_SUBST(CAP_LIBS)
-LIBS=
-AC_SEARCH_LIBS([mq_open], [rt], [], [AC_MSG_ERROR([*** POSIX RT library not found])])
-RT_LIBS="$LIBS"
-AC_SUBST(RT_LIBS)
-LIBS="$save_LIBS"
-
AC_CHECK_FUNCS([memfd_create])
AC_CHECK_FUNCS([__secure_getenv secure_getenv])
AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, kcmp, LO_FLAGS_PARTSCAN],
commit b18d3e16618eb0cb148a25ea9bcfb48351a4e484
Merge: eb56be2 4363358
Author: Daniel Mack <github at zonque.org>
Date: Sat Jun 13 10:38:22 2015 +0200
Merge pull request #184 from l10n-tw/master
po: fix zh_TW mailing list again.
commit eb56be2b93edbcda6a35e1a2ff40c23bf25c4691
Merge: eb59b60 9299416
Author: keszybz <zbyszek at in.waw.pl>
Date: Fri Jun 12 23:52:13 2015 -0400
Merge pull request #167 from keszybz/line-oriented-ima-setup
ima-setup: write policy one line at a time
commit 43633588f90660bd25614cf07d102356153e25ba
Author: Jeff Huang <s8321414 at gmail.com>
Date: Sat Jun 13 10:45:12 2015 +0800
po: fix zh_TW mailing list again.
diff --git a/po/zh_TW.po b/po/zh_TW.po
index f96d484..77be569 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -9,7 +9,7 @@ msgstr ""
"POT-Creation-Date: 2015-06-10 11:41+0000\n"
"PO-Revision-Date: 2015-06-11 12:44+0800\n"
"Last-Translator: Jeff Huang <s8321414 at gmail.com>\n"
-"Language-Team: Chinese Traditional <https://groups.google.com/forum/#!forum/chinese-l10n>\n"
+"Language-Team: chinese-l10n <chinese-l10n at googlegroups.com>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
commit eb59b6094197a7dcef89639ec3e91eef61b639bb
Merge: cca91b4 1f41b33
Author: Ronny Chevalier <chevalier.ronny at gmail.com>
Date: Fri Jun 12 15:30:54 2015 +0200
Merge pull request #179 from l10n-tw/master
po: fix zh_TW mailing list.
commit 1f41b33b8658edddfbe668ccb11a3e15226d2a04
Author: Jeff Huang <s8321414 at gmail.com>
Date: Fri Jun 12 21:14:04 2015 +0800
po: fix zh_TW mailing list.
diff --git a/po/zh_TW.po b/po/zh_TW.po
index a0a0877..f96d484 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -9,7 +9,7 @@ msgstr ""
"POT-Creation-Date: 2015-06-10 11:41+0000\n"
"PO-Revision-Date: 2015-06-11 12:44+0800\n"
"Last-Translator: Jeff Huang <s8321414 at gmail.com>\n"
-"Language-Team: Chinese Traditional <kde-i18n-doc at kde.org>\n"
+"Language-Team: Chinese Traditional <https://groups.google.com/forum/#!forum/chinese-l10n>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
commit cca91b41d993de3a5e69e3c0d893756aeae1c8b8
Merge: c04754b ea01a90
Author: Ronny Chevalier <chevalier.ronny at gmail.com>
Date: Fri Jun 12 15:02:00 2015 +0200
Merge pull request #164 from l10n-tw/master
Add zh_TW translation.
commit ea01a90df4d51eb8babfb1d7e2b01ff881e280fa
Author: Jeff Huang <s8321414 at gmail.com>
Date: Wed Jun 10 22:39:44 2015 +0800
po,catalog: add zh_TW translation.
diff --git a/Makefile.am b/Makefile.am
index 5d30d7d..fb00e6e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4500,6 +4500,7 @@ dist_catalog_DATA = \
catalog/systemd.pl.catalog \
catalog/systemd.pt_BR.catalog \
catalog/systemd.ru.catalog \
+ catalog/systemd.zh_TW.catalog \
catalog/systemd.catalog
SOCKETS_TARGET_WANTS += \
diff --git a/catalog/systemd.zh_TW.catalog b/catalog/systemd.zh_TW.catalog
new file mode 100644
index 0000000..027ffe4
--- /dev/null
+++ b/catalog/systemd.zh_TW.catalog
@@ -0,0 +1,263 @@
+# This file is part of systemd.
+#
+# Copyright 2012 Lennart Poettering
+# Copyright 2015 Jeff Huang
+#
+# 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
+# Traditional Chinese translation
+
+# Catalog 的格式記錄於
+# http://www.freedesktop.org/wiki/Software/systemd/catalog
+
+# For an explanation why we do all this, see https://xkcd.com/1024/
+
+-- f77379a8490b408bbe5f6940505a777b
+Subject: 日誌已開始
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+系統日誌行程已啟動,已開啟日誌
+檔案供寫入並準備好對行程的要求做出回應。
+
+-- d93fb3c9c24d451a97cea615ce59c00b
+Subject: 日誌已停止
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+系統日誌行程已關閉,且關閉所有目前
+活躍的日誌檔案。
+
+-- a596d6fe7bfa4994828e72309e95d61e
+Subject: 從服務而來的訊息已被抑制
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:journald.conf(5)
+
+有一個服務在一個時間週期內記錄了太多訊息。
+從該服務而來的訊息已被丟棄。
+
+注意,只有有問題的服務之訊息被丟棄,
+其他服務的訊息則不受影響。
+
+可以在 /etc/systemd/journald.conf 中設定
+RateLimitInterval= 以及 RateLimitBurst=
+來控制當訊息要開始被丟棄時的限制。參見 journald.conf(5) 以獲得更多資訊。
+
+-- e9bf28e6e834481bb6f48f548ad13606
+Subject: 日誌訊息已遺失
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+因日誌系統對核心訊息的處理不夠快速,
+部份訊息已遺失。
+
+-- fc2e22bc6ee647b6b90729ab34a250b1
+Subject: 行程 @COREDUMP_PID@ (@COREDUMP_COMM@) 核心傾印
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:core(5)
+
+行程 @COREDUMP_PID@ (@COREDUMP_COMM@) 當掉並核心傾印。
+
+這通常代表了在當掉的程式中的一個程式錯誤
+並需要回報錯誤給其開發者。
+
+-- 8d45620c1a4348dbb17410da57c60c66
+Subject: 新的工作階段 @SESSION_ID@ 已為使用者 @USER_ID@ 建立
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+一個新的工作階段,ID @SESSION_ID@ 已為使用者 @USER_ID@ 建立。
+
+這個工作階段的領導行程為 @LEADER@。
+
+-- 3354939424b4456d9802ca8333ed424a
+Subject: 工作階段 @SESSION_ID@ 已結束
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+一個工作階段,ID @SESSION_ID@ 已結束。
+
+-- fcbefc5da23d428093f97c82a9290f7b
+Subject: 新的座位 @SEAT_ID@ 可用
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+一個新的座位 @SEAT_ID@ 已被設定且現在可用。
+
+-- e7852bfe46784ed0accde04bc864c2d5
+Subject: 座位 @SEAT_ID@ 已被移除
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+座位 @SEAT_ID@ 已被移除且不再可用。
+
+-- c7a787079b354eaaa9e77b371893cd27
+Subject: 時間變更
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+系統時間已變更為1970年1月1日後 @REALTIME@ 微秒。
+
+-- 45f82f4aef7a4bbf942ce861d1f20990
+Subject: 時區變更為 @TIMEZONE@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+系統時區已變更為 @TIMEZONE@。
+
+-- b07a249cd024414a82dd00cd181378ff
+Subject: 系統啟動已完成
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+所有開機所必要的系統服務都已成功啟動。
+注意這並不代表這臺機器有空閒的時間
+可以服務,可能仍忙於完成啟動。
+
+核心啟動需要 @KERNEL_USEC@ 微秒。
+
+初始 RAM 磁碟啟動需要 @INITRD_USEC@ 微秒。
+
+使用者空間啟動需要 @USERSPACE_USEC@ 微秒。
+
+-- 6bbd95ee977941e497c48be27c254128
+Subject: 系統進入 @SLEEP@ 睡眠狀態
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+系統現在已進入 @SLEEP@ 睡眠狀態。
+
+-- 8811e6df2a8e40f58a94cea26f8ebf14
+Subject: 系統離開 @SLEEP@ 睡眠狀態
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+系統現在已離開 @SLEEP@ 睡眠狀態。
+
+-- 98268866d1d54a499c4e98921d93bc40
+Subject: 系統關機開始
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Systemd 關閉已經開始。關閉已開始且所有系統服務
+都已結束,所有的檔案系統也都已被卸載。
+
+-- 7d4958e842da4a758f6c1cdc7b36dcc5
+Subject: 單位 @UNIT@ 已開始啟動
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 已開始啟動。
+
+-- 39f53479d3a045ac8e11786248231fbf
+Subject: 單位 @UNIT@ 啟動已結束
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 啟動已結束。
+
+啟動結果為 @RESULT@。
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: 單位 @UNIT@ 已開始關閉
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 已開始關閉。
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: 單位 @UNIT@ 已關閉結束
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 已關閉結束。
+
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: 單位 @UNIT@ 已失敗
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 已失敗。
+
+結果為 @RESULT@。
+
+-- d34d037fff1847e6ae669a370e694725
+Subject: 單位 @UNIT@ 已開始重新載入其設定
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 已開始重新載入其設定
+
+-- 7b05ebc668384222baa8881179cfda54
+Subject: 單位 @UNIT@ 已結束重新載入其設定
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+單位 @UNIT@ 已結束重新載入其設定
+
+結果為 @RESULT@。
+
+-- 641257651c1b4ec9a8624d7a40a9e1e7
+Subject: 行程 @EXECUTABLE@ 無法執行
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+行程 @EXECUTABLE@ 無法執行且失敗。
+
+由該行程所回傳的錯誤碼為 @ERRNO@。
+
+-- 0027229ca0644181a76c4e92458afa2e
+Subject: 一個或更多訊息無法被轉發到 syslog
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+一個或更多訊息無法被轉發到 syslog 服務
+以及並行執行的 journald。這通常代表著
+syslog 實作並無未跟上佇列中訊息
+的速度。
+
+-- 1dee0369c7fc4736b7099b38ecb46ee7
+Subject: 掛載點不為空
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+目錄 @WHERE@ 被指定為掛載點(在 /etc/fstab 中的
+第二欄或是在 systemd 單位檔案中的 Where= 欄位)且其不為空。
+這並不會干擾掛載,但在此目錄中已存在的檔案
+會變成無法存取的狀態。要檢視這些 over-mounted 的檔案,
+請手動掛載下面的檔案系統到次要
+位置。
+
+-- 24d8d4452573402496068381a6312df2
+Subject: 虛擬機器或容器已啟動
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+虛擬機器 @NAME@ 包含它的領導 PID @LEADER@ 現在
+已經開始並已經可以使用。
+
+-- 58432bd3bace477cb514b56381b8a758
+Subject: 虛擬機器或容器已結束
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+虛擬機器 @NAME@ 包含它的領導 PID @LEADER@ 已經
+關閉。
diff --git a/po/LINGUAS b/po/LINGUAS
index 562c562..3d0ad2b 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -9,3 +9,4 @@ ru
uk
sv
es
+zh_TW
diff --git a/po/zh_TW.po b/po/zh_TW.po
new file mode 100644
index 0000000..a0a0877
--- /dev/null
+++ b/po/zh_TW.po
@@ -0,0 +1,477 @@
+# Traditional Chinese translation for systemd.
+# Copyright (C) 2015 systemd's COPYRIGHT HOLDER
+# This file is distributed under the same license as the systemd package.
+# Jeff Huang <s8321414 at gmail.com>, 2015.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
+"POT-Creation-Date: 2015-06-10 11:41+0000\n"
+"PO-Revision-Date: 2015-06-11 12:44+0800\n"
+"Last-Translator: Jeff Huang <s8321414 at gmail.com>\n"
+"Language-Team: Chinese Traditional <kde-i18n-doc at kde.org>\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
+msgid "Send passphrase back to system"
+msgstr "傳回密碼片語到系統"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
+msgstr "傳回已輸入的密碼片語到系統需要驗證。"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
+msgid "Manage system services or other units"
+msgstr "管理系統服務或其他單位"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
+msgid "Authentication is required to manage system services or other units."
+msgstr "管理系統服務或其他單位需要驗證。"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
+msgid "Manage system service or unit files"
+msgstr "管理系統服務或單位檔案"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6
+msgid "Authentication is required to manage system service or unit files."
+msgstr "管理系統服務或單位檔案需要驗證。"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
+msgid "Set or unset system and service manager environment variables"
+msgstr "設定或取消設定系統及服務管理員環境變量"
+
+#: ../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 ""
+"設定或取消設定系統及服務管理員環境變量時"
+"需要驗證"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
+msgid "Reload the systemd state"
+msgstr "重新載入 systemd 狀態"
+
+#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10
+msgid "Authentication is required to reload the systemd state."
+msgstr "重新載入 systemd 狀態需要驗證。"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+msgid "Set host name"
+msgstr "設定主機名稱"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
+msgid "Authentication is required to set the local host name."
+msgstr "設定主機名稱需要驗證。"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+msgid "Set static host name"
+msgstr "設定靜態主機名稱"
+
+#: ../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 ""
+"設定靜態設定的本機主機名稱時需要驗證,"
+"同時也需要漂亮的主機名稱"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+msgid "Set machine information"
+msgstr "設定機器資訊"
+
+#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6
+msgid "Authentication is required to set local machine information."
+msgstr "設定機器資訊需要驗證"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:1
+msgid "Import a VM or container image"
+msgstr "匯入虛擬機器或容器映像"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:2
+msgid "Authentication is required to import a VM or container image"
+msgstr "匯入虛擬機器或容器映像需要驗證"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:3
+msgid "Export a VM or container image"
+msgstr "匯出虛擬機器或容器映像"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:4
+msgid "Authentication is required to export a VM or container image"
+msgstr "匯出虛擬機器或容器映像需要驗證"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:5
+msgid "Download a VM or container image"
+msgstr "下載虛擬機器或容器映像"
+
+#: ../src/import/org.freedesktop.import1.policy.in.h:6
+msgid "Authentication is required to download a VM or container image"
+msgstr "下載虛擬機器或容器映像需要驗證"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:1
+msgid "Set system locale"
+msgstr "設定系統語系"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:2
+msgid "Authentication is required to set the system locale."
+msgstr "設定系統語系需要驗證。"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+msgid "Set system keyboard settings"
+msgstr "設定系統鍵盤設定"
+
+#: ../src/locale/org.freedesktop.locale1.policy.in.h:4
+msgid "Authentication is required to set the system keyboard settings."
+msgstr "設定系統鍵盤設定需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:1
+msgid "Allow applications to inhibit system shutdown"
+msgstr "允許應用程式阻止系統關機"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:2
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
+msgstr "要讓應用程式阻止系統關機需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:3
+msgid "Allow applications to delay system shutdown"
+msgstr "允許應用程式延遲系統關機"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:4
+msgid "Authentication is required for an application to delay system shutdown."
+msgstr "要讓應用程式延遲系統關機需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:5
+msgid "Allow applications to inhibit system sleep"
+msgstr "允許應用程式阻止系統睡眠"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:6
+msgid "Authentication is required for an application to inhibit system sleep."
+msgstr "要讓應用程式阻止系統睡眠需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:7
+msgid "Allow applications to delay system sleep"
+msgstr "允許應用程式延遲系統睡眠"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:8
+msgid "Authentication is required for an application to delay system sleep."
+msgstr "要讓應用程式延遲系統睡眠需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:9
+msgid "Allow applications to inhibit automatic system suspend"
+msgstr "允許應用程式阻止自動系統暫停"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:10
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+"要讓應用程式阻止自動系統暫停"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:11
+msgid "Allow applications to inhibit system handling of the power key"
+msgstr "允許應用程式阻止系統處理電源鍵"
+
+#: ../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 ""
+"要讓應用程式阻止系統處理電源鍵"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:13
+msgid "Allow applications to inhibit system handling of the suspend key"
+msgstr "允許應用程式阻止系統處理暫停鍵"
+
+#: ../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 ""
+"要讓應用程式阻止系統處理暫停鍵"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:15
+msgid "Allow applications to inhibit system handling of the hibernate key"
+msgstr "允許應用程式阻止系統處理冬眠鍵"
+
+#: ../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 ""
+"要讓應用程式阻止系統處理冬眠鍵"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:17
+msgid "Allow applications to inhibit system handling of the lid switch"
+msgstr "允許應用程式阻止系統處理上蓋開關"
+
+#: ../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 ""
+"要讓應用程式阻止系統處理上蓋開關"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:19
+msgid "Allow non-logged-in users to run programs"
+msgstr "允許未登入的使用者執行程式"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:20
+msgid "Authentication is required to run programs as a non-logged-in user."
+msgstr "要讓未登入的使用者執行程式需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:21
+msgid "Allow attaching devices to seats"
+msgstr "允許將設備連接到座位"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:22
+msgid "Authentication is required for attaching a device to a seat."
+msgstr "將設備連接到座位需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:23
+msgid "Flush device to seat attachments"
+msgstr "暴露裝置以安裝附件"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:24
+msgid ""
+"Authentication is required for resetting how devices are attached to seats."
+msgstr "要重置裝置如何連接到座位需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:25
+msgid "Power off the system"
+msgstr "關閉系統電源"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:26
+msgid "Authentication is required for powering off the system."
+msgstr "關閉系統電源需要驗證"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:27
+msgid "Power off the system while other users are logged in"
+msgstr "在有其他使用者登入時關閉系統電源"
+
+#: ../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 ""
+"在有其他使用者登入時關閉系統電源"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:29
+msgid "Power off the system while an application asked to inhibit it"
+msgstr "當應用程式阻止系統電源關閉時將其關閉"
+
+#: ../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 ""
+"當應用程式阻止系統電源關閉時將系統電源關閉"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:31
+msgid "Reboot the system"
+msgstr "重新啟動系統"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:32
+msgid "Authentication is required for rebooting the system."
+msgstr "重新啟動系統需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:33
+msgid "Reboot the system while other users are logged in"
+msgstr "在有其他使用者登入時重新啟動系統"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:34
+msgid ""
+"Authentication is required for rebooting the system while other users are "
+"logged in."
+msgstr ""
+"在有其他使用者登入時重新啟動系統"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:35
+msgid "Reboot the system while an application asked to inhibit it"
+msgstr "當應用程式阻止重新啟動系統時將系統重新啟動"
+
+#: ../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 ""
+"當應用程式阻止系統重新啟動時將系統重新啟動"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:37
+msgid "Suspend the system"
+msgstr "暫停系統"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:38
+msgid "Authentication is required for suspending the system."
+msgstr "暫停系統需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:39
+msgid "Suspend the system while other users are logged in"
+msgstr "在有其他使用者登入時暫停系統"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:40
+msgid ""
+"Authentication is required for suspending the system while other users are "
+"logged in."
+msgstr ""
+"在有其他使用者登入時暫停系統"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:41
+msgid "Suspend the system while an application asked to inhibit it"
+msgstr "當應用程式阻止暫停系統時將系統暫停"
+
+#: ../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 ""
+"當應用程式阻止系統暫停時將系統暫停"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:43
+msgid "Hibernate the system"
+msgstr "系統冬眠"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:44
+msgid "Authentication is required for hibernating the system."
+msgstr "系統冬眠需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:45
+msgid "Hibernate the system while other users are logged in"
+msgstr "在有其他使用者登入時冬眠系統"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:46
+msgid ""
+"Authentication is required for hibernating the system while other users are "
+"logged in."
+msgstr ""
+"在有其他使用者登入時冬眠系統"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:47
+msgid "Hibernate the system while an application asked to inhibit it"
+msgstr "當應用程式阻止冬眠系統時將系統冬眠"
+
+#: ../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 ""
+"當應用程式阻止系統冬眠時將系統冬眠"
+"需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:49
+msgid "Manage active sessions, users and seats"
+msgstr "管理活躍的工作階段、使用者與座位"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:50
+msgid ""
+"Authentication is required for managing active sessions, users and seats."
+msgstr "管理活躍的工作階段、使用者與座位需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:51
+msgid "Lock or unlock active sessions"
+msgstr "鎖定或解鎖活躍的工作階段"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:52
+msgid "Authentication is required to lock or unlock active sessions."
+msgstr "鎖定或解鎖活躍的工作階段需要驗證。"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:53
+msgid "Allow indication to the firmware to boot to setup interface"
+msgstr "允許對韌體的指示以開始設定介面"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:54
+msgid ""
+"Authentication is required to indicate to the firmware to boot to setup "
+"interface."
+msgstr ""
+"對韌體的指示以開始設定介面"
+"需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:1
+msgid "Log into a local container"
+msgstr "登入到本機容器"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:2
+msgid "Authentication is required to log into a local container."
+msgstr "登入到本機容器需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+msgid "Manage local virtual machines and containers"
+msgstr "管理本機虛擬機器及容器"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+msgid ""
+"Authentication is required to manage local virtual machines and containers."
+msgstr "管理本機虛擬機器及容器需要驗證。"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+msgid "Manage local virtual machine and container images"
+msgstr "管理本機虛擬機器及容器映像"
+
+#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+msgid ""
+"Authentication is required to manage local virtual machine and container "
+"images."
+msgstr ""
+"管理本機虛擬機器及容器映像"
+"需要驗證。"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+msgid "Set system time"
+msgstr "設定系統時間"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2
+msgid "Authentication is required to set the system time."
+msgstr "設定系統時間需要驗證。"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+msgid "Set system timezone"
+msgstr "設定系統時區"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4
+msgid "Authentication is required to set the system timezone."
+msgstr "設定系統時區需要驗證。"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5
+msgid "Set RTC to local timezone or UTC"
+msgstr "將 RTC 設定為本地時區或 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 ""
+"控制 RTC 儲存本地或 UTC 時間"
+"需要驗證。"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7
+msgid "Turn network time synchronization on or off"
+msgstr "打開或關閉網路時間同步"
+
+#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8
+msgid ""
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
+msgstr ""
+"控制網路時間同步是否啟用"
+"需要驗證。"
+
+
commit c04754bcb6a47f479baab0c151bfffcddb97a475
Author: Lennart Poettering <lennart at poettering.net>
Date: Fri Jun 12 13:35:59 2015 +0200
selinux: whitespace fixes
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index decd42f..e9a9a02 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -82,11 +82,19 @@ static int audit_callback(
static int callback_type_to_priority(int type) {
switch(type) {
- case SELINUX_ERROR: return LOG_ERR;
- case SELINUX_WARNING: return LOG_WARNING;
- case SELINUX_INFO: return LOG_INFO;
+
+ case SELINUX_ERROR:
+ return LOG_ERR;
+
+ case SELINUX_WARNING:
+ return LOG_WARNING;
+
+ case SELINUX_INFO:
+ return LOG_INFO;
+
case SELINUX_AVC:
- default: return LOG_NOTICE;
+ default:
+ return LOG_NOTICE;
}
}
@@ -281,11 +289,13 @@ finish:
#endif
}
-int mac_selinux_unit_access_check_strv(char **units,
- sd_bus_message *message,
- Manager *m,
- const char *permission,
- sd_bus_error *error) {
+int mac_selinux_unit_access_check_strv(
+ char **units,
+ sd_bus_message *message,
+ Manager *m,
+ const char *permission,
+ sd_bus_error *error) {
+
#ifdef HAVE_SELINUX
char **i;
Unit *u;
commit 911d79686b6e596cc85e62671705ef8a8d749034
Merge: 533b432 fb2d301
Author: Kay Sievers <kay at vrfy.org>
Date: Fri Jun 12 13:38:07 2015 +0200
Merge pull request #153 from crawford/cciss
rules: re-add cciss rules
commit 533b4329404ef34081bf0cb4fcdef2aeda4a8820
Merge: 4ff7d7e 2972cba
Author: Lennart Poettering <lennart at poettering.net>
Date: Fri Jun 12 12:28:16 2015 +0200
Merge pull request #173 from mischief/ipforwarding-3
IPForwarding=kernel v3
commit 4ff7d7e90ccc47e1e7e1f99bbeaae669d04ce782
Merge: ed810b6 034e10d
Author: Daniel Mack <github at zonque.org>
Date: Fri Jun 12 08:11:22 2015 +0200
Merge pull request #177 from ssahani/net
core: fix CID 996302
commit 034e10d7fb8bd3d574bc4861f84cf4a86c343bf2
Author: Susant Sahani <susant at redhat.com>
Date: Fri Jun 12 10:24:26 2015 +0530
core: fix CID 996302
CID 996302: Error handling issues (CHECKED_RETURN)
diff --git a/src/core/umount.c b/src/core/umount.c
index bee267a..d59b5d0 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -385,7 +385,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
* alias read-only we hence should be
* relatively safe regarding keeping the fs we
* can otherwise not see dirty. */
- mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+ (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
}
/* Skip / and /usr since we cannot unmount that
commit 2972cba11712c3bfbe9fa07d1f3c3c4330127733
Author: Nick Owens <nick.owens at coreos.com>
Date: Tue Jun 2 15:42:21 2015 -0700
man: document IPForward=kernel option
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index bd061c2..54fef4c 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -366,7 +366,8 @@
the routing table. Takes either a boolean argument, or the
values <literal>ipv4</literal> or <literal>ipv6</literal>,
which only enables IP forwarding for the specified address
- family. This controls the
+ family, or <literal>kernel</literal>, which preserves existing sysctl settings.
+ This controls the
<filename>net.ipv4.conf.<interface>.forwarding</filename>
and
<filename>net.ipv6.conf.<interface>.forwarding</filename>
@@ -375,8 +376,8 @@
for details about sysctl options). Defaults to
<literal>no</literal>.</para>
- <para>Note: unless this option is turned on, no IP
- forwarding is done on this interface, even if this is
+ <para>Note: unless this option is turned on, or set to <literal>kernel</literal>,
+ no IP forwarding is done on this interface, even if this is
globally turned on in the kernel, with the
<filename>net.ipv4.ip_forward</filename> and
<filename>net.ipv4.ip_forward</filename> sysctl
commit 8add5f79709f323f1b077dde8a4a1aa7f560886f
Author: Nick Owens <nick.owens at coreos.com>
Date: Fri May 1 11:48:08 2015 -0700
networkd: create "kernel" setting for IPForwarding
In 5a8bcb674f71a20e95df55319b34c556638378ce, IPForwarding was introduced
to set forwarding flags on interfaces in .network files. networkd sets
forwarding options regardless of the previous setting, even if it was
set by e.g. sysctl. This commit creates a new option for IPForwarding,
"kernel", that preserves the sysctl settings rather than always setting
them.
See https://bugs.freedesktop.org/show_bug.cgi?id=89509 for the initial
bug report.
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 7841f69..b194788 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1477,6 +1477,9 @@ static int link_set_ipv4_forward(Link *link) {
const char *p = NULL;
int r;
+ if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ return 0;
+
p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding");
r = write_string_file_no_create(p, one_zero(link_ipv4_forward_enabled(link)));
if (r < 0)
@@ -1493,6 +1496,9 @@ static int link_set_ipv6_forward(Link *link) {
if (!socket_ipv6_is_supported())
return 0;
+ if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
+ return 0;
+
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding");
r = write_string_file_no_create(p, one_zero(link_ipv6_forward_enabled(link)));
if (r < 0)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 8abf5bc..b05bc94 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -15,69 +15,69 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac)
-Match.Path, config_parse_strv, 0, offsetof(Network, match_path)
-Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver)
-Match.Type, config_parse_strv, 0, offsetof(Network, match_type)
-Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
-Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
-Network.Description, config_parse_string, 0, offsetof(Network, description)
-Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
-Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
-Network.VLAN, config_parse_netdev, 0, 0
-Network.MACVLAN, config_parse_netdev, 0, 0
-Network.IPVLAN, config_parse_netdev, 0, 0
-Network.VXLAN, config_parse_netdev, 0, 0
-Network.Tunnel, config_parse_tunnel, 0, 0
-Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
-Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
-Network.LinkLocalAddressing, config_parse_address_family_boolean,0, offsetof(Network, link_local)
-Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
-Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token)
-Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp)
-Network.Address, config_parse_address, 0, 0
-Network.Gateway, config_parse_gateway, 0, 0
-Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
-Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
-Network.LLMNR, config_parse_llmnr, 0, offsetof(Network, llmnr)
-Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
-Network.IPForward, config_parse_address_family_boolean,0, offsetof(Network, ip_forward)
-Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
-Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
-Address.Address, config_parse_address, 0, 0
-Address.Peer, config_parse_address, 0, 0
-Address.Broadcast, config_parse_broadcast, 0, 0
-Address.Label, config_parse_label, 0, 0
-Route.Gateway, config_parse_gateway, 0, 0
-Route.Destination, config_parse_destination, 0, 0
-Route.Source, config_parse_destination, 0, 0
-Route.Metric, config_parse_route_priority, 0, 0
-Route.Scope, config_parse_route_scope, 0, 0
-DHCP.ClientIdentifier, config_parse_dhcp_client_identifier,0, offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
-DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
-DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_sendhost)
-DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
-DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
-DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost)
-BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
-BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
+Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac)
+Match.Path, config_parse_strv, 0, offsetof(Network, match_path)
+Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver)
+Match.Type, config_parse_strv, 0, offsetof(Network, match_type)
+Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
+Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
+Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
+Network.Description, config_parse_string, 0, offsetof(Network, description)
+Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
+Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
+Network.VLAN, config_parse_netdev, 0, 0
+Network.MACVLAN, config_parse_netdev, 0, 0
+Network.IPVLAN, config_parse_netdev, 0, 0
+Network.VXLAN, config_parse_netdev, 0, 0
+Network.Tunnel, config_parse_tunnel, 0, 0
+Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
+Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
+Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local)
+Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
+Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token)
+Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp)
+Network.Address, config_parse_address, 0, 0
+Network.Gateway, config_parse_gateway, 0, 0
+Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
+Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
+Network.LLMNR, config_parse_llmnr, 0, offsetof(Network, llmnr)
+Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
+Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
+Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
+Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
+Address.Address, config_parse_address, 0, 0
+Address.Peer, config_parse_address, 0, 0
+Address.Broadcast, config_parse_broadcast, 0, 0
+Address.Label, config_parse_label, 0, 0
+Route.Gateway, config_parse_gateway, 0, 0
+Route.Destination, config_parse_destination, 0, 0
+Route.Source, config_parse_destination, 0, 0
+Route.Metric, config_parse_route_priority, 0, 0
+Route.Scope, config_parse_route_scope, 0, 0
+DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
+DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
+DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
+DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
+DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
+DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
+DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_sendhost)
+DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
+DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost)
+BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
+BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
/* backwards compatibility: do not add new entries to this section */
-Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
-DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
+Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
+DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
+DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
+DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
+DHCP.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 6f32e5f..ec95c86 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -717,3 +717,37 @@ int config_parse_ipv6token(
return 0;
}
+
+int config_parse_address_family_boolean_with_kernel(
+ 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) {
+
+ AddressFamilyBoolean *fwd = data, s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = address_family_boolean_from_string(rvalue);
+ if (s < 0) {
+ if (streq(rvalue, "kernel"))
+ s = _ADDRESS_FAMILY_BOOLEAN_INVALID;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding option, ignoring: %s", rvalue);
+ return 0;
+ }
+ }
+
+ *fwd = s;
+
+ return 0;
+}
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 4b13d4a..2c191a7 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -448,6 +448,9 @@ AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
int config_parse_address_family_boolean(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);
+/* IPForwarding parser */
+int config_parse_address_family_boolean_with_kernel(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);
+
/* Operational State */
const char* link_operstate_to_string(LinkOperationalState s) _const_;
commit ed810b68d323a58b6c36fd00d1118699466ca34a
Merge: c067b4a 31710be
Author: David Herrmann <dh.herrmann at googlemail.com>
Date: Thu Jun 11 18:05:18 2015 +0200
Merge pull request #171 from teg/rtnl-broadcast-2
sd-rtnl: make joining broadcast groups implicit
commit 31710be527104abad7541b122ee10c4560bd14d2
Author: Tom Gundersen <teg at jklm.no>
Date: Thu Jun 11 15:55:37 2015 +0200
sd-rtnl: make joining broadcast groups implicit
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 63b15c1..938f3ab 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -70,7 +70,7 @@ int loopback_setup(void) {
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
int r;
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/test-pppoe.c b/src/libsystemd-network/test-pppoe.c
index 9c8d6f7..cc51197 100644
--- a/src/libsystemd-network/test-pppoe.c
+++ b/src/libsystemd-network/test-pppoe.c
@@ -94,7 +94,7 @@ static int test_pppoe_server(sd_event *e) {
assert_se(r >= 0);
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(sd_rtnl_attach_event(rtnl, e, 0) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
diff --git a/src/libsystemd/sd-rtnl/local-addresses.c b/src/libsystemd/sd-rtnl/local-addresses.c
index 31bfa06..f05cbf1 100644
--- a/src/libsystemd/sd-rtnl/local-addresses.c
+++ b/src/libsystemd/sd-rtnl/local-addresses.c
@@ -66,7 +66,7 @@ int local_addresses(sd_rtnl *context, int ifindex, int af, struct local_address
if (context)
rtnl = sd_rtnl_ref(context);
else {
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return r;
}
@@ -177,7 +177,7 @@ int local_gateways(sd_rtnl *context, int ifindex, int af, struct local_address *
if (context)
rtnl = sd_rtnl_ref(context);
else {
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return r;
}
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.c b/src/libsystemd/sd-rtnl/rtnl-util.c
index 9ddf074..c2b1a5c 100644
--- a/src/libsystemd/sd-rtnl/rtnl-util.c
+++ b/src/libsystemd/sd-rtnl/rtnl-util.c
@@ -34,7 +34,7 @@ int rtnl_set_link_name(sd_rtnl **rtnl, int ifindex, const char *name) {
assert(name);
if (!*rtnl) {
- r = sd_rtnl_open(rtnl, 0);
+ r = sd_rtnl_open(rtnl);
if (r < 0)
return r;
}
@@ -66,7 +66,7 @@ int rtnl_set_link_properties(sd_rtnl **rtnl, int ifindex, const char *alias,
return 0;
if (!*rtnl) {
- r = sd_rtnl_open(rtnl, 0);
+ r = sd_rtnl_open(rtnl);
if (r < 0)
return r;
}
diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c
index 40dea12..c682007 100644
--- a/src/libsystemd/sd-rtnl/sd-rtnl.c
+++ b/src/libsystemd/sd-rtnl/sd-rtnl.c
@@ -106,25 +106,7 @@ static bool rtnl_pid_changed(sd_rtnl *rtnl) {
return rtnl->original_pid != getpid();
}
-static int rtnl_compute_groups_ap(uint32_t *_groups, unsigned n_groups, va_list ap) {
- uint32_t groups = 0;
- unsigned i;
-
- for (i = 0; i < n_groups; i++) {
- unsigned group;
-
- group = va_arg(ap, unsigned);
- assert_return(group < 32, -EINVAL);
-
- groups |= group ? (1 << (group - 1)) : 0;
- }
-
- *_groups = groups;
-
- return 0;
-}
-
-static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap) {
+int sd_rtnl_open_fd(sd_rtnl **ret, int fd) {
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
socklen_t addrlen;
int r, one = 1;
@@ -144,10 +126,6 @@ static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap)
if (r < 0)
return -errno;
- r = rtnl_compute_groups_ap(&rtnl->sockaddr.nl.nl_groups, n_groups, ap);
- if (r < 0)
- return r;
-
addrlen = sizeof(rtnl->sockaddr);
r = bind(fd, &rtnl->sockaddr.sa, addrlen);
@@ -167,33 +145,33 @@ static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap)
return 0;
}
-int sd_rtnl_open_fd(sd_rtnl **ret, int fd, unsigned n_groups, ...) {
- va_list ap;
+int sd_rtnl_open(sd_rtnl **ret) {
+ _cleanup_close_ int fd = -1;
int r;
- va_start(ap, n_groups);
- r = rtnl_open_fd_ap(ret, fd, n_groups, ap);
- va_end(ap);
-
- return r;
-}
-
-int sd_rtnl_open(sd_rtnl **ret, unsigned n_groups, ...) {
- va_list ap;
- int fd, r;
-
fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
if (fd < 0)
return -errno;
- va_start(ap, n_groups);
- r = rtnl_open_fd_ap(ret, fd, n_groups, ap);
- va_end(ap);
-
- if (r < 0) {
- safe_close(fd);
+ r = sd_rtnl_open_fd(ret, fd);
+ if (r < 0)
return r;
- }
+
+ fd = -1;
+
+ return 0;
+}
+
+static int rtnl_join_broadcast_group(sd_rtnl *rtnl, unsigned group) {
+ int r;
+
+ assert(rtnl);
+ assert(rtnl->fd >= 0);
+ assert(group > 0);
+
+ r = setsockopt(rtnl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
+ if (r < 0)
+ return -errno;
return 0;
}
@@ -1005,14 +983,12 @@ int sd_rtnl_add_match(sd_rtnl *rtnl,
uint16_t type,
sd_rtnl_message_handler_t callback,
void *userdata) {
- struct match_callback *c;
+ _cleanup_free_ struct match_callback *c = NULL;
+ int r;
assert_return(rtnl, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
- assert_return(rtnl_message_type_is_link(type) ||
- rtnl_message_type_is_addr(type) ||
- rtnl_message_type_is_route(type), -EOPNOTSUPP);
c = new0(struct match_callback, 1);
if (!c)
@@ -1022,8 +998,36 @@ int sd_rtnl_add_match(sd_rtnl *rtnl,
c->type = type;
c->userdata = userdata;
+ switch (type) {
+ case RTM_NEWLINK:
+ case RTM_SETLINK:
+ case RTM_GETLINK:
+ case RTM_DELLINK:
+ r = rtnl_join_broadcast_group(rtnl, RTNLGRP_LINK);
+ if (r < 0)
+ return r;
+
+ break;
+ case RTM_NEWADDR:
+ case RTM_GETADDR:
+ case RTM_DELADDR:
+ r = rtnl_join_broadcast_group(rtnl, RTNLGRP_IPV4_IFADDR);
+ if (r < 0)
+ return r;
+
+ r = rtnl_join_broadcast_group(rtnl, RTNLGRP_IPV6_IFADDR);
+ if (r < 0)
+ return r;
+
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
+ c = NULL;
+
return 0;
}
@@ -1037,6 +1041,13 @@ int sd_rtnl_remove_match(sd_rtnl *rtnl,
assert_return(callback, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ /* we should unsubscribe from the broadcast groups at this point, but it is not so
+ trivial for a few reasons: the refcounting is a bit of a mess and not obvious
+ how it will look like after we add genetlink support, and it is also not possible
+ to query what broadcast groups were subscribed to when we inherit the socket to get
+ the initial refcount. The latter could indeed be done for the first 32 broadcast
+ groups (which incidentally is all we currently support in .socket units anyway),
+ but we better not rely on only ever using 32 groups. */
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
if (c->callback == callback && c->type == type && c->userdata == userdata) {
LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c
index 47cce64..94b1cb7 100644
--- a/src/libsystemd/sd-rtnl/test-rtnl.c
+++ b/src/libsystemd/sd-rtnl/test-rtnl.c
@@ -184,8 +184,8 @@ static void test_route(void) {
static void test_multiple(void) {
sd_rtnl *rtnl1, *rtnl2;
- assert_se(sd_rtnl_open(&rtnl1, 0) >= 0);
- assert_se(sd_rtnl_open(&rtnl2, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl1) >= 0);
+ assert_se(sd_rtnl_open(&rtnl2) >= 0);
rtnl1 = sd_rtnl_unref(rtnl1);
rtnl2 = sd_rtnl_unref(rtnl2);
@@ -216,7 +216,7 @@ static void test_event_loop(int ifindex) {
ifname = strdup("lo2");
assert_se(ifname);
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_rtnl_call_async(rtnl, m, &link_handler, ifname, 0, NULL) >= 0);
@@ -256,7 +256,7 @@ static void test_async(int ifindex) {
ifname = strdup("lo");
assert_se(ifname);
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
@@ -273,7 +273,7 @@ static void test_pipe(int ifindex) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m1 = NULL, *m2 = NULL;
int counter = 0;
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0);
@@ -330,7 +330,7 @@ static void test_container(void) {
static void test_match(void) {
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(sd_rtnl_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0);
assert_se(sd_rtnl_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0);
@@ -395,7 +395,7 @@ int main(void) {
test_container();
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(rtnl);
if_loopback = (int) if_nametoindex("lo");
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 68925de..8e20f70 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -199,7 +199,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
pager_open_if_enabled();
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@@ -670,7 +670,7 @@ static int link_status(int argc, char *argv[], void *userdata) {
char **name;
int r;
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@@ -910,7 +910,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
pager_open_if_enabled();
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index db737ad..9936a8e 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -385,9 +385,9 @@ static int manager_connect_rtnl(Manager *m) {
fd = systemd_netlink_fd();
if (fd < 0)
- r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR);
+ r = sd_rtnl_open(&m->rtnl);
else
- r = sd_rtnl_open_fd(&m->rtnl, fd, 0);
+ r = sd_rtnl_open_fd(&m->rtnl, fd);
if (r < 0)
return r;
diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c
index 1c997a5..39f3845 100644
--- a/src/network/networkd-wait-online-manager.c
+++ b/src/network/networkd-wait-online-manager.c
@@ -177,7 +177,7 @@ static int manager_rtnl_listen(Manager *m) {
assert(m);
/* First, subscribe to interfaces coming and going */
- r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR);
+ r = sd_rtnl_open(&m->rtnl);
if (r < 0)
return r;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 251af4a..6a21ed5 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2062,7 +2062,7 @@ static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
- r = sd_rtnl_open_fd(&rtnl, fd, 1, RTNLGRP_IPV4_IFADDR);
+ r = sd_rtnl_open_fd(&rtnl, fd);
if (r < 0) {
safe_close(fd);
return log_error_errno(r, "Failed to create rtnl object: %m");
@@ -2574,7 +2574,7 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) {
if (r < 0)
return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@@ -2659,7 +2659,7 @@ static int setup_bridge(const char veth_name[], int *ifi) {
*ifi = bridge;
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@@ -2720,7 +2720,7 @@ static int move_network_interfaces(pid_t pid) {
if (strv_isempty(arg_network_interfaces))
return 0;
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@@ -2767,7 +2767,7 @@ static int setup_macvlan(pid_t pid) {
if (strv_isempty(arg_network_macvlan))
return 0;
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@@ -2857,7 +2857,7 @@ static int setup_ipvlan(pid_t pid) {
if (strv_isempty(arg_network_ipvlan))
return 0;
- r = sd_rtnl_open(&rtnl, 0);
+ r = sd_rtnl_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index f8d4db7..aa78885 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -195,7 +195,7 @@ static int manager_rtnl_listen(Manager *m) {
assert(m);
/* First, subscribe to interfaces coming and going */
- r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR);
+ r = sd_rtnl_open(&m->rtnl);
if (r < 0)
return r;
diff --git a/src/systemd/sd-rtnl.h b/src/systemd/sd-rtnl.h
index be318e5..b05690c 100644
--- a/src/systemd/sd-rtnl.h
+++ b/src/systemd/sd-rtnl.h
@@ -42,8 +42,8 @@ typedef int (*sd_rtnl_message_handler_t)(sd_rtnl *rtnl, sd_rtnl_message *m, void
/* bus */
int sd_rtnl_new_from_netlink(sd_rtnl **nl, int fd);
-int sd_rtnl_open(sd_rtnl **nl, unsigned n_groups, ...);
-int sd_rtnl_open_fd(sd_rtnl **nl, int fd, unsigned n_groups, ...);
+int sd_rtnl_open(sd_rtnl **nl);
+int sd_rtnl_open_fd(sd_rtnl **nl, int fd);
int sd_rtnl_inc_rcvbuf(const sd_rtnl *const rtnl, const int size);
sd_rtnl *sd_rtnl_ref(sd_rtnl *nl);
@@ -57,7 +57,6 @@ int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial);
int sd_rtnl_call(sd_rtnl *nl, sd_rtnl_message *message, uint64_t timeout,
sd_rtnl_message **reply);
-
int sd_rtnl_get_events(sd_rtnl *nl);
int sd_rtnl_get_timeout(sd_rtnl *nl, uint64_t *timeout);
int sd_rtnl_process(sd_rtnl *nl, sd_rtnl_message **ret);
diff --git a/src/test/test-rtnl-manual.c b/src/test/test-rtnl-manual.c
index c406454..9fc860d 100644
--- a/src/test/test-rtnl-manual.c
+++ b/src/test/test-rtnl-manual.c
@@ -136,7 +136,7 @@ int main(int argc, char *argv[]) {
sd_rtnl *rtnl;
int r;
- assert_se(sd_rtnl_open(&rtnl, 0) >= 0);
+ assert_se(sd_rtnl_open(&rtnl) >= 0);
assert_se(rtnl);
r = test_tunnel_configure(rtnl);
commit c067b4acef6715050a9fe92d5e16293d06ec69ee
Merge: 6f6fdcb 76f0a56
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 17:42:46 2015 +0200
Merge pull request #143 from teg/networkd-packets-per-slave-mode
networkd: bond - only set packets_per_slave on balance-rr mode
commit 6f6fdcb5906ff9259443b97daca429ed1de2559c
Merge: 9d1b6c8 ec5ff44
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 17:42:06 2015 +0200
Merge pull request #156 from filbranden/journal_leading_whitespace
journald: do not strip leading whitespace from messages
commit 9d1b6c8bcaef54e9a8b9174b969aa4d1c1a32d43
Merge: 512b85a 78d298b
Author: David Herrmann <dh.herrmann at googlemail.com>
Date: Thu Jun 11 17:01:39 2015 +0200
Merge pull request #166 from zonque/kmod
kmod-setup: don't print warning on -ENOSYS
commit 78d298bbc57e412574ea35e6e66f562d97fd9ebc
Author: Daniel Mack <daniel at zonque.org>
Date: Thu Jun 11 13:10:39 2015 +0200
kmod-setup: don't print warning on -ENOSYS
-ENOSYS is returned from kmod_module_probe_insert_module() if a module isn't
available, not -ENOENT. Don't spit out a warning in that case unless the
warn_if_unavailable flag is set.
Also factor out the condition into an own variable for better readability.
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index d956f9b..f5584b6 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -117,10 +117,12 @@ int kmod_setup(void) {
log_info("Inserted module '%s'", kmod_module_get_name(mod));
else if (r == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
- else
- log_full_errno((kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOENT)) ? LOG_WARNING : LOG_DEBUG,
- r,
+ else {
+ bool print_warning = kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOSYS);
+
+ log_full_errno(print_warning ? LOG_WARNING : LOG_DEBUG, r,
"Failed to insert module '%s': %m", kmod_module_get_name(mod));
+ }
kmod_module_unref(mod);
}
commit 512b85af26a68a41a441e1b0489932a96a6f757a
Merge: 4aa2764 2ce8483
Author: David Herrmann <dh.herrmann at googlemail.com>
Date: Thu Jun 11 16:27:32 2015 +0200
Merge pull request #170 from teg/rtnl-recv
sd-rtnl: use netlink header rather than ucred in socket_recv_message()
commit 2ce8483538887f7d64d3ae5afe03b72c34210b20
Author: Tom Gundersen <teg at jklm.no>
Date: Thu Jun 11 11:34:54 2015 +0200
sd-rtnl: use netlink header rather than ucred in socket_recv_message()
Lets us simplify the function and drop SO_PASSCRED.
Thanks to Alexander Larsson and David Herrmann.
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c
index 9dcf7df..79e67f6 100644
--- a/src/libsystemd/sd-rtnl/rtnl-message.c
+++ b/src/libsystemd/sd-rtnl/rtnl-message.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include "util.h"
+#include "socket-util.h"
#include "formats-util.h"
#include "refcnt.h"
#include "missing.h"
@@ -1415,17 +1416,18 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
}
static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
- uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(sizeof(struct nl_pktinfo))];
+ union sockaddr_union sender;
+ uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
struct msghdr msg = {
.msg_iov = iov,
.msg_iovlen = 1,
- .msg_control = cred_buffer,
- .msg_controllen = sizeof(cred_buffer),
+ .msg_name = &sender,
+ .msg_namelen = sizeof(sender),
+ .msg_control = cmsg_buffer,
+ .msg_controllen = sizeof(cmsg_buffer),
};
struct cmsghdr *cmsg;
uint32_t group = 0;
- bool auth = false;
int r;
assert(fd >= 0);
@@ -1442,29 +1444,10 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
}
- CMSG_FOREACH(cmsg, &msg) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
- struct ucred *ucred = (void *)CMSG_DATA(cmsg);
-
- /* from the kernel */
- if (ucred->pid == 0)
- auth = true;
- else
- log_debug("rtnl: ignoring message from PID "PID_FMT, ucred->pid);
- } else if (cmsg->cmsg_level == SOL_NETLINK &&
- cmsg->cmsg_type == NETLINK_PKTINFO &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
- struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
-
- /* multi-cast group */
- group = pktinfo->group;
- }
- }
-
- if (!auth) {
+ if (sender.nl.nl_pid != 0) {
/* not from the kernel, ignore */
+ log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
+
if (peek) {
/* drop the message */
r = recvmsg(fd, &msg, 0);
@@ -1475,6 +1458,17 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
return 0;
}
+ CMSG_FOREACH(cmsg, &msg) {
+ if (cmsg->cmsg_level == SOL_NETLINK &&
+ cmsg->cmsg_type == NETLINK_PKTINFO &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
+ struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
+
+ /* multi-cast group */
+ group = pktinfo->group;
+ }
+ }
+
if (_group)
*_group = group;
diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c
index 40dea12..ae9a40c 100644
--- a/src/libsystemd/sd-rtnl/sd-rtnl.c
+++ b/src/libsystemd/sd-rtnl/sd-rtnl.c
@@ -136,10 +136,6 @@ static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap)
if (r < 0)
return r;
- r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
- if (r < 0)
- return -errno;
-
r = setsockopt(fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
if (r < 0)
return -errno;
commit 92994160afa888255a7ede525dd16e3f1e2ed10d
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date: Wed Jun 10 15:19:03 2015 -0400
ima-setup: write policy one line at a time
ima_write_policy() expects data to be written as one or more
rules, no more than PAGE_SIZE at a time. Easiest way to ensure
that we are not splitting rules is to read and write one line at
a time.
https://bugzilla.redhat.com/show_bug.cgi?id=1226948
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index 4d8b638..42a3e97 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -23,9 +23,6 @@
#include <unistd.h>
#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
#include "ima-setup.h"
#include "util.h"
@@ -36,20 +33,19 @@
#define IMA_POLICY_PATH "/etc/ima/ima-policy"
int ima_setup(void) {
- int r = 0;
-
#ifdef HAVE_IMA
- _cleanup_close_ int policyfd = -1, imafd = -1;
- struct stat st;
- char *policy;
+ _cleanup_fclose_ FILE *input = NULL;
+ _cleanup_close_ int imafd = -1;
+ unsigned lineno = 0;
+ char line[page_size()];
if (access(IMA_SECFS_DIR, F_OK) < 0) {
log_debug("IMA support is disabled in the kernel, ignoring.");
return 0;
}
- policyfd = open(IMA_POLICY_PATH, O_RDONLY|O_CLOEXEC);
- if (policyfd < 0) {
+ input = fopen(IMA_POLICY_PATH, "re");
+ if (!input) {
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open the IMA custom policy file "IMA_POLICY_PATH", ignoring: %m");
return 0;
@@ -66,20 +62,19 @@ int ima_setup(void) {
return 0;
}
- if (fstat(policyfd, &st) < 0)
- return log_error_errno(errno, "Failed to fstat "IMA_POLICY_PATH": %m");
+ FOREACH_LINE(line, input,
+ return log_error_errno(errno, "Failed to read the IMA custom policy file "IMA_POLICY_PATH": %m")) {
+ size_t len;
- policy = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, policyfd, 0);
- if (policy == MAP_FAILED)
- return log_error_errno(errno, "Failed to mmap "IMA_POLICY_PATH": %m");
+ len = strlen(line);
+ lineno++;
- r = loop_write(imafd, policy, (size_t) st.st_size, false);
- if (r < 0)
- log_error_errno(r, "Failed to load the IMA custom policy file "IMA_POLICY_PATH": %m");
- else
- log_info("Successfully loaded the IMA custom policy "IMA_POLICY_PATH".");
+ if (len > 0 && write(imafd, line, len) < 0)
+ return log_error_errno(errno, "Failed to load the IMA custom policy file "IMA_POLICY_PATH"%u: %m",
+ lineno);
+ }
- munmap(policy, st.st_size);
+ log_info("Successfully loaded the IMA custom policy "IMA_POLICY_PATH".");
#endif /* HAVE_IMA */
- return r;
+ return 0;
}
commit 4aa2764cade3e6d5233f2e390d64931d5cff1410
Merge: d814f99 a0ab162
Author: Daniel Mack <github at zonque.org>
Date: Thu Jun 11 12:51:27 2015 +0200
Merge pull request #84 from blueyed/zsh-optimize-filter_units_by_property
zsh-completion: optimize _filter_units_by_property
commit d814f9904bb9705070dde8458464b8afe6cfbb22
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 12:13:03 2015 +0200
kmod-setup: when we fail to load a kmod, log the error cause
(Also, downgrade message from LOG_ERROR to LOG_WARNING, after all we
don't care much and just proceed)
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index cf543c8..d956f9b 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -118,8 +118,9 @@ int kmod_setup(void) {
else if (r == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
else
- log_full((kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOENT)) ? LOG_ERR : LOG_DEBUG,
- "Failed to insert module '%s'", kmod_module_get_name(mod));
+ log_full_errno((kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOENT)) ? LOG_WARNING : LOG_DEBUG,
+ r,
+ "Failed to insert module '%s': %m", kmod_module_get_name(mod));
kmod_module_unref(mod);
}
commit de1d4f9b5c6345f63edd46f643485eca909995bf
Author: Werner Fink <werner at suse.de>
Date: Wed Jun 10 14:36:50 2015 +0200
core: Let two more booleans survive a daemon-reload
Without the boolean bus_name_good services as well as cgroup_realized
for units a unit of Type=dbus and ExecReload sending SIGHUP to $MAINPID
will be terminated if systemd will be daemon reloaded.
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=746151
https://bugs.freedesktop.org/show_bug.cgi?id=78311
https://bugzilla.opensuse.org/show_bug.cgi?id=934077
diff --git a/src/core/service.c b/src/core/service.c
index c7e6577..71252e2 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -2008,6 +2008,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item_format(u, f, "main-pid", PID_FMT, s->main_pid);
unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
+ unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good));
if (s->status_text) {
_cleanup_free_ char *c = NULL;
@@ -2131,6 +2132,14 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
log_unit_debug(u, "Failed to parse main-pid-known value: %s", value);
else
s->main_pid_known = b;
+ } else if (streq(key, "bus-name-good")) {
+ int b;
+
+ b = parse_boolean(value);
+ if (b < 0)
+ log_unit_debug(u, "Failed to parse bus-name-good value: %s", value);
+ else
+ s->bus_name_good = b;
} else if (streq(key, "status-text")) {
char *t;
diff --git a/src/core/unit.c b/src/core/unit.c
index e380276..7bb2afc 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2596,6 +2596,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
+ unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
if (serialize_jobs) {
if (u->job) {
@@ -2806,6 +2807,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
continue;
+ } else if (streq(l, "cgroup-realized")) {
+ int b;
+
+ b = parse_boolean(v);
+ if (b < 0)
+ log_unit_debug(u, "Failed to parse cgroup-realized bool %s, ignoring.", v);
+ else
+ u->cgroup_realized = b;
+
+ continue;
}
if (unit_can_serialize(u)) {
commit 64b8c0d030263fc37003f8655b53523231ec81d2
Merge: 814acdd 3de1c8c
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 11:52:58 2015 +0200
Merge pull request #158 from ssahani/net
Net
commit 814acddde14d2053d715221590298db6531676e4
Merge: 94f1f58 f73fb7b
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 11:50:26 2015 +0200
Merge pull request #157 from filbranden/revert_unused_result
build-sys: suppress warnings of unused results
commit 94f1f58d2b0aaab4db413f3c4ec6896a91b4c4ff
Merge: ed4dc5b a095315
Author: Daniel Mack <github at zonque.org>
Date: Thu Jun 11 11:50:18 2015 +0200
Merge pull request #160 from kaysievers/libsplit
build-sys: split internal basic/ library from shared/
commit ed4dc5bbd1d985d267ed146f98f7a02caf1c5078
Merge: 37c47e5 a315ac4
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 11:34:38 2015 +0200
Merge pull request #150 from poettering/journal-no-recv-loop
journald: don't employ inner loop for reading from incoming sockets
commit a095315b3c31f7a419baceac82c26c3c5ac0cd12
Author: Kay Sievers <kay at vrfy.org>
Date: Tue Jun 9 14:27:33 2015 +0200
build-sys: split internal basic/ library from shared/
basic/ can be used by everything
cannot use anything outside of basic/
libsystemd/ can use basic/
cannot use shared/
shared/ can use libsystemd/
diff --git a/Makefile.am b/Makefile.am
index 783e19b..9c52dcb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -211,8 +211,10 @@ AM_CPPFLAGS = \
-DROOTLIBEXECDIR=\"$(rootlibexecdir)\" \
-DTEST_DIR=\"$(abs_top_srcdir)/test\" \
-I $(top_srcdir)/src \
- -I $(top_builddir)/src/shared \
+ -I $(top_builddir)/src/basic \
+ -I $(top_srcdir)/src/basic \
-I $(top_srcdir)/src/shared \
+ -I $(top_builddir)/src/shared \
-I $(top_srcdir)/src/network \
-I $(top_srcdir)/src/login \
-I $(top_srcdir)/src/journal \
@@ -230,6 +232,7 @@ AM_CPPFLAGS = \
-I $(top_builddir)/src/udev \
-I $(top_srcdir)/src/libsystemd/sd-bus \
-I $(top_srcdir)/src/libsystemd/sd-event \
+ -I $(top_srcdir)/src/libsystemd/sd-login \
-I $(top_srcdir)/src/libsystemd/sd-rtnl \
-I $(top_srcdir)/src/libsystemd/sd-network \
-I $(top_srcdir)/src/libsystemd/sd-hwdb \
@@ -742,32 +745,180 @@ EXTRA_DIST += \
# ------------------------------------------------------------------------------
noinst_LTLIBRARIES += \
- libsystemd-shared.la
+ libbasic.la
+
+libbasic_la_SOURCES = \
+ src/basic/missing.h \
+ src/basic/capability.c \
+ src/basic/capability.h \
+ src/basic/conf-files.c \
+ src/basic/conf-files.h \
+ src/basic/hostname-util.h \
+ src/basic/hostname-util.c \
+ src/basic/unit-name.c \
+ src/basic/unit-name.h \
+ src/basic/ioprio.h \
+ src/basic/securebits.h \
+ src/basic/special.h \
+ src/basic/list.h \
+ src/basic/unaligned.h \
+ src/basic/macro.h \
+ src/basic/def.h \
+ src/basic/sparse-endian.h \
+ src/basic/refcnt.h \
+ src/basic/util.c \
+ src/basic/util.h \
+ src/basic/lockfile-util.c \
+ src/basic/lockfile-util.h \
+ src/basic/path-util.c \
+ src/basic/path-util.h \
+ src/basic/time-util.c \
+ src/basic/time-util.h \
+ src/basic/locale-util.c \
+ src/basic/locale-util.h \
+ src/basic/signal-util.c \
+ src/basic/signal-util.h \
+ src/basic/mempool.c \
+ src/basic/mempool.h \
+ src/basic/hashmap.c \
+ src/basic/hashmap.h \
+ src/basic/siphash24.c \
+ src/basic/siphash24.h \
+ src/basic/set.h \
+ src/basic/ordered-set.h \
+ src/basic/fdset.c \
+ src/basic/fdset.h \
+ src/basic/prioq.c \
+ src/basic/prioq.h \
+ src/basic/strv.c \
+ src/basic/strv.h \
+ src/basic/env-util.c \
+ src/basic/env-util.h \
+ src/basic/strbuf.c \
+ src/basic/strbuf.h \
+ src/basic/strxcpyx.c \
+ src/basic/strxcpyx.h \
+ src/basic/log.c \
+ src/basic/log.h \
+ src/basic/bus-label.c \
+ src/basic/bus-label.h \
+ src/basic/ratelimit.h \
+ src/basic/ratelimit.c \
+ src/basic/exit-status.c \
+ src/basic/exit-status.h \
+ src/basic/virt.c \
+ src/basic/virt.h \
+ src/basic/smack-util.c \
+ src/basic/smack-util.h \
+ src/basic/device-nodes.c \
+ src/basic/device-nodes.h \
+ src/basic/utf8.c \
+ src/basic/utf8.h \
+ src/basic/gunicode.c \
+ src/basic/gunicode.h \
+ src/basic/socket-util.c \
+ src/basic/socket-util.h \
+ src/basic/in-addr-util.c \
+ src/basic/in-addr-util.h \
+ src/basic/ether-addr-util.h \
+ src/basic/replace-var.c \
+ src/basic/replace-var.h \
+ src/basic/clock-util.c \
+ src/basic/clock-util.h \
+ src/basic/calendarspec.c \
+ src/basic/calendarspec.h \
+ src/basic/fileio.c \
+ src/basic/fileio.h \
+ src/basic/MurmurHash2.c \
+ src/basic/MurmurHash2.h \
+ src/basic/mkdir.c \
+ src/basic/mkdir.h \
+ src/basic/cgroup-util.c \
+ src/basic/cgroup-util.h \
+ src/basic/errno-list.c \
+ src/basic/errno-list.h \
+ src/basic/af-list.c \
+ src/basic/af-list.h \
+ src/basic/arphrd-list.c \
+ src/basic/arphrd-list.h \
+ src/basic/terminal-util.c \
+ src/basic/terminal-util.h \
+ src/basic/login-util.h \
+ src/basic/login-util.c \
+ src/basic/cap-list.c \
+ src/basic/cap-list.h \
+ src/basic/audit.c \
+ src/basic/audit.h \
+ src/basic/xml.c \
+ src/basic/xml.h \
+ src/basic/json.c \
+ src/basic/json.h \
+ src/basic/ring.c \
+ src/basic/ring.h \
+ src/basic/barrier.c \
+ src/basic/barrier.h \
+ src/basic/async.c \
+ src/basic/async.h \
+ src/basic/memfd-util.c \
+ src/basic/memfd-util.h \
+ src/basic/process-util.c \
+ src/basic/process-util.h \
+ src/basic/random-util.c \
+ src/basic/random-util.h \
+ src/basic/verbs.c \
+ src/basic/verbs.h \
+ src/basic/sigbus.c \
+ src/basic/sigbus.h \
+ src/basic/build.h \
+ src/basic/socket-label.c \
+ src/basic/label.c \
+ src/basic/label.h \
+ src/basic/btrfs-util.c \
+ src/basic/btrfs-util.h \
+ src/basic/btrfs-ctree.h \
+ src/basic/selinux-util.c \
+ src/basic/selinux-util.h \
+ src/basic/mkdir-label.c \
+ src/basic/fileio-label.c \
+ src/basic/fileio-label.h \
+ src/basic/rm-rf.c \
+ src/basic/rm-rf.h \
+ src/basic/copy.c \
+ src/basic/copy.h
+
+nodist_libbasic_la_SOURCES = \
+ src/basic/errno-from-name.h \
+ src/basic/errno-to-name.h \
+ src/basic/af-from-name.h \
+ src/basic/af-to-name.h \
+ src/basic/arphrd-from-name.h \
+ src/basic/arphrd-to-name.h \
+ src/basic/cap-from-name.h \
+ src/basic/cap-to-name.h
+
+libbasic_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SELINUX_CFLAGS) \
+ $(CAP_CFLAGS) \
+ -pthread
+
+libbasic_la_LIBADD = \
+ $(SELINUX_LIBS) \
+ $(CAP_LIBS) \
+ -lm
+
+# -----------------------------------------------------------------------------
+noinst_LTLIBRARIES += \
+ libshared.la
-libsystemd_shared_la_SOURCES = \
- src/shared/capability.c \
- src/shared/capability.h \
+libshared_la_SOURCES = \
+ src/shared/output-mode.h \
+ src/shared/gpt.h \
+ src/shared/udev-util.h \
src/shared/linux/auto_dev-ioctl.h \
- src/shared/ioprio.h \
- src/shared/missing.h \
src/shared/initreq.h \
- src/shared/securebits.h \
- src/shared/special.h \
- src/shared/list.h \
- src/shared/unaligned.h \
- src/shared/macro.h \
- src/shared/def.h \
- src/shared/sparse-endian.h \
- src/shared/refcnt.h \
- src/shared/udev-util.h \
- src/shared/device-nodes.c \
- src/shared/device-nodes.h \
src/shared/dns-domain.c \
src/shared/dns-domain.h \
- src/shared/util.c \
- src/shared/util.h \
- src/shared/virt.c \
- src/shared/virt.h \
src/shared/architecture.c \
src/shared/architecture.h \
src/shared/efivars.c \
@@ -775,238 +926,122 @@ libsystemd_shared_la_SOURCES = \
src/shared/formats-util.h \
src/shared/fstab-util.c \
src/shared/fstab-util.h \
- src/shared/lockfile-util.c \
- src/shared/lockfile-util.h \
- src/shared/path-util.c \
- src/shared/path-util.h \
- src/shared/time-util.c \
- src/shared/time-util.h \
- src/shared/locale-util.c \
- src/shared/locale-util.h \
- src/shared/signal-util.c \
- src/shared/signal-util.h \
- src/shared/mempool.c \
- src/shared/mempool.h \
- src/shared/hashmap.c \
- src/shared/hashmap.h \
- src/shared/siphash24.c \
- src/shared/siphash24.h \
- src/shared/set.h \
- src/shared/ordered-set.h \
- src/shared/fdset.c \
- src/shared/fdset.h \
- src/shared/prioq.c \
- src/shared/prioq.h \
src/shared/sleep-config.c \
src/shared/sleep-config.h \
- src/shared/strv.c \
- src/shared/strv.h \
- src/shared/env-util.c \
- src/shared/env-util.h \
- src/shared/strbuf.c \
- src/shared/strbuf.h \
- src/shared/strxcpyx.c \
- src/shared/strxcpyx.h \
src/shared/conf-parser.c \
src/shared/conf-parser.h \
- src/shared/log.c \
- src/shared/log.h \
- src/shared/ratelimit.h \
- src/shared/ratelimit.c \
- src/shared/exit-status.c \
- src/shared/exit-status.h \
- src/shared/utf8.c \
- src/shared/utf8.h \
- src/shared/gunicode.c \
- src/shared/gunicode.h \
src/shared/pager.c \
src/shared/pager.h \
- src/shared/socket-util.c \
- src/shared/socket-util.h \
- src/shared/in-addr-util.c \
- src/shared/in-addr-util.h \
- src/shared/ether-addr-util.h \
- src/shared/conf-files.c \
- src/shared/conf-files.h \
- src/shared/cgroup-util.c \
- src/shared/cgroup-util.h \
- src/shared/cgroup-show.c \
- src/shared/cgroup-show.h \
- src/shared/unit-name.c \
- src/shared/unit-name.h \
- src/shared/utmp-wtmp.h \
- src/shared/watchdog.c \
- src/shared/watchdog.h \
- src/shared/spawn-ask-password-agent.c \
- src/shared/spawn-ask-password-agent.h \
- src/shared/replace-var.c \
- src/shared/replace-var.h \
src/shared/spawn-polkit-agent.c \
src/shared/spawn-polkit-agent.h \
- src/shared/clock-util.c \
- src/shared/clock-util.h \
- src/shared/calendarspec.c \
- src/shared/calendarspec.h \
- src/shared/fileio.c \
- src/shared/fileio.h \
- src/shared/output-mode.h \
- src/shared/MurmurHash2.c \
- src/shared/MurmurHash2.h \
- src/shared/acpi-fpdt.h \
- src/shared/acpi-fpdt.c \
- src/shared/boot-timestamps.h \
- src/shared/boot-timestamps.c \
- src/shared/mkdir.c \
- src/shared/mkdir.h \
- src/shared/smack-util.c \
- src/shared/smack-util.h \
src/shared/apparmor-util.c \
src/shared/apparmor-util.h \
src/shared/ima-util.c \
src/shared/ima-util.h \
src/shared/ptyfwd.c \
src/shared/ptyfwd.h \
- src/shared/errno-list.c \
- src/shared/errno-list.h \
- src/shared/af-list.c \
- src/shared/af-list.h \
- src/shared/arphrd-list.c \
- src/shared/arphrd-list.h \
- src/shared/cap-list.c \
- src/shared/cap-list.h \
- src/shared/audit.c \
- src/shared/audit.h \
- src/shared/xml.c \
- src/shared/xml.h \
- src/shared/json.c \
- src/shared/json.h \
- src/shared/bus-label.c \
- src/shared/bus-label.h \
- src/shared/gpt.h \
- src/shared/clean-ipc.h \
- src/shared/clean-ipc.c \
- src/shared/login-shared.c \
- src/shared/login-shared.h \
- src/shared/ring.c \
- src/shared/ring.h \
- src/shared/barrier.c \
- src/shared/barrier.h \
src/shared/pty.c \
src/shared/pty.h \
- src/shared/async.c \
- src/shared/async.h \
src/shared/base-filesystem.c \
src/shared/base-filesystem.h \
- src/shared/memfd-util.c \
- src/shared/memfd-util.h \
- src/shared/process-util.c \
- src/shared/process-util.h \
- src/shared/random-util.c \
- src/shared/random-util.h \
- src/shared/terminal-util.c \
- src/shared/terminal-util.h \
src/shared/uid-range.c \
src/shared/uid-range.h \
src/shared/nss-util.h \
- src/shared/verbs.c \
- src/shared/verbs.h \
- src/shared/sigbus.c \
- src/shared/sigbus.h \
- src/shared/build.h \
- src/shared/import-util.c \
- src/shared/import-util.h \
- src/shared/sysctl-util.c \
- src/shared/sysctl-util.h \
- src/shared/hostname-util.h \
- src/shared/hostname-util.c \
- src/shared/socket-label.c \
- src/shared/label.c \
- src/shared/label.h \
- src/shared/selinux-util.c \
- src/shared/selinux-util.h \
- src/shared/mkdir-label.c \
- src/shared/ask-password-api.c \
- src/shared/ask-password-api.h \
- src/shared/switch-root.h \
- src/shared/switch-root.c \
- src/shared/fileio-label.c \
- src/shared/fileio-label.h \
+ src/shared/install.c \
+ src/shared/install.h \
+ src/shared/install-printf.c \
+ src/shared/install-printf.h \
+ src/shared/path-lookup.c \
+ src/shared/path-lookup.h \
+ src/shared/specifier.c \
+ src/shared/specifier.h \
src/shared/dev-setup.c \
src/shared/dev-setup.h \
src/shared/dropin.c \
src/shared/dropin.h \
src/shared/condition.c \
src/shared/condition.h \
+ src/shared/clean-ipc.c \
+ src/shared/clean-ipc.h \
src/shared/generator.h \
src/shared/generator.c \
- src/shared/btrfs-util.c \
- src/shared/btrfs-util.h \
- src/shared/btrfs-ctree.h \
- src/shared/rm-rf.c \
- src/shared/rm-rf.h \
- src/shared/copy.c \
- src/shared/copy.h \
- src/shared/install.c \
- src/shared/install.h \
- src/shared/install-printf.c \
- src/shared/install-printf.h \
- src/shared/path-lookup.c \
- src/shared/path-lookup.h \
- src/shared/specifier.c \
- src/shared/specifier.h
+ src/shared/acpi-fpdt.h \
+ src/shared/acpi-fpdt.c \
+ src/shared/boot-timestamps.h \
+ src/shared/boot-timestamps.c \
+ src/shared/cgroup-show.c \
+ src/shared/cgroup-show.h \
+ src/shared/utmp-wtmp.h \
+ src/shared/watchdog.c \
+ src/shared/watchdog.h \
+ src/shared/spawn-ask-password-agent.c \
+ src/shared/spawn-ask-password-agent.h \
+ src/shared/ask-password-api.c \
+ src/shared/ask-password-api.h \
+ src/shared/switch-root.h \
+ src/shared/switch-root.c \
+ src/shared/import-util.c \
+ src/shared/import-util.h \
+ src/shared/sysctl-util.c \
+ src/shared/sysctl-util.h \
+ src/shared/bus-util.c \
+ src/shared/bus-util.h \
+ src/shared/logs-show.c \
+ src/shared/logs-show.h \
+ src/shared/machine-image.c \
+ src/shared/machine-image.h \
+ src/shared/machine-pool.c \
+ src/shared/machine-pool.h
if HAVE_UTMP
-libsystemd_shared_la_SOURCES += \
+libshared_la_SOURCES += \
src/shared/utmp-wtmp.c
endif
-nodist_libsystemd_shared_la_SOURCES = \
- src/shared/errno-from-name.h \
- src/shared/errno-to-name.h \
- src/shared/af-from-name.h \
- src/shared/af-to-name.h \
- src/shared/arphrd-from-name.h \
- src/shared/arphrd-to-name.h \
- src/shared/cap-from-name.h \
- src/shared/cap-to-name.h
-
-libsystemd_shared_la_CFLAGS = \
+if HAVE_SECCOMP
+libshared_la_SOURCES += \
+ src/shared/seccomp-util.h \
+ src/shared/seccomp-util.c
+endif
+
+if HAVE_ACL
+libshared_la_SOURCES += \
+ src/shared/acl-util.c \
+ src/shared/acl-util.h
+endif
+
+libshared_la_CFLAGS = \
$(AM_CFLAGS) \
$(SELINUX_CFLAGS) \
$(CAP_CFLAGS) \
+ $(ACL_CFLAGS) \
$(SECCOMP_CFLAGS) \
-pthread
-libsystemd_shared_la_LIBADD = \
+libshared_la_LIBADD = \
+ libsystemd-internal.la \
+ libsystemd-journal-internal.la \
$(SELINUX_LIBS) \
$(CAP_LIBS) \
+ $(ACL_LIBS) \
$(LIBIDN_LIBS) \
+ $(SECCOMP_LIBS) \
-lm
# -----------------------------------------------------------------------------
-noinst_LTLIBRARIES += \
- libsystemd-machine.la
-
-libsystemd_machine_la_SOURCES = \
- src/shared/machine-image.c \
- src/shared/machine-image.h \
- src/shared/machine-pool.c \
- src/shared/machine-pool.h
-
-# -----------------------------------------------------------------------------
if HAVE_LIBIPTC
noinst_LTLIBRARIES += \
- libsystemd-fw.la
+ libfirewall.la
-libsystemd_fw_la_SOURCES = \
+libfirewall_la_SOURCES = \
src/shared/fw-util.h \
src/shared/fw-util.c
-libsystemd_fw_la_CFLAGS = \
+libfirewall_la_CFLAGS = \
$(AM_CFLAGS) \
$(LIBIPTC_CFLAGS)
-libsystemd_fw_la_LIBADD = \
+libfirewall_la_LIBADD = \
$(LIBIPTC_LIBS)
endif
@@ -1020,53 +1055,10 @@ SYSINIT_TARGET_WANTS += \
endif
# ------------------------------------------------------------------------------
-
-if HAVE_SECCOMP
-noinst_LTLIBRARIES += \
- libsystemd-seccomp.la
-
-libsystemd_seccomp_la_SOURCES = \
- src/shared/seccomp-util.h \
- src/shared/seccomp-util.c
-
-libsystemd_seccomp_la_CFLAGS = \
- $(AM_CFLAGS) \
- $(SECCOMP_CFLAGS)
-
-libsystemd_seccomp_la_LIBADD = \
- $(SECCOMP_LIBS)
-endif
-
-# ------------------------------------------------------------------------------
-noinst_LTLIBRARIES += \
- libsystemd-logs.la
-
-libsystemd_logs_la_SOURCES = \
- src/shared/logs-show.c \
- src/shared/logs-show.h
-
-# ------------------------------------------------------------------------------
-if HAVE_ACL
-noinst_LTLIBRARIES += \
- libsystemd-acl.la
-
-libsystemd_acl_la_SOURCES = \
- src/shared/acl-util.c \
- src/shared/acl-util.h
-
-libsystemd_acl_la_CFLAGS = \
- $(AM_CFLAGS) \
- $(ACL_CFLAGS)
-
-libsystemd_acl_la_LIBADD = \
- $(ACL_LIBS)
-endif
-
-# ------------------------------------------------------------------------------
noinst_LTLIBRARIES += \
- libsystemd-core.la
+ libcore.la
-libsystemd_core_la_SOURCES = \
+libcore_la_SOURCES = \
src/core/unit.c \
src/core/unit.h \
src/core/unit-printf.c \
@@ -1188,36 +1180,28 @@ libsystemd_core_la_SOURCES = \
src/core/failure-action.c \
src/core/failure-action.h
-nodist_libsystemd_core_la_SOURCES = \
+nodist_libcore_la_SOURCES = \
src/core/load-fragment-gperf.c \
src/core/load-fragment-gperf-nulstr.c
-libsystemd_core_la_CFLAGS = \
+libcore_la_CFLAGS = \
$(AM_CFLAGS) \
$(PAM_CFLAGS) \
$(AUDIT_CFLAGS) \
$(KMOD_CFLAGS) \
$(APPARMOR_CFLAGS) \
- $(SECCOMP_CFLAGS) \
$(MOUNT_CFLAGS) \
-pthread
-libsystemd_core_la_LIBADD = \
+libcore_la_LIBADD = \
libudev-internal.la \
- libsystemd-shared.la \
- libsystemd-internal.la \
+ libshared.la \
$(PAM_LIBS) \
$(AUDIT_LIBS) \
$(KMOD_LIBS) \
$(APPARMOR_LIBS) \
- $(SECCOMP_LIBS) \
$(MOUNT_LIBS)
-if HAVE_SECCOMP
-libsystemd_core_la_LIBADD += \
- libsystemd-seccomp.la
-endif
-
src/core/load-fragment-gperf-nulstr.c: src/core/load-fragment-gperf.gperf
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ keywords=0 ; FS="," ; print "extern const char load_fragment_gperf_nulstr[];" ; print "const char load_fragment_gperf_nulstr[] ="} ; keyword==1 { print "\"" $$1 "\\0\"" } ; /%%/ { keyword=1} ; END { print ";" }' < $< > $@
@@ -1226,10 +1210,10 @@ gperf_gperf_m4_sources = \
src/core/load-fragment-gperf.gperf.m4
gperf_txt_sources = \
- src/shared/errno-list.txt \
- src/shared/af-list.txt \
- src/shared/arphrd-list.txt \
- src/shared/cap-list.txt
+ src/basic/errno-list.txt \
+ src/basic/af-list.txt \
+ src/basic/arphrd-list.txt \
+ src/basic/cap-list.txt
BUILT_SOURCES += \
$(gperf_gperf_m4_sources:-gperf.gperf.m4=-gperf.c) \
@@ -1259,50 +1243,50 @@ CLEANFILES += \
$(AM_V_GPERF)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_$(notdir $*) -H hash_$(notdir $*)_name -p -C <$< >$@
-src/shared/errno-list.txt:
+src/basic/errno-list.txt:
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include errno.h - </dev/null | $(AWK) '/^#define[ \t]+E[^ _]+[ \t]+/ { print $$2; }' >$@
-src/shared/errno-to-name.h: src/shared/errno-list.txt
+src/basic/errno-to-name.h: src/basic/errno-list.txt
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const errno_names[] = { "} !/EDEADLOCK/ && !/EWOULDBLOCK/ && !/ENOTSUP/ { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@
-src/shared/af-list.txt:
+src/basic/af-list.txt:
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include sys/socket.h - </dev/null | grep -v AF_UNSPEC | grep -v AF_MAX | $(AWK) '/^#define[ \t]+AF_[^ \t]+[ \t]+PF_[^ \t]/ { print $$2; }' >$@
-src/shared/af-to-name.h: src/shared/af-list.txt
+src/basic/af-to-name.h: src/basic/af-list.txt
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const af_names[] = { "} !/AF_FILE/ && !/AF_ROUTE/ && !/AF_LOCAL/ { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@
-src/shared/arphrd-list.txt:
+src/basic/arphrd-list.txt:
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include net/if_arp.h - </dev/null | $(AWK) '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $$2; }' | sed -e 's/ARPHRD_//' >$@
-src/shared/arphrd-to-name.h: src/shared/arphrd-list.txt
+src/basic/arphrd-to-name.h: src/basic/arphrd-list.txt
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const arphrd_names[] = { "} !/CISCO/ { printf "[ARPHRD_%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@
-src/shared/arphrd-from-name.gperf: src/shared/arphrd-list.txt
+src/basic/arphrd-from-name.gperf: src/basic/arphrd-list.txt
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ print "struct arphrd_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, ARPHRD_%s\n", $$1, $$1 }' <$< >$@
-src/shared/cap-list.txt:
+src/basic/cap-list.txt:
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include linux/capability.h -include missing.h - </dev/null | $(AWK) '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $$2; }' | grep -v CAP_LAST_CAP >$@
-src/shared/cap-to-name.h: src/shared/cap-list.txt
+src/basic/cap-to-name.h: src/basic/cap-list.txt
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const capability_names[] = { "} { printf "[%s] = \"%s\",\n", $$1, tolower($$1) } END{print "};"}' <$< >$@
-src/shared/cap-from-name.gperf: src/shared/cap-list.txt
+src/basic/cap-from-name.gperf: src/basic/cap-list.txt
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(AWK) 'BEGIN{ print "struct capability_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, %s\n", $$1, $$1 }' <$< >$@
-src/shared/cap-from-name.h: src/shared/cap-from-name.gperf
+src/basic/cap-from-name.h: src/basic/cap-from-name.gperf
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GPERF)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_capability -H hash_capability_name -p -C <$< >$@
@@ -1336,12 +1320,8 @@ src/resolve/dns_type-from-name.gperf: src/resolve/dns_type-list.txt
systemd_SOURCES = \
src/core/main.c
-systemd_CFLAGS = \
- $(AM_CFLAGS) \
- $(SECCOMP_CFLAGS)
-
systemd_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
dist_pkgsysconf_DATA += \
@@ -1535,7 +1515,7 @@ test_device_nodes_SOURCES = \
src/test/test-device-nodes.c
test_device_nodes_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_engine_SOURCES = \
src/test/test-engine.c
@@ -1545,7 +1525,7 @@ test_engine_CFLAGS = \
$(SECCOMP_CFLAGS)
test_engine_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
test_job_type_SOURCES = \
@@ -1556,7 +1536,7 @@ test_job_type_CFLAGS = \
$(SECCOMP_CFLAGS)
test_job_type_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
test_ns_SOURCES = \
@@ -1567,27 +1547,26 @@ test_ns_CFLAGS = \
$(SECCOMP_CFLAGS)
test_ns_LDADD = \
- libsystemd-core.la
+ libcore.la
test_loopback_SOURCES = \
src/test/test-loopback.c
test_loopback_LDADD = \
- libsystemd-core.la
+ libcore.la
test_hostname_SOURCES = \
src/test/test-hostname.c
test_hostname_LDADD = \
- libsystemd-core.la
+ libcore.la
test_dns_domain_SOURCES = \
src/test/test-dns-domain.c
test_dns_domain_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(LIBIDN_LIBS)
if ENABLE_EFI
@@ -1598,7 +1577,7 @@ test_boot_timestamp_SOURCES = \
src/test/test-boot-timestamps.c
test_boot_timestamp_LDADD = \
- libsystemd-shared.la
+ libshared.la
endif
test_unit_name_SOURCES = \
@@ -1609,7 +1588,7 @@ test_unit_name_CFLAGS = \
$(SECCOMP_CFLAGS)
test_unit_name_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
test_unit_file_SOURCES = \
@@ -1620,137 +1599,135 @@ test_unit_file_CFLAGS = \
$(SECCOMP_CFLAGS)
test_unit_file_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
test_utf8_SOURCES = \
src/test/test-utf8.c
test_utf8_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_capability_SOURCES = \
src/test/test-capability.c
test_capability_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_async_SOURCES = \
src/test/test-async.c
test_async_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_locale_util_SOURCES = \
src/test/test-locale-util.c
test_locale_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_copy_SOURCES = \
src/test/test-copy.c
test_copy_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_sigbus_SOURCES = \
src/test/test-sigbus.c
test_sigbus_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_condition_SOURCES = \
src/test/test-condition.c
test_condition_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
test_fdset_SOURCES = \
src/test/test-fdset.c
test_fdset_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
test_fstab_util_SOURCES = \
src/test/test-fstab-util.c
test_fstab_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_ratelimit_SOURCES = \
src/test/test-ratelimit.c
test_ratelimit_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_util_SOURCES = \
src/test/test-util.c
test_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_process_util_SOURCES = \
src/test/test-process-util.c
test_process_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_terminal_util_SOURCES = \
src/test/test-terminal-util.c
test_terminal_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_path_lookup_SOURCES = \
src/test/test-path-lookup.c
test_path_lookup_LDADD = \
-lm \
- libsystemd-shared.la
+ libshared.la
test_uid_range_SOURCES = \
src/test/test-uid-range.c
test_uid_range_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_cap_list_SOURCES = \
src/test/test-cap-list.c
test_cap_list_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_socket_util_SOURCES = \
src/test/test-socket-util.c
test_socket_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_ring_SOURCES = \
src/test/test-ring.c
test_ring_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_barrier_SOURCES = \
src/test/test-barrier.c
test_barrier_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_pty_SOURCES = \
src/test/test-pty.c
test_pty_LDADD = \
- libsystemd-core.la
+ libcore.la
test_tmpfiles_SOURCES = \
src/test/test-tmpfiles.c
test_tmpfiles_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_namespace_SOURCES = \
src/test/test-namespace.c
@@ -1759,10 +1736,10 @@ test_verbs_SOURCES = \
src/test/test-verbs.c
test_verbs_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_namespace_LDADD = \
- libsystemd-core.la
+ libcore.la
BUILT_SOURCES += \
src/test/test-hashmap-ordered.c
@@ -1784,34 +1761,34 @@ test_hashmap_SOURCES = \
src/test/test-hashmap-plain.c
test_hashmap_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_set_SOURCES = \
src/test/test-set.c
test_set_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_xml_SOURCES = \
src/test/test-xml.c
test_xml_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_json_SOURCES = \
src/test/test-json.c
test_json_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_list_SOURCES = \
src/test/test-list.c
test_list_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_unaligned_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_unaligned_SOURCES = \
src/test/test-unaligned.c
@@ -1833,10 +1810,9 @@ test_tables_CFLAGS = \
$(SECCOMP_CFLAGS)
test_tables_LDADD = \
- libsystemd-logs.la \
libsystemd-journal-internal.la \
- libsystemd-journal-core.la \
- libsystemd-core.la \
+ libjournal-core.la \
+ libcore.la \
libudev-core.la \
$(RT_LIBS)
@@ -1844,44 +1820,44 @@ test_prioq_SOURCES = \
src/test/test-prioq.c
test_prioq_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_fileio_SOURCES = \
src/test/test-fileio.c
test_fileio_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_time_SOURCES = \
src/test/test-time.c
test_time_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_architecture_SOURCES = \
src/test/test-architecture.c
test_architecture_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_log_SOURCES = \
src/test/test-log.c
test_log_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_ipcrm_SOURCES = \
src/test/test-ipcrm.c
test_ipcrm_LDADD = \
- libsystemd-shared.la \
+ libshared.la \
-lrt
test_btrfs_SOURCES = \
src/test/test-btrfs.c
test_btrfs_LDADD = \
- libsystemd-shared.la
+ libshared.la
if HAVE_LIBIPTC
test_fw_util_SOURCES = \
@@ -1892,8 +1868,8 @@ test_fw_util_CFLAGS = \
$(LIBIPTC_CFLAGS)
test_fw_util_LDADD = \
- libsystemd-fw.la \
- libsystemd-shared.la \
+ libfirewall.la \
+ libshared.la \
$(LIBIPTC_LIBS)
endif
@@ -1905,59 +1881,56 @@ test_rtnl_manual_CFLAGS = \
$(KMOD_CFLAGS)
test_rtnl_manual_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(KMOD_LIBS)
test_ellipsize_SOURCES = \
src/test/test-ellipsize.c
test_ellipsize_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_date_SOURCES = \
src/test/test-date.c
test_date_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_sleep_SOURCES = \
src/test/test-sleep.c
test_sleep_LDADD = \
- libsystemd-core.la
+ libcore.la
test_replace_var_SOURCES = \
src/test/test-replace-var.c
test_replace_var_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_calendarspec_SOURCES = \
src/test/test-calendarspec.c
test_calendarspec_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_strip_tab_ansi_SOURCES = \
src/test/test-strip-tab-ansi.c
test_strip_tab_ansi_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_daemon_SOURCES = \
src/test/test-daemon.c
test_daemon_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_cgroup_SOURCES = \
src/test/test-cgroup.c
test_cgroup_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
test_cgroup_mask_SOURCES = \
src/test/test-cgroup-mask.c
@@ -1970,40 +1943,38 @@ test_cgroup_mask_CFLAGS = \
$(SECCOMP_CFLAGS)
test_cgroup_mask_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
test_cgroup_util_SOURCES = \
src/test/test-cgroup-util.c
test_cgroup_util_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
test_env_replace_SOURCES = \
src/test/test-env-replace.c
test_env_replace_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_strbuf_SOURCES = \
src/test/test-strbuf.c
test_strbuf_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_strv_SOURCES = \
src/test/test-strv.c
test_strv_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
test_path_util_SOURCES = \
src/test/test-path-util.c
test_path_util_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_path_SOURCES = \
src/test/test-path.c
@@ -2012,7 +1983,7 @@ test_path_CFLAGS = \
$(AM_CFLAGS)
test_path_LDADD = \
- libsystemd-core.la
+ libcore.la
test_execute_SOURCES = \
src/test/test-execute.c
@@ -2021,26 +1992,25 @@ test_execute_CFLAGS = \
$(AM_CFLAGS)
test_execute_LDADD = \
- libsystemd-core.la
+ libcore.la
test_strxcpyx_SOURCES = \
src/test/test-strxcpyx.c
test_strxcpyx_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_install_SOURCES = \
src/test/test-install.c
test_install_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
test_watchdog_SOURCES = \
src/test/test-watchdog.c
test_watchdog_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_sched_prio_SOURCES = \
src/test/test-sched-prio.c
@@ -2053,28 +2023,27 @@ test_sched_prio_CFLAGS = \
$(SECCOMP_CFLAGS)
test_sched_prio_LDADD = \
- libsystemd-core.la \
+ libcore.la \
$(RT_LIBS)
test_conf_files_SOURCES = \
src/test/test-conf-files.c
test_conf_files_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_conf_parser_SOURCES = \
src/test/test-conf-parser.c
test_conf_parser_LDADD = \
- libsystemd-shared.la
+ libshared.la
test_bus_policy_SOURCES = \
src/bus-proxyd/test-bus-xml-policy.c
test_bus_policy_LDADD = \
- libsystemd-proxy.la \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libbus-proxy-core.la \
+ libshared.la
# ------------------------------------------------------------------------------
## .PHONY so it always rebuilds it
@@ -2126,9 +2095,7 @@ systemd_analyze_CFLAGS = \
$(SECCOMP_CFLAGS)
systemd_analyze_LDADD = \
- libsystemd-core.la \
- libsystemd-shared.la \
- libsystemd-internal.la \
+ libcore.la \
$(RT_LIBS)
# ------------------------------------------------------------------------------
@@ -2136,8 +2103,7 @@ systemd_initctl_SOURCES = \
src/initctl/initctl.c
systemd_initctl_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_update_utmp_SOURCES = \
@@ -2148,8 +2114,7 @@ systemd_update_utmp_CFLAGS = \
$(AUDIT_CFLAGS)
systemd_update_utmp_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(AUDIT_LIBS)
# ------------------------------------------------------------------------------
@@ -2157,8 +2122,7 @@ systemd_update_done_SOURCES = \
src/update-done/update-done.c
systemd_update_done_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_shutdown_SOURCES = \
@@ -2172,8 +2136,7 @@ systemd_shutdown_SOURCES = \
systemd_shutdown_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
if HAVE_KMOD
@@ -2185,7 +2148,7 @@ systemd_modules_load_CFLAGS = \
$(KMOD_CFLAGS)
systemd_modules_load_LDADD = \
- libsystemd-shared.la \
+ libshared.la \
$(KMOD_LIBS)
rootlibexec_PROGRAMS += \
@@ -2216,13 +2179,7 @@ systemd_tmpfiles_SOURCES = \
src/tmpfiles/tmpfiles.c
systemd_tmpfiles_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
-
-if HAVE_ACL
-systemd_tmpfiles_LDADD += \
- libsystemd-acl.la
-endif
+ libshared.la
rootbin_PROGRAMS += \
systemd-tmpfiles
@@ -2279,8 +2236,7 @@ systemd_sysusers_SOURCES = \
src/sysusers/sysusers.c
systemd_sysusers_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
rootbin_PROGRAMS += \
systemd-sysusers
@@ -2320,8 +2276,7 @@ systemd_firstboot_SOURCES = \
src/firstboot/firstboot.c
systemd_firstboot_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la \
+ libshared.la \
-lcrypt
rootbin_PROGRAMS += \
@@ -2344,22 +2299,21 @@ systemd_machine_id_setup_SOURCES = \
src/core/machine-id-setup.h
systemd_machine_id_setup_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_sysctl_SOURCES = \
src/sysctl/sysctl.c
systemd_sysctl_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_sleep_SOURCES = \
src/sleep/sleep.c
systemd_sleep_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_fsck_SOURCES = \
@@ -2367,8 +2321,7 @@ systemd_fsck_SOURCES = \
systemd_fsck_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_machine_id_commit_SOURCES = \
@@ -2377,8 +2330,7 @@ systemd_machine_id_commit_SOURCES = \
src/core/machine-id-setup.h
systemd_machine_id_commit_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
SYSINIT_TARGET_WANTS += \
systemd-machine-id-commit.service
@@ -2389,15 +2341,14 @@ systemd_ac_power_SOURCES = \
systemd_ac_power_LDADD = \
libudev-internal.la \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_detect_virt_SOURCES = \
src/detect-virt/detect-virt.c
systemd_detect_virt_LDADD = \
- libsystemd-shared.la
+ libshared.la
INSTALL_EXEC_HOOKS += \
systemd-detect-virt-install-hook
@@ -2407,21 +2358,21 @@ systemd_delta_SOURCES = \
src/delta/delta.c
systemd_delta_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_getty_generator_SOURCES = \
src/getty-generator/getty-generator.c
systemd_getty_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_debug_generator_SOURCES = \
src/debug-generator/debug-generator.c
systemd_debug_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_fstab_generator_SOURCES = \
@@ -2429,14 +2380,14 @@ systemd_fstab_generator_SOURCES = \
src/core/mount-setup.c
systemd_fstab_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_system_update_generator_SOURCES = \
src/system-update-generator/system-update-generator.c
systemd_system_update_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
if ENABLE_HIBERNATE
@@ -2450,14 +2401,13 @@ systemd_hibernate_resume_SOURCES = \
src/hibernate-resume/hibernate-resume.c
systemd_hibernate_resume_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
systemd_hibernate_resume_generator_SOURCES = \
src/hibernate-resume/hibernate-resume-generator.c
systemd_hibernate_resume_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
dist_systemunit_DATA += \
units/hibernate.target \
@@ -2483,7 +2433,7 @@ systemd_efi_boot_generator_SOURCES = \
src/efi-boot-generator/efi-boot-generator.c
systemd_efi_boot_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
if HAVE_BLKID
@@ -2500,8 +2450,7 @@ bootctl_CFLAGS = \
$(BLKID_CFLAGS)
bootctl_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la \
+ libshared.la \
$(BLKID_LIBS)
bin_PROGRAMS += \
@@ -2675,12 +2624,11 @@ systemgenerator_PROGRAMS += \
systemd_gpt_auto_generator_SOURCES = \
src/gpt-auto-generator/gpt-auto-generator.c \
- src/shared/blkid-util.h
+ src/basic/blkid-util.h
systemd_gpt_auto_generator_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(BLKID_LIBS)
systemd_gpt_auto_generator_CFLAGS = \
@@ -2697,8 +2645,7 @@ systemd_dbus1_generator_SOURCES = \
src/dbus1-generator/dbus1-generator.c
systemd_dbus1_generator_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
dbus1-generator-install-hook:
$(AM_V_at)$(MKDIR_P) $(DESTDIR)$(usergeneratordir)
@@ -2720,15 +2667,14 @@ systemd_sysv_generator_SOURCES = \
src/sysv-generator/sysv-generator.c
systemd_sysv_generator_LDADD = \
- libsystemd-core.la \
- libsystemd-shared.la
+ libcore.la
# ------------------------------------------------------------------------------
systemd_rc_local_generator_SOURCES = \
src/rc-local-generator/rc-local-generator.c
systemd_rc_local_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_remount_fs_SOURCES = \
@@ -2737,77 +2683,71 @@ systemd_remount_fs_SOURCES = \
src/core/mount-setup.h
systemd_remount_fs_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_cgroups_agent_SOURCES = \
src/cgroups-agent/cgroups-agent.c
systemd_cgroups_agent_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_escape_SOURCES = \
src/escape/escape.c
systemd_escape_LDADD = \
- libsystemd-shared.la
+ libshared.la
# -----------------------------------------------------------------------------
systemctl_SOURCES = \
src/systemctl/systemctl.c
systemctl_LDADD = \
- libsystemd-logs.la \
libsystemd-journal-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_notify_SOURCES = \
src/notify/notify.c
systemd_notify_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_path_SOURCES = \
src/path/path.c
systemd_path_LDADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_ask_password_SOURCES = \
src/ask-password/ask-password.c
systemd_ask_password_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_reply_password_SOURCES = \
src/reply-password/reply-password.c
systemd_reply_password_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_cgls_SOURCES = \
src/cgls/cgls.c
systemd_cgls_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_cgtop_SOURCES = \
src/cgtop/cgtop.c
systemd_cgtop_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_nspawn_SOURCES = \
@@ -2819,25 +2759,16 @@ systemd_nspawn_SOURCES = \
systemd_nspawn_CFLAGS = \
$(AM_CFLAGS) \
- $(SECCOMP_CFLAGS) \
$(BLKID_CFLAGS)
systemd_nspawn_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-machine.la \
- libsystemd-shared.la \
+ libshared.la \
$(BLKID_LIBS)
-if HAVE_SECCOMP
-systemd_nspawn_LDADD += \
- libsystemd-seccomp.la \
- $(SECCOMP_LIBS)
-endif
-
if HAVE_LIBIPTC
systemd_nspawn_LDADD += \
- libsystemd-fw.la
+ libfirewall.la
endif
# ------------------------------------------------------------------------------
@@ -2845,14 +2776,13 @@ systemd_run_SOURCES = \
src/run/run.c
systemd_run_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
noinst_LTLIBRARIES += \
- libsystemd-proxy.la
+ libbus-proxy-core.la
-libsystemd_proxy_la_SOURCES = \
+libbus_proxy_core_la_SOURCES = \
src/bus-proxyd/bus-xml-policy.c \
src/bus-proxyd/bus-xml-policy.h \
src/bus-proxyd/driver.c \
@@ -2862,13 +2792,12 @@ libsystemd_proxy_la_SOURCES = \
src/bus-proxyd/synthesize.c \
src/bus-proxyd/synthesize.h
-libsystemd_proxy_la_CFLAGS = \
+libbus_proxy_core_la_CFLAGS = \
$(AM_CFLAGS) \
-pthread
-libsystemd_proxy_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+libbus_proxy_core_la_LIBADD = \
+ libshared.la
systemd_bus_proxyd_SOURCES = \
src/bus-proxyd/bus-proxyd.c
@@ -2878,17 +2807,15 @@ systemd_bus_proxyd_CFLAGS = \
-pthread
systemd_bus_proxyd_LDADD = \
- libsystemd-proxy.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libbus-proxy-core.la \
+ libshared.la
systemd_stdio_bridge_SOURCES = \
src/bus-proxyd/stdio-bridge.c
systemd_stdio_bridge_LDADD = \
- libsystemd-proxy.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libbus-proxy-core.la \
+ libshared.la
if ENABLE_KDBUS
nodist_systemunit_DATA += \
@@ -2920,7 +2847,7 @@ systemd_tty_ask_password_agent_SOURCES = \
src/tty-ask-password-agent/tty-ask-password-agent.c
systemd_tty_ask_password_agent_LDADD = \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
libsystemd_internal_la_SOURCES = \
@@ -2974,12 +2901,12 @@ libsystemd_internal_la_SOURCES = \
src/libsystemd/sd-bus/bus-convenience.c \
src/libsystemd/sd-bus/bus-track.c \
src/libsystemd/sd-bus/bus-track.h \
- src/libsystemd/sd-bus/bus-util.c \
- src/libsystemd/sd-bus/bus-util.h \
src/libsystemd/sd-bus/bus-slot.c \
src/libsystemd/sd-bus/bus-slot.h \
src/libsystemd/sd-bus/bus-protocol.h \
src/libsystemd/sd-bus/kdbus.h \
+ src/libsystemd/sd-bus/bus-dump.c \
+ src/libsystemd/sd-bus/bus-dump.h \
src/libsystemd/sd-utf8/sd-utf8.c \
src/libsystemd/sd-event/sd-event.c \
src/libsystemd/sd-event/event-util.h \
@@ -3018,6 +2945,7 @@ libsystemd_internal_la_CFLAGS = \
-pthread
libsystemd_internal_la_LIBADD = \
+ libbasic.la \
$(RT_LIBS)
libsystemd_resolve_la_SOURCES = \
@@ -3035,13 +2963,6 @@ noinst_LTLIBRARIES += \
libsystemd-internal.la \
libsystemd-resolve.la
-libsystemd_dump_la_SOURCES = \
- src/libsystemd/sd-bus/bus-dump.c \
- src/libsystemd/sd-bus/bus-dump.h
-
-noinst_LTLIBRARIES += \
- libsystemd-dump.la
-
EXTRA_DIST += \
src/libsystemd/libsystemd.sym.m4 \
src/libsystemd/libsystemd.pc.in \
@@ -3070,7 +2991,6 @@ libsystemd_la_LDFLAGS = \
-Wl,--version-script=$(top_builddir)/src/libsystemd/libsystemd.sym
libsystemd_la_LIBADD = \
- libsystemd-shared.la \
$(libsystemd_internal_la_LIBADD) \
$(libsystemd_journal_internal_la_LIBADD) \
$(libsystemd_resolve_la_LIBADD)
@@ -3135,9 +3055,7 @@ test_bus_marshal_SOURCES = \
src/libsystemd/sd-bus/test-bus-marshal.c
test_bus_marshal_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(GLIB_LIBS) \
$(DBUS_LIBS)
@@ -3150,8 +3068,7 @@ test_bus_signature_SOURCES = \
src/libsystemd/sd-bus/test-bus-signature.c
test_bus_signature_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_chat_SOURCES = \
src/libsystemd/sd-bus/test-bus-chat.c
@@ -3161,8 +3078,7 @@ test_bus_chat_CFLAGS = \
-pthread
test_bus_chat_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_cleanup_SOURCES = \
src/libsystemd/sd-bus/test-bus-cleanup.c
@@ -3172,8 +3088,7 @@ test_bus_cleanup_CFLAGS = \
$(SECCOMP_CFLAGS)
test_bus_cleanup_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_server_SOURCES = \
src/libsystemd/sd-bus/test-bus-server.c
@@ -3183,8 +3098,7 @@ test_bus_server_CFLAGS = \
-pthread
test_bus_server_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_objects_SOURCES = \
src/libsystemd/sd-bus/test-bus-objects.c
@@ -3194,24 +3108,19 @@ test_bus_objects_CFLAGS = \
-pthread
test_bus_objects_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_error_SOURCES = \
src/libsystemd/sd-bus/test-bus-error.c
test_bus_error_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_gvariant_SOURCES = \
src/libsystemd/sd-bus/test-bus-gvariant.c
test_bus_gvariant_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(GLIB_LIBS)
test_bus_gvariant_CFLAGS = \
@@ -3222,82 +3131,68 @@ test_bus_creds_SOURCES = \
src/libsystemd/sd-bus/test-bus-creds.c
test_bus_creds_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_match_SOURCES = \
src/libsystemd/sd-bus/test-bus-match.c
test_bus_match_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_kernel_SOURCES = \
src/libsystemd/sd-bus/test-bus-kernel.c
test_bus_kernel_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_kernel_bloom_SOURCES = \
src/libsystemd/sd-bus/test-bus-kernel-bloom.c
test_bus_kernel_bloom_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_benchmark_SOURCES = \
src/libsystemd/sd-bus/test-bus-benchmark.c
test_bus_benchmark_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_zero_copy_SOURCES = \
src/libsystemd/sd-bus/test-bus-zero-copy.c
test_bus_zero_copy_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_bus_introspect_SOURCES = \
src/libsystemd/sd-bus/test-bus-introspect.c
test_bus_introspect_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_event_SOURCES = \
src/libsystemd/sd-event/test-event.c
test_event_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_rtnl_SOURCES = \
src/libsystemd/sd-rtnl/test-rtnl.c
test_rtnl_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_local_addresses_SOURCES = \
src/libsystemd/sd-rtnl/test-local-addresses.c
test_local_addresses_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_resolve_SOURCES = \
src/libsystemd/sd-resolve/test-resolve.c
test_resolve_LDADD = \
libsystemd-resolve.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_resolve_CFLAGS = \
$(AM_CFLAGS) \
@@ -3309,9 +3204,7 @@ busctl_SOURCES = \
src/libsystemd/sd-bus/busctl-introspect.h
busctl_LDADD = \
- libsystemd-dump.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
noinst_LTLIBRARIES += \
@@ -3371,9 +3264,6 @@ libsystemd_network_la_SOURCES = \
src/libsystemd-network/sd-lldp.c
libsystemd_network_la_LIBADD = \
- libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
$(KMOD_LIBS)
test_dhcp_option_SOURCES = \
@@ -3383,8 +3273,7 @@ test_dhcp_option_SOURCES = \
test_dhcp_option_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_dhcp_client_SOURCES = \
src/systemd/sd-dhcp-client.h \
@@ -3394,8 +3283,8 @@ test_dhcp_client_SOURCES = \
test_dhcp_client_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libudev-internal.la \
+ libshared.la \
$(LIBIDN_LIBS)
test_dhcp_server_SOURCES = \
@@ -3403,8 +3292,7 @@ test_dhcp_server_SOURCES = \
test_dhcp_server_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_ipv4ll_SOURCES = \
src/systemd/sd-ipv4ll.h \
@@ -3413,8 +3301,7 @@ test_ipv4ll_SOURCES = \
test_ipv4ll_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_pppoe_SOURCES = \
src/systemd/sd-pppoe.h \
@@ -3422,7 +3309,7 @@ test_pppoe_SOURCES = \
test_pppoe_LDADD = \
libsystemd-network.la \
- libsystemd-shared.la
+ libshared.la
test_icmp6_rs_SOURCES = \
src/systemd/sd-dhcp6-client.h \
@@ -3434,8 +3321,8 @@ test_icmp6_rs_SOURCES = \
test_icmp6_rs_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libudev.la \
+ libshared.la
test_dhcp6_client_SOURCES = \
src/systemd/sd-dhcp6-client.h \
@@ -3446,19 +3333,18 @@ test_dhcp6_client_SOURCES = \
test_dhcp6_client_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libudev.la \
+ libshared.la
test_lldp_SOURCES = \
- src/libsystemd-network/lldp.h \
- src/libsystemd-network/lldp-tlv.h \
- src/libsystemd-network/lldp-tlv.c \
- src/libsystemd-network/test-lldp.c
+ src/libsystemd-network/lldp.h \
+ src/libsystemd-network/lldp-tlv.h \
+ src/libsystemd-network/lldp-tlv.c \
+ src/libsystemd-network/test-lldp.c
test_lldp_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
tests += \
test-dhcp-option \
@@ -3533,8 +3419,7 @@ libsystemd_terminal_la_SOURCES = \
libsystemd_terminal_la_LIBADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(TERMINAL_LIBS)
systemd_consoled_CFLAGS = \
@@ -3552,8 +3437,7 @@ systemd_consoled_SOURCES = \
systemd_consoled_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(TERMINAL_LIBS)
systemd_evcat_CFLAGS = \
@@ -3565,8 +3449,7 @@ systemd_evcat_SOURCES = \
systemd_evcat_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(TERMINAL_LIBS)
systemd_modeset_CFLAGS = \
@@ -3578,8 +3461,7 @@ systemd_modeset_SOURCES = \
systemd_modeset_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(TERMINAL_LIBS)
systemd_subterm_SOURCES = \
@@ -3587,32 +3469,28 @@ systemd_subterm_SOURCES = \
systemd_subterm_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_term_page_SOURCES = \
src/libsystemd-terminal/test-term-page.c
test_term_page_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_term_parser_SOURCES = \
src/libsystemd-terminal/test-term-parser.c
test_term_parser_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_unifont_SOURCES = \
src/libsystemd-terminal/test-unifont.c
test_unifont_LDADD = \
libsystemd-terminal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
src/libsystemd-terminal/unifont-glyph-array.bin: tools/compile-unifont.py $(UNIFONT)
$(AM_V_GEN)$(PYTHON) $< <$(UNIFONT) >$@
@@ -3651,8 +3529,7 @@ libudev_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/libudev/libudev.sym
libudev_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libsystemd-internal.la
pkgconfiglib_DATA += \
src/libudev/libudev.pc
@@ -3796,9 +3673,8 @@ libudev_core_la_CFLAGS = \
libudev_core_la_LIBADD = \
libudev-internal.la \
- libsystemd-internal.la \
libsystemd-network.la \
- libsystemd-shared.la \
+ libshared.la \
$(BLKID_LIBS) \
$(KMOD_LIBS)
@@ -3821,9 +3697,6 @@ libudev_core_la_SOURCES += \
src/login/logind-acl.c \
src/libsystemd/sd-login/sd-login.c \
src/systemd/sd-login.h
-
-libudev_core_la_LIBADD += \
- libsystemd-acl.la
endif
systemd_udevd_SOURCES = \
@@ -3859,8 +3732,7 @@ systemd_hwdb_SOURCES = \
systemd_hwdb_LDADD = \
libudev-internal.la \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
rootbin_PROGRAMS += \
systemd-hwdb
@@ -3928,8 +3800,8 @@ test_libudev_SOURCES = \
test_libudev_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la \
+ $(ACL_LIBS)
test_udev_SOURCES = \
src/test/test-udev.c
@@ -3938,12 +3810,8 @@ test_udev_LDADD = \
libudev-core.la \
$(BLKID_LIBS) \
$(KMOD_LIBS) \
- $(SELINUX_LIBS)
-
-if HAVE_ACL
-test_udev_LDADD += \
- libsystemd-acl.la
-endif
+ $(SELINUX_LIBS) \
+ $(ACL_LIBS)
check_DATA += \
test/sys
@@ -3972,8 +3840,7 @@ ata_id_SOURCES = \
ata_id_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
udevlibexec_PROGRAMS += \
ata_id
@@ -3984,8 +3851,7 @@ cdrom_id_SOURCES = \
cdrom_id_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
udevlibexec_PROGRAMS += \
cdrom_id
@@ -3999,8 +3865,7 @@ collect_SOURCES = \
collect_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
udevlibexec_PROGRAMS += \
collect
@@ -4014,8 +3879,7 @@ scsi_id_SOURCES =\
scsi_id_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
udevlibexec_PROGRAMS += \
scsi_id
@@ -4029,8 +3893,7 @@ v4l_id_SOURCES = \
v4l_id_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
udevlibexec_PROGRAMS += \
v4l_id
@@ -4044,8 +3907,7 @@ accelerometer_SOURCES = \
accelerometer_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
udevlibexec_PROGRAMS += \
accelerometer
@@ -4070,8 +3932,7 @@ test_id128_SOURCES = \
src/test/test-id128.c
test_id128_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
tests += \
test-id128
@@ -4085,8 +3946,7 @@ systemd_activate_SOURCES = \
src/activate/activate.c
systemd_activate_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
# ------------------------------------------------------------------------------
systemd_journald_SOURCES = \
@@ -4094,15 +3954,14 @@ systemd_journald_SOURCES = \
src/journal/journald-server.h
systemd_journald_LDADD = \
- libsystemd-journal-core.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libjournal-core.la \
+ libshared.la
systemd_cat_SOURCES = \
src/journal/cat.c
systemd_cat_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
if HAVE_MICROHTTPD
rootlibexec_PROGRAMS += \
@@ -4117,8 +3976,7 @@ systemd_journal_remote_SOURCES = \
src/journal-remote/journal-remote.c
systemd_journal_remote_LDADD = \
- libsystemd-internal.la \
- libsystemd-journal-core.la
+ libjournal-core.la
systemd_journal_remote_SOURCES += \
src/journal-remote/microhttpd-util.h \
@@ -4184,9 +4042,8 @@ systemd_journal_upload_CFLAGS = \
$(LIBCURL_CFLAGS)
systemd_journal_upload_LDADD = \
- libsystemd-internal.la \
libsystemd-journal-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(LIBCURL_LIBS)
nodist_systemunit_DATA += \
@@ -4210,14 +4067,7 @@ journalctl_SOURCES = \
journalctl_LDADD = \
libsystemd-journal-internal.la \
- libsystemd-internal.la \
- libsystemd-logs.la \
- libsystemd-shared.la
-
-if HAVE_ACL
-journalctl_LDADD += \
- libsystemd-acl.la
-endif
+ libshared.la
if HAVE_QRENCODE
journalctl_SOURCES += \
@@ -4235,67 +4085,67 @@ test_journal_SOURCES = \
src/journal/test-journal.c
test_journal_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_send_SOURCES = \
src/journal/test-journal-send.c
test_journal_send_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_syslog_SOURCES = \
src/journal/test-journal-syslog.c
test_journal_syslog_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_match_SOURCES = \
src/journal/test-journal-match.c
test_journal_match_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_enum_SOURCES = \
src/journal/test-journal-enum.c
test_journal_enum_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_stream_SOURCES = \
src/journal/test-journal-stream.c
test_journal_stream_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_flush_SOURCES = \
src/journal/test-journal-flush.c
test_journal_flush_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_init_SOURCES = \
src/journal/test-journal-init.c
test_journal_init_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_verify_SOURCES = \
src/journal/test-journal-verify.c
test_journal_verify_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_journal_interleaving_SOURCES = \
src/journal/test-journal-interleaving.c
test_journal_interleaving_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_mmap_cache_SOURCES = \
src/journal/test-mmap-cache.c
test_mmap_cache_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_catalog_SOURCES = \
src/journal/test-catalog.c
@@ -4305,29 +4155,29 @@ test_catalog_CPPFLAGS = \
-DCATALOG_DIR=\"$(abs_top_srcdir)/catalog\"
test_catalog_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
test_compress_SOURCES = \
src/journal/test-compress.c
test_compress_LDADD = \
libsystemd-journal-internal.la \
- libsystemd-shared.la
+ libshared.la
test_compress_benchmark_SOURCES = \
src/journal/test-compress-benchmark.c
test_compress_benchmark_LDADD = \
libsystemd-journal-internal.la \
- libsystemd-shared.la
+ libshared.la
test_audit_type_SOURCES = \
src/journal/test-audit-type.c
test_audit_type_LDADD = \
- libsystemd-journal-core.la
+ libjournal-core.la
-libsystemd_journal_core_la_SOURCES = \
+libjournal_core_la_SOURCES = \
src/journal/journald-kmsg.c \
src/journal/journald-kmsg.h \
src/journal/journald-syslog.c \
@@ -4348,22 +4198,16 @@ libsystemd_journal_core_la_SOURCES = \
src/journal/journald-rate-limit.h \
src/journal/journal-internal.h
-nodist_libsystemd_journal_core_la_SOURCES = \
+nodist_libjournal_core_la_SOURCES = \
src/journal/journald-gperf.c
-libsystemd_journal_core_la_LIBADD = \
+libjournal_core_la_LIBADD = \
libsystemd-journal-internal.la \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
-
-if HAVE_ACL
-libsystemd_journal_core_la_LIBADD += \
- libsystemd-acl.la
-endif
+ libshared.la
noinst_LTLIBRARIES += \
- libsystemd-journal-core.la
+ libjournal-core.la
journal-install-hook:
-$(MKDIR_P) $(DESTDIR)/var/log/journal
@@ -4550,10 +4394,8 @@ systemd_journal_gatewayd_SOURCES = \
src/journal-remote/microhttpd-util.c
systemd_journal_gatewayd_LDADD = \
- libsystemd-logs.la \
libsystemd-journal-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(MICROHTTPD_LIBS)
if HAVE_GNUTLS
@@ -4589,10 +4431,8 @@ systemd_socket_proxyd_SOURCES = \
src/socket-proxy/socket-proxyd.c
systemd_socket_proxyd_LDADD = \
- libsystemd-logs.la \
- libsystemd-internal.la \
libsystemd-journal-internal.la \
- libsystemd-shared.la \
+ libshared.la \
libsystemd-resolve.la
# ------------------------------------------------------------------------------
@@ -4604,8 +4444,7 @@ systemd_coredump_SOURCES = \
systemd_coredump_LDADD = \
libsystemd-journal-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
if HAVE_ELFUTILS
systemd_coredump_SOURCES += \
@@ -4622,18 +4461,12 @@ rootlibexec_PROGRAMS += \
dist_pkgsysconf_DATA += \
src/journal/coredump.conf
-if HAVE_ACL
-systemd_coredump_LDADD += \
- libsystemd-acl.la
-endif
-
coredumpctl_SOURCES = \
src/journal/coredumpctl.c
coredumpctl_LDADD = \
libsystemd-journal-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
bin_PROGRAMS += \
coredumpctl
@@ -4647,8 +4480,7 @@ test_coredump_vacuum_SOURCES = \
src/journal/coredump-vacuum.h
test_coredump_vacuum_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
dist_bashcompletion_DATA += \
shell-completion/bash/coredumpctl
@@ -4672,7 +4504,7 @@ systemd_binfmt_SOURCES = \
src/binfmt/binfmt.c
systemd_binfmt_LDADD = \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-binfmt
@@ -4703,7 +4535,7 @@ systemd_vconsole_setup_SOURCES = \
src/vconsole/vconsole-setup.c
systemd_vconsole_setup_LDADD = \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-vconsole-setup
@@ -4734,7 +4566,7 @@ systemd_bootchart_SOURCES = \
systemd_bootchart_LDADD = \
libsystemd-journal-internal.la \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-bootchart
@@ -4761,7 +4593,7 @@ systemd_quotacheck_SOURCES = \
src/quotacheck/quotacheck.c
systemd_quotacheck_LDADD = \
- libsystemd-shared.la
+ libshared.la
endif
EXTRA_DIST += \
@@ -4782,7 +4614,7 @@ systemd_random_seed_SOURCES = \
src/random-seed/random-seed.c
systemd_random_seed_LDADD = \
- libsystemd-shared.la
+ libshared.la
SYSINIT_TARGET_WANTS += \
systemd-random-seed.service
@@ -4805,8 +4637,7 @@ systemd_backlight_SOURCES = \
systemd_backlight_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
endif
EXTRA_DIST += \
@@ -4825,8 +4656,7 @@ systemd_rfkill_SOURCES = \
systemd_rfkill_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
endif
EXTRA_DIST += \
@@ -4853,15 +4683,14 @@ systemd_cryptsetup_CFLAGS = \
systemd_cryptsetup_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(LIBCRYPTSETUP_LIBS)
systemd_cryptsetup_generator_SOURCES = \
src/cryptsetup/cryptsetup-generator.c
systemd_cryptsetup_generator_LDADD = \
- libsystemd-shared.la
+ libshared.la
SYSINIT_TARGET_WANTS += \
cryptsetup.target
@@ -4874,8 +4703,7 @@ systemd_hostnamed_SOURCES = \
src/hostname/hostnamed.c
systemd_hostnamed_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-hostnamed
@@ -4905,8 +4733,7 @@ hostnamectl_SOURCES = \
src/hostname/hostnamectl.c
hostnamectl_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
bin_PROGRAMS += \
hostnamectl
@@ -4938,8 +4765,7 @@ systemd_localed_SOURCES = \
src/locale/localed.c
systemd_localed_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(XKBCOMMON_LIBS)
systemd_localed_CFLAGS = \
@@ -4978,8 +4804,7 @@ localectl_SOURCES = \
src/locale/localectl.c
localectl_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
bin_PROGRAMS += \
localectl
@@ -5006,8 +4831,7 @@ systemd_timedated_SOURCES = \
src/timedate/timedated.c
systemd_timedated_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-timedated
@@ -5037,8 +4861,7 @@ timedatectl_SOURCES = \
src/timedate/timedatectl.c
timedatectl_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
bin_PROGRAMS += \
timedatectl
@@ -5076,8 +4899,7 @@ gperf_gperf_sources += \
systemd_timesyncd_LDADD = \
libsystemd-resolve.la \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-timesyncd
@@ -5114,8 +4936,8 @@ libnss_myhostname_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/nss-myhostname/nss-myhostname.sym
libnss_myhostname_la_LIBADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
+#libsystemd-network.la
lib_LTLIBRARIES += \
libnss_myhostname.la
@@ -5128,12 +4950,12 @@ systemd_machined_SOURCES = \
src/machine/machined.h
systemd_machined_LDADD = \
- libsystemd-machine-core.la
+ libmachine-core.la
rootlibexec_PROGRAMS += \
systemd-machined
-libsystemd_machine_core_la_SOURCES = \
+libmachine_core_la_SOURCES = \
src/machine/machine.c \
src/machine/machine.h \
src/machine/machined-dbus.c \
@@ -5142,23 +4964,19 @@ libsystemd_machine_core_la_SOURCES = \
src/machine/image-dbus.c \
src/machine/image-dbus.h
-libsystemd_machine_core_la_LIBADD = \
+libmachine_core_la_LIBADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-machine.la \
- libsystemd-shared.la
+ libshared.la
noinst_LTLIBRARIES += \
- libsystemd-machine-core.la
+ libmachine-core.la
machinectl_SOURCES = \
src/machine/machinectl.c
machinectl_LDADD = \
- libsystemd-internal.la \
- libsystemd-logs.la \
libsystemd-journal-internal.la \
- libsystemd-shared.la
+ libshared.la
rootbin_PROGRAMS += \
machinectl
@@ -5170,7 +4988,7 @@ test_machine_tables_SOURCES = \
src/machine/test-machine-tables.c
test_machine_tables_LDADD = \
- libsystemd-machine-core.la
+ libmachine-core.la
tests += \
test-machine-tables
@@ -5217,8 +5035,7 @@ libnss_mymachines_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/nss-mymachines/nss-mymachines.sym
libnss_mymachines_la_LIBADD = \
- libsystemd-shared.la \
- libsystemd-internal.la
+ libshared.la
lib_LTLIBRARIES += \
libnss_mymachines.la
@@ -5256,9 +5073,7 @@ systemd_importd_CFLAGS = \
-D SYSTEMD_EXPORT_PATH=\"$(rootlibexecdir)/systemd-export\"
systemd_importd_LDADD = \
- libsystemd-machine.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
systemd_pull_SOURCES = \
src/import/pull.c \
@@ -5293,9 +5108,7 @@ systemd_pull_CFLAGS = \
-D USER_KEYRING_PATH=\"$(pkgsysconfdir)/import-pubring.gpg\"
systemd_pull_LDADD = \
- libsystemd-machine.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(LIBCURL_LIBS) \
$(XZ_LIBS) \
$(ZLIB_LIBS) \
@@ -5321,9 +5134,7 @@ systemd_import_CFLAGS = \
$(ZLIB_CFLAGS)
systemd_import_LDADD = \
- libsystemd-machine.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(XZ_LIBS) \
$(ZLIB_LIBS) \
-lbz2
@@ -5345,9 +5156,7 @@ systemd_export_CFLAGS = \
$(ZLIB_CFLAGS)
systemd_export_LDADD = \
- libsystemd-machine.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(XZ_LIBS) \
$(ZLIB_LIBS) \
-lbz2
@@ -5389,8 +5198,7 @@ test_qcow2_CFLAGS = \
$(ZLIB_CFLAGS)
test_qcow2_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(ZLIB_LIBS)
endif
@@ -5460,8 +5268,7 @@ gperf_txt_sources += \
systemd_resolved_LDADD = \
libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(LIBIDN_LIBS)
rootlibexec_PROGRAMS += \
@@ -5491,6 +5298,9 @@ GENERAL_ALIASES += \
nodist_pkgsysconf_DATA += \
src/resolve/resolved.conf
+tests += \
+ test-dns-domain
+
libnss_resolve_la_SOURCES = \
src/nss-resolve/nss-resolve.sym \
src/nss-resolve/nss-resolve.c
@@ -5505,8 +5315,7 @@ libnss_resolve_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/nss-resolve/nss-resolve.sym
libnss_resolve_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
lib_LTLIBRARIES += \
libnss_resolve.la
@@ -5529,8 +5338,7 @@ nodist_systemd_resolve_host_SOURCES = \
src/resolve/dns_type-to-name.h
systemd_resolve_host_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(LIBIDN_LIBS)
rootlibexec_PROGRAMS += \
@@ -5550,21 +5358,21 @@ systemd_networkd_SOURCES = \
src/network/networkd.c
systemd_networkd_LDADD = \
- libsystemd-networkd-core.la \
+ libnetworkd-core.la \
$(LIBIDN_LIBS)
if HAVE_LIBIPTC
systemd_networkd_LDADD += \
- libsystemd-fw.la
+ libfirewall.la
endif
noinst_LTLIBRARIES += \
- libsystemd-networkd-core.la
+ libnetworkd-core.la
-libsystemd_networkd_core_la_CFLAGS = \
+libnetworkd_core_la_CFLAGS = \
$(AM_CFLAGS)
-libsystemd_networkd_core_la_SOURCES = \
+libnetworkd_core_la_SOURCES = \
src/libsystemd-network/network-internal.h \
src/network/networkd.h \
src/network/networkd-link.h \
@@ -5604,15 +5412,14 @@ libsystemd_networkd_core_la_SOURCES = \
src/network/networkd-fdb.c \
src/network/networkd-address-pool.c
-nodist_libsystemd_networkd_core_la_SOURCES = \
+nodist_libnetworkd_core_la_SOURCES = \
src/network/networkd-network-gperf.c \
src/network/networkd-netdev-gperf.c
-libsystemd_networkd_core_la_LIBADD = \
+libnetworkd_core_la_LIBADD = \
libudev-internal.la \
- libsystemd-internal.la \
libsystemd-network.la \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-networkd-wait-online
@@ -5631,8 +5438,7 @@ systemd_networkd_wait_online_SOURCES = \
systemd_networkd_wait_online_LDADD = \
libsystemd-network.la \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
rootbin_PROGRAMS += \
networkctl
@@ -5642,20 +5448,19 @@ networkctl_SOURCES = \
networkctl_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
libsystemd-network.la
test_network_SOURCES = \
src/network/test-network.c
test_network_LDADD = \
- libsystemd-networkd-core.la \
+ libnetworkd-core.la \
$(LIBIDN_LIBS)
if HAVE_LIBIPTC
test_network_LDADD += \
- libsystemd-fw.la
+ libfirewall.la
endif
test_network_tables_SOURCES = \
@@ -5663,12 +5468,12 @@ test_network_tables_SOURCES = \
src/shared/test-tables.h
test_network_tables_LDADD = \
- libsystemd-networkd-core.la \
+ libnetworkd-core.la \
libudev-core.la
if HAVE_LIBIPTC
test_network_tables_LDADD += \
- libsystemd-fw.la
+ libfirewall.la
endif
tests += \
@@ -5721,9 +5526,9 @@ nodist_systemd_logind_SOURCES = \
src/login/logind-gperf.c
systemd_logind_LDADD = \
- libsystemd-logind-core.la
+ liblogind-core.la
-libsystemd_logind_core_la_SOURCES = \
+liblogind_core_la_SOURCES = \
src/login/logind-core.c \
src/login/logind-device.c \
src/login/logind-device.h \
@@ -5748,21 +5553,17 @@ libsystemd_logind_core_la_SOURCES = \
src/login/logind-utmp.c \
src/login/logind-acl.h
-libsystemd_logind_core_la_LIBADD = \
+liblogind_core_la_LIBADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
if HAVE_ACL
-libsystemd_logind_core_la_SOURCES += \
+liblogind_core_la_SOURCES += \
src/login/logind-acl.c
-
-libsystemd_logind_core_la_LIBADD += \
- libsystemd-acl.la
endif
noinst_LTLIBRARIES += \
- libsystemd-logind-core.la
+ liblogind-core.la
rootlibexec_PROGRAMS += \
systemd-logind
@@ -5774,10 +5575,8 @@ loginctl_SOURCES = \
loginctl_LDADD = \
libudev-internal.la \
- libsystemd-internal.la \
- libsystemd-logs.la \
libsystemd-journal-internal.la \
- libsystemd-shared.la
+ libshared.la
rootbin_PROGRAMS += \
loginctl
@@ -5793,8 +5592,7 @@ systemd_inhibit_SOURCES = \
src/login/inhibit.c
systemd_inhibit_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
rootbin_PROGRAMS += \
systemd-inhibit
@@ -5803,28 +5601,25 @@ test_login_SOURCES = \
src/libsystemd/sd-login/test-login.c
test_login_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_login_shared_SOURCES = \
src/login/test-login-shared.c
test_login_shared_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_inhibit_SOURCES = \
src/login/test-inhibit.c
test_inhibit_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
test_login_tables_SOURCES = \
src/login/test-login-tables.c
test_login_tables_LDADD = \
- libsystemd-logind-core.la
+ liblogind-core.la
manual_tests += \
test-login \
@@ -5853,8 +5648,7 @@ pam_systemd_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/login/pam_systemd.sym
pam_systemd_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la \
+ libshared.la \
$(PAM_LIBS)
pamlib_LTLIBRARIES = \
@@ -5926,7 +5720,7 @@ systemd_user_sessions_SOURCES = \
src/user-sessions/user-sessions.c
systemd_user_sessions_LDADD = \
- libsystemd-shared.la
+ libshared.la
rootlibexec_PROGRAMS += \
systemd-user-sessions
@@ -5992,7 +5786,7 @@ id128_la_LDFLAGS = \
id128_la_LIBADD = \
$(PYTHON_DEVEL_LIBS) \
- libsystemd-shared.la \
+ libshared.la \
libsystemd.la
_daemon_la_SOURCES = \
@@ -6014,7 +5808,7 @@ _daemon_la_LDFLAGS = \
_daemon_la_LIBADD = \
$(PYTHON_DEVEL_LIBS) \
- libsystemd-shared.la \
+ libshared.la \
libsystemd.la
_reader_la_SOURCES = \
@@ -6035,7 +5829,7 @@ _reader_la_LDFLAGS = \
_reader_la_LIBADD = \
$(PYTHON_DEVEL_LIBS) \
- libsystemd-shared.la \
+ libshared.la \
libsystemd.la
login_la_SOURCES = \
@@ -6056,7 +5850,7 @@ login_la_LDFLAGS = \
login_la_LIBADD = \
$(PYTHON_DEVEL_LIBS) \
- libsystemd-shared.la \
+ libshared.la \
libsystemd.la
dist_pkgpyexec_PYTHON = \
@@ -6128,8 +5922,7 @@ libsystemd_journal_la_LDFLAGS = \
libsystemd_journal_la_LIBADD = \
libsystemd-journal-internal.la \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libsystemd-internal.la
libsystemd_login_la_SOURCES = \
libsystemd-login.c \
@@ -6145,8 +5938,7 @@ libsystemd_login_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/compat-libs/libsystemd-login.sym
libsystemd_login_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
libsystemd_id128_la_SOURCES = \
libsystemd-id128.c \
@@ -6162,8 +5954,7 @@ libsystemd_id128_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/compat-libs/libsystemd-id128.sym
libsystemd_id128_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
libsystemd_daemon_la_SOURCES = \
libsystemd-daemon.c \
@@ -6179,8 +5970,7 @@ libsystemd_daemon_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/src/compat-libs/libsystemd-daemon.sym
libsystemd_daemon_la_LIBADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
+ libshared.la
lib_LTLIBRARIES += \
libsystemd-journal.la \
diff --git a/README b/README
index 777b343..651aa98 100644
--- a/README
+++ b/README
@@ -30,8 +30,8 @@ AUTHOR:
LICENSE:
LGPLv2.1+ for all code
- - except src/shared/MurmurHash2.c which is Public Domain
- - except src/shared/siphash24.c which is CC0 Public Domain
+ - except src/basic/MurmurHash2.c which is Public Domain
+ - except src/basic/siphash24.c which is CC0 Public Domain
- except src/journal/lookup3.c which is Public Domain
- except src/udev/* which is (currently still) GPLv2, GPLv2+
diff --git a/src/basic/.gitignore b/src/basic/.gitignore
new file mode 100644
index 0000000..e22411e
--- /dev/null
+++ b/src/basic/.gitignore
@@ -0,0 +1,16 @@
+/cap-from-name.gperf
+/cap-from-name.h
+/cap-list.txt
+/cap-to-name.h
+/errno-from-name.gperf
+/errno-from-name.h
+/errno-list.txt
+/errno-to-name.h
+/af-from-name.gperf
+/af-from-name.h
+/af-list.txt
+/af-to-name.h
+/arphrd-from-name.gperf
+/arphrd-from-name.h
+/arphrd-list.txt
+/arphrd-to-name.h
diff --git a/src/basic/Makefile b/src/basic/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/basic/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c
new file mode 100644
index 0000000..2f4149d
--- /dev/null
+++ b/src/basic/MurmurHash2.c
@@ -0,0 +1,86 @@
+//-----------------------------------------------------------------------------
+// MurmurHash2 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+
+// Note - This code makes a few assumptions about how your machine behaves -
+
+// 1. We can read a 4-byte value from any address without crashing
+// 2. sizeof(int) == 4
+
+// And it has a few limitations -
+
+// 1. It will not work incrementally.
+// 2. It will not produce the same results on little-endian and big-endian
+// machines.
+
+#include "MurmurHash2.h"
+
+//-----------------------------------------------------------------------------
+// Platform-specific functions and macros
+
+// Microsoft Visual Studio
+
+#if defined(_MSC_VER)
+
+#define BIG_CONSTANT(x) (x)
+
+// Other compilers
+
+#else // defined(_MSC_VER)
+
+#define BIG_CONSTANT(x) (x##LLU)
+
+#endif // !defined(_MSC_VER)
+
+//-----------------------------------------------------------------------------
+
+uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
+{
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+
+ const uint32_t m = 0x5bd1e995;
+ const int r = 24;
+
+ // Initialize the hash to a 'random' value
+
+ uint32_t h = seed ^ len;
+
+ // Mix 4 bytes at a time into the hash
+
+ const unsigned char * data = (const unsigned char *)key;
+
+ while(len >= 4)
+ {
+ uint32_t k = *(uint32_t*)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ // Handle the last few bytes of the input array
+
+ switch(len)
+ {
+ case 3: h ^= data[2] << 16;
+ case 2: h ^= data[1] << 8;
+ case 1: h ^= data[0];
+ h *= m;
+ };
+
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
diff --git a/src/basic/MurmurHash2.h b/src/basic/MurmurHash2.h
new file mode 100644
index 0000000..93362dd
--- /dev/null
+++ b/src/basic/MurmurHash2.h
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------------
+// MurmurHash2 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+
+#ifndef _MURMURHASH2_H_
+#define _MURMURHASH2_H_
+
+//-----------------------------------------------------------------------------
+// Platform-specific functions and macros
+
+// Microsoft Visual Studio
+
+#if defined(_MSC_VER)
+
+typedef unsigned char uint8_t;
+typedef unsigned long uint32_t;
+typedef unsigned __int64 uint64_t;
+
+// Other compilers
+
+#else // defined(_MSC_VER)
+
+#include <stdint.h>
+
+#endif // !defined(_MSC_VER)
+
+//-----------------------------------------------------------------------------
+
+uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed );
+
+//-----------------------------------------------------------------------------
+
+#endif // _MURMURHASH2_H_
diff --git a/src/basic/af-list.c b/src/basic/af-list.c
new file mode 100644
index 0000000..f396115
--- /dev/null
+++ b/src/basic/af-list.c
@@ -0,0 +1,58 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/socket.h>
+#include <string.h>
+
+#include "util.h"
+#include "af-list.h"
+
+static const struct af_name* lookup_af(register const char *str, register unsigned int len);
+
+#include "af-to-name.h"
+#include "af-from-name.h"
+
+const char *af_to_name(int id) {
+
+ if (id <= 0)
+ return NULL;
+
+ if (id >= (int) ELEMENTSOF(af_names))
+ return NULL;
+
+ return af_names[id];
+}
+
+int af_from_name(const char *name) {
+ const struct af_name *sc;
+
+ assert(name);
+
+ sc = lookup_af(name, strlen(name));
+ if (!sc)
+ return AF_UNSPEC;
+
+ return sc->id;
+}
+
+int af_max(void) {
+ return ELEMENTSOF(af_names);
+}
diff --git a/src/basic/af-list.h b/src/basic/af-list.h
new file mode 100644
index 0000000..e346ab8
--- /dev/null
+++ b/src/basic/af-list.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+const char *af_to_name(int id);
+int af_from_name(const char *name);
+
+int af_max(void);
diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c
new file mode 100644
index 0000000..284043c
--- /dev/null
+++ b/src/basic/arphrd-list.c
@@ -0,0 +1,58 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <net/if_arp.h>
+#include <string.h>
+
+#include "util.h"
+#include "arphrd-list.h"
+
+static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
+
+#include "arphrd-to-name.h"
+#include "arphrd-from-name.h"
+
+const char *arphrd_to_name(int id) {
+
+ if (id <= 0)
+ return NULL;
+
+ if (id >= (int) ELEMENTSOF(arphrd_names))
+ return NULL;
+
+ return arphrd_names[id];
+}
+
+int arphrd_from_name(const char *name) {
+ const struct arphrd_name *sc;
+
+ assert(name);
+
+ sc = lookup_arphrd(name, strlen(name));
+ if (!sc)
+ return 0;
+
+ return sc->id;
+}
+
+int arphrd_max(void) {
+ return ELEMENTSOF(arphrd_names);
+}
diff --git a/src/basic/arphrd-list.h b/src/basic/arphrd-list.h
new file mode 100644
index 0000000..5ca182c
--- /dev/null
+++ b/src/basic/arphrd-list.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+const char *arphrd_to_name(int id);
+int arphrd_from_name(const char *name);
+
+int arphrd_max(void);
diff --git a/src/basic/async.c b/src/basic/async.c
new file mode 100644
index 0000000..7725e6d
--- /dev/null
+++ b/src/basic/async.c
@@ -0,0 +1,92 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <pthread.h>
+#include <unistd.h>
+
+#include "async.h"
+#include "log.h"
+#include "util.h"
+
+int asynchronous_job(void* (*func)(void *p), void *arg) {
+ pthread_attr_t a;
+ pthread_t t;
+ int r;
+
+ /* It kinda sucks that we have to resort to threads to
+ * implement an asynchronous sync(), but well, such is
+ * life.
+ *
+ * Note that issuing this command right before exiting a
+ * process will cause the process to wait for the sync() to
+ * complete. This function hence is nicely asynchronous really
+ * only in long running processes. */
+
+ r = pthread_attr_init(&a);
+ if (r > 0)
+ return -r;
+
+ r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+ if (r > 0)
+ goto finish;
+
+ r = pthread_create(&t, &a, func, arg);
+
+finish:
+ pthread_attr_destroy(&a);
+ return -r;
+}
+
+static void *sync_thread(void *p) {
+ sync();
+ return NULL;
+}
+
+int asynchronous_sync(void) {
+ log_debug("Spawning new thread for sync");
+
+ return asynchronous_job(sync_thread, NULL);
+}
+
+static void *close_thread(void *p) {
+ assert_se(close_nointr(PTR_TO_INT(p)) != -EBADF);
+ return NULL;
+}
+
+int asynchronous_close(int fd) {
+ int r;
+
+ /* This is supposed to behave similar to safe_close(), but
+ * actually invoke close() asynchronously, so that it will
+ * never block. Ideally the kernel would have an API for this,
+ * but it doesn't, so we work around it, and hide this as a
+ * far away as we can. */
+
+ if (fd >= 0) {
+ PROTECT_ERRNO;
+
+ r = asynchronous_job(close_thread, INT_TO_PTR(fd));
+ if (r < 0)
+ assert_se(close_nointr(fd) != -EBADF);
+ }
+
+ return -1;
+}
diff --git a/src/basic/async.h b/src/basic/async.h
new file mode 100644
index 0000000..7f1ef79
--- /dev/null
+++ b/src/basic/async.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/>.
+***/
+
+int asynchronous_job(void* (*func)(void *p), void *arg);
+
+int asynchronous_sync(void);
+int asynchronous_close(int fd);
diff --git a/src/basic/audit.c b/src/basic/audit.c
new file mode 100644
index 0000000..54148fc
--- /dev/null
+++ b/src/basic/audit.c
@@ -0,0 +1,94 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <errno.h>
+#include <stdio.h>
+
+#include "macro.h"
+#include "audit.h"
+#include "util.h"
+#include "process-util.h"
+#include "fileio.h"
+
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+ _cleanup_free_ char *s = NULL;
+ const char *p;
+ uint32_t u;
+ int r;
+
+ assert(id);
+
+ p = procfs_file_alloca(pid, "sessionid");
+
+ r = read_one_line_file(p, &s);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u == AUDIT_SESSION_INVALID || u <= 0)
+ return -ENXIO;
+
+ *id = u;
+ return 0;
+}
+
+int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
+ _cleanup_free_ char *s = NULL;
+ const char *p;
+ uid_t u;
+ int r;
+
+ assert(uid);
+
+ p = procfs_file_alloca(pid, "loginuid");
+
+ r = read_one_line_file(p, &s);
+ if (r < 0)
+ return r;
+
+ r = parse_uid(s, &u);
+ if (r < 0)
+ return r;
+
+ *uid = (uid_t) u;
+ return 0;
+}
+
+bool use_audit(void) {
+ static int cached_use = -1;
+
+ if (cached_use < 0) {
+ int fd;
+
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
+ if (fd < 0)
+ cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT;
+ else {
+ cached_use = true;
+ safe_close(fd);
+ }
+ }
+
+ return cached_use;
+}
diff --git a/src/basic/audit.h b/src/basic/audit.h
new file mode 100644
index 0000000..6de331c
--- /dev/null
+++ b/src/basic/audit.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#define AUDIT_SESSION_INVALID ((uint32_t) -1)
+
+int audit_session_from_pid(pid_t pid, uint32_t *id);
+int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
+
+bool use_audit(void);
diff --git a/src/basic/barrier.c b/src/basic/barrier.c
new file mode 100644
index 0000000..436ba95
--- /dev/null
+++ b/src/basic/barrier.c
@@ -0,0 +1,416 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
+
+ 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 <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "barrier.h"
+#include "macro.h"
+#include "util.h"
+
+/**
+ * Barriers
+ * This barrier implementation provides a simple synchronization method based
+ * on file-descriptors that can safely be used between threads and processes. A
+ * barrier object contains 2 shared counters based on eventfd. Both processes
+ * can now place barriers and wait for the other end to reach a random or
+ * specific barrier.
+ * Barriers are numbered, so you can either wait for the other end to reach any
+ * barrier or the last barrier that you placed. This way, you can use barriers
+ * for one-way *and* full synchronization. Note that even-though barriers are
+ * numbered, these numbers are internal and recycled once both sides reached the
+ * same barrier (implemented as a simple signed counter). It is thus not
+ * possible to address barriers by their ID.
+ *
+ * Barrier-API: Both ends can place as many barriers via barrier_place() as
+ * they want and each pair of barriers on both sides will be implicitly linked.
+ * Each side can use the barrier_wait/sync_*() family of calls to wait for the
+ * other side to place a specific barrier. barrier_wait_next() waits until the
+ * other side calls barrier_place(). No links between the barriers are
+ * considered and this simply serves as most basic asynchronous barrier.
+ * barrier_sync_next() is like barrier_wait_next() and waits for the other side
+ * to place their next barrier via barrier_place(). However, it only waits for
+ * barriers that are linked to a barrier we already placed. If the other side
+ * already placed more barriers than we did, barrier_sync_next() returns
+ * immediately.
+ * barrier_sync() extends barrier_sync_next() and waits until the other end
+ * placed as many barriers via barrier_place() as we did. If they already placed
+ * as many as we did (or more), it returns immediately.
+ *
+ * Additionally to basic barriers, an abortion event is available.
+ * barrier_abort() places an abortion event that cannot be undone. An abortion
+ * immediately cancels all placed barriers and replaces them. Any running and
+ * following wait/sync call besides barrier_wait_abortion() will immediately
+ * return false on both sides (otherwise, they always return true).
+ * barrier_abort() can be called multiple times on both ends and will be a
+ * no-op if already called on this side.
+ * barrier_wait_abortion() can be used to wait for the other side to call
+ * barrier_abort() and is the only wait/sync call that does not return
+ * immediately if we aborted outself. It only returns once the other side
+ * called barrier_abort().
+ *
+ * Barriers can be used for in-process and inter-process synchronization.
+ * However, for in-process synchronization you could just use mutexes.
+ * Therefore, main target is IPC and we require both sides to *not* share the FD
+ * table. If that's given, barriers provide target tracking: If the remote side
+ * exit()s, an abortion event is implicitly queued on the other side. This way,
+ * a sync/wait call will be woken up if the remote side crashed or exited
+ * unexpectedly. However, note that these abortion events are only queued if the
+ * barrier-queue has been drained. Therefore, it is safe to place a barrier and
+ * exit. The other side can safely wait on the barrier even though the exit
+ * queued an abortion event. Usually, the abortion event would overwrite the
+ * barrier, however, that's not true for exit-abortion events. Those are only
+ * queued if the barrier-queue is drained (thus, the receiving side has placed
+ * more barriers than the remote side).
+ */
+
+/**
+ * barrier_create() - Initialize a barrier object
+ * @obj: barrier to initialize
+ *
+ * This initializes a barrier object. The caller is responsible of allocating
+ * the memory and keeping it valid. The memory does not have to be zeroed
+ * beforehand.
+ * Two eventfd objects are allocated for each barrier. If allocation fails, an
+ * error is returned.
+ *
+ * If this function fails, the barrier is reset to an invalid state so it is
+ * safe to call barrier_destroy() on the object regardless whether the
+ * initialization succeeded or not.
+ *
+ * The caller is responsible to destroy the object via barrier_destroy() before
+ * releasing the underlying memory.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int barrier_create(Barrier *b) {
+ _cleanup_(barrier_destroyp) Barrier *staging = b;
+ int r;
+
+ assert(b);
+
+ b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+ if (b->me < 0)
+ return -errno;
+
+ b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+ if (b->them < 0)
+ return -errno;
+
+ r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK);
+ if (r < 0)
+ return -errno;
+
+ staging = NULL;
+ return 0;
+}
+
+/**
+ * barrier_destroy() - Destroy a barrier object
+ * @b: barrier to destroy or NULL
+ *
+ * This destroys a barrier object that has previously been passed to
+ * barrier_create(). The object is released and reset to invalid
+ * state. Therefore, it is safe to call barrier_destroy() multiple
+ * times or even if barrier_create() failed. However, barrier must be
+ * always initialized with BARRIER_NULL.
+ *
+ * If @b is NULL, this is a no-op.
+ */
+void barrier_destroy(Barrier *b) {
+ if (!b)
+ return;
+
+ b->me = safe_close(b->me);
+ b->them = safe_close(b->them);
+ safe_close_pair(b->pipe);
+ b->barriers = 0;
+}
+
+/**
+ * barrier_set_role() - Set the local role of the barrier
+ * @b: barrier to operate on
+ * @role: role to set on the barrier
+ *
+ * This sets the roles on a barrier object. This is needed to know
+ * which side of the barrier you're on. Usually, the parent creates
+ * the barrier via barrier_create() and then calls fork() or clone().
+ * Therefore, the FDs are duplicated and the child retains the same
+ * barrier object.
+ *
+ * Both sides need to call barrier_set_role() after fork() or clone()
+ * are done. If this is not done, barriers will not work correctly.
+ *
+ * Note that barriers could be supported without fork() or clone(). However,
+ * this is currently not needed so it hasn't been implemented.
+ */
+void barrier_set_role(Barrier *b, unsigned int role) {
+ int fd;
+
+ assert(b);
+ assert(role == BARRIER_PARENT || role == BARRIER_CHILD);
+ /* make sure this is only called once */
+ assert(b->pipe[0] >= 0 && b->pipe[1] >= 0);
+
+ if (role == BARRIER_PARENT)
+ b->pipe[1] = safe_close(b->pipe[1]);
+ else {
+ b->pipe[0] = safe_close(b->pipe[0]);
+
+ /* swap me/them for children */
+ fd = b->me;
+ b->me = b->them;
+ b->them = fd;
+ }
+}
+
+/* places barrier; returns false if we aborted, otherwise true */
+static bool barrier_write(Barrier *b, uint64_t buf) {
+ ssize_t len;
+
+ /* prevent new sync-points if we already aborted */
+ if (barrier_i_aborted(b))
+ return false;
+
+ do {
+ len = write(b->me, &buf, sizeof(buf));
+ } while (len < 0 && IN_SET(errno, EAGAIN, EINTR));
+
+ if (len != sizeof(buf))
+ goto error;
+
+ /* lock if we aborted */
+ if (buf >= (uint64_t)BARRIER_ABORTION) {
+ if (barrier_they_aborted(b))
+ b->barriers = BARRIER_WE_ABORTED;
+ else
+ b->barriers = BARRIER_I_ABORTED;
+ } else if (!barrier_is_aborted(b))
+ b->barriers += buf;
+
+ return !barrier_i_aborted(b);
+
+error:
+ /* If there is an unexpected error, we have to make this fatal. There
+ * is no way we can recover from sync-errors. Therefore, we close the
+ * pipe-ends and treat this as abortion. The other end will notice the
+ * pipe-close and treat it as abortion, too. */
+
+ safe_close_pair(b->pipe);
+ b->barriers = BARRIER_WE_ABORTED;
+ return false;
+}
+
+/* waits for barriers; returns false if they aborted, otherwise true */
+static bool barrier_read(Barrier *b, int64_t comp) {
+ if (barrier_they_aborted(b))
+ return false;
+
+ while (b->barriers > comp) {
+ struct pollfd pfd[2] = {
+ { .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1],
+ .events = POLLHUP },
+ { .fd = b->them,
+ .events = POLLIN }};
+ uint64_t buf;
+ int r;
+
+ r = poll(pfd, 2, -1);
+ if (r < 0 && IN_SET(errno, EAGAIN, EINTR))
+ continue;
+ else if (r < 0)
+ goto error;
+
+ if (pfd[1].revents) {
+ ssize_t len;
+
+ /* events on @them signal new data for us */
+ len = read(b->them, &buf, sizeof(buf));
+ if (len < 0 && IN_SET(errno, EAGAIN, EINTR))
+ continue;
+
+ if (len != sizeof(buf))
+ goto error;
+ } else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL))
+ /* POLLHUP on the pipe tells us the other side exited.
+ * We treat this as implicit abortion. But we only
+ * handle it if there's no event on the eventfd. This
+ * guarantees that exit-abortions do not overwrite real
+ * barriers. */
+ buf = BARRIER_ABORTION;
+ else
+ continue;
+
+ /* lock if they aborted */
+ if (buf >= (uint64_t)BARRIER_ABORTION) {
+ if (barrier_i_aborted(b))
+ b->barriers = BARRIER_WE_ABORTED;
+ else
+ b->barriers = BARRIER_THEY_ABORTED;
+ } else if (!barrier_is_aborted(b))
+ b->barriers -= buf;
+ }
+
+ return !barrier_they_aborted(b);
+
+error:
+ /* If there is an unexpected error, we have to make this fatal. There
+ * is no way we can recover from sync-errors. Therefore, we close the
+ * pipe-ends and treat this as abortion. The other end will notice the
+ * pipe-close and treat it as abortion, too. */
+
+ safe_close_pair(b->pipe);
+ b->barriers = BARRIER_WE_ABORTED;
+ return false;
+}
+
+/**
+ * barrier_place() - Place a new barrier
+ * @b: barrier object
+ *
+ * This places a new barrier on the barrier object. If either side already
+ * aborted, this is a no-op and returns "false". Otherwise, the barrier is
+ * placed and this returns "true".
+ *
+ * Returns: true if barrier was placed, false if either side aborted.
+ */
+bool barrier_place(Barrier *b) {
+ assert(b);
+
+ if (barrier_is_aborted(b))
+ return false;
+
+ barrier_write(b, BARRIER_SINGLE);
+ return true;
+}
+
+/**
+ * barrier_abort() - Abort the synchronization
+ * @b: barrier object to abort
+ *
+ * This aborts the barrier-synchronization. If barrier_abort() was already
+ * called on this side, this is a no-op. Otherwise, the barrier is put into the
+ * ABORT-state and will stay there. The other side is notified about the
+ * abortion. Any following attempt to place normal barriers or to wait on normal
+ * barriers will return immediately as "false".
+ *
+ * You can wait for the other side to call barrier_abort(), too. Use
+ * barrier_wait_abortion() for that.
+ *
+ * Returns: false if the other side already aborted, true otherwise.
+ */
+bool barrier_abort(Barrier *b) {
+ assert(b);
+
+ barrier_write(b, BARRIER_ABORTION);
+ return !barrier_they_aborted(b);
+}
+
+/**
+ * barrier_wait_next() - Wait for the next barrier of the other side
+ * @b: barrier to operate on
+ *
+ * This waits until the other side places its next barrier. This is independent
+ * of any barrier-links and just waits for any next barrier of the other side.
+ *
+ * If either side aborted, this returns false.
+ *
+ * Returns: false if either side aborted, true otherwise.
+ */
+bool barrier_wait_next(Barrier *b) {
+ assert(b);
+
+ if (barrier_is_aborted(b))
+ return false;
+
+ barrier_read(b, b->barriers - 1);
+ return !barrier_is_aborted(b);
+}
+
+/**
+ * barrier_wait_abortion() - Wait for the other side to abort
+ * @b: barrier to operate on
+ *
+ * This waits until the other side called barrier_abort(). This can be called
+ * regardless whether the local side already called barrier_abort() or not.
+ *
+ * If the other side has already aborted, this returns immediately.
+ *
+ * Returns: false if the local side aborted, true otherwise.
+ */
+bool barrier_wait_abortion(Barrier *b) {
+ assert(b);
+
+ barrier_read(b, BARRIER_THEY_ABORTED);
+ return !barrier_i_aborted(b);
+}
+
+/**
+ * barrier_sync_next() - Wait for the other side to place a next linked barrier
+ * @b: barrier to operate on
+ *
+ * This is like barrier_wait_next() and waits for the other side to call
+ * barrier_place(). However, this only waits for linked barriers. That means, if
+ * the other side already placed more barriers than (or as much as) we did, this
+ * returns immediately instead of waiting.
+ *
+ * If either side aborted, this returns false.
+ *
+ * Returns: false if either side aborted, true otherwise.
+ */
+bool barrier_sync_next(Barrier *b) {
+ assert(b);
+
+ if (barrier_is_aborted(b))
+ return false;
+
+ barrier_read(b, MAX((int64_t)0, b->barriers - 1));
+ return !barrier_is_aborted(b);
+}
+
+/**
+ * barrier_sync() - Wait for the other side to place as many barriers as we did
+ * @b: barrier to operate on
+ *
+ * This is like barrier_sync_next() but waits for the other side to call
+ * barrier_place() as often as we did (in total). If they already placed as much
+ * as we did (or more), this returns immediately instead of waiting.
+ *
+ * If either side aborted, this returns false.
+ *
+ * Returns: false if either side aborted, true otherwise.
+ */
+bool barrier_sync(Barrier *b) {
+ assert(b);
+
+ if (barrier_is_aborted(b))
+ return false;
+
+ barrier_read(b, 0);
+ return !barrier_is_aborted(b);
+}
diff --git a/src/basic/barrier.h b/src/basic/barrier.h
new file mode 100644
index 0000000..b895469
--- /dev/null
+++ b/src/basic/barrier.h
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
+
+ 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>
+
+#include "macro.h"
+
+/* See source file for an API description. */
+
+typedef struct Barrier Barrier;
+
+enum {
+ BARRIER_SINGLE = 1LL,
+ BARRIER_ABORTION = INT64_MAX,
+
+ /* bias values to store state; keep @WE < @THEY < @I */
+ BARRIER_BIAS = INT64_MIN,
+ BARRIER_WE_ABORTED = BARRIER_BIAS + 1LL,
+ BARRIER_THEY_ABORTED = BARRIER_BIAS + 2LL,
+ BARRIER_I_ABORTED = BARRIER_BIAS + 3LL,
+};
+
+enum {
+ BARRIER_PARENT,
+ BARRIER_CHILD,
+};
+
+struct Barrier {
+ int me;
+ int them;
+ int pipe[2];
+ int64_t barriers;
+};
+
+#define BARRIER_NULL {-1, -1, {-1, -1}, 0}
+
+int barrier_create(Barrier *obj);
+void barrier_destroy(Barrier *b);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy);
+
+void barrier_set_role(Barrier *b, unsigned int role);
+
+bool barrier_place(Barrier *b);
+bool barrier_abort(Barrier *b);
+
+bool barrier_wait_next(Barrier *b);
+bool barrier_wait_abortion(Barrier *b);
+bool barrier_sync_next(Barrier *b);
+bool barrier_sync(Barrier *b);
+
+static inline bool barrier_i_aborted(Barrier *b) {
+ return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED;
+}
+
+static inline bool barrier_they_aborted(Barrier *b) {
+ return b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED;
+}
+
+static inline bool barrier_we_aborted(Barrier *b) {
+ return b->barriers == BARRIER_WE_ABORTED;
+}
+
+static inline bool barrier_is_aborted(Barrier *b) {
+ return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED;
+}
+
+static inline bool barrier_place_and_sync(Barrier *b) {
+ (void) barrier_place(b);
+ return barrier_sync(b);
+}
diff --git a/src/basic/blkid-util.h b/src/basic/blkid-util.h
new file mode 100644
index 0000000..c689310
--- /dev/null
+++ b/src/basic/blkid-util.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+#ifdef HAVE_BLKID
+#include <blkid/blkid.h>
+#endif
+
+#include "util.h"
+
+#ifdef HAVE_BLKID
+DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
+#define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep)
+#endif
diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h
new file mode 100644
index 0000000..d3ae573
--- /dev/null
+++ b/src/basic/btrfs-ctree.h
@@ -0,0 +1,98 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+/* Stolen from btrfs' ctree.h */
+
+struct btrfs_timespec {
+ le64_t sec;
+ le32_t nsec;
+} _packed_;
+
+struct btrfs_disk_key {
+ le64_t objectid;
+ uint8_t type;
+ le64_t offset;
+} _packed_;
+
+struct btrfs_inode_item {
+ le64_t generation;
+ le64_t transid;
+ le64_t size;
+ le64_t nbytes;
+ le64_t block_group;
+ le32_t nlink;
+ le32_t uid;
+ le32_t gid;
+ le32_t mode;
+ le64_t rdev;
+ le64_t flags;
+ le64_t sequence;
+ le64_t reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+} _packed_;
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ le64_t generation;
+ le64_t root_dirid;
+ le64_t bytenr;
+ le64_t byte_limit;
+ le64_t bytes_used;
+ le64_t last_snapshot;
+ le64_t flags;
+ le32_t refs;
+ struct btrfs_disk_key drop_progress;
+ uint8_t drop_level;
+ uint8_t level;
+ le64_t generation_v2;
+ uint8_t uuid[BTRFS_UUID_SIZE];
+ uint8_t parent_uuid[BTRFS_UUID_SIZE];
+ uint8_t received_uuid[BTRFS_UUID_SIZE];
+ le64_t ctransid;
+ le64_t otransid;
+ le64_t stransid;
+ le64_t rtransid;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec otime;
+ struct btrfs_timespec stime;
+ struct btrfs_timespec rtime;
+ le64_t reserved[8];
+} _packed_;
+
+#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
+
+struct btrfs_qgroup_info_item {
+ le64_t generation;
+ le64_t rfer;
+ le64_t rfer_cmpr;
+ le64_t excl;
+ le64_t excl_cmpr;
+} _packed_;
+
+#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
+#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
+#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
+#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
+#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
+#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
+
+struct btrfs_qgroup_limit_item {
+ le64_t flags;
+ le64_t max_rfer;
+ le64_t max_excl;
+ le64_t rsv_rfer;
+ le64_t rsv_excl;
+} _packed_;
+
+struct btrfs_root_ref {
+ le64_t dirid;
+ le64_t sequence;
+ le16_t name_len;
+} _packed_;
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
new file mode 100644
index 0000000..49528db
--- /dev/null
+++ b/src/basic/btrfs-util.c
@@ -0,0 +1,1152 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <sys/vfs.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_LINUX_BTRFS_H
+#include <linux/btrfs.h>
+#endif
+
+#include "missing.h"
+#include "util.h"
+#include "path-util.h"
+#include "macro.h"
+#include "copy.h"
+#include "selinux-util.h"
+#include "smack-util.h"
+#include "fileio.h"
+#include "btrfs-ctree.h"
+#include "btrfs-util.h"
+
+/* WARNING: Be careful with file system ioctls! When we get an fd, we
+ * need to make sure it either refers to only a regular file or
+ * directory, or that it is located on btrfs, before invoking any
+ * btrfs ioctls. The ioctl numbers are reused by some device drivers
+ * (such as DRM), and hence might have bad effects when invoked on
+ * device nodes (that reference drivers) rather than fds to normal
+ * files or directories. */
+
+static int validate_subvolume_name(const char *name) {
+
+ if (!filename_is_valid(name))
+ return -EINVAL;
+
+ if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
+ return -E2BIG;
+
+ return 0;
+}
+
+static int open_parent(const char *path, int flags) {
+ _cleanup_free_ char *parent = NULL;
+ int r, fd;
+
+ assert(path);
+
+ r = path_get_parent(path, &parent);
+ if (r < 0)
+ return r;
+
+ fd = open(parent, flags);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+static int extract_subvolume_name(const char *path, const char **subvolume) {
+ const char *fn;
+ int r;
+
+ assert(path);
+ assert(subvolume);
+
+ fn = basename(path);
+
+ r = validate_subvolume_name(fn);
+ if (r < 0)
+ return r;
+
+ *subvolume = fn;
+ return 0;
+}
+
+int btrfs_is_filesystem(int fd) {
+ struct statfs sfs;
+
+ assert(fd >= 0);
+
+ if (fstatfs(fd, &sfs) < 0)
+ return -errno;
+
+ return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
+}
+
+int btrfs_is_subvol(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ /* On btrfs subvolumes always have the inode 256 */
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return 0;
+
+ return btrfs_is_filesystem(fd);
+}
+
+int btrfs_subvol_make(const char *path) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_close_ int fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(path);
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return fd;
+
+ strncpy(args.name, subvolume, sizeof(args.name)-1);
+
+ if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_subvol_make_label(const char *path) {
+ int r;
+
+ assert(path);
+
+ r = mac_selinux_create_file_prepare(path, S_IFDIR);
+ if (r < 0)
+ return r;
+
+ r = btrfs_subvol_make(path);
+ mac_selinux_create_file_clear();
+
+ if (r < 0)
+ return r;
+
+ return mac_smack_fix(path, false, false);
+}
+
+int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+ uint64_t flags, nflags;
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return -EINVAL;
+
+ if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
+ return -errno;
+
+ if (b)
+ nflags = flags | BTRFS_SUBVOL_RDONLY;
+ else
+ nflags = flags & ~BTRFS_SUBVOL_RDONLY;
+
+ if (flags == nflags)
+ return 0;
+
+ if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_subvol_set_read_only(const char *path, bool b) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_subvol_set_read_only_fd(fd, b);
+}
+
+int btrfs_subvol_get_read_only_fd(int fd) {
+ uint64_t flags;
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return -EINVAL;
+
+ if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
+ return -errno;
+
+ return !!(flags & BTRFS_SUBVOL_RDONLY);
+}
+
+int btrfs_reflink(int infd, int outfd) {
+ struct stat st;
+ int r;
+
+ assert(infd >= 0);
+ assert(outfd >= 0);
+
+ /* Make sure we invoke the ioctl on a regular file, so that no
+ * device driver accidentally gets it. */
+
+ if (fstat(outfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
+ r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
+ struct btrfs_ioctl_clone_range_args args = {
+ .src_fd = infd,
+ .src_offset = in_offset,
+ .src_length = sz,
+ .dest_offset = out_offset,
+ };
+ struct stat st;
+ int r;
+
+ assert(infd >= 0);
+ assert(outfd >= 0);
+ assert(sz > 0);
+
+ if (fstat(outfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
+ r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_get_block_device_fd(int fd, dev_t *dev) {
+ struct btrfs_ioctl_fs_info_args fsi = {};
+ uint64_t id;
+ int r;
+
+ assert(fd >= 0);
+ assert(dev);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
+ return -errno;
+
+ /* We won't do this for btrfs RAID */
+ if (fsi.num_devices != 1)
+ return 0;
+
+ for (id = 1; id <= fsi.max_id; id++) {
+ struct btrfs_ioctl_dev_info_args di = {
+ .devid = id,
+ };
+ struct stat st;
+
+ if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
+ if (errno == ENODEV)
+ continue;
+
+ return -errno;
+ }
+
+ if (stat((char*) di.path, &st) < 0)
+ return -errno;
+
+ if (!S_ISBLK(st.st_mode))
+ return -ENODEV;
+
+ if (major(st.st_rdev) == 0)
+ return -ENODEV;
+
+ *dev = st.st_rdev;
+ return 1;
+ }
+
+ return -ENODEV;
+}
+
+int btrfs_get_block_device(const char *path, dev_t *dev) {
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+ assert(dev);
+
+ fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_get_block_device_fd(fd, dev);
+}
+
+int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
+ struct btrfs_ioctl_ino_lookup_args args = {
+ .objectid = BTRFS_FIRST_FREE_OBJECTID
+ };
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
+ return -errno;
+
+ *ret = args.treeid;
+ return 0;
+}
+
+static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
+ assert(args);
+
+ /* the objectid, type, offset together make up the btrfs key,
+ * which is considered a single 136byte integer when
+ * comparing. This call increases the counter by one, dealing
+ * with the overflow between the overflows */
+
+ if (args->key.min_offset < (uint64_t) -1) {
+ args->key.min_offset++;
+ return true;
+ }
+
+ if (args->key.min_type < (uint8_t) -1) {
+ args->key.min_type++;
+ args->key.min_offset = 0;
+ return true;
+ }
+
+ if (args->key.min_objectid < (uint64_t) -1) {
+ args->key.min_objectid++;
+ args->key.min_offset = 0;
+ args->key.min_type = 0;
+ return true;
+ }
+
+ return 0;
+}
+
+static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
+ assert(args);
+ assert(h);
+
+ args->key.min_objectid = h->objectid;
+ args->key.min_type = h->type;
+ args->key.min_offset = h->offset;
+}
+
+static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
+ assert(args);
+
+ /* Compare min and max */
+
+ if (args->key.min_objectid < args->key.max_objectid)
+ return -1;
+ if (args->key.min_objectid > args->key.max_objectid)
+ return 1;
+
+ if (args->key.min_type < args->key.max_type)
+ return -1;
+ if (args->key.min_type > args->key.max_type)
+ return 1;
+
+ if (args->key.min_offset < args->key.max_offset)
+ return -1;
+ if (args->key.min_offset > args->key.max_offset)
+ return 1;
+
+ return 0;
+}
+
+#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
+ for ((i) = 0, \
+ (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
+ (i) < (args).key.nr_items; \
+ (i)++, \
+ (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
+
+#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
+ ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
+
+int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
+ struct btrfs_ioctl_search_args args = {
+ /* Tree of tree roots */
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ /* Look precisely for the subvolume items */
+ .key.min_type = BTRFS_ROOT_ITEM_KEY,
+ .key.max_type = BTRFS_ROOT_ITEM_KEY,
+
+ .key.min_offset = 0,
+ .key.max_offset = (uint64_t) -1,
+
+ /* No restrictions on the other components */
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ uint64_t subvol_id;
+ bool found = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+ if (r < 0)
+ return r;
+
+ args.key.min_objectid = args.key.max_objectid = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+
+ const struct btrfs_root_item *ri;
+
+ /* Make sure we start the next search at least from this entry */
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->objectid != subvol_id)
+ continue;
+ if (sh->type != BTRFS_ROOT_ITEM_KEY)
+ continue;
+
+ /* Older versions of the struct lacked the otime setting */
+ if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
+ continue;
+
+ ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
+ (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
+
+ ret->subvol_id = subvol_id;
+ ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
+
+ assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
+ memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
+ memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
+
+ found = true;
+ goto finish;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+finish:
+ if (!found)
+ return -ENODATA;
+
+ return 0;
+}
+
+int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
+
+ struct btrfs_ioctl_search_args args = {
+ /* Tree of quota items */
+ .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
+
+ /* The object ID is always 0 */
+ .key.min_objectid = 0,
+ .key.max_objectid = 0,
+
+ /* Look precisely for the quota items */
+ .key.min_type = BTRFS_QGROUP_STATUS_KEY,
+ .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
+
+ /* No restrictions on the other components */
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ uint64_t subvol_id;
+ bool found_info = false, found_limit = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+ if (r < 0)
+ return r;
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+
+ /* Make sure we start the next search at least from this entry */
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->objectid != 0)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ if (sh->type == BTRFS_QGROUP_INFO_KEY) {
+ const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ ret->referenced = le64toh(qii->rfer);
+ ret->exclusive = le64toh(qii->excl);
+
+ found_info = true;
+
+ } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
+ const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ ret->referenced_max = le64toh(qli->max_rfer);
+ ret->exclusive_max = le64toh(qli->max_excl);
+
+ if (ret->referenced_max == 0)
+ ret->referenced_max = (uint64_t) -1;
+ if (ret->exclusive_max == 0)
+ ret->exclusive_max = (uint64_t) -1;
+
+ found_limit = true;
+ }
+
+ if (found_info && found_limit)
+ goto finish;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+finish:
+ if (!found_limit && !found_info)
+ return -ENODATA;
+
+ if (!found_info) {
+ ret->referenced = (uint64_t) -1;
+ ret->exclusive = (uint64_t) -1;
+ }
+
+ if (!found_limit) {
+ ret->referenced_max = (uint64_t) -1;
+ ret->exclusive_max = (uint64_t) -1;
+ }
+
+ return 0;
+}
+
+int btrfs_defrag_fd(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
+ if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_defrag(const char *p) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_defrag_fd(fd);
+}
+
+int btrfs_quota_enable_fd(int fd, bool b) {
+ struct btrfs_ioctl_quota_ctl_args args = {
+ .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
+ };
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_enable(const char *path, bool b) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_quota_enable_fd(fd, b);
+}
+
+int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
+ struct btrfs_ioctl_qgroup_limit_args args = {
+ .lim.max_rfer =
+ referenced_max == (uint64_t) -1 ? 0 :
+ referenced_max == 0 ? 1 : referenced_max,
+ .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
+ };
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_quota_limit_fd(fd, referenced_max);
+}
+
+int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
+ _cleanup_close_ int loop_fd = -1, backing_fd = -1;
+ struct stat st;
+ dev_t dev = 0;
+ int r;
+
+ /* btrfs cannot handle file systems < 16M, hence use this as minimum */
+ if (new_size < 16*1024*1024)
+ new_size = 16*1024*1024;
+
+ r = btrfs_get_block_device_fd(fd, &dev);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENODEV;
+
+ if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
+ return -ENOMEM;
+ r = read_one_line_file(p, &backing);
+ if (r == -ENOENT)
+ return -ENODEV;
+ if (r < 0)
+ return r;
+ if (isempty(backing) || !path_is_absolute(backing))
+ return -ENODEV;
+
+ backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (backing_fd < 0)
+ return -errno;
+
+ if (fstat(backing_fd, &st) < 0)
+ return -errno;
+ if (!S_ISREG(st.st_mode))
+ return -ENODEV;
+
+ if (new_size == (uint64_t) st.st_size)
+ return 0;
+
+ if (grow_only && new_size < (uint64_t) st.st_size)
+ return -EINVAL;
+
+ if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
+ return -ENOMEM;
+ loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (loop_fd < 0)
+ return -errno;
+
+ if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
+ return -EINVAL;
+
+ if (new_size < (uint64_t) st.st_size) {
+ /* Decrease size: first decrease btrfs size, then shorten loopback */
+ if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
+ return -errno;
+ }
+
+ if (ftruncate(backing_fd, new_size) < 0)
+ return -errno;
+
+ if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
+ return -errno;
+
+ if (new_size > (uint64_t) st.st_size) {
+ /* Increase size: first enlarge loopback, then increase btrfs size */
+ if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
+ return -errno;
+ }
+
+ /* Make sure the free disk space is correctly updated for both file systems */
+ (void) fsync(fd);
+ (void) fsync(backing_fd);
+
+ return 1;
+}
+
+int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_resize_loopback_fd(fd, new_size, grow_only);
+}
+
+static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
+ struct btrfs_ioctl_search_args args = {
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
+ .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
+
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ struct btrfs_ioctl_vol_args vol_args = {};
+ _cleanup_close_ int subvol_fd = -1;
+ struct stat st;
+ bool made_writable = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(subvolume);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -EINVAL;
+
+ /* First, try to remove the subvolume. If it happens to be
+ * already empty, this will just work. */
+ strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
+ return 0;
+ if (!recursive || errno != ENOTEMPTY)
+ return -errno;
+
+ /* OK, the subvolume is not empty, let's look for child
+ * subvolumes, and remove them, first */
+ subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvol_fd < 0)
+ return -errno;
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ _cleanup_free_ char *p = NULL;
+ const struct btrfs_root_ref *ref;
+ struct btrfs_ioctl_ino_lookup_args ino_args;
+
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
+ if (!p)
+ return -ENOMEM;
+
+ zero(ino_args);
+ ino_args.treeid = subvol_id;
+ ino_args.objectid = htole64(ref->dirid);
+
+ if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
+ return -errno;
+
+ if (!made_writable) {
+ r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
+ if (r < 0)
+ return r;
+
+ made_writable = true;
+ }
+
+ if (isempty(ino_args.name))
+ /* Subvolume is in the top-level
+ * directory of the subvolume. */
+ r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
+ else {
+ _cleanup_close_ int child_fd = -1;
+
+ /* Subvolume is somewhere further down,
+ * hence we need to open the
+ * containing directory first */
+
+ child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (child_fd < 0)
+ return -errno;
+
+ r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
+ }
+ if (r < 0)
+ return r;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ /* OK, the child subvolumes should all be gone now, let's try
+ * again to remove the subvolume */
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_subvol_remove(const char *path, bool recursive) {
+ _cleanup_close_ int fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(path);
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return fd;
+
+ return subvol_remove_children(fd, subvolume, 0, recursive);
+}
+
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
+ return subvol_remove_children(fd, subvolume, 0, recursive);
+}
+
+static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
+
+ struct btrfs_ioctl_search_args args = {
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
+ .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
+
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ struct btrfs_ioctl_vol_args_v2 vol_args = {
+ .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
+ .fd = old_fd,
+ };
+ int r;
+ _cleanup_close_ int subvolume_fd = -1;
+
+ assert(old_fd >= 0);
+ assert(new_fd >= 0);
+ assert(subvolume);
+
+ strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
+ vol_args.fd = old_fd;
+
+ if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
+ return -errno;
+
+ if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
+ return 0;
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
+ struct btrfs_ioctl_ino_lookup_args ino_args;
+ const struct btrfs_root_ref *ref;
+ _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
+
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
+ if (!p)
+ return -ENOMEM;
+
+ zero(ino_args);
+ ino_args.treeid = subvol_id;
+ ino_args.objectid = htole64(ref->dirid);
+
+ if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
+ return -errno;
+
+ /* The kernel returns an empty name if the
+ * subvolume is in the top-level directory,
+ * and otherwise appends a slash, so that we
+ * can just concatenate easily here, without
+ * adding a slash. */
+ c = strappend(ino_args.name, p);
+ if (!c)
+ return -ENOMEM;
+
+ old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (old_child_fd < 0)
+ return -errno;
+
+ np = strjoin(subvolume, "/", ino_args.name, NULL);
+ if (!np)
+ return -ENOMEM;
+
+ new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (new_child_fd < 0)
+ return -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ /* If the snapshot is read-only we
+ * need to mark it writable
+ * temporarily, to put the subsnapshot
+ * into place. */
+
+ if (subvolume_fd < 0) {
+ subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvolume_fd < 0)
+ return -errno;
+ }
+
+ r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
+ if (r < 0)
+ return r;
+ }
+
+ /* When btrfs clones the subvolumes, child
+ * subvolumes appear as directories. Remove
+ * them, so that we can create a new snapshot
+ * in their place */
+ if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
+ int k = -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY)
+ (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+
+ return k;
+ }
+
+ r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
+
+ /* Restore the readonly flag */
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ int k;
+
+ k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+ if (r >= 0 && k < 0)
+ return k;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ return 0;
+}
+
+int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
+ _cleanup_close_ int new_fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(old_fd >= 0);
+ assert(new_path);
+
+ r = btrfs_is_subvol(old_fd);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
+ return -EISDIR;
+
+ r = btrfs_subvol_make(new_path);
+ if (r < 0)
+ return r;
+
+ r = copy_directory_fd(old_fd, new_path, true);
+ if (r < 0) {
+ btrfs_subvol_remove(new_path, false);
+ return r;
+ }
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ r = btrfs_subvol_set_read_only(new_path, true);
+ if (r < 0) {
+ btrfs_subvol_remove(new_path, false);
+ return r;
+ }
+ }
+
+ return 0;
+ }
+
+ r = extract_subvolume_name(new_path, &subvolume);
+ if (r < 0)
+ return r;
+
+ new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (new_fd < 0)
+ return new_fd;
+
+ return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
+}
+
+int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
+ _cleanup_close_ int old_fd = -1;
+
+ assert(old_path);
+ assert(new_path);
+
+ old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (old_fd < 0)
+ return -errno;
+
+ return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
+}
diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
new file mode 100644
index 0000000..a7eb895
--- /dev/null
+++ b/src/basic/btrfs-util.h
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "time-util.h"
+
+typedef struct BtrfsSubvolInfo {
+ uint64_t subvol_id;
+ usec_t otime;
+
+ sd_id128_t uuid;
+ sd_id128_t parent_uuid;
+
+ bool read_only;
+} BtrfsSubvolInfo;
+
+typedef struct BtrfsQuotaInfo {
+ uint64_t referenced;
+ uint64_t exclusive;
+ uint64_t referenced_max;
+ uint64_t exclusive_max;
+} BtrfsQuotaInfo;
+
+typedef enum BtrfsSnapshotFlags {
+ BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
+ BTRFS_SNAPSHOT_READ_ONLY = 2,
+ BTRFS_SNAPSHOT_RECURSIVE = 4,
+} BtrfsSnapshotFlags;
+
+int btrfs_is_filesystem(int fd);
+int btrfs_is_subvol(int fd);
+
+int btrfs_subvol_make(const char *path);
+int btrfs_subvol_make_label(const char *path);
+
+int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
+int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
+
+int btrfs_subvol_set_read_only_fd(int fd, bool b);
+int btrfs_subvol_set_read_only(const char *path, bool b);
+int btrfs_subvol_get_read_only_fd(int fd);
+int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
+int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
+int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
+
+int btrfs_reflink(int infd, int outfd);
+int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
+
+int btrfs_get_block_device_fd(int fd, dev_t *dev);
+int btrfs_get_block_device(const char *path, dev_t *dev);
+
+int btrfs_defrag_fd(int fd);
+int btrfs_defrag(const char *p);
+
+int btrfs_quota_enable_fd(int fd, bool b);
+int btrfs_quota_enable(const char *path, bool b);
+
+int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
+int btrfs_quota_limit(const char *path, uint64_t referenced_max);
+
+int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
+int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
+
+int btrfs_subvol_remove(const char *path, bool recursive);
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
diff --git a/src/basic/build.h b/src/basic/build.h
new file mode 100644
index 0000000..24873ab
--- /dev/null
+++ b/src/basic/build.h
@@ -0,0 +1,157 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/>.
+***/
+
+#ifdef HAVE_PAM
+#define _PAM_FEATURE_ "+PAM"
+#else
+#define _PAM_FEATURE_ "-PAM"
+#endif
+
+#ifdef HAVE_AUDIT
+#define _AUDIT_FEATURE_ "+AUDIT"
+#else
+#define _AUDIT_FEATURE_ "-AUDIT"
+#endif
+
+#ifdef HAVE_SELINUX
+#define _SELINUX_FEATURE_ "+SELINUX"
+#else
+#define _SELINUX_FEATURE_ "-SELINUX"
+#endif
+
+#ifdef HAVE_APPARMOR
+#define _APPARMOR_FEATURE_ "+APPARMOR"
+#else
+#define _APPARMOR_FEATURE_ "-APPARMOR"
+#endif
+
+#ifdef HAVE_IMA
+#define _IMA_FEATURE_ "+IMA"
+#else
+#define _IMA_FEATURE_ "-IMA"
+#endif
+
+#ifdef HAVE_SMACK
+#define _SMACK_FEATURE_ "+SMACK"
+#else
+#define _SMACK_FEATURE_ "-SMACK"
+#endif
+
+#ifdef HAVE_SYSV_COMPAT
+#define _SYSVINIT_FEATURE_ "+SYSVINIT"
+#else
+#define _SYSVINIT_FEATURE_ "-SYSVINIT"
+#endif
+
+#ifdef HAVE_UTMP
+#define _UTMP_FEATURE_ "+UTMP"
+#else
+#define _UTMP_FEATURE_ "-UTMP"
+#endif
+
+#ifdef HAVE_LIBCRYPTSETUP
+#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP"
+#else
+#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP"
+#endif
+
+#ifdef HAVE_GCRYPT
+#define _GCRYPT_FEATURE_ "+GCRYPT"
+#else
+#define _GCRYPT_FEATURE_ "-GCRYPT"
+#endif
+
+#ifdef HAVE_GNUTLS
+#define _GNUTLS_FEATURE_ "+GNUTLS"
+#else
+#define _GNUTLS_FEATURE_ "-GNUTLS"
+#endif
+
+#ifdef HAVE_ACL
+#define _ACL_FEATURE_ "+ACL"
+#else
+#define _ACL_FEATURE_ "-ACL"
+#endif
+
+#ifdef HAVE_XZ
+#define _XZ_FEATURE_ "+XZ"
+#else
+#define _XZ_FEATURE_ "-XZ"
+#endif
+
+#ifdef HAVE_LZ4
+#define _LZ4_FEATURE_ "+LZ4"
+#else
+#define _LZ4_FEATURE_ "-LZ4"
+#endif
+
+#ifdef HAVE_SECCOMP
+#define _SECCOMP_FEATURE_ "+SECCOMP"
+#else
+#define _SECCOMP_FEATURE_ "-SECCOMP"
+#endif
+
+#ifdef HAVE_BLKID
+#define _BLKID_FEATURE_ "+BLKID"
+#else
+#define _BLKID_FEATURE_ "-BLKID"
+#endif
+
+#ifdef HAVE_ELFUTILS
+#define _ELFUTILS_FEATURE_ "+ELFUTILS"
+#else
+#define _ELFUTILS_FEATURE_ "-ELFUTILS"
+#endif
+
+#ifdef HAVE_KMOD
+#define _KMOD_FEATURE_ "+KMOD"
+#else
+#define _KMOD_FEATURE_ "-KMOD"
+#endif
+
+#ifdef HAVE_LIBIDN
+#define _IDN_FEATURE_ "+IDN"
+#else
+#define _IDN_FEATURE_ "-IDN"
+#endif
+
+#define SYSTEMD_FEATURES \
+ _PAM_FEATURE_ " " \
+ _AUDIT_FEATURE_ " " \
+ _SELINUX_FEATURE_ " " \
+ _IMA_FEATURE_ " " \
+ _APPARMOR_FEATURE_ " " \
+ _SMACK_FEATURE_ " " \
+ _SYSVINIT_FEATURE_ " " \
+ _UTMP_FEATURE_ " " \
+ _LIBCRYPTSETUP_FEATURE_ " " \
+ _GCRYPT_FEATURE_ " " \
+ _GNUTLS_FEATURE_ " " \
+ _ACL_FEATURE_ " " \
+ _XZ_FEATURE_ " " \
+ _LZ4_FEATURE_ " " \
+ _SECCOMP_FEATURE_ " " \
+ _BLKID_FEATURE_ " " \
+ _ELFUTILS_FEATURE_ " " \
+ _KMOD_FEATURE_ " " \
+ _IDN_FEATURE_
diff --git a/src/basic/bus-label.c b/src/basic/bus-label.c
new file mode 100644
index 0000000..ccc9f2b
--- /dev/null
+++ b/src/basic/bus-label.c
@@ -0,0 +1,100 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 "util.h"
+#include "macro.h"
+
+#include "bus-label.h"
+
+char *bus_label_escape(const char *s) {
+ char *r, *t;
+ const char *f;
+
+ assert_return(s, NULL);
+
+ /* Escapes all chars that D-Bus' object path cannot deal
+ * with. Can be reversed with bus_path_unescape(). We special
+ * case the empty string. */
+
+ if (*s == 0)
+ return strdup("_");
+
+ r = new(char, strlen(s)*3 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ /* Escape everything that is not a-zA-Z0-9. We also
+ * escape 0-9 if it's the first character */
+
+ if (!(*f >= 'A' && *f <= 'Z') &&
+ !(*f >= 'a' && *f <= 'z') &&
+ !(f > s && *f >= '0' && *f <= '9')) {
+ *(t++) = '_';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+char *bus_label_unescape_n(const char *f, size_t l) {
+ char *r, *t;
+ size_t i;
+
+ assert_return(f, NULL);
+
+ /* Special case for the empty string */
+ if (l == 1 && *f == '_')
+ return strdup("");
+
+ r = new(char, l + 1);
+ if (!r)
+ return NULL;
+
+ for (i = 0, t = r; i < l; ++i) {
+ if (f[i] == '_') {
+ int a, b;
+
+ if (l - i < 3 ||
+ (a = unhexchar(f[i + 1])) < 0 ||
+ (b = unhexchar(f[i + 2])) < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '_';
+ } else {
+ *(t++) = (char) ((a << 4) | b);
+ i += 2;
+ }
+ } else
+ *(t++) = f[i];
+ }
+
+ *t = 0;
+
+ return r;
+}
diff --git a/src/basic/bus-label.h b/src/basic/bus-label.h
new file mode 100644
index 0000000..ed1dc4e
--- /dev/null
+++ b/src/basic/bus-label.h
@@ -0,0 +1,32 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <string.h>
+
+char *bus_label_escape(const char *s);
+char *bus_label_unescape_n(const char *f, size_t l);
+
+static inline char *bus_label_unescape(const char *f) {
+ return bus_label_unescape_n(f, f ? strlen(f) : 0);
+}
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
new file mode 100644
index 0000000..2fde3e1
--- /dev/null
+++ b/src/basic/calendarspec.c
@@ -0,0 +1,1006 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <stdlib.h>
+#include <string.h>
+
+#include "calendarspec.h"
+
+#define BITS_WEEKDAYS 127
+
+static void free_chain(CalendarComponent *c) {
+ CalendarComponent *n;
+
+ while (c) {
+ n = c->next;
+ free(c);
+ c = n;
+ }
+}
+
+void calendar_spec_free(CalendarSpec *c) {
+
+ if (!c)
+ return;
+
+ free_chain(c->year);
+ free_chain(c->month);
+ free_chain(c->day);
+ free_chain(c->hour);
+ free_chain(c->minute);
+ free_chain(c->second);
+
+ free(c);
+}
+
+static int component_compare(const void *_a, const void *_b) {
+ CalendarComponent * const *a = _a, * const *b = _b;
+
+ if ((*a)->value < (*b)->value)
+ return -1;
+ if ((*a)->value > (*b)->value)
+ return 1;
+
+ if ((*a)->repeat < (*b)->repeat)
+ return -1;
+ if ((*a)->repeat > (*b)->repeat)
+ return 1;
+
+ return 0;
+}
+
+static void sort_chain(CalendarComponent **c) {
+ unsigned n = 0, k;
+ CalendarComponent **b, *i, **j, *next;
+
+ assert(c);
+
+ for (i = *c; i; i = i->next)
+ n++;
+
+ if (n <= 1)
+ return;
+
+ j = b = alloca(sizeof(CalendarComponent*) * n);
+ for (i = *c; i; i = i->next)
+ *(j++) = i;
+
+ qsort(b, n, sizeof(CalendarComponent*), component_compare);
+
+ b[n-1]->next = NULL;
+ next = b[n-1];
+
+ /* Drop non-unique entries */
+ for (k = n-1; k > 0; k--) {
+ if (b[k-1]->value == next->value &&
+ b[k-1]->repeat == next->repeat) {
+ free(b[k-1]);
+ continue;
+ }
+
+ b[k-1]->next = next;
+ next = b[k-1];
+ }
+
+ *c = next;
+}
+
+static void fix_year(CalendarComponent *c) {
+ /* Turns 12 → 2012, 89 → 1989 */
+
+ while(c) {
+ CalendarComponent *n = c->next;
+
+ if (c->value >= 0 && c->value < 70)
+ c->value += 2000;
+
+ if (c->value >= 70 && c->value < 100)
+ c->value += 1900;
+
+ c = n;
+ }
+}
+
+int calendar_spec_normalize(CalendarSpec *c) {
+ assert(c);
+
+ if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
+ c->weekdays_bits = -1;
+
+ fix_year(c->year);
+
+ sort_chain(&c->year);
+ sort_chain(&c->month);
+ sort_chain(&c->day);
+ sort_chain(&c->hour);
+ sort_chain(&c->minute);
+ sort_chain(&c->second);
+
+ return 0;
+}
+
+_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
+ if (!c)
+ return true;
+
+ if (c->value < from || c->value > to)
+ return false;
+
+ if (c->value + c->repeat > to)
+ return false;
+
+ if (c->next)
+ return chain_valid(c->next, from, to);
+
+ return true;
+}
+
+_pure_ bool calendar_spec_valid(CalendarSpec *c) {
+ assert(c);
+
+ if (c->weekdays_bits > BITS_WEEKDAYS)
+ return false;
+
+ if (!chain_valid(c->year, 1970, 2199))
+ return false;
+
+ if (!chain_valid(c->month, 1, 12))
+ return false;
+
+ if (!chain_valid(c->day, 1, 31))
+ return false;
+
+ if (!chain_valid(c->hour, 0, 23))
+ return false;
+
+ if (!chain_valid(c->minute, 0, 59))
+ return false;
+
+ if (!chain_valid(c->second, 0, 59))
+ return false;
+
+ return true;
+}
+
+static void format_weekdays(FILE *f, const CalendarSpec *c) {
+ static const char *const days[] = {
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun"
+ };
+
+ int l, x;
+ bool need_colon = false;
+
+ assert(f);
+ assert(c);
+ assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
+
+ for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
+
+ if (c->weekdays_bits & (1 << x)) {
+
+ if (l < 0) {
+ if (need_colon)
+ fputc(',', f);
+ else
+ need_colon = true;
+
+ fputs(days[x], f);
+ l = x;
+ }
+
+ } else if (l >= 0) {
+
+ if (x > l + 1) {
+ fputc(x > l + 2 ? '-' : ',', f);
+ fputs(days[x-1], f);
+ }
+
+ l = -1;
+ }
+ }
+
+ if (l >= 0 && x > l + 1) {
+ fputc(x > l + 2 ? '-' : ',', f);
+ fputs(days[x-1], f);
+ }
+}
+
+static void format_chain(FILE *f, int space, const CalendarComponent *c) {
+ assert(f);
+
+ if (!c) {
+ fputc('*', f);
+ return;
+ }
+
+ assert(c->value >= 0);
+ fprintf(f, "%0*i", space, c->value);
+
+ if (c->repeat > 0)
+ fprintf(f, "/%i", c->repeat);
+
+ if (c->next) {
+ fputc(',', f);
+ format_chain(f, space, c->next);
+ }
+}
+
+int calendar_spec_to_string(const CalendarSpec *c, char **p) {
+ char *buf = NULL;
+ size_t sz = 0;
+ FILE *f;
+
+ assert(c);
+ assert(p);
+
+ f = open_memstream(&buf, &sz);
+ if (!f)
+ return -ENOMEM;
+
+ if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
+ format_weekdays(f, c);
+ fputc(' ', f);
+ }
+
+ format_chain(f, 4, c->year);
+ fputc('-', f);
+ format_chain(f, 2, c->month);
+ fputc('-', f);
+ format_chain(f, 2, c->day);
+ fputc(' ', f);
+ format_chain(f, 2, c->hour);
+ fputc(':', f);
+ format_chain(f, 2, c->minute);
+ fputc(':', f);
+ format_chain(f, 2, c->second);
+
+ fflush(f);
+
+ if (ferror(f)) {
+ free(buf);
+ fclose(f);
+ return -ENOMEM;
+ }
+
+ fclose(f);
+
+ *p = buf;
+ return 0;
+}
+
+static int parse_weekdays(const char **p, CalendarSpec *c) {
+ static const struct {
+ const char *name;
+ const int nr;
+ } day_nr[] = {
+ { "Monday", 0 },
+ { "Mon", 0 },
+ { "Tuesday", 1 },
+ { "Tue", 1 },
+ { "Wednesday", 2 },
+ { "Wed", 2 },
+ { "Thursday", 3 },
+ { "Thu", 3 },
+ { "Friday", 4 },
+ { "Fri", 4 },
+ { "Saturday", 5 },
+ { "Sat", 5 },
+ { "Sunday", 6 },
+ { "Sun", 6 }
+ };
+
+ int l = -1;
+ bool first = true;
+
+ assert(p);
+ assert(*p);
+ assert(c);
+
+ for (;;) {
+ unsigned i;
+
+ if (!first && **p == ' ')
+ return 0;
+
+ for (i = 0; i < ELEMENTSOF(day_nr); i++) {
+ size_t skip;
+
+ if (!startswith_no_case(*p, day_nr[i].name))
+ continue;
+
+ skip = strlen(day_nr[i].name);
+
+ if ((*p)[skip] != '-' &&
+ (*p)[skip] != ',' &&
+ (*p)[skip] != ' ' &&
+ (*p)[skip] != 0)
+ return -EINVAL;
+
+ c->weekdays_bits |= 1 << day_nr[i].nr;
+
+ if (l >= 0) {
+ int j;
+
+ if (l > day_nr[i].nr)
+ return -EINVAL;
+
+ for (j = l + 1; j < day_nr[i].nr; j++)
+ c->weekdays_bits |= 1 << j;
+ }
+
+ *p += skip;
+ break;
+ }
+
+ /* Couldn't find this prefix, so let's assume the
+ weekday was not specified and let's continue with
+ the date */
+ if (i >= ELEMENTSOF(day_nr))
+ return first ? 0 : -EINVAL;
+
+ /* We reached the end of the string */
+ if (**p == 0)
+ return 0;
+
+ /* We reached the end of the weekday spec part */
+ if (**p == ' ') {
+ *p += strspn(*p, " ");
+ return 0;
+ }
+
+ if (**p == '-') {
+ if (l >= 0)
+ return -EINVAL;
+
+ l = day_nr[i].nr;
+ } else
+ l = -1;
+
+ *p += 1;
+ first = false;
+ }
+}
+
+static int prepend_component(const char **p, CalendarComponent **c) {
+ unsigned long value, repeat = 0;
+ char *e = NULL, *ee = NULL;
+ CalendarComponent *cc;
+
+ assert(p);
+ assert(c);
+
+ errno = 0;
+ value = strtoul(*p, &e, 10);
+ if (errno > 0)
+ return -errno;
+ if (e == *p)
+ return -EINVAL;
+ if ((unsigned long) (int) value != value)
+ return -ERANGE;
+
+ if (*e == '/') {
+ repeat = strtoul(e+1, &ee, 10);
+ if (errno > 0)
+ return -errno;
+ if (ee == e+1)
+ return -EINVAL;
+ if ((unsigned long) (int) repeat != repeat)
+ return -ERANGE;
+ if (repeat <= 0)
+ return -ERANGE;
+
+ e = ee;
+ }
+
+ if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
+ return -EINVAL;
+
+ cc = new0(CalendarComponent, 1);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->value = value;
+ cc->repeat = repeat;
+ cc->next = *c;
+
+ *p = e;
+ *c = cc;
+
+ if (*e ==',') {
+ *p += 1;
+ return prepend_component(p, c);
+ }
+
+ return 0;
+}
+
+static int parse_chain(const char **p, CalendarComponent **c) {
+ const char *t;
+ CalendarComponent *cc = NULL;
+ int r;
+
+ assert(p);
+ assert(c);
+
+ t = *p;
+
+ if (t[0] == '*') {
+ *p = t + 1;
+ *c = NULL;
+ return 0;
+ }
+
+ r = prepend_component(&t, &cc);
+ if (r < 0) {
+ free_chain(cc);
+ return r;
+ }
+
+ *p = t;
+ *c = cc;
+ return 0;
+}
+
+static int const_chain(int value, CalendarComponent **c) {
+ CalendarComponent *cc = NULL;
+
+ assert(c);
+
+ cc = new0(CalendarComponent, 1);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->value = value;
+ cc->repeat = 0;
+ cc->next = *c;
+
+ *c = cc;
+
+ return 0;
+}
+
+static int parse_date(const char **p, CalendarSpec *c) {
+ const char *t;
+ int r;
+ CalendarComponent *first, *second, *third;
+
+ assert(p);
+ assert(*p);
+ assert(c);
+
+ t = *p;
+
+ if (*t == 0)
+ return 0;
+
+ r = parse_chain(&t, &first);
+ if (r < 0)
+ return r;
+
+ /* Already the end? A ':' as separator? In that case this was a time, not a date */
+ if (*t == 0 || *t == ':') {
+ free_chain(first);
+ return 0;
+ }
+
+ if (*t != '-') {
+ free_chain(first);
+ return -EINVAL;
+ }
+
+ t++;
+ r = parse_chain(&t, &second);
+ if (r < 0) {
+ free_chain(first);
+ return r;
+ }
+
+ /* Got two parts, hence it's month and day */
+ if (*t == ' ' || *t == 0) {
+ *p = t + strspn(t, " ");
+ c->month = first;
+ c->day = second;
+ return 0;
+ }
+
+ if (*t != '-') {
+ free_chain(first);
+ free_chain(second);
+ return -EINVAL;
+ }
+
+ t++;
+ r = parse_chain(&t, &third);
+ if (r < 0) {
+ free_chain(first);
+ free_chain(second);
+ return r;
+ }
+
+ /* Got tree parts, hence it is year, month and day */
+ if (*t == ' ' || *t == 0) {
+ *p = t + strspn(t, " ");
+ c->year = first;
+ c->month = second;
+ c->day = third;
+ return 0;
+ }
+
+ free_chain(first);
+ free_chain(second);
+ free_chain(third);
+ return -EINVAL;
+}
+
+static int parse_time(const char **p, CalendarSpec *c) {
+ CalendarComponent *h = NULL, *m = NULL, *s = NULL;
+ const char *t;
+ int r;
+
+ assert(p);
+ assert(*p);
+ assert(c);
+
+ t = *p;
+
+ if (*t == 0) {
+ /* If no time is specified at all, but a date of some
+ * kind, then this means 00:00:00 */
+ if (c->day || c->weekdays_bits > 0)
+ goto null_hour;
+
+ goto finish;
+ }
+
+ r = parse_chain(&t, &h);
+ if (r < 0)
+ goto fail;
+
+ if (*t != ':') {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ t++;
+ r = parse_chain(&t, &m);
+ if (r < 0)
+ goto fail;
+
+ /* Already at the end? Then it's hours and minutes, and seconds are 0 */
+ if (*t == 0) {
+ if (m != NULL)
+ goto null_second;
+
+ goto finish;
+ }
+
+ if (*t != ':') {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ t++;
+ r = parse_chain(&t, &s);
+ if (r < 0)
+ goto fail;
+
+ /* At the end? Then it's hours, minutes and seconds */
+ if (*t == 0)
+ goto finish;
+
+ r = -EINVAL;
+ goto fail;
+
+null_hour:
+ r = const_chain(0, &h);
+ if (r < 0)
+ goto fail;
+
+ r = const_chain(0, &m);
+ if (r < 0)
+ goto fail;
+
+null_second:
+ r = const_chain(0, &s);
+ if (r < 0)
+ goto fail;
+
+finish:
+ *p = t;
+ c->hour = h;
+ c->minute = m;
+ c->second = s;
+ return 0;
+
+fail:
+ free_chain(h);
+ free_chain(m);
+ free_chain(s);
+ return r;
+}
+
+int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
+ CalendarSpec *c;
+ int r;
+
+ assert(p);
+ assert(spec);
+
+ if (isempty(p))
+ return -EINVAL;
+
+ c = new0(CalendarSpec, 1);
+ if (!c)
+ return -ENOMEM;
+
+ if (strcaseeq(p, "minutely")) {
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "hourly")) {
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "daily")) {
+ r = const_chain(0, &c->hour);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "monthly")) {
+ r = const_chain(1, &c->day);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->hour);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "annually") ||
+ strcaseeq(p, "yearly") ||
+ strcaseeq(p, "anually") /* backwards compatibility */ ) {
+
+ r = const_chain(1, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(1, &c->day);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->hour);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "weekly")) {
+
+ c->weekdays_bits = 1;
+
+ r = const_chain(0, &c->hour);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "quarterly")) {
+
+ r = const_chain(1, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(4, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(7, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(10, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(1, &c->day);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->hour);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else if (strcaseeq(p, "biannually") ||
+ strcaseeq(p, "bi-annually") ||
+ strcaseeq(p, "semiannually") ||
+ strcaseeq(p, "semi-annually")) {
+
+ r = const_chain(1, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(7, &c->month);
+ if (r < 0)
+ goto fail;
+ r = const_chain(1, &c->day);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->hour);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->minute);
+ if (r < 0)
+ goto fail;
+ r = const_chain(0, &c->second);
+ if (r < 0)
+ goto fail;
+
+ } else {
+ r = parse_weekdays(&p, c);
+ if (r < 0)
+ goto fail;
+
+ r = parse_date(&p, c);
+ if (r < 0)
+ goto fail;
+
+ r = parse_time(&p, c);
+ if (r < 0)
+ goto fail;
+
+ if (*p != 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+ }
+
+ r = calendar_spec_normalize(c);
+ if (r < 0)
+ goto fail;
+
+ if (!calendar_spec_valid(c)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ *spec = c;
+ return 0;
+
+fail:
+ calendar_spec_free(c);
+ return r;
+}
+
+static int find_matching_component(const CalendarComponent *c, int *val) {
+ const CalendarComponent *n;
+ int d = -1;
+ bool d_set = false;
+ int r;
+
+ assert(val);
+
+ if (!c)
+ return 0;
+
+ while (c) {
+ n = c->next;
+
+ if (c->value >= *val) {
+
+ if (!d_set || c->value < d) {
+ d = c->value;
+ d_set = true;
+ }
+
+ } else if (c->repeat > 0) {
+ int k;
+
+ k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
+
+ if (!d_set || k < d) {
+ d = k;
+ d_set = true;
+ }
+ }
+
+ c = n;
+ }
+
+ if (!d_set)
+ return -ENOENT;
+
+ r = *val != d;
+ *val = d;
+ return r;
+}
+
+static bool tm_out_of_bounds(const struct tm *tm) {
+ struct tm t;
+ assert(tm);
+
+ t = *tm;
+
+ if (mktime(&t) == (time_t) -1)
+ return true;
+
+ /* Did any normalization take place? If so, it was out of bounds before */
+ return
+ t.tm_year != tm->tm_year ||
+ t.tm_mon != tm->tm_mon ||
+ t.tm_mday != tm->tm_mday ||
+ t.tm_hour != tm->tm_hour ||
+ t.tm_min != tm->tm_min ||
+ t.tm_sec != tm->tm_sec;
+}
+
+static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
+ struct tm t;
+ int k;
+
+ if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
+ return true;
+
+ t = *tm;
+ if (mktime(&t) == (time_t) -1)
+ return false;
+
+ k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
+ return (weekdays_bits & (1 << k));
+}
+
+static int find_next(const CalendarSpec *spec, struct tm *tm) {
+ struct tm c;
+ int r;
+
+ assert(spec);
+ assert(tm);
+
+ c = *tm;
+
+ for (;;) {
+ /* Normalize the current date */
+ mktime(&c);
+ c.tm_isdst = -1;
+
+ c.tm_year += 1900;
+ r = find_matching_component(spec->year, &c.tm_year);
+ c.tm_year -= 1900;
+
+ if (r > 0) {
+ c.tm_mon = 0;
+ c.tm_mday = 1;
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ }
+ if (r < 0 || tm_out_of_bounds(&c))
+ return r;
+
+ c.tm_mon += 1;
+ r = find_matching_component(spec->month, &c.tm_mon);
+ c.tm_mon -= 1;
+
+ if (r > 0) {
+ c.tm_mday = 1;
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ }
+ if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_year ++;
+ c.tm_mon = 0;
+ c.tm_mday = 1;
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ continue;
+ }
+
+ r = find_matching_component(spec->day, &c.tm_mday);
+ if (r > 0)
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_mon ++;
+ c.tm_mday = 1;
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ continue;
+ }
+
+ if (!matches_weekday(spec->weekdays_bits, &c)) {
+ c.tm_mday++;
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ continue;
+ }
+
+ r = find_matching_component(spec->hour, &c.tm_hour);
+ if (r > 0)
+ c.tm_min = c.tm_sec = 0;
+ if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_mday ++;
+ c.tm_hour = c.tm_min = c.tm_sec = 0;
+ continue;
+ }
+
+ r = find_matching_component(spec->minute, &c.tm_min);
+ if (r > 0)
+ c.tm_sec = 0;
+ if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_hour ++;
+ c.tm_min = c.tm_sec = 0;
+ continue;
+ }
+
+ r = find_matching_component(spec->second, &c.tm_sec);
+ if (r < 0 || tm_out_of_bounds(&c)) {
+ c.tm_min ++;
+ c.tm_sec = 0;
+ continue;
+ }
+
+
+ *tm = c;
+ return 0;
+ }
+}
+
+int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
+ struct tm tm;
+ time_t t;
+ int r;
+
+ assert(spec);
+ assert(next);
+
+ t = (time_t) (usec / USEC_PER_SEC) + 1;
+ assert_se(localtime_r(&t, &tm));
+
+ r = find_next(spec, &tm);
+ if (r < 0)
+ return r;
+
+ t = mktime(&tm);
+ if (t == (time_t) -1)
+ return -EINVAL;
+
+ *next = (usec_t) t * USEC_PER_SEC;
+ return 0;
+}
diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h
new file mode 100644
index 0000000..7baf318
--- /dev/null
+++ b/src/basic/calendarspec.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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/>.
+***/
+
+/* A structure for specifying (possibly repetitive) points in calendar
+ * time, a la cron */
+
+#include <stdbool.h>
+#include "util.h"
+
+typedef struct CalendarComponent {
+ int value;
+ int repeat;
+
+ struct CalendarComponent *next;
+} CalendarComponent;
+
+typedef struct CalendarSpec {
+ int weekdays_bits;
+
+ CalendarComponent *year;
+ CalendarComponent *month;
+ CalendarComponent *day;
+
+ CalendarComponent *hour;
+ CalendarComponent *minute;
+ CalendarComponent *second;
+} CalendarSpec;
+
+void calendar_spec_free(CalendarSpec *c);
+
+int calendar_spec_normalize(CalendarSpec *spec);
+bool calendar_spec_valid(CalendarSpec *spec);
+
+int calendar_spec_to_string(const CalendarSpec *spec, char **p);
+int calendar_spec_from_string(const char *p, CalendarSpec **spec);
+
+int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next);
diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c
new file mode 100644
index 0000000..bd5bffb
--- /dev/null
+++ b/src/basic/cap-list.c
@@ -0,0 +1,65 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <string.h>
+
+#include "util.h"
+#include "cap-list.h"
+#include "missing.h"
+
+static const struct capability_name* lookup_capability(register const char *str, register unsigned int len);
+
+#include "cap-to-name.h"
+#include "cap-from-name.h"
+
+const char *capability_to_name(int id) {
+
+ if (id < 0)
+ return NULL;
+
+ if (id >= (int) ELEMENTSOF(capability_names))
+ return NULL;
+
+ return capability_names[id];
+}
+
+int capability_from_name(const char *name) {
+ const struct capability_name *sc;
+ int r, i;
+
+ assert(name);
+
+ /* Try to parse numeric capability */
+ r = safe_atoi(name, &i);
+ if (r >= 0 && i >= 0)
+ return i;
+
+ /* Try to parse string capability */
+ sc = lookup_capability(name, strlen(name));
+ if (!sc)
+ return -EINVAL;
+
+ return sc->id;
+}
+
+int capability_list_length(void) {
+ return (int) ELEMENTSOF(capability_names);
+}
diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h
new file mode 100644
index 0000000..9824fad
--- /dev/null
+++ b/src/basic/cap-list.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+const char *capability_to_name(int id);
+int capability_from_name(const char *name);
+int capability_list_length(void);
diff --git a/src/basic/capability.c b/src/basic/capability.c
new file mode 100644
index 0000000..58f00e6
--- /dev/null
+++ b/src/basic/capability.c
@@ -0,0 +1,307 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include "grp.h"
+
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+#include "fileio.h"
+#include "capability.h"
+
+int have_effective_cap(int value) {
+ _cleanup_cap_free_ cap_t cap;
+ cap_flag_value_t fv;
+
+ cap = cap_get_proc();
+ if (!cap)
+ return -errno;
+
+ if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
+ return -errno;
+ else
+ return fv == CAP_SET;
+}
+
+unsigned long cap_last_cap(void) {
+ static thread_local unsigned long saved;
+ static thread_local bool valid = false;
+ _cleanup_free_ char *content = NULL;
+ unsigned long p = 0;
+ int r;
+
+ if (valid)
+ return saved;
+
+ /* available since linux-3.2 */
+ r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
+ if (r >= 0) {
+ r = safe_atolu(content, &p);
+ if (r >= 0) {
+ saved = p;
+ valid = true;
+ return p;
+ }
+ }
+
+ /* fall back to syscall-probing for pre linux-3.2 */
+ p = (unsigned long) CAP_LAST_CAP;
+
+ if (prctl(PR_CAPBSET_READ, p) < 0) {
+
+ /* Hmm, look downwards, until we find one that
+ * works */
+ for (p--; p > 0; p --)
+ if (prctl(PR_CAPBSET_READ, p) >= 0)
+ break;
+
+ } else {
+
+ /* Hmm, look upwards, until we find one that doesn't
+ * work */
+ for (;; p++)
+ if (prctl(PR_CAPBSET_READ, p+1) < 0)
+ break;
+ }
+
+ saved = p;
+ valid = true;
+
+ return p;
+}
+
+int capability_bounding_set_drop(uint64_t drop, bool right_now) {
+ _cleanup_cap_free_ cap_t after_cap = NULL;
+ cap_flag_value_t fv;
+ unsigned long i;
+ int r;
+
+ /* If we are run as PID 1 we will lack CAP_SETPCAP by default
+ * in the effective set (yes, the kernel drops that when
+ * executing init!), so get it back temporarily so that we can
+ * call PR_CAPBSET_DROP. */
+
+ after_cap = cap_get_proc();
+ if (!after_cap)
+ return -errno;
+
+ if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
+ return -errno;
+
+ if (fv != CAP_SET) {
+ _cleanup_cap_free_ cap_t temp_cap = NULL;
+ static const cap_value_t v = CAP_SETPCAP;
+
+ temp_cap = cap_dup(after_cap);
+ if (!temp_cap) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (cap_set_proc(temp_cap) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ for (i = 0; i <= cap_last_cap(); i++) {
+
+ if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
+ cap_value_t v;
+
+ /* Drop it from the bounding set */
+ if (prctl(PR_CAPBSET_DROP, i) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ v = (cap_value_t) i;
+
+ /* Also drop it from the inheritable set, so
+ * that anything we exec() loses the
+ * capability for good. */
+ if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* If we shall apply this right now drop it
+ * also from our own capability sets. */
+ if (right_now) {
+ if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
+ cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (cap_set_proc(after_cap) < 0)
+ return -errno;
+
+ return r;
+}
+
+static int drop_from_file(const char *fn, uint64_t drop) {
+ int r, k;
+ uint32_t hi, lo;
+ uint64_t current, after;
+ char *p;
+
+ r = read_one_line_file(fn, &p);
+ if (r < 0)
+ return r;
+
+ assert_cc(sizeof(hi) == sizeof(unsigned));
+ assert_cc(sizeof(lo) == sizeof(unsigned));
+
+ k = sscanf(p, "%u %u", &lo, &hi);
+ free(p);
+
+ if (k != 2)
+ return -EIO;
+
+ current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
+ after = current & ~drop;
+
+ if (current == after)
+ return 0;
+
+ lo = (unsigned) (after & 0xFFFFFFFFULL);
+ hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
+
+ if (asprintf(&p, "%u %u", lo, hi) < 0)
+ return -ENOMEM;
+
+ r = write_string_file(fn, p);
+ free(p);
+
+ return r;
+}
+
+int capability_bounding_set_drop_usermode(uint64_t drop) {
+ int r;
+
+ r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
+ if (r < 0)
+ return r;
+
+ r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
+ if (r < 0)
+ return r;
+
+ return r;
+}
+
+int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
+ _cleanup_cap_free_ cap_t d = NULL;
+ unsigned i, j = 0;
+ int r;
+
+ /* Unfortunately we cannot leave privilege dropping to PID 1
+ * here, since we want to run as user but want to keep some
+ * capabilities. Since file capabilities have been introduced
+ * this cannot be done across exec() anymore, unless our
+ * binary has the capability configured in the file system,
+ * which we want to avoid. */
+
+ if (setresgid(gid, gid, gid) < 0)
+ return log_error_errno(errno, "Failed to change group ID: %m");
+
+ if (setgroups(0, NULL) < 0)
+ return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
+
+ /* Ensure we keep the permitted caps across the setresuid() */
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0)
+ return log_error_errno(errno, "Failed to enable keep capabilities flag: %m");
+
+ r = setresuid(uid, uid, uid);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to change user ID: %m");
+
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0)
+ return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
+
+ /* Drop all caps from the bounding set, except the ones we want */
+ r = capability_bounding_set_drop(~keep_capabilities, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop capabilities: %m");
+
+ /* Now upgrade the permitted caps we still kept to effective caps */
+ d = cap_init();
+ if (!d)
+ return log_oom();
+
+ if (keep_capabilities) {
+ cap_value_t bits[u64log2(keep_capabilities) + 1];
+
+ for (i = 0; i < ELEMENTSOF(bits); i++)
+ if (keep_capabilities & (1ULL << i))
+ bits[j++] = i;
+
+ /* use enough bits */
+ assert(i == 64 || (keep_capabilities >> i) == 0);
+ /* don't use too many bits */
+ assert(keep_capabilities & (1ULL << (i - 1)));
+
+ if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
+ cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) {
+ log_error_errno(errno, "Failed to enable capabilities bits: %m");
+ return -errno;
+ }
+
+ if (cap_set_proc(d) < 0)
+ return log_error_errno(errno, "Failed to increase capabilities: %m");
+ }
+
+ return 0;
+}
+
+int drop_capability(cap_value_t cv) {
+ _cleanup_cap_free_ cap_t tmp_cap = NULL;
+
+ tmp_cap = cap_get_proc();
+ if (!tmp_cap)
+ return -errno;
+
+ if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
+ (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
+ (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
+ return -errno;
+
+ if (cap_set_proc(tmp_cap) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/basic/capability.h b/src/basic/capability.h
new file mode 100644
index 0000000..4eb5c2a
--- /dev/null
+++ b/src/basic/capability.h
@@ -0,0 +1,45 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <sys/capability.h>
+
+#include "util.h"
+
+unsigned long cap_last_cap(void);
+int have_effective_cap(int value);
+int capability_bounding_set_drop(uint64_t drop, bool right_now);
+int capability_bounding_set_drop_usermode(uint64_t drop);
+
+int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
+
+int drop_capability(cap_value_t cv);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free);
+#define _cleanup_cap_free_ _cleanup_(cap_freep)
+
+static inline void cap_free_charpp(char **p) {
+ if (*p)
+ cap_free(*p);
+}
+#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
new file mode 100644
index 0000000..66857f1
--- /dev/null
+++ b/src/basic/cgroup-util.c
@@ -0,0 +1,1917 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ftw.h>
+
+#include "cgroup-util.h"
+#include "set.h"
+#include "macro.h"
+#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "path-util.h"
+#include "unit-name.h"
+#include "fileio.h"
+#include "special.h"
+#include "mkdir.h"
+#include "login-util.h"
+
+int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
+ _cleanup_free_ char *fs = NULL;
+ FILE *f;
+ int r;
+
+ assert(_f);
+
+ r = cg_get_path(controller, path, "cgroup.procs", &fs);
+ if (r < 0)
+ return r;
+
+ f = fopen(fs, "re");
+ if (!f)
+ return -errno;
+
+ *_f = f;
+ return 0;
+}
+
+int cg_read_pid(FILE *f, pid_t *_pid) {
+ unsigned long ul;
+
+ /* Note that the cgroup.procs might contain duplicates! See
+ * cgroups.txt for details. */
+
+ assert(f);
+ assert(_pid);
+
+ errno = 0;
+ if (fscanf(f, "%lu", &ul) != 1) {
+
+ if (feof(f))
+ return 0;
+
+ return errno ? -errno : -EIO;
+ }
+
+ if (ul <= 0)
+ return -EIO;
+
+ *_pid = (pid_t) ul;
+ return 1;
+}
+
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+ DIR *d;
+
+ assert(_d);
+
+ /* This is not recursive! */
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ d = opendir(fs);
+ if (!d)
+ return -errno;
+
+ *_d = d;
+ return 0;
+}
+
+int cg_read_subgroup(DIR *d, char **fn) {
+ struct dirent *de;
+
+ assert(d);
+ assert(fn);
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ char *b;
+
+ if (de->d_type != DT_DIR)
+ continue;
+
+ if (streq(de->d_name, ".") ||
+ streq(de->d_name, ".."))
+ continue;
+
+ b = strdup(de->d_name);
+ if (!b)
+ return -ENOMEM;
+
+ *fn = b;
+ return 1;
+ }
+
+ return 0;
+}
+
+int cg_rmdir(const char *controller, const char *path) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, NULL, &p);
+ if (r < 0)
+ return r;
+
+ r = rmdir(p);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+}
+
+int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
+ _cleanup_set_free_ Set *allocated_set = NULL;
+ bool done = false;
+ int r, ret = 0;
+ pid_t my_pid;
+
+ assert(sig >= 0);
+
+ /* This goes through the tasks list and kills them all. This
+ * is repeated until no further processes are added to the
+ * tasks list, to properly handle forking processes */
+
+ if (!s) {
+ s = allocated_set = set_new(NULL);
+ if (!s)
+ return -ENOMEM;
+ }
+
+ my_pid = getpid();
+
+ do {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid = 0;
+ done = true;
+
+ r = cg_enumerate_processes(controller, path, &f);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
+
+ return ret;
+ }
+
+ while ((r = cg_read_pid(f, &pid)) > 0) {
+
+ if (ignore_self && pid == my_pid)
+ continue;
+
+ if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
+ continue;
+
+ /* If we haven't killed this process yet, kill
+ * it */
+ if (kill(pid, sig) < 0) {
+ if (ret >= 0 && errno != ESRCH)
+ ret = -errno;
+ } else {
+ if (sigcont && sig != SIGKILL)
+ kill(pid, SIGCONT);
+
+ if (ret == 0)
+ ret = 1;
+ }
+
+ done = false;
+
+ r = set_put(s, LONG_TO_PTR(pid));
+ if (r < 0) {
+ if (ret >= 0)
+ return r;
+
+ return ret;
+ }
+ }
+
+ if (r < 0) {
+ if (ret >= 0)
+ return r;
+
+ return ret;
+ }
+
+ /* To avoid racing against processes which fork
+ * quicker than we can kill them we repeat this until
+ * no new pids need to be killed. */
+
+ } while (!done);
+
+ return ret;
+}
+
+int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
+ _cleanup_set_free_ Set *allocated_set = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ int r, ret = 0;
+ char *fn;
+
+ assert(path);
+ assert(sig >= 0);
+
+ if (!s) {
+ s = allocated_set = set_new(NULL);
+ if (!s)
+ return -ENOMEM;
+ }
+
+ ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
+
+ r = cg_enumerate_subgroups(controller, path, &d);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
+
+ return ret;
+ }
+
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin(path, "/", fn, NULL);
+ free(fn);
+ if (!p)
+ return -ENOMEM;
+
+ r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
+ if (ret >= 0 && r != 0)
+ ret = r;
+ }
+
+ if (ret >= 0 && r < 0)
+ ret = r;
+
+ if (rem) {
+ r = cg_rmdir(controller, path);
+ if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
+ return r;
+ }
+
+ return ret;
+}
+
+int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
+ bool done = false;
+ _cleanup_set_free_ Set *s = NULL;
+ int r, ret = 0;
+ pid_t my_pid;
+
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
+
+ s = set_new(NULL);
+ if (!s)
+ return -ENOMEM;
+
+ my_pid = getpid();
+
+ do {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid = 0;
+ done = true;
+
+ r = cg_enumerate_processes(cfrom, pfrom, &f);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
+
+ return ret;
+ }
+
+ while ((r = cg_read_pid(f, &pid)) > 0) {
+
+ /* This might do weird stuff if we aren't a
+ * single-threaded program. However, we
+ * luckily know we are not */
+ if (ignore_self && pid == my_pid)
+ continue;
+
+ if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
+ continue;
+
+ r = cg_attach(cto, pto, pid);
+ if (r < 0) {
+ if (ret >= 0 && r != -ESRCH)
+ ret = r;
+ } else if (ret == 0)
+ ret = 1;
+
+ done = false;
+
+ r = set_put(s, LONG_TO_PTR(pid));
+ if (r < 0) {
+ if (ret >= 0)
+ return r;
+
+ return ret;
+ }
+ }
+
+ if (r < 0) {
+ if (ret >= 0)
+ return r;
+
+ return ret;
+ }
+ } while (!done);
+
+ return ret;
+}
+
+int cg_migrate_recursive(
+ const char *cfrom,
+ const char *pfrom,
+ const char *cto,
+ const char *pto,
+ bool ignore_self,
+ bool rem) {
+
+ _cleanup_closedir_ DIR *d = NULL;
+ int r, ret = 0;
+ char *fn;
+
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
+
+ ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
+
+ r = cg_enumerate_subgroups(cfrom, pfrom, &d);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
+
+ return ret;
+ }
+
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin(pfrom, "/", fn, NULL);
+ free(fn);
+ if (!p) {
+ if (ret >= 0)
+ return -ENOMEM;
+
+ return ret;
+ }
+
+ r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
+ if (r != 0 && ret >= 0)
+ ret = r;
+ }
+
+ if (r < 0 && ret >= 0)
+ ret = r;
+
+ if (rem) {
+ r = cg_rmdir(cfrom, pfrom);
+ if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
+ return r;
+ }
+
+ return ret;
+}
+
+int cg_migrate_recursive_fallback(
+ const char *cfrom,
+ const char *pfrom,
+ const char *cto,
+ const char *pto,
+ bool ignore_self,
+ bool rem) {
+
+ int r;
+
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
+
+ r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
+ if (r < 0) {
+ char prefix[strlen(pto) + 1];
+
+ /* This didn't work? Then let's try all prefixes of the destination */
+
+ PATH_FOREACH_PREFIX(prefix, pto) {
+ r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
+ if (r >= 0)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const char *normalize_controller(const char *controller) {
+
+ assert(controller);
+
+ if (startswith(controller, "name="))
+ return controller + 5;
+ else
+ return controller;
+}
+
+static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
+ char *t = NULL;
+
+ if (!isempty(controller)) {
+ if (!isempty(path) && !isempty(suffix))
+ t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
+ else if (!isempty(path))
+ t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
+ else if (!isempty(suffix))
+ t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
+ else
+ t = strappend("/sys/fs/cgroup/", controller);
+ } else {
+ if (!isempty(path) && !isempty(suffix))
+ t = strjoin(path, "/", suffix, NULL);
+ else if (!isempty(path))
+ t = strdup(path);
+ else
+ return -EINVAL;
+ }
+
+ if (!t)
+ return -ENOMEM;
+
+ *fs = path_kill_slashes(t);
+ return 0;
+}
+
+int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
+ const char *p;
+ static thread_local bool good = false;
+
+ assert(fs);
+
+ if (controller && !cg_controller_is_valid(controller))
+ return -EINVAL;
+
+ if (_unlikely_(!good)) {
+ int r;
+
+ r = path_is_mount_point("/sys/fs/cgroup", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ /* Cache this to save a few stat()s */
+ good = true;
+ }
+
+ p = controller ? normalize_controller(controller) : NULL;
+
+ return join_path(p, path, suffix, fs);
+}
+
+static int check_hierarchy(const char *p) {
+ const char *cc;
+
+ assert(p);
+
+ if (!filename_is_valid(p))
+ return 0;
+
+ /* Check if this controller actually really exists */
+ cc = strjoina("/sys/fs/cgroup/", p);
+ if (laccess(cc, F_OK) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
+ const char *p;
+ int r;
+
+ assert(fs);
+
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+
+ /* Normalize the controller syntax */
+ p = normalize_controller(controller);
+
+ /* Check if this controller actually really exists */
+ r = check_hierarchy(p);
+ if (r < 0)
+ return r;
+
+ return join_path(p, path, suffix, fs);
+}
+
+static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
+ assert(path);
+ assert(sb);
+ assert(ftwbuf);
+
+ if (typeflag != FTW_DP)
+ return 0;
+
+ if (ftwbuf->level < 1)
+ return 0;
+
+ rmdir(path);
+ return 0;
+}
+
+int cg_trim(const char *controller, const char *path, bool delete_root) {
+ _cleanup_free_ char *fs = NULL;
+ int r = 0;
+
+ assert(path);
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ errno = 0;
+ if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
+ r = errno ? -errno : -EIO;
+
+ if (delete_root) {
+ if (rmdir(fs) < 0 && errno != ENOENT)
+ return -errno;
+ }
+
+ return r;
+}
+
+int cg_delete(const char *controller, const char *path) {
+ _cleanup_free_ char *parent = NULL;
+ int r;
+
+ assert(path);
+
+ r = path_get_parent(path, &parent);
+ if (r < 0)
+ return r;
+
+ r = cg_migrate_recursive(controller, path, controller, parent, false, true);
+ return r == -ENOENT ? 0 : r;
+}
+
+int cg_create(const char *controller, const char *path) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path_and_check(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ r = mkdir_parents(fs, 0755);
+ if (r < 0)
+ return r;
+
+ if (mkdir(fs, 0755) < 0) {
+
+ if (errno == EEXIST)
+ return 0;
+
+ return -errno;
+ }
+
+ return 1;
+}
+
+int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
+ int r, q;
+
+ assert(pid >= 0);
+
+ r = cg_create(controller, path);
+ if (r < 0)
+ return r;
+
+ q = cg_attach(controller, path, pid);
+ if (q < 0)
+ return q;
+
+ /* This does not remove the cgroup on failure */
+ return r;
+}
+
+int cg_attach(const char *controller, const char *path, pid_t pid) {
+ _cleanup_free_ char *fs = NULL;
+ char c[DECIMAL_STR_MAX(pid_t) + 2];
+ int r;
+
+ assert(path);
+ assert(pid >= 0);
+
+ r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
+ if (r < 0)
+ return r;
+
+ if (pid == 0)
+ pid = getpid();
+
+ snprintf(c, sizeof(c), PID_FMT"\n", pid);
+
+ return write_string_file_no_create(fs, c);
+}
+
+int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
+ int r;
+
+ assert(controller);
+ assert(path);
+ assert(pid >= 0);
+
+ r = cg_attach(controller, path, pid);
+ if (r < 0) {
+ char prefix[strlen(path) + 1];
+
+ /* This didn't work? Then let's try all prefixes of
+ * the destination */
+
+ PATH_FOREACH_PREFIX(prefix, path) {
+ r = cg_attach(controller, prefix, pid);
+ if (r >= 0)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int cg_set_group_access(
+ const char *controller,
+ const char *path,
+ mode_t mode,
+ uid_t uid,
+ gid_t gid) {
+
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ assert(path);
+
+ if (mode != MODE_INVALID)
+ mode &= 0777;
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ return chmod_and_chown(fs, mode, uid, gid);
+}
+
+int cg_set_task_access(
+ const char *controller,
+ const char *path,
+ mode_t mode,
+ uid_t uid,
+ gid_t gid) {
+
+ _cleanup_free_ char *fs = NULL, *procs = NULL;
+ int r;
+
+ assert(path);
+
+ if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
+ return 0;
+
+ if (mode != MODE_INVALID)
+ mode &= 0666;
+
+ r = cg_get_path(controller, path, "cgroup.procs", &fs);
+ if (r < 0)
+ return r;
+
+ r = chmod_and_chown(fs, mode, uid, gid);
+ if (r < 0)
+ return r;
+
+ /* Compatibility, Always keep values for "tasks" in sync with
+ * "cgroup.procs" */
+ r = cg_get_path(controller, path, "tasks", &procs);
+ if (r < 0)
+ return r;
+
+ return chmod_and_chown(procs, mode, uid, gid);
+}
+
+int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *fs;
+ size_t cs;
+
+ assert(path);
+ assert(pid >= 0);
+
+ if (controller) {
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+
+ controller = normalize_controller(controller);
+ } else
+ controller = SYSTEMD_CGROUP_CONTROLLER;
+
+ fs = procfs_file_alloca(pid, "cgroup");
+
+ f = fopen(fs, "re");
+ if (!f)
+ return errno == ENOENT ? -ESRCH : -errno;
+
+ cs = strlen(controller);
+
+ FOREACH_LINE(line, f, return -errno) {
+ char *l, *p, *e;
+ size_t k;
+ const char *word, *state;
+ bool found = false;
+
+ truncate_nl(line);
+
+ l = strchr(line, ':');
+ if (!l)
+ continue;
+
+ l++;
+ e = strchr(l, ':');
+ if (!e)
+ continue;
+
+ *e = 0;
+
+ FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
+
+ if (k == cs && memcmp(word, controller, cs) == 0) {
+ found = true;
+ break;
+ }
+
+ if (k == 5 + cs &&
+ memcmp(word, "name=", 5) == 0 &&
+ memcmp(word+5, controller, cs) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ continue;
+
+ p = strdup(e + 1);
+ if (!p)
+ return -ENOMEM;
+
+ *path = p;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int cg_install_release_agent(const char *controller, const char *agent) {
+ _cleanup_free_ char *fs = NULL, *contents = NULL;
+ char *sc;
+ int r;
+
+ assert(agent);
+
+ r = cg_get_path(controller, NULL, "release_agent", &fs);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(fs, &contents);
+ if (r < 0)
+ return r;
+
+ sc = strstrip(contents);
+ if (sc[0] == 0) {
+ r = write_string_file_no_create(fs, agent);
+ if (r < 0)
+ return r;
+ } else if (!streq(sc, agent))
+ return -EEXIST;
+
+ free(fs);
+ fs = NULL;
+ r = cg_get_path(controller, NULL, "notify_on_release", &fs);
+ if (r < 0)
+ return r;
+
+ free(contents);
+ contents = NULL;
+ r = read_one_line_file(fs, &contents);
+ if (r < 0)
+ return r;
+
+ sc = strstrip(contents);
+ if (streq(sc, "0")) {
+ r = write_string_file_no_create(fs, "1");
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ if (!streq(sc, "1"))
+ return -EIO;
+
+ return 0;
+}
+
+int cg_uninstall_release_agent(const char *controller) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path(controller, NULL, "notify_on_release", &fs);
+ if (r < 0)
+ return r;
+
+ r = write_string_file_no_create(fs, "0");
+ if (r < 0)
+ return r;
+
+ free(fs);
+ fs = NULL;
+
+ r = cg_get_path(controller, NULL, "release_agent", &fs);
+ if (r < 0)
+ return r;
+
+ r = write_string_file_no_create(fs, "");
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid = 0, self_pid;
+ bool found = false;
+ int r;
+
+ assert(path);
+
+ r = cg_enumerate_processes(controller, path, &f);
+ if (r < 0)
+ return r == -ENOENT ? 1 : r;
+
+ self_pid = getpid();
+
+ while ((r = cg_read_pid(f, &pid)) > 0) {
+
+ if (ignore_self && pid == self_pid)
+ continue;
+
+ found = true;
+ break;
+ }
+
+ if (r < 0)
+ return r;
+
+ return !found;
+}
+
+int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
+ _cleanup_closedir_ DIR *d = NULL;
+ char *fn;
+ int r;
+
+ assert(path);
+
+ r = cg_is_empty(controller, path, ignore_self);
+ if (r <= 0)
+ return r;
+
+ r = cg_enumerate_subgroups(controller, path, &d);
+ if (r < 0)
+ return r == -ENOENT ? 1 : r;
+
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin(path, "/", fn, NULL);
+ free(fn);
+ if (!p)
+ return -ENOMEM;
+
+ r = cg_is_empty_recursive(controller, p, ignore_self);
+ if (r <= 0)
+ return r;
+ }
+
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int cg_split_spec(const char *spec, char **controller, char **path) {
+ const char *e;
+ char *t = NULL, *u = NULL;
+ _cleanup_free_ char *v = NULL;
+
+ assert(spec);
+
+ if (*spec == '/') {
+ if (!path_is_safe(spec))
+ return -EINVAL;
+
+ if (path) {
+ t = strdup(spec);
+ if (!t)
+ return -ENOMEM;
+
+ *path = path_kill_slashes(t);
+ }
+
+ if (controller)
+ *controller = NULL;
+
+ return 0;
+ }
+
+ e = strchr(spec, ':');
+ if (!e) {
+ if (!cg_controller_is_valid(spec))
+ return -EINVAL;
+
+ if (controller) {
+ t = strdup(normalize_controller(spec));
+ if (!t)
+ return -ENOMEM;
+
+ *controller = t;
+ }
+
+ if (path)
+ *path = NULL;
+
+ return 0;
+ }
+
+ v = strndup(spec, e-spec);
+ if (!v)
+ return -ENOMEM;
+ t = strdup(normalize_controller(v));
+ if (!t)
+ return -ENOMEM;
+ if (!cg_controller_is_valid(t)) {
+ free(t);
+ return -EINVAL;
+ }
+
+ if (streq(e+1, "")) {
+ u = strdup("/");
+ if (!u) {
+ free(t);
+ return -ENOMEM;
+ }
+ } else {
+ u = strdup(e+1);
+ if (!u) {
+ free(t);
+ return -ENOMEM;
+ }
+
+ if (!path_is_safe(u) ||
+ !path_is_absolute(u)) {
+ free(t);
+ free(u);
+ return -EINVAL;
+ }
+
+ path_kill_slashes(u);
+ }
+
+ if (controller)
+ *controller = t;
+ else
+ free(t);
+
+ if (path)
+ *path = u;
+ else
+ free(u);
+
+ return 0;
+}
+
+int cg_mangle_path(const char *path, char **result) {
+ _cleanup_free_ char *c = NULL, *p = NULL;
+ char *t;
+ int r;
+
+ assert(path);
+ assert(result);
+
+ /* First, check if it already is a filesystem path */
+ if (path_startswith(path, "/sys/fs/cgroup")) {
+
+ t = strdup(path);
+ if (!t)
+ return -ENOMEM;
+
+ *result = path_kill_slashes(t);
+ return 0;
+ }
+
+ /* Otherwise, treat it as cg spec */
+ r = cg_split_spec(path, &c, &p);
+ if (r < 0)
+ return r;
+
+ return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
+}
+
+int cg_get_root_path(char **path) {
+ char *p, *e;
+ int r;
+
+ assert(path);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
+ if (r < 0)
+ return r;
+
+ e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
+ if (e)
+ *e = 0;
+
+ *path = p;
+ return 0;
+}
+
+int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
+ _cleanup_free_ char *rt = NULL;
+ char *p;
+ int r;
+
+ assert(cgroup);
+ assert(shifted);
+
+ if (!root) {
+ /* If the root was specified let's use that, otherwise
+ * let's determine it from PID 1 */
+
+ r = cg_get_root_path(&rt);
+ if (r < 0)
+ return r;
+
+ root = rt;
+ }
+
+ p = path_startswith(cgroup, root);
+ if (p)
+ *shifted = p - 1;
+ else
+ *shifted = cgroup;
+
+ return 0;
+}
+
+int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
+ _cleanup_free_ char *raw = NULL;
+ const char *c;
+ int r;
+
+ assert(pid >= 0);
+ assert(cgroup);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
+ if (r < 0)
+ return r;
+
+ r = cg_shift_path(raw, root, &c);
+ if (r < 0)
+ return r;
+
+ if (c == raw) {
+ *cgroup = raw;
+ raw = NULL;
+ } else {
+ char *n;
+
+ n = strdup(c);
+ if (!n)
+ return -ENOMEM;
+
+ *cgroup = n;
+ }
+
+ return 0;
+}
+
+int cg_path_decode_unit(const char *cgroup, char **unit){
+ char *c, *s;
+ size_t n;
+
+ assert(cgroup);
+ assert(unit);
+
+ n = strcspn(cgroup, "/");
+ if (n < 3)
+ return -ENXIO;
+
+ c = strndupa(cgroup, n);
+ c = cg_unescape(c);
+
+ if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+ return -ENXIO;
+
+ s = strdup(c);
+ if (!s)
+ return -ENOMEM;
+
+ *unit = s;
+ return 0;
+}
+
+static bool valid_slice_name(const char *p, size_t n) {
+
+ if (!p)
+ return false;
+
+ if (n < strlen("x.slice"))
+ return false;
+
+ if (memcmp(p + n - 6, ".slice", 6) == 0) {
+ char buf[n+1], *c;
+
+ memcpy(buf, p, n);
+ buf[n] = 0;
+
+ c = cg_unescape(buf);
+
+ return unit_name_is_valid(c, UNIT_NAME_PLAIN);
+ }
+
+ return false;
+}
+
+static const char *skip_slices(const char *p) {
+ assert(p);
+
+ /* Skips over all slice assignments */
+
+ for (;;) {
+ size_t n;
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (!valid_slice_name(p, n))
+ return p;
+
+ p += n;
+ }
+}
+
+int cg_path_get_unit(const char *path, char **ret) {
+ const char *e;
+ char *unit;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ e = skip_slices(path);
+
+ r = cg_path_decode_unit(e, &unit);
+ if (r < 0)
+ return r;
+
+ /* We skipped over the slices, don't accept any now */
+ if (endswith(unit, ".slice")) {
+ free(unit);
+ return -ENXIO;
+ }
+
+ *ret = unit;
+ return 0;
+}
+
+int cg_pid_get_unit(pid_t pid, char **unit) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(unit);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_unit(cgroup, unit);
+}
+
+/**
+ * Skip session-*.scope, but require it to be there.
+ */
+static const char *skip_session(const char *p) {
+ size_t n;
+
+ if (isempty(p))
+ return NULL;
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n < strlen("session-x.scope"))
+ return NULL;
+
+ if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
+ char buf[n - 8 - 6 + 1];
+
+ memcpy(buf, p + 8, n - 8 - 6);
+ buf[n - 8 - 6] = 0;
+
+ /* Note that session scopes never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (!session_id_valid(buf))
+ return false;
+
+ p += n;
+ p += strspn(p, "/");
+ return p;
+ }
+
+ return NULL;
+}
+
+/**
+ * Skip user@*.service, but require it to be there.
+ */
+static const char *skip_user_manager(const char *p) {
+ size_t n;
+
+ if (isempty(p))
+ return NULL;
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n < strlen("user at x.service"))
+ return NULL;
+
+ if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
+ char buf[n - 5 - 8 + 1];
+
+ memcpy(buf, p + 5, n - 5 - 8);
+ buf[n - 5 - 8] = 0;
+
+ /* Note that user manager services never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (parse_uid(buf, NULL) < 0)
+ return NULL;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+ }
+
+ return NULL;
+}
+
+static const char *skip_user_prefix(const char *path) {
+ const char *e, *t;
+
+ assert(path);
+
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
+
+ /* Skip the user manager, if it's in the path now... */
+ t = skip_user_manager(e);
+ if (t)
+ return t;
+
+ /* Alternatively skip the user session if it is in the path... */
+ return skip_session(e);
+}
+
+int cg_path_get_user_unit(const char *path, char **ret) {
+ const char *t;
+
+ assert(path);
+ assert(ret);
+
+ t = skip_user_prefix(path);
+ if (!t)
+ return -ENXIO;
+
+ /* And from here on it looks pretty much the same as for a
+ * system unit, hence let's use the same parser from here
+ * on. */
+ return cg_path_get_unit(t, ret);
+}
+
+int cg_pid_get_user_unit(pid_t pid, char **unit) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(unit);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_user_unit(cgroup, unit);
+}
+
+int cg_path_get_machine_name(const char *path, char **machine) {
+ _cleanup_free_ char *u = NULL, *sl = NULL;
+ int r;
+
+ r = cg_path_get_unit(path, &u);
+ if (r < 0)
+ return r;
+
+ sl = strjoin("/run/systemd/machines/unit:", u, NULL);
+ if (!sl)
+ return -ENOMEM;
+
+ return readlink_malloc(sl, machine);
+}
+
+int cg_pid_get_machine_name(pid_t pid, char **machine) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(machine);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_machine_name(cgroup, machine);
+}
+
+int cg_path_get_session(const char *path, char **session) {
+ _cleanup_free_ char *unit = NULL;
+ char *start, *end;
+ int r;
+
+ assert(path);
+
+ r = cg_path_get_unit(path, &unit);
+ if (r < 0)
+ return r;
+
+ start = startswith(unit, "session-");
+ if (!start)
+ return -ENXIO;
+ end = endswith(start, ".scope");
+ if (!end)
+ return -ENXIO;
+
+ *end = 0;
+ if (!session_id_valid(start))
+ return -ENXIO;
+
+ if (session) {
+ char *rr;
+
+ rr = strdup(start);
+ if (!rr)
+ return -ENOMEM;
+
+ *session = rr;
+ }
+
+ return 0;
+}
+
+int cg_pid_get_session(pid_t pid, char **session) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_session(cgroup, session);
+}
+
+int cg_path_get_owner_uid(const char *path, uid_t *uid) {
+ _cleanup_free_ char *slice = NULL;
+ char *start, *end;
+ int r;
+
+ assert(path);
+
+ r = cg_path_get_slice(path, &slice);
+ if (r < 0)
+ return r;
+
+ start = startswith(slice, "user-");
+ if (!start)
+ return -ENXIO;
+ end = endswith(start, ".slice");
+ if (!end)
+ return -ENXIO;
+
+ *end = 0;
+ if (parse_uid(start, uid) < 0)
+ return -ENXIO;
+
+ return 0;
+}
+
+int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_owner_uid(cgroup, uid);
+}
+
+int cg_path_get_slice(const char *p, char **slice) {
+ const char *e = NULL;
+
+ assert(p);
+ assert(slice);
+
+ /* Finds the right-most slice unit from the beginning, but
+ * stops before we come to the first non-slice unit. */
+
+ for (;;) {
+ size_t n;
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (!valid_slice_name(p, n)) {
+
+ if (!e) {
+ char *s;
+
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
+
+ *slice = s;
+ return 0;
+ }
+
+ return cg_path_decode_unit(e, slice);
+ }
+
+ e = p;
+ p += n;
+ }
+}
+
+int cg_pid_get_slice(pid_t pid, char **slice) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(slice);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_slice(cgroup, slice);
+}
+
+int cg_path_get_user_slice(const char *p, char **slice) {
+ const char *t;
+ assert(p);
+ assert(slice);
+
+ t = skip_user_prefix(p);
+ if (!t)
+ return -ENXIO;
+
+ /* And now it looks pretty much the same as for a system
+ * slice, so let's just use the same parser from here on. */
+ return cg_path_get_slice(t, slice);
+}
+
+int cg_pid_get_user_slice(pid_t pid, char **slice) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(slice);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_user_slice(cgroup, slice);
+}
+
+char *cg_escape(const char *p) {
+ bool need_prefix = false;
+
+ /* This implements very minimal escaping for names to be used
+ * as file names in the cgroup tree: any name which might
+ * conflict with a kernel name or is prefixed with '_' is
+ * prefixed with a '_'. That way, when reading cgroup names it
+ * is sufficient to remove a single prefixing underscore if
+ * there is one. */
+
+ /* The return value of this function (unlike cg_unescape())
+ * needs free()! */
+
+ if (p[0] == 0 ||
+ p[0] == '_' ||
+ p[0] == '.' ||
+ streq(p, "notify_on_release") ||
+ streq(p, "release_agent") ||
+ streq(p, "tasks"))
+ need_prefix = true;
+ else {
+ const char *dot;
+
+ dot = strrchr(p, '.');
+ if (dot) {
+
+ if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
+ need_prefix = true;
+ else {
+ char *n;
+
+ n = strndupa(p, dot - p);
+
+ if (check_hierarchy(n) >= 0)
+ need_prefix = true;
+ }
+ }
+ }
+
+ if (need_prefix)
+ return strappend("_", p);
+ else
+ return strdup(p);
+}
+
+char *cg_unescape(const char *p) {
+ assert(p);
+
+ /* The return value of this function (unlike cg_escape())
+ * doesn't need free()! */
+
+ if (p[0] == '_')
+ return (char*) p+1;
+
+ return (char*) p;
+}
+
+#define CONTROLLER_VALID \
+ DIGITS LETTERS \
+ "_"
+
+bool cg_controller_is_valid(const char *p) {
+ const char *t, *s;
+
+ if (!p)
+ return false;
+
+ s = startswith(p, "name=");
+ if (s)
+ p = s;
+
+ if (*p == 0 || *p == '_')
+ return false;
+
+ for (t = p; *t; t++)
+ if (!strchr(CONTROLLER_VALID, *t))
+ return false;
+
+ if (t - p > FILENAME_MAX)
+ return false;
+
+ return true;
+}
+
+int cg_slice_to_path(const char *unit, char **ret) {
+ _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
+ const char *dash;
+ int r;
+
+ assert(unit);
+ assert(ret);
+
+ if (streq(unit, "-.slice")) {
+ char *x;
+
+ x = strdup("");
+ if (!x)
+ return -ENOMEM;
+ *ret = x;
+ return 0;
+ }
+
+ if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
+ return -EINVAL;
+
+ if (!endswith(unit, ".slice"))
+ return -EINVAL;
+
+ r = unit_name_to_prefix(unit, &p);
+ if (r < 0)
+ return r;
+
+ dash = strchr(p, '-');
+
+ /* Don't allow initial dashes */
+ if (dash == p)
+ return -EINVAL;
+
+ while (dash) {
+ _cleanup_free_ char *escaped = NULL;
+ char n[dash - p + sizeof(".slice")];
+
+ /* Don't allow trailing or double dashes */
+ if (dash[1] == 0 || dash[1] == '-')
+ return -EINVAL;
+
+ strcpy(stpncpy(n, p, dash - p), ".slice");
+ if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
+ return -EINVAL;
+
+ escaped = cg_escape(n);
+ if (!escaped)
+ return -ENOMEM;
+
+ if (!strextend(&s, escaped, "/", NULL))
+ return -ENOMEM;
+
+ dash = strchr(dash+1, '-');
+ }
+
+ e = cg_escape(unit);
+ if (!e)
+ return -ENOMEM;
+
+ if (!strextend(&s, e, NULL))
+ return -ENOMEM;
+
+ *ret = s;
+ s = NULL;
+
+ return 0;
+}
+
+int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, attribute, &p);
+ if (r < 0)
+ return r;
+
+ return write_string_file_no_create(p, value);
+}
+
+int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, attribute, &p);
+ if (r < 0)
+ return r;
+
+ return read_one_line_file(p, ret);
+}
+
+static const char mask_names[] =
+ "cpu\0"
+ "cpuacct\0"
+ "blkio\0"
+ "memory\0"
+ "devices\0";
+
+int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ /* This one will create a cgroup in our private tree, but also
+ * duplicate it in the trees specified in mask, and remove it
+ * in all others */
+
+ /* First create the cgroup in our own hierarchy. */
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+ if (r < 0)
+ return r;
+
+ /* Then, do the same in the other hierarchies */
+ NULSTR_FOREACH(n, mask_names) {
+ if (mask & bit)
+ cg_create(n, path);
+ else if (supported & bit)
+ cg_trim(n, path, true);
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
+ if (r < 0)
+ return r;
+
+ NULSTR_FOREACH(n, mask_names) {
+
+ if (supported & bit) {
+ const char *p = NULL;
+
+ if (path_callback)
+ p = path_callback(bit, userdata);
+
+ if (!p)
+ p = path;
+
+ cg_attach_fallback(n, path, pid);
+ }
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
+ Iterator i;
+ void *pidp;
+ int r = 0;
+
+ SET_FOREACH(pidp, pids, i) {
+ pid_t pid = PTR_TO_LONG(pidp);
+ int q;
+
+ q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
+ if (q < 0)
+ r = q;
+ }
+
+ return r;
+}
+
+int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ if (!path_equal(from, to)) {
+ r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
+ if (r < 0)
+ return r;
+ }
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (supported & bit) {
+ const char *p = NULL;
+
+ if (to_callback)
+ p = to_callback(bit, userdata);
+
+ if (!p)
+ p = to;
+
+ cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
+ }
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
+ CGroupControllerMask bit = 1;
+ const char *n;
+ int r;
+
+ r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
+ if (r < 0)
+ return r;
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (supported & bit)
+ cg_trim(n, path, delete_root);
+
+ bit <<= 1;
+ }
+
+ return 0;
+}
+
+CGroupControllerMask cg_mask_supported(void) {
+ CGroupControllerMask bit = 1, mask = 0;
+ const char *n;
+
+ NULSTR_FOREACH(n, mask_names) {
+ if (check_hierarchy(n) >= 0)
+ mask |= bit;
+
+ bit <<= 1;
+ }
+
+ return mask;
+}
+
+int cg_kernel_controllers(Set *controllers) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char buf[LINE_MAX];
+ int r;
+
+ assert(controllers);
+
+ f = fopen("/proc/cgroups", "re");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ /* Ignore the header line */
+ (void) fgets(buf, sizeof(buf), f);
+
+ for (;;) {
+ char *controller;
+ int enabled = 0;
+
+ errno = 0;
+ if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+
+ if (feof(f))
+ break;
+
+ if (ferror(f) && errno)
+ return -errno;
+
+ return -EBADMSG;
+ }
+
+ if (!enabled) {
+ free(controller);
+ continue;
+ }
+
+ if (!filename_is_valid(controller)) {
+ free(controller);
+ return -EBADMSG;
+ }
+
+ r = set_consume(controllers, controller);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
new file mode 100644
index 0000000..fd72e9e
--- /dev/null
+++ b/src/basic/cgroup-util.h
@@ -0,0 +1,139 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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>
+#include <stdio.h>
+#include <dirent.h>
+
+#include "set.h"
+#include "def.h"
+
+/* A bit mask of well known cgroup controllers */
+typedef enum CGroupControllerMask {
+ CGROUP_CPU = 1,
+ CGROUP_CPUACCT = 2,
+ CGROUP_BLKIO = 4,
+ CGROUP_MEMORY = 8,
+ CGROUP_DEVICE = 16,
+ _CGROUP_CONTROLLER_MASK_ALL = 31
+} CGroupControllerMask;
+
+/*
+ * General rules:
+ *
+ * We accept named hierarchies in the syntax "foo" and "name=foo".
+ *
+ * We expect that named hierarchies do not conflict in name with a
+ * kernel hierarchy, modulo the "name=" prefix.
+ *
+ * We always generate "normalized" controller names, i.e. without the
+ * "name=" prefix.
+ *
+ * We require absolute cgroup paths. When returning, we will always
+ * generate paths with multiple adjacent / removed.
+ */
+
+int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
+int cg_read_pid(FILE *f, pid_t *_pid);
+
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
+int cg_read_subgroup(DIR *d, char **fn);
+
+int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s);
+int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool remove, Set *s);
+
+int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self);
+int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool remove);
+int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem);
+
+int cg_split_spec(const char *spec, char **controller, char **path);
+int cg_mangle_path(const char *path, char **result);
+
+int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
+int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
+
+int cg_pid_get_path(const char *controller, pid_t pid, char **path);
+
+int cg_trim(const char *controller, const char *path, bool delete_root);
+
+int cg_rmdir(const char *controller, const char *path);
+int cg_delete(const char *controller, const char *path);
+
+int cg_create(const char *controller, const char *path);
+int cg_attach(const char *controller, const char *path, pid_t pid);
+int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
+int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
+
+int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
+int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
+
+int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
+int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
+
+int cg_install_release_agent(const char *controller, const char *agent);
+int cg_uninstall_release_agent(const char *controller);
+
+int cg_is_empty(const char *controller, const char *path, bool ignore_self);
+int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
+
+int cg_get_root_path(char **path);
+
+int cg_path_get_session(const char *path, char **session);
+int cg_path_get_owner_uid(const char *path, uid_t *uid);
+int cg_path_get_unit(const char *path, char **unit);
+int cg_path_get_user_unit(const char *path, char **unit);
+int cg_path_get_machine_name(const char *path, char **machine);
+int cg_path_get_slice(const char *path, char **slice);
+int cg_path_get_user_slice(const char *path, char **slice);
+
+int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
+int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
+
+int cg_pid_get_session(pid_t pid, char **session);
+int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
+int cg_pid_get_unit(pid_t pid, char **unit);
+int cg_pid_get_user_unit(pid_t pid, char **unit);
+int cg_pid_get_machine_name(pid_t pid, char **machine);
+int cg_pid_get_slice(pid_t pid, char **slice);
+int cg_pid_get_user_slice(pid_t pid, char **slice);
+
+int cg_path_decode_unit(const char *cgroup, char **unit);
+
+char *cg_escape(const char *p);
+char *cg_unescape(const char *p) _pure_;
+
+bool cg_controller_is_valid(const char *p);
+
+int cg_slice_to_path(const char *unit, char **ret);
+
+typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata);
+
+int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path);
+int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
+int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
+int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
+int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
+
+CGroupControllerMask cg_mask_supported(void);
+
+int cg_kernel_controllers(Set *controllers);
diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c
new file mode 100644
index 0000000..e4e03df
--- /dev/null
+++ b/src/basic/clock-util.c
@@ -0,0 +1,142 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-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 <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
+
+#include "macro.h"
+#include "util.h"
+#include "clock-util.h"
+
+int clock_get_hwclock(struct tm *tm) {
+ _cleanup_close_ int fd = -1;
+
+ assert(tm);
+
+ fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ /* This leaves the timezone fields of struct tm
+ * uninitialized! */
+ if (ioctl(fd, RTC_RD_TIME, tm) < 0)
+ return -errno;
+
+ /* We don't know daylight saving, so we reset this in order not
+ * to confuse mktime(). */
+ tm->tm_isdst = -1;
+
+ return 0;
+}
+
+int clock_set_hwclock(const struct tm *tm) {
+ _cleanup_close_ int fd = -1;
+
+ assert(tm);
+
+ fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (ioctl(fd, RTC_SET_TIME, tm) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int clock_is_localtime(void) {
+ _cleanup_fclose_ FILE *f;
+
+ /*
+ * The third line of adjtime is "UTC" or "LOCAL" or nothing.
+ * # /etc/adjtime
+ * 0.0 0 0
+ * 0
+ * UTC
+ */
+ f = fopen("/etc/adjtime", "re");
+ if (f) {
+ char line[LINE_MAX];
+ bool b;
+
+ b = fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f);
+ if (!b)
+ return -EIO;
+
+ truncate_nl(line);
+ return streq(line, "LOCAL");
+
+ } else if (errno != ENOENT)
+ return -errno;
+
+ return 0;
+}
+
+int clock_set_timezone(int *min) {
+ const struct timeval *tv_null = NULL;
+ struct timespec ts;
+ struct tm *tm;
+ int minutesdelta;
+ struct timezone tz;
+
+ assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+ assert_se(tm = localtime(&ts.tv_sec));
+ minutesdelta = tm->tm_gmtoff / 60;
+
+ tz.tz_minuteswest = -minutesdelta;
+ tz.tz_dsttime = 0; /* DST_NONE */
+
+ /*
+ * If the RTC does not run in UTC but in local time, the very first
+ * call to settimeofday() will set the kernel's timezone and will warp the
+ * system clock, so that it runs in UTC instead of the local time we
+ * have read from the RTC.
+ */
+ if (settimeofday(tv_null, &tz) < 0)
+ return -errno;
+ if (min)
+ *min = minutesdelta;
+ return 0;
+}
+
+int clock_reset_timewarp(void) {
+ const struct timeval *tv_null = NULL;
+ struct timezone tz;
+
+ tz.tz_minuteswest = 0;
+ tz.tz_dsttime = 0; /* DST_NONE */
+
+ /*
+ * The very first call to settimeofday() does time warp magic. Do a
+ * dummy call here, so the time warping is sealed and all later calls
+ * behave as expected.
+ */
+ if (settimeofday(tv_null, &tz) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/basic/clock-util.h b/src/basic/clock-util.h
new file mode 100644
index 0000000..8c2d235
--- /dev/null
+++ b/src/basic/clock-util.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-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/>.
+***/
+
+
+int clock_is_localtime(void);
+int clock_set_timezone(int *min);
+int clock_reset_timewarp(void);
+int clock_get_hwclock(struct tm *tm);
+int clock_set_hwclock(const struct tm *tm);
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
new file mode 100644
index 0000000..da8745b
--- /dev/null
+++ b/src/basic/conf-files.c
@@ -0,0 +1,174 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#include "macro.h"
+#include "util.h"
+#include "missing.h"
+#include "log.h"
+#include "strv.h"
+#include "path-util.h"
+#include "hashmap.h"
+#include "conf-files.h"
+
+static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ const char *dirpath;
+ int r;
+
+ assert(path);
+ assert(suffix);
+
+ dirpath = prefix_roota(root, path);
+
+ dir = opendir(dirpath);
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ for (;;) {
+ struct dirent *de;
+ char *p;
+
+ errno = 0;
+ de = readdir(dir);
+ if (!de && errno != 0)
+ return -errno;
+
+ if (!de)
+ break;
+
+ if (!dirent_is_file_with_suffix(de, suffix))
+ continue;
+
+ p = strjoin(dirpath, "/", de->d_name, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = hashmap_put(h, basename(p), p);
+ if (r == -EEXIST) {
+ log_debug("Skipping overridden file: %s.", p);
+ free(p);
+ } else if (r < 0) {
+ free(p);
+ return r;
+ } else if (r == 0) {
+ log_debug("Duplicate file %s", p);
+ free(p);
+ }
+ }
+
+ return 0;
+}
+
+static int base_cmp(const void *a, const void *b) {
+ const char *s1, *s2;
+
+ s1 = *(char * const *)a;
+ s2 = *(char * const *)b;
+ return strcmp(basename(s1), basename(s2));
+}
+
+static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
+ _cleanup_hashmap_free_ Hashmap *fh = NULL;
+ char **files, **p;
+ int r;
+
+ assert(strv);
+ assert(suffix);
+
+ /* This alters the dirs string array */
+ if (!path_strv_resolve_uniq(dirs, root))
+ return -ENOMEM;
+
+ fh = hashmap_new(&string_hash_ops);
+ if (!fh)
+ return -ENOMEM;
+
+ STRV_FOREACH(p, dirs) {
+ r = files_add(fh, root, *p, suffix);
+ if (r == -ENOMEM) {
+ return r;
+ } else if (r < 0)
+ log_debug_errno(r, "Failed to search for files in %s: %m",
+ *p);
+ }
+
+ files = hashmap_get_strv(fh);
+ if (files == NULL) {
+ return -ENOMEM;
+ }
+
+ qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
+ *strv = files;
+
+ return 0;
+}
+
+int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) {
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(strv);
+ assert(suffix);
+
+ copy = strv_copy((char**) dirs);
+ if (!copy)
+ return -ENOMEM;
+
+ return conf_files_list_strv_internal(strv, suffix, root, copy);
+}
+
+int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) {
+ _cleanup_strv_free_ char **dirs = NULL;
+ va_list ap;
+
+ assert(strv);
+ assert(suffix);
+
+ va_start(ap, dir);
+ dirs = strv_new_ap(dir, ap);
+ va_end(ap);
+
+ if (!dirs)
+ return -ENOMEM;
+
+ return conf_files_list_strv_internal(strv, suffix, root, dirs);
+}
+
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) {
+ _cleanup_strv_free_ char **dirs = NULL;
+
+ assert(strv);
+ assert(suffix);
+
+ dirs = strv_split_nulstr(d);
+ if (!dirs)
+ return -ENOMEM;
+
+ return conf_files_list_strv_internal(strv, suffix, root, dirs);
+}
diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h
new file mode 100644
index 0000000..3169a90
--- /dev/null
+++ b/src/basic/conf-files.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2012 Lennart Poettering
+ Copyright 2010-2012 Kay Sievers
+
+ 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/>.
+***/
+
+
+int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
+int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs);
diff --git a/src/basic/copy.c b/src/basic/copy.c
new file mode 100644
index 0000000..1282cb8
--- /dev/null
+++ b/src/basic/copy.c
@@ -0,0 +1,507 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/sendfile.h>
+#include <sys/xattr.h>
+
+#include "util.h"
+#include "btrfs-util.h"
+#include "copy.h"
+
+#define COPY_BUFFER_SIZE (16*1024)
+
+int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
+ bool try_sendfile = true;
+ int r;
+
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
+ /* Try btrfs reflinks first. */
+ if (try_reflink && max_bytes == (off_t) -1) {
+ r = btrfs_reflink(fdf, fdt);
+ if (r >= 0)
+ return r;
+ }
+
+ for (;;) {
+ size_t m = COPY_BUFFER_SIZE;
+ ssize_t n;
+
+ if (max_bytes != (off_t) -1) {
+
+ if (max_bytes <= 0)
+ return -EFBIG;
+
+ if ((off_t) m > max_bytes)
+ m = (size_t) max_bytes;
+ }
+
+ /* First try sendfile(), unless we already tried */
+ if (try_sendfile) {
+
+ n = sendfile(fdt, fdf, NULL, m);
+ if (n < 0) {
+ if (errno != EINVAL && errno != ENOSYS)
+ return -errno;
+
+ try_sendfile = false;
+ /* use fallback below */
+ } else if (n == 0) /* EOF */
+ break;
+ else if (n > 0)
+ /* Succcess! */
+ goto next;
+ }
+
+ /* As a fallback just copy bits by hand */
+ {
+ char buf[m];
+
+ n = read(fdf, buf, m);
+ if (n < 0)
+ return -errno;
+ if (n == 0) /* EOF */
+ break;
+
+ r = loop_write(fdt, buf, (size_t) n, false);
+ if (r < 0)
+ return r;
+ }
+
+ next:
+ if (max_bytes != (off_t) -1) {
+ assert(max_bytes >= n);
+ max_bytes -= n;
+ }
+ }
+
+ return 0;
+}
+
+static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ _cleanup_free_ char *target = NULL;
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ r = readlinkat_malloc(df, from, &target);
+ if (r < 0)
+ return r;
+
+ if (symlinkat(target, dt, to) < 0)
+ return -errno;
+
+ if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ _cleanup_close_ int fdf = -1, fdt = -1;
+ struct timespec ts[2];
+ int r, q;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fdf < 0)
+ return -errno;
+
+ fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
+ if (fdt < 0)
+ return -errno;
+
+ r = copy_bytes(fdf, fdt, (off_t) -1, true);
+ if (r < 0) {
+ unlinkat(dt, to, 0);
+ return r;
+ }
+
+ if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ r = -errno;
+
+ if (fchmod(fdt, st->st_mode & 07777) < 0)
+ r = -errno;
+
+ ts[0] = st->st_atim;
+ ts[1] = st->st_mtim;
+ (void) futimens(fdt, ts);
+
+ (void) copy_xattr(fdf, fdt);
+
+ q = close(fdt);
+ fdt = -1;
+
+ if (q < 0) {
+ r = -errno;
+ unlinkat(dt, to, 0);
+ }
+
+ return r;
+}
+
+static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ r = mkfifoat(dt, to, st->st_mode & 07777);
+ if (r < 0)
+ return -errno;
+
+ if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ r = -errno;
+
+ if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
+ r = -errno;
+
+ return r;
+}
+
+static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ r = mknodat(dt, to, st->st_mode, st->st_rdev);
+ if (r < 0)
+ return -errno;
+
+ if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ r = -errno;
+
+ if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
+ r = -errno;
+
+ return r;
+}
+
+static int fd_copy_directory(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ dev_t original_device,
+ bool merge) {
+
+ _cleanup_close_ int fdf = -1, fdt = -1;
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ bool created;
+ int r;
+
+ assert(st);
+ assert(to);
+
+ if (from)
+ fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ else
+ fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
+
+ d = fdopendir(fdf);
+ if (!d)
+ return -errno;
+ fdf = -1;
+
+ r = mkdirat(dt, to, st->st_mode & 07777);
+ if (r >= 0)
+ created = true;
+ else if (errno == EEXIST && merge)
+ created = false;
+ else
+ return -errno;
+
+ fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fdt < 0)
+ return -errno;
+
+ r = 0;
+
+ if (created) {
+ struct timespec ut[2] = {
+ st->st_atim,
+ st->st_mtim
+ };
+
+ if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ r = -errno;
+
+ if (fchmod(fdt, st->st_mode & 07777) < 0)
+ r = -errno;
+
+ (void) futimens(fdt, ut);
+ (void) copy_xattr(dirfd(d), fdt);
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ struct stat buf;
+ int q;
+
+ if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
+ r = -errno;
+ continue;
+ }
+
+ if (buf.st_dev != original_device)
+ continue;
+
+ if (S_ISREG(buf.st_mode))
+ q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ else if (S_ISDIR(buf.st_mode))
+ q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
+ else if (S_ISLNK(buf.st_mode))
+ q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ else if (S_ISFIFO(buf.st_mode))
+ q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
+ q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ else
+ q = -EOPNOTSUPP;
+
+ if (q == -EEXIST && merge)
+ q = 0;
+
+ if (q < 0)
+ r = q;
+ }
+
+ return r;
+}
+
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
+ struct stat st;
+
+ assert(from);
+ assert(to);
+
+ if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ if (S_ISREG(st.st_mode))
+ return fd_copy_regular(fdf, from, &st, fdt, to);
+ else if (S_ISDIR(st.st_mode))
+ return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
+ else if (S_ISLNK(st.st_mode))
+ return fd_copy_symlink(fdf, from, &st, fdt, to);
+ else if (S_ISFIFO(st.st_mode))
+ return fd_copy_fifo(fdf, from, &st, fdt, to);
+ else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+ return fd_copy_node(fdf, from, &st, fdt, to);
+ else
+ return -EOPNOTSUPP;
+}
+
+int copy_tree(const char *from, const char *to, bool merge) {
+ return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
+}
+
+int copy_directory_fd(int dirfd, const char *to, bool merge) {
+
+ struct stat st;
+
+ assert(dirfd >= 0);
+ assert(to);
+
+ if (fstat(dirfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -ENOTDIR;
+
+ return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
+}
+
+int copy_file_fd(const char *from, int fdt, bool try_reflink) {
+ _cleanup_close_ int fdf = -1;
+ int r;
+
+ assert(from);
+ assert(fdt >= 0);
+
+ fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fdf < 0)
+ return -errno;
+
+ r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
+
+ (void) copy_times(fdf, fdt);
+ (void) copy_xattr(fdf, fdt);
+
+ return r;
+}
+
+int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
+ int fdt = -1, r;
+
+ assert(from);
+ assert(to);
+
+ RUN_WITH_UMASK(0000) {
+ fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
+ if (fdt < 0)
+ return -errno;
+ }
+
+ if (chattr_flags != 0)
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
+
+ r = copy_file_fd(from, fdt, true);
+ if (r < 0) {
+ close(fdt);
+ unlink(to);
+ return r;
+ }
+
+ if (close(fdt) < 0) {
+ unlink_noerrno(to);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ r = tempfn_random(to, &t);
+ if (r < 0)
+ return r;
+
+ r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
+ if (r < 0)
+ return r;
+
+ if (replace) {
+ r = renameat(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0)
+ r = -errno;
+ } else
+ r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0) {
+ (void) unlink_noerrno(t);
+ return r;
+ }
+
+ return 0;
+}
+
+int copy_times(int fdf, int fdt) {
+ struct timespec ut[2];
+ struct stat st;
+ usec_t crtime = 0;
+
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
+ if (fstat(fdf, &st) < 0)
+ return -errno;
+
+ ut[0] = st.st_atim;
+ ut[1] = st.st_mtim;
+
+ if (futimens(fdt, ut) < 0)
+ return -errno;
+
+ if (fd_getcrtime(fdf, &crtime) >= 0)
+ (void) fd_setcrtime(fdt, crtime);
+
+ return 0;
+}
+
+int copy_xattr(int fdf, int fdt) {
+ _cleanup_free_ char *bufa = NULL, *bufb = NULL;
+ size_t sza = 100, szb = 100;
+ ssize_t n;
+ int ret = 0;
+ const char *p;
+
+ for (;;) {
+ bufa = malloc(sza);
+ if (!bufa)
+ return -ENOMEM;
+
+ n = flistxattr(fdf, bufa, sza);
+ if (n == 0)
+ return 0;
+ if (n > 0)
+ break;
+ if (errno != ERANGE)
+ return -errno;
+
+ sza *= 2;
+
+ free(bufa);
+ bufa = NULL;
+ }
+
+ p = bufa;
+ while (n > 0) {
+ size_t l;
+
+ l = strlen(p);
+ assert(l < (size_t) n);
+
+ if (startswith(p, "user.")) {
+ ssize_t m;
+
+ if (!bufb) {
+ bufb = malloc(szb);
+ if (!bufb)
+ return -ENOMEM;
+ }
+
+ m = fgetxattr(fdf, p, bufb, szb);
+ if (m < 0) {
+ if (errno == ERANGE) {
+ szb *= 2;
+ free(bufb);
+ bufb = NULL;
+ continue;
+ }
+
+ return -errno;
+ }
+
+ if (fsetxattr(fdt, p, bufb, m, 0) < 0)
+ ret = -errno;
+ }
+
+ p += l + 1;
+ n -= l + 1;
+ }
+
+ return ret;
+}
diff --git a/src/basic/copy.h b/src/basic/copy.h
new file mode 100644
index 0000000..8de0cfb
--- /dev/null
+++ b/src/basic/copy.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <stdbool.h>
+#include <sys/types.h>
+
+int copy_file_fd(const char *from, int to, bool try_reflink);
+int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags);
+int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags);
+int copy_tree(const char *from, const char *to, bool merge);
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
+int copy_directory_fd(int dirfd, const char *to, bool merge);
+int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink);
+int copy_times(int fdf, int fdt);
+int copy_xattr(int fdf, int fdt);
diff --git a/src/basic/def.h b/src/basic/def.h
new file mode 100644
index 0000000..011c7c6
--- /dev/null
+++ b/src/basic/def.h
@@ -0,0 +1,86 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "util.h"
+
+#define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC)
+#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
+#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC)
+
+#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC)
+#define DEFAULT_START_LIMIT_BURST 5
+
+/* The default time after which exit-on-idle services exit. This
+ * should be kept lower than the watchdog timeout, because otherwise
+ * the watchdog pings will keep the loop busy. */
+#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
+
+#define SYSTEMD_CGROUP_CONTROLLER "systemd"
+
+#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
+#define SIGNALS_IGNORE SIGPIPE
+
+#define DIGITS "0123456789"
+#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
+#define ALPHANUMERICAL LETTERS DIGITS
+
+#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
+
+#ifdef HAVE_SPLIT_USR
+#define KBD_KEYMAP_DIRS \
+ "/usr/share/keymaps/\0" \
+ "/usr/share/kbd/keymaps/\0" \
+ "/usr/lib/kbd/keymaps/\0" \
+ "/lib/kbd/keymaps/\0"
+#else
+#define KBD_KEYMAP_DIRS \
+ "/usr/share/keymaps/\0" \
+ "/usr/share/kbd/keymaps/\0" \
+ "/usr/lib/kbd/keymaps/\0"
+#endif
+
+#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
+#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus"
+
+#ifdef ENABLE_KDBUS
+# define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS
+#else
+# define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS
+#endif
+
+#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
+#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus"
+
+#define PLYMOUTH_SOCKET { \
+ .un.sun_family = AF_UNIX, \
+ .un.sun_path = "\0/org/freedesktop/plymouthd", \
+ }
+
+#ifndef TTY_GID
+#define TTY_GID 5
+#endif
+
+#define NOTIFY_FD_MAX 768
+#define NOTIFY_BUFFER_MAX PIPE_BUF
diff --git a/src/basic/device-nodes.c b/src/basic/device-nodes.c
new file mode 100644
index 0000000..9d5af72
--- /dev/null
+++ b/src/basic/device-nodes.c
@@ -0,0 +1,80 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2011 Kay Sievers
+
+ 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 <stdio.h>
+
+#include "device-nodes.h"
+#include "utf8.h"
+
+int whitelisted_char_for_devnode(char c, const char *white) {
+
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ strchr("#+-.:=@_", c) != NULL ||
+ (white != NULL && strchr(white, c) != NULL))
+ return 1;
+
+ return 0;
+}
+
+int encode_devnode_name(const char *str, char *str_enc, size_t len) {
+ size_t i, j;
+
+ if (str == NULL || str_enc == NULL)
+ return -EINVAL;
+
+ for (i = 0, j = 0; str[i] != '\0'; i++) {
+ int seqlen;
+
+ seqlen = utf8_encoded_valid_unichar(&str[i]);
+ if (seqlen > 1) {
+
+ if (len-j < (size_t)seqlen)
+ return -EINVAL;
+
+ memcpy(&str_enc[j], &str[i], seqlen);
+ j += seqlen;
+ i += (seqlen-1);
+
+ } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) {
+
+ if (len-j < 4)
+ return -EINVAL;
+
+ sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
+ j += 4;
+
+ } else {
+ if (len-j < 1)
+ return -EINVAL;
+
+ str_enc[j] = str[i];
+ j++;
+ }
+ }
+
+ if (len-j < 1)
+ return -EINVAL;
+
+ str_enc[j] = '\0';
+ return 0;
+}
diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h
new file mode 100644
index 0000000..04ba489
--- /dev/null
+++ b/src/basic/device-nodes.h
@@ -0,0 +1,25 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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/>.
+***/
+
+int encode_devnode_name(const char *str, char *str_enc, size_t len);
+int whitelisted_char_for_devnode(char c, const char *additional);
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
new file mode 100644
index 0000000..ac7bbdc
--- /dev/null
+++ b/src/basic/env-util.c
@@ -0,0 +1,594 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <limits.h>
+#include <unistd.h>
+
+#include "strv.h"
+#include "utf8.h"
+#include "util.h"
+#include "env-util.h"
+#include "def.h"
+
+#define VALID_CHARS_ENV_NAME \
+ DIGITS LETTERS \
+ "_"
+
+#ifndef ARG_MAX
+#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
+#endif
+
+static bool env_name_is_valid_n(const char *e, size_t n) {
+ const char *p;
+
+ if (!e)
+ return false;
+
+ if (n <= 0)
+ return false;
+
+ if (e[0] >= '0' && e[0] <= '9')
+ return false;
+
+ /* POSIX says the overall size of the environment block cannot
+ * be > ARG_MAX, an individual assignment hence cannot be
+ * either. Discounting the equal sign and trailing NUL this
+ * hence leaves ARG_MAX-2 as longest possible variable
+ * name. */
+ if (n > ARG_MAX - 2)
+ return false;
+
+ for (p = e; p < e + n; p++)
+ if (!strchr(VALID_CHARS_ENV_NAME, *p))
+ return false;
+
+ return true;
+}
+
+bool env_name_is_valid(const char *e) {
+ if (!e)
+ return false;
+
+ return env_name_is_valid_n(e, strlen(e));
+}
+
+bool env_value_is_valid(const char *e) {
+ if (!e)
+ return false;
+
+ if (!utf8_is_valid(e))
+ return false;
+
+ /* bash allows tabs in environment variables, and so should
+ * we */
+ if (string_has_cc(e, "\t"))
+ return false;
+
+ /* POSIX says the overall size of the environment block cannot
+ * be > ARG_MAX, an individual assignment hence cannot be
+ * either. Discounting the shortest possible variable name of
+ * length 1, the equal sign and trailing NUL this hence leaves
+ * ARG_MAX-3 as longest possible variable value. */
+ if (strlen(e) > ARG_MAX - 3)
+ return false;
+
+ return true;
+}
+
+bool env_assignment_is_valid(const char *e) {
+ const char *eq;
+
+ eq = strchr(e, '=');
+ if (!eq)
+ return false;
+
+ if (!env_name_is_valid_n(e, eq - e))
+ return false;
+
+ if (!env_value_is_valid(eq + 1))
+ return false;
+
+ /* POSIX says the overall size of the environment block cannot
+ * be > ARG_MAX, hence the individual variable assignments
+ * cannot be either, but let's leave room for one trailing NUL
+ * byte. */
+ if (strlen(e) > ARG_MAX - 1)
+ return false;
+
+ return true;
+}
+
+bool strv_env_is_valid(char **e) {
+ char **p, **q;
+
+ STRV_FOREACH(p, e) {
+ size_t k;
+
+ if (!env_assignment_is_valid(*p))
+ return false;
+
+ /* Check if there are duplicate assginments */
+ k = strcspn(*p, "=");
+ STRV_FOREACH(q, p + 1)
+ if (strneq(*p, *q, k) && (*q)[k] == '=')
+ return false;
+ }
+
+ return true;
+}
+
+bool strv_env_name_or_assignment_is_valid(char **l) {
+ char **p, **q;
+
+ STRV_FOREACH(p, l) {
+ if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
+ return false;
+
+ STRV_FOREACH(q, p + 1)
+ if (streq(*p, *q))
+ return false;
+ }
+
+ return true;
+}
+
+static int env_append(char **r, char ***k, char **a) {
+ assert(r);
+ assert(k);
+
+ if (!a)
+ return 0;
+
+ /* Add the entries of a to *k unless they already exist in *r
+ * in which case they are overridden instead. This assumes
+ * there is enough space in the r array. */
+
+ for (; *a; a++) {
+ char **j;
+ size_t n;
+
+ n = strcspn(*a, "=");
+
+ if ((*a)[n] == '=')
+ n++;
+
+ for (j = r; j < *k; j++)
+ if (strneq(*j, *a, n))
+ break;
+
+ if (j >= *k)
+ (*k)++;
+ else
+ free(*j);
+
+ *j = strdup(*a);
+ if (!*j)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+char **strv_env_merge(unsigned n_lists, ...) {
+ size_t n = 0;
+ char **l, **k, **r;
+ va_list ap;
+ unsigned i;
+
+ /* Merges an arbitrary number of environment sets */
+
+ va_start(ap, n_lists);
+ for (i = 0; i < n_lists; i++) {
+ l = va_arg(ap, char**);
+ n += strv_length(l);
+ }
+ va_end(ap);
+
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ k = r;
+
+ va_start(ap, n_lists);
+ for (i = 0; i < n_lists; i++) {
+ l = va_arg(ap, char**);
+ if (env_append(r, &k, l) < 0)
+ goto fail;
+ }
+ va_end(ap);
+
+ *k = NULL;
+
+ return r;
+
+fail:
+ va_end(ap);
+ strv_free(r);
+
+ return NULL;
+}
+
+_pure_ static bool env_match(const char *t, const char *pattern) {
+ assert(t);
+ assert(pattern);
+
+ /* pattern a matches string a
+ * a matches a=
+ * a matches a=b
+ * a= matches a=
+ * a=b matches a=b
+ * a= does not match a
+ * a=b does not match a=
+ * a=b does not match a
+ * a=b does not match a=c */
+
+ if (streq(t, pattern))
+ return true;
+
+ if (!strchr(pattern, '=')) {
+ size_t l = strlen(pattern);
+
+ return strneq(t, pattern, l) && t[l] == '=';
+ }
+
+ return false;
+}
+
+char **strv_env_delete(char **x, unsigned n_lists, ...) {
+ size_t n, i = 0;
+ char **k, **r;
+ va_list ap;
+
+ /* Deletes every entry from x that is mentioned in the other
+ * string lists */
+
+ n = strv_length(x);
+
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ STRV_FOREACH(k, x) {
+ unsigned v;
+
+ va_start(ap, n_lists);
+ for (v = 0; v < n_lists; v++) {
+ char **l, **j;
+
+ l = va_arg(ap, char**);
+ STRV_FOREACH(j, l)
+ if (env_match(*k, *j))
+ goto skip;
+ }
+ va_end(ap);
+
+ r[i] = strdup(*k);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+
+ i++;
+ continue;
+
+ skip:
+ va_end(ap);
+ }
+
+ r[i] = NULL;
+
+ assert(i <= n);
+
+ return r;
+}
+
+char **strv_env_unset(char **l, const char *p) {
+
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(p);
+
+ /* Drops every occurrence of the env var setting p in the
+ * string list. Edits in-place. */
+
+ for (f = t = l; *f; f++) {
+
+ if (env_match(*f, p)) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_env_unset_many(char **l, ...) {
+
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ /* Like strv_env_unset() but applies many at once. Edits in-place. */
+
+ for (f = t = l; *f; f++) {
+ bool found = false;
+ const char *p;
+ va_list ap;
+
+ va_start(ap, l);
+
+ while ((p = va_arg(ap, const char*))) {
+ if (env_match(*f, p)) {
+ found = true;
+ break;
+ }
+ }
+
+ va_end(ap);
+
+ if (found) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_env_set(char **x, const char *p) {
+
+ char **k, **r;
+ char* m[2] = { (char*) p, NULL };
+
+ /* Overrides the env var setting of p, returns a new copy */
+
+ r = new(char*, strv_length(x)+2);
+ if (!r)
+ return NULL;
+
+ k = r;
+ if (env_append(r, &k, x) < 0)
+ goto fail;
+
+ if (env_append(r, &k, m) < 0)
+ goto fail;
+
+ *k = NULL;
+
+ return r;
+
+fail:
+ strv_free(r);
+ return NULL;
+}
+
+char *strv_env_get_n(char **l, const char *name, size_t k) {
+ char **i;
+
+ assert(name);
+
+ if (k <= 0)
+ return NULL;
+
+ STRV_FOREACH(i, l)
+ if (strneq(*i, name, k) &&
+ (*i)[k] == '=')
+ return *i + k + 1;
+
+ return NULL;
+}
+
+char *strv_env_get(char **l, const char *name) {
+ assert(name);
+
+ return strv_env_get_n(l, name, strlen(name));
+}
+
+char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
+ char **p, **q;
+ int k = 0;
+
+ STRV_FOREACH(p, e) {
+ size_t n;
+ bool duplicate = false;
+
+ if (!env_assignment_is_valid(*p)) {
+ if (invalid_callback)
+ invalid_callback(*p, userdata);
+ free(*p);
+ continue;
+ }
+
+ n = strcspn(*p, "=");
+ STRV_FOREACH(q, p + 1)
+ if (strneq(*p, *q, n) && (*q)[n] == '=') {
+ duplicate = true;
+ break;
+ }
+
+ if (duplicate) {
+ free(*p);
+ continue;
+ }
+
+ e[k++] = *p;
+ }
+
+ if (e)
+ e[k] = NULL;
+
+ return e;
+}
+
+char *replace_env(const char *format, char **env) {
+ enum {
+ WORD,
+ CURLY,
+ VARIABLE
+ } state = WORD;
+
+ const char *e, *word = format;
+ char *r = NULL, *k;
+
+ assert(format);
+
+ for (e = format; *e; e ++) {
+
+ switch (state) {
+
+ case WORD:
+ if (*e == '$')
+ state = CURLY;
+ break;
+
+ case CURLY:
+ if (*e == '{') {
+ k = strnappend(r, word, e-word-1);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE;
+
+ } else if (*e == '$') {
+ k = strnappend(r, word, e-word);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ } else
+ state = WORD;
+ break;
+
+ case VARIABLE:
+ if (*e == '}') {
+ const char *t;
+
+ t = strempty(strv_env_get_n(env, word+2, e-word-2));
+
+ k = strappend(r, t);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+ }
+ }
+
+ k = strnappend(r, word, e-word);
+ if (!k)
+ goto fail;
+
+ free(r);
+ return k;
+
+fail:
+ free(r);
+ return NULL;
+}
+
+char **replace_env_argv(char **argv, char **env) {
+ char **ret, **i;
+ unsigned k = 0, l = 0;
+
+ l = strv_length(argv);
+
+ ret = new(char*, l+1);
+ if (!ret)
+ return NULL;
+
+ STRV_FOREACH(i, argv) {
+
+ /* If $FOO appears as single word, replace it by the split up variable */
+ if ((*i)[0] == '$' && (*i)[1] != '{') {
+ char *e;
+ char **w, **m = NULL;
+ unsigned q;
+
+ e = strv_env_get(env, *i+1);
+ if (e) {
+ int r;
+
+ r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
+ if (r < 0) {
+ ret[k] = NULL;
+ strv_free(ret);
+ return NULL;
+ }
+ } else
+ m = NULL;
+
+ q = strv_length(m);
+ l = l + q - 1;
+
+ w = realloc(ret, sizeof(char*) * (l+1));
+ if (!w) {
+ ret[k] = NULL;
+ strv_free(ret);
+ strv_free(m);
+ return NULL;
+ }
+
+ ret = w;
+ if (m) {
+ memcpy(ret + k, m, q * sizeof(char*));
+ free(m);
+ }
+
+ k += q;
+ continue;
+ }
+
+ /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+ ret[k] = replace_env(*i, env);
+ if (!ret[k]) {
+ strv_free(ret);
+ return NULL;
+ }
+ k++;
+ }
+
+ ret[k] = NULL;
+ return ret;
+}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
new file mode 100644
index 0000000..803aa61
--- /dev/null
+++ b/src/basic/env-util.h
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <stdbool.h>
+
+#include "macro.h"
+
+bool env_name_is_valid(const char *e);
+bool env_value_is_valid(const char *e);
+bool env_assignment_is_valid(const char *e);
+
+char *replace_env(const char *format, char **env);
+char **replace_env_argv(char **argv, char **env);
+
+bool strv_env_is_valid(char **e);
+#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
+char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
+
+bool strv_env_name_or_assignment_is_valid(char **l);
+
+char **strv_env_merge(unsigned n_lists, ...);
+char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */
+
+char **strv_env_set(char **x, const char *p); /* New copy ... */
+char **strv_env_unset(char **l, const char *p); /* In place ... */
+char **strv_env_unset_many(char **l, ...) _sentinel_;
+
+char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
+char *strv_env_get(char **x, const char *n) _pure_;
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
new file mode 100644
index 0000000..34d1331
--- /dev/null
+++ b/src/basic/errno-list.c
@@ -0,0 +1,58 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <string.h>
+
+#include "util.h"
+#include "errno-list.h"
+
+static const struct errno_name* lookup_errno(register const char *str,
+ register unsigned int len);
+
+#include "errno-to-name.h"
+#include "errno-from-name.h"
+
+const char *errno_to_name(int id) {
+
+ if (id < 0)
+ id = -id;
+
+ if (id >= (int) ELEMENTSOF(errno_names))
+ return NULL;
+
+ return errno_names[id];
+}
+
+int errno_from_name(const char *name) {
+ const struct errno_name *sc;
+
+ assert(name);
+
+ sc = lookup_errno(name, strlen(name));
+ if (!sc)
+ return 0;
+
+ return sc->id;
+}
+
+int errno_max(void) {
+ return ELEMENTSOF(errno_names);
+}
diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h
new file mode 100644
index 0000000..ba53329
--- /dev/null
+++ b/src/basic/errno-list.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/>.
+***/
+
+const char *errno_to_name(int id);
+int errno_from_name(const char *name);
+
+int errno_max(void);
diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h
new file mode 100644
index 0000000..7033138
--- /dev/null
+++ b/src/basic/ether-addr-util.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Tom Gundersen
+
+ 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 <net/ethernet.h>
+
+#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
+#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
new file mode 100644
index 0000000..c09efdd
--- /dev/null
+++ b/src/basic/exit-status.c
@@ -0,0 +1,241 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "exit-status.h"
+#include "set.h"
+#include "macro.h"
+
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
+
+ /* We cast to int here, so that -Wenum doesn't complain that
+ * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
+
+ switch ((int) status) {
+
+ case EXIT_SUCCESS:
+ return "SUCCESS";
+
+ case EXIT_FAILURE:
+ return "FAILURE";
+ }
+
+
+ if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
+ switch ((int) status) {
+
+ case EXIT_CHDIR:
+ return "CHDIR";
+
+ case EXIT_NICE:
+ return "NICE";
+
+ case EXIT_FDS:
+ return "FDS";
+
+ case EXIT_EXEC:
+ return "EXEC";
+
+ case EXIT_MEMORY:
+ return "MEMORY";
+
+ case EXIT_LIMITS:
+ return "LIMITS";
+
+ case EXIT_OOM_ADJUST:
+ return "OOM_ADJUST";
+
+ case EXIT_SIGNAL_MASK:
+ return "SIGNAL_MASK";
+
+ case EXIT_STDIN:
+ return "STDIN";
+
+ case EXIT_STDOUT:
+ return "STDOUT";
+
+ case EXIT_CHROOT:
+ return "CHROOT";
+
+ case EXIT_IOPRIO:
+ return "IOPRIO";
+
+ case EXIT_TIMERSLACK:
+ return "TIMERSLACK";
+
+ case EXIT_SECUREBITS:
+ return "SECUREBITS";
+
+ case EXIT_SETSCHEDULER:
+ return "SETSCHEDULER";
+
+ case EXIT_CPUAFFINITY:
+ return "CPUAFFINITY";
+
+ case EXIT_GROUP:
+ return "GROUP";
+
+ case EXIT_USER:
+ return "USER";
+
+ case EXIT_CAPABILITIES:
+ return "CAPABILITIES";
+
+ case EXIT_CGROUP:
+ return "CGROUP";
+
+ case EXIT_SETSID:
+ return "SETSID";
+
+ case EXIT_CONFIRM:
+ return "CONFIRM";
+
+ case EXIT_STDERR:
+ return "STDERR";
+
+ case EXIT_PAM:
+ return "PAM";
+
+ case EXIT_NETWORK:
+ return "NETWORK";
+
+ case EXIT_NAMESPACE:
+ return "NAMESPACE";
+
+ case EXIT_NO_NEW_PRIVILEGES:
+ return "NO_NEW_PRIVILEGES";
+
+ case EXIT_SECCOMP:
+ return "SECCOMP";
+
+ case EXIT_SELINUX_CONTEXT:
+ return "SELINUX_CONTEXT";
+
+ case EXIT_PERSONALITY:
+ return "PERSONALITY";
+
+ case EXIT_APPARMOR_PROFILE:
+ return "APPARMOR";
+
+ case EXIT_ADDRESS_FAMILIES:
+ return "ADDRESS_FAMILIES";
+
+ case EXIT_RUNTIME_DIRECTORY:
+ return "RUNTIME_DIRECTORY";
+
+ case EXIT_CHOWN:
+ return "CHOWN";
+
+ case EXIT_MAKE_STARTER:
+ return "MAKE_STARTER";
+
+ case EXIT_BUS_ENDPOINT:
+ return "BUS_ENDPOINT";
+ }
+ }
+
+ if (level == EXIT_STATUS_LSB) {
+ switch ((int) status) {
+
+ case EXIT_INVALIDARGUMENT:
+ return "INVALIDARGUMENT";
+
+ case EXIT_NOTIMPLEMENTED:
+ return "NOTIMPLEMENTED";
+
+ case EXIT_NOPERMISSION:
+ return "NOPERMISSION";
+
+ case EXIT_NOTINSTALLED:
+ return "NOTINSTALLED";
+
+ case EXIT_NOTCONFIGURED:
+ return "NOTCONFIGURED";
+
+ case EXIT_NOTRUNNING:
+ return "NOTRUNNING";
+ }
+ }
+
+ return NULL;
+}
+
+
+bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
+
+ if (code == CLD_EXITED)
+ return status == 0 ||
+ (success_status &&
+ set_contains(success_status->status, INT_TO_PTR(status)));
+
+ /* If a daemon does not implement handlers for some of the
+ * signals that's not considered an unclean shutdown */
+ if (code == CLD_KILLED)
+ return
+ status == SIGHUP ||
+ status == SIGINT ||
+ status == SIGTERM ||
+ status == SIGPIPE ||
+ (success_status &&
+ set_contains(success_status->signal, INT_TO_PTR(status)));
+
+ return false;
+}
+
+bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
+
+ if (is_clean_exit(code, status, success_status))
+ return true;
+
+ return
+ code == CLD_EXITED &&
+ (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
+}
+
+void exit_status_set_free(ExitStatusSet *x) {
+ assert(x);
+
+ set_free(x->status);
+ set_free(x->signal);
+ x->status = x->signal = NULL;
+}
+
+bool exit_status_set_is_empty(ExitStatusSet *x) {
+ if (!x)
+ return true;
+
+ return set_isempty(x->status) && set_isempty(x->signal);
+}
+
+bool exit_status_set_test(ExitStatusSet *x, int code, int status) {
+
+ if (exit_status_set_is_empty(x))
+ return false;
+
+ if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
+ return true;
+
+ if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status)))
+ return true;
+
+ return false;
+}
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
new file mode 100644
index 0000000..7259cd1
--- /dev/null
+++ b/src/basic/exit-status.h
@@ -0,0 +1,103 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+
+#include "set.h"
+
+typedef enum ExitStatus {
+ /* EXIT_SUCCESS defined by libc */
+ /* EXIT_FAILURE defined by libc */
+ EXIT_INVALIDARGUMENT = 2,
+ EXIT_NOTIMPLEMENTED = 3,
+ EXIT_NOPERMISSION = 4,
+ EXIT_NOTINSTALLED = 5,
+ EXIT_NOTCONFIGURED = 6,
+ EXIT_NOTRUNNING = 7,
+
+ /* The LSB suggests that error codes >= 200 are "reserved". We
+ * use them here under the assumption that they hence are
+ * unused by init scripts.
+ *
+ * http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
+
+ EXIT_CHDIR = 200,
+ EXIT_NICE,
+ EXIT_FDS,
+ EXIT_EXEC,
+ EXIT_MEMORY,
+ EXIT_LIMITS,
+ EXIT_OOM_ADJUST,
+ EXIT_SIGNAL_MASK,
+ EXIT_STDIN,
+ EXIT_STDOUT,
+ EXIT_CHROOT, /* 210 */
+ EXIT_IOPRIO,
+ EXIT_TIMERSLACK,
+ EXIT_SECUREBITS,
+ EXIT_SETSCHEDULER,
+ EXIT_CPUAFFINITY,
+ EXIT_GROUP,
+ EXIT_USER,
+ EXIT_CAPABILITIES,
+ EXIT_CGROUP,
+ EXIT_SETSID, /* 220 */
+ EXIT_CONFIRM,
+ EXIT_STDERR,
+ _EXIT_RESERVED, /* used to be tcpwrap, don't reuse! */
+ EXIT_PAM,
+ EXIT_NETWORK,
+ EXIT_NAMESPACE,
+ EXIT_NO_NEW_PRIVILEGES,
+ EXIT_SECCOMP,
+ EXIT_SELINUX_CONTEXT,
+ EXIT_PERSONALITY, /* 230 */
+ EXIT_APPARMOR_PROFILE,
+ EXIT_ADDRESS_FAMILIES,
+ EXIT_RUNTIME_DIRECTORY,
+ EXIT_MAKE_STARTER,
+ EXIT_CHOWN,
+ EXIT_BUS_ENDPOINT,
+ EXIT_SMACK_PROCESS_LABEL,
+} ExitStatus;
+
+typedef enum ExitStatusLevel {
+ EXIT_STATUS_MINIMAL,
+ EXIT_STATUS_SYSTEMD,
+ EXIT_STATUS_LSB,
+ EXIT_STATUS_FULL = EXIT_STATUS_LSB
+} ExitStatusLevel;
+
+typedef struct ExitStatusSet {
+ Set *status;
+ Set *signal;
+} ExitStatusSet;
+
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
+
+bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
+bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
+
+void exit_status_set_free(ExitStatusSet *x);
+bool exit_status_set_is_empty(ExitStatusSet *x);
+bool exit_status_set_test(ExitStatusSet *x, int code, int status);
diff --git a/src/basic/fdset.c b/src/basic/fdset.c
new file mode 100644
index 0000000..6101b62
--- /dev/null
+++ b/src/basic/fdset.c
@@ -0,0 +1,285 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "set.h"
+#include "util.h"
+#include "macro.h"
+#include "fdset.h"
+#include "sd-daemon.h"
+
+#define MAKE_SET(s) ((Set*) s)
+#define MAKE_FDSET(s) ((FDSet*) s)
+
+/* Make sure we can distinguish fd 0 and NULL */
+#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
+#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
+
+FDSet *fdset_new(void) {
+ return MAKE_FDSET(set_new(NULL));
+}
+
+int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds) {
+ unsigned i;
+ FDSet *s;
+ int r;
+
+ assert(ret);
+
+ s = fdset_new();
+ if (!s)
+ return -ENOMEM;
+
+ for (i = 0; i < n_fds; i++) {
+
+ r = fdset_put(s, fds[i]);
+ if (r < 0) {
+ set_free(MAKE_SET(s));
+ return r;
+ }
+ }
+
+ *ret = s;
+ return 0;
+}
+
+FDSet* fdset_free(FDSet *s) {
+ void *p;
+
+ while ((p = set_steal_first(MAKE_SET(s)))) {
+ /* Valgrind's fd might have ended up in this set here,
+ * due to fdset_new_fill(). We'll ignore all failures
+ * here, so that the EBADFD that valgrind will return
+ * us on close() doesn't influence us */
+
+ /* When reloading duplicates of the private bus
+ * connection fds and suchlike are closed here, which
+ * has no effect at all, since they are only
+ * duplicates. So don't be surprised about these log
+ * messages. */
+
+ log_debug("Closing left-over fd %i", PTR_TO_FD(p));
+ close_nointr(PTR_TO_FD(p));
+ }
+
+ set_free(MAKE_SET(s));
+ return NULL;
+}
+
+int fdset_put(FDSet *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ return set_put(MAKE_SET(s), FD_TO_PTR(fd));
+}
+
+int fdset_consume(FDSet *s, int fd) {
+ int r;
+
+ assert(s);
+ assert(fd >= 0);
+
+ r = fdset_put(s, fd);
+ if (r <= 0)
+ safe_close(fd);
+
+ return r;
+}
+
+int fdset_put_dup(FDSet *s, int fd) {
+ int copy, r;
+
+ assert(s);
+ assert(fd >= 0);
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ r = fdset_put(s, copy);
+ if (r < 0) {
+ safe_close(copy);
+ return r;
+ }
+
+ return copy;
+}
+
+bool fdset_contains(FDSet *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
+}
+
+int fdset_remove(FDSet *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
+}
+
+int fdset_new_fill(FDSet **_s) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0;
+ FDSet *s;
+
+ assert(_s);
+
+ /* Creates an fdset and fills in all currently open file
+ * descriptors. */
+
+ d = opendir("/proc/self/fd");
+ if (!d)
+ return -errno;
+
+ s = fdset_new();
+ if (!s) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ while ((de = readdir(d))) {
+ int fd = -1;
+
+ if (hidden_file(de->d_name))
+ continue;
+
+ r = safe_atoi(de->d_name, &fd);
+ if (r < 0)
+ goto finish;
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ r = fdset_put(s, fd);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = 0;
+ *_s = s;
+ s = NULL;
+
+finish:
+ /* We won't close the fds here! */
+ if (s)
+ set_free(MAKE_SET(s));
+
+ return r;
+}
+
+int fdset_cloexec(FDSet *fds, bool b) {
+ Iterator i;
+ void *p;
+ int r;
+
+ assert(fds);
+
+ SET_FOREACH(p, MAKE_SET(fds), i)
+ if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
+ return r;
+
+ return 0;
+}
+
+int fdset_new_listen_fds(FDSet **_s, bool unset) {
+ int n, fd, r;
+ FDSet *s;
+
+ assert(_s);
+
+ /* Creates an fdset and fills in all passed file descriptors */
+
+ s = fdset_new();
+ if (!s) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ n = sd_listen_fds(unset);
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
+ r = fdset_put(s, fd);
+ if (r < 0)
+ goto fail;
+ }
+
+ *_s = s;
+ return 0;
+
+
+fail:
+ if (s)
+ set_free(MAKE_SET(s));
+
+ return r;
+}
+
+int fdset_close_others(FDSet *fds) {
+ void *e;
+ Iterator i;
+ int *a;
+ unsigned j, m;
+
+ j = 0, m = fdset_size(fds);
+ a = alloca(sizeof(int) * m);
+ SET_FOREACH(e, MAKE_SET(fds), i)
+ a[j++] = PTR_TO_FD(e);
+
+ assert(j == m);
+
+ return close_all_fds(a, j);
+}
+
+unsigned fdset_size(FDSet *fds) {
+ return set_size(MAKE_SET(fds));
+}
+
+bool fdset_isempty(FDSet *fds) {
+ return set_isempty(MAKE_SET(fds));
+}
+
+int fdset_iterate(FDSet *s, Iterator *i) {
+ void *p;
+
+ p = set_iterate(MAKE_SET(s), i);
+ if (!p)
+ return -ENOENT;
+
+ return PTR_TO_FD(p);
+}
+
+int fdset_steal_first(FDSet *fds) {
+ void *p;
+
+ p = set_steal_first(MAKE_SET(fds));
+ if (!p)
+ return -ENOENT;
+
+ return PTR_TO_FD(p);
+}
diff --git a/src/basic/fdset.h b/src/basic/fdset.h
new file mode 100644
index 0000000..340438d
--- /dev/null
+++ b/src/basic/fdset.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "set.h"
+
+typedef struct FDSet FDSet;
+
+FDSet* fdset_new(void);
+FDSet* fdset_free(FDSet *s);
+
+int fdset_put(FDSet *s, int fd);
+int fdset_put_dup(FDSet *s, int fd);
+int fdset_consume(FDSet *s, int fd);
+
+bool fdset_contains(FDSet *s, int fd);
+int fdset_remove(FDSet *s, int fd);
+
+int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds);
+int fdset_new_fill(FDSet **ret);
+int fdset_new_listen_fds(FDSet **ret, bool unset);
+
+int fdset_cloexec(FDSet *fds, bool b);
+
+int fdset_close_others(FDSet *fds);
+
+unsigned fdset_size(FDSet *fds);
+bool fdset_isempty(FDSet *fds);
+
+int fdset_iterate(FDSet *s, Iterator *i);
+
+int fdset_steal_first(FDSet *fds);
+
+#define FDSET_FOREACH(fd, fds, i) \
+ for ((i) = ITERATOR_FIRST, (fd) = fdset_iterate((fds), &(i)); (fd) >= 0; (fd) = fdset_iterate((fds), &(i)))
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free);
+#define _cleanup_fdset_free_ _cleanup_(fdset_freep)
diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c
new file mode 100644
index 0000000..bec988c
--- /dev/null
+++ b/src/basic/fileio-label.c
@@ -0,0 +1,68 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Harald Hoyer
+
+ 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 "util.h"
+#include "selinux-util.h"
+#include "fileio-label.h"
+
+int write_string_file_atomic_label(const char *fn, const char *line) {
+ int r;
+
+ r = mac_selinux_create_file_prepare(fn, S_IFREG);
+ if (r < 0)
+ return r;
+
+ r = write_string_file_atomic(fn, line);
+
+ mac_selinux_create_file_clear();
+
+ return r;
+}
+
+int write_env_file_label(const char *fname, char **l) {
+ int r;
+
+ r = mac_selinux_create_file_prepare(fname, S_IFREG);
+ if (r < 0)
+ return r;
+
+ r = write_env_file(fname, l);
+
+ mac_selinux_create_file_clear();
+
+ return r;
+}
+
+int fopen_temporary_label(const char *target,
+ const char *path, FILE **f, char **temp_path) {
+ int r;
+
+ r = mac_selinux_create_file_prepare(target, S_IFREG);
+ if (r < 0)
+ return r;
+
+ r = fopen_temporary(path, f, temp_path);
+
+ mac_selinux_create_file_clear();
+
+ return r;
+}
diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h
new file mode 100644
index 0000000..25fa351
--- /dev/null
+++ b/src/basic/fileio-label.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Harald Hoyer
+
+ 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 <stdio.h>
+#include "fileio.h"
+
+int write_string_file_atomic_label(const char *fn, const char *line);
+int write_env_file_label(const char *fname, char **l);
+int fopen_temporary_label(const char *target,
+ const char *path, FILE **f, char **temp_path);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
new file mode 100644
index 0000000..ff6b1a7
--- /dev/null
+++ b/src/basic/fileio.c
@@ -0,0 +1,817 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <unistd.h>
+
+#include "util.h"
+#include "strv.h"
+#include "utf8.h"
+#include "ctype.h"
+#include "fileio.h"
+
+int write_string_stream(FILE *f, const char *line) {
+ assert(f);
+ assert(line);
+
+ errno = 0;
+
+ fputs(line, f);
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f))
+ return errno ? -errno : -EIO;
+
+ return 0;
+}
+
+int write_string_file(const char *fn, const char *line) {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ assert(fn);
+ assert(line);
+
+ f = fopen(fn, "we");
+ if (!f)
+ return -errno;
+
+ return write_string_stream(f, line);
+}
+
+int write_string_file_no_create(const char *fn, const char *line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ int fd;
+
+ assert(fn);
+ assert(line);
+
+ /* We manually build our own version of fopen(..., "we") that
+ * works without O_CREAT */
+ fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return -errno;
+
+ f = fdopen(fd, "we");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ return write_string_stream(f, line);
+}
+
+int write_string_file_atomic(const char *fn, const char *line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(fn);
+ assert(line);
+
+ r = fopen_temporary(fn, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ r = write_string_stream(f, line);
+ if (r >= 0) {
+ if (rename(p, fn) < 0)
+ r = -errno;
+ }
+
+ if (r < 0)
+ unlink(p);
+
+ return r;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char t[LINE_MAX], *c;
+
+ assert(fn);
+ assert(line);
+
+ f = fopen(fn, "re");
+ if (!f)
+ return -errno;
+
+ if (!fgets(t, sizeof(t), f)) {
+
+ if (ferror(f))
+ return errno ? -errno : -EIO;
+
+ t[0] = 0;
+ }
+
+ c = strdup(t);
+ if (!c)
+ return -ENOMEM;
+ truncate_nl(c);
+
+ *line = c;
+ return 0;
+}
+
+int read_full_stream(FILE *f, char **contents, size_t *size) {
+ size_t n, l;
+ _cleanup_free_ char *buf = NULL;
+ struct stat st;
+
+ assert(f);
+ assert(contents);
+
+ if (fstat(fileno(f), &st) < 0)
+ return -errno;
+
+ n = LINE_MAX;
+
+ if (S_ISREG(st.st_mode)) {
+
+ /* Safety check */
+ if (st.st_size > 4*1024*1024)
+ return -E2BIG;
+
+ /* Start with the right file size, but be prepared for
+ * files from /proc which generally report a file size
+ * of 0 */
+ if (st.st_size > 0)
+ n = st.st_size;
+ }
+
+ l = 0;
+ for (;;) {
+ char *t;
+ size_t k;
+
+ t = realloc(buf, n+1);
+ if (!t)
+ return -ENOMEM;
+
+ buf = t;
+ k = fread(buf + l, 1, n - l, f);
+
+ if (k <= 0) {
+ if (ferror(f))
+ return -errno;
+
+ break;
+ }
+
+ l += k;
+ n *= 2;
+
+ /* Safety check */
+ if (n > 4*1024*1024)
+ return -E2BIG;
+ }
+
+ buf[l] = 0;
+ *contents = buf;
+ buf = NULL; /* do not free */
+
+ if (size)
+ *size = l;
+
+ return 0;
+}
+
+int read_full_file(const char *fn, char **contents, size_t *size) {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ assert(fn);
+ assert(contents);
+
+ f = fopen(fn, "re");
+ if (!f)
+ return -errno;
+
+ return read_full_stream(f, contents, size);
+}
+
+static int parse_env_file_internal(
+ FILE *f,
+ const char *fname,
+ const char *newline,
+ int (*push) (const char *filename, unsigned line,
+ const char *key, char *value, void *userdata, int *n_pushed),
+ void *userdata,
+ int *n_pushed) {
+
+ _cleanup_free_ char *contents = NULL, *key = NULL;
+ size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
+ char *p, *value = NULL;
+ int r;
+ unsigned line = 1;
+
+ enum {
+ PRE_KEY,
+ KEY,
+ PRE_VALUE,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE_VALUE,
+ SINGLE_QUOTE_VALUE_ESCAPE,
+ DOUBLE_QUOTE_VALUE,
+ DOUBLE_QUOTE_VALUE_ESCAPE,
+ COMMENT,
+ COMMENT_ESCAPE
+ } state = PRE_KEY;
+
+ assert(newline);
+
+ if (f)
+ r = read_full_stream(f, &contents, NULL);
+ else
+ r = read_full_file(fname, &contents, NULL);
+ if (r < 0)
+ return r;
+
+ for (p = contents; *p; p++) {
+ char c = *p;
+
+ switch (state) {
+
+ case PRE_KEY:
+ if (strchr(COMMENTS, c))
+ state = COMMENT;
+ else if (!strchr(WHITESPACE, c)) {
+ state = KEY;
+ last_key_whitespace = (size_t) -1;
+
+ if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ key[n_key++] = c;
+ }
+ break;
+
+ case KEY:
+ if (strchr(newline, c)) {
+ state = PRE_KEY;
+ line ++;
+ n_key = 0;
+ } else if (c == '=') {
+ state = PRE_VALUE;
+ last_value_whitespace = (size_t) -1;
+ } else {
+ if (!strchr(WHITESPACE, c))
+ last_key_whitespace = (size_t) -1;
+ else if (last_key_whitespace == (size_t) -1)
+ last_key_whitespace = n_key;
+
+ if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ key[n_key++] = c;
+ }
+
+ break;
+
+ case PRE_VALUE:
+ if (strchr(newline, c)) {
+ state = PRE_KEY;
+ line ++;
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(fname, line, key, value, userdata, n_pushed);
+ if (r < 0)
+ goto fail;
+
+ n_key = 0;
+ value = NULL;
+ value_alloc = n_value = 0;
+
+ } else if (c == '\'')
+ state = SINGLE_QUOTE_VALUE;
+ else if (c == '\"')
+ state = DOUBLE_QUOTE_VALUE;
+ else if (c == '\\')
+ state = VALUE_ESCAPE;
+ else if (!strchr(WHITESPACE, c)) {
+ state = VALUE;
+
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case VALUE:
+ if (strchr(newline, c)) {
+ state = PRE_KEY;
+ line ++;
+
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ /* Chomp off trailing whitespace from value */
+ if (last_value_whitespace != (size_t) -1)
+ value[last_value_whitespace] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(fname, line, key, value, userdata, n_pushed);
+ if (r < 0)
+ goto fail;
+
+ n_key = 0;
+ value = NULL;
+ value_alloc = n_value = 0;
+
+ } else if (c == '\\') {
+ state = VALUE_ESCAPE;
+ last_value_whitespace = (size_t) -1;
+ } else {
+ if (!strchr(WHITESPACE, c))
+ last_value_whitespace = (size_t) -1;
+ else if (last_value_whitespace == (size_t) -1)
+ last_value_whitespace = n_value;
+
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case VALUE_ESCAPE:
+ state = VALUE;
+
+ if (!strchr(newline, c)) {
+ /* Escaped newlines we eat up entirely */
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case SINGLE_QUOTE_VALUE:
+ if (c == '\'')
+ state = PRE_VALUE;
+ else if (c == '\\')
+ state = SINGLE_QUOTE_VALUE_ESCAPE;
+ else {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case SINGLE_QUOTE_VALUE_ESCAPE:
+ state = SINGLE_QUOTE_VALUE;
+
+ if (!strchr(newline, c)) {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case DOUBLE_QUOTE_VALUE:
+ if (c == '\"')
+ state = PRE_VALUE;
+ else if (c == '\\')
+ state = DOUBLE_QUOTE_VALUE_ESCAPE;
+ else {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case DOUBLE_QUOTE_VALUE_ESCAPE:
+ state = DOUBLE_QUOTE_VALUE;
+
+ if (!strchr(newline, c)) {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case COMMENT:
+ if (c == '\\')
+ state = COMMENT_ESCAPE;
+ else if (strchr(newline, c)) {
+ state = PRE_KEY;
+ line ++;
+ }
+ break;
+
+ case COMMENT_ESCAPE:
+ state = COMMENT;
+ break;
+ }
+ }
+
+ if (state == PRE_VALUE ||
+ state == VALUE ||
+ state == VALUE_ESCAPE ||
+ state == SINGLE_QUOTE_VALUE ||
+ state == SINGLE_QUOTE_VALUE_ESCAPE ||
+ state == DOUBLE_QUOTE_VALUE ||
+ state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ if (state == VALUE)
+ if (last_value_whitespace != (size_t) -1)
+ value[last_value_whitespace] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(fname, line, key, value, userdata, n_pushed);
+ if (r < 0)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ free(value);
+ return r;
+}
+
+static int parse_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ const char *k;
+ va_list aq, *ap = userdata;
+
+ if (!utf8_is_valid(key)) {
+ _cleanup_free_ char *p;
+
+ p = utf8_escape_invalid(key);
+ log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
+ return -EINVAL;
+ }
+
+ if (value && !utf8_is_valid(value)) {
+ _cleanup_free_ char *p;
+
+ p = utf8_escape_invalid(value);
+ log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
+ return -EINVAL;
+ }
+
+ va_copy(aq, *ap);
+
+ while ((k = va_arg(aq, const char *))) {
+ char **v;
+
+ v = va_arg(aq, char **);
+
+ if (streq(key, k)) {
+ va_end(aq);
+ free(*v);
+ *v = value;
+
+ if (n_pushed)
+ (*n_pushed)++;
+
+ return 1;
+ }
+ }
+
+ va_end(aq);
+ free(value);
+
+ return 0;
+}
+
+int parse_env_file(
+ const char *fname,
+ const char *newline, ...) {
+
+ va_list ap;
+ int r, n_pushed = 0;
+
+ if (!newline)
+ newline = NEWLINE;
+
+ va_start(ap, newline);
+ r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
+ va_end(ap);
+
+ return r < 0 ? r : n_pushed;
+}
+
+static int load_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+ char ***m = userdata;
+ char *p;
+ int r;
+
+ if (!utf8_is_valid(key)) {
+ _cleanup_free_ char *t = utf8_escape_invalid(key);
+
+ log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
+ return -EINVAL;
+ }
+
+ if (value && !utf8_is_valid(value)) {
+ _cleanup_free_ char *t = utf8_escape_invalid(value);
+
+ log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
+ return -EINVAL;
+ }
+
+ p = strjoin(key, "=", strempty(value), NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = strv_consume(m, p);
+ if (r < 0)
+ return r;
+
+ if (n_pushed)
+ (*n_pushed)++;
+
+ free(value);
+ return 0;
+}
+
+int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
+ char **m = NULL;
+ int r;
+
+ if (!newline)
+ newline = NEWLINE;
+
+ r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
+ if (r < 0) {
+ strv_free(m);
+ return r;
+ }
+
+ *rl = m;
+ return 0;
+}
+
+static int load_env_file_push_pairs(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+ char ***m = userdata;
+ int r;
+
+ if (!utf8_is_valid(key)) {
+ _cleanup_free_ char *t = utf8_escape_invalid(key);
+
+ log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
+ return -EINVAL;
+ }
+
+ if (value && !utf8_is_valid(value)) {
+ _cleanup_free_ char *t = utf8_escape_invalid(value);
+
+ log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
+ return -EINVAL;
+ }
+
+ r = strv_extend(m, key);
+ if (r < 0)
+ return -ENOMEM;
+
+ if (!value) {
+ r = strv_extend(m, "");
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ r = strv_push(m, value);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_pushed)
+ (*n_pushed)++;
+
+ return 0;
+}
+
+int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
+ char **m = NULL;
+ int r;
+
+ if (!newline)
+ newline = NEWLINE;
+
+ r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
+ if (r < 0) {
+ strv_free(m);
+ return r;
+ }
+
+ *rl = m;
+ return 0;
+}
+
+static void write_env_var(FILE *f, const char *v) {
+ const char *p;
+
+ p = strchr(v, '=');
+ if (!p) {
+ /* Fallback */
+ fputs(v, f);
+ fputc('\n', f);
+ return;
+ }
+
+ p++;
+ fwrite(v, 1, p-v, f);
+
+ if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
+ fputc('\"', f);
+
+ for (; *p; p++) {
+ if (strchr(SHELL_NEED_ESCAPE, *p))
+ fputc('\\', f);
+
+ fputc(*p, f);
+ }
+
+ fputc('\"', f);
+ } else
+ fputs(p, f);
+
+ fputc('\n', f);
+}
+
+int write_env_file(const char *fname, char **l) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ char **i;
+ int r;
+
+ assert(fname);
+
+ r = fopen_temporary(fname, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ STRV_FOREACH(i, l)
+ write_env_var(f, *i);
+
+ r = fflush_and_check(f);
+ if (r >= 0) {
+ if (rename(p, fname) >= 0)
+ return 0;
+
+ r = -errno;
+ }
+
+ unlink(p);
+ return r;
+}
+
+int executable_is_script(const char *path, char **interpreter) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ int len;
+ char *ans;
+
+ assert(path);
+
+ r = read_one_line_file(path, &line);
+ if (r < 0)
+ return r;
+
+ if (!startswith(line, "#!"))
+ return 0;
+
+ ans = strstrip(line + 2);
+ len = strcspn(ans, " \t");
+
+ if (len == 0)
+ return 0;
+
+ ans = strndup(ans, len);
+ if (!ans)
+ return -ENOMEM;
+
+ *interpreter = ans;
+ return 1;
+}
+
+/**
+ * Retrieve one field from a file like /proc/self/status. pattern
+ * should start with '\n' and end with a ':'. Whitespace and zeros
+ * after the ':' will be skipped. field must be freed afterwards.
+ */
+int get_status_field(const char *filename, const char *pattern, char **field) {
+ _cleanup_free_ char *status = NULL;
+ char *t;
+ size_t len;
+ int r;
+
+ assert(filename);
+ assert(pattern);
+ assert(field);
+
+ r = read_full_file(filename, &status, NULL);
+ if (r < 0)
+ return r;
+
+ t = strstr(status, pattern);
+ if (!t)
+ return -ENOENT;
+
+ t += strlen(pattern);
+ if (*t) {
+ t += strspn(t, " \t");
+
+ /* Also skip zeros, because when this is used for
+ * capabilities, we don't want the zeros. This way the
+ * same capability set always maps to the same string,
+ * irrespective of the total capability set size. For
+ * other numbers it shouldn't matter. */
+ t += strspn(t, "0");
+ /* Back off one char if there's nothing but whitespace
+ and zeros */
+ if (!*t || isspace(*t))
+ t --;
+ }
+
+ len = strcspn(t, WHITESPACE);
+
+ *field = strndup(t, len);
+ if (!*field)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
new file mode 100644
index 0000000..5ae51c1
--- /dev/null
+++ b/src/basic/fileio.h
@@ -0,0 +1,45 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stddef.h>
+#include <stdio.h>
+
+#include "macro.h"
+
+int write_string_stream(FILE *f, const char *line);
+int write_string_file(const char *fn, const char *line);
+int write_string_file_no_create(const char *fn, const char *line);
+int write_string_file_atomic(const char *fn, const char *line);
+
+int read_one_line_file(const char *fn, char **line);
+int read_full_file(const char *fn, char **contents, size_t *size);
+int read_full_stream(FILE *f, char **contents, size_t *size);
+
+int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
+int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
+int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+
+int write_env_file(const char *fname, char **l);
+
+int executable_is_script(const char *path, char **interpreter);
+
+int get_status_field(const char *filename, const char *pattern, char **field);
diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c
new file mode 100644
index 0000000..d89a2f3
--- /dev/null
+++ b/src/basic/gunicode.c
@@ -0,0 +1,110 @@
+/* gunicode.c - Unicode manipulation functions
+ *
+ * Copyright (C) 1999, 2000 Tom Tromey
+ * Copyright 2000, 2005 Red Hat, Inc.
+ */
+
+#include "gunicode.h"
+
+#define unichar uint32_t
+
+/**
+ * g_utf8_prev_char:
+ * @p: a pointer to a position within a UTF-8 encoded string
+ *
+ * Finds the previous UTF-8 character in the string before @p.
+ *
+ * @p does not have to be at the beginning of a UTF-8 character. No check
+ * is made to see if the character found is actually valid other than
+ * it starts with an appropriate byte. If @p might be the first
+ * character of the string, you must use g_utf8_find_prev_char() instead.
+ *
+ * Return value: a pointer to the found character.
+ **/
+char *
+utf8_prev_char (const char *p)
+{
+ while (1)
+ {
+ p--;
+ if ((*p & 0xc0) != 0x80)
+ return (char *)p;
+ }
+}
+
+struct Interval
+{
+ unichar start, end;
+};
+
+static int
+interval_compare (const void *key, const void *elt)
+{
+ unichar c = (unichar) (long) (key);
+ struct Interval *interval = (struct Interval *)elt;
+
+ if (c < interval->start)
+ return -1;
+ if (c > interval->end)
+ return +1;
+
+ return 0;
+}
+
+/*
+ * NOTE:
+ *
+ * The tables for g_unichar_iswide() and g_unichar_iswide_cjk() are
+ * generated from the Unicode Character Database's file
+ * extracted/DerivedEastAsianWidth.txt using the gen-iswide-table.py
+ * in this way:
+ *
+ * ./gen-iswide-table.py < path/to/ucd/extracted/DerivedEastAsianWidth.txt | fmt
+ *
+ * Last update for Unicode 6.0.
+ */
+
+/**
+ * g_unichar_iswide:
+ * @c: a Unicode character
+ *
+ * Determines if a character is typically rendered in a double-width
+ * cell.
+ *
+ * Return value: %TRUE if the character is wide
+ **/
+bool
+unichar_iswide (unichar c)
+{
+ /* See NOTE earlier for how to update this table. */
+ static const struct Interval wide[] = {
+ {0x1100, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3},
+ {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096},
+ {0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA},
+ {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE},
+ {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
+ {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
+ {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6},
+ {0x1B000, 0x1B001}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23A},
+ {0x1F240, 0x1F248}, {0x1F250, 0x1F251},
+ {0x1F300, 0x1F567}, /* Miscellaneous Symbols and Pictographs */
+ {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
+ };
+
+ if (bsearch ((void *)(uintptr_t)c, wide, (sizeof (wide) / sizeof ((wide)[0])), sizeof wide[0],
+ interval_compare))
+ return true;
+
+ return false;
+}
+
+const char utf8_skip_data[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h
new file mode 100644
index 0000000..e70818f
--- /dev/null
+++ b/src/basic/gunicode.h
@@ -0,0 +1,30 @@
+/* gunicode.h - Unicode manipulation functions
+ *
+ * Copyright (C) 1999, 2000 Tom Tromey
+ * Copyright 2000, 2005 Red Hat, Inc.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+char *utf8_prev_char (const char *p);
+
+extern const char utf8_skip_data[256];
+
+/**
+ * g_utf8_next_char:
+ * @p: Pointer to the start of a valid UTF-8 character
+ *
+ * Skips to the next character in a UTF-8 string. The string must be
+ * valid; this macro is as fast as possible, and has no error-checking.
+ * You would use this macro to iterate over a string character by
+ * character. The macro returns the start of the next UTF-8 character.
+ * Before using this macro, use g_utf8_validate() to validate strings
+ * that may contain invalid UTF-8.
+ */
+#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)])
+
+bool unichar_iswide (uint32_t c);
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
new file mode 100644
index 0000000..20d599d
--- /dev/null
+++ b/src/basic/hashmap.c
@@ -0,0 +1,1854 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ 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 <errno.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "set.h"
+#include "macro.h"
+#include "siphash24.h"
+#include "strv.h"
+#include "mempool.h"
+#include "random-util.h"
+
+#ifdef ENABLE_DEBUG_HASHMAP
+#include "list.h"
+#endif
+
+/*
+ * Implementation of hashmaps.
+ * Addressing: open
+ * - uses less RAM compared to closed addressing (chaining), because
+ * our entries are small (especially in Sets, which tend to contain
+ * the majority of entries in systemd).
+ * Collision resolution: Robin Hood
+ * - tends to equalize displacement of entries from their optimal buckets.
+ * Probe sequence: linear
+ * - though theoretically worse than random probing/uniform hashing/double
+ * hashing, it is good for cache locality.
+ *
+ * References:
+ * Celis, P. 1986. Robin Hood Hashing.
+ * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
+ * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
+ * - The results are derived for random probing. Suggests deletion with
+ * tombstones and two mean-centered search methods. None of that works
+ * well for linear probing.
+ *
+ * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
+ * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
+ * http://www.math.uu.se/~svante/papers/sj157.pdf
+ * - Applies to Robin Hood with linear probing. Contains remarks on
+ * the unsuitability of mean-centered search with linear probing.
+ *
+ * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
+ * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
+ * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
+ * in a successful search), and Janson writes about displacement. C = d + 1.
+ *
+ * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
+ * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
+ * - Explanation of backward shift deletion with pictures.
+ *
+ * Khuong, P. 2013. The Other Robin Hood Hashing.
+ * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
+ * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
+ */
+
+/*
+ * XXX Ideas for improvement:
+ * For unordered hashmaps, randomize iteration order, similarly to Perl:
+ * http://blog.booking.com/hardening-perls-hash-function.html
+ */
+
+/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
+ * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
+#define INV_KEEP_FREE 5U
+
+/* Fields common to entries of all hashmap/set types */
+struct hashmap_base_entry {
+ const void *key;
+};
+
+/* Entry types for specific hashmap/set types
+ * hashmap_base_entry must be at the beginning of each entry struct. */
+
+struct plain_hashmap_entry {
+ struct hashmap_base_entry b;
+ void *value;
+};
+
+struct ordered_hashmap_entry {
+ struct plain_hashmap_entry p;
+ unsigned iterate_next, iterate_previous;
+};
+
+struct set_entry {
+ struct hashmap_base_entry b;
+};
+
+/* In several functions it is advantageous to have the hash table extended
+ * virtually by a couple of additional buckets. We reserve special index values
+ * for these "swap" buckets. */
+#define _IDX_SWAP_BEGIN (UINT_MAX - 3)
+#define IDX_PUT (_IDX_SWAP_BEGIN + 0)
+#define IDX_TMP (_IDX_SWAP_BEGIN + 1)
+#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2)
+
+#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */
+#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */
+
+assert_cc(IDX_FIRST == _IDX_SWAP_END);
+assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
+
+/* Storage space for the "swap" buckets.
+ * All entry types can fit into a ordered_hashmap_entry. */
+struct swap_entries {
+ struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
+};
+
+/* Distance from Initial Bucket */
+typedef uint8_t dib_raw_t;
+#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
+#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
+#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
+#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
+
+#define DIB_FREE UINT_MAX
+
+#ifdef ENABLE_DEBUG_HASHMAP
+struct hashmap_debug_info {
+ LIST_FIELDS(struct hashmap_debug_info, debug_list);
+ unsigned max_entries; /* high watermark of n_entries */
+
+ /* who allocated this hashmap */
+ int line;
+ const char *file;
+ const char *func;
+
+ /* fields to detect modification while iterating */
+ unsigned put_count; /* counts puts into the hashmap */
+ unsigned rem_count; /* counts removals from hashmap */
+ unsigned last_rem_idx; /* remembers last removal index */
+};
+
+/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
+static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
+
+#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
+
+#else /* !ENABLE_DEBUG_HASHMAP */
+#define HASHMAP_DEBUG_FIELDS
+#endif /* ENABLE_DEBUG_HASHMAP */
+
+enum HashmapType {
+ HASHMAP_TYPE_PLAIN,
+ HASHMAP_TYPE_ORDERED,
+ HASHMAP_TYPE_SET,
+ _HASHMAP_TYPE_MAX
+};
+
+struct _packed_ indirect_storage {
+ char *storage; /* where buckets and DIBs are stored */
+ uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */
+
+ unsigned n_entries; /* number of stored entries */
+ unsigned n_buckets; /* number of buckets */
+
+ unsigned idx_lowest_entry; /* Index below which all buckets are free.
+ Makes "while(hashmap_steal_first())" loops
+ O(n) instead of O(n^2) for unordered hashmaps. */
+ uint8_t _pad[3]; /* padding for the whole HashmapBase */
+ /* The bitfields in HashmapBase complete the alignment of the whole thing. */
+};
+
+struct direct_storage {
+ /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
+ * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
+ * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
+ char storage[sizeof(struct indirect_storage)];
+};
+
+#define DIRECT_BUCKETS(entry_t) \
+ (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
+
+/* We should be able to store at least one entry directly. */
+assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
+
+/* We have 3 bits for n_direct_entries. */
+assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
+
+/* Hashmaps with directly stored entries all use this shared hash key.
+ * It's no big deal if the key is guessed, because there can be only
+ * a handful of directly stored entries in a hashmap. When a hashmap
+ * outgrows direct storage, it gets its own key for indirect storage. */
+static uint8_t shared_hash_key[HASH_KEY_SIZE];
+static bool shared_hash_key_initialized;
+
+/* Fields that all hashmap/set types must have */
+struct HashmapBase {
+ const struct hash_ops *hash_ops; /* hash and compare ops to use */
+
+ union _packed_ {
+ struct indirect_storage indirect; /* if has_indirect */
+ struct direct_storage direct; /* if !has_indirect */
+ };
+
+ enum HashmapType type:2; /* HASHMAP_TYPE_* */
+ bool has_indirect:1; /* whether indirect storage is used */
+ unsigned n_direct_entries:3; /* Number of entries in direct storage.
+ * Only valid if !has_indirect. */
+ bool from_pool:1; /* whether was allocated from mempool */
+ HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
+};
+
+/* Specific hash types
+ * HashmapBase must be at the beginning of each hashmap struct. */
+
+struct Hashmap {
+ struct HashmapBase b;
+};
+
+struct OrderedHashmap {
+ struct HashmapBase b;
+ unsigned iterate_list_head, iterate_list_tail;
+};
+
+struct Set {
+ struct HashmapBase b;
+};
+
+DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
+DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
+/* No need for a separate Set pool */
+assert_cc(sizeof(Hashmap) == sizeof(Set));
+
+struct hashmap_type_info {
+ size_t head_size;
+ size_t entry_size;
+ struct mempool *mempool;
+ unsigned n_direct_buckets;
+};
+
+static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
+ [HASHMAP_TYPE_PLAIN] = {
+ .head_size = sizeof(Hashmap),
+ .entry_size = sizeof(struct plain_hashmap_entry),
+ .mempool = &hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
+ },
+ [HASHMAP_TYPE_ORDERED] = {
+ .head_size = sizeof(OrderedHashmap),
+ .entry_size = sizeof(struct ordered_hashmap_entry),
+ .mempool = &ordered_hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
+ },
+ [HASHMAP_TYPE_SET] = {
+ .head_size = sizeof(Set),
+ .entry_size = sizeof(struct set_entry),
+ .mempool = &hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
+ },
+};
+
+unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, p, strlen(p), hash_key);
+ return (unsigned long) u;
+}
+
+int string_compare_func(const void *a, const void *b) {
+ return strcmp(a, b);
+}
+
+const struct hash_ops string_hash_ops = {
+ .hash = string_hash_func,
+ .compare = string_compare_func
+};
+
+unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, &p, sizeof(p), hash_key);
+ return (unsigned long) u;
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops trivial_hash_ops = {
+ .hash = trivial_hash_func,
+ .compare = trivial_compare_func
+};
+
+unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
+ return (unsigned long) u;
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+ uint64_t a, b;
+ a = *(const uint64_t*) _a;
+ b = *(const uint64_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops uint64_hash_ops = {
+ .hash = uint64_hash_func,
+ .compare = uint64_compare_func
+};
+
+#if SIZEOF_DEV_T != 8
+unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
+ return (unsigned long) u;
+}
+
+int devt_compare_func(const void *_a, const void *_b) {
+ dev_t a, b;
+ a = *(const dev_t*) _a;
+ b = *(const dev_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#endif
+
+static unsigned n_buckets(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.n_buckets
+ : hashmap_type_info[h->type].n_direct_buckets;
+}
+
+static unsigned n_entries(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.n_entries
+ : h->n_direct_entries;
+}
+
+static void n_entries_inc(HashmapBase *h) {
+ if (h->has_indirect)
+ h->indirect.n_entries++;
+ else
+ h->n_direct_entries++;
+}
+
+static void n_entries_dec(HashmapBase *h) {
+ if (h->has_indirect)
+ h->indirect.n_entries--;
+ else
+ h->n_direct_entries--;
+}
+
+static char *storage_ptr(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.storage
+ : h->direct.storage;
+}
+
+static uint8_t *hash_key(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.hash_key
+ : shared_hash_key;
+}
+
+static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
+ return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h));
+}
+#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
+
+static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
+ static uint8_t current[HASH_KEY_SIZE];
+ static bool current_initialized = false;
+
+ /* Returns a hash function key to use. In order to keep things
+ * fast we will not generate a new key each time we allocate a
+ * new hash table. Instead, we'll just reuse the most recently
+ * generated one, except if we never generated one or when we
+ * are rehashing an entire hash table because we reached a
+ * fill level */
+
+ if (!current_initialized || !reuse_is_ok) {
+ random_bytes(current, sizeof(current));
+ current_initialized = true;
+ }
+
+ memcpy(hash_key, current, sizeof(current));
+}
+
+static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
+ return (struct hashmap_base_entry*)
+ (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+}
+
+static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
+ return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
+ return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
+ return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
+ return &swap->e[idx - _IDX_SWAP_BEGIN];
+}
+
+/* Returns a pointer to the bucket at index idx.
+ * Understands real indexes and swap indexes, hence "_virtual". */
+static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
+ unsigned idx) {
+ if (idx < _IDX_SWAP_BEGIN)
+ return bucket_at(h, idx);
+
+ if (idx < _IDX_SWAP_END)
+ return &bucket_at_swap(swap, idx)->p.b;
+
+ assert_not_reached("Invalid index");
+}
+
+static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
+ return (dib_raw_t*)
+ (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
+}
+
+static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
+ return idx >= from ? idx - from
+ : n_buckets(h) + idx - from;
+}
+
+static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
+ unsigned initial_bucket;
+
+ if (raw_dib == DIB_RAW_FREE)
+ return DIB_FREE;
+
+ if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
+ return raw_dib;
+
+ /*
+ * Having an overflow DIB value is very unlikely. The hash function
+ * would have to be bad. For example, in a table of size 2^24 filled
+ * to load factor 0.9 the maximum observed DIB is only about 60.
+ * In theory (assuming I used Maxima correctly), for an infinite size
+ * hash table with load factor 0.8 the probability of a given entry
+ * having DIB > 40 is 1.9e-8.
+ * This returns the correct DIB value by recomputing the hash value in
+ * the unlikely case. XXX Hitting this case could be a hint to rehash.
+ */
+ initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
+ return bucket_distance(h, idx, initial_bucket);
+}
+
+static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
+ dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
+}
+
+static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
+ dib_raw_t *dibs;
+
+ dibs = dib_raw_ptr(h);
+
+ for ( ; idx < n_buckets(h); idx++)
+ if (dibs[idx] != DIB_RAW_FREE)
+ return idx;
+
+ return IDX_NIL;
+}
+
+static void bucket_mark_free(HashmapBase *h, unsigned idx) {
+ memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size);
+ bucket_set_dib(h, idx, DIB_FREE);
+}
+
+static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
+ unsigned from, unsigned to) {
+ struct hashmap_base_entry *e_from, *e_to;
+
+ assert(from != to);
+
+ e_from = bucket_at_virtual(h, swap, from);
+ e_to = bucket_at_virtual(h, swap, to);
+
+ memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ struct ordered_hashmap_entry *le, *le_to;
+
+ le_to = (struct ordered_hashmap_entry*) e_to;
+
+ if (le_to->iterate_next != IDX_NIL) {
+ le = (struct ordered_hashmap_entry*)
+ bucket_at_virtual(h, swap, le_to->iterate_next);
+ le->iterate_previous = to;
+ }
+
+ if (le_to->iterate_previous != IDX_NIL) {
+ le = (struct ordered_hashmap_entry*)
+ bucket_at_virtual(h, swap, le_to->iterate_previous);
+ le->iterate_next = to;
+ }
+
+ if (lh->iterate_list_head == from)
+ lh->iterate_list_head = to;
+ if (lh->iterate_list_tail == from)
+ lh->iterate_list_tail = to;
+ }
+}
+
+static unsigned next_idx(HashmapBase *h, unsigned idx) {
+ return (idx + 1U) % n_buckets(h);
+}
+
+static unsigned prev_idx(HashmapBase *h, unsigned idx) {
+ return (n_buckets(h) + idx - 1U) % n_buckets(h);
+}
+
+static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
+ switch (h->type) {
+
+ case HASHMAP_TYPE_PLAIN:
+ case HASHMAP_TYPE_ORDERED:
+ return ((struct plain_hashmap_entry*)e)->value;
+
+ case HASHMAP_TYPE_SET:
+ return (void*) e->key;
+
+ default:
+ assert_not_reached("Unknown hashmap type");
+ }
+}
+
+static void base_remove_entry(HashmapBase *h, unsigned idx) {
+ unsigned left, right, prev, dib;
+ dib_raw_t raw_dib, *dibs;
+
+ dibs = dib_raw_ptr(h);
+ assert(dibs[idx] != DIB_RAW_FREE);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.rem_count++;
+ h->debug.last_rem_idx = idx;
+#endif
+
+ left = idx;
+ /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
+ for (right = next_idx(h, left); ; right = next_idx(h, right)) {
+ raw_dib = dibs[right];
+ if (raw_dib == 0 || raw_dib == DIB_RAW_FREE)
+ break;
+
+ /* The buckets are not supposed to be all occupied and with DIB > 0.
+ * That would mean we could make everyone better off by shifting them
+ * backward. This scenario is impossible. */
+ assert(left != right);
+ }
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
+
+ if (le->iterate_next != IDX_NIL)
+ ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
+ else
+ lh->iterate_list_tail = le->iterate_previous;
+
+ if (le->iterate_previous != IDX_NIL)
+ ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
+ else
+ lh->iterate_list_head = le->iterate_next;
+ }
+
+ /* Now shift all buckets in the interval (left, right) one step backwards */
+ for (prev = left, left = next_idx(h, left); left != right;
+ prev = left, left = next_idx(h, left)) {
+ dib = bucket_calculate_dib(h, left, dibs[left]);
+ assert(dib != 0);
+ bucket_move_entry(h, NULL, left, prev);
+ bucket_set_dib(h, prev, dib - 1);
+ }
+
+ bucket_mark_free(h, prev);
+ n_entries_dec(h);
+}
+#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
+
+static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
+ struct ordered_hashmap_entry *e;
+ unsigned idx;
+
+ assert(h);
+ assert(i);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST) {
+ idx = h->iterate_list_head;
+ e = ordered_bucket_at(h, idx);
+ } else {
+ idx = i->idx;
+ e = ordered_bucket_at(h, idx);
+ /*
+ * We allow removing the current entry while iterating, but removal may cause
+ * a backward shift. The next entry may thus move one bucket to the left.
+ * To detect when it happens, we remember the key pointer of the entry we were
+ * going to iterate next. If it does not match, there was a backward shift.
+ */
+ if (e->p.b.key != i->next_key) {
+ idx = prev_idx(HASHMAP_BASE(h), idx);
+ e = ordered_bucket_at(h, idx);
+ }
+ assert(e->p.b.key == i->next_key);
+ }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ i->prev_idx = idx;
+#endif
+
+ if (e->iterate_next != IDX_NIL) {
+ struct ordered_hashmap_entry *n;
+ i->idx = e->iterate_next;
+ n = ordered_bucket_at(h, i->idx);
+ i->next_key = n->p.b.key;
+ } else
+ i->idx = IDX_NIL;
+
+ return idx;
+
+at_end:
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+}
+
+static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
+ unsigned idx;
+
+ assert(h);
+ assert(i);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST) {
+ /* fast forward to the first occupied bucket */
+ if (h->has_indirect) {
+ i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
+ h->indirect.idx_lowest_entry = i->idx;
+ } else
+ i->idx = skip_free_buckets(h, 0);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+ } else {
+ struct hashmap_base_entry *e;
+
+ assert(i->idx > 0);
+
+ e = bucket_at(h, i->idx);
+ /*
+ * We allow removing the current entry while iterating, but removal may cause
+ * a backward shift. The next entry may thus move one bucket to the left.
+ * To detect when it happens, we remember the key pointer of the entry we were
+ * going to iterate next. If it does not match, there was a backward shift.
+ */
+ if (e->key != i->next_key)
+ e = bucket_at(h, --i->idx);
+
+ assert(e->key == i->next_key);
+ }
+
+ idx = i->idx;
+#ifdef ENABLE_DEBUG_HASHMAP
+ i->prev_idx = idx;
+#endif
+
+ i->idx = skip_free_buckets(h, i->idx + 1);
+ if (i->idx != IDX_NIL)
+ i->next_key = bucket_at(h, i->idx)->key;
+ else
+ i->idx = IDX_NIL;
+
+ return idx;
+
+at_end:
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+}
+
+static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
+ if (!h) {
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+ }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ if (i->idx == IDX_FIRST) {
+ i->put_count = h->debug.put_count;
+ i->rem_count = h->debug.rem_count;
+ } else {
+ /* While iterating, must not add any new entries */
+ assert(i->put_count == h->debug.put_count);
+ /* ... or remove entries other than the current one */
+ assert(i->rem_count == h->debug.rem_count ||
+ (i->rem_count == h->debug.rem_count - 1 &&
+ i->prev_idx == h->debug.last_rem_idx));
+ /* Reset our removals counter */
+ i->rem_count = h->debug.rem_count;
+ }
+#endif
+
+ return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
+ : hashmap_iterate_in_internal_order(h, i);
+}
+
+void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key) {
+ struct hashmap_base_entry *e;
+ void *data;
+ unsigned idx;
+
+ idx = hashmap_iterate_entry(h, i);
+ if (idx == IDX_NIL) {
+ if (key)
+ *key = NULL;
+
+ return NULL;
+ }
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ if (key)
+ *key = e->key;
+
+ return data;
+}
+
+void *set_iterate(Set *s, Iterator *i) {
+ return internal_hashmap_iterate(HASHMAP_BASE(s), i, NULL);
+}
+
+#define HASHMAP_FOREACH_IDX(idx, h, i) \
+ for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
+ (idx != IDX_NIL); \
+ (idx) = hashmap_iterate_entry((h), &(i)))
+
+static void reset_direct_storage(HashmapBase *h) {
+ const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
+ void *p;
+
+ assert(!h->has_indirect);
+
+ p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
+ memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
+}
+
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ HashmapBase *h;
+ const struct hashmap_type_info *hi = &hashmap_type_info[type];
+ bool use_pool;
+
+ use_pool = is_main_thread();
+
+ h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
+
+ if (!h)
+ return NULL;
+
+ h->type = type;
+ h->from_pool = use_pool;
+ h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
+
+ if (type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*)h;
+ lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+ }
+
+ reset_direct_storage(h);
+
+ if (!shared_hash_key_initialized) {
+ random_bytes(shared_hash_key, sizeof(shared_hash_key));
+ shared_hash_key_initialized= true;
+ }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
+ h->debug.func = func;
+ h->debug.file = file;
+ h->debug.line = line;
+#endif
+
+ return h;
+}
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
+ enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ HashmapBase *q;
+
+ assert(h);
+
+ if (*h)
+ return 0;
+
+ q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+ if (!q)
+ return -ENOMEM;
+
+ *h = q;
+ return 0;
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static void hashmap_free_no_clear(HashmapBase *h) {
+ assert(!h->has_indirect);
+ assert(!h->n_direct_entries);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
+#endif
+
+ if (h->from_pool)
+ mempool_free_tile(hashmap_type_info[h->type].mempool, h);
+ else
+ free(h);
+}
+
+HashmapBase *internal_hashmap_free(HashmapBase *h) {
+
+ /* Free the hashmap, but nothing in it */
+
+ if (h) {
+ internal_hashmap_clear(h);
+ hashmap_free_no_clear(h);
+ }
+
+ return NULL;
+}
+
+HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
+
+ /* Free the hashmap and all data objects in it, but not the
+ * keys */
+
+ if (h) {
+ internal_hashmap_clear_free(h);
+ hashmap_free_no_clear(h);
+ }
+
+ return NULL;
+}
+
+Hashmap *hashmap_free_free_free(Hashmap *h) {
+
+ /* Free the hashmap and all data and key objects in it */
+
+ if (h) {
+ hashmap_clear_free_free(h);
+ hashmap_free_no_clear(HASHMAP_BASE(h));
+ }
+
+ return NULL;
+}
+
+void internal_hashmap_clear(HashmapBase *h) {
+ if (!h)
+ return;
+
+ if (h->has_indirect) {
+ free(h->indirect.storage);
+ h->has_indirect = false;
+ }
+
+ h->n_direct_entries = 0;
+ reset_direct_storage(h);
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+ }
+}
+
+void internal_hashmap_clear_free(HashmapBase *h) {
+ unsigned idx;
+
+ if (!h)
+ return;
+
+ for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
+ idx = skip_free_buckets(h, idx + 1))
+ free(entry_value(h, bucket_at(h, idx)));
+
+ internal_hashmap_clear(h);
+}
+
+void hashmap_clear_free_free(Hashmap *h) {
+ unsigned idx;
+
+ if (!h)
+ return;
+
+ for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
+ idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
+ struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
+ free((void*)e->b.key);
+ free(e->value);
+ }
+
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+static int resize_buckets(HashmapBase *h, unsigned entries_add);
+
+/*
+ * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
+ * Performs Robin Hood swaps as it goes. The entry to put must be placed
+ * by the caller into swap slot IDX_PUT.
+ * If used for in-place resizing, may leave a displaced entry in swap slot
+ * IDX_PUT. Caller must rehash it next.
+ * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
+ * false otherwise.
+ */
+static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
+ struct swap_entries *swap) {
+ dib_raw_t raw_dib, *dibs;
+ unsigned dib, distance;
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.put_count++;
+#endif
+
+ dibs = dib_raw_ptr(h);
+
+ for (distance = 0; ; distance++) {
+ raw_dib = dibs[idx];
+ if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) {
+ if (raw_dib == DIB_RAW_REHASH)
+ bucket_move_entry(h, swap, idx, IDX_TMP);
+
+ if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
+ h->indirect.idx_lowest_entry = idx;
+
+ bucket_set_dib(h, idx, distance);
+ bucket_move_entry(h, swap, IDX_PUT, idx);
+ if (raw_dib == DIB_RAW_REHASH) {
+ bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+ return true;
+ }
+
+ return false;
+ }
+
+ dib = bucket_calculate_dib(h, idx, raw_dib);
+
+ if (dib < distance) {
+ /* Found a wealthier entry. Go Robin Hood! */
+ bucket_set_dib(h, idx, distance);
+
+ /* swap the entries */
+ bucket_move_entry(h, swap, idx, IDX_TMP);
+ bucket_move_entry(h, swap, IDX_PUT, idx);
+ bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+
+ distance = dib;
+ }
+
+ idx = next_idx(h, idx);
+ }
+}
+
+/*
+ * Puts an entry into a hashmap, boldly - no check whether key already exists.
+ * The caller must place the entry (only its key and value, not link indexes)
+ * in swap slot IDX_PUT.
+ * Caller must ensure: the key does not exist yet in the hashmap.
+ * that resize is not needed if !may_resize.
+ * Returns: 1 if entry was put successfully.
+ * -ENOMEM if may_resize==true and resize failed with -ENOMEM.
+ * Cannot return -ENOMEM if !may_resize.
+ */
+static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
+ struct swap_entries *swap, bool may_resize) {
+ struct ordered_hashmap_entry *new_entry;
+ int r;
+
+ assert(idx < n_buckets(h));
+
+ new_entry = bucket_at_swap(swap, IDX_PUT);
+
+ if (may_resize) {
+ r = resize_buckets(h, 1);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ idx = bucket_hash(h, new_entry->p.b.key);
+ }
+ assert(n_entries(h) < n_buckets(h));
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+
+ new_entry->iterate_next = IDX_NIL;
+ new_entry->iterate_previous = lh->iterate_list_tail;
+
+ if (lh->iterate_list_tail != IDX_NIL) {
+ struct ordered_hashmap_entry *old_tail;
+
+ old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
+ assert(old_tail->iterate_next == IDX_NIL);
+ old_tail->iterate_next = IDX_PUT;
+ }
+
+ lh->iterate_list_tail = IDX_PUT;
+ if (lh->iterate_list_head == IDX_NIL)
+ lh->iterate_list_head = IDX_PUT;
+ }
+
+ assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
+
+ n_entries_inc(h);
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
+#endif
+
+ return 1;
+}
+#define hashmap_put_boldly(h, idx, swap, may_resize) \
+ hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
+
+/*
+ * Returns 0 if resize is not needed.
+ * 1 if successfully resized.
+ * -ENOMEM on allocation failure.
+ */
+static int resize_buckets(HashmapBase *h, unsigned entries_add) {
+ struct swap_entries swap;
+ char *new_storage;
+ dib_raw_t *old_dibs, *new_dibs;
+ const struct hashmap_type_info *hi;
+ unsigned idx, optimal_idx;
+ unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
+ uint8_t new_shift;
+ bool rehash_next;
+
+ assert(h);
+
+ hi = &hashmap_type_info[h->type];
+ new_n_entries = n_entries(h) + entries_add;
+
+ /* overflow? */
+ if (_unlikely_(new_n_entries < entries_add))
+ return -ENOMEM;
+
+ /* For direct storage we allow 100% load, because it's tiny. */
+ if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
+ return 0;
+
+ /*
+ * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
+ * From it follows: m = n + n/(INV_KEEP_FREE - 1)
+ */
+ new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
+ /* overflow? */
+ if (_unlikely_(new_n_buckets < new_n_entries))
+ return -ENOMEM;
+
+ if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
+ return -ENOMEM;
+
+ old_n_buckets = n_buckets(h);
+
+ if (_likely_(new_n_buckets <= old_n_buckets))
+ return 0;
+
+ new_shift = log2u_round_up(MAX(
+ new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
+ 2 * sizeof(struct direct_storage)));
+
+ /* Realloc storage (buckets and DIB array). */
+ new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
+ 1U << new_shift);
+ if (!new_storage)
+ return -ENOMEM;
+
+ /* Must upgrade direct to indirect storage. */
+ if (!h->has_indirect) {
+ memcpy(new_storage, h->direct.storage,
+ old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
+ h->indirect.n_entries = h->n_direct_entries;
+ h->indirect.idx_lowest_entry = 0;
+ h->n_direct_entries = 0;
+ }
+
+ /* Get a new hash key. If we've just upgraded to indirect storage,
+ * allow reusing a previously generated key. It's still a different key
+ * from the shared one that we used for direct storage. */
+ get_hash_key(h->indirect.hash_key, !h->has_indirect);
+
+ h->has_indirect = true;
+ h->indirect.storage = new_storage;
+ h->indirect.n_buckets = (1U << new_shift) /
+ (hi->entry_size + sizeof(dib_raw_t));
+
+ old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
+ new_dibs = dib_raw_ptr(h);
+
+ /*
+ * Move the DIB array to the new place, replacing valid DIB values with
+ * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
+ * Note: Overlap is not possible, because we have at least doubled the
+ * number of buckets and dib_raw_t is smaller than any entry type.
+ */
+ for (idx = 0; idx < old_n_buckets; idx++) {
+ assert(old_dibs[idx] != DIB_RAW_REHASH);
+ new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
+ : DIB_RAW_REHASH;
+ }
+
+ /* Zero the area of newly added entries (including the old DIB area) */
+ memzero(bucket_at(h, old_n_buckets),
+ (n_buckets(h) - old_n_buckets) * hi->entry_size);
+
+ /* The upper half of the new DIB array needs initialization */
+ memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
+ (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
+
+ /* Rehash entries that need it */
+ n_rehashed = 0;
+ for (idx = 0; idx < old_n_buckets; idx++) {
+ if (new_dibs[idx] != DIB_RAW_REHASH)
+ continue;
+
+ optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
+
+ /*
+ * Not much to do if by luck the entry hashes to its current
+ * location. Just set its DIB.
+ */
+ if (optimal_idx == idx) {
+ new_dibs[idx] = 0;
+ n_rehashed++;
+ continue;
+ }
+
+ new_dibs[idx] = DIB_RAW_FREE;
+ bucket_move_entry(h, &swap, idx, IDX_PUT);
+ /* bucket_move_entry does not clear the source */
+ memzero(bucket_at(h, idx), hi->entry_size);
+
+ do {
+ /*
+ * Find the new bucket for the current entry. This may make
+ * another entry homeless and load it into IDX_PUT.
+ */
+ rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
+ n_rehashed++;
+
+ /* Did the current entry displace another one? */
+ if (rehash_next)
+ optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
+ } while (rehash_next);
+ }
+
+ assert(n_rehashed == n_entries(h));
+
+ return 1;
+}
+
+/*
+ * Finds an entry with a matching key
+ * Returns: index of the found entry, or IDX_NIL if not found.
+ */
+static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned dib, distance;
+ dib_raw_t *dibs = dib_raw_ptr(h);
+
+ assert(idx < n_buckets(h));
+
+ for (distance = 0; ; distance++) {
+ if (dibs[idx] == DIB_RAW_FREE)
+ return IDX_NIL;
+
+ dib = bucket_calculate_dib(h, idx, dibs[idx]);
+
+ if (dib < distance)
+ return IDX_NIL;
+ if (dib == distance) {
+ e = bucket_at(h, idx);
+ if (h->hash_ops->compare(e->key, key) == 0)
+ return idx;
+ }
+
+ idx = next_idx(h, idx);
+ }
+}
+#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
+
+int hashmap_put(Hashmap *h, const void *key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx != IDX_NIL) {
+ e = plain_bucket_at(h, idx);
+ if (e->value == value)
+ return 0;
+ return -EEXIST;
+ }
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = key;
+ e->value = value;
+ return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int set_put(Set *s, const void *key) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+
+ assert(s);
+
+ hash = bucket_hash(s, key);
+ idx = bucket_scan(s, hash, key);
+ if (idx != IDX_NIL)
+ return 0;
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ e->key = key;
+ return hashmap_put_boldly(s, hash, &swap, true);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx != IDX_NIL) {
+ e = plain_bucket_at(h, idx);
+#ifdef ENABLE_DEBUG_HASHMAP
+ /* Although the key is equal, the key pointer may have changed,
+ * and this would break our assumption for iterating. So count
+ * this operation as incompatible with iteration. */
+ if (e->b.key != key) {
+ h->b.debug.put_count++;
+ h->b.debug.rem_count++;
+ h->b.debug.last_rem_idx = idx;
+ }
+#endif
+ e->b.key = key;
+ e->value = value;
+ return 0;
+ }
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = key;
+ e->value = value;
+ return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ e = plain_bucket_at(h, idx);
+ e->value = value;
+ return 0;
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ return entry_value(h, e);
+}
+
+void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = plain_bucket_at(h, idx);
+ if (key2)
+ *key2 = (void*) e->b.key;
+
+ return e->value;
+}
+
+bool internal_hashmap_contains(HashmapBase *h, const void *key) {
+ unsigned hash;
+
+ if (!h)
+ return false;
+
+ hash = bucket_hash(h, key);
+ return bucket_scan(h, hash, key) != IDX_NIL;
+}
+
+void *internal_hashmap_remove(HashmapBase *h, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+ void *data;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ remove_entry(h, idx);
+
+ return data;
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+ void *data;
+
+ if (!h) {
+ if (rkey)
+ *rkey = NULL;
+ return NULL;
+ }
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL) {
+ if (rkey)
+ *rkey = NULL;
+ return NULL;
+ }
+
+ e = plain_bucket_at(h, idx);
+ data = e->value;
+ if (rkey)
+ *rkey = (void*) e->b.key;
+
+ remove_entry(h, idx);
+
+ return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned old_hash, new_hash, idx;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = bucket_hash(h, old_key);
+ idx = bucket_scan(h, old_hash, old_key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ new_hash = bucket_hash(h, new_key);
+ if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
+ return -EEXIST;
+
+ remove_entry(h, idx);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = new_key;
+ e->value = value;
+ assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e;
+ unsigned old_hash, new_hash, idx;
+
+ if (!s)
+ return -ENOENT;
+
+ old_hash = bucket_hash(s, old_key);
+ idx = bucket_scan(s, old_hash, old_key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ new_hash = bucket_hash(s, new_key);
+ if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
+ return -EEXIST;
+
+ remove_entry(s, idx);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ e->key = new_key;
+ assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned old_hash, new_hash, idx_old, idx_new;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = bucket_hash(h, old_key);
+ idx_old = bucket_scan(h, old_hash, old_key);
+ if (idx_old == IDX_NIL)
+ return -ENOENT;
+
+ old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
+
+ new_hash = bucket_hash(h, new_key);
+ idx_new = bucket_scan(h, new_hash, new_key);
+ if (idx_new != IDX_NIL)
+ if (idx_old != idx_new) {
+ remove_entry(h, idx_new);
+ /* Compensate for a possible backward shift. */
+ if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
+ idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
+ assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
+ }
+
+ remove_entry(h, idx_old);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = new_key;
+ e->value = value;
+ assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = plain_bucket_at(h, idx);
+ if (e->value != value)
+ return NULL;
+
+ remove_entry(h, idx);
+
+ return value;
+}
+
+static unsigned find_first_entry(HashmapBase *h) {
+ Iterator i = ITERATOR_FIRST;
+
+ if (!h || !n_entries(h))
+ return IDX_NIL;
+
+ return hashmap_iterate_entry(h, &i);
+}
+
+void *internal_hashmap_first(HashmapBase *h) {
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ return entry_value(h, bucket_at(h, idx));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ return (void*) e->key;
+}
+
+void *internal_hashmap_steal_first(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ void *data;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ remove_entry(h, idx);
+
+ return data;
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ void *key;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ key = (void*) e->key;
+ remove_entry(h, idx);
+
+ return key;
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) {
+
+ if (!h)
+ return 0;
+
+ return n_entries(h);
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) {
+
+ if (!h)
+ return 0;
+
+ return n_buckets(h);
+}
+
+int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
+ Iterator i;
+ unsigned idx;
+
+ assert(h);
+
+ HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+ struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
+ int r;
+
+ r = hashmap_put(h, pe->b.key, pe->value);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ return 0;
+}
+
+int set_merge(Set *s, Set *other) {
+ Iterator i;
+ unsigned idx;
+
+ assert(s);
+
+ HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+ struct set_entry *se = set_bucket_at(other, idx);
+ int r;
+
+ r = set_put(s, se->b.key);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
+ int r;
+
+ assert(h);
+
+ r = resize_buckets(h, entries_add);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/*
+ * The same as hashmap_merge(), but every new item from other is moved to h.
+ * Keys already in h are skipped and stay in other.
+ * Returns: 0 on success.
+ * -ENOMEM on alloc failure, in which case no move has been done.
+ */
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e, *n;
+ Iterator i;
+ unsigned idx;
+ int r;
+
+ assert(h);
+
+ if (!other)
+ return 0;
+
+ assert(other->type == h->type);
+
+ /*
+ * This reserves buckets for the worst case, where none of other's
+ * entries are yet present in h. This is preferable to risking
+ * an allocation failure in the middle of the moving and having to
+ * rollback or return a partial result.
+ */
+ r = resize_buckets(h, n_entries(other));
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH_IDX(idx, other, i) {
+ unsigned h_hash;
+
+ e = bucket_at(other, idx);
+ h_hash = bucket_hash(h, e->key);
+ if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
+ continue;
+
+ n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ n->key = e->key;
+ if (h->type != HASHMAP_TYPE_SET)
+ ((struct plain_hashmap_entry*) n)->value =
+ ((struct plain_hashmap_entry*) e)->value;
+ assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
+
+ remove_entry(other, idx);
+ }
+
+ return 0;
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
+ struct swap_entries swap;
+ unsigned h_hash, other_hash, idx;
+ struct hashmap_base_entry *e, *n;
+ int r;
+
+ assert(h);
+
+ h_hash = bucket_hash(h, key);
+ if (bucket_scan(h, h_hash, key) != IDX_NIL)
+ return -EEXIST;
+
+ if (!other)
+ return -ENOENT;
+
+ assert(other->type == h->type);
+
+ other_hash = bucket_hash(other, key);
+ idx = bucket_scan(other, other_hash, key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ e = bucket_at(other, idx);
+
+ n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ n->key = e->key;
+ if (h->type != HASHMAP_TYPE_SET)
+ ((struct plain_hashmap_entry*) n)->value =
+ ((struct plain_hashmap_entry*) e)->value;
+ r = hashmap_put_boldly(h, h_hash, &swap, true);
+ if (r < 0)
+ return r;
+
+ remove_entry(other, idx);
+ return 0;
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h) {
+ HashmapBase *copy;
+ int r;
+
+ assert(h);
+
+ copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS);
+ if (!copy)
+ return NULL;
+
+ switch (h->type) {
+ case HASHMAP_TYPE_PLAIN:
+ case HASHMAP_TYPE_ORDERED:
+ r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
+ break;
+ case HASHMAP_TYPE_SET:
+ r = set_merge((Set*)copy, (Set*)h);
+ break;
+ default:
+ assert_not_reached("Unknown hashmap type");
+ }
+
+ if (r < 0) {
+ internal_hashmap_free(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+char **internal_hashmap_get_strv(HashmapBase *h) {
+ char **sv;
+ Iterator i;
+ unsigned idx, n;
+
+ sv = new(char*, n_entries(h)+1);
+ if (!sv)
+ return NULL;
+
+ n = 0;
+ HASHMAP_FOREACH_IDX(idx, h, i)
+ sv[n++] = entry_value(h, bucket_at(h, idx));
+ sv[n] = NULL;
+
+ return sv;
+}
+
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
+ struct ordered_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(key);
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = ordered_bucket_at(h, idx);
+ if (e->iterate_next == IDX_NIL)
+ return NULL;
+ return ordered_bucket_at(h, e->iterate_next)->p.value;
+}
+
+int set_consume(Set *s, void *value) {
+ int r;
+
+ r = set_put(s, value);
+ if (r <= 0)
+ free(value);
+
+ return r;
+}
+
+int set_put_strdup(Set *s, const char *p) {
+ char *c;
+ int r;
+
+ assert(s);
+ assert(p);
+
+ c = strdup(p);
+ if (!c)
+ return -ENOMEM;
+
+ r = set_consume(s, c);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+int set_put_strdupv(Set *s, char **l) {
+ int n = 0, r;
+ char **i;
+
+ STRV_FOREACH(i, l) {
+ r = set_put_strdup(s, *i);
+ if (r < 0)
+ return r;
+
+ n += r;
+ }
+
+ return n;
+}
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
new file mode 100644
index 0000000..a03ee58
--- /dev/null
+++ b/src/basic/hashmap.h
@@ -0,0 +1,420 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ 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 <stdbool.h>
+
+#include "macro.h"
+#include "util.h"
+
+/*
+ * A hash table implementation. As a minor optimization a NULL hashmap object
+ * will be treated as empty hashmap for all read operations. That way it is not
+ * necessary to instantiate an object for each Hashmap use.
+ *
+ * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
+ * the implemention will:
+ * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
+ * - perform extra checks for invalid use of iterators
+ */
+
+#define HASH_KEY_SIZE 16
+
+/* The base type for all hashmap and set types. Many functions in the
+ * implementation take (HashmapBase*) parameters and are run-time polymorphic,
+ * though the API is not meant to be polymorphic (do not call functions
+ * internal_*() directly). */
+typedef struct HashmapBase HashmapBase;
+
+/* Specific hashmap/set types */
+typedef struct Hashmap Hashmap; /* Maps keys to values */
+typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
+typedef struct Set Set; /* Stores just keys */
+
+/* Ideally the Iterator would be an opaque struct, but it is instantiated
+ * by hashmap users, so the definition has to be here. Do not use its fields
+ * directly. */
+typedef struct {
+ unsigned idx; /* index of an entry to be iterated next */
+ const void *next_key; /* expected value of that entry's key pointer */
+#ifdef ENABLE_DEBUG_HASHMAP
+ unsigned put_count; /* hashmap's put_count recorded at start of iteration */
+ unsigned rem_count; /* hashmap's rem_count in previous iteration */
+ unsigned prev_idx; /* idx in previous iteration */
+#endif
+} Iterator;
+
+#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
+#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
+
+typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+struct hash_ops {
+ hash_func_t hash;
+ compare_func_t compare;
+};
+
+unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+int string_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops string_hash_ops;
+
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+int trivial_compare_func(const void *a, const void *b) _const_;
+extern const struct hash_ops trivial_hash_ops;
+
+/* 32bit values we can always just embedd in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
+unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+int uint64_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops uint64_hash_ops;
+
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+#if SIZEOF_DEV_T != 8
+unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+int devt_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#else
+#define devt_hash_func uint64_hash_func
+#define devt_compare_func uint64_compare_func
+#define devt_hash_ops uint64_hash_ops
+#endif
+
+/* Macros for type checking */
+#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
+ (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
+ __builtin_types_compatible_p(typeof(h), Hashmap*) || \
+ __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
+ __builtin_types_compatible_p(typeof(h), Set*))
+
+#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
+ (__builtin_types_compatible_p(typeof(h), Hashmap*) || \
+ __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
+
+#define HASHMAP_BASE(h) \
+ __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
+ (HashmapBase*)(h), \
+ (void)0)
+
+#define PLAIN_HASHMAP(h) \
+ __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
+ (Hashmap*)(h), \
+ (void)0)
+
+#ifdef ENABLE_DEBUG_HASHMAP
+# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
+# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
+# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
+#else
+# define HASHMAP_DEBUG_PARAMS
+# define HASHMAP_DEBUG_SRC_ARGS
+# define HASHMAP_DEBUG_PASS_ARGS
+#endif
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+HashmapBase *internal_hashmap_free(HashmapBase *h);
+static inline Hashmap *hashmap_free(Hashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+}
+
+HashmapBase *internal_hashmap_free_free(HashmapBase *h);
+static inline Hashmap *hashmap_free_free(Hashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+
+Hashmap *hashmap_free_free_free(Hashmap *h);
+static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
+ return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h);
+static inline Hashmap *hashmap_copy(Hashmap *h) {
+ return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
+ return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_put(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_update(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_replace(PLAIN_HASHMAP(h), key, value);
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key);
+static inline void *hashmap_get(Hashmap *h, const void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+
+void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
+ return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
+}
+
+bool internal_hashmap_contains(HashmapBase *h, const void *key);
+static inline bool hashmap_contains(Hashmap *h, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+
+void *internal_hashmap_remove(HashmapBase *h, const void *key);
+static inline void *hashmap_remove(Hashmap *h, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
+ return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+ return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+ return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
+ * should just work, allow this by having looser type-checking here. */
+int internal_hashmap_merge(Hashmap *h, Hashmap *other);
+#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
+#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
+static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
+/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
+static inline int hashmap_move(Hashmap *h, Hashmap *other) {
+ return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
+ return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
+static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) _pure_;
+static inline unsigned hashmap_size(Hashmap *h) {
+ return internal_hashmap_size(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
+ return internal_hashmap_size(HASHMAP_BASE(h));
+}
+
+static inline bool hashmap_isempty(Hashmap *h) {
+ return hashmap_size(h) == 0;
+}
+static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
+ return ordered_hashmap_size(h) == 0;
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
+static inline unsigned hashmap_buckets(Hashmap *h) {
+ return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
+ return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key);
+static inline void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
+ return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
+}
+static inline void *ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, const void **key) {
+ return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
+}
+
+void internal_hashmap_clear(HashmapBase *h);
+static inline void hashmap_clear(Hashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear(OrderedHashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+void internal_hashmap_clear_free(HashmapBase *h);
+static inline void hashmap_clear_free(Hashmap *h) {
+ internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
+ internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+
+void hashmap_clear_free_free(Hashmap *h);
+static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
+ hashmap_clear_free_free(PLAIN_HASHMAP(h));
+}
+
+/*
+ * Note about all *_first*() functions
+ *
+ * For plain Hashmaps and Sets the order of entries is undefined.
+ * The functions find whatever entry is first in the implementation
+ * internal order.
+ *
+ * Only for OrderedHashmaps the order is well defined and finding
+ * the first entry is O(1).
+ */
+
+void *internal_hashmap_steal_first(HashmapBase *h);
+static inline void *hashmap_steal_first(Hashmap *h) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h);
+static inline void *hashmap_steal_first_key(Hashmap *h) {
+ return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
+ return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) _pure_;
+static inline void *hashmap_first_key(Hashmap *h) {
+ return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
+ return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first(HashmapBase *h) _pure_;
+static inline void *hashmap_first(Hashmap *h) {
+ return internal_hashmap_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first(OrderedHashmap *h) {
+ return internal_hashmap_first(HASHMAP_BASE(h));
+}
+
+/* no hashmap_next */
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
+
+char **internal_hashmap_get_strv(HashmapBase *h);
+static inline char **hashmap_get_strv(Hashmap *h) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+
+/*
+ * Hashmaps are iterated in unpredictable order.
+ * OrderedHashmaps are an exception to this. They are iterated in the order
+ * the entries were inserted.
+ * It is safe to remove the current entry.
+ */
+#define HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); \
+ (e); \
+ (e) = hashmap_iterate((h), &(i), NULL))
+
+#define ORDERED_HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), NULL); \
+ (e); \
+ (e) = ordered_hashmap_iterate((h), &(i), NULL))
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); \
+ (e); \
+ (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
+
+#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)); \
+ (e); \
+ (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)))
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
+
+#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
+#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
+#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
+#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
+#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
+#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
new file mode 100644
index 0000000..e336f26
--- /dev/null
+++ b/src/basic/hostname-util.c
@@ -0,0 +1,193 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/utsname.h>
+#include <ctype.h>
+
+#include "util.h"
+#include "hostname-util.h"
+
+bool hostname_is_set(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (isempty(u.nodename))
+ return false;
+
+ /* This is the built-in kernel default host name */
+ if (streq(u.nodename, "(none)"))
+ return false;
+
+ return true;
+}
+
+char* gethostname_malloc(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (isempty(u.nodename) || streq(u.nodename, "(none)"))
+ return strdup(u.sysname);
+
+ return strdup(u.nodename);
+}
+
+static bool hostname_valid_char(char c) {
+ return
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '_' ||
+ c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+ const char *p;
+ bool dot;
+
+ if (isempty(s))
+ return false;
+
+ /* Doesn't accept empty hostnames, hostnames with trailing or
+ * leading dots, and hostnames with multiple dots in a
+ * sequence. Also ensures that the length stays below
+ * HOST_NAME_MAX. */
+
+ for (p = s, dot = true; *p; p++) {
+ if (*p == '.') {
+ if (dot)
+ return false;
+
+ dot = true;
+ } else {
+ if (!hostname_valid_char(*p))
+ return false;
+
+ dot = false;
+ }
+ }
+
+ if (dot)
+ return false;
+
+ if (p-s > HOST_NAME_MAX)
+ return false;
+
+ return true;
+}
+
+char* hostname_cleanup(char *s, bool lowercase) {
+ char *p, *d;
+ bool dot;
+
+ assert(s);
+
+ for (p = s, d = s, dot = true; *p; p++) {
+ if (*p == '.') {
+ if (dot)
+ continue;
+
+ *(d++) = '.';
+ dot = true;
+ } else if (hostname_valid_char(*p)) {
+ *(d++) = lowercase ? tolower(*p) : *p;
+ dot = false;
+ }
+
+ }
+
+ if (dot && d > s)
+ d[-1] = 0;
+ else
+ *d = 0;
+
+ strshorten(s, HOST_NAME_MAX);
+
+ return s;
+}
+
+bool is_localhost(const char *hostname) {
+ assert(hostname);
+
+ /* This tries to identify local host and domain names
+ * described in RFC6761 plus the redhatism of .localdomain */
+
+ return streq(hostname, "localhost") ||
+ streq(hostname, "localhost.") ||
+ streq(hostname, "localdomain.") ||
+ streq(hostname, "localdomain") ||
+ endswith(hostname, ".localhost") ||
+ endswith(hostname, ".localhost.") ||
+ endswith(hostname, ".localdomain") ||
+ endswith(hostname, ".localdomain.");
+}
+
+int sethostname_idempotent(const char *s) {
+ char buf[HOST_NAME_MAX + 1] = {};
+
+ assert(s);
+
+ if (gethostname(buf, sizeof(buf)) < 0)
+ return -errno;
+
+ if (streq(buf, s))
+ return 0;
+
+ if (sethostname(s, strlen(s)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int read_hostname_config(const char *path, char **hostname) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char l[LINE_MAX];
+ char *name = NULL;
+
+ assert(path);
+ assert(hostname);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ /* may have comments, ignore them */
+ FOREACH_LINE(l, f, return -errno) {
+ truncate_nl(l);
+ if (l[0] != '\0' && l[0] != '#') {
+ /* found line with value */
+ name = hostname_cleanup(l, false);
+ name = strdup(name);
+ if (!name)
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ if (!name)
+ /* no non-empty line found */
+ return -ENOENT;
+
+ *hostname = name;
+ return 0;
+}
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
new file mode 100644
index 0000000..0c4763c
--- /dev/null
+++ b/src/basic/hostname-util.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 <stdbool.h>
+
+#include "macro.h"
+
+bool hostname_is_set(void);
+
+char* gethostname_malloc(void);
+
+bool hostname_is_valid(const char *s) _pure_;
+char* hostname_cleanup(char *s, bool lowercase);
+
+bool is_localhost(const char *hostname);
+
+int sethostname_idempotent(const char *s);
+
+int read_hostname_config(const char *path, char **hostname);
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
new file mode 100644
index 0000000..d88864b
--- /dev/null
+++ b/src/basic/in-addr-util.c
@@ -0,0 +1,338 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <arpa/inet.h>
+
+#include "in-addr-util.h"
+
+int in_addr_is_null(int family, const union in_addr_union *u) {
+ assert(u);
+
+ if (family == AF_INET)
+ return u->in.s_addr == 0;
+
+ if (family == AF_INET6)
+ return
+ u->in6.s6_addr32[0] == 0 &&
+ u->in6.s6_addr32[1] == 0 &&
+ u->in6.s6_addr32[2] == 0 &&
+ u->in6.s6_addr32[3] == 0;
+
+ return -EAFNOSUPPORT;
+}
+
+int in_addr_is_link_local(int family, const union in_addr_union *u) {
+ assert(u);
+
+ if (family == AF_INET)
+ return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
+
+ if (family == AF_INET6)
+ return IN6_IS_ADDR_LINKLOCAL(&u->in6);
+
+ return -EAFNOSUPPORT;
+}
+
+int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
+ assert(a);
+ assert(b);
+
+ if (family == AF_INET)
+ return a->in.s_addr == b->in.s_addr;
+
+ if (family == AF_INET6)
+ return
+ a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
+ a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
+ a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
+ a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
+
+ return -EAFNOSUPPORT;
+}
+
+int in_addr_prefix_intersect(
+ int family,
+ const union in_addr_union *a,
+ unsigned aprefixlen,
+ const union in_addr_union *b,
+ unsigned bprefixlen) {
+
+ unsigned m;
+
+ assert(a);
+ assert(b);
+
+ /* Checks whether there are any addresses that are in both
+ * networks */
+
+ m = MIN(aprefixlen, bprefixlen);
+
+ if (family == AF_INET) {
+ uint32_t x, nm;
+
+ x = be32toh(a->in.s_addr ^ b->in.s_addr);
+ nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
+
+ return (x & nm) == 0;
+ }
+
+ if (family == AF_INET6) {
+ unsigned i;
+
+ if (m > 128)
+ m = 128;
+
+ for (i = 0; i < 16; i++) {
+ uint8_t x, nm;
+
+ x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
+
+ if (m < 8)
+ nm = 0xFF << (8 - m);
+ else
+ nm = 0xFF;
+
+ if ((x & nm) != 0)
+ return 0;
+
+ if (m > 8)
+ m -= 8;
+ else
+ m = 0;
+ }
+
+ return 1;
+ }
+
+ return -EAFNOSUPPORT;
+}
+
+int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
+ assert(u);
+
+ /* Increases the network part of an address by one. Returns
+ * positive it that succeeds, or 0 if this overflows. */
+
+ if (prefixlen <= 0)
+ return 0;
+
+ if (family == AF_INET) {
+ uint32_t c, n;
+
+ if (prefixlen > 32)
+ prefixlen = 32;
+
+ c = be32toh(u->in.s_addr);
+ n = c + (1UL << (32 - prefixlen));
+ if (n < c)
+ return 0;
+ n &= 0xFFFFFFFFUL << (32 - prefixlen);
+
+ u->in.s_addr = htobe32(n);
+ return 1;
+ }
+
+ if (family == AF_INET6) {
+ struct in6_addr add = {}, result;
+ uint8_t overflow = 0;
+ unsigned i;
+
+ if (prefixlen > 128)
+ prefixlen = 128;
+
+ /* First calculate what we have to add */
+ add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
+
+ for (i = 16; i > 0; i--) {
+ unsigned j = i - 1;
+
+ result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
+ overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
+ }
+
+ if (overflow)
+ return 0;
+
+ u->in6 = result;
+ return 1;
+ }
+
+ return -EAFNOSUPPORT;
+}
+
+int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
+ char *x;
+ size_t l;
+
+ assert(u);
+ assert(ret);
+
+ if (family == AF_INET)
+ l = INET_ADDRSTRLEN;
+ else if (family == AF_INET6)
+ l = INET6_ADDRSTRLEN;
+ else
+ return -EAFNOSUPPORT;
+
+ x = new(char, l);
+ if (!x)
+ return -ENOMEM;
+
+ errno = 0;
+ if (!inet_ntop(family, u, x, l)) {
+ free(x);
+ return errno ? -errno : -EINVAL;
+ }
+
+ *ret = x;
+ return 0;
+}
+
+int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
+
+ assert(s);
+ assert(ret);
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return -EAFNOSUPPORT;
+
+ errno = 0;
+ if (inet_pton(family, s, ret) <= 0)
+ return errno ? -errno : -EINVAL;
+
+ return 0;
+}
+
+int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
+ int r;
+
+ assert(s);
+ assert(family);
+ assert(ret);
+
+ r = in_addr_from_string(AF_INET, s, ret);
+ if (r >= 0) {
+ *family = AF_INET;
+ return 0;
+ }
+
+ r = in_addr_from_string(AF_INET6, s, ret);
+ if (r >= 0) {
+ *family = AF_INET6;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
+ assert(addr);
+
+ return 32 - u32ctz(be32toh(addr->s_addr));
+}
+
+struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
+ assert(addr);
+ assert(prefixlen <= 32);
+
+ /* Shifting beyond 32 is not defined, handle this specially. */
+ if (prefixlen == 0)
+ addr->s_addr = 0;
+ else
+ addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
+
+ return addr;
+}
+
+int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
+ uint8_t msb_octet = *(uint8_t*) addr;
+
+ /* addr may not be aligned, so make sure we only access it byte-wise */
+
+ assert(addr);
+ assert(prefixlen);
+
+ if (msb_octet < 128)
+ /* class A, leading bits: 0 */
+ *prefixlen = 8;
+ else if (msb_octet < 192)
+ /* class B, leading bits 10 */
+ *prefixlen = 16;
+ else if (msb_octet < 224)
+ /* class C, leading bits 110 */
+ *prefixlen = 24;
+ else
+ /* class D or E, no default prefixlen */
+ return -ERANGE;
+
+ return 0;
+}
+
+int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
+ unsigned char prefixlen;
+ int r;
+
+ assert(addr);
+ assert(mask);
+
+ r = in_addr_default_prefixlen(addr, &prefixlen);
+ if (r < 0)
+ return r;
+
+ in_addr_prefixlen_to_netmask(mask, prefixlen);
+ return 0;
+}
+
+int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
+ assert(addr);
+
+ if (family == AF_INET) {
+ struct in_addr mask;
+
+ if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
+ return -EINVAL;
+
+ addr->in.s_addr &= mask.s_addr;
+ return 0;
+ }
+
+ if (family == AF_INET6) {
+ unsigned i;
+
+ for (i = 0; i < 16; i++) {
+ uint8_t mask;
+
+ if (prefixlen >= 8) {
+ mask = 0xFF;
+ prefixlen -= 8;
+ } else {
+ mask = 0xFF << (8 - prefixlen);
+ prefixlen = 0;
+ }
+
+ addr->in6.s6_addr[i] &= mask;
+ }
+
+ return 0;
+ }
+
+ return -EAFNOSUPPORT;
+}
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
new file mode 100644
index 0000000..51af088
--- /dev/null
+++ b/src/basic/in-addr-util.h
@@ -0,0 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <netinet/in.h>
+
+#include "macro.h"
+#include "util.h"
+
+union in_addr_union {
+ struct in_addr in;
+ struct in6_addr in6;
+};
+
+int in_addr_is_null(int family, const union in_addr_union *u);
+int in_addr_is_link_local(int family, const union in_addr_union *u);
+int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
+int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
+int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
+int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
+int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
+int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret);
+unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr);
+struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
+int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
+int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
+int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
+
+static inline size_t FAMILY_ADDRESS_SIZE(int family) {
+ assert(family == AF_INET || family == AF_INET6);
+ return family == AF_INET6 ? 16 : 4;
+}
+
+#define IN_ADDR_NULL ((union in_addr_union) {})
diff --git a/src/basic/ioprio.h b/src/basic/ioprio.h
new file mode 100644
index 0000000..e5c71d0
--- /dev/null
+++ b/src/basic/ioprio.h
@@ -0,0 +1,55 @@
+#ifndef IOPRIO_H
+#define IOPRIO_H
+
+/* This is minimal version of Linux' linux/ioprio.h header file, which
+ * is licensed GPL2 */
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+/*
+ * Gives us 8 prio classes with 13-bits of data for each class
+ */
+#define IOPRIO_BITS (16)
+#define IOPRIO_CLASS_SHIFT (13)
+#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
+
+#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
+#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
+#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
+
+#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE)
+
+/*
+ * These are the io priority groups as implemented by CFQ. RT is the realtime
+ * class, it always gets premium service. BE is the best-effort scheduling
+ * class, the default for any process. IDLE is the idle scheduling class, it
+ * is only served when no one else is using the disk.
+ */
+enum {
+ IOPRIO_CLASS_NONE,
+ IOPRIO_CLASS_RT,
+ IOPRIO_CLASS_BE,
+ IOPRIO_CLASS_IDLE,
+};
+
+/*
+ * 8 best effort priority levels are supported
+ */
+#define IOPRIO_BE_NR (8)
+
+enum {
+ IOPRIO_WHO_PROCESS = 1,
+ IOPRIO_WHO_PGRP,
+ IOPRIO_WHO_USER,
+};
+
+static inline int ioprio_set(int which, int who, int ioprio) {
+ return syscall(__NR_ioprio_set, which, who, ioprio);
+}
+
+static inline int ioprio_get(int which, int who) {
+ return syscall(__NR_ioprio_get, which, who);
+}
+
+#endif
diff --git a/src/basic/json.c b/src/basic/json.c
new file mode 100644
index 0000000..be40a0d
--- /dev/null
+++ b/src/basic/json.c
@@ -0,0 +1,866 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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>
+#include <math.h>
+#include "macro.h"
+#include "utf8.h"
+#include "json.h"
+
+int json_variant_new(JsonVariant **ret, JsonVariantType type) {
+ JsonVariant *v;
+
+ v = new0(JsonVariant, 1);
+ if (!v)
+ return -ENOMEM;
+ v->type = type;
+ *ret = v;
+ return 0;
+}
+
+static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
+ int r;
+
+ assert(ret);
+ assert(variant);
+
+ ret->type = variant->type;
+ ret->size = variant->size;
+
+ if (variant->type == JSON_VARIANT_STRING) {
+ ret->string = memdup(variant->string, variant->size+1);
+ if (!ret->string)
+ return -ENOMEM;
+ } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
+ size_t i;
+
+ ret->objects = new0(JsonVariant, variant->size);
+ if (!ret->objects)
+ return -ENOMEM;
+
+ for (i = 0; i < variant->size; ++i) {
+ r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
+ if (r < 0)
+ return r;
+ }
+ } else
+ ret->value = variant->value;
+
+ return 0;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant);
+
+static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ return json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ return NULL;
+}
+
+static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
+ if (!variant)
+ return NULL;
+
+ for (size_t i = 0; i < size; ++i)
+ json_variant_unref_inner(&variant[i]);
+
+ free(variant);
+ return NULL;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant) {
+ size_t i;
+
+ assert(variant);
+
+ if (!variant->objects)
+ return NULL;
+
+ for (i = 0; i < variant->size; ++i)
+ json_variant_unref_inner(&variant->objects[i]);
+
+ free(variant->objects);
+ return NULL;
+}
+
+static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
+ size_t i = 0;
+ JsonVariant *p = NULL;
+
+ if (!variant)
+ return NULL;
+
+ while((p = (variant[i++])) != NULL) {
+ if (p->type == JSON_VARIANT_STRING)
+ free(p->string);
+ free(p);
+ }
+
+ free(variant);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
+
+JsonVariant *json_variant_unref(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ free(variant);
+
+ return NULL;
+}
+
+char *json_variant_string(JsonVariant *variant){
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_STRING);
+
+ return variant->string;
+}
+
+bool json_variant_bool(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_BOOLEAN);
+
+ return variant->value.boolean;
+}
+
+intmax_t json_variant_integer(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_INTEGER);
+
+ return variant->value.integer;
+}
+
+double json_variant_real(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_REAL);
+
+ return variant->value.real;
+}
+
+JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
+ assert(index < variant->size);
+ assert(variant->objects);
+
+ return &variant->objects[index];
+}
+
+JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
+ size_t i;
+
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_OBJECT);
+ assert(variant->objects);
+
+ for (i = 0; i < variant->size; i += 2) {
+ JsonVariant *p = &variant->objects[i];
+ if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
+ return &variant->objects[i + 1];
+ }
+
+ return NULL;
+}
+
+static void inc_lines(unsigned *line, const char *s, size_t n) {
+ const char *p = s;
+
+ if (!line)
+ return;
+
+ for (;;) {
+ const char *f;
+
+ f = memchr(p, '\n', n);
+ if (!f)
+ return;
+
+ n -= (f - p) + 1;
+ p = f + 1;
+ (*line)++;
+ }
+}
+
+static int unhex_ucs2(const char *c, uint16_t *ret) {
+ int aa, bb, cc, dd;
+ uint16_t x;
+
+ assert(c);
+ assert(ret);
+
+ aa = unhexchar(c[0]);
+ if (aa < 0)
+ return -EINVAL;
+
+ bb = unhexchar(c[1]);
+ if (bb < 0)
+ return -EINVAL;
+
+ cc = unhexchar(c[2]);
+ if (cc < 0)
+ return -EINVAL;
+
+ dd = unhexchar(c[3]);
+ if (dd < 0)
+ return -EINVAL;
+
+ x = ((uint16_t) aa << 12) |
+ ((uint16_t) bb << 8) |
+ ((uint16_t) cc << 4) |
+ ((uint16_t) dd);
+
+ if (x <= 0)
+ return -EINVAL;
+
+ *ret = x;
+
+ return 0;
+}
+
+static int json_parse_string(const char **p, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ size_t n = 0, allocated = 0;
+ const char *c;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ c = *p;
+
+ if (*c != '"')
+ return -EINVAL;
+
+ c++;
+
+ for (;;) {
+ int len;
+
+ /* Check for EOF */
+ if (*c == 0)
+ return -EINVAL;
+
+ /* Check for control characters 0x00..0x1f */
+ if (*c > 0 && *c < ' ')
+ return -EINVAL;
+
+ /* Check for control character 0x7f */
+ if (*c == 0x7f)
+ return -EINVAL;
+
+ if (*c == '"') {
+ if (!s) {
+ s = strdup("");
+ if (!s)
+ return -ENOMEM;
+ } else
+ s[n] = 0;
+
+ *p = c + 1;
+
+ *ret = s;
+ s = NULL;
+ return JSON_STRING;
+ }
+
+ if (*c == '\\') {
+ char ch = 0;
+ c++;
+
+ if (*c == 0)
+ return -EINVAL;
+
+ if (IN_SET(*c, '"', '\\', '/'))
+ ch = *c;
+ else if (*c == 'b')
+ ch = '\b';
+ else if (*c == 'f')
+ ch = '\f';
+ else if (*c == 'n')
+ ch = '\n';
+ else if (*c == 'r')
+ ch = '\r';
+ else if (*c == 't')
+ ch = '\t';
+ else if (*c == 'u') {
+ uint16_t x;
+ int r;
+
+ r = unhex_ucs2(c + 1, &x);
+ if (r < 0)
+ return r;
+
+ c += 5;
+
+ if (!GREEDY_REALLOC(s, allocated, n + 4))
+ return -ENOMEM;
+
+ if (!utf16_is_surrogate(x))
+ n += utf8_encode_unichar(s + n, x);
+ else if (utf16_is_trailing_surrogate(x))
+ return -EINVAL;
+ else {
+ uint16_t y;
+
+ if (c[0] != '\\' || c[1] != 'u')
+ return -EINVAL;
+
+ r = unhex_ucs2(c + 2, &y);
+ if (r < 0)
+ return r;
+
+ c += 6;
+
+ if (!utf16_is_trailing_surrogate(y))
+ return -EINVAL;
+
+ n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
+ }
+
+ continue;
+ } else
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(s, allocated, n + 2))
+ return -ENOMEM;
+
+ s[n++] = ch;
+ c ++;
+ continue;
+ }
+
+ len = utf8_encoded_valid_unichar(c);
+ if (len < 0)
+ return len;
+
+ if (!GREEDY_REALLOC(s, allocated, n + len + 1))
+ return -ENOMEM;
+
+ memcpy(s + n, c, len);
+ n += len;
+ c += len;
+ }
+}
+
+static int json_parse_number(const char **p, union json_value *ret) {
+ bool negative = false, exponent_negative = false, is_double = false;
+ double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
+ intmax_t i = 0;
+ const char *c;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ c = *p;
+
+ if (*c == '-') {
+ negative = true;
+ c++;
+ }
+
+ if (*c == '0')
+ c++;
+ else {
+ if (!strchr("123456789", *c) || *c == 0)
+ return -EINVAL;
+
+ do {
+ if (!is_double) {
+ int64_t t;
+
+ t = 10 * i + (*c - '0');
+ if (t < i) /* overflow */
+ is_double = false;
+ else
+ i = t;
+ }
+
+ x = 10.0 * x + (*c - '0');
+ c++;
+ } while (strchr("0123456789", *c) && *c != 0);
+ }
+
+ if (*c == '.') {
+ is_double = true;
+ c++;
+
+ if (!strchr("0123456789", *c) || *c == 0)
+ return -EINVAL;
+
+ do {
+ y = 10.0 * y + (*c - '0');
+ shift = 10.0 * shift;
+ c++;
+ } while (strchr("0123456789", *c) && *c != 0);
+ }
+
+ if (*c == 'e' || *c == 'E') {
+ is_double = true;
+ c++;
+
+ if (*c == '-') {
+ exponent_negative = true;
+ c++;
+ } else if (*c == '+')
+ c++;
+
+ if (!strchr("0123456789", *c) || *c == 0)
+ return -EINVAL;
+
+ do {
+ exponent = 10.0 * exponent + (*c - '0');
+ c++;
+ } while (strchr("0123456789", *c) && *c != 0);
+ }
+
+ *p = c;
+
+ if (is_double) {
+ ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent);
+ return JSON_REAL;
+ } else {
+ ret->integer = negative ? -i : i;
+ return JSON_INTEGER;
+ }
+}
+
+int json_tokenize(
+ const char **p,
+ char **ret_string,
+ union json_value *ret_value,
+ void **state,
+ unsigned *line) {
+
+ const char *c;
+ int t;
+ int r;
+
+ enum {
+ STATE_NULL,
+ STATE_VALUE,
+ STATE_VALUE_POST,
+ };
+
+ assert(p);
+ assert(*p);
+ assert(ret_string);
+ assert(ret_value);
+ assert(state);
+
+ t = PTR_TO_INT(*state);
+ c = *p;
+
+ if (t == STATE_NULL) {
+ if (line)
+ *line = 1;
+ t = STATE_VALUE;
+ }
+
+ for (;;) {
+ const char *b;
+
+ b = c + strspn(c, WHITESPACE);
+ if (*b == 0)
+ return JSON_END;
+
+ inc_lines(line, c, b - c);
+ c = b;
+
+ switch (t) {
+
+ case STATE_VALUE:
+
+ if (*c == '{') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE);
+ return JSON_OBJECT_OPEN;
+
+ } else if (*c == '}') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_OBJECT_CLOSE;
+
+ } else if (*c == '[') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE);
+ return JSON_ARRAY_OPEN;
+
+ } else if (*c == ']') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_ARRAY_CLOSE;
+
+ } else if (*c == '"') {
+ r = json_parse_string(&c, ret_string);
+ if (r < 0)
+ return r;
+
+ *ret_value = JSON_VALUE_NULL;
+ *p = c;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return r;
+
+ } else if (strchr("-0123456789", *c)) {
+ r = json_parse_number(&c, ret_value);
+ if (r < 0)
+ return r;
+
+ *ret_string = NULL;
+ *p = c;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return r;
+
+ } else if (startswith(c, "true")) {
+ *ret_string = NULL;
+ ret_value->boolean = true;
+ *p = c + 4;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_BOOLEAN;
+
+ } else if (startswith(c, "false")) {
+ *ret_string = NULL;
+ ret_value->boolean = false;
+ *p = c + 5;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_BOOLEAN;
+
+ } else if (startswith(c, "null")) {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 4;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_NULL;
+
+ } else
+ return -EINVAL;
+
+ case STATE_VALUE_POST:
+
+ if (*c == ':') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE);
+ return JSON_COLON;
+ } else if (*c == ',') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE);
+ return JSON_COMMA;
+ } else if (*c == '}') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_OBJECT_CLOSE;
+ } else if (*c == ']') {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ *p = c + 1;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ return JSON_ARRAY_CLOSE;
+ } else
+ return -EINVAL;
+ }
+
+ }
+}
+
+static bool json_is_value(JsonVariant *var) {
+ assert(var);
+
+ return var->type != JSON_VARIANT_CONTROL;
+}
+
+static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
+ bool arr = scope->type == JSON_VARIANT_ARRAY;
+ int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
+ size_t allocated = 0, size = 0;
+ JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
+ enum {
+ STATE_KEY,
+ STATE_COLON,
+ STATE_COMMA,
+ STATE_VALUE
+ } state = arr ? STATE_VALUE : STATE_KEY;
+
+ assert(tokens);
+ assert(i);
+ assert(scope);
+
+ while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
+ bool stopper;
+ int r;
+
+ stopper = !json_is_value(var) && var->value.integer == terminator;
+
+ if (stopper) {
+ if (state != STATE_COMMA && size > 0)
+ goto error;
+
+ goto out;
+ }
+
+ if (state == STATE_KEY) {
+ if (var->type != JSON_VARIANT_STRING)
+ goto error;
+ else {
+ key = var;
+ state = STATE_COLON;
+ }
+ }
+ else if (state == STATE_COLON) {
+ if (key == NULL)
+ goto error;
+
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COLON)
+ goto error;
+
+ state = STATE_VALUE;
+ }
+ else if (state == STATE_VALUE) {
+ _cleanup_json_variant_unref_ JsonVariant *v = NULL;
+ size_t toadd = arr ? 1 : 2;
+
+ if (!json_is_value(var)) {
+ int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
+
+ r = json_variant_new(&v, type);
+ if (r < 0)
+ goto error;
+
+ r = json_scoped_parse(tokens, i, n, v);
+ if (r < 0)
+ goto error;
+
+ value = v;
+ }
+ else
+ value = var;
+
+ if(!GREEDY_REALLOC(items, allocated, size + toadd))
+ goto error;
+
+ if (arr) {
+ r = json_variant_deep_copy(&items[size], value);
+ if (r < 0)
+ goto error;
+ } else {
+ r = json_variant_deep_copy(&items[size], key);
+ if (r < 0)
+ goto error;
+
+ r = json_variant_deep_copy(&items[size+1], value);
+ if (r < 0)
+ goto error;
+ }
+
+ size += toadd;
+ state = STATE_COMMA;
+ }
+ else if (state == STATE_COMMA) {
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COMMA)
+ goto error;
+
+ key = NULL;
+ value = NULL;
+
+ state = arr ? STATE_VALUE : STATE_KEY;
+ }
+ }
+
+error:
+ json_raw_unref(items, size);
+ return -EBADMSG;
+
+out:
+ scope->size = size;
+ scope->objects = items;
+
+ return scope->type;
+}
+
+static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
+ size_t it = 0;
+ int r;
+ JsonVariant *e;
+ _cleanup_json_variant_unref_ JsonVariant *p = NULL;
+
+ assert(tokens);
+ assert(ntokens);
+
+ e = tokens[it++];
+ r = json_variant_new(&p, JSON_VARIANT_OBJECT);
+ if (r < 0)
+ return r;
+
+ if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
+ return -EBADMSG;
+
+ r = json_scoped_parse(tokens, &it, ntokens, p);
+ if (r < 0)
+ return r;
+
+ *rv = p;
+ p = NULL;
+
+ return 0;
+}
+
+static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
+ union json_value v = {};
+ void *json_state = NULL;
+ const char *p;
+ int t, r;
+ size_t allocated = 0, s = 0;
+
+ assert(string);
+ assert(n);
+
+ if (size <= 0)
+ return -EBADMSG;
+
+ buf = strndup(string, size);
+ if (!buf)
+ return -ENOMEM;
+
+ p = buf;
+ for (;;) {
+ _cleanup_json_variant_unref_ JsonVariant *var = NULL;
+ _cleanup_free_ char *rstr = NULL;
+
+ t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
+
+ if (t < 0)
+ return t;
+ else if (t == JSON_END)
+ break;
+
+ if (t <= JSON_ARRAY_CLOSE) {
+ r = json_variant_new(&var, JSON_VARIANT_CONTROL);
+ if (r < 0)
+ return r;
+ var->value.integer = t;
+ } else {
+ switch (t) {
+ case JSON_STRING:
+ r = json_variant_new(&var, JSON_VARIANT_STRING);
+ if (r < 0)
+ return r;
+ var->size = strlen(rstr);
+ var->string = strdup(rstr);
+ if (!var->string) {
+ return -ENOMEM;
+ }
+ break;
+ case JSON_INTEGER:
+ r = json_variant_new(&var, JSON_VARIANT_INTEGER);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_REAL:
+ r = json_variant_new(&var, JSON_VARIANT_REAL);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_BOOLEAN:
+ r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_NULL:
+ r = json_variant_new(&var, JSON_VARIANT_NULL);
+ if (r < 0)
+ return r;
+ break;
+ }
+ }
+
+ if (!GREEDY_REALLOC(items, allocated, s+2))
+ return -ENOMEM;
+
+ items[s++] = var;
+ items[s] = NULL;
+ var = NULL;
+ }
+
+ *n = s;
+ *tokens = items;
+ items = NULL;
+
+ return 0;
+}
+
+int json_parse(const char *string, JsonVariant **rv) {
+ _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
+ JsonVariant *v = NULL;
+ size_t n = 0;
+ int r;
+
+ assert(string);
+ assert(rv);
+
+ r = json_tokens(string, strlen(string), &s, &n);
+ if (r < 0)
+ return r;
+
+ r = json_parse_tokens(s, n, &v);
+ if (r < 0)
+ return r;
+
+ *rv = v;
+ return 0;
+}
diff --git a/src/basic/json.h b/src/basic/json.h
new file mode 100644
index 0000000..e0b4d81
--- /dev/null
+++ b/src/basic/json.h
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <stdbool.h>
+#include "util.h"
+
+enum {
+ JSON_END,
+ JSON_COLON,
+ JSON_COMMA,
+ JSON_OBJECT_OPEN,
+ JSON_OBJECT_CLOSE,
+ JSON_ARRAY_OPEN,
+ JSON_ARRAY_CLOSE,
+ JSON_STRING,
+ JSON_REAL,
+ JSON_INTEGER,
+ JSON_BOOLEAN,
+ JSON_NULL,
+};
+
+typedef enum {
+ JSON_VARIANT_CONTROL,
+ JSON_VARIANT_STRING,
+ JSON_VARIANT_INTEGER,
+ JSON_VARIANT_BOOLEAN,
+ JSON_VARIANT_REAL,
+ JSON_VARIANT_ARRAY,
+ JSON_VARIANT_OBJECT,
+ JSON_VARIANT_NULL
+} JsonVariantType;
+
+union json_value {
+ bool boolean;
+ double real;
+ intmax_t integer;
+};
+
+typedef struct JsonVariant {
+ JsonVariantType type;
+ size_t size;
+ union {
+ char *string;
+ struct JsonVariant *objects;
+ union json_value value;
+ };
+} JsonVariant;
+
+int json_variant_new(JsonVariant **ret, JsonVariantType type);
+JsonVariant *json_variant_unref(JsonVariant *v);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
+#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
+
+char *json_variant_string(JsonVariant *v);
+bool json_variant_bool(JsonVariant *v);
+intmax_t json_variant_integer(JsonVariant *v);
+double json_variant_real(JsonVariant *v);
+
+JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
+JsonVariant *json_variant_value(JsonVariant *v, const char *key);
+
+#define JSON_VALUE_NULL ((union json_value) {})
+
+int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
+
+int json_parse(const char *string, JsonVariant **rv);
+int json_parse_measure(const char *string, size_t *size);
diff --git a/src/basic/label.c b/src/basic/label.c
new file mode 100644
index 0000000..82f10b2
--- /dev/null
+++ b/src/basic/label.c
@@ -0,0 +1,80 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "selinux-util.h"
+#include "smack-util.h"
+#include "util.h"
+#include "label.h"
+
+int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+ int r, q;
+
+ r = mac_selinux_fix(path, ignore_enoent, ignore_erofs);
+ q = mac_smack_fix(path, ignore_enoent, ignore_erofs);
+
+ if (r < 0)
+ return r;
+ if (q < 0)
+ return q;
+
+ return 0;
+}
+
+int mkdir_label(const char *path, mode_t mode) {
+ int r;
+
+ assert(path);
+
+ r = mac_selinux_create_file_prepare(path, S_IFDIR);
+ if (r < 0)
+ return r;
+
+ if (mkdir(path, mode) < 0)
+ r = -errno;
+
+ mac_selinux_create_file_clear();
+
+ if (r < 0)
+ return r;
+
+ return mac_smack_fix(path, false, false);
+}
+
+int symlink_label(const char *old_path, const char *new_path) {
+ int r;
+
+ assert(old_path);
+ assert(new_path);
+
+ r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
+ if (r < 0)
+ return r;
+
+ if (symlink(old_path, new_path) < 0)
+ r = -errno;
+
+ mac_selinux_create_file_clear();
+
+ if (r < 0)
+ return r;
+
+ return mac_smack_fix(new_path, false, false);
+}
diff --git a/src/basic/label.h b/src/basic/label.h
new file mode 100644
index 0000000..8070bcb
--- /dev/null
+++ b/src/basic/label.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <sys/types.h>
+
+int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
+
+int mkdir_label(const char *path, mode_t mode);
+int symlink_label(const char *old_path, const char *new_path);
diff --git a/src/basic/linux/Makefile b/src/basic/linux/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/basic/linux/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/basic/list.h b/src/basic/list.h
new file mode 100644
index 0000000..2939216
--- /dev/null
+++ b/src/basic/list.h
@@ -0,0 +1,158 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/>.
+***/
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define LIST_HEAD(t,name) \
+ t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define LIST_FIELDS(t,name) \
+ t *name##_next, *name##_prev
+
+/* Initialize the list's head */
+#define LIST_HEAD_INIT(head) \
+ do { \
+ (head) = NULL; } \
+ while(false)
+
+/* Initialize a list item */
+#define LIST_INIT(name,item) \
+ do { \
+ typeof(*(item)) *_item = (item); \
+ assert(_item); \
+ _item->name##_prev = _item->name##_next = NULL; \
+ } while(false)
+
+/* Prepend an item to the list */
+#define LIST_PREPEND(name,head,item) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_item = (item); \
+ assert(_item); \
+ if ((_item->name##_next = *_head)) \
+ _item->name##_next->name##_prev = _item; \
+ _item->name##_prev = NULL; \
+ *_head = _item; \
+ } while(false)
+
+/* Append an item to the list */
+#define LIST_APPEND(name,head,item) \
+ do { \
+ typeof(*(head)) *_tail; \
+ LIST_FIND_TAIL(name,head,_tail); \
+ LIST_INSERT_AFTER(name,head,_tail,item); \
+ } while(false)
+
+/* Remove an item from the list */
+#define LIST_REMOVE(name,head,item) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_item = (item); \
+ assert(_item); \
+ if (_item->name##_next) \
+ _item->name##_next->name##_prev = _item->name##_prev; \
+ if (_item->name##_prev) \
+ _item->name##_prev->name##_next = _item->name##_next; \
+ else { \
+ assert(*_head == _item); \
+ *_head = _item->name##_next; \
+ } \
+ _item->name##_next = _item->name##_prev = NULL; \
+ } while(false)
+
+/* Find the head of the list */
+#define LIST_FIND_HEAD(name,item,head) \
+ do { \
+ typeof(*(item)) *_item = (item); \
+ if (!_item) \
+ (head) = NULL; \
+ else { \
+ while (_item->name##_prev) \
+ _item = _item->name##_prev; \
+ (head) = _item; \
+ } \
+ } while (false)
+
+/* Find the tail of the list */
+#define LIST_FIND_TAIL(name,item,tail) \
+ do { \
+ typeof(*(item)) *_item = (item); \
+ if (!_item) \
+ (tail) = NULL; \
+ else { \
+ while (_item->name##_next) \
+ _item = _item->name##_next; \
+ (tail) = _item; \
+ } \
+ } while (false)
+
+/* Insert an item after another one (a = where, b = what) */
+#define LIST_INSERT_AFTER(name,head,a,b) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
+ assert(_b); \
+ if (!_a) { \
+ if ((_b->name##_next = *_head)) \
+ _b->name##_next->name##_prev = _b; \
+ _b->name##_prev = NULL; \
+ *_head = _b; \
+ } else { \
+ if ((_b->name##_next = _a->name##_next)) \
+ _b->name##_next->name##_prev = _b; \
+ _b->name##_prev = _a; \
+ _a->name##_next = _b; \
+ } \
+ } while(false)
+
+#define LIST_JUST_US(name,item) \
+ (!(item)->name##_prev && !(item)->name##_next) \
+
+#define LIST_FOREACH(name,i,head) \
+ for ((i) = (head); (i); (i) = (i)->name##_next)
+
+#define LIST_FOREACH_SAFE(name,i,n,head) \
+ for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
+
+#define LIST_FOREACH_BEFORE(name,i,p) \
+ for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
+
+#define LIST_FOREACH_AFTER(name,i,p) \
+ for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+
+/* Iterate through all the members of the list p is included in, but skip over p */
+#define LIST_FOREACH_OTHERS(name,i,p) \
+ for (({ \
+ (i) = (p); \
+ while ((i) && (i)->name##_prev) \
+ (i) = (i)->name##_prev; \
+ if ((i) == (p)) \
+ (i) = (p)->name##_next; \
+ }); \
+ (i); \
+ (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
+
+/* Loop starting from p->next until p->prev.
+ p can be adjusted meanwhile. */
+#define LIST_LOOP_BUT_ONE(name,i,head,p) \
+ for ((i) = (p)->name##_next ? (p)->name##_next : (head); \
+ (i) != (p); \
+ (i) = (i)->name##_next ? (i)->name##_next : (head))
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
new file mode 100644
index 0000000..61db9a8
--- /dev/null
+++ b/src/basic/locale-util.c
@@ -0,0 +1,224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/mman.h>
+
+#include "set.h"
+#include "util.h"
+#include "utf8.h"
+#include "strv.h"
+
+#include "locale-util.h"
+
+static int add_locales_from_archive(Set *locales) {
+ /* Stolen from glibc... */
+
+ struct locarhead {
+ uint32_t magic;
+ /* Serial number. */
+ uint32_t serial;
+ /* Name hash table. */
+ uint32_t namehash_offset;
+ uint32_t namehash_used;
+ uint32_t namehash_size;
+ /* String table. */
+ uint32_t string_offset;
+ uint32_t string_used;
+ uint32_t string_size;
+ /* Table with locale records. */
+ uint32_t locrectab_offset;
+ uint32_t locrectab_used;
+ uint32_t locrectab_size;
+ /* MD5 sum hash table. */
+ uint32_t sumhash_offset;
+ uint32_t sumhash_used;
+ uint32_t sumhash_size;
+ };
+
+ struct namehashent {
+ /* Hash value of the name. */
+ uint32_t hashval;
+ /* Offset of the name in the string table. */
+ uint32_t name_offset;
+ /* Offset of the locale record. */
+ uint32_t locrec_offset;
+ };
+
+ const struct locarhead *h;
+ const struct namehashent *e;
+ const void *p = MAP_FAILED;
+ _cleanup_close_ int fd = -1;
+ size_t sz = 0;
+ struct stat st;
+ unsigned i;
+ int r;
+
+ fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return errno == ENOENT ? 0 : -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EBADMSG;
+
+ if (st.st_size < (off_t) sizeof(struct locarhead))
+ return -EBADMSG;
+
+ p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (p == MAP_FAILED)
+ return -errno;
+
+ h = (const struct locarhead *) p;
+ if (h->magic != 0xde020109 ||
+ h->namehash_offset + h->namehash_size > st.st_size ||
+ h->string_offset + h->string_size > st.st_size ||
+ h->locrectab_offset + h->locrectab_size > st.st_size ||
+ h->sumhash_offset + h->sumhash_size > st.st_size) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
+ for (i = 0; i < h->namehash_size; i++) {
+ char *z;
+
+ if (e[i].locrec_offset == 0)
+ continue;
+
+ if (!utf8_is_valid((char*) p + e[i].name_offset))
+ continue;
+
+ z = strdup((char*) p + e[i].name_offset);
+ if (!z) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = set_consume(locales, z);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+ finish:
+ if (p != MAP_FAILED)
+ munmap((void*) p, sz);
+
+ return r;
+}
+
+static int add_locales_from_libdir (Set *locales) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *entry;
+ int r;
+
+ dir = opendir("/usr/lib/locale");
+ if (!dir)
+ return errno == ENOENT ? 0 : -errno;
+
+ FOREACH_DIRENT(entry, dir, return -errno) {
+ char *z;
+
+ if (entry->d_type != DT_DIR)
+ continue;
+
+ z = strdup(entry->d_name);
+ if (!z)
+ return -ENOMEM;
+
+ r = set_consume(locales, z);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ return 0;
+}
+
+int get_locales(char ***ret) {
+ _cleanup_set_free_ Set *locales = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ int r;
+
+ locales = set_new(&string_hash_ops);
+ if (!locales)
+ return -ENOMEM;
+
+ r = add_locales_from_archive(locales);
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+ r = add_locales_from_libdir(locales);
+ if (r < 0)
+ return r;
+
+ l = set_get_strv(locales);
+ if (!l)
+ return -ENOMEM;
+
+ strv_sort(l);
+
+ *ret = l;
+ l = NULL;
+
+ return 0;
+}
+
+bool locale_is_valid(const char *name) {
+
+ if (isempty(name))
+ return false;
+
+ if (strlen(name) >= 128)
+ return false;
+
+ if (!utf8_is_valid(name))
+ return false;
+
+ if (!filename_is_valid(name))
+ return false;
+
+ if (!string_is_safe(name))
+ return false;
+
+ return true;
+}
+
+static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
+ [VARIABLE_LANG] = "LANG",
+ [VARIABLE_LANGUAGE] = "LANGUAGE",
+ [VARIABLE_LC_CTYPE] = "LC_CTYPE",
+ [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
+ [VARIABLE_LC_TIME] = "LC_TIME",
+ [VARIABLE_LC_COLLATE] = "LC_COLLATE",
+ [VARIABLE_LC_MONETARY] = "LC_MONETARY",
+ [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
+ [VARIABLE_LC_PAPER] = "LC_PAPER",
+ [VARIABLE_LC_NAME] = "LC_NAME",
+ [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
+ [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
+ [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
+ [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
new file mode 100644
index 0000000..e48aa3d
--- /dev/null
+++ b/src/basic/locale-util.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <stdbool.h>
+
+#include "macro.h"
+
+typedef enum LocaleVariable {
+ /* We don't list LC_ALL here on purpose. People should be
+ * using LANG instead. */
+
+ VARIABLE_LANG,
+ VARIABLE_LANGUAGE,
+ VARIABLE_LC_CTYPE,
+ VARIABLE_LC_NUMERIC,
+ VARIABLE_LC_TIME,
+ VARIABLE_LC_COLLATE,
+ VARIABLE_LC_MONETARY,
+ VARIABLE_LC_MESSAGES,
+ VARIABLE_LC_PAPER,
+ VARIABLE_LC_NAME,
+ VARIABLE_LC_ADDRESS,
+ VARIABLE_LC_TELEPHONE,
+ VARIABLE_LC_MEASUREMENT,
+ VARIABLE_LC_IDENTIFICATION,
+ _VARIABLE_LC_MAX,
+ _VARIABLE_LC_INVALID = -1
+} LocaleVariable;
+
+int get_locales(char ***l);
+bool locale_is_valid(const char *name);
+
+const char* locale_variable_to_string(LocaleVariable i) _const_;
+LocaleVariable locale_variable_from_string(const char *s) _pure_;
diff --git a/src/basic/lockfile-util.c b/src/basic/lockfile-util.c
new file mode 100644
index 0000000..05e16d1
--- /dev/null
+++ b/src/basic/lockfile-util.c
@@ -0,0 +1,154 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/file.h>
+
+#include "util.h"
+#include "lockfile-util.h"
+#include "fileio.h"
+
+int make_lock_file(const char *p, int operation, LockFile *ret) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ /*
+ * We use UNPOSIX locks if they are available. They have nice
+ * semantics, and are mostly compatible with NFS. However,
+ * they are only available on new kernels. When we detect we
+ * are running on an older kernel, then we fall back to good
+ * old BSD locks. They also have nice semantics, but are
+ * slightly problematic on NFS, where they are upgraded to
+ * POSIX locks, even though locally they are orthogonal to
+ * POSIX locks.
+ */
+
+ t = strdup(p);
+ if (!t)
+ return -ENOMEM;
+
+ for (;;) {
+ struct flock fl = {
+ .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
+ .l_whence = SEEK_SET,
+ };
+ struct stat st;
+
+ fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
+ if (fd < 0)
+ return -errno;
+
+ r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
+ if (r < 0) {
+
+ /* If the kernel is too old, use good old BSD locks */
+ if (errno == EINVAL)
+ r = flock(fd, operation);
+
+ if (r < 0)
+ return errno == EAGAIN ? -EBUSY : -errno;
+ }
+
+ /* If we acquired the lock, let's check if the file
+ * still exists in the file system. If not, then the
+ * previous exclusive owner removed it and then closed
+ * it. In such a case our acquired lock is worthless,
+ * hence try again. */
+
+ r = fstat(fd, &st);
+ if (r < 0)
+ return -errno;
+ if (st.st_nlink > 0)
+ break;
+
+ fd = safe_close(fd);
+ }
+
+ ret->path = t;
+ ret->fd = fd;
+ ret->operation = operation;
+
+ fd = -1;
+ t = NULL;
+
+ return r;
+}
+
+int make_lock_file_for(const char *p, int operation, LockFile *ret) {
+ const char *fn;
+ char *t;
+
+ assert(p);
+ assert(ret);
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ t = newa(char, strlen(p) + 2 + 4 + 1);
+ stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
+
+ return make_lock_file(t, operation, ret);
+}
+
+void release_lock_file(LockFile *f) {
+ int r;
+
+ if (!f)
+ return;
+
+ if (f->path) {
+
+ /* If we are the exclusive owner we can safely delete
+ * the lock file itself. If we are not the exclusive
+ * owner, we can try becoming it. */
+
+ if (f->fd >= 0 &&
+ (f->operation & ~LOCK_NB) == LOCK_SH) {
+ static const struct flock fl = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ };
+
+ r = fcntl(f->fd, F_OFD_SETLK, &fl);
+ if (r < 0 && errno == EINVAL)
+ r = flock(f->fd, LOCK_EX|LOCK_NB);
+
+ if (r >= 0)
+ f->operation = LOCK_EX|LOCK_NB;
+ }
+
+ if ((f->operation & ~LOCK_NB) == LOCK_EX)
+ unlink_noerrno(f->path);
+
+ free(f->path);
+ f->path = NULL;
+ }
+
+ f->fd = safe_close(f->fd);
+ f->operation = 0;
+}
diff --git a/src/basic/lockfile-util.h b/src/basic/lockfile-util.h
new file mode 100644
index 0000000..38d4709
--- /dev/null
+++ b/src/basic/lockfile-util.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 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 "macro.h"
+#include "missing.h"
+
+typedef struct LockFile {
+ char *path;
+ int fd;
+ int operation;
+} LockFile;
+
+int make_lock_file(const char *p, int operation, LockFile *ret);
+int make_lock_file_for(const char *p, int operation, LockFile *ret);
+void release_lock_file(LockFile *f);
+
+#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
+
+#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
diff --git a/src/basic/log.c b/src/basic/log.c
new file mode 100644
index 0000000..b96afc4
--- /dev/null
+++ b/src/basic/log.c
@@ -0,0 +1,1138 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stddef.h>
+#include <printf.h>
+
+#include "sd-messages.h"
+#include "log.h"
+#include "util.h"
+#include "missing.h"
+#include "macro.h"
+#include "socket-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
+
+static LogTarget log_target = LOG_TARGET_CONSOLE;
+static int log_max_level = LOG_INFO;
+static int log_facility = LOG_DAEMON;
+
+static int console_fd = STDERR_FILENO;
+static int syslog_fd = -1;
+static int kmsg_fd = -1;
+static int journal_fd = -1;
+
+static bool syslog_is_stream = false;
+
+static bool show_color = false;
+static bool show_location = false;
+
+static bool upgrade_syslog_to_journal = false;
+
+/* Akin to glibc's __abort_msg; which is private and we hence cannot
+ * use here. */
+static char *log_abort_msg = NULL;
+
+void log_close_console(void) {
+
+ if (console_fd < 0)
+ return;
+
+ if (getpid() == 1) {
+ if (console_fd >= 3)
+ safe_close(console_fd);
+
+ console_fd = -1;
+ }
+}
+
+static int log_open_console(void) {
+
+ if (console_fd >= 0)
+ return 0;
+
+ if (getpid() == 1) {
+ console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (console_fd < 0)
+ return console_fd;
+ } else
+ console_fd = STDERR_FILENO;
+
+ return 0;
+}
+
+void log_close_kmsg(void) {
+ kmsg_fd = safe_close(kmsg_fd);
+}
+
+static int log_open_kmsg(void) {
+
+ if (kmsg_fd >= 0)
+ return 0;
+
+ kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (kmsg_fd < 0)
+ return -errno;
+
+ return 0;
+}
+
+void log_close_syslog(void) {
+ syslog_fd = safe_close(syslog_fd);
+}
+
+static int create_log_socket(int type) {
+ struct timeval tv;
+ int fd;
+
+ fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
+ /* We need a blocking fd here since we'd otherwise lose
+ messages way too early. However, let's not hang forever in the
+ unlikely case of a deadlock. */
+ if (getpid() == 1)
+ timeval_store(&tv, 10 * USEC_PER_MSEC);
+ else
+ timeval_store(&tv, 10 * USEC_PER_SEC);
+ (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+
+ return fd;
+}
+
+static int log_open_syslog(void) {
+
+ static const union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/dev/log",
+ };
+
+ int r;
+
+ if (syslog_fd >= 0)
+ return 0;
+
+ syslog_fd = create_log_socket(SOCK_DGRAM);
+ if (syslog_fd < 0) {
+ r = syslog_fd;
+ goto fail;
+ }
+
+ if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ safe_close(syslog_fd);
+
+ /* Some legacy syslog systems still use stream
+ * sockets. They really shouldn't. But what can we
+ * do... */
+ syslog_fd = create_log_socket(SOCK_STREAM);
+ if (syslog_fd < 0) {
+ r = syslog_fd;
+ goto fail;
+ }
+
+ if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ syslog_is_stream = true;
+ } else
+ syslog_is_stream = false;
+
+ return 0;
+
+fail:
+ log_close_syslog();
+ return r;
+}
+
+void log_close_journal(void) {
+ journal_fd = safe_close(journal_fd);
+}
+
+static int log_open_journal(void) {
+
+ static const union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/journal/socket",
+ };
+
+ int r;
+
+ if (journal_fd >= 0)
+ return 0;
+
+ journal_fd = create_log_socket(SOCK_DGRAM);
+ if (journal_fd < 0) {
+ r = journal_fd;
+ goto fail;
+ }
+
+ if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ log_close_journal();
+ return r;
+}
+
+int log_open(void) {
+ int r;
+
+ /* If we don't use the console we close it here, to not get
+ * killed by SAK. If we don't use syslog we close it here so
+ * that we are not confused by somebody deleting the socket in
+ * the fs. If we don't use /dev/kmsg we still keep it open,
+ * because there is no reason to close it. */
+
+ if (log_target == LOG_TARGET_NULL) {
+ log_close_journal();
+ log_close_syslog();
+ log_close_console();
+ return 0;
+ }
+
+ if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) ||
+ getpid() == 1 ||
+ isatty(STDERR_FILENO) <= 0) {
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) {
+ r = log_open_journal();
+ if (r >= 0) {
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
+ }
+
+ if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG) {
+ r = log_open_syslog();
+ if (r >= 0) {
+ log_close_journal();
+ log_close_console();
+ return r;
+ }
+ }
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SAFE ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_KMSG) {
+ r = log_open_kmsg();
+ if (r >= 0) {
+ log_close_journal();
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
+ }
+ }
+
+ log_close_journal();
+ log_close_syslog();
+
+ return log_open_console();
+}
+
+void log_set_target(LogTarget target) {
+ assert(target >= 0);
+ assert(target < _LOG_TARGET_MAX);
+
+ if (upgrade_syslog_to_journal) {
+ if (target == LOG_TARGET_SYSLOG)
+ target = LOG_TARGET_JOURNAL;
+ else if (target == LOG_TARGET_SYSLOG_OR_KMSG)
+ target = LOG_TARGET_JOURNAL_OR_KMSG;
+ }
+
+ log_target = target;
+}
+
+void log_close(void) {
+ log_close_journal();
+ log_close_syslog();
+ log_close_kmsg();
+ log_close_console();
+}
+
+void log_forget_fds(void) {
+ console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
+}
+
+void log_set_max_level(int level) {
+ assert((level & LOG_PRIMASK) == level);
+
+ log_max_level = level;
+}
+
+void log_set_facility(int facility) {
+ log_facility = facility;
+}
+
+static int write_to_console(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
+
+ char location[64], prefix[1 + DECIMAL_STR_MAX(int) + 2];
+ struct iovec iovec[6] = {};
+ unsigned n = 0;
+ bool highlight;
+
+ if (console_fd < 0)
+ return 0;
+
+ if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
+ sprintf(prefix, "<%i>", level);
+ IOVEC_SET_STRING(iovec[n++], prefix);
+ }
+
+ highlight = LOG_PRI(level) <= LOG_ERR && show_color;
+
+ if (show_location) {
+ snprintf(location, sizeof(location), "(%s:%i) ", file, line);
+ IOVEC_SET_STRING(iovec[n++], location);
+ }
+
+ if (highlight)
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
+ IOVEC_SET_STRING(iovec[n++], buffer);
+ if (highlight)
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(console_fd, iovec, n) < 0) {
+
+ if (errno == EIO && getpid() == 1) {
+
+ /* If somebody tried to kick us from our
+ * console tty (via vhangup() or suchlike),
+ * try to reconnect */
+
+ log_close_console();
+ log_open_console();
+
+ if (console_fd < 0)
+ return 0;
+
+ if (writev(console_fd, iovec, n) < 0)
+ return -errno;
+ } else
+ return -errno;
+ }
+
+ return 1;
+}
+
+static int write_to_syslog(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
+
+ char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
+ header_time[64],
+ header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
+ struct iovec iovec[5] = {};
+ struct msghdr msghdr = {
+ .msg_iov = iovec,
+ .msg_iovlen = ELEMENTSOF(iovec),
+ };
+ time_t t;
+ struct tm *tm;
+
+ if (syslog_fd < 0)
+ return 0;
+
+ xsprintf(header_priority, "<%i>", level);
+
+ t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
+ tm = localtime(&t);
+ if (!tm)
+ return -EINVAL;
+
+ if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+ return -EINVAL;
+
+ xsprintf(header_pid, "["PID_FMT"]: ", getpid());
+
+ IOVEC_SET_STRING(iovec[0], header_priority);
+ IOVEC_SET_STRING(iovec[1], header_time);
+ IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
+ IOVEC_SET_STRING(iovec[3], header_pid);
+ IOVEC_SET_STRING(iovec[4], buffer);
+
+ /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
+ if (syslog_is_stream)
+ iovec[4].iov_len++;
+
+ for (;;) {
+ ssize_t n;
+
+ n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
+ if (n < 0)
+ return -errno;
+
+ if (!syslog_is_stream ||
+ (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
+ break;
+
+ IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
+ }
+
+ return 1;
+}
+
+static int write_to_kmsg(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
+
+ char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
+ header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
+ struct iovec iovec[5] = {};
+
+ if (kmsg_fd < 0)
+ return 0;
+
+ xsprintf(header_priority, "<%i>", level);
+ xsprintf(header_pid, "["PID_FMT"]: ", getpid());
+
+ IOVEC_SET_STRING(iovec[0], header_priority);
+ IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
+ IOVEC_SET_STRING(iovec[2], header_pid);
+ IOVEC_SET_STRING(iovec[3], buffer);
+ IOVEC_SET_STRING(iovec[4], "\n");
+
+ if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static int log_do_header(
+ char *header,
+ size_t size,
+ int level,
+ int error,
+ const char *file, int line, const char *func,
+ const char *object_field, const char *object) {
+
+ snprintf(header, size,
+ "PRIORITY=%i\n"
+ "SYSLOG_FACILITY=%i\n"
+ "%s%s%s"
+ "%s%.*i%s"
+ "%s%s%s"
+ "%s%.*i%s"
+ "%s%s%s"
+ "SYSLOG_IDENTIFIER=%s\n",
+ LOG_PRI(level),
+ LOG_FAC(level),
+ isempty(file) ? "" : "CODE_FILE=",
+ isempty(file) ? "" : file,
+ isempty(file) ? "" : "\n",
+ line ? "CODE_LINE=" : "",
+ line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
+ line ? "\n" : "",
+ isempty(func) ? "" : "CODE_FUNCTION=",
+ isempty(func) ? "" : func,
+ isempty(func) ? "" : "\n",
+ error ? "ERRNO=" : "",
+ error ? 1 : 0, error,
+ error ? "\n" : "",
+ isempty(object) ? "" : object_field,
+ isempty(object) ? "" : object,
+ isempty(object) ? "" : "\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int write_to_journal(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
+
+ char header[LINE_MAX];
+ struct iovec iovec[4] = {};
+ struct msghdr mh = {};
+
+ if (journal_fd < 0)
+ return 0;
+
+ log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object);
+
+ IOVEC_SET_STRING(iovec[0], header);
+ IOVEC_SET_STRING(iovec[1], "MESSAGE=");
+ IOVEC_SET_STRING(iovec[2], buffer);
+ IOVEC_SET_STRING(iovec[3], "\n");
+
+ mh.msg_iov = iovec;
+ mh.msg_iovlen = ELEMENTSOF(iovec);
+
+ if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static int log_dispatch(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ char *buffer) {
+
+ assert(buffer);
+
+ if (log_target == LOG_TARGET_NULL)
+ return -error;
+
+ /* Patch in LOG_DAEMON facility if necessary */
+ if ((level & LOG_FACMASK) == 0)
+ level = log_facility | LOG_PRI(level);
+
+ if (error < 0)
+ error = -error;
+
+ do {
+ char *e;
+ int k = 0;
+
+ buffer += strspn(buffer, NEWLINE);
+
+ if (buffer[0] == 0)
+ break;
+
+ if ((e = strpbrk(buffer, NEWLINE)))
+ *(e++) = 0;
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) {
+
+ k = write_to_journal(level, error, file, line, func, object_field, object, buffer);
+ if (k < 0) {
+ if (k != -EAGAIN)
+ log_close_journal();
+ log_open_kmsg();
+ }
+ }
+
+ if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG) {
+
+ k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
+ if (k < 0) {
+ if (k != -EAGAIN)
+ log_close_syslog();
+ log_open_kmsg();
+ }
+ }
+
+ if (k <= 0 &&
+ (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SAFE ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_KMSG)) {
+
+ k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
+ if (k < 0) {
+ log_close_kmsg();
+ log_open_console();
+ }
+ }
+
+ if (k <= 0)
+ (void) write_to_console(level, error, file, line, func, object_field, object, buffer);
+
+ buffer = e;
+ } while (buffer);
+
+ return -error;
+}
+
+int log_dump_internal(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ char *buffer) {
+
+ PROTECT_ERRNO;
+
+ /* This modifies the buffer... */
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
+}
+
+int log_internalv(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format,
+ va_list ap) {
+
+ PROTECT_ERRNO;
+ char buffer[LINE_MAX];
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ /* Make sure that %m maps to the specified error */
+ if (error != 0)
+ errno = error;
+
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
+}
+
+int log_internal(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ va_list ap;
+ int r;
+
+ va_start(ap, format);
+ r = log_internalv(level, error, file, line, func, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int log_object_internalv(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *format,
+ va_list ap) {
+
+ PROTECT_ERRNO;
+ char *buffer, *b;
+ size_t l;
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ /* Make sure that %m maps to the specified error */
+ if (error != 0)
+ errno = error;
+
+ /* Prepend the object name before the message */
+ if (object) {
+ size_t n;
+
+ n = strlen(object);
+ l = n + 2 + LINE_MAX;
+
+ buffer = newa(char, l);
+ b = stpcpy(stpcpy(buffer, object), ": ");
+ } else {
+ l = LINE_MAX;
+ b = buffer = newa(char, l);
+ }
+
+ vsnprintf(b, l, format, ap);
+
+ return log_dispatch(level, error, file, line, func, object_field, object, buffer);
+}
+
+int log_object_internal(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *format, ...) {
+
+ va_list ap;
+ int r;
+
+ va_start(ap, format);
+ r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+static void log_assert(
+ int level,
+ const char *text,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format) {
+
+ static char buffer[LINE_MAX];
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return;
+
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ snprintf(buffer, sizeof(buffer), format, text, file, line, func);
+ REENABLE_WARNING;
+
+ log_abort_msg = buffer;
+
+ log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
+}
+
+noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
+ log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
+ abort();
+}
+
+noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) {
+ log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+ abort();
+}
+
+void log_assert_failed_return(const char *text, const char *file, int line, const char *func) {
+ PROTECT_ERRNO;
+ log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.");
+}
+
+int log_oom_internal(const char *file, int line, const char *func) {
+ log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory.");
+ return -ENOMEM;
+}
+
+int log_struct_internal(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ char buf[LINE_MAX];
+ bool found = false;
+ PROTECT_ERRNO;
+ va_list ap;
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ if (log_target == LOG_TARGET_NULL)
+ return -error;
+
+ if ((level & LOG_FACMASK) == 0)
+ level = log_facility | LOG_PRI(level);
+
+ if ((log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) &&
+ journal_fd >= 0) {
+ char header[LINE_MAX];
+ struct iovec iovec[17] = {};
+ unsigned n = 0, i;
+ struct msghdr mh = {
+ .msg_iov = iovec,
+ };
+ static const char nl = '\n';
+ bool fallback = false;
+
+ /* If the journal is available do structured logging */
+ log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL);
+ IOVEC_SET_STRING(iovec[n++], header);
+
+ va_start(ap, format);
+ while (format && n + 1 < ELEMENTSOF(iovec)) {
+ va_list aq;
+ char *m;
+
+ /* We need to copy the va_list structure,
+ * since vasprintf() leaves it afterwards at
+ * an undefined location */
+
+ if (error != 0)
+ errno = error;
+
+ va_copy(aq, ap);
+ if (vasprintf(&m, format, aq) < 0) {
+ va_end(aq);
+ fallback = true;
+ goto finish;
+ }
+ va_end(aq);
+
+ /* Now, jump enough ahead, so that we point to
+ * the next format string */
+ VA_FORMAT_ADVANCE(format, ap);
+
+ IOVEC_SET_STRING(iovec[n++], m);
+
+ iovec[n].iov_base = (char*) &nl;
+ iovec[n].iov_len = 1;
+ n++;
+
+ format = va_arg(ap, char *);
+ }
+
+ mh.msg_iovlen = n;
+
+ (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
+
+ finish:
+ va_end(ap);
+ for (i = 1; i < n; i += 2)
+ free(iovec[i].iov_base);
+
+ if (!fallback)
+ return -error;
+ }
+
+ /* Fallback if journal logging is not available or didn't work. */
+
+ va_start(ap, format);
+ while (format) {
+ va_list aq;
+
+ if (error != 0)
+ errno = error;
+
+ va_copy(aq, ap);
+ vsnprintf(buf, sizeof(buf), format, aq);
+ va_end(aq);
+
+ if (startswith(buf, "MESSAGE=")) {
+ found = true;
+ break;
+ }
+
+ VA_FORMAT_ADVANCE(format, ap);
+
+ format = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ if (!found)
+ return -error;
+
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8);
+}
+
+int log_set_target_from_string(const char *e) {
+ LogTarget t;
+
+ t = log_target_from_string(e);
+ if (t < 0)
+ return -EINVAL;
+
+ log_set_target(t);
+ return 0;
+}
+
+int log_set_max_level_from_string(const char *e) {
+ int t;
+
+ t = log_level_from_string(e);
+ if (t < 0)
+ return t;
+
+ log_set_max_level(t);
+ return 0;
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value) {
+
+ /*
+ * The systemd.log_xyz= settings are parsed by all tools, and
+ * so is "debug".
+ *
+ * However, "quiet" is only parsed by PID 1, and only turns of
+ * status output to /dev/console, but does not alter the log
+ * level.
+ */
+
+ if (streq(key, "debug") && !value)
+ log_set_max_level(LOG_DEBUG);
+
+ else if (streq(key, "systemd.log_target") && value) {
+
+ if (log_set_target_from_string(value) < 0)
+ log_warning("Failed to parse log target '%s'. Ignoring.", value);
+
+ } else if (streq(key, "systemd.log_level") && value) {
+
+ if (log_set_max_level_from_string(value) < 0)
+ log_warning("Failed to parse log level '%s'. Ignoring.", value);
+
+ } else if (streq(key, "systemd.log_color") && value) {
+
+ if (log_show_color_from_string(value) < 0)
+ log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
+
+ } else if (streq(key, "systemd.log_location") && value) {
+
+ if (log_show_location_from_string(value) < 0)
+ log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
+ }
+
+ return 0;
+}
+
+void log_parse_environment(void) {
+ const char *e;
+
+ if (get_ctty_devnr(0, NULL) < 0)
+ /* Only try to read the command line in daemons.
+ We assume that anything that has a controlling
+ tty is user stuff. */
+ (void) parse_proc_cmdline(parse_proc_cmdline_item);
+
+ e = secure_getenv("SYSTEMD_LOG_TARGET");
+ if (e && log_set_target_from_string(e) < 0)
+ log_warning("Failed to parse log target '%s'. Ignoring.", e);
+
+ e = secure_getenv("SYSTEMD_LOG_LEVEL");
+ if (e && log_set_max_level_from_string(e) < 0)
+ log_warning("Failed to parse log level '%s'. Ignoring.", e);
+
+ e = secure_getenv("SYSTEMD_LOG_COLOR");
+ if (e && log_show_color_from_string(e) < 0)
+ log_warning("Failed to parse bool '%s'. Ignoring.", e);
+
+ e = secure_getenv("SYSTEMD_LOG_LOCATION");
+ if (e && log_show_location_from_string(e) < 0)
+ log_warning("Failed to parse bool '%s'. Ignoring.", e);
+}
+
+LogTarget log_get_target(void) {
+ return log_target;
+}
+
+int log_get_max_level(void) {
+ return log_max_level;
+}
+
+void log_show_color(bool b) {
+ show_color = b;
+}
+
+bool log_get_show_color(void) {
+ return show_color;
+}
+
+void log_show_location(bool b) {
+ show_location = b;
+}
+
+bool log_get_show_location(void) {
+ return show_location;
+}
+
+int log_show_color_from_string(const char *e) {
+ int t;
+
+ t = parse_boolean(e);
+ if (t < 0)
+ return t;
+
+ log_show_color(t);
+ return 0;
+}
+
+int log_show_location_from_string(const char *e) {
+ int t;
+
+ t = parse_boolean(e);
+ if (t < 0)
+ return t;
+
+ log_show_location(t);
+ return 0;
+}
+
+bool log_on_console(void) {
+ if (log_target == LOG_TARGET_CONSOLE ||
+ log_target == LOG_TARGET_CONSOLE_PREFIXED)
+ return true;
+
+ return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
+}
+
+static const char *const log_target_table[_LOG_TARGET_MAX] = {
+ [LOG_TARGET_CONSOLE] = "console",
+ [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed",
+ [LOG_TARGET_KMSG] = "kmsg",
+ [LOG_TARGET_JOURNAL] = "journal",
+ [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
+ [LOG_TARGET_SYSLOG] = "syslog",
+ [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
+ [LOG_TARGET_AUTO] = "auto",
+ [LOG_TARGET_SAFE] = "safe",
+ [LOG_TARGET_NULL] = "null"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
+
+void log_received_signal(int level, const struct signalfd_siginfo *si) {
+ if (si->ssi_pid > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ get_process_comm(si->ssi_pid, &p);
+
+ log_full(level,
+ "Received SIG%s from PID %"PRIu32" (%s).",
+ signal_to_string(si->ssi_signo),
+ si->ssi_pid, strna(p));
+ } else
+ log_full(level,
+ "Received SIG%s.",
+ signal_to_string(si->ssi_signo));
+
+}
+
+void log_set_upgrade_syslog_to_journal(bool b) {
+ upgrade_syslog_to_journal = b;
+}
+
+int log_syntax_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ PROTECT_ERRNO;
+ char buffer[LINE_MAX];
+ int r;
+ va_list ap;
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ if (log_target == LOG_TARGET_NULL)
+ return -error;
+
+ if (error != 0)
+ errno = error;
+
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+
+ if (unit)
+ r = log_struct_internal(
+ level, error,
+ file, line, func,
+ getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
+ NULL);
+ else
+ r = log_struct_internal(
+ level, error,
+ file, line, func,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
+ NULL);
+
+ return r;
+}
diff --git a/src/basic/log.h b/src/basic/log.h
new file mode 100644
index 0000000..569762d
--- /dev/null
+++ b/src/basic/log.h
@@ -0,0 +1,229 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/signalfd.h>
+#include <errno.h>
+
+#include "sd-id128.h"
+#include "macro.h"
+
+typedef enum LogTarget{
+ LOG_TARGET_CONSOLE,
+ LOG_TARGET_CONSOLE_PREFIXED,
+ LOG_TARGET_KMSG,
+ LOG_TARGET_JOURNAL,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_SYSLOG,
+ LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
+ LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
+ LOG_TARGET_NULL,
+ _LOG_TARGET_MAX,
+ _LOG_TARGET_INVALID = -1
+} LogTarget;
+
+void log_set_target(LogTarget target);
+void log_set_max_level(int level);
+void log_set_facility(int facility);
+
+int log_set_target_from_string(const char *e);
+int log_set_max_level_from_string(const char *e);
+
+void log_show_color(bool b);
+bool log_get_show_color(void) _pure_;
+void log_show_location(bool b);
+bool log_get_show_location(void) _pure_;
+
+int log_show_color_from_string(const char *e);
+int log_show_location_from_string(const char *e);
+
+LogTarget log_get_target(void) _pure_;
+int log_get_max_level(void) _pure_;
+
+int log_open(void);
+void log_close(void);
+void log_forget_fds(void);
+
+void log_close_syslog(void);
+void log_close_journal(void);
+void log_close_kmsg(void);
+void log_close_console(void);
+
+void log_parse_environment(void);
+
+int log_internal(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) _printf_(6,7);
+
+int log_internalv(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format,
+ va_list ap) _printf_(6,0);
+
+int log_object_internal(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *format, ...) _printf_(8,9);
+
+int log_object_internalv(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *format,
+ va_list ap) _printf_(8,0);
+
+int log_struct_internal(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) _printf_(6,0) _sentinel_;
+
+int log_oom_internal(
+ const char *file,
+ int line,
+ const char *func);
+
+/* This modifies the buffer passed! */
+int log_dump_internal(
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ char *buffer);
+
+/* Logging for various assertions */
+noreturn void log_assert_failed(
+ const char *text,
+ const char *file,
+ int line,
+ const char *func);
+
+noreturn void log_assert_failed_unreachable(
+ const char *text,
+ const char *file,
+ int line,
+ const char *func);
+
+void log_assert_failed_return(
+ const char *text,
+ const char *file,
+ int line,
+ const char *func);
+
+/* Logging with level */
+#define log_full_errno(level, error, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
+ })
+
+#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
+
+/* Normal logging */
+#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
+#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
+#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
+#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
+#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
+#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
+
+/* Logging triggered by an errno-like error */
+#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
+#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__)
+#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
+#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
+#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
+
+#ifdef LOG_TRACE
+# define log_trace(...) log_debug(__VA_ARGS__)
+#else
+# define log_trace(...) do {} while(0)
+#endif
+
+/* Structured logging */
+#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/* This modifies the buffer passed! */
+#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer)
+
+#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
+
+bool log_on_console(void) _pure_;
+
+const char *log_target_to_string(LogTarget target) _const_;
+LogTarget log_target_from_string(const char *s) _pure_;
+
+/* Helpers to prepare various fields for structured logging */
+#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
+#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
+
+void log_received_signal(int level, const struct signalfd_siginfo *si);
+
+void log_set_upgrade_syslog_to_journal(bool b);
+
+int log_syntax_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) _printf_(9, 10);
+
+#define log_syntax(unit, level, config_file, config_line, error, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
+ })
diff --git a/src/basic/login-util.c b/src/basic/login-util.c
new file mode 100644
index 0000000..e25437f
--- /dev/null
+++ b/src/basic/login-util.c
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
+
+ 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 "login-util.h"
+#include "def.h"
+
+bool session_id_valid(const char *id) {
+
+ if (isempty(id))
+ return false;
+
+ return id[strspn(id, LETTERS DIGITS)] == '\0';
+}
diff --git a/src/basic/login-util.h b/src/basic/login-util.h
new file mode 100644
index 0000000..a79f20c
--- /dev/null
+++ b/src/basic/login-util.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
+
+ 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/>.
+***/
+
+#pragma once
+
+#include <stdbool.h>
+
+bool session_id_valid(const char *id);
diff --git a/src/basic/macro.h b/src/basic/macro.h
new file mode 100644
index 0000000..cc1c9e7
--- /dev/null
+++ b/src/basic/macro.h
@@ -0,0 +1,473 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+
+#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
+#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
+#define _sentinel_ __attribute__ ((sentinel))
+#define _unused_ __attribute__ ((unused))
+#define _destructor_ __attribute__ ((destructor))
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _deprecated_ __attribute__ ((deprecated))
+#define _packed_ __attribute__ ((packed))
+#define _malloc_ __attribute__ ((malloc))
+#define _weak_ __attribute__ ((weak))
+#define _likely_(x) (__builtin_expect(!!(x),1))
+#define _unlikely_(x) (__builtin_expect(!!(x),0))
+#define _public_ __attribute__ ((visibility("default")))
+#define _hidden_ __attribute__ ((visibility("hidden")))
+#define _weakref_(x) __attribute__((weakref(#x)))
+#define _alignas_(x) __attribute__((aligned(__alignof(x))))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+/* Temporarily disable some warnings */
+#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")
+
+#define DISABLE_WARNING_FORMAT_NONLITERAL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
+
+#define DISABLE_WARNING_MISSING_PROTOTYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+
+#define DISABLE_WARNING_NONNULL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wnonnull\"")
+
+#define DISABLE_WARNING_SHADOW \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wshadow\"")
+
+#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
+
+#define REENABLE_WARNING \
+ _Pragma("GCC diagnostic pop")
+
+/* automake test harness */
+#define EXIT_TEST_SKIP 77
+
+#define XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+#define XCONCATENATE(x, y) x ## y
+#define CONCATENATE(x, y) XCONCATENATE(x, y)
+
+#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
+#define UNIQ __COUNTER__
+
+/* Rounds up */
+
+#define ALIGN4(l) (((l) + 3) & ~3)
+#define ALIGN8(l) (((l) + 7) & ~7)
+
+#if __SIZEOF_POINTER__ == 8
+#define ALIGN(l) ALIGN8(l)
+#elif __SIZEOF_POINTER__ == 4
+#define ALIGN(l) ALIGN4(l)
+#else
+#error "Wut? Pointers are neither 4 nor 8 bytes long?"
+#endif
+
+#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p)))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+ return ((l + ali - 1) & ~(ali - 1));
+}
+
+#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
+
+/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
+static inline unsigned long ALIGN_POWER2(unsigned long u) {
+ /* clz(0) is undefined */
+ if (u == 1)
+ return 1;
+
+ /* left-shift overflow is undefined */
+ if (__builtin_clzl(u - 1UL) < 1)
+ return 0;
+
+ return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
+}
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+/*
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ */
+#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member)
+#define __container_of(uniq, ptr, type, member) \
+ __extension__ ({ \
+ const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \
+ (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \
+ })
+
+#undef MAX
+#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
+#define __MAX(aq, a, bq, b) \
+ __extension__ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
+ })
+
+/* evaluates to (void) if _A or _B are not constant or of different types */
+#define CONST_MAX(_A, _B) \
+ __extension__ (__builtin_choose_expr( \
+ __builtin_constant_p(_A) && \
+ __builtin_constant_p(_B) && \
+ __builtin_types_compatible_p(typeof(_A), typeof(_B)), \
+ ((_A) > (_B)) ? (_A) : (_B), \
+ (void)0))
+
+/* takes two types and returns the size of the larger one */
+#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
+
+#define MAX3(x,y,z) \
+ __extension__ ({ \
+ const typeof(x) _c = MAX(x,y); \
+ MAX(_c, z); \
+ })
+
+#undef MIN
+#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
+#define __MIN(aq, a, bq, b) \
+ __extension__ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
+ })
+
+#define MIN3(x,y,z) \
+ __extension__ ({ \
+ const typeof(x) _c = MIN(x,y); \
+ MIN(_c, z); \
+ })
+
+#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
+#define __LESS_BY(aq, a, bq, b) \
+ __extension__ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \
+ })
+
+#undef CLAMP
+#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
+#define __CLAMP(xq, x, lowq, low, highq, high) \
+ __extension__ ({ \
+ const typeof(x) UNIQ_T(X,xq) = (x); \
+ const typeof(low) UNIQ_T(LOW,lowq) = (low); \
+ const typeof(high) UNIQ_T(HIGH,highq) = (high); \
+ UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \
+ UNIQ_T(HIGH,highq) : \
+ UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \
+ UNIQ_T(LOW,lowq) : \
+ UNIQ_T(X,xq); \
+ })
+
+/* [(x + y - 1) / y] suffers from an integer overflow, even though the
+ * computation should be possible in the given type. Therefore, we use
+ * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
+ * quotient and the remainder, so both should be equally fast. */
+#define DIV_ROUND_UP(_x, _y) \
+ __extension__ ({ \
+ const typeof(_x) __x = (_x); \
+ const typeof(_y) __y = (_y); \
+ (__x / __y + !!(__x % __y)); \
+ })
+
+#define assert_se(expr) \
+ do { \
+ if (_unlikely_(!(expr))) \
+ log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false) \
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) do {} while(false)
+#else
+#define assert(expr) assert_se(expr)
+#endif
+
+#define assert_not_reached(t) \
+ do { \
+ log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false)
+
+#if defined(static_assert)
+/* static_assert() is sometimes defined in a way that trips up
+ * -Wdeclaration-after-statement, hence let's temporarily turn off
+ * this warning around it. */
+#define assert_cc(expr) \
+ DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
+ static_assert(expr, #expr); \
+ REENABLE_WARNING
+#else
+#define assert_cc(expr) \
+ DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
+ struct CONCATENATE(_assert_struct_, __COUNTER__) { \
+ char x[(expr) ? 0 : -1]; \
+ }; \
+ REENABLE_WARNING
+#endif
+
+#define assert_return(expr, r) \
+ do { \
+ if (_unlikely_(!(expr))) { \
+ log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ return (r); \
+ } \
+ } while (false)
+
+#define assert_return_errno(expr, r, err) \
+ do { \
+ if (_unlikely_(!(expr))) { \
+ log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ errno = err; \
+ return (r); \
+ } \
+ } while (false)
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
+#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
+#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p)))
+#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u)))
+#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
+#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
+#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+/* The following macros add 1 when converting things, since UID 0 is a
+ * valid UID, while the pointer NULL is special */
+#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
+#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
+#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+
+#define memzero(x,l) (memset((x), 0, (l)))
+#define zero(x) (memzero(&(x), sizeof(x)))
+
+#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
+
+#define char_array_0(x) x[sizeof(x)-1] = 0;
+
+#define IOVEC_SET_STRING(i, s) \
+ do { \
+ struct iovec *_i = &(i); \
+ char *_s = (char *)(s); \
+ _i->iov_base = _s; \
+ _i->iov_len = strlen(_s); \
+ } while(false)
+
+static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
+ unsigned j;
+ size_t r = 0;
+
+ for (j = 0; j < n; j++)
+ r += i[j].iov_len;
+
+ return r;
+}
+
+static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
+ unsigned j;
+
+ for (j = 0; j < n; j++) {
+ size_t sub;
+
+ if (_unlikely_(k <= 0))
+ break;
+
+ sub = MIN(i[j].iov_len, k);
+ i[j].iov_len -= sub;
+ i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
+ k -= sub;
+ }
+
+ return k;
+}
+
+#define VA_FORMAT_ADVANCE(format, ap) \
+do { \
+ int _argtypes[128]; \
+ size_t _i, _k; \
+ _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
+ assert(_k < ELEMENTSOF(_argtypes)); \
+ for (_i = 0; _i < _k; _i++) { \
+ if (_argtypes[_i] & PA_FLAG_PTR) { \
+ (void) va_arg(ap, void*); \
+ continue; \
+ } \
+ \
+ switch (_argtypes[_i]) { \
+ case PA_INT: \
+ case PA_INT|PA_FLAG_SHORT: \
+ case PA_CHAR: \
+ (void) va_arg(ap, int); \
+ break; \
+ case PA_INT|PA_FLAG_LONG: \
+ (void) va_arg(ap, long int); \
+ break; \
+ case PA_INT|PA_FLAG_LONG_LONG: \
+ (void) va_arg(ap, long long int); \
+ break; \
+ case PA_WCHAR: \
+ (void) va_arg(ap, wchar_t); \
+ break; \
+ case PA_WSTRING: \
+ case PA_STRING: \
+ case PA_POINTER: \
+ (void) va_arg(ap, void*); \
+ break; \
+ case PA_FLOAT: \
+ case PA_DOUBLE: \
+ (void) va_arg(ap, double); \
+ break; \
+ case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
+ (void) va_arg(ap, long double); \
+ break; \
+ default: \
+ assert_not_reached("Unknown format string argument."); \
+ } \
+ } \
+} while(false)
+
+ /* Because statfs.t_type can be int on some architectures, we have to cast
+ * the const magic to the type, otherwise the compiler warns about
+ * signed/unsigned comparison, because the magic can be 32 bit unsigned.
+ */
+#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
+
+/* Returns the number of chars needed to format variables of the
+ * specified type as a decimal string. Adds in extra space for a
+ * negative '-' prefix (hence works correctly on signed
+ * types). Includes space for the trailing NUL. */
+#define DECIMAL_STR_MAX(type) \
+ (2+(sizeof(type) <= 1 ? 3 : \
+ sizeof(type) <= 2 ? 5 : \
+ sizeof(type) <= 4 ? 10 : \
+ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+
+#define SET_FLAG(v, flag, b) \
+ (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
+
+#define IN_SET(x, y, ...) \
+ ({ \
+ const typeof(y) _y = (y); \
+ const typeof(_y) _x = (x); \
+ unsigned _i; \
+ bool _found = false; \
+ for (_i = 0; _i < 1 + sizeof((const typeof(_x)[]) { __VA_ARGS__ })/sizeof(const typeof(_x)); _i++) \
+ if (((const typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \
+ _found = true; \
+ break; \
+ } \
+ _found; \
+ })
+
+/* Return a nulstr for a standard cascade of configuration directories,
+ * suitable to pass to conf_files_list_nulstr or config_parse_many. */
+#define CONF_DIRS_NULSTR(n) \
+ "/etc/" n ".d\0" \
+ "/run/" n ".d\0" \
+ "/usr/local/lib/" n ".d\0" \
+ "/usr/lib/" n ".d\0" \
+ CONF_DIR_SPLIT_USR(n)
+
+#ifdef HAVE_SPLIT_USR
+#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0"
+#else
+#define CONF_DIR_SPLIT_USR(n)
+#endif
+
+/* Define C11 thread_local attribute even on older gcc compiler
+ * version */
+#ifndef thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
+#define thread_local _Thread_local
+#else
+#define thread_local __thread
+#endif
+#endif
+
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef noreturn
+#if __STDC_VERSION__ >= 201112L
+#define noreturn _Noreturn
+#else
+#define noreturn __attribute__((noreturn))
+#endif
+#endif
+
+#define UID_INVALID ((uid_t) -1)
+#define GID_INVALID ((gid_t) -1)
+#define MODE_INVALID ((mode_t) -1)
+
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
+ static inline void func##p(type *p) { \
+ if (*p) \
+ func(*p); \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define CMSG_FOREACH(cmsg, mh) \
+ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
+
+#include "log.h"
diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c
new file mode 100644
index 0000000..e99a738
--- /dev/null
+++ b/src/basic/memfd-util.c
@@ -0,0 +1,171 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+
+#ifdef HAVE_LINUX_MEMFD_H
+# include <linux/memfd.h>
+#endif
+
+#include "util.h"
+#include "memfd-util.h"
+#include "utf8.h"
+#include "missing.h"
+
+int memfd_new(const char *name) {
+ _cleanup_free_ char *g = NULL;
+ int fd;
+
+ if (!name) {
+ char pr[17] = {};
+
+ /* If no name is specified we generate one. We include
+ * a hint indicating our library implementation, and
+ * add the thread name to it */
+
+ assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
+
+ if (isempty(pr))
+ name = "sd";
+ else {
+ _cleanup_free_ char *e = NULL;
+
+ e = utf8_escape_invalid(pr);
+ if (!e)
+ return -ENOMEM;
+
+ g = strappend("sd-", e);
+ if (!g)
+ return -ENOMEM;
+
+ name = g;
+ }
+ }
+
+ fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int memfd_map(int fd, uint64_t offset, size_t size, void **p) {
+ void *q;
+ int sealed;
+
+ assert(fd >= 0);
+ assert(size > 0);
+ assert(p);
+
+ sealed = memfd_get_sealed(fd);
+ if (sealed < 0)
+ return sealed;
+
+ if (sealed)
+ q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset);
+ else
+ q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
+
+ if (q == MAP_FAILED)
+ return -errno;
+
+ *p = q;
+ return 0;
+}
+
+int memfd_set_sealed(int fd) {
+ int r;
+
+ assert(fd >= 0);
+
+ r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int memfd_get_sealed(int fd) {
+ int r;
+
+ assert(fd >= 0);
+
+ r = fcntl(fd, F_GET_SEALS);
+ if (r < 0)
+ return -errno;
+
+ return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
+}
+
+int memfd_get_size(int fd, uint64_t *sz) {
+ struct stat stat;
+ int r;
+
+ assert(fd >= 0);
+ assert(sz);
+
+ r = fstat(fd, &stat);
+ if (r < 0)
+ return -errno;
+
+ *sz = stat.st_size;
+ return 0;
+}
+
+int memfd_set_size(int fd, uint64_t sz) {
+ int r;
+
+ assert(fd >= 0);
+
+ r = ftruncate(fd, sz);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int memfd_new_and_map(const char *name, size_t sz, void **p) {
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(sz > 0);
+ assert(p);
+
+ fd = memfd_new(name);
+ if (fd < 0)
+ return fd;
+
+ r = memfd_set_size(fd, sz);
+ if (r < 0)
+ return r;
+
+ r = memfd_map(fd, 0, sz, p);
+ if (r < 0)
+ return r;
+
+ r = fd;
+ fd = -1;
+
+ return r;
+}
diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h
new file mode 100644
index 0000000..3ed551f
--- /dev/null
+++ b/src/basic/memfd-util.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/>.
+***/
+
+
+
+int memfd_new(const char *name);
+int memfd_new_and_map(const char *name, size_t sz, void **p);
+
+int memfd_map(int fd, uint64_t offset, size_t size, void **p);
+
+int memfd_set_sealed(int fd);
+int memfd_get_sealed(int fd);
+
+int memfd_get_size(int fd, uint64_t *sz);
+int memfd_set_size(int fd, uint64_t sz);
diff --git a/src/basic/mempool.c b/src/basic/mempool.c
new file mode 100644
index 0000000..d5d98d8
--- /dev/null
+++ b/src/basic/mempool.c
@@ -0,0 +1,103 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2014 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ 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 "mempool.h"
+#include "macro.h"
+#include "util.h"
+
+struct pool {
+ struct pool *next;
+ unsigned n_tiles;
+ unsigned n_used;
+};
+
+void* mempool_alloc_tile(struct mempool *mp) {
+ unsigned i;
+
+ /* When a tile is released we add it to the list and simply
+ * place the next pointer at its offset 0. */
+
+ assert(mp->tile_size >= sizeof(void*));
+ assert(mp->at_least > 0);
+
+ if (mp->freelist) {
+ void *r;
+
+ r = mp->freelist;
+ mp->freelist = * (void**) mp->freelist;
+ return r;
+ }
+
+ if (_unlikely_(!mp->first_pool) ||
+ _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
+ unsigned n;
+ size_t size;
+ struct pool *p;
+
+ n = mp->first_pool ? mp->first_pool->n_tiles : 0;
+ n = MAX(mp->at_least, n * 2);
+ size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
+ n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
+
+ p = malloc(size);
+ if (!p)
+ return NULL;
+
+ p->next = mp->first_pool;
+ p->n_tiles = n;
+ p->n_used = 0;
+
+ mp->first_pool = p;
+ }
+
+ i = mp->first_pool->n_used++;
+
+ return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
+}
+
+void* mempool_alloc0_tile(struct mempool *mp) {
+ void *p;
+
+ p = mempool_alloc_tile(mp);
+ if (p)
+ memzero(p, mp->tile_size);
+ return p;
+}
+
+void mempool_free_tile(struct mempool *mp, void *p) {
+ * (void**) p = mp->freelist;
+ mp->freelist = p;
+}
+
+#ifdef VALGRIND
+
+void mempool_drop(struct mempool *mp) {
+ struct pool *p = mp->first_pool;
+ while (p) {
+ struct pool *n;
+ n = p->next;
+ free(p);
+ p = n;
+ }
+}
+
+#endif
diff --git a/src/basic/mempool.h b/src/basic/mempool.h
new file mode 100644
index 0000000..42f473b
--- /dev/null
+++ b/src/basic/mempool.h
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011-2014 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ 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 <stddef.h>
+
+struct pool;
+
+struct mempool {
+ struct pool *first_pool;
+ void *freelist;
+ size_t tile_size;
+ unsigned at_least;
+};
+
+void* mempool_alloc_tile(struct mempool *mp);
+void* mempool_alloc0_tile(struct mempool *mp);
+void mempool_free_tile(struct mempool *mp, void *p);
+
+#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
+struct mempool pool_name = { \
+ .tile_size = sizeof(tile_type), \
+ .at_least = alloc_at_least, \
+}
+
+
+#ifdef VALGRIND
+void mempool_drop(struct mempool *mp);
+#endif
diff --git a/src/basic/missing.h b/src/basic/missing.h
new file mode 100644
index 0000000..be7f618
--- /dev/null
+++ b/src/basic/missing.h
@@ -0,0 +1,1001 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/>.
+***/
+
+/* Missing glibc definitions to access certain kernel APIs */
+
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/oom.h>
+#include <linux/input.h>
+#include <linux/if_link.h>
+#include <linux/loop.h>
+#include <linux/audit.h>
+#include <linux/capability.h>
+#include <linux/neighbour.h>
+
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+
+#ifdef ARCH_MIPS
+#include <asm/sgidefs.h>
+#endif
+
+#ifdef HAVE_LINUX_BTRFS_H
+#include <linux/btrfs.h>
+#endif
+
+#include "macro.h"
+
+#ifndef RLIMIT_RTTIME
+#define RLIMIT_RTTIME 15
+#endif
+
+/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */
+#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS)
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
+#endif
+
+#ifndef F_GETPIPE_SZ
+#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
+#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
+#define F_SEAL_GROW 0x0004 /* prevent file from growing */
+#define F_SEAL_WRITE 0x0008 /* prevent writes */
+#endif
+
+#ifndef F_OFD_GETLK
+#define F_OFD_GETLK 36
+#define F_OFD_SETLK 37
+#define F_OFD_SETLKW 38
+#endif
+
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
+
+#ifndef IP_FREEBIND
+#define IP_FREEBIND 15
+#endif
+
+#ifndef OOM_SCORE_ADJ_MIN
+#define OOM_SCORE_ADJ_MIN (-1000)
+#endif
+
+#ifndef OOM_SCORE_ADJ_MAX
+#define OOM_SCORE_ADJ_MAX 1000
+#endif
+
+#ifndef AUDIT_SERVICE_START
+#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */
+#endif
+
+#ifndef AUDIT_SERVICE_STOP
+#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
+#endif
+
+#ifndef TIOCVHANGUP
+#define TIOCVHANGUP 0x5437
+#endif
+
+#ifndef IP_TRANSPARENT
+#define IP_TRANSPARENT 19
+#endif
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#if !HAVE_DECL_PIVOT_ROOT
+static inline int pivot_root(const char *new_root, const char *put_old) {
+ return syscall(SYS_pivot_root, new_root, put_old);
+}
+#endif
+
+#ifndef __NR_memfd_create
+# if defined __x86_64__
+# define __NR_memfd_create 319
+# elif defined __arm__
+# define __NR_memfd_create 385
+# elif defined __aarch64__
+# define __NR_memfd_create 279
+# elif defined _MIPS_SIM
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define __NR_memfd_create 4354
+# endif
+# if _MIPS_SIM == _MIPS_SIM_NABI32
+# define __NR_memfd_create 6318
+# endif
+# if _MIPS_SIM == _MIPS_SIM_ABI64
+# define __NR_memfd_create 5314
+# endif
+# elif defined __i386__
+# define __NR_memfd_create 356
+# else
+# warning "__NR_memfd_create unknown for your architecture"
+# define __NR_memfd_create 0xffffffff
+# endif
+#endif
+
+#ifndef HAVE_MEMFD_CREATE
+static inline int memfd_create(const char *name, unsigned int flags) {
+ return syscall(__NR_memfd_create, name, flags);
+}
+#endif
+
+#ifndef __NR_getrandom
+# if defined __x86_64__
+# define __NR_getrandom 318
+# elif defined(__i386__)
+# define __NR_getrandom 355
+# elif defined(__arm__)
+# define __NR_getrandom 384
+# elif defined(__aarch64__)
+# define __NR_getrandom 278
+# elif defined(__ia64__)
+# define __NR_getrandom 1339
+# elif defined(__m68k__)
+# define __NR_getrandom 352
+# elif defined(__s390x__)
+# define __NR_getrandom 349
+# elif defined(__powerpc__)
+# define __NR_getrandom 359
+# elif defined _MIPS_SIM
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define __NR_getrandom 4353
+# endif
+# if _MIPS_SIM == _MIPS_SIM_NABI32
+# define __NR_getrandom 6317
+# endif
+# if _MIPS_SIM == _MIPS_SIM_ABI64
+# define __NR_getrandom 5313
+# endif
+# else
+# warning "__NR_getrandom unknown for your architecture"
+# define __NR_getrandom 0xffffffff
+# endif
+#endif
+
+#if !HAVE_DECL_GETRANDOM
+static inline int getrandom(void *buffer, size_t count, unsigned flags) {
+ return syscall(__NR_getrandom, buffer, count, flags);
+}
+#endif
+
+#ifndef GRND_NONBLOCK
+#define GRND_NONBLOCK 0x0001
+#endif
+
+#ifndef GRND_RANDOM
+#define GRND_RANDOM 0x0002
+#endif
+
+#ifndef BTRFS_IOCTL_MAGIC
+#define BTRFS_IOCTL_MAGIC 0x94
+#endif
+
+#ifndef BTRFS_PATH_NAME_MAX
+#define BTRFS_PATH_NAME_MAX 4087
+#endif
+
+#ifndef BTRFS_DEVICE_PATH_NAME_MAX
+#define BTRFS_DEVICE_PATH_NAME_MAX 1024
+#endif
+
+#ifndef BTRFS_FSID_SIZE
+#define BTRFS_FSID_SIZE 16
+#endif
+
+#ifndef BTRFS_UUID_SIZE
+#define BTRFS_UUID_SIZE 16
+#endif
+
+#ifndef BTRFS_SUBVOL_RDONLY
+#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+#endif
+
+#ifndef BTRFS_SUBVOL_NAME_MAX
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#endif
+
+#ifndef BTRFS_INO_LOOKUP_PATH_MAX
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
+#endif
+
+#ifndef BTRFS_SEARCH_ARGS_BUFSIZE
+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
+#endif
+
+#ifndef HAVE_LINUX_BTRFS_H
+struct btrfs_ioctl_vol_args {
+ int64_t fd;
+ char name[BTRFS_PATH_NAME_MAX + 1];
+};
+
+struct btrfs_qgroup_limit {
+ __u64 flags;
+ __u64 max_rfer;
+ __u64 max_excl;
+ __u64 rsv_rfer;
+ __u64 rsv_excl;
+};
+
+struct btrfs_qgroup_inherit {
+ __u64 flags;
+ __u64 num_qgroups;
+ __u64 num_ref_copies;
+ __u64 num_excl_copies;
+ struct btrfs_qgroup_limit lim;
+ __u64 qgroups[0];
+};
+
+struct btrfs_ioctl_qgroup_limit_args {
+ __u64 qgroupid;
+ struct btrfs_qgroup_limit lim;
+};
+
+struct btrfs_ioctl_vol_args_v2 {
+ __s64 fd;
+ __u64 transid;
+ __u64 flags;
+ union {
+ struct {
+ __u64 size;
+ struct btrfs_qgroup_inherit *qgroup_inherit;
+ };
+ __u64 unused[4];
+ };
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+};
+
+struct btrfs_ioctl_dev_info_args {
+ uint64_t devid; /* in/out */
+ uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */
+ uint64_t bytes_used; /* out */
+ uint64_t total_bytes; /* out */
+ uint64_t unused[379]; /* pad to 4k */
+ char path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
+};
+
+struct btrfs_ioctl_fs_info_args {
+ uint64_t max_id; /* out */
+ uint64_t num_devices; /* out */
+ uint8_t fsid[BTRFS_FSID_SIZE]; /* out */
+ uint64_t reserved[124]; /* pad to 1k */
+};
+
+struct btrfs_ioctl_ino_lookup_args {
+ __u64 treeid;
+ __u64 objectid;
+ char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
+struct btrfs_ioctl_search_key {
+ /* which root are we searching. 0 is the tree of tree roots */
+ __u64 tree_id;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_objectid;
+ __u64 max_objectid;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_offset;
+ __u64 max_offset;
+
+ /* max and min transids to search for */
+ __u64 min_transid;
+ __u64 max_transid;
+
+ /* keys returned will be >= min and <= max */
+ __u32 min_type;
+ __u32 max_type;
+
+ /*
+ * how many items did userland ask for, and how many are we
+ * returning
+ */
+ __u32 nr_items;
+
+ /* align to 64 bits */
+ __u32 unused;
+
+ /* some extra for later */
+ __u64 unused1;
+ __u64 unused2;
+ __u64 unused3;
+ __u64 unused4;
+};
+
+struct btrfs_ioctl_search_header {
+ __u64 transid;
+ __u64 objectid;
+ __u64 offset;
+ __u32 type;
+ __u32 len;
+};
+
+
+struct btrfs_ioctl_search_args {
+ struct btrfs_ioctl_search_key key;
+ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
+};
+
+struct btrfs_ioctl_clone_range_args {
+ __s64 src_fd;
+ __u64 src_offset, src_length;
+ __u64 dest_offset;
+};
+
+#define BTRFS_QUOTA_CTL_ENABLE 1
+#define BTRFS_QUOTA_CTL_DISABLE 2
+#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
+struct btrfs_ioctl_quota_ctl_args {
+ __u64 cmd;
+ __u64 status;
+};
+#endif
+
+#ifndef BTRFS_IOC_DEFRAG
+#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_RESIZE
+#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_CLONE
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+#endif
+
+#ifndef BTRFS_IOC_CLONE_RANGE
+#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
+ struct btrfs_ioctl_clone_range_args)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_CREATE
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_SNAP_DESTROY
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_TREE_SEARCH
+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args)
+#endif
+
+#ifndef BTRFS_IOC_INO_LOOKUP
+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
+ struct btrfs_ioctl_ino_lookup_args)
+#endif
+
+#ifndef BTRFS_IOC_SNAP_CREATE_V2
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
+ struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_GETFLAGS
+#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_SETFLAGS
+#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
+#endif
+
+#ifndef BTRFS_IOC_DEV_INFO
+#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
+ struct btrfs_ioctl_dev_info_args)
+#endif
+
+#ifndef BTRFS_IOC_FS_INFO
+#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
+ struct btrfs_ioctl_fs_info_args)
+#endif
+
+#ifndef BTRFS_IOC_DEVICES_READY
+#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_QUOTA_CTL
+#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
+ struct btrfs_ioctl_quota_ctl_args)
+#endif
+
+#ifndef BTRFS_IOC_QGROUP_LIMIT
+#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
+ struct btrfs_ioctl_qgroup_limit_args)
+#endif
+
+#ifndef BTRFS_FIRST_FREE_OBJECTID
+#define BTRFS_FIRST_FREE_OBJECTID 256
+#endif
+
+#ifndef BTRFS_LAST_FREE_OBJECTID
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#endif
+
+#ifndef BTRFS_ROOT_TREE_OBJECTID
+#define BTRFS_ROOT_TREE_OBJECTID 1
+#endif
+
+#ifndef BTRFS_QUOTA_TREE_OBJECTID
+#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+#endif
+
+#ifndef BTRFS_ROOT_ITEM_KEY
+#define BTRFS_ROOT_ITEM_KEY 132
+#endif
+
+#ifndef BTRFS_QGROUP_STATUS_KEY
+#define BTRFS_QGROUP_STATUS_KEY 240
+#endif
+
+#ifndef BTRFS_QGROUP_INFO_KEY
+#define BTRFS_QGROUP_INFO_KEY 242
+#endif
+
+#ifndef BTRFS_QGROUP_LIMIT_KEY
+#define BTRFS_QGROUP_LIMIT_KEY 244
+#endif
+
+#ifndef BTRFS_ROOT_BACKREF_KEY
+#define BTRFS_ROOT_BACKREF_KEY 144
+#endif
+
+#ifndef BTRFS_SUPER_MAGIC
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#endif
+
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
+#ifndef MS_PRIVATE
+#define MS_PRIVATE (1 << 18)
+#endif
+
+#if !HAVE_DECL_GETTID
+static inline pid_t gettid(void) {
+ return (pid_t) syscall(SYS_gettid);
+}
+#endif
+
+#ifndef SCM_SECURITY
+#define SCM_SECURITY 0x03
+#endif
+
+#ifndef MS_STRICTATIME
+#define MS_STRICTATIME (1<<24)
+#endif
+
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20)
+#endif
+
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#endif
+
+#ifndef PR_SET_CHILD_SUBREAPER
+#define PR_SET_CHILD_SUBREAPER 36
+#endif
+
+#ifndef MAX_HANDLE_SZ
+#define MAX_HANDLE_SZ 128
+#endif
+
+#ifndef __NR_name_to_handle_at
+# if defined(__x86_64__)
+# define __NR_name_to_handle_at 303
+# elif defined(__i386__)
+# define __NR_name_to_handle_at 341
+# elif defined(__arm__)
+# define __NR_name_to_handle_at 370
+# elif defined(__powerpc__)
+# define __NR_name_to_handle_at 345
+# else
+# error "__NR_name_to_handle_at is not defined"
+# endif
+#endif
+
+#if !HAVE_DECL_NAME_TO_HANDLE_AT
+struct file_handle {
+ unsigned int handle_bytes;
+ int handle_type;
+ unsigned char f_handle[0];
+};
+
+static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
+ return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
+}
+#endif
+
+#ifndef HAVE_SECURE_GETENV
+# ifdef HAVE___SECURE_GETENV
+# define secure_getenv __secure_getenv
+# else
+# error "neither secure_getenv nor __secure_getenv are available"
+# endif
+#endif
+
+#ifndef CIFS_MAGIC_NUMBER
+# define CIFS_MAGIC_NUMBER 0xFF534D42
+#endif
+
+#ifndef TFD_TIMER_CANCEL_ON_SET
+# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#endif
+
+#ifndef SO_REUSEPORT
+# define SO_REUSEPORT 15
+#endif
+
+#ifndef EVIOCREVOKE
+# define EVIOCREVOKE _IOW('E', 0x91, int)
+#endif
+
+#ifndef DRM_IOCTL_SET_MASTER
+# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
+#endif
+
+#ifndef DRM_IOCTL_DROP_MASTER
+# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/* The precise definition of __O_TMPFILE is arch specific, so let's
+ * just define this on x86 where we know the value. */
+
+#ifndef __O_TMPFILE
+#define __O_TMPFILE 020000000
+#endif
+
+/* a horrid kludge trying to make sure that this will fail on old kernels */
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#endif
+
+#endif
+
+#ifndef __NR_setns
+# if defined(__x86_64__)
+# define __NR_setns 308
+# elif defined(__i386__)
+# define __NR_setns 346
+# else
+# error "__NR_setns is not defined"
+# endif
+#endif
+
+#if !HAVE_DECL_SETNS
+static inline int setns(int fd, int nstype) {
+ return syscall(__NR_setns, fd, nstype);
+}
+#endif
+
+#if !HAVE_DECL_LO_FLAGS_PARTSCAN
+#define LO_FLAGS_PARTSCAN 8
+#endif
+
+#ifndef LOOP_CTL_REMOVE
+#define LOOP_CTL_REMOVE 0x4C81
+#endif
+
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
+#if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE
+#define IFLA_INET6_UNSPEC 0
+#define IFLA_INET6_FLAGS 1
+#define IFLA_INET6_CONF 2
+#define IFLA_INET6_STATS 3
+#define IFLA_INET6_MCAST 4
+#define IFLA_INET6_CACHEINFO 5
+#define IFLA_INET6_ICMP6STATS 6
+#define IFLA_INET6_TOKEN 7
+#define IFLA_INET6_ADDR_GEN_MODE 8
+#define __IFLA_INET6_MAX 9
+
+#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
+
+#define IN6_ADDR_GEN_MODE_EUI64 0
+#define IN6_ADDR_GEN_MODE_NONE 1
+#endif
+
+#if !HAVE_DECL_IFLA_MACVLAN_FLAGS
+#define IFLA_MACVLAN_UNSPEC 0
+#define IFLA_MACVLAN_MODE 1
+#define IFLA_MACVLAN_FLAGS 2
+#define __IFLA_MACVLAN_MAX 3
+
+#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_IPVLAN_MODE
+#define IFLA_IPVLAN_UNSPEC 0
+#define IFLA_IPVLAN_MODE 1
+#define __IFLA_IPVLAN_MAX 2
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+#define IPVLAN_MODE_L2 0
+#define IPVLAN_MODE_L3 1
+#define IPVLAN_MAX 2
+#endif
+
+#if !HAVE_DECL_IFLA_VTI_REMOTE
+#define IFLA_VTI_UNSPEC 0
+#define IFLA_VTI_LINK 1
+#define IFLA_VTI_IKEY 2
+#define IFLA_VTI_OKEY 3
+#define IFLA_VTI_LOCAL 4
+#define IFLA_VTI_REMOTE 5
+#define __IFLA_VTI_MAX 6
+
+#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_PHYS_PORT_ID
+#undef IFLA_PROMISCUITY
+#define IFLA_PROMISCUITY 30
+#define IFLA_NUM_TX_QUEUES 31
+#define IFLA_NUM_RX_QUEUES 32
+#define IFLA_CARRIER 33
+#define IFLA_PHYS_PORT_ID 34
+#define __IFLA_MAX 35
+
+#define IFLA_MAX (__IFLA_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_BOND_AD_INFO
+#define IFLA_BOND_UNSPEC 0
+#define IFLA_BOND_MODE 1
+#define IFLA_BOND_ACTIVE_SLAVE 2
+#define IFLA_BOND_MIIMON 3
+#define IFLA_BOND_UPDELAY 4
+#define IFLA_BOND_DOWNDELAY 5
+#define IFLA_BOND_USE_CARRIER 6
+#define IFLA_BOND_ARP_INTERVAL 7
+#define IFLA_BOND_ARP_IP_TARGET 8
+#define IFLA_BOND_ARP_VALIDATE 9
+#define IFLA_BOND_ARP_ALL_TARGETS 10
+#define IFLA_BOND_PRIMARY 11
+#define IFLA_BOND_PRIMARY_RESELECT 12
+#define IFLA_BOND_FAIL_OVER_MAC 13
+#define IFLA_BOND_XMIT_HASH_POLICY 14
+#define IFLA_BOND_RESEND_IGMP 15
+#define IFLA_BOND_NUM_PEER_NOTIF 16
+#define IFLA_BOND_ALL_SLAVES_ACTIVE 17
+#define IFLA_BOND_MIN_LINKS 18
+#define IFLA_BOND_LP_INTERVAL 19
+#define IFLA_BOND_PACKETS_PER_SLAVE 20
+#define IFLA_BOND_AD_LACP_RATE 21
+#define IFLA_BOND_AD_SELECT 22
+#define IFLA_BOND_AD_INFO 23
+#define __IFLA_BOND_MAX 24
+
+#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_VLAN_PROTOCOL
+#define IFLA_VLAN_UNSPEC 0
+#define IFLA_VLAN_ID 1
+#define IFLA_VLAN_FLAGS 2
+#define IFLA_VLAN_EGRESS_QOS 3
+#define IFLA_VLAN_INGRESS_QOS 4
+#define IFLA_VLAN_PROTOCOL 5
+#define __IFLA_VLAN_MAX 6
+
+#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL
+#define IFLA_VXLAN_UNSPEC 0
+#define IFLA_VXLAN_ID 1
+#define IFLA_VXLAN_GROUP 2
+#define IFLA_VXLAN_LINK 3
+#define IFLA_VXLAN_LOCAL 4
+#define IFLA_VXLAN_TTL 5
+#define IFLA_VXLAN_TOS 6
+#define IFLA_VXLAN_LEARNING 7
+#define IFLA_VXLAN_AGEING 8
+#define IFLA_VXLAN_LIMIT 9
+#define IFLA_VXLAN_PORT_RANGE 10
+#define IFLA_VXLAN_PROXY 11
+#define IFLA_VXLAN_RSC 12
+#define IFLA_VXLAN_L2MISS 13
+#define IFLA_VXLAN_L3MISS 14
+#define IFLA_VXLAN_PORT 15
+#define IFLA_VXLAN_GROUP6 16
+#define IFLA_VXLAN_LOCAL6 17
+#define IFLA_VXLAN_UDP_CSUM 18
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
+#define IFLA_VXLAN_REMCSUM_TX 21
+#define IFLA_VXLAN_REMCSUM_RX 22
+#define IFLA_VXLAN_GBP 23
+#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
+#define __IFLA_VXLAN_MAX 25
+
+#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_IPTUN_6RD_RELAY_PREFIXLEN
+#define IFLA_IPTUN_UNSPEC 0
+#define IFLA_IPTUN_LINK 1
+#define IFLA_IPTUN_LOCAL 2
+#define IFLA_IPTUN_REMOTE 3
+#define IFLA_IPTUN_TTL 4
+#define IFLA_IPTUN_TOS 5
+#define IFLA_IPTUN_ENCAP_LIMIT 6
+#define IFLA_IPTUN_FLOWINFO 7
+#define IFLA_IPTUN_FLAGS 8
+#define IFLA_IPTUN_PROTO 9
+#define IFLA_IPTUN_PMTUDISC 10
+#define IFLA_IPTUN_6RD_PREFIX 11
+#define IFLA_IPTUN_6RD_RELAY_PREFIX 12
+#define IFLA_IPTUN_6RD_PREFIXLEN 13
+#define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14
+#define __IFLA_IPTUN_MAX 15
+
+#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO
+#define IFLA_BRIDGE_FLAGS 0
+#define IFLA_BRIDGE_MODE 1
+#define IFLA_BRIDGE_VLAN_INFO 2
+#define __IFLA_BRIDGE_MAX 3
+
+#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
+#endif
+
+#if !HAVE_DECL_IFLA_BRPORT_UNICAST_FLOOD
+#define IFLA_BRPORT_UNSPEC 0
+#define IFLA_BRPORT_STATE 1
+#define IFLA_BRPORT_PRIORITY 2
+#define IFLA_BRPORT_COST 3
+#define IFLA_BRPORT_MODE 4
+#define IFLA_BRPORT_GUARD 5
+#define IFLA_BRPORT_PROTECT 6
+#define IFLA_BRPORT_FAST_LEAVE 7
+#define IFLA_BRPORT_LEARNING 8
+#define IFLA_BRPORT_UNICAST_FLOOD 9
+#define __IFLA_BRPORT_MAX 10
+
+#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+#endif
+
+#if !HAVE_DECL_NDA_IFINDEX
+#define NDA_UNSPEC 0
+#define NDA_DST 1
+#define NDA_LLADDR 2
+#define NDA_CACHEINFO 3
+#define NDA_PROBES 4
+#define NDA_VLAN 5
+#define NDA_PORT 6
+#define NDA_VNI 7
+#define NDA_IFINDEX 8
+#define __NDA_MAX 9
+
+#define NDA_MAX (__NDA_MAX - 1)
+#endif
+
+#ifndef IPV6_UNICAST_IF
+#define IPV6_UNICAST_IF 76
+#endif
+
+#ifndef IFF_MULTI_QUEUE
+#define IFF_MULTI_QUEUE 0x100
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000
+#endif
+
+#ifndef IFF_DORMANT
+#define IFF_DORMANT 0x20000
+#endif
+
+#ifndef BOND_XMIT_POLICY_ENCAP23
+#define BOND_XMIT_POLICY_ENCAP23 3
+#endif
+
+#ifndef BOND_XMIT_POLICY_ENCAP34
+#define BOND_XMIT_POLICY_ENCAP34 4
+#endif
+
+#ifndef NET_ADDR_RANDOM
+# define NET_ADDR_RANDOM 1
+#endif
+
+#ifndef NET_NAME_UNKNOWN
+# define NET_NAME_UNKNOWN 0
+#endif
+
+#ifndef NET_NAME_ENUM
+# define NET_NAME_ENUM 1
+#endif
+
+#ifndef NET_NAME_PREDICTABLE
+# define NET_NAME_PREDICTABLE 2
+#endif
+
+#ifndef NET_NAME_USER
+# define NET_NAME_USER 3
+#endif
+
+#ifndef NET_NAME_RENAMED
+# define NET_NAME_RENAMED 4
+#endif
+
+#ifndef BPF_XOR
+# define BPF_XOR 0xa0
+#endif
+
+/* Note that LOOPBACK_IFINDEX is currently not exported by the
+ * kernel/glibc, but hardcoded internally by the kernel. However, as
+ * it is exported to userspace indirectly via rtnetlink and the
+ * ioctls, and made use of widely we define it here too, in a way that
+ * is compatible with the kernel's internal definition. */
+#ifndef LOOPBACK_IFINDEX
+#define LOOPBACK_IFINDEX 1
+#endif
+
+#if !HAVE_DECL_IFA_FLAGS
+#define IFA_FLAGS 8
+#endif
+
+#ifndef IFA_F_NOPREFIXROUTE
+#define IFA_F_NOPREFIXROUTE 0x200
+#endif
+
+#ifndef MAX_AUDIT_MESSAGE_LENGTH
+#define MAX_AUDIT_MESSAGE_LENGTH 8970
+#endif
+
+#ifndef AUDIT_NLGRP_MAX
+#define AUDIT_NLGRP_READLOG 1
+#endif
+
+#ifndef CAP_MAC_OVERRIDE
+#define CAP_MAC_OVERRIDE 32
+#endif
+
+#ifndef CAP_MAC_ADMIN
+#define CAP_MAC_ADMIN 33
+#endif
+
+#ifndef CAP_SYSLOG
+#define CAP_SYSLOG 34
+#endif
+
+#ifndef CAP_WAKE_ALARM
+#define CAP_WAKE_ALARM 35
+#endif
+
+#ifndef CAP_BLOCK_SUSPEND
+#define CAP_BLOCK_SUSPEND 36
+#endif
+
+#ifndef CAP_AUDIT_READ
+#define CAP_AUDIT_READ 37
+#endif
+
+static inline int raw_clone(unsigned long flags, void *child_stack) {
+#if defined(__s390__) || defined(__CRIS__)
+ /* On s390 and cris the order of the first and second arguments
+ * of the raw clone() system call is reversed. */
+ return (int) syscall(__NR_clone, child_stack, flags);
+#else
+ return (int) syscall(__NR_clone, flags, child_stack);
+#endif
+}
+
+static inline pid_t raw_getpid(void) {
+ return (pid_t) syscall(__NR_getpid);
+}
+
+#if !HAVE_DECL_RENAMEAT2
+
+#ifndef __NR_renameat2
+# if defined __x86_64__
+# define __NR_renameat2 316
+# elif defined __arm__
+# define __NR_renameat2 382
+# elif defined _MIPS_SIM
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define __NR_renameat2 4351
+# endif
+# if _MIPS_SIM == _MIPS_SIM_NABI32
+# define __NR_renameat2 6315
+# endif
+# if _MIPS_SIM == _MIPS_SIM_ABI64
+# define __NR_renameat2 5311
+# endif
+# elif defined __i386__
+# define __NR_renameat2 353
+# else
+# warning "__NR_renameat2 unknown for your architecture"
+# define __NR_renameat2 0xffffffff
+# endif
+#endif
+
+static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
+ return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
+}
+#endif
+
+#ifndef RENAME_NOREPLACE
+#define RENAME_NOREPLACE (1 << 0)
+#endif
+
+#if !HAVE_DECL_KCMP
+static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
+ return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
+}
+#endif
+
+#ifndef KCMP_FILE
+#define KCMP_FILE 0
+#endif
+
+#ifndef INPUT_PROP_POINTING_STICK
+#define INPUT_PROP_POINTING_STICK 0x05
+#endif
+
+#ifndef INPUT_PROP_ACCELEROMETER
+#define INPUT_PROP_ACCELEROMETER 0x06
+#endif
diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c
new file mode 100644
index 0000000..76bbc1e
--- /dev/null
+++ b/src/basic/mkdir-label.c
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2013 Kay Sievers
+
+ 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 <unistd.h>
+#include <stdio.h>
+
+#include "label.h"
+#include "mkdir.h"
+
+int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ return mkdir_safe_internal(path, mode, uid, gid, mkdir_label);
+}
+
+int mkdir_parents_label(const char *path, mode_t mode) {
+ return mkdir_parents_internal(NULL, path, mode, mkdir_label);
+}
+
+int mkdir_p_label(const char *path, mode_t mode) {
+ return mkdir_p_internal(NULL, path, mode, mkdir_label);
+}
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
new file mode 100644
index 0000000..7ee4546
--- /dev/null
+++ b/src/basic/mkdir.c
@@ -0,0 +1,125 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "path-util.h"
+#include "mkdir.h"
+
+int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) {
+ struct stat st;
+
+ if (_mkdir(path, mode) >= 0)
+ if (chmod_and_chown(path, mode, uid, gid) < 0)
+ return -errno;
+
+ if (lstat(path, &st) < 0)
+ return -errno;
+
+ if ((st.st_mode & 0007) > (mode & 0007) ||
+ (st.st_mode & 0070) > (mode & 0070) ||
+ (st.st_mode & 0700) > (mode & 0700) ||
+ (uid != UID_INVALID && st.st_uid != uid) ||
+ (gid != GID_INVALID && st.st_gid != gid) ||
+ !S_ISDIR(st.st_mode))
+ return -EEXIST;
+
+ return 0;
+}
+
+int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ return mkdir_safe_internal(path, mode, uid, gid, mkdir);
+}
+
+int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
+ const char *p, *e;
+ int r;
+
+ assert(path);
+
+ if (prefix && !path_startswith(path, prefix))
+ return -ENOTDIR;
+
+ /* return immediately if directory exists */
+ e = strrchr(path, '/');
+ if (!e)
+ return -EINVAL;
+
+ if (e == path)
+ return 0;
+
+ p = strndupa(path, e - path);
+ r = is_dir(p, true);
+ if (r > 0)
+ return 0;
+ if (r == 0)
+ return -ENOTDIR;
+
+ /* create every parent directory in the path, except the last component */
+ p = path + strspn(path, "/");
+ for (;;) {
+ char t[strlen(path) + 1];
+
+ e = p + strcspn(p, "/");
+ p = e + strspn(e, "/");
+
+ /* Is this the last component? If so, then we're
+ * done */
+ if (*p == 0)
+ return 0;
+
+ memcpy(t, path, e - path);
+ t[e-path] = 0;
+
+ if (prefix && path_startswith(prefix, t))
+ continue;
+
+ r = _mkdir(t, mode);
+ if (r < 0 && errno != EEXIST)
+ return -errno;
+ }
+}
+
+int mkdir_parents(const char *path, mode_t mode) {
+ return mkdir_parents_internal(NULL, path, mode, mkdir);
+}
+
+int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
+ int r;
+
+ /* Like mkdir -p */
+
+ r = mkdir_parents_internal(prefix, path, mode, _mkdir);
+ if (r < 0)
+ return r;
+
+ r = _mkdir(path, mode);
+ if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
+ return -errno;
+
+ return 0;
+}
+
+int mkdir_p(const char *path, mode_t mode) {
+ return mkdir_p_internal(NULL, path, mode, mkdir);
+}
diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
new file mode 100644
index 0000000..2392d1f
--- /dev/null
+++ b/src/basic/mkdir.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2013 Kay Sievers
+
+ 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 mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_parents(const char *path, mode_t mode);
+int mkdir_p(const char *path, mode_t mode);
+
+/* mandatory access control(MAC) versions */
+int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_parents_label(const char *path, mode_t mode);
+int mkdir_p_label(const char *path, mode_t mode);
+
+/* internally used */
+typedef int (*mkdir_func_t)(const char *pathname, mode_t mode);
+int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir);
+int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
+int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
new file mode 100644
index 0000000..766a1f2
--- /dev/null
+++ b/src/basic/ordered-set.h
@@ -0,0 +1,59 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "hashmap.h"
+
+typedef struct OrderedSet OrderedSet;
+
+static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
+ return (OrderedSet*) ordered_hashmap_new(ops);
+}
+
+static inline OrderedSet* ordered_set_free(OrderedSet *s) {
+ ordered_hashmap_free((OrderedHashmap*) s);
+ return NULL;
+}
+
+static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
+ ordered_hashmap_free_free((OrderedHashmap*) s);
+ return NULL;
+}
+
+static inline int ordered_set_put(OrderedSet *s, void *p) {
+ return ordered_hashmap_put((OrderedHashmap*) s, p, p);
+}
+
+static inline bool ordered_set_isempty(OrderedSet *s) {
+ return ordered_hashmap_isempty((OrderedHashmap*) s);
+}
+
+static inline void *ordered_set_iterate(OrderedSet *s, Iterator *i) {
+ return ordered_hashmap_iterate((OrderedHashmap*) s, i, NULL);
+}
+
+#define ORDERED_SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_set_iterate((s), &(i)); (e); (e) = ordered_set_iterate((s), &(i)))
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
+
+#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
new file mode 100644
index 0000000..5377054
--- /dev/null
+++ b/src/basic/path-util.c
@@ -0,0 +1,853 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/statvfs.h>
+
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+#include "strv.h"
+#include "path-util.h"
+#include "missing.h"
+#include "fileio.h"
+
+bool path_is_absolute(const char *p) {
+ return p[0] == '/';
+}
+
+bool is_path(const char *p) {
+ return !!strchr(p, '/');
+}
+
+int path_get_parent(const char *path, char **_r) {
+ const char *e, *a = NULL, *b = NULL, *p;
+ char *r;
+ bool slash = false;
+
+ assert(path);
+ assert(_r);
+
+ if (!*path)
+ return -EINVAL;
+
+ for (e = path; *e; e++) {
+
+ if (!slash && *e == '/') {
+ a = b;
+ b = e;
+ slash = true;
+ } else if (slash && *e != '/')
+ slash = false;
+ }
+
+ if (*(e-1) == '/')
+ p = a;
+ else
+ p = b;
+
+ if (!p)
+ return -EINVAL;
+
+ if (p == path)
+ r = strdup("/");
+ else
+ r = strndup(path, p-path);
+
+ if (!r)
+ return -ENOMEM;
+
+ *_r = r;
+ return 0;
+}
+
+char **path_split_and_make_absolute(const char *p) {
+ char **l;
+ assert(p);
+
+ l = strv_split(p, ":");
+ if (!l)
+ return NULL;
+
+ if (!path_strv_make_absolute_cwd(l)) {
+ strv_free(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+char *path_make_absolute(const char *p, const char *prefix) {
+ assert(p);
+
+ /* Makes every item in the list an absolute path by prepending
+ * the prefix, if specified and necessary */
+
+ if (path_is_absolute(p) || !prefix)
+ return strdup(p);
+
+ return strjoin(prefix, "/", p, NULL);
+}
+
+char *path_make_absolute_cwd(const char *p) {
+ _cleanup_free_ char *cwd = NULL;
+
+ assert(p);
+
+ /* Similar to path_make_absolute(), but prefixes with the
+ * current working directory. */
+
+ if (path_is_absolute(p))
+ return strdup(p);
+
+ cwd = get_current_dir_name();
+ if (!cwd)
+ return NULL;
+
+ return strjoin(cwd, "/", p, NULL);
+}
+
+int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
+ char *r, *p;
+ unsigned n_parents;
+
+ assert(from_dir);
+ assert(to_path);
+ assert(_r);
+
+ /* Strips the common part, and adds ".." elements as necessary. */
+
+ if (!path_is_absolute(from_dir))
+ return -EINVAL;
+
+ if (!path_is_absolute(to_path))
+ return -EINVAL;
+
+ /* Skip the common part. */
+ for (;;) {
+ size_t a;
+ size_t b;
+
+ from_dir += strspn(from_dir, "/");
+ to_path += strspn(to_path, "/");
+
+ if (!*from_dir) {
+ if (!*to_path)
+ /* from_dir equals to_path. */
+ r = strdup(".");
+ else
+ /* from_dir is a parent directory of to_path. */
+ r = strdup(to_path);
+
+ if (!r)
+ return -ENOMEM;
+
+ path_kill_slashes(r);
+
+ *_r = r;
+ return 0;
+ }
+
+ if (!*to_path)
+ break;
+
+ a = strcspn(from_dir, "/");
+ b = strcspn(to_path, "/");
+
+ if (a != b)
+ break;
+
+ if (memcmp(from_dir, to_path, a) != 0)
+ break;
+
+ from_dir += a;
+ to_path += b;
+ }
+
+ /* If we're here, then "from_dir" has one or more elements that need to
+ * be replaced with "..". */
+
+ /* Count the number of necessary ".." elements. */
+ for (n_parents = 0;;) {
+ from_dir += strspn(from_dir, "/");
+
+ if (!*from_dir)
+ break;
+
+ from_dir += strcspn(from_dir, "/");
+ n_parents++;
+ }
+
+ r = malloc(n_parents * 3 + strlen(to_path) + 1);
+ if (!r)
+ return -ENOMEM;
+
+ for (p = r; n_parents > 0; n_parents--, p += 3)
+ memcpy(p, "../", 3);
+
+ strcpy(p, to_path);
+ path_kill_slashes(r);
+
+ *_r = r;
+ return 0;
+}
+
+char **path_strv_make_absolute_cwd(char **l) {
+ char **s;
+
+ /* Goes through every item in the string list and makes it
+ * absolute. This works in place and won't rollback any
+ * changes on failure. */
+
+ STRV_FOREACH(s, l) {
+ char *t;
+
+ t = path_make_absolute_cwd(*s);
+ if (!t)
+ return NULL;
+
+ free(*s);
+ *s = t;
+ }
+
+ return l;
+}
+
+char **path_strv_resolve(char **l, const char *prefix) {
+ char **s;
+ unsigned k = 0;
+ bool enomem = false;
+
+ if (strv_isempty(l))
+ return l;
+
+ /* Goes through every item in the string list and canonicalize
+ * the path. This works in place and won't rollback any
+ * changes on failure. */
+
+ STRV_FOREACH(s, l) {
+ char *t, *u;
+ _cleanup_free_ char *orig = NULL;
+
+ if (!path_is_absolute(*s)) {
+ free(*s);
+ continue;
+ }
+
+ if (prefix) {
+ orig = *s;
+ t = strappend(prefix, orig);
+ if (!t) {
+ enomem = true;
+ continue;
+ }
+ } else
+ t = *s;
+
+ errno = 0;
+ u = canonicalize_file_name(t);
+ if (!u) {
+ if (errno == ENOENT) {
+ if (prefix) {
+ u = orig;
+ orig = NULL;
+ free(t);
+ } else
+ u = t;
+ } else {
+ free(t);
+ if (errno == ENOMEM || errno == 0)
+ enomem = true;
+
+ continue;
+ }
+ } else if (prefix) {
+ char *x;
+
+ free(t);
+ x = path_startswith(u, prefix);
+ if (x) {
+ /* restore the slash if it was lost */
+ if (!startswith(x, "/"))
+ *(--x) = '/';
+
+ t = strdup(x);
+ free(u);
+ if (!t) {
+ enomem = true;
+ continue;
+ }
+ u = t;
+ } else {
+ /* canonicalized path goes outside of
+ * prefix, keep the original path instead */
+ free(u);
+ u = orig;
+ orig = NULL;
+ }
+ } else
+ free(t);
+
+ l[k++] = u;
+ }
+
+ l[k] = NULL;
+
+ if (enomem)
+ return NULL;
+
+ return l;
+}
+
+char **path_strv_resolve_uniq(char **l, const char *prefix) {
+
+ if (strv_isempty(l))
+ return l;
+
+ if (!path_strv_resolve(l, prefix))
+ return NULL;
+
+ return strv_uniq(l);
+}
+
+char *path_kill_slashes(char *path) {
+ char *f, *t;
+ bool slash = false;
+
+ /* Removes redundant inner and trailing slashes. Modifies the
+ * passed string in-place.
+ *
+ * ///foo///bar/ becomes /foo/bar
+ */
+
+ for (f = path, t = path; *f; f++) {
+
+ if (*f == '/') {
+ slash = true;
+ continue;
+ }
+
+ if (slash) {
+ slash = false;
+ *(t++) = '/';
+ }
+
+ *(t++) = *f;
+ }
+
+ /* Special rule, if we are talking of the root directory, a
+ trailing slash is good */
+
+ if (t == path && slash)
+ *(t++) = '/';
+
+ *t = 0;
+ return path;
+}
+
+char* path_startswith(const char *path, const char *prefix) {
+ assert(path);
+ assert(prefix);
+
+ if ((path[0] == '/') != (prefix[0] == '/'))
+ return NULL;
+
+ for (;;) {
+ size_t a, b;
+
+ path += strspn(path, "/");
+ prefix += strspn(prefix, "/");
+
+ if (*prefix == 0)
+ return (char*) path;
+
+ if (*path == 0)
+ return NULL;
+
+ a = strcspn(path, "/");
+ b = strcspn(prefix, "/");
+
+ if (a != b)
+ return NULL;
+
+ if (memcmp(path, prefix, a) != 0)
+ return NULL;
+
+ path += a;
+ prefix += b;
+ }
+}
+
+int path_compare(const char *a, const char *b) {
+ int d;
+
+ assert(a);
+ assert(b);
+
+ /* A relative path and an abolute path must not compare as equal.
+ * Which one is sorted before the other does not really matter.
+ * Here a relative path is ordered before an absolute path. */
+ d = (a[0] == '/') - (b[0] == '/');
+ if (d)
+ return d;
+
+ for (;;) {
+ size_t j, k;
+
+ a += strspn(a, "/");
+ b += strspn(b, "/");
+
+ if (*a == 0 && *b == 0)
+ return 0;
+
+ /* Order prefixes first: "/foo" before "/foo/bar" */
+ if (*a == 0)
+ return -1;
+ if (*b == 0)
+ return 1;
+
+ j = strcspn(a, "/");
+ k = strcspn(b, "/");
+
+ /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
+ d = memcmp(a, b, MIN(j, k));
+ if (d)
+ return (d > 0) - (d < 0); /* sign of d */
+
+ /* Sort "/foo/a" before "/foo/aaa" */
+ d = (j > k) - (j < k); /* sign of (j - k) */
+ if (d)
+ return d;
+
+ a += j;
+ b += k;
+ }
+}
+
+bool path_equal(const char *a, const char *b) {
+ return path_compare(a, b) == 0;
+}
+
+bool path_equal_or_files_same(const char *a, const char *b) {
+ return path_equal(a, b) || files_same(a, b) > 0;
+}
+
+char* path_join(const char *root, const char *path, const char *rest) {
+ assert(path);
+
+ if (!isempty(root))
+ return strjoin(root, endswith(root, "/") ? "" : "/",
+ path[0] == '/' ? path+1 : path,
+ rest ? (endswith(path, "/") ? "" : "/") : NULL,
+ rest && rest[0] == '/' ? rest+1 : rest,
+ NULL);
+ else
+ return strjoin(path,
+ rest ? (endswith(path, "/") ? "" : "/") : NULL,
+ rest && rest[0] == '/' ? rest+1 : rest,
+ NULL);
+}
+
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
+ char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+ _cleanup_free_ char *fdinfo = NULL;
+ _cleanup_close_ int subfd = -1;
+ char *p;
+ int r;
+
+ if ((flags & AT_EMPTY_PATH) && isempty(filename))
+ xsprintf(path, "/proc/self/fdinfo/%i", fd);
+ else {
+ subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
+ if (subfd < 0)
+ return -errno;
+
+ xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+ }
+
+ r = read_full_file(path, &fdinfo, NULL);
+ if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
+ return -EOPNOTSUPP;
+ if (r < 0)
+ return -errno;
+
+ p = startswith(fdinfo, "mnt_id:");
+ if (!p) {
+ p = strstr(fdinfo, "\nmnt_id:");
+ if (!p) /* The mnt_id field is a relatively new addition */
+ return -EOPNOTSUPP;
+
+ p += 8;
+ }
+
+ p += strspn(p, WHITESPACE);
+ p[strcspn(p, WHITESPACE)] = 0;
+
+ return safe_atoi(p, mnt_id);
+}
+
+int fd_is_mount_point(int fd, const char *filename, int flags) {
+ union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
+ int mount_id = -1, mount_id_parent = -1;
+ bool nosupp = false, check_st_dev = true;
+ struct stat a, b;
+ int r;
+
+ assert(fd >= 0);
+ assert(filename);
+
+ /* First we will try the name_to_handle_at() syscall, which
+ * tells us the mount id and an opaque file "handle". It is
+ * not supported everywhere though (kernel compile-time
+ * option, not all file systems are hooked up). If it works
+ * the mount id is usually good enough to tell us whether
+ * something is a mount point.
+ *
+ * If that didn't work we will try to read the mount id from
+ * /proc/self/fdinfo/<fd>. This is almost as good as
+ * name_to_handle_at(), however, does not return the the
+ * opaque file handle. The opaque file handle is pretty useful
+ * to detect the root directory, which we should always
+ * consider a mount point. Hence we use this only as
+ * fallback. Exporting the mnt_id in fdinfo is a pretty recent
+ * kernel addition.
+ *
+ * As last fallback we do traditional fstat() based st_dev
+ * comparisons. This is how things were traditionally done,
+ * but unionfs breaks breaks this since it exposes file
+ * systems with a variety of st_dev reported. Also, btrfs
+ * subvolumes have different st_dev, even though they aren't
+ * real mounts of their own. */
+
+ r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
+ if (r < 0) {
+ if (errno == ENOSYS)
+ /* This kernel does not support name_to_handle_at()
+ * fall back to simpler logic. */
+ goto fallback_fdinfo;
+ else if (errno == EOPNOTSUPP)
+ /* This kernel or file system does not support
+ * name_to_handle_at(), hence let's see if the
+ * upper fs supports it (in which case it is a
+ * mount point), otherwise fallback to the
+ * traditional stat() logic */
+ nosupp = true;
+ else
+ return -errno;
+ }
+
+ r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
+ if (r < 0) {
+ if (errno == EOPNOTSUPP) {
+ if (nosupp)
+ /* Neither parent nor child do name_to_handle_at()?
+ We have no choice but to fall back. */
+ goto fallback_fdinfo;
+ else
+ /* The parent can't do name_to_handle_at() but the
+ * directory we are interested in can?
+ * If so, it must be a mount point. */
+ return 1;
+ } else
+ return -errno;
+ }
+
+ /* The parent can do name_to_handle_at() but the
+ * directory we are interested in can't? If so, it
+ * must be a mount point. */
+ if (nosupp)
+ return 1;
+
+ /* If the file handle for the directory we are
+ * interested in and its parent are identical, we
+ * assume this is the root directory, which is a mount
+ * point. */
+
+ if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
+ h.handle.handle_type == h_parent.handle.handle_type &&
+ memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
+ return 1;
+
+ return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (r == -EOPNOTSUPP)
+ goto fallback_fstat;
+ if (r < 0)
+ return r;
+
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (r < 0)
+ return r;
+
+ if (mount_id != mount_id_parent)
+ return 1;
+
+ /* Hmm, so, the mount ids are the same. This leaves one
+ * special case though for the root file system. For that,
+ * let's see if the parent directory has the same inode as we
+ * are interested in. Hence, let's also do fstat() checks now,
+ * too, but avoid the st_dev comparisons, since they aren't
+ * that useful on unionfs mounts. */
+ check_st_dev = false;
+
+fallback_fstat:
+ /* yay for fstatat() taking a different set of flags than the other
+ * _at() above */
+ if (flags & AT_SYMLINK_FOLLOW)
+ flags &= ~AT_SYMLINK_FOLLOW;
+ else
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (fstatat(fd, filename, &a, flags) < 0)
+ return -errno;
+
+ if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ /* A directory with same device and inode as its parent? Must
+ * be the root directory */
+ if (a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino)
+ return 1;
+
+ return check_st_dev && (a.st_dev != b.st_dev);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, int flags) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *canonical = NULL, *parent = NULL;
+ int r;
+
+ assert(t);
+
+ if (path_equal(t, "/"))
+ return 1;
+
+ /* we need to resolve symlinks manually, we can't just rely on
+ * fd_is_mount_point() to do that for us; if we have a structure like
+ * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+ * look at needs to be /usr, not /. */
+ if (flags & AT_SYMLINK_FOLLOW) {
+ canonical = canonicalize_file_name(t);
+ if (!canonical)
+ return -errno;
+ }
+
+ r = path_get_parent(canonical ?: t, &parent);
+ if (r < 0)
+ return r;
+
+ fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_mount_point(fd, basename(canonical ?: t), flags);
+}
+
+int path_is_read_only_fs(const char *path) {
+ struct statvfs st;
+
+ assert(path);
+
+ if (statvfs(path, &st) < 0)
+ return -errno;
+
+ if (st.f_flag & ST_RDONLY)
+ return true;
+
+ /* On NFS, statvfs() might not reflect whether we can actually
+ * write to the remote share. Let's try again with
+ * access(W_OK) which is more reliable, at least sometimes. */
+ if (access(path, W_OK) < 0 && errno == EROFS)
+ return true;
+
+ return false;
+}
+
+int path_is_os_tree(const char *path) {
+ char *p;
+ int r;
+
+ /* We use /usr/lib/os-release as flag file if something is an OS */
+ p = strjoina(path, "/usr/lib/os-release");
+ r = access(p, F_OK);
+
+ if (r >= 0)
+ return 1;
+
+ /* Also check for the old location in /etc, just in case. */
+ p = strjoina(path, "/etc/os-release");
+ r = access(p, F_OK);
+
+ return r >= 0;
+}
+
+int find_binary(const char *name, bool local, char **filename) {
+ assert(name);
+
+ if (is_path(name)) {
+ if (local && access(name, X_OK) < 0)
+ return -errno;
+
+ if (filename) {
+ char *p;
+
+ p = path_make_absolute_cwd(name);
+ if (!p)
+ return -ENOMEM;
+
+ *filename = p;
+ }
+
+ return 0;
+ } else {
+ const char *path;
+ const char *word, *state;
+ size_t l;
+
+ /**
+ * Plain getenv, not secure_getenv, because we want
+ * to actually allow the user to pick the binary.
+ */
+ path = getenv("PATH");
+ if (!path)
+ path = DEFAULT_PATH;
+
+ FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
+ _cleanup_free_ char *p = NULL;
+
+ if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
+ return -ENOMEM;
+
+ if (access(p, X_OK) < 0)
+ continue;
+
+ if (filename) {
+ *filename = path_kill_slashes(p);
+ p = NULL;
+ }
+
+ return 0;
+ }
+
+ return -ENOENT;
+ }
+}
+
+bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
+ bool changed = false;
+ const char* const* i;
+
+ assert(timestamp);
+
+ if (paths == NULL)
+ return false;
+
+ STRV_FOREACH(i, paths) {
+ struct stat stats;
+ usec_t u;
+
+ if (stat(*i, &stats) < 0)
+ continue;
+
+ u = timespec_load(&stats.st_mtim);
+
+ /* first check */
+ if (*timestamp >= u)
+ continue;
+
+ log_debug("timestamp of '%s' changed", *i);
+
+ /* update timestamp */
+ if (update) {
+ *timestamp = u;
+ changed = true;
+ } else
+ return true;
+ }
+
+ return changed;
+}
+
+int fsck_exists(const char *fstype) {
+ _cleanup_free_ char *p = NULL, *d = NULL;
+ const char *checker;
+ int r;
+
+ checker = strjoina("fsck.", fstype);
+
+ r = find_binary(checker, true, &p);
+ if (r < 0)
+ return r;
+
+ /* An fsck that is linked to /bin/true is a non-existent
+ * fsck */
+
+ r = readlink_malloc(p, &d);
+ if (r >= 0 &&
+ (path_equal(d, "/bin/true") ||
+ path_equal(d, "/usr/bin/true") ||
+ path_equal(d, "/dev/null")))
+ return -ENOENT;
+
+ return 0;
+}
+
+char *prefix_root(const char *root, const char *path) {
+ char *n, *p;
+ size_t l;
+
+ /* If root is passed, prefixes path with it. Otherwise returns
+ * it as is. */
+
+ assert(path);
+
+ /* First, drop duplicate prefixing slashes from the path */
+ while (path[0] == '/' && path[1] == '/')
+ path++;
+
+ if (isempty(root) || path_equal(root, "/"))
+ return strdup(path);
+
+ l = strlen(root) + 1 + strlen(path) + 1;
+
+ n = new(char, l);
+ if (!n)
+ return NULL;
+
+ p = stpcpy(n, root);
+
+ while (p > n && p[-1] == '/')
+ p--;
+
+ if (path[0] != '/')
+ *(p++) = '/';
+
+ strcpy(p, path);
+ return n;
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
new file mode 100644
index 0000000..1eac89c
--- /dev/null
+++ b/src/basic/path-util.h
@@ -0,0 +1,102 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-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 <stdbool.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
+#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin"
+
+#ifdef HAVE_SPLIT_USR
+# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
+#else
+# define DEFAULT_PATH DEFAULT_PATH_NORMAL
+#endif
+
+bool is_path(const char *p) _pure_;
+char** path_split_and_make_absolute(const char *p);
+int path_get_parent(const char *path, char **parent);
+bool path_is_absolute(const char *p) _pure_;
+char* path_make_absolute(const char *p, const char *prefix);
+char* path_make_absolute_cwd(const char *p);
+int path_make_relative(const char *from_dir, const char *to_path, char **_r);
+char* path_kill_slashes(char *path);
+char* path_startswith(const char *path, const char *prefix) _pure_;
+int path_compare(const char *a, const char *b) _pure_;
+bool path_equal(const char *a, const char *b) _pure_;
+bool path_equal_or_files_same(const char *a, const char *b);
+char* path_join(const char *root, const char *path, const char *rest);
+
+char** path_strv_make_absolute_cwd(char **l);
+char** path_strv_resolve(char **l, const char *prefix);
+char** path_strv_resolve_uniq(char **l, const char *prefix);
+
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, int flags);
+int path_is_read_only_fs(const char *path);
+int path_is_os_tree(const char *path);
+
+int find_binary(const char *name, bool local, char **filename);
+
+bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
+
+int fsck_exists(const char *fstype);
+
+/* Iterates through the path prefixes of the specified path, going up
+ * the tree, to root. Also returns "" (and not "/"!) for the root
+ * directory. Excludes the specified directory itself */
+#define PATH_FOREACH_PREFIX(prefix, path) \
+ for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+
+/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
+#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
+ for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+
+char *prefix_root(const char *root, const char *path);
+
+/* Similar to prefix_root(), but returns an alloca() buffer, or
+ * possibly a const pointer into the path parameter */
+#define prefix_roota(root, path) \
+ ({ \
+ const char* _path = (path), *_root = (root), *_ret; \
+ char *_p, *_n; \
+ size_t _l; \
+ while (_path[0] == '/' && _path[1] == '/') \
+ _path ++; \
+ if (isempty(_root) || path_equal(_root, "/")) \
+ _ret = _path; \
+ else { \
+ _l = strlen(_root) + 1 + strlen(_path) + 1; \
+ _n = alloca(_l); \
+ _p = stpcpy(_n, _root); \
+ while (_p > _n && _p[-1] == '/') \
+ _p--; \
+ if (_path[0] != '/') \
+ *(_p++) = '/'; \
+ strcpy(_p, _path); \
+ _ret = _n; \
+ } \
+ _ret; \
+ })
diff --git a/src/basic/prioq.c b/src/basic/prioq.c
new file mode 100644
index 0000000..b89888b
--- /dev/null
+++ b/src/basic/prioq.c
@@ -0,0 +1,308 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 "util.h"
+#include "prioq.h"
+
+struct prioq_item {
+ void *data;
+ unsigned *idx;
+};
+
+struct Prioq {
+ compare_func_t compare_func;
+ unsigned n_items, n_allocated;
+
+ struct prioq_item *items;
+};
+
+Prioq *prioq_new(compare_func_t compare_func) {
+ Prioq *q;
+
+ q = new0(Prioq, 1);
+ if (!q)
+ return q;
+
+ q->compare_func = compare_func;
+ return q;
+}
+
+Prioq* prioq_free(Prioq *q) {
+ if (!q)
+ return NULL;
+
+ free(q->items);
+ free(q);
+
+ return NULL;
+}
+
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
+ assert(q);
+
+ if (*q)
+ return 0;
+
+ *q = prioq_new(compare_func);
+ if (!*q)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void swap(Prioq *q, unsigned j, unsigned k) {
+ void *saved_data;
+ unsigned *saved_idx;
+
+ assert(q);
+ assert(j < q->n_items);
+ assert(k < q->n_items);
+
+ assert(!q->items[j].idx || *(q->items[j].idx) == j);
+ assert(!q->items[k].idx || *(q->items[k].idx) == k);
+
+ saved_data = q->items[j].data;
+ saved_idx = q->items[j].idx;
+ q->items[j].data = q->items[k].data;
+ q->items[j].idx = q->items[k].idx;
+ q->items[k].data = saved_data;
+ q->items[k].idx = saved_idx;
+
+ if (q->items[j].idx)
+ *q->items[j].idx = j;
+
+ if (q->items[k].idx)
+ *q->items[k].idx = k;
+}
+
+static unsigned shuffle_up(Prioq *q, unsigned idx) {
+ assert(q);
+
+ while (idx > 0) {
+ unsigned k;
+
+ k = (idx-1)/2;
+
+ if (q->compare_func(q->items[k].data, q->items[idx].data) < 0)
+ break;
+
+ swap(q, idx, k);
+ idx = k;
+ }
+
+ return idx;
+}
+
+static unsigned shuffle_down(Prioq *q, unsigned idx) {
+ assert(q);
+
+ for (;;) {
+ unsigned j, k, s;
+
+ k = (idx+1)*2; /* right child */
+ j = k-1; /* left child */
+
+ if (j >= q->n_items)
+ break;
+
+ if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
+
+ /* So our left child is smaller than we are, let's
+ * remember this fact */
+ s = j;
+ else
+ s = idx;
+
+ if (k < q->n_items &&
+ q->compare_func(q->items[k].data, q->items[s].data) < 0)
+
+ /* So our right child is smaller than we are, let's
+ * remember this fact */
+ s = k;
+
+ /* s now points to the smallest of the three items */
+
+ if (s == idx)
+ /* No swap necessary, we're done */
+ break;
+
+ swap(q, idx, s);
+ idx = s;
+ }
+
+ return idx;
+}
+
+int prioq_put(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+ unsigned k;
+
+ assert(q);
+
+ if (q->n_items >= q->n_allocated) {
+ unsigned n;
+ struct prioq_item *j;
+
+ n = MAX((q->n_items+1) * 2, 16u);
+ j = realloc(q->items, sizeof(struct prioq_item) * n);
+ if (!j)
+ return -ENOMEM;
+
+ q->items = j;
+ q->n_allocated = n;
+ }
+
+ k = q->n_items++;
+ i = q->items + k;
+ i->data = data;
+ i->idx = idx;
+
+ if (idx)
+ *idx = k;
+
+ shuffle_up(q, k);
+
+ return 0;
+}
+
+static void remove_item(Prioq *q, struct prioq_item *i) {
+ struct prioq_item *l;
+
+ assert(q);
+ assert(i);
+
+ l = q->items + q->n_items - 1;
+
+ if (i == l)
+ /* Last entry, let's just remove it */
+ q->n_items--;
+ else {
+ unsigned k;
+
+ /* Not last entry, let's replace the last entry with
+ * this one, and reshuffle */
+
+ k = i - q->items;
+
+ i->data = l->data;
+ i->idx = l->idx;
+ if (i->idx)
+ *i->idx = k;
+ q->n_items--;
+
+ k = shuffle_down(q, k);
+ shuffle_up(q, k);
+ }
+}
+
+_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+
+ assert(q);
+
+ if (idx) {
+ if (*idx == PRIOQ_IDX_NULL ||
+ *idx > q->n_items)
+ return NULL;
+
+ i = q->items + *idx;
+ if (i->data != data)
+ return NULL;
+
+ return i;
+ } else {
+ for (i = q->items; i < q->items + q->n_items; i++)
+ if (i->data == data)
+ return i;
+ return NULL;
+ }
+}
+
+int prioq_remove(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+
+ if (!q)
+ return 0;
+
+ i = find_item(q, data, idx);
+ if (!i)
+ return 0;
+
+ remove_item(q, i);
+ return 1;
+}
+
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+ unsigned k;
+
+ assert(q);
+
+ i = find_item(q, data, idx);
+ if (!i)
+ return 0;
+
+ k = i - q->items;
+ k = shuffle_down(q, k);
+ shuffle_up(q, k);
+ return 1;
+}
+
+void *prioq_peek(Prioq *q) {
+
+ if (!q)
+ return NULL;
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ return q->items[0].data;
+}
+
+void *prioq_pop(Prioq *q) {
+ void *data;
+
+ if (!q)
+ return NULL;
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ data = q->items[0].data;
+ remove_item(q, q->items);
+ return data;
+}
+
+unsigned prioq_size(Prioq *q) {
+
+ if (!q)
+ return 0;
+
+ return q->n_items;
+}
+
+bool prioq_isempty(Prioq *q) {
+
+ if (!q)
+ return true;
+
+ return q->n_items <= 0;
+}
diff --git a/src/basic/prioq.h b/src/basic/prioq.h
new file mode 100644
index 0000000..1c044b1
--- /dev/null
+++ b/src/basic/prioq.h
@@ -0,0 +1,42 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 "hashmap.h"
+
+typedef struct Prioq Prioq;
+
+#define PRIOQ_IDX_NULL ((unsigned) -1)
+
+Prioq *prioq_new(compare_func_t compare);
+Prioq *prioq_free(Prioq *q);
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
+
+int prioq_put(Prioq *q, void *data, unsigned *idx);
+int prioq_remove(Prioq *q, void *data, unsigned *idx);
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
+
+void *prioq_peek(Prioq *q) _pure_;
+void *prioq_pop(Prioq *q);
+
+unsigned prioq_size(Prioq *q) _pure_;
+bool prioq_isempty(Prioq *q) _pure_;
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
new file mode 100644
index 0000000..cfc8765
--- /dev/null
+++ b/src/basic/process-util.c
@@ -0,0 +1,539 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "fileio.h"
+#include "util.h"
+#include "log.h"
+#include "signal-util.h"
+#include "process-util.h"
+
+int get_process_state(pid_t pid) {
+ const char *p;
+ char state;
+ int r;
+ _cleanup_free_ char *line = NULL;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " %c", &state) != 1)
+ return -EIO;
+
+ return (unsigned char) state;
+}
+
+int get_process_comm(pid_t pid, char **name) {
+ const char *p;
+ int r;
+
+ assert(name);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "comm");
+
+ r = read_one_line_file(p, name);
+ if (r == -ENOENT)
+ return -ESRCH;
+
+ return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *r = NULL, *k;
+ const char *p;
+ int c;
+
+ assert(line);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ if (max_length == 0) {
+ size_t len = 0, allocated = 0;
+
+ while ((c = getc(f)) != EOF) {
+
+ if (!GREEDY_REALLOC(r, allocated, len+2)) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ r[len++] = isprint(c) ? c : ' ';
+ }
+
+ if (len > 0)
+ r[len-1] = 0;
+
+ } else {
+ bool space = false;
+ size_t left;
+
+ r = new(char, max_length);
+ if (!r)
+ return -ENOMEM;
+
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
+
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
+
+ if (left <= 4)
+ break;
+
+ *(k++) = (char) c;
+ left--;
+ } else
+ space = true;
+ }
+
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
+ }
+
+ /* Kernel threads have no argv[] */
+ if (isempty(r)) {
+ _cleanup_free_ char *t = NULL;
+ int h;
+
+ free(r);
+
+ if (!comm_fallback)
+ return -ENOENT;
+
+ h = get_process_comm(pid, &t);
+ if (h < 0)
+ return h;
+
+ r = strjoin("[", t, "]", NULL);
+ if (!r)
+ return -ENOMEM;
+ }
+
+ *line = r;
+ return 0;
+}
+
+int is_kernel_thread(pid_t pid) {
+ const char *p;
+ size_t count;
+ char c;
+ bool eof;
+ FILE *f;
+
+ if (pid == 0)
+ return 0;
+
+ assert(pid > 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ count = fread(&c, 1, 1, f);
+ eof = feof(f);
+ fclose(f);
+
+ /* Kernel threads have an empty cmdline */
+
+ if (count <= 0)
+ return eof ? 1 : -errno;
+
+ return 0;
+}
+
+int get_process_capeff(pid_t pid, char **capeff) {
+ const char *p;
+
+ assert(capeff);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "status");
+
+ return get_status_field(p, "\nCapEff:", capeff);
+}
+
+static int get_process_link_contents(const char *proc_file, char **name) {
+ int r;
+
+ assert(proc_file);
+ assert(name);
+
+ r = readlink_malloc(proc_file, name);
+ if (r < 0)
+ return r == -ENOENT ? -ESRCH : r;
+
+ return 0;
+}
+
+int get_process_exe(pid_t pid, char **name) {
+ const char *p;
+ char *d;
+ int r;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "exe");
+ r = get_process_link_contents(p, name);
+ if (r < 0)
+ return r;
+
+ d = endswith(*name, " (deleted)");
+ if (d)
+ *d = '\0';
+
+ return 0;
+}
+
+static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *p;
+
+ assert(field);
+ assert(uid);
+
+ if (pid == 0)
+ return getuid();
+
+ p = procfs_file_alloca(pid, "status");
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ FOREACH_LINE(line, f, return -errno) {
+ char *l;
+
+ l = strstrip(line);
+
+ if (startswith(l, field)) {
+ l += strlen(field);
+ l += strspn(l, WHITESPACE);
+
+ l[strcspn(l, WHITESPACE)] = 0;
+
+ return parse_uid(l, uid);
+ }
+ }
+
+ return -EIO;
+}
+
+int get_process_uid(pid_t pid, uid_t *uid) {
+ return get_process_id(pid, "Uid:", uid);
+}
+
+int get_process_gid(pid_t pid, gid_t *gid) {
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ return get_process_id(pid, "Gid:", gid);
+}
+
+int get_process_cwd(pid_t pid, char **cwd) {
+ const char *p;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cwd");
+
+ return get_process_link_contents(p, cwd);
+}
+
+int get_process_root(pid_t pid, char **root) {
+ const char *p;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "root");
+
+ return get_process_link_contents(p, root);
+}
+
+int get_process_environ(pid_t pid, char **env) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *outcome = NULL;
+ int c;
+ const char *p;
+ size_t allocated = 0, sz = 0;
+
+ assert(pid >= 0);
+ assert(env);
+
+ p = procfs_file_alloca(pid, "environ");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
+ return -ENOMEM;
+
+ if (c == '\0')
+ outcome[sz++] = '\n';
+ else
+ sz += cescape_char(c, outcome + sz);
+ }
+
+ outcome[sz] = '\0';
+ *env = outcome;
+ outcome = NULL;
+
+ return 0;
+}
+
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ long unsigned ppid;
+ const char *p;
+
+ assert(pid >= 0);
+ assert(_ppid);
+
+ if (pid == 0) {
+ *_ppid = getppid();
+ return 0;
+ }
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ /* Let's skip the pid and comm fields. The latter is enclosed
+ * in () but does not escape any () in its value, so let's
+ * skip over it manually */
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%lu ", /* ppid */
+ &ppid) != 1)
+ return -EIO;
+
+ if ((long unsigned) (pid_t) ppid != ppid)
+ return -ERANGE;
+
+ *_ppid = (pid_t) ppid;
+
+ return 0;
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+ siginfo_t dummy;
+
+ assert(pid >= 1);
+
+ if (!status)
+ status = &dummy;
+
+ for (;;) {
+ zero(*status);
+
+ if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ return 0;
+ }
+}
+
+/*
+ * Return values:
+ * < 0 : wait_for_terminate() failed to get the state of the
+ * process, the process was terminated by a signal, or
+ * failed for an unknown reason.
+ * >=0 : The process terminated normally, and its exit code is
+ * returned.
+ *
+ * That is, success is indicated by a return value of zero, and an
+ * error is indicated by a non-zero value.
+ *
+ * A warning is emitted if the process terminates abnormally,
+ * and also if it returns non-zero unless check_exit_code is true.
+ */
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
+ int r;
+ siginfo_t status;
+
+ assert(name);
+ assert(pid > 1);
+
+ r = wait_for_terminate(pid, &status);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to wait for %s: %m", name);
+
+ if (status.si_code == CLD_EXITED) {
+ if (status.si_status != 0)
+ log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
+ "%s failed with error code %i.", name, status.si_status);
+ else
+ log_debug("%s succeeded.", name);
+
+ return status.si_status;
+ } else if (status.si_code == CLD_KILLED ||
+ status.si_code == CLD_DUMPED) {
+
+ log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ return -EPROTO;
+ }
+
+ log_warning("%s failed due to unknown reason.", name);
+ return -EPROTO;
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+ int r;
+
+ r = kill(pid, sig) < 0 ? -errno : 0;
+
+ if (r >= 0)
+ kill(pid, SIGCONT);
+
+ return r;
+}
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *value = NULL;
+ int r;
+ bool done = false;
+ size_t l;
+ const char *path;
+
+ assert(pid >= 0);
+ assert(field);
+ assert(_value);
+
+ path = procfs_file_alloca(pid, "environ");
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ l = strlen(field);
+ r = 0;
+
+ do {
+ char line[LINE_MAX];
+ unsigned i;
+
+ for (i = 0; i < sizeof(line)-1; i++) {
+ int c;
+
+ c = getc(f);
+ if (_unlikely_(c == EOF)) {
+ done = true;
+ break;
+ } else if (c == 0)
+ break;
+
+ line[i] = c;
+ }
+ line[i] = 0;
+
+ if (memcmp(line, field, l) == 0 && line[l] == '=') {
+ value = strdup(line + l + 1);
+ if (!value)
+ return -ENOMEM;
+
+ r = 1;
+ break;
+ }
+
+ } while (!done);
+
+ *_value = value;
+ return r;
+}
+
+bool pid_is_unwaited(pid_t pid) {
+ /* Checks whether a PID is still valid at all, including a zombie */
+
+ if (pid <= 0)
+ return false;
+
+ if (kill(pid, 0) >= 0)
+ return true;
+
+ return errno != ESRCH;
+}
+
+bool pid_is_alive(pid_t pid) {
+ int r;
+
+ /* Checks whether a PID is still valid and not a zombie */
+
+ if (pid <= 0)
+ return false;
+
+ r = get_process_state(pid);
+ if (r == -ENOENT || r == 'Z')
+ return false;
+
+ return true;
+}
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
new file mode 100644
index 0000000..07431d0
--- /dev/null
+++ b/src/basic/process-util.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <sys/types.h>
+#include <alloca.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "formats-util.h"
+
+#define procfs_file_alloca(pid, field) \
+ ({ \
+ pid_t _pid_ = (pid); \
+ const char *_r_; \
+ if (_pid_ == 0) { \
+ _r_ = ("/proc/self/" field); \
+ } else { \
+ _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+ } \
+ _r_; \
+ })
+
+int get_process_state(pid_t pid);
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_exe(pid_t pid, char **name);
+int get_process_uid(pid_t pid, uid_t *uid);
+int get_process_gid(pid_t pid, gid_t *gid);
+int get_process_capeff(pid_t pid, char **capeff);
+int get_process_cwd(pid_t pid, char **cwd);
+int get_process_root(pid_t pid, char **root);
+int get_process_environ(pid_t pid, char **environ);
+
+int wait_for_terminate(pid_t pid, siginfo_t *status);
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
+
+int kill_and_sigcont(pid_t pid, int sig);
+pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
+void rename_process(const char name[8]);
+int is_kernel_thread(pid_t pid);
+int getenv_for_pid(pid_t pid, const char *field, char **_value);
+
+bool pid_is_alive(pid_t pid);
+bool pid_is_unwaited(pid_t pid);
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
new file mode 100644
index 0000000..b230044
--- /dev/null
+++ b/src/basic/random-util.c
@@ -0,0 +1,129 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+#include <linux/random.h>
+
+#include "random-util.h"
+#include "time-util.h"
+#include "missing.h"
+#include "util.h"
+
+int dev_urandom(void *p, size_t n) {
+ static int have_syscall = -1;
+
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ /* Gathers some randomness from the kernel. This call will
+ * never block, and will always return some data from the
+ * kernel, regardless if the random pool is fully initialized
+ * or not. It thus makes no guarantee for the quality of the
+ * returned entropy, but is good enough for or usual usecases
+ * of seeding the hash functions for hashtable */
+
+ /* Use the getrandom() syscall unless we know we don't have
+ * it, or when the requested size is too large for it. */
+ if (have_syscall != 0 || (size_t) (int) n != n) {
+ r = getrandom(p, n, GRND_NONBLOCK);
+ if (r == (int) n) {
+ have_syscall = true;
+ return 0;
+ }
+
+ if (r < 0) {
+ if (errno == ENOSYS)
+ /* we lack the syscall, continue with
+ * reading from /dev/urandom */
+ have_syscall = false;
+ else if (errno == EAGAIN)
+ /* not enough entropy for now. Let's
+ * remember to use the syscall the
+ * next time, again, but also read
+ * from /dev/urandom for now, which
+ * doesn't care about the current
+ * amount of entropy. */
+ have_syscall = true;
+ else
+ return -errno;
+ } else
+ /* too short read? */
+ return -ENODATA;
+ }
+
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return errno == ENOENT ? -ENOSYS : -errno;
+
+ return loop_read_exact(fd, p, n, true);
+}
+
+void initialize_srand(void) {
+ static bool srand_called = false;
+ unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+ void *auxv;
+#endif
+
+ if (srand_called)
+ return;
+
+ x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+ /* The kernel provides us with a bit of entropy in auxv, so
+ * let's try to make use of that to seed the pseudo-random
+ * generator. It's better than nothing... */
+
+ auxv = (void*) getauxval(AT_RANDOM);
+ if (auxv)
+ x ^= *(unsigned*) auxv;
+#endif
+
+ x ^= (unsigned) now(CLOCK_REALTIME);
+ x ^= (unsigned) gettid();
+
+ srand(x);
+ srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
+ uint8_t *q;
+ int r;
+
+ r = dev_urandom(p, n);
+ if (r >= 0)
+ return;
+
+ /* If some idiot made /dev/urandom unavailable to us, he'll
+ * get a PRNG instead. */
+
+ initialize_srand();
+
+ for (q = p; q < (uint8_t*) p + n; q ++)
+ *q = rand();
+}
diff --git a/src/basic/random-util.h b/src/basic/random-util.h
new file mode 100644
index 0000000..f7862c8
--- /dev/null
+++ b/src/basic/random-util.h
@@ -0,0 +1,38 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdint.h>
+
+int dev_urandom(void *p, size_t n);
+void random_bytes(void *p, size_t n);
+void initialize_srand(void);
+
+static inline uint64_t random_u64(void) {
+ uint64_t u;
+ random_bytes(&u, sizeof(u));
+ return u;
+}
+
+static inline uint32_t random_u32(void) {
+ uint32_t u;
+ random_bytes(&u, sizeof(u));
+ return u;
+}
diff --git a/src/basic/ratelimit.c b/src/basic/ratelimit.c
new file mode 100644
index 0000000..81fc9c1
--- /dev/null
+++ b/src/basic/ratelimit.c
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "ratelimit.h"
+
+/* Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
+
+bool ratelimit_test(RateLimit *r) {
+ usec_t ts;
+
+ assert(r);
+
+ if (r->interval <= 0 || r->burst <= 0)
+ return true;
+
+ ts = now(CLOCK_MONOTONIC);
+
+ if (r->begin <= 0 ||
+ r->begin + r->interval < ts) {
+ r->begin = ts;
+
+ /* Reset counter */
+ r->num = 0;
+ goto good;
+ }
+
+ if (r->num < r->burst)
+ goto good;
+
+ return false;
+
+good:
+ r->num++;
+ return true;
+}
diff --git a/src/basic/ratelimit.h b/src/basic/ratelimit.h
new file mode 100644
index 0000000..58efca7
--- /dev/null
+++ b/src/basic/ratelimit.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "util.h"
+
+typedef struct RateLimit {
+ usec_t interval;
+ usec_t begin;
+ unsigned burst;
+ unsigned num;
+} RateLimit;
+
+#define RATELIMIT_DEFINE(_name, _interval, _burst) \
+ RateLimit _name = { \
+ .interval = (_interval), \
+ .burst = (_burst), \
+ .num = 0, \
+ .begin = 0 \
+ }
+
+#define RATELIMIT_INIT(v, _interval, _burst) \
+ do { \
+ RateLimit *_r = &(v); \
+ _r->interval = (_interval); \
+ _r->burst = (_burst); \
+ _r->num = 0; \
+ _r->begin = 0; \
+ } while (false)
+
+#define RATELIMIT_RESET(v) \
+ do { \
+ RateLimit *_r = &(v); \
+ _r->num = 0; \
+ _r->begin = 0; \
+ } while (false)
+
+bool ratelimit_test(RateLimit *r);
diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h
new file mode 100644
index 0000000..0502c20
--- /dev/null
+++ b/src/basic/refcnt.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/>.
+***/
+
+/* A type-safe atomic refcounter */
+
+typedef struct {
+ volatile unsigned _value;
+} RefCount;
+
+#define REFCNT_GET(r) ((r)._value)
+#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
+#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
+
+#define REFCNT_INIT ((RefCount) { ._value = 1 })
diff --git a/src/basic/replace-var.c b/src/basic/replace-var.c
new file mode 100644
index 0000000..478fc43
--- /dev/null
+++ b/src/basic/replace-var.c
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 <string.h>
+
+#include "macro.h"
+#include "util.h"
+#include "replace-var.h"
+#include "def.h"
+
+/*
+ * Generic infrastructure for replacing @FOO@ style variables in
+ * strings. Will call a callback for each replacement.
+ */
+
+static int get_variable(const char *b, char **r) {
+ size_t k;
+ char *t;
+
+ assert(b);
+ assert(r);
+
+ if (*b != '@')
+ return 0;
+
+ k = strspn(b + 1, UPPERCASE_LETTERS "_");
+ if (k <= 0 || b[k+1] != '@')
+ return 0;
+
+ t = strndup(b + 1, k);
+ if (!t)
+ return -ENOMEM;
+
+ *r = t;
+ return 1;
+}
+
+char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) {
+ char *r, *t;
+ const char *f;
+ size_t l;
+
+ assert(text);
+ assert(lookup);
+
+ l = strlen(text);
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ f = text;
+ t = r;
+ while (*f) {
+ _cleanup_free_ char *v = NULL, *n = NULL;
+ char *a;
+ int k;
+ size_t skip, d, nl;
+
+ k = get_variable(f, &v);
+ if (k < 0)
+ goto oom;
+ if (k == 0) {
+ *(t++) = *(f++);
+ continue;
+ }
+
+ n = lookup(v, userdata);
+ if (!n)
+ goto oom;
+
+ skip = strlen(v) + 2;
+
+ d = t - r;
+ nl = l - skip + strlen(n);
+ a = realloc(r, nl + 1);
+ if (!a)
+ goto oom;
+
+ l = nl;
+ r = a;
+ t = r + d;
+
+ t = stpcpy(t, n);
+ f += skip;
+ }
+
+ *t = 0;
+ return r;
+
+oom:
+ free(r);
+ return NULL;
+}
diff --git a/src/basic/replace-var.h b/src/basic/replace-var.h
new file mode 100644
index 0000000..7eaee93
--- /dev/null
+++ b/src/basic/replace-var.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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/>.
+***/
+
+char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata);
diff --git a/src/basic/ring.c b/src/basic/ring.c
new file mode 100644
index 0000000..6814918
--- /dev/null
+++ b/src/basic/ring.c
@@ -0,0 +1,209 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
+
+ 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include "macro.h"
+#include "ring.h"
+
+#define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1))
+
+void ring_flush(Ring *r) {
+ assert(r);
+
+ r->start = 0;
+ r->used = 0;
+}
+
+void ring_clear(Ring *r) {
+ assert(r);
+
+ free(r->buf);
+ zero(*r);
+}
+
+/*
+ * Get data pointers for current ring-buffer data. @vec must be an array of 2
+ * iovec objects. They are filled according to the data available in the
+ * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
+ * that were filled (0 meaning buffer is empty).
+ *
+ * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
+ * struct iovec {
+ * void *iov_base;
+ * size_t iov_len;
+ * };
+ */
+size_t ring_peek(Ring *r, struct iovec *vec) {
+ assert(r);
+
+ if (r->used == 0) {
+ return 0;
+ } else if (r->start + r->used <= r->size) {
+ if (vec) {
+ vec[0].iov_base = &r->buf[r->start];
+ vec[0].iov_len = r->used;
+ }
+ return 1;
+ } else {
+ if (vec) {
+ vec[0].iov_base = &r->buf[r->start];
+ vec[0].iov_len = r->size - r->start;
+ vec[1].iov_base = r->buf;
+ vec[1].iov_len = r->used - (r->size - r->start);
+ }
+ return 2;
+ }
+}
+
+/*
+ * Copy data from the ring buffer into the linear external buffer @buf. Copy
+ * at most @size bytes. If the ring buffer size is smaller, copy less bytes and
+ * return the number of bytes copied.
+ */
+size_t ring_copy(Ring *r, void *buf, size_t size) {
+ size_t l;
+
+ assert(r);
+ assert(buf);
+
+ if (size > r->used)
+ size = r->used;
+
+ if (size > 0) {
+ l = r->size - r->start;
+ if (size <= l) {
+ memcpy(buf, &r->buf[r->start], size);
+ } else {
+ memcpy(buf, &r->buf[r->start], l);
+ memcpy((uint8_t*)buf + l, r->buf, size - l);
+ }
+ }
+
+ return size;
+}
+
+/*
+ * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
+ * ring operations will behave incorrectly.
+ */
+static int ring_resize(Ring *r, size_t nsize) {
+ uint8_t *buf;
+ size_t l;
+
+ assert(r);
+ assert(nsize > 0);
+
+ buf = malloc(nsize);
+ if (!buf)
+ return -ENOMEM;
+
+ if (r->used > 0) {
+ l = r->size - r->start;
+ if (r->used <= l) {
+ memcpy(buf, &r->buf[r->start], r->used);
+ } else {
+ memcpy(buf, &r->buf[r->start], l);
+ memcpy(&buf[l], r->buf, r->used - l);
+ }
+ }
+
+ free(r->buf);
+ r->buf = buf;
+ r->size = nsize;
+ r->start = 0;
+
+ return 0;
+}
+
+/*
+ * Resize ring-buffer to provide enough room for @add bytes of new data. This
+ * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
+ * success.
+ */
+static int ring_grow(Ring *r, size_t add) {
+ size_t need;
+
+ assert(r);
+
+ if (r->size - r->used >= add)
+ return 0;
+
+ need = r->used + add;
+ if (need <= r->used)
+ return -ENOMEM;
+ else if (need < 4096)
+ need = 4096;
+
+ need = ALIGN_POWER2(need);
+ if (need == 0)
+ return -ENOMEM;
+
+ return ring_resize(r, need);
+}
+
+/*
+ * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
+ * is too small. -ENOMEM is returned on OOM, 0 on success.
+ */
+int ring_push(Ring *r, const void *u8, size_t size) {
+ int err;
+ size_t pos, l;
+
+ assert(r);
+ assert(u8);
+
+ if (size == 0)
+ return 0;
+
+ err = ring_grow(r, size);
+ if (err < 0)
+ return err;
+
+ pos = RING_MASK(r, r->start + r->used);
+ l = r->size - pos;
+ if (l >= size) {
+ memcpy(&r->buf[pos], u8, size);
+ } else {
+ memcpy(&r->buf[pos], u8, l);
+ memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
+ }
+
+ r->used += size;
+
+ return 0;
+}
+
+/*
+ * Remove @len bytes from the start of the ring-buffer. Note that we protect
+ * against overflows so removing more bytes than available is safe.
+ */
+void ring_pull(Ring *r, size_t size) {
+ assert(r);
+
+ if (size > r->used)
+ size = r->used;
+
+ r->start = RING_MASK(r, r->start + size);
+ r->used -= size;
+}
diff --git a/src/basic/ring.h b/src/basic/ring.h
new file mode 100644
index 0000000..a7c44d1
--- /dev/null
+++ b/src/basic/ring.h
@@ -0,0 +1,56 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
+
+ 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/>.
+***/
+
+
+typedef struct Ring Ring;
+
+struct Ring {
+ uint8_t *buf; /* buffer or NULL */
+ size_t size; /* actual size of @buf */
+ size_t start; /* start position of ring */
+ size_t used; /* number of actually used bytes */
+};
+
+/* flush buffer so it is empty again */
+void ring_flush(Ring *r);
+
+/* flush buffer, free allocated data and reset to initial state */
+void ring_clear(Ring *r);
+
+/* get pointers to buffer data and their length */
+size_t ring_peek(Ring *r, struct iovec *vec);
+
+/* copy data into external linear buffer */
+size_t ring_copy(Ring *r, void *buf, size_t size);
+
+/* push data to the end of the buffer */
+int ring_push(Ring *r, const void *u8, size_t size);
+
+/* pull data from the front of the buffer */
+void ring_pull(Ring *r, size_t size);
+
+/* return size of occupied buffer in bytes */
+static inline size_t ring_get_size(Ring *r)
+{
+ return r->used;
+}
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
new file mode 100644
index 0000000..bafd483
--- /dev/null
+++ b/src/basic/rm-rf.c
@@ -0,0 +1,224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "util.h"
+#include "path-util.h"
+#include "btrfs-util.h"
+#include "rm-rf.h"
+
+int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ int ret = 0, r;
+
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on. This closes the passed fd. */
+
+ if (!(flags & REMOVE_PHYSICAL)) {
+
+ r = fd_is_temporary_fs(fd);
+ if (r < 0) {
+ safe_close(fd);
+ return r;
+ }
+
+ if (!r) {
+ /* We refuse to clean physical file systems
+ * with this call, unless explicitly
+ * requested. This is extra paranoia just to
+ * be sure we never ever remove non-state
+ * data */
+
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ safe_close(fd);
+ return -EPERM;
+ }
+ }
+
+ d = fdopendir(fd);
+ if (!d) {
+ safe_close(fd);
+ return errno == ENOENT ? 0 : -errno;
+ }
+
+ for (;;) {
+ struct dirent *de;
+ bool is_dir;
+ struct stat st;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de) {
+ if (errno != 0 && ret == 0)
+ ret = -errno;
+ return ret;
+ }
+
+ if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ continue;
+
+ if (de->d_type == DT_UNKNOWN ||
+ (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ is_dir = S_ISDIR(st.st_mode);
+ } else
+ is_dir = de->d_type == DT_DIR;
+
+ if (is_dir) {
+ int subdir_fd;
+
+ /* if root_dev is set, remove subdirectories only if device is same */
+ if (root_dev && st.st_dev != root_dev->st_dev)
+ continue;
+
+ subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (subdir_fd < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ /* Stop at mount points */
+ r = fd_is_mount_point(fd, de->d_name, 0);
+ if (r < 0) {
+ if (ret == 0 && r != -ENOENT)
+ ret = r;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+ if (r) {
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
+
+ /* This could be a subvolume, try to remove it */
+
+ r = btrfs_subvol_remove_fd(fd, de->d_name, true);
+ if (r < 0) {
+ if (r != -ENOTTY && r != -EINVAL) {
+ if (ret == 0)
+ ret = r;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ /* ENOTTY, then it wasn't a
+ * btrfs subvolume, continue
+ * below. */
+ } else {
+ /* It was a subvolume, continue. */
+ safe_close(subdir_fd);
+ continue;
+ }
+ }
+
+ /* We pass REMOVE_PHYSICAL here, to avoid
+ * doing the fstatfs() to check the file
+ * system type again for each directory */
+ r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
+ if (r < 0 && ret == 0)
+ ret = r;
+
+ if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+
+ } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+
+ if (unlinkat(fd, de->d_name, 0) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+ }
+ }
+}
+
+int rm_rf(const char *path, RemoveFlags flags) {
+ int fd, r;
+ struct statfs s;
+
+ assert(path);
+
+ /* We refuse to clean the root file system with this
+ * call. This is extra paranoia to never cause a really
+ * seriously broken system. */
+ if (path_equal(path, "/")) {
+ log_error("Attempted to remove entire root file system, and we can't allow that.");
+ return -EPERM;
+ }
+
+ if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
+ /* Try to remove as subvolume first */
+ r = btrfs_subvol_remove(path, true);
+ if (r >= 0)
+ return r;
+
+ if (r != -ENOTTY && r != -EINVAL)
+ return r;
+
+ /* Not btrfs or not a subvolume */
+ }
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (fd < 0) {
+
+ if (errno != ENOTDIR && errno != ELOOP)
+ return -errno;
+
+ if (!(flags & REMOVE_PHYSICAL)) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (!is_temporary_fs(&s)) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ return -EPERM;
+ }
+ }
+
+ if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
+ if (unlink(path) < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+ }
+
+ r = rm_rf_children(fd, flags, NULL);
+
+ if (flags & REMOVE_ROOT) {
+ if (rmdir(path) < 0) {
+ if (r == 0 && errno != ENOENT)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
new file mode 100644
index 0000000..96579eb
--- /dev/null
+++ b/src/basic/rm-rf.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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/stat.h>
+
+typedef enum RemoveFlags {
+ REMOVE_ONLY_DIRECTORIES = 1,
+ REMOVE_ROOT = 2,
+ REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */
+ REMOVE_SUBVOLUME = 8,
+} RemoveFlags;
+
+int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
+int rm_rf(const char *path, RemoveFlags flags);
diff --git a/src/basic/securebits.h b/src/basic/securebits.h
new file mode 100644
index 0000000..98fbe0d
--- /dev/null
+++ b/src/basic/securebits.h
@@ -0,0 +1,45 @@
+#ifndef _LINUX_SECUREBITS_H
+#define _LINUX_SECUREBITS_H 1
+
+/* This is minimal version of Linux' linux/securebits.h header file,
+ * which is licensed GPL2 */
+
+#define SECUREBITS_DEFAULT 0x00000000
+
+/* When set UID 0 has no special privileges. When unset, we support
+ inheritance of root-permissions and suid-root executable under
+ compatibility mode. We raise the effective and inheritable bitmasks
+ *of the executable file* if the effective uid of the new process is
+ 0. If the real uid is 0, we raise the effective (legacy) bit of the
+ executable file. */
+#define SECURE_NOROOT 0
+#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */
+
+/* When set, setuid to/from uid 0 does not trigger capability-"fixup".
+ When unset, to provide compatibility with old programs relying on
+ set*uid to gain/lose privilege, transitions to/from uid 0 cause
+ capabilities to be gained/lost. */
+#define SECURE_NO_SETUID_FIXUP 2
+#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */
+
+/* When set, a process can retain its capabilities even after
+ transitioning to a non-root user (the set-uid fixup suppressed by
+ bit 2). Bit-4 is cleared when a process calls exec(); setting both
+ bit 4 and 5 will create a barrier through exec that no exec()'d
+ child can use this feature again. */
+#define SECURE_KEEP_CAPS 4
+#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */
+
+/* Each securesetting is implemented using two bits. One bit specifies
+ whether the setting is on or off. The other bit specify whether the
+ setting is locked or not. A setting which is locked cannot be
+ changed from user-level. */
+#define issecure_mask(X) (1 << (X))
+#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
+
+#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
+ issecure_mask(SECURE_NO_SETUID_FIXUP) | \
+ issecure_mask(SECURE_KEEP_CAPS))
+#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
+
+#endif /* !_LINUX_SECUREBITS_H */
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
new file mode 100644
index 0000000..7c58985
--- /dev/null
+++ b/src/basic/selinux-util.c
@@ -0,0 +1,462 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <errno.h>
+#include <malloc.h>
+#include <sys/un.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/context.h>
+#endif
+
+#include "strv.h"
+#include "path-util.h"
+#include "selinux-util.h"
+
+#ifdef HAVE_SELINUX
+DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
+DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
+
+#define _cleanup_security_context_free_ _cleanup_(freeconp)
+#define _cleanup_context_free_ _cleanup_(context_freep)
+
+static int cached_use = -1;
+static struct selabel_handle *label_hnd = NULL;
+
+#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
+#endif
+
+bool mac_selinux_use(void) {
+#ifdef HAVE_SELINUX
+ if (cached_use < 0)
+ cached_use = is_selinux_enabled() > 0;
+
+ return cached_use;
+#else
+ return false;
+#endif
+}
+
+void mac_selinux_retest(void) {
+#ifdef HAVE_SELINUX
+ cached_use = -1;
+#endif
+}
+
+int mac_selinux_init(const char *prefix) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ usec_t before_timestamp, after_timestamp;
+ struct mallinfo before_mallinfo, after_mallinfo;
+
+ if (!mac_selinux_use())
+ return 0;
+
+ if (label_hnd)
+ return 0;
+
+ before_mallinfo = mallinfo();
+ before_timestamp = now(CLOCK_MONOTONIC);
+
+ if (prefix) {
+ struct selinux_opt options[] = {
+ { .type = SELABEL_OPT_SUBSET, .value = prefix },
+ };
+
+ label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
+ } else
+ label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+
+ if (!label_hnd) {
+ log_enforcing("Failed to initialize SELinux context: %m");
+ r = security_getenforce() == 1 ? -errno : 0;
+ } else {
+ char timespan[FORMAT_TIMESPAN_MAX];
+ int l;
+
+ after_timestamp = now(CLOCK_MONOTONIC);
+ after_mallinfo = mallinfo();
+
+ l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
+
+ log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
+ format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
+ (l+1023)/1024);
+ }
+#endif
+
+ return r;
+}
+
+void mac_selinux_finish(void) {
+
+#ifdef HAVE_SELINUX
+ if (!label_hnd)
+ return;
+
+ selabel_close(label_hnd);
+ label_hnd = NULL;
+#endif
+}
+
+int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+
+#ifdef HAVE_SELINUX
+ struct stat st;
+ int r;
+
+ assert(path);
+
+ /* if mac_selinux_init() wasn't called before we are a NOOP */
+ if (!label_hnd)
+ return 0;
+
+ r = lstat(path, &st);
+ if (r >= 0) {
+ _cleanup_security_context_free_ security_context_t fcon = NULL;
+
+ r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
+
+ /* If there's no label to set, then exit without warning */
+ if (r < 0 && errno == ENOENT)
+ return 0;
+
+ if (r >= 0) {
+ r = lsetfilecon(path, fcon);
+
+ /* If the FS doesn't support labels, then exit without warning */
+ if (r < 0 && errno == EOPNOTSUPP)
+ return 0;
+ }
+ }
+
+ if (r < 0) {
+ /* Ignore ENOENT in some cases */
+ if (ignore_enoent && errno == ENOENT)
+ return 0;
+
+ if (ignore_erofs && errno == EROFS)
+ return 0;
+
+ log_enforcing("Unable to fix SELinux security context of %s: %m", path);
+ if (security_getenforce() == 1)
+ return -errno;
+ }
+#endif
+
+ return 0;
+}
+
+int mac_selinux_apply(const char *path, const char *label) {
+
+#ifdef HAVE_SELINUX
+ assert(path);
+ assert(label);
+
+ if (!mac_selinux_use())
+ return 0;
+
+ if (setfilecon(path, (security_context_t) label) < 0) {
+ log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
+ if (security_getenforce() == 1)
+ return -errno;
+ }
+#endif
+ return 0;
+}
+
+int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
+ int r = -EOPNOTSUPP;
+
+#ifdef HAVE_SELINUX
+ _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
+ security_class_t sclass;
+
+ assert(exe);
+ assert(label);
+
+ if (!mac_selinux_use())
+ return -EOPNOTSUPP;
+
+ r = getcon(&mycon);
+ if (r < 0)
+ return -errno;
+
+ r = getfilecon(exe, &fcon);
+ if (r < 0)
+ return -errno;
+
+ sclass = string_to_security_class("process");
+ r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+int mac_selinux_get_our_label(char **label) {
+ int r = -EOPNOTSUPP;
+
+ assert(label);
+
+#ifdef HAVE_SELINUX
+ if (!mac_selinux_use())
+ return -EOPNOTSUPP;
+
+ r = getcon(label);
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
+ int r = -EOPNOTSUPP;
+
+#ifdef HAVE_SELINUX
+ _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
+ _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
+ security_class_t sclass;
+ const char *range = NULL;
+
+ assert(socket_fd >= 0);
+ assert(exe);
+ assert(label);
+
+ if (!mac_selinux_use())
+ return -EOPNOTSUPP;
+
+ r = getcon(&mycon);
+ if (r < 0)
+ return -errno;
+
+ r = getpeercon(socket_fd, &peercon);
+ if (r < 0)
+ return -errno;
+
+ if (!exec_label) {
+ /* If there is no context set for next exec let's use context
+ of target executable */
+ r = getfilecon(exe, &fcon);
+ if (r < 0)
+ return -errno;
+ }
+
+ bcon = context_new(mycon);
+ if (!bcon)
+ return -ENOMEM;
+
+ pcon = context_new(peercon);
+ if (!pcon)
+ return -ENOMEM;
+
+ range = context_range_get(pcon);
+ if (!range)
+ return -errno;
+
+ r = context_range_set(bcon, range);
+ if (r)
+ return -errno;
+
+ freecon(mycon);
+ mycon = strdup(context_str(bcon));
+ if (!mycon)
+ return -ENOMEM;
+
+ sclass = string_to_security_class("process");
+ r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+void mac_selinux_free(char *label) {
+
+#ifdef HAVE_SELINUX
+ if (!mac_selinux_use())
+ return;
+
+ freecon((security_context_t) label);
+#endif
+}
+
+int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ _cleanup_security_context_free_ security_context_t filecon = NULL;
+
+ assert(path);
+
+ if (!label_hnd)
+ return 0;
+
+ if (path_is_absolute(path))
+ r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
+ else {
+ _cleanup_free_ char *newpath;
+
+ newpath = path_make_absolute_cwd(path);
+ if (!newpath)
+ return -ENOMEM;
+
+ r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
+ }
+
+ /* No context specified by the policy? Proceed without setting it. */
+ if (r < 0 && errno == ENOENT)
+ return 0;
+
+ if (r < 0)
+ r = -errno;
+ else {
+ r = setfscreatecon(filecon);
+ if (r < 0) {
+ log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
+ r = -errno;
+ }
+ }
+
+ if (r < 0 && security_getenforce() == 0)
+ r = 0;
+#endif
+
+ return r;
+}
+
+void mac_selinux_create_file_clear(void) {
+
+#ifdef HAVE_SELINUX
+ PROTECT_ERRNO;
+
+ if (!mac_selinux_use())
+ return;
+
+ setfscreatecon(NULL);
+#endif
+}
+
+int mac_selinux_create_socket_prepare(const char *label) {
+
+#ifdef HAVE_SELINUX
+ if (!mac_selinux_use())
+ return 0;
+
+ assert(label);
+
+ if (setsockcreatecon((security_context_t) label) < 0) {
+ log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
+
+ if (security_getenforce() == 1)
+ return -errno;
+ }
+#endif
+
+ return 0;
+}
+
+void mac_selinux_create_socket_clear(void) {
+
+#ifdef HAVE_SELINUX
+ PROTECT_ERRNO;
+
+ if (!mac_selinux_use())
+ return;
+
+ setsockcreatecon(NULL);
+#endif
+}
+
+int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
+
+ /* Binds a socket and label its file system object according to the SELinux policy */
+
+#ifdef HAVE_SELINUX
+ _cleanup_security_context_free_ security_context_t fcon = NULL;
+ const struct sockaddr_un *un;
+ char *path;
+ int r;
+
+ assert(fd >= 0);
+ assert(addr);
+ assert(addrlen >= sizeof(sa_family_t));
+
+ if (!label_hnd)
+ goto skipped;
+
+ /* Filter out non-local sockets */
+ if (addr->sa_family != AF_UNIX)
+ goto skipped;
+
+ /* Filter out anonymous sockets */
+ if (addrlen < sizeof(sa_family_t) + 1)
+ goto skipped;
+
+ /* Filter out abstract namespace sockets */
+ un = (const struct sockaddr_un*) addr;
+ if (un->sun_path[0] == 0)
+ goto skipped;
+
+ path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
+
+ if (path_is_absolute(path))
+ r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
+ else {
+ _cleanup_free_ char *newpath;
+
+ newpath = path_make_absolute_cwd(path);
+ if (!newpath)
+ return -ENOMEM;
+
+ r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
+ }
+
+ if (r == 0)
+ r = setfscreatecon(fcon);
+
+ if (r < 0 && errno != ENOENT) {
+ log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
+
+ if (security_getenforce() == 1) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = bind(fd, addr, addrlen);
+ if (r < 0)
+ r = -errno;
+
+finish:
+ setfscreatecon(NULL);
+ return r;
+
+skipped:
+#endif
+ return bind(fd, addr, addrlen) < 0 ? -errno : 0;
+}
diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
new file mode 100644
index 0000000..8467185
--- /dev/null
+++ b/src/basic/selinux-util.h
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/socket.h>
+#include <stdbool.h>
+
+bool mac_selinux_use(void);
+void mac_selinux_retest(void);
+
+int mac_selinux_init(const char *prefix);
+void mac_selinux_finish(void);
+
+int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
+int mac_selinux_apply(const char *path, const char *label);
+
+int mac_selinux_get_create_label_from_exe(const char *exe, char **label);
+int mac_selinux_get_our_label(char **label);
+int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label);
+void mac_selinux_free(char *label);
+
+int mac_selinux_create_file_prepare(const char *path, mode_t mode);
+void mac_selinux_create_file_clear(void);
+
+int mac_selinux_create_socket_prepare(const char *label);
+void mac_selinux_create_socket_clear(void);
+
+int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
diff --git a/src/basic/set.h b/src/basic/set.h
new file mode 100644
index 0000000..4dffecd
--- /dev/null
+++ b/src/basic/set.h
@@ -0,0 +1,134 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "hashmap.h"
+#include "macro.h"
+
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+
+static inline void set_free(Set *s) {
+ internal_hashmap_free(HASHMAP_BASE(s));
+}
+
+static inline void set_free_free(Set *s) {
+ internal_hashmap_free_free(HASHMAP_BASE(s));
+}
+
+/* no set_free_free_free */
+
+static inline Set *set_copy(Set *s) {
+ return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+
+int set_put(Set *s, const void *key);
+/* no set_update */
+/* no set_replace */
+static inline void *set_get(Set *s, void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(s), key);
+}
+/* no set_get2 */
+
+static inline bool set_contains(Set *s, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(s), key);
+}
+
+static inline void *set_remove(Set *s, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(s), key);
+}
+
+/* no set_remove2 */
+/* no set_remove_value */
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
+/* no set_remove_and_replace */
+int set_merge(Set *s, Set *other);
+
+static inline int set_reserve(Set *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+static inline int set_move(Set *s, Set *other) {
+ return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
+}
+
+static inline int set_move_one(Set *s, Set *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
+}
+
+static inline unsigned set_size(Set *s) {
+ return internal_hashmap_size(HASHMAP_BASE(s));
+}
+
+static inline bool set_isempty(Set *s) {
+ return set_size(s) == 0;
+}
+
+static inline unsigned set_buckets(Set *s) {
+ return internal_hashmap_buckets(HASHMAP_BASE(s));
+}
+
+void *set_iterate(Set *s, Iterator *i);
+
+static inline void set_clear(Set *s) {
+ internal_hashmap_clear(HASHMAP_BASE(s));
+}
+
+static inline void set_clear_free(Set *s) {
+ internal_hashmap_clear_free(HASHMAP_BASE(s));
+}
+
+/* no set_clear_free_free */
+
+static inline void *set_steal_first(Set *s) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(s));
+}
+
+/* no set_steal_first_key */
+/* no set_first_key */
+
+static inline void *set_first(Set *s) {
+ return internal_hashmap_first(HASHMAP_BASE(s));
+}
+
+/* no set_next */
+
+static inline char **set_get_strv(Set *s) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(s));
+}
+
+int set_consume(Set *s, void *value);
+int set_put_strdup(Set *s, const char *p);
+int set_put_strdupv(Set *s, char **l);
+
+#define SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
+
+#define _cleanup_set_free_ _cleanup_(set_freep)
+#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c
new file mode 100644
index 0000000..0108603
--- /dev/null
+++ b/src/basic/sigbus.c
@@ -0,0 +1,152 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 <signal.h>
+#include <sys/mman.h>
+
+#include "macro.h"
+#include "util.h"
+#include "sigbus.h"
+
+#define SIGBUS_QUEUE_MAX 64
+
+static struct sigaction old_sigaction;
+static unsigned n_installed = 0;
+
+/* We maintain a fixed size list of page addresses that triggered a
+ SIGBUS. We access with list with atomic operations, so that we
+ don't have to deal with locks between signal handler and main
+ programs in possibly multiple threads. */
+
+static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX];
+static volatile sig_atomic_t n_sigbus_queue = 0;
+
+static void sigbus_push(void *addr) {
+ unsigned u;
+
+ assert(addr);
+
+ /* Find a free place, increase the number of entries and leave, if we can */
+ for (u = 0; u < SIGBUS_QUEUE_MAX; u++)
+ if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
+ __sync_fetch_and_add(&n_sigbus_queue, 1);
+ return;
+ }
+
+ /* If we can't, make sure the queue size is out of bounds, to
+ * mark it as overflow */
+ for (;;) {
+ unsigned c;
+
+ __sync_synchronize();
+ c = n_sigbus_queue;
+
+ if (c > SIGBUS_QUEUE_MAX) /* already overflow */
+ return;
+
+ if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX))
+ return;
+ }
+}
+
+int sigbus_pop(void **ret) {
+ assert(ret);
+
+ for (;;) {
+ unsigned u, c;
+
+ __sync_synchronize();
+ c = n_sigbus_queue;
+
+ if (_likely_(c == 0))
+ return 0;
+
+ if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
+ return -EOVERFLOW;
+
+ for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
+ void *addr;
+
+ addr = sigbus_queue[u];
+ if (!addr)
+ continue;
+
+ if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
+ __sync_fetch_and_sub(&n_sigbus_queue, 1);
+ *ret = addr;
+ return 1;
+ }
+ }
+ }
+}
+
+static void sigbus_handler(int sn, siginfo_t *si, void *data) {
+ unsigned long ul;
+ void *aligned;
+
+ assert(sn == SIGBUS);
+ assert(si);
+
+ if (si->si_code != BUS_ADRERR || !si->si_addr) {
+ assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
+ raise(SIGBUS);
+ return;
+ }
+
+ ul = (unsigned long) si->si_addr;
+ ul = ul / page_size();
+ ul = ul * page_size();
+ aligned = (void*) ul;
+
+ /* Let's remember which address failed */
+ sigbus_push(aligned);
+
+ /* Replace mapping with an anonymous page, so that the
+ * execution can continue, however with a zeroed out page */
+ assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned);
+}
+
+void sigbus_install(void) {
+ struct sigaction sa = {
+ .sa_sigaction = sigbus_handler,
+ .sa_flags = SA_SIGINFO,
+ };
+
+ n_installed++;
+
+ if (n_installed == 1)
+ assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0);
+
+ return;
+}
+
+void sigbus_reset(void) {
+
+ if (n_installed <= 0)
+ return;
+
+ n_installed--;
+
+ if (n_installed == 0)
+ assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
+
+ return;
+}
diff --git a/src/basic/sigbus.h b/src/basic/sigbus.h
new file mode 100644
index 0000000..23edc6d
--- /dev/null
+++ b/src/basic/sigbus.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+#pragma once
+
+void sigbus_install(void);
+void sigbus_reset(void);
+
+int sigbus_pop(void **ret);
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
new file mode 100644
index 0000000..84cf42b
--- /dev/null
+++ b/src/basic/signal-util.c
@@ -0,0 +1,268 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "util.h"
+#include "signal-util.h"
+
+int reset_all_signal_handlers(void) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+ int sig, r = 0;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+
+ /* These two cannot be caught... */
+ if (sig == SIGKILL || sig == SIGSTOP)
+ continue;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL && r >= 0)
+ r = -errno;
+ }
+
+ return r;
+}
+
+int reset_signal_mask(void) {
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -errno;
+
+ if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) {
+ int r = 0;
+
+ /* negative signal ends the list. 0 signal is skipped. */
+
+ if (sig < 0)
+ return 0;
+
+ if (sig > 0) {
+ if (sigaction(sig, sa, NULL) < 0)
+ r = -errno;
+ }
+
+ while ((sig = va_arg(ap, int)) >= 0) {
+
+ if (sig == 0)
+ continue;
+
+ if (sigaction(sig, sa, NULL) < 0) {
+ if (r >= 0)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+ va_list ap;
+ int r;
+
+ va_start(ap, sa);
+ r = sigaction_many_ap(sa, 0, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int ignore_signals(int sig, ...) {
+
+ static const struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
+ va_list ap;
+ int r;
+
+ va_start(ap, sig);
+ r = sigaction_many_ap(&sa, sig, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int default_signals(int sig, ...) {
+
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+
+ va_list ap;
+ int r;
+
+ va_start(ap, sig);
+ r = sigaction_many_ap(&sa, sig, ap);
+ va_end(ap);
+
+ return r;
+}
+
+static int sigset_add_many_ap(sigset_t *ss, va_list ap) {
+ int sig, r = 0;
+
+ assert(ss);
+
+ while ((sig = va_arg(ap, int)) >= 0) {
+
+ if (sig == 0)
+ continue;
+
+ if (sigaddset(ss, sig) < 0) {
+ if (r >= 0)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
+
+int sigset_add_many(sigset_t *ss, ...) {
+ va_list ap;
+ int r;
+
+ va_start(ap, ss);
+ r = sigset_add_many_ap(ss, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int sigprocmask_many(int how, ...) {
+ va_list ap;
+ sigset_t ss;
+ int r;
+
+ if (sigemptyset(&ss) < 0)
+ return -errno;
+
+ va_start(ap, how);
+ r = sigset_add_many_ap(&ss, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return r;
+
+ if (sigprocmask(how, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static const char *const __signal_table[] = {
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
+ [SIGVTALRM] = "VTALRM",
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+ static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+ const char *name;
+
+ name = __signal_to_string(signo);
+ if (name)
+ return name;
+
+ if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+ snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+ else
+ snprintf(buf, sizeof(buf), "%d", signo);
+
+ return buf;
+}
+
+int signal_from_string(const char *s) {
+ int signo;
+ int offset = 0;
+ unsigned u;
+
+ signo = __signal_from_string(s);
+ if (signo > 0)
+ return signo;
+
+ if (startswith(s, "RTMIN+")) {
+ s += 6;
+ offset = SIGRTMIN;
+ }
+ if (safe_atou(s, &u) >= 0) {
+ signo = (int) u + offset;
+ if (signo > 0 && signo < _NSIG)
+ return signo;
+ }
+ return -EINVAL;
+}
+
+int signal_from_string_try_harder(const char *s) {
+ int signo;
+ assert(s);
+
+ signo = signal_from_string(s);
+ if (signo <= 0)
+ if (startswith(s, "SIG"))
+ return signal_from_string(s+3);
+
+ return signo;
+}
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
new file mode 100644
index 0000000..9dc8a28
--- /dev/null
+++ b/src/basic/signal-util.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 <signal.h>
+
+#include "macro.h"
+
+int reset_all_signal_handlers(void);
+int reset_signal_mask(void);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+int sigset_add_many(sigset_t *ss, ...);
+int sigprocmask_many(int how, ...);
+
+const char *signal_to_string(int i) _const_;
+int signal_from_string(const char *s) _pure_;
+
+int signal_from_string_try_harder(const char *s);
diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c
new file mode 100644
index 0000000..f68bd28
--- /dev/null
+++ b/src/basic/siphash24.c
@@ -0,0 +1,135 @@
+/*
+ SipHash reference C implementation
+
+ Written in 2012 by
+ Jean-Philippe Aumasson <jeanphilippe.aumasson at gmail.com>
+ Daniel J. Bernstein <djb at cr.yp.to>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+ (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
+*/
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "siphash24.h"
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint8_t u8;
+
+#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define U32TO8_LE(p, v) \
+ (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
+ (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
+
+#define U64TO8_LE(p, v) \
+ U32TO8_LE((p), (u32)((v) )); \
+ U32TO8_LE((p) + 4, (u32)((v) >> 32));
+
+#define U8TO64_LE(p) \
+ (((u64)((p)[0]) ) | \
+ ((u64)((p)[1]) << 8) | \
+ ((u64)((p)[2]) << 16) | \
+ ((u64)((p)[3]) << 24) | \
+ ((u64)((p)[4]) << 32) | \
+ ((u64)((p)[5]) << 40) | \
+ ((u64)((p)[6]) << 48) | \
+ ((u64)((p)[7]) << 56))
+
+#define SIPROUND \
+ do { \
+ v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+ v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
+ v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
+ v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+ } while(0)
+
+/* SipHash-2-4 */
+void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
+{
+ /* "somepseudorandomlygeneratedbytes" */
+ u64 v0 = 0x736f6d6570736575ULL;
+ u64 v1 = 0x646f72616e646f6dULL;
+ u64 v2 = 0x6c7967656e657261ULL;
+ u64 v3 = 0x7465646279746573ULL;
+ u64 b;
+ u64 k0 = U8TO64_LE( k );
+ u64 k1 = U8TO64_LE( k + 8 );
+ u64 m;
+ const u8 *in = _in;
+ const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
+ const int left = inlen & 7;
+ b = ( ( u64 )inlen ) << 56;
+ v3 ^= k1;
+ v2 ^= k0;
+ v1 ^= k1;
+ v0 ^= k0;
+
+ for ( ; in != end; in += 8 )
+ {
+ m = U8TO64_LE( in );
+#ifdef DEBUG
+ printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+ printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+ printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+ printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+ printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
+#endif
+ v3 ^= m;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= m;
+ }
+
+ switch( left )
+ {
+ case 7: b |= ( ( u64 )in[ 6] ) << 48;
+
+ case 6: b |= ( ( u64 )in[ 5] ) << 40;
+
+ case 5: b |= ( ( u64 )in[ 4] ) << 32;
+
+ case 4: b |= ( ( u64 )in[ 3] ) << 24;
+
+ case 3: b |= ( ( u64 )in[ 2] ) << 16;
+
+ case 2: b |= ( ( u64 )in[ 1] ) << 8;
+
+ case 1: b |= ( ( u64 )in[ 0] ); break;
+
+ case 0: break;
+ }
+
+#ifdef DEBUG
+ printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+ printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+ printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+ printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+ printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
+#endif
+ v3 ^= b;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= b;
+#ifdef DEBUG
+ printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+ printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+ printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+ printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+#endif
+ v2 ^= 0xff;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ b = v0 ^ v1 ^ v2 ^ v3;
+ U64TO8_LE( out, b );
+}
diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h
new file mode 100644
index 0000000..62e1168
--- /dev/null
+++ b/src/basic/siphash24.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
new file mode 100644
index 0000000..2e24b1e
--- /dev/null
+++ b/src/basic/smack-util.c
@@ -0,0 +1,208 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Intel Corporation
+
+ Author: Auke Kok <auke-jan.h.kok at intel.com>
+
+ 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/xattr.h>
+
+#include "util.h"
+#include "process-util.h"
+#include "path-util.h"
+#include "fileio.h"
+#include "smack-util.h"
+
+#define SMACK_FLOOR_LABEL "_"
+#define SMACK_STAR_LABEL "*"
+
+bool mac_smack_use(void) {
+#ifdef HAVE_SMACK
+ static int cached_use = -1;
+
+ if (cached_use < 0)
+ cached_use = access("/sys/fs/smackfs/", F_OK) >= 0;
+
+ return cached_use;
+#else
+ return false;
+#endif
+}
+
+int mac_smack_apply(const char *path, const char *label) {
+ int r = 0;
+
+ assert(path);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ if (label)
+ r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
+ else
+ r = lremovexattr(path, "security.SMACK64");
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+int mac_smack_apply_fd(int fd, const char *label) {
+ int r = 0;
+
+ assert(fd >= 0);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ if (label)
+ r = fsetxattr(fd, "security.SMACK64", label, strlen(label), 0);
+ else
+ r = fremovexattr(fd, "security.SMACK64");
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+int mac_smack_apply_ip_out_fd(int fd, const char *label) {
+ int r = 0;
+
+ assert(fd >= 0);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ if (label)
+ r = fsetxattr(fd, "security.SMACK64IPOUT", label, strlen(label), 0);
+ else
+ r = fremovexattr(fd, "security.SMACK64IPOUT");
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+int mac_smack_apply_ip_in_fd(int fd, const char *label) {
+ int r = 0;
+
+ assert(fd >= 0);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ if (label)
+ r = fsetxattr(fd, "security.SMACK64IPIN", label, strlen(label), 0);
+ else
+ r = fremovexattr(fd, "security.SMACK64IPIN");
+ if (r < 0)
+ return -errno;
+#endif
+
+ return r;
+}
+
+int mac_smack_apply_pid(pid_t pid, const char *label) {
+
+#ifdef HAVE_SMACK
+ const char *p;
+#endif
+ int r = 0;
+
+ assert(label);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ p = procfs_file_alloca(pid, "attr/current");
+ r = write_string_file(p, label);
+ if (r < 0)
+ return r;
+#endif
+
+ return r;
+}
+
+int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+
+#ifdef HAVE_SMACK
+ struct stat st;
+#endif
+ int r = 0;
+
+ assert(path);
+
+#ifdef HAVE_SMACK
+ if (!mac_smack_use())
+ return 0;
+
+ /*
+ * Path must be in /dev and must exist
+ */
+ if (!path_startswith(path, "/dev"))
+ return 0;
+
+ r = lstat(path, &st);
+ if (r >= 0) {
+ const char *label;
+
+ /*
+ * Label directories and character devices "*".
+ * Label symlinks "_".
+ * Don't change anything else.
+ */
+
+ if (S_ISDIR(st.st_mode))
+ label = SMACK_STAR_LABEL;
+ else if (S_ISLNK(st.st_mode))
+ label = SMACK_FLOOR_LABEL;
+ else if (S_ISCHR(st.st_mode))
+ label = SMACK_STAR_LABEL;
+ else
+ return 0;
+
+ r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
+
+ /* If the FS doesn't support labels, then exit without warning */
+ if (r < 0 && errno == EOPNOTSUPP)
+ return 0;
+ }
+
+ if (r < 0) {
+ /* Ignore ENOENT in some cases */
+ if (ignore_enoent && errno == ENOENT)
+ return 0;
+
+ if (ignore_erofs && errno == EROFS)
+ return 0;
+
+ r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path);
+ }
+#endif
+
+ return r;
+}
diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h
new file mode 100644
index 0000000..50f55b1
--- /dev/null
+++ b/src/basic/smack-util.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Intel Corporation
+
+ Author: Auke Kok <auke-jan.h.kok at intel.com>
+
+ 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 <stdbool.h>
+
+bool mac_smack_use(void);
+
+int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
+
+int mac_smack_apply(const char *path, const char *label);
+int mac_smack_apply_fd(int fd, const char *label);
+int mac_smack_apply_pid(pid_t pid, const char *label);
+int mac_smack_apply_ip_in_fd(int fd, const char *label);
+int mac_smack_apply_ip_out_fd(int fd, const char *label);
diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c
new file mode 100644
index 0000000..cbe3ff2
--- /dev/null
+++ b/src/basic/socket-label.c
@@ -0,0 +1,164 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stddef.h>
+
+#include "macro.h"
+#include "util.h"
+#include "mkdir.h"
+#include "missing.h"
+#include "selinux-util.h"
+#include "socket-util.h"
+
+int socket_address_listen(
+ const SocketAddress *a,
+ int flags,
+ int backlog,
+ SocketAddressBindIPv6Only only,
+ const char *bind_to_device,
+ bool free_bind,
+ bool transparent,
+ mode_t directory_mode,
+ mode_t socket_mode,
+ const char *label) {
+
+ _cleanup_close_ int fd = -1;
+ int r, one;
+
+ assert(a);
+
+ r = socket_address_verify(a);
+ if (r < 0)
+ return r;
+
+ if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
+ return -EAFNOSUPPORT;
+
+ if (label) {
+ r = mac_selinux_create_socket_prepare(label);
+ if (r < 0)
+ return r;
+ }
+
+ fd = socket(socket_address_family(a), a->type | flags, a->protocol);
+ r = fd < 0 ? -errno : 0;
+
+ if (label)
+ mac_selinux_create_socket_clear();
+
+ if (r < 0)
+ return r;
+
+ if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
+ int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
+ return -errno;
+ }
+
+ if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
+ if (bind_to_device)
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
+ return -errno;
+
+ if (free_bind) {
+ one = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
+ log_warning_errno(errno, "IP_FREEBIND failed: %m");
+ }
+
+ if (transparent) {
+ one = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
+ log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
+ }
+ }
+
+ one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+ return -errno;
+
+ if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
+ mode_t old_mask;
+
+ /* Create parents */
+ mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
+
+ /* Enforce the right access mode for the socket */
+ old_mask = umask(~ socket_mode);
+
+ r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
+
+ if (r < 0 && errno == EADDRINUSE) {
+ /* Unlink and try again */
+ unlink(a->sockaddr.un.sun_path);
+ r = bind(fd, &a->sockaddr.sa, a->size);
+ }
+
+ umask(old_mask);
+ } else
+ r = bind(fd, &a->sockaddr.sa, a->size);
+
+ if (r < 0)
+ return -errno;
+
+ if (socket_address_can_accept(a))
+ if (listen(fd, backlog) < 0)
+ return -errno;
+
+ r = fd;
+ fd = -1;
+
+ return r;
+}
+
+int make_socket_fd(int log_level, const char* address, int flags) {
+ SocketAddress a;
+ int fd, r;
+
+ r = socket_address_parse(&a, address);
+ if (r < 0) {
+ log_error("Failed to parse socket address \"%s\": %s",
+ address, strerror(-r));
+ return r;
+ }
+
+ fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
+ NULL, false, false, 0755, 0644, NULL);
+ if (fd < 0 || log_get_max_level() >= log_level) {
+ _cleanup_free_ char *p = NULL;
+
+ r = socket_address_print(&a, &p);
+ if (r < 0)
+ return log_error_errno(r, "socket_address_print(): %m");
+
+ if (fd < 0)
+ log_error_errno(fd, "Failed to listen on %s: %m", p);
+ else
+ log_full(log_level, "Listening on %s", p);
+ }
+
+ return fd;
+}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
new file mode 100644
index 0000000..e8bb10d
--- /dev/null
+++ b/src/basic/socket-util.c
@@ -0,0 +1,769 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <stddef.h>
+#include <netdb.h>
+
+#include "macro.h"
+#include "path-util.h"
+#include "util.h"
+#include "socket-util.h"
+#include "missing.h"
+#include "fileio.h"
+#include "formats-util.h"
+
+int socket_address_parse(SocketAddress *a, const char *s) {
+ char *e, *n;
+ unsigned u;
+ int r;
+
+ assert(a);
+ assert(s);
+
+ zero(*a);
+ a->type = SOCK_STREAM;
+
+ if (*s == '[') {
+ /* IPv6 in [x:.....:z]:p notation */
+
+ e = strchr(s+1, ']');
+ if (!e)
+ return -EINVAL;
+
+ n = strndupa(s+1, e-s-1);
+
+ errno = 0;
+ if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
+ return errno > 0 ? -errno : -EINVAL;
+
+ e++;
+ if (*e != ':')
+ return -EINVAL;
+
+ e++;
+ r = safe_atou(e, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
+
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->size = sizeof(struct sockaddr_in6);
+
+ } else if (*s == '/') {
+ /* AF_UNIX socket */
+
+ size_t l;
+
+ l = strlen(s);
+ if (l >= sizeof(a->sockaddr.un.sun_path))
+ return -EINVAL;
+
+ a->sockaddr.un.sun_family = AF_UNIX;
+ memcpy(a->sockaddr.un.sun_path, s, l);
+ a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+
+ } else if (*s == '@') {
+ /* Abstract AF_UNIX socket */
+ size_t l;
+
+ l = strlen(s+1);
+ if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+ return -EINVAL;
+
+ a->sockaddr.un.sun_family = AF_UNIX;
+ memcpy(a->sockaddr.un.sun_path+1, s+1, l);
+ a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+
+ } else {
+ e = strchr(s, ':');
+ if (e) {
+ r = safe_atou(e+1, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
+
+ n = strndupa(s, e-s);
+
+ /* IPv4 in w.x.y.z:p notation? */
+ r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
+ if (r < 0)
+ return -errno;
+
+ if (r > 0) {
+ /* Gotcha, it's a traditional IPv4 address */
+ a->sockaddr.in.sin_family = AF_INET;
+ a->sockaddr.in.sin_port = htons((uint16_t) u);
+ a->size = sizeof(struct sockaddr_in);
+ } else {
+ unsigned idx;
+
+ if (strlen(n) > IF_NAMESIZE-1)
+ return -EINVAL;
+
+ /* Uh, our last resort, an interface name */
+ idx = if_nametoindex(n);
+ if (idx == 0)
+ return -EINVAL;
+
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_scope_id = idx;
+ a->sockaddr.in6.sin6_addr = in6addr_any;
+ a->size = sizeof(struct sockaddr_in6);
+ }
+ } else {
+
+ /* Just a port */
+ r = safe_atou(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
+
+ if (socket_ipv6_is_supported()) {
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_addr = in6addr_any;
+ a->size = sizeof(struct sockaddr_in6);
+ } else {
+ a->sockaddr.in.sin_family = AF_INET;
+ a->sockaddr.in.sin_port = htons((uint16_t) u);
+ a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
+ a->size = sizeof(struct sockaddr_in);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
+ SocketAddress b;
+ int r;
+
+ /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
+
+ r = socket_address_parse(&b, s);
+ if (r < 0)
+ return r;
+
+ if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
+ log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
+ *a = b;
+ return 0;
+}
+
+int socket_address_parse_netlink(SocketAddress *a, const char *s) {
+ int family;
+ unsigned group = 0;
+ _cleanup_free_ char *sfamily = NULL;
+ assert(a);
+ assert(s);
+
+ zero(*a);
+ a->type = SOCK_RAW;
+
+ errno = 0;
+ if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
+ return errno > 0 ? -errno : -EINVAL;
+
+ family = netlink_family_from_string(sfamily);
+ if (family < 0)
+ return -EINVAL;
+
+ a->sockaddr.nl.nl_family = AF_NETLINK;
+ a->sockaddr.nl.nl_groups = group;
+
+ a->type = SOCK_RAW;
+ a->size = sizeof(struct sockaddr_nl);
+ a->protocol = family;
+
+ return 0;
+}
+
+int socket_address_verify(const SocketAddress *a) {
+ assert(a);
+
+ switch (socket_address_family(a)) {
+
+ case AF_INET:
+ if (a->size != sizeof(struct sockaddr_in))
+ return -EINVAL;
+
+ if (a->sockaddr.in.sin_port == 0)
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
+ case AF_INET6:
+ if (a->size != sizeof(struct sockaddr_in6))
+ return -EINVAL;
+
+ if (a->sockaddr.in6.sin6_port == 0)
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
+ case AF_UNIX:
+ if (a->size < offsetof(struct sockaddr_un, sun_path))
+ return -EINVAL;
+
+ if (a->size > offsetof(struct sockaddr_un, sun_path)) {
+
+ if (a->sockaddr.un.sun_path[0] != 0) {
+ char *e;
+
+ /* path */
+ e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
+ if (!e)
+ return -EINVAL;
+
+ if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
+ return -EINVAL;
+ }
+ }
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
+ return -EINVAL;
+
+ return 0;
+
+ case AF_NETLINK:
+
+ if (a->size != sizeof(struct sockaddr_nl))
+ return -EINVAL;
+
+ if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
+ default:
+ return -EAFNOSUPPORT;
+ }
+}
+
+int socket_address_print(const SocketAddress *a, char **ret) {
+ int r;
+
+ assert(a);
+ assert(ret);
+
+ r = socket_address_verify(a);
+ if (r < 0)
+ return r;
+
+ if (socket_address_family(a) == AF_NETLINK) {
+ _cleanup_free_ char *sfamily = NULL;
+
+ r = netlink_family_to_string_alloc(a->protocol, &sfamily);
+ if (r < 0)
+ return r;
+
+ r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
+ if (r < 0)
+ return -ENOMEM;
+
+ return 0;
+ }
+
+ return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
+}
+
+bool socket_address_can_accept(const SocketAddress *a) {
+ assert(a);
+
+ return
+ a->type == SOCK_STREAM ||
+ a->type == SOCK_SEQPACKET;
+}
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
+ assert(a);
+ assert(b);
+
+ /* Invalid addresses are unequal to all */
+ if (socket_address_verify(a) < 0 ||
+ socket_address_verify(b) < 0)
+ return false;
+
+ if (a->type != b->type)
+ return false;
+
+ if (socket_address_family(a) != socket_address_family(b))
+ return false;
+
+ switch (socket_address_family(a)) {
+
+ case AF_INET:
+ if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
+ return false;
+
+ if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
+ return false;
+
+ break;
+
+ case AF_INET6:
+ if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
+ return false;
+
+ if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
+ return false;
+
+ break;
+
+ case AF_UNIX:
+ if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
+ b->size <= offsetof(struct sockaddr_un, sun_path))
+ return false;
+
+ if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
+ return false;
+
+ if (a->sockaddr.un.sun_path[0]) {
+ if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
+ return false;
+ } else {
+ if (a->size != b->size)
+ return false;
+
+ if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
+ return false;
+ }
+
+ break;
+
+ case AF_NETLINK:
+ if (a->protocol != b->protocol)
+ return false;
+
+ if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
+ return false;
+
+ break;
+
+ default:
+ /* Cannot compare, so we assume the addresses are different */
+ return false;
+ }
+
+ return true;
+}
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type) {
+ struct SocketAddress b;
+
+ assert(a);
+ assert(s);
+
+ if (socket_address_parse(&b, s) < 0)
+ return false;
+
+ b.type = type;
+
+ return socket_address_equal(a, &b);
+}
+
+bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
+ struct SocketAddress b;
+
+ assert(a);
+ assert(s);
+
+ if (socket_address_parse_netlink(&b, s) < 0)
+ return false;
+
+ return socket_address_equal(a, &b);
+}
+
+const char* socket_address_get_path(const SocketAddress *a) {
+ assert(a);
+
+ if (socket_address_family(a) != AF_UNIX)
+ return NULL;
+
+ if (a->sockaddr.un.sun_path[0] == 0)
+ return NULL;
+
+ return a->sockaddr.un.sun_path;
+}
+
+bool socket_ipv6_is_supported(void) {
+ _cleanup_free_ char *l = NULL;
+
+ if (access("/sys/module/ipv6", F_OK) != 0)
+ return false;
+
+ /* If we can't check "disable" parameter, assume enabled */
+ if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
+ return true;
+
+ /* If module was loaded with disable=1 no IPv6 available */
+ return l[0] == '0';
+}
+
+bool socket_address_matches_fd(const SocketAddress *a, int fd) {
+ SocketAddress b;
+ socklen_t solen;
+
+ assert(a);
+ assert(fd >= 0);
+
+ b.size = sizeof(b.sockaddr);
+ if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
+ return false;
+
+ if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
+ return false;
+
+ solen = sizeof(b.type);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
+ return false;
+
+ if (b.type != a->type)
+ return false;
+
+ if (a->protocol != 0) {
+ solen = sizeof(b.protocol);
+ if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
+ return false;
+
+ if (b.protocol != a->protocol)
+ return false;
+ }
+
+ return socket_address_equal(a, &b);
+}
+
+int sockaddr_port(const struct sockaddr *_sa) {
+ union sockaddr_union *sa = (union sockaddr_union*) _sa;
+
+ assert(sa);
+
+ if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
+ return -EAFNOSUPPORT;
+
+ return ntohs(sa->sa.sa_family == AF_INET6 ?
+ sa->in6.sin6_port :
+ sa->in.sin_port);
+}
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
+ union sockaddr_union *sa = (union sockaddr_union*) _sa;
+ char *p;
+ int r;
+
+ assert(sa);
+ assert(salen >= sizeof(sa->sa.sa_family));
+
+ switch (sa->sa.sa_family) {
+
+ case AF_INET: {
+ uint32_t a;
+
+ a = ntohl(sa->in.sin_addr.s_addr);
+
+ if (include_port)
+ r = asprintf(&p,
+ "%u.%u.%u.%u:%u",
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
+ ntohs(sa->in.sin_port));
+ else
+ r = asprintf(&p,
+ "%u.%u.%u.%u",
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
+ if (r < 0)
+ return -ENOMEM;
+ break;
+ }
+
+ case AF_INET6: {
+ static const unsigned char ipv4_prefix[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
+ };
+
+ if (translate_ipv6 &&
+ memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
+ const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
+ if (include_port)
+ r = asprintf(&p,
+ "%u.%u.%u.%u:%u",
+ a[0], a[1], a[2], a[3],
+ ntohs(sa->in6.sin6_port));
+ else
+ r = asprintf(&p,
+ "%u.%u.%u.%u",
+ a[0], a[1], a[2], a[3]);
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ char a[INET6_ADDRSTRLEN];
+
+ inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+
+ if (include_port) {
+ r = asprintf(&p,
+ "[%s]:%u",
+ a,
+ ntohs(sa->in6.sin6_port));
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ p = strdup(a);
+ if (!p)
+ return -ENOMEM;
+ }
+ }
+
+ break;
+ }
+
+ case AF_UNIX:
+ if (salen <= offsetof(struct sockaddr_un, sun_path)) {
+ p = strdup("<unnamed>");
+ if (!p)
+ return -ENOMEM;
+
+ } else if (sa->un.sun_path[0] == 0) {
+ /* abstract */
+
+ /* FIXME: We assume we can print the
+ * socket path here and that it hasn't
+ * more than one NUL byte. That is
+ * actually an invalid assumption */
+
+ p = new(char, sizeof(sa->un.sun_path)+1);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = '@';
+ memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
+ p[sizeof(sa->un.sun_path)] = 0;
+
+ } else {
+ p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
+ if (!ret)
+ return -ENOMEM;
+ }
+
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+
+ *ret = p;
+ return 0;
+}
+
+int getpeername_pretty(int fd, char **ret) {
+ union sockaddr_union sa;
+ socklen_t salen = sizeof(sa);
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (getpeername(fd, &sa.sa, &salen) < 0)
+ return -errno;
+
+ if (sa.sa.sa_family == AF_UNIX) {
+ struct ucred ucred = {};
+
+ /* UNIX connection sockets are anonymous, so let's use
+ * PID/UID as pretty credentials instead */
+
+ r = getpeercred(fd, &ucred);
+ if (r < 0)
+ return r;
+
+ if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
+ return -ENOMEM;
+
+ return 0;
+ }
+
+ /* For remote sockets we translate IPv6 addresses back to IPv4
+ * if applicable, since that's nicer. */
+
+ return sockaddr_pretty(&sa.sa, salen, true, true, ret);
+}
+
+int getsockname_pretty(int fd, char **ret) {
+ union sockaddr_union sa;
+ socklen_t salen = sizeof(sa);
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (getsockname(fd, &sa.sa, &salen) < 0)
+ return -errno;
+
+ /* For local sockets we do not translate IPv6 addresses back
+ * to IPv6 if applicable, since this is usually used for
+ * listening sockets where the difference between IPv4 and
+ * IPv6 matters. */
+
+ return sockaddr_pretty(&sa.sa, salen, false, true, ret);
+}
+
+int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+ int r;
+ char host[NI_MAXHOST], *ret;
+
+ assert(_ret);
+
+ r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
+ NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
+ if (r != 0) {
+ int saved_errno = errno;
+
+ r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
+ if (r < 0)
+ return log_error_errno(r, "sockadd_pretty() failed: %m");
+
+ log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
+ } else {
+ ret = strdup(host);
+ if (!ret)
+ return log_oom();
+ }
+
+ *_ret = ret;
+ return 0;
+}
+
+int getnameinfo_pretty(int fd, char **ret) {
+ union sockaddr_union sa;
+ socklen_t salen = sizeof(sa);
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (getsockname(fd, &sa.sa, &salen) < 0)
+ return log_error_errno(errno, "getsockname(%d) failed: %m", fd);
+
+ return socknameinfo_pretty(&sa, salen, ret);
+}
+
+int socket_address_unlink(SocketAddress *a) {
+ assert(a);
+
+ if (socket_address_family(a) != AF_UNIX)
+ return 0;
+
+ if (a->sockaddr.un.sun_path[0] == 0)
+ return 0;
+
+ if (unlink(a->sockaddr.un.sun_path) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static const char* const netlink_family_table[] = {
+ [NETLINK_ROUTE] = "route",
+ [NETLINK_FIREWALL] = "firewall",
+ [NETLINK_INET_DIAG] = "inet-diag",
+ [NETLINK_NFLOG] = "nflog",
+ [NETLINK_XFRM] = "xfrm",
+ [NETLINK_SELINUX] = "selinux",
+ [NETLINK_ISCSI] = "iscsi",
+ [NETLINK_AUDIT] = "audit",
+ [NETLINK_FIB_LOOKUP] = "fib-lookup",
+ [NETLINK_CONNECTOR] = "connector",
+ [NETLINK_NETFILTER] = "netfilter",
+ [NETLINK_IP6_FW] = "ip6-fw",
+ [NETLINK_DNRTMSG] = "dnrtmsg",
+ [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
+ [NETLINK_GENERIC] = "generic",
+ [NETLINK_SCSITRANSPORT] = "scsitransport",
+ [NETLINK_ECRYPTFS] = "ecryptfs"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
+
+static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
+ [SOCKET_ADDRESS_DEFAULT] = "default",
+ [SOCKET_ADDRESS_BOTH] = "both",
+ [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+
+bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
+ assert(a);
+ assert(b);
+
+ if (a->sa.sa_family != b->sa.sa_family)
+ return false;
+
+ if (a->sa.sa_family == AF_INET)
+ return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
+
+ if (a->sa.sa_family == AF_INET6)
+ return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
+
+ return false;
+}
+
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
+ assert(addr);
+ assert(buffer);
+
+ /* Like ether_ntoa() but uses %02x instead of %x to print
+ * ethernet addresses, which makes them look less funny. Also,
+ * doesn't use a static buffer. */
+
+ sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
+ addr->ether_addr_octet[0],
+ addr->ether_addr_octet[1],
+ addr->ether_addr_octet[2],
+ addr->ether_addr_octet[3],
+ addr->ether_addr_octet[4],
+ addr->ether_addr_octet[5]);
+
+ return buffer;
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
new file mode 100644
index 0000000..538cf59
--- /dev/null
+++ b/src/basic/socket-util.h
@@ -0,0 +1,120 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/socket.h>
+#include <netinet/in.h>
+#include <netinet/ether.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+#include <linux/if_packet.h>
+
+#include "macro.h"
+#include "util.h"
+
+union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ struct sockaddr_un un;
+ struct sockaddr_nl nl;
+ struct sockaddr_storage storage;
+ struct sockaddr_ll ll;
+};
+
+typedef struct SocketAddress {
+ union sockaddr_union sockaddr;
+
+ /* We store the size here explicitly due to the weird
+ * sockaddr_un semantics for abstract sockets */
+ socklen_t size;
+
+ /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
+ int type;
+
+ /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
+ int protocol;
+} SocketAddress;
+
+typedef enum SocketAddressBindIPv6Only {
+ SOCKET_ADDRESS_DEFAULT,
+ SOCKET_ADDRESS_BOTH,
+ SOCKET_ADDRESS_IPV6_ONLY,
+ _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
+ _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
+} SocketAddressBindIPv6Only;
+
+#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
+
+int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_parse_and_warn(SocketAddress *a, const char *s);
+int socket_address_parse_netlink(SocketAddress *a, const char *s);
+int socket_address_print(const SocketAddress *a, char **p);
+int socket_address_verify(const SocketAddress *a) _pure_;
+int socket_address_unlink(SocketAddress *a);
+
+bool socket_address_can_accept(const SocketAddress *a) _pure_;
+
+int socket_address_listen(
+ const SocketAddress *a,
+ int flags,
+ int backlog,
+ SocketAddressBindIPv6Only only,
+ const char *bind_to_device,
+ bool free_bind,
+ bool transparent,
+ mode_t directory_mode,
+ mode_t socket_mode,
+ const char *label);
+int make_socket_fd(int log_level, const char* address, int flags);
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type);
+bool socket_address_is_netlink(const SocketAddress *a, const char *s);
+
+bool socket_address_matches_fd(const SocketAddress *a, int fd);
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
+
+const char* socket_address_get_path(const SocketAddress *a);
+
+bool socket_ipv6_is_supported(void);
+
+int sockaddr_port(const struct sockaddr *_sa) _pure_;
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
+int getpeername_pretty(int fd, char **ret);
+int getsockname_pretty(int fd, char **ret);
+
+int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
+int getnameinfo_pretty(int fd, char **ret);
+
+const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
+SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
+
+int netlink_family_to_string_alloc(int b, char **s);
+int netlink_family_from_string(const char *s) _pure_;
+
+bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
+
+#define ETHER_ADDR_TO_STRING_MAX (3*6)
+
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
diff --git a/src/basic/sparse-endian.h b/src/basic/sparse-endian.h
new file mode 100644
index 0000000..c913fda
--- /dev/null
+++ b/src/basic/sparse-endian.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2012 Josh Triplett <josh at joshtriplett.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef SPARSE_ENDIAN_H
+#define SPARSE_ENDIAN_H
+
+#include <byteswap.h>
+#include <endian.h>
+#include <stdint.h>
+
+#ifdef __CHECKER__
+#define __bitwise __attribute__((bitwise))
+#define __force __attribute__((force))
+#else
+#define __bitwise
+#define __force
+#endif
+
+typedef uint16_t __bitwise le16_t;
+typedef uint16_t __bitwise be16_t;
+typedef uint32_t __bitwise le32_t;
+typedef uint32_t __bitwise be32_t;
+typedef uint64_t __bitwise le64_t;
+typedef uint64_t __bitwise be64_t;
+
+#undef htobe16
+#undef htole16
+#undef be16toh
+#undef le16toh
+#undef htobe32
+#undef htole32
+#undef be32toh
+#undef le32toh
+#undef htobe64
+#undef htole64
+#undef be64toh
+#undef le64toh
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define bswap_16_on_le(x) __bswap_16(x)
+#define bswap_32_on_le(x) __bswap_32(x)
+#define bswap_64_on_le(x) __bswap_64(x)
+#define bswap_16_on_be(x) (x)
+#define bswap_32_on_be(x) (x)
+#define bswap_64_on_be(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define bswap_16_on_le(x) (x)
+#define bswap_32_on_le(x) (x)
+#define bswap_64_on_le(x) (x)
+#define bswap_16_on_be(x) __bswap_16(x)
+#define bswap_32_on_be(x) __bswap_32(x)
+#define bswap_64_on_be(x) __bswap_64(x)
+#endif
+
+static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); }
+static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); }
+static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); }
+
+static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); }
+static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); }
+static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); }
+
+static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); }
+static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); }
+static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); }
+
+static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); }
+static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); }
+static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); }
+
+#endif /* SPARSE_ENDIAN_H */
diff --git a/src/basic/special.h b/src/basic/special.h
new file mode 100644
index 0000000..e51310e
--- /dev/null
+++ b/src/basic/special.h
@@ -0,0 +1,117 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/>.
+***/
+
+#define SPECIAL_DEFAULT_TARGET "default.target"
+
+/* Shutdown targets */
+#define SPECIAL_UMOUNT_TARGET "umount.target"
+/* This is not really intended to be started by directly. This is
+ * mostly so that other targets (reboot/halt/poweroff) can depend on
+ * it to bring all services down that want to be brought down on
+ * system shutdown. */
+#define SPECIAL_SHUTDOWN_TARGET "shutdown.target"
+#define SPECIAL_HALT_TARGET "halt.target"
+#define SPECIAL_POWEROFF_TARGET "poweroff.target"
+#define SPECIAL_REBOOT_TARGET "reboot.target"
+#define SPECIAL_KEXEC_TARGET "kexec.target"
+#define SPECIAL_EXIT_TARGET "exit.target"
+#define SPECIAL_SUSPEND_TARGET "suspend.target"
+#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
+#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
+
+/* Special boot targets */
+#define SPECIAL_RESCUE_TARGET "rescue.target"
+#define SPECIAL_EMERGENCY_TARGET "emergency.target"
+#define SPECIAL_MULTI_USER_TARGET "multi-user.target"
+#define SPECIAL_GRAPHICAL_TARGET "graphical.target"
+
+/* Early boot targets */
+#define SPECIAL_SYSINIT_TARGET "sysinit.target"
+#define SPECIAL_SOCKETS_TARGET "sockets.target"
+#define SPECIAL_BUSNAMES_TARGET "busnames.target"
+#define SPECIAL_TIMERS_TARGET "timers.target"
+#define SPECIAL_PATHS_TARGET "paths.target"
+#define SPECIAL_LOCAL_FS_TARGET "local-fs.target"
+#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target"
+#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target"
+#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target"
+#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
+#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
+#define SPECIAL_SWAP_TARGET "swap.target"
+#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target"
+#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
+#define SPECIAL_BASIC_TARGET "basic.target"
+
+/* LSB compatibility */
+#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
+#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
+#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
+
+/*
+ * Rules regarding adding further high level targets like the above:
+ *
+ * - Be conservative, only add more of these when we really need
+ * them. We need strong usecases for further additions.
+ *
+ * - When there can be multiple implementations running side-by-side,
+ * it needs to be a .target unit which can pull in all
+ * implementations.
+ *
+ * - If something can be implemented with socket activation, and
+ * without, it needs to be a .target unit, so that it can pull in
+ * the appropriate unit.
+ *
+ * - Otherwise, it should be a .service unit.
+ *
+ * - In some cases it is OK to have both a .service and a .target
+ * unit, i.e. if there can be multiple parallel implementations, but
+ * only one is the "system" one. Example: syslog.
+ *
+ * Or to put this in other words: .service symlinks can be used to
+ * arbitrate between multiple implementations if there can be only one
+ * of a kind. .target units can be used to support multiple
+ * implementations that can run side-by-side.
+ */
+
+/* Magic early boot services */
+#define SPECIAL_FSCK_SERVICE "systemd-fsck at .service"
+#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service"
+#define SPECIAL_QUOTAON_SERVICE "quotaon.service"
+#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
+
+/* Services systemd relies on */
+#define SPECIAL_DBUS_SERVICE "dbus.service"
+#define SPECIAL_DBUS_SOCKET "dbus.socket"
+#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket"
+#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service"
+
+/* Magic init signals */
+#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
+#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
+#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
+
+/* Where we add all our system units, users and machines by default */
+#define SPECIAL_SYSTEM_SLICE "system.slice"
+#define SPECIAL_USER_SLICE "user.slice"
+#define SPECIAL_MACHINE_SLICE "machine.slice"
+#define SPECIAL_ROOT_SLICE "-.slice"
diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c
new file mode 100644
index 0000000..01a076c
--- /dev/null
+++ b/src/basic/strbuf.c
@@ -0,0 +1,201 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Kay Sievers <kay at vrfy.org>
+
+ 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 <string.h>
+
+#include "util.h"
+#include "strbuf.h"
+
+/*
+ * Strbuf stores given strings in a single continuous allocated memory
+ * area. Identical strings are de-duplicated and return the same offset
+ * as the first string stored. If the tail of a string already exists
+ * in the buffer, the tail is returned.
+ *
+ * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the
+ * information about the stored strings.
+ *
+ * Example of udev rules:
+ * $ ./udevadm test .
+ * ...
+ * read rules file: /usr/lib/udev/rules.d/99-systemd.rules
+ * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings
+ * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used
+ * ...
+ */
+
+struct strbuf *strbuf_new(void) {
+ struct strbuf *str;
+
+ str = new0(struct strbuf, 1);
+ if (!str)
+ return NULL;
+
+ str->buf = new0(char, 1);
+ if (!str->buf)
+ goto err;
+ str->len = 1;
+
+ str->root = new0(struct strbuf_node, 1);
+ if (!str->root)
+ goto err;
+ str->nodes_count = 1;
+ return str;
+err:
+ free(str->buf);
+ free(str->root);
+ free(str);
+ return NULL;
+}
+
+static void strbuf_node_cleanup(struct strbuf_node *node) {
+ size_t i;
+
+ for (i = 0; i < node->children_count; i++)
+ strbuf_node_cleanup(node->children[i].child);
+ free(node->children);
+ free(node);
+}
+
+/* clean up trie data, leave only the string buffer */
+void strbuf_complete(struct strbuf *str) {
+ if (!str)
+ return;
+ if (str->root)
+ strbuf_node_cleanup(str->root);
+ str->root = NULL;
+}
+
+/* clean up everything */
+void strbuf_cleanup(struct strbuf *str) {
+ if (!str)
+ return;
+ if (str->root)
+ strbuf_node_cleanup(str->root);
+ free(str->buf);
+ free(str);
+}
+
+static int strbuf_children_cmp(const struct strbuf_child_entry *n1,
+ const struct strbuf_child_entry *n2) {
+ return n1->c - n2->c;
+}
+
+static void bubbleinsert(struct strbuf_node *node,
+ uint8_t c,
+ struct strbuf_node *node_child) {
+
+ struct strbuf_child_entry new = {
+ .c = c,
+ .child = node_child,
+ };
+ int left = 0, right = node->children_count;
+
+ while (right > left) {
+ int middle = (right + left) / 2 ;
+ if (strbuf_children_cmp(&node->children[middle], &new) <= 0)
+ left = middle + 1;
+ else
+ right = middle;
+ }
+
+ memmove(node->children + left + 1, node->children + left,
+ sizeof(struct strbuf_child_entry) * (node->children_count - left));
+ node->children[left] = new;
+
+ node->children_count ++;
+}
+
+/* add string, return the index/offset into the buffer */
+ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
+ uint8_t c;
+ struct strbuf_node *node;
+ size_t depth;
+ char *buf_new;
+ struct strbuf_child_entry *child;
+ struct strbuf_node *node_child;
+ ssize_t off;
+
+ if (!str->root)
+ return -EINVAL;
+
+ /* search string; start from last character to find possibly matching tails */
+ if (len == 0)
+ return 0;
+ str->in_count++;
+ str->in_len += len;
+
+ node = str->root;
+ c = s[len-1];
+ for (depth = 0; depth <= len; depth++) {
+ struct strbuf_child_entry search;
+
+ /* match against current node */
+ off = node->value_off + node->value_len - len;
+ if (depth == len || (node->value_len >= len && memcmp(str->buf + off, s, len) == 0)) {
+ str->dedup_len += len;
+ str->dedup_count++;
+ return off;
+ }
+
+ /* lookup child node */
+ c = s[len - 1 - depth];
+ search.c = c;
+ child = bsearch(&search, node->children, node->children_count,
+ sizeof(struct strbuf_child_entry),
+ (__compar_fn_t) strbuf_children_cmp);
+ if (!child)
+ break;
+ node = child->child;
+ }
+
+ /* add new string */
+ buf_new = realloc(str->buf, str->len + len+1);
+ if (!buf_new)
+ return -ENOMEM;
+ str->buf = buf_new;
+ off = str->len;
+ memcpy(str->buf + off, s, len);
+ str->len += len;
+ str->buf[str->len++] = '\0';
+
+ /* new node */
+ node_child = new0(struct strbuf_node, 1);
+ if (!node_child)
+ return -ENOMEM;
+ node_child->value_off = off;
+ node_child->value_len = len;
+
+ /* extend array, add new entry, sort for bisection */
+ child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry));
+ if (!child) {
+ free(node_child);
+ return -ENOMEM;
+ }
+
+ str->nodes_count++;
+
+ node->children = child;
+ bubbleinsert(node, c, node_child);
+
+ return off;
+}
diff --git a/src/basic/strbuf.h b/src/basic/strbuf.h
new file mode 100644
index 0000000..fbc4e5f
--- /dev/null
+++ b/src/basic/strbuf.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Kay Sievers <kay at vrfy.org>
+
+ 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 <stdint.h>
+
+struct strbuf {
+ char *buf;
+ size_t len;
+ struct strbuf_node *root;
+
+ size_t nodes_count;
+ size_t in_count;
+ size_t in_len;
+ size_t dedup_len;
+ size_t dedup_count;
+};
+
+struct strbuf_node {
+ size_t value_off;
+ size_t value_len;
+
+ struct strbuf_child_entry *children;
+ uint8_t children_count;
+};
+
+struct strbuf_child_entry {
+ uint8_t c;
+ struct strbuf_node *child;
+};
+
+struct strbuf *strbuf_new(void);
+ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len);
+void strbuf_complete(struct strbuf *str);
+void strbuf_cleanup(struct strbuf *str);
diff --git a/src/basic/strv.c b/src/basic/strv.c
new file mode 100644
index 0000000..d44a72f
--- /dev/null
+++ b/src/basic/strv.c
@@ -0,0 +1,704 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "strv.h"
+
+char *strv_find(char **l, const char *name) {
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l)
+ if (streq(*i, name))
+ return *i;
+
+ return NULL;
+}
+
+char *strv_find_prefix(char **l, const char *name) {
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l)
+ if (startswith(*i, name))
+ return *i;
+
+ return NULL;
+}
+
+char *strv_find_startswith(char **l, const char *name) {
+ char **i, *e;
+
+ assert(name);
+
+ /* Like strv_find_prefix, but actually returns only the
+ * suffix, not the whole item */
+
+ STRV_FOREACH(i, l) {
+ e = startswith(*i, name);
+ if (e)
+ return e;
+ }
+
+ return NULL;
+}
+
+void strv_clear(char **l) {
+ char **k;
+
+ if (!l)
+ return;
+
+ for (k = l; *k; k++)
+ free(*k);
+
+ *l = NULL;
+}
+
+char **strv_free(char **l) {
+ strv_clear(l);
+ free(l);
+ return NULL;
+}
+
+char **strv_copy(char * const *l) {
+ char **r, **k;
+
+ k = r = new(char*, strv_length(l) + 1);
+ if (!r)
+ return NULL;
+
+ if (l)
+ for (; *l; k++, l++) {
+ *k = strdup(*l);
+ if (!*k) {
+ strv_free(r);
+ return NULL;
+ }
+ }
+
+ *k = NULL;
+ return r;
+}
+
+unsigned strv_length(char * const *l) {
+ unsigned n = 0;
+
+ if (!l)
+ return 0;
+
+ for (; *l; l++)
+ n++;
+
+ return n;
+}
+
+char **strv_new_ap(const char *x, va_list ap) {
+ const char *s;
+ char **a;
+ unsigned n = 0, i = 0;
+ va_list aq;
+
+ /* As a special trick we ignore all listed strings that equal
+ * (const char*) -1. This is supposed to be used with the
+ * STRV_IFNOTNULL() macro to include possibly NULL strings in
+ * the string list. */
+
+ if (x) {
+ n = x == (const char*) -1 ? 0 : 1;
+
+ va_copy(aq, ap);
+ while ((s = va_arg(aq, const char*))) {
+ if (s == (const char*) -1)
+ continue;
+
+ n++;
+ }
+
+ va_end(aq);
+ }
+
+ a = new(char*, n+1);
+ if (!a)
+ return NULL;
+
+ if (x) {
+ if (x != (const char*) -1) {
+ a[i] = strdup(x);
+ if (!a[i])
+ goto fail;
+ i++;
+ }
+
+ while ((s = va_arg(ap, const char*))) {
+
+ if (s == (const char*) -1)
+ continue;
+
+ a[i] = strdup(s);
+ if (!a[i])
+ goto fail;
+
+ i++;
+ }
+ }
+
+ a[i] = NULL;
+
+ return a;
+
+fail:
+ strv_free(a);
+ return NULL;
+}
+
+char **strv_new(const char *x, ...) {
+ char **r;
+ va_list ap;
+
+ va_start(ap, x);
+ r = strv_new_ap(x, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int strv_extend_strv(char ***a, char **b) {
+ int r;
+ char **s;
+
+ STRV_FOREACH(s, b) {
+ r = strv_extend(a, *s);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
+ int r;
+ char **s;
+
+ STRV_FOREACH(s, b) {
+ char *v;
+
+ v = strappend(*s, suffix);
+ if (!v)
+ return -ENOMEM;
+
+ r = strv_push(a, v);
+ if (r < 0) {
+ free(v);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+char **strv_split(const char *s, const char *separator) {
+ const char *word, *state;
+ size_t l;
+ unsigned n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
+ n++;
+
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
+ r[i] = strndup(word, l);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+
+ i++;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char **strv_split_newlines(const char *s) {
+ char **l;
+ unsigned n;
+
+ assert(s);
+
+ /* Special version of strv_split() that splits on newlines and
+ * suppresses an empty string at the end */
+
+ l = strv_split(s, NEWLINE);
+ if (!l)
+ return NULL;
+
+ n = strv_length(l);
+ if (n <= 0)
+ return l;
+
+ if (isempty(l[n-1])) {
+ free(l[n-1]);
+ l[n-1] = NULL;
+ }
+
+ return l;
+}
+
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
+ size_t n = 0, allocated = 0;
+ _cleanup_strv_free_ char **l = NULL;
+ int r;
+
+ assert(t);
+ assert(s);
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = unquote_first_word(&s, &word, flags);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (!GREEDY_REALLOC(l, allocated, n + 2))
+ return -ENOMEM;
+
+ l[n++] = word;
+ word = NULL;
+
+ l[n] = NULL;
+ }
+
+ if (!l)
+ l = new0(char*, 1);
+
+ *t = l;
+ l = NULL;
+
+ return 0;
+}
+
+char *strv_join(char **l, const char *separator) {
+ char *r, *e;
+ char **s;
+ size_t n, k;
+
+ if (!separator)
+ separator = " ";
+
+ k = strlen(separator);
+
+ n = 0;
+ STRV_FOREACH(s, l) {
+ if (n != 0)
+ n += k;
+ n += strlen(*s);
+ }
+
+ r = new(char, n+1);
+ if (!r)
+ return NULL;
+
+ e = r;
+ STRV_FOREACH(s, l) {
+ if (e != r)
+ e = stpcpy(e, separator);
+
+ e = stpcpy(e, *s);
+ }
+
+ *e = 0;
+
+ return r;
+}
+
+char *strv_join_quoted(char **l) {
+ char *buf = NULL;
+ char **s;
+ size_t allocated = 0, len = 0;
+
+ STRV_FOREACH(s, l) {
+ /* assuming here that escaped string cannot be more
+ * than twice as long, and reserving space for the
+ * separator and quotes.
+ */
+ _cleanup_free_ char *esc = NULL;
+ size_t needed;
+
+ if (!GREEDY_REALLOC(buf, allocated,
+ len + strlen(*s) * 2 + 3))
+ goto oom;
+
+ esc = cescape(*s);
+ if (!esc)
+ goto oom;
+
+ needed = snprintf(buf + len, allocated - len, "%s\"%s\"",
+ len > 0 ? " " : "", esc);
+ assert(needed < allocated - len);
+ len += needed;
+ }
+
+ if (!buf)
+ buf = malloc0(1);
+
+ return buf;
+
+ oom:
+ free(buf);
+ return NULL;
+}
+
+int strv_push(char ***l, char *value) {
+ char **c;
+ unsigned n, m;
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+
+ /* Increase and check for overflow */
+ m = n + 2;
+ if (m < n)
+ return -ENOMEM;
+
+ c = realloc_multiply(*l, sizeof(char*), m);
+ if (!c)
+ return -ENOMEM;
+
+ c[n] = value;
+ c[n+1] = NULL;
+
+ *l = c;
+ return 0;
+}
+
+int strv_push_pair(char ***l, char *a, char *b) {
+ char **c;
+ unsigned n, m;
+
+ if (!a && !b)
+ return 0;
+
+ n = strv_length(*l);
+
+ /* increase and check for overflow */
+ m = n + !!a + !!b + 1;
+ if (m < n)
+ return -ENOMEM;
+
+ c = realloc_multiply(*l, sizeof(char*), m);
+ if (!c)
+ return -ENOMEM;
+
+ if (a)
+ c[n++] = a;
+ if (b)
+ c[n++] = b;
+ c[n] = NULL;
+
+ *l = c;
+ return 0;
+}
+
+int strv_push_prepend(char ***l, char *value) {
+ char **c;
+ unsigned n, m, i;
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+
+ /* increase and check for overflow */
+ m = n + 2;
+ if (m < n)
+ return -ENOMEM;
+
+ c = new(char*, m);
+ if (!c)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++)
+ c[i+1] = (*l)[i];
+
+ c[0] = value;
+ c[n+1] = NULL;
+
+ free(*l);
+ *l = c;
+
+ return 0;
+}
+
+int strv_consume(char ***l, char *value) {
+ int r;
+
+ r = strv_push(l, value);
+ if (r < 0)
+ free(value);
+
+ return r;
+}
+
+int strv_consume_pair(char ***l, char *a, char *b) {
+ int r;
+
+ r = strv_push_pair(l, a, b);
+ if (r < 0) {
+ free(a);
+ free(b);
+ }
+
+ return r;
+}
+
+int strv_consume_prepend(char ***l, char *value) {
+ int r;
+
+ r = strv_push_prepend(l, value);
+ if (r < 0)
+ free(value);
+
+ return r;
+}
+
+int strv_extend(char ***l, const char *value) {
+ char *v;
+
+ if (!value)
+ return 0;
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ return strv_consume(l, v);
+}
+
+char **strv_uniq(char **l) {
+ char **i;
+
+ /* Drops duplicate entries. The first identical string will be
+ * kept, the others dropped */
+
+ STRV_FOREACH(i, l)
+ strv_remove(i+1, *i);
+
+ return l;
+}
+
+bool strv_is_uniq(char **l) {
+ char **i;
+
+ STRV_FOREACH(i, l)
+ if (strv_find(i+1, *i))
+ return false;
+
+ return true;
+}
+
+char **strv_remove(char **l, const char *s) {
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(s);
+
+ /* Drops every occurrence of s in the string list, edits
+ * in-place. */
+
+ for (f = t = l; *f; f++)
+ if (streq(*f, s))
+ free(*f);
+ else
+ *(t++) = *f;
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_parse_nulstr(const char *s, size_t l) {
+ const char *p;
+ unsigned c = 0, i = 0;
+ char **v;
+
+ assert(s || l <= 0);
+
+ if (l <= 0)
+ return new0(char*, 1);
+
+ for (p = s; p < s + l; p++)
+ if (*p == 0)
+ c++;
+
+ if (s[l-1] != 0)
+ c++;
+
+ v = new0(char*, c+1);
+ if (!v)
+ return NULL;
+
+ p = s;
+ while (p < s + l) {
+ const char *e;
+
+ e = memchr(p, 0, s + l - p);
+
+ v[i] = strndup(p, e ? e - p : s + l - p);
+ if (!v[i]) {
+ strv_free(v);
+ return NULL;
+ }
+
+ i++;
+
+ if (!e)
+ break;
+
+ p = e + 1;
+ }
+
+ assert(i == c);
+
+ return v;
+}
+
+char **strv_split_nulstr(const char *s) {
+ const char *i;
+ char **r = NULL;
+
+ NULSTR_FOREACH(i, s)
+ if (strv_extend(&r, i) < 0) {
+ strv_free(r);
+ return NULL;
+ }
+
+ if (!r)
+ return strv_new(NULL, NULL);
+
+ return r;
+}
+
+bool strv_overlap(char **a, char **b) {
+ char **i;
+
+ STRV_FOREACH(i, a)
+ if (strv_contains(b, *i))
+ return true;
+
+ return false;
+}
+
+static int str_compare(const void *_a, const void *_b) {
+ const char **a = (const char**) _a, **b = (const char**) _b;
+
+ return strcmp(*a, *b);
+}
+
+char **strv_sort(char **l) {
+
+ if (strv_isempty(l))
+ return l;
+
+ qsort(l, strv_length(l), sizeof(char*), str_compare);
+ return l;
+}
+
+bool strv_equal(char **a, char **b) {
+ if (!a || !b)
+ return a == b;
+
+ for ( ; *a || *b; ++a, ++b)
+ if (!streq_ptr(*a, *b))
+ return false;
+
+ return true;
+}
+
+void strv_print(char **l) {
+ char **s;
+
+ STRV_FOREACH(s, l)
+ puts(*s);
+}
+
+int strv_extendf(char ***l, const char *format, ...) {
+ va_list ap;
+ char *x;
+ int r;
+
+ va_start(ap, format);
+ r = vasprintf(&x, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return strv_consume(l, x);
+}
+
+char **strv_reverse(char **l) {
+ unsigned n, i;
+
+ n = strv_length(l);
+ if (n <= 1)
+ return l;
+
+ for (i = 0; i < n / 2; i++) {
+ char *t;
+
+ t = l[i];
+ l[i] = l[n-1-i];
+ l[n-1-i] = t;
+ }
+
+ return l;
+}
+
+bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
+ char* const* p;
+
+ STRV_FOREACH(p, patterns)
+ if (fnmatch(*p, s, 0) == 0)
+ return true;
+
+ return false;
+}
diff --git a/src/basic/strv.h b/src/basic/strv.h
new file mode 100644
index 0000000..22f8f98
--- /dev/null
+++ b/src/basic/strv.h
@@ -0,0 +1,155 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdarg.h>
+#include <stdbool.h>
+#include <fnmatch.h>
+
+#include "util.h"
+
+char *strv_find(char **l, const char *name) _pure_;
+char *strv_find_prefix(char **l, const char *name) _pure_;
+char *strv_find_startswith(char **l, const char *name) _pure_;
+
+char **strv_free(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
+#define _cleanup_strv_free_ _cleanup_(strv_freep)
+
+void strv_clear(char **l);
+
+char **strv_copy(char * const *l);
+unsigned strv_length(char * const *l) _pure_;
+
+int strv_extend_strv(char ***a, char **b);
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
+int strv_extend(char ***l, const char *value);
+int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
+int strv_push(char ***l, char *value);
+int strv_push_pair(char ***l, char *a, char *b);
+int strv_push_prepend(char ***l, char *value);
+int strv_consume(char ***l, char *value);
+int strv_consume_pair(char ***l, char *a, char *b);
+int strv_consume_prepend(char ***l, char *value);
+
+char **strv_remove(char **l, const char *s);
+char **strv_uniq(char **l);
+bool strv_is_uniq(char **l);
+
+bool strv_equal(char **a, char **b);
+
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+
+char **strv_new(const char *x, ...) _sentinel_;
+char **strv_new_ap(const char *x, va_list ap);
+
+static inline const char* STRV_IFNOTNULL(const char *x) {
+ return x ? x : (const char *) -1;
+}
+
+static inline bool strv_isempty(char * const *l) {
+ return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator);
+char **strv_split_newlines(const char *s);
+
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
+
+char *strv_join(char **l, const char *separator);
+char *strv_join_quoted(char **l);
+
+char **strv_parse_nulstr(const char *s, size_t l);
+char **strv_split_nulstr(const char *s);
+
+bool strv_overlap(char **a, char **b) _pure_;
+
+#define STRV_FOREACH(s, l) \
+ for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l) \
+ STRV_FOREACH(s, l) \
+ ; \
+ for ((s)--; (l) && ((s) >= (l)); (s)--)
+
+#define STRV_FOREACH_PAIR(x, y, l) \
+ for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+
+char **strv_sort(char **l);
+void strv_print(char **l);
+
+#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
+
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
+
+#define strv_from_stdarg_alloca(first) \
+ ({ \
+ char **_l; \
+ \
+ if (!first) \
+ _l = (char**) &first; \
+ else { \
+ unsigned _n; \
+ va_list _ap; \
+ \
+ _n = 1; \
+ va_start(_ap, first); \
+ while (va_arg(_ap, char*)) \
+ _n++; \
+ va_end(_ap); \
+ \
+ _l = newa(char*, _n+1); \
+ _l[_n = 0] = (char*) first; \
+ va_start(_ap, first); \
+ for (;;) { \
+ _l[++_n] = va_arg(_ap, char*); \
+ if (!_l[_n]) \
+ break; \
+ } \
+ va_end(_ap); \
+ } \
+ _l; \
+ })
+
+#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
+
+#define FOREACH_STRING(x, ...) \
+ for (char **_l = ({ \
+ char **_ll = STRV_MAKE(__VA_ARGS__); \
+ x = _ll ? _ll[0] : NULL; \
+ _ll; \
+ }); \
+ _l && *_l; \
+ x = ({ \
+ _l ++; \
+ _l[0]; \
+ }))
+
+char **strv_reverse(char **l);
+
+bool strv_fnmatch(char* const* patterns, const char *s, int flags);
+
+static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
+ assert(s);
+ return strv_isempty(patterns) ||
+ strv_fnmatch(patterns, s, flags);
+}
diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c
new file mode 100644
index 0000000..6542c0a
--- /dev/null
+++ b/src/basic/strxcpyx.c
@@ -0,0 +1,100 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Kay Sievers
+
+ 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/>.
+***/
+
+/*
+ * Concatenates/copies strings. In any case, terminates in all cases
+ * with '\0' * and moves the @dest pointer forward to the added '\0'.
+ * Returns the * remaining size, and 0 if the string was truncated.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "strxcpyx.h"
+
+size_t strpcpy(char **dest, size_t size, const char *src) {
+ size_t len;
+
+ len = strlen(src);
+ if (len >= size) {
+ if (size > 1)
+ *dest = mempcpy(*dest, src, size-1);
+ size = 0;
+ } else {
+ if (len > 0) {
+ *dest = mempcpy(*dest, src, len);
+ size -= len;
+ }
+ }
+ *dest[0] = '\0';
+ return size;
+}
+
+size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
+ va_list va;
+ int i;
+
+ va_start(va, src);
+ i = vsnprintf(*dest, size, src, va);
+ if (i < (int)size) {
+ *dest += i;
+ size -= i;
+ } else {
+ *dest += size;
+ size = 0;
+ }
+ va_end(va);
+ *dest[0] = '\0';
+ return size;
+}
+
+size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
+ va_list va;
+
+ va_start(va, src);
+ do {
+ size = strpcpy(dest, size, src);
+ src = va_arg(va, char *);
+ } while (src != NULL);
+ va_end(va);
+ return size;
+}
+
+size_t strscpy(char *dest, size_t size, const char *src) {
+ char *s;
+
+ s = dest;
+ return strpcpy(&s, size, src);
+}
+
+size_t strscpyl(char *dest, size_t size, const char *src, ...) {
+ va_list va;
+ char *s;
+
+ va_start(va, src);
+ s = dest;
+ do {
+ size = strpcpy(&s, size, src);
+ src = va_arg(va, char *);
+ } while (src != NULL);
+ va_end(va);
+
+ return size;
+}
diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h
new file mode 100644
index 0000000..ccc7e52
--- /dev/null
+++ b/src/basic/strxcpyx.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Kay Sievers
+
+ 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 "macro.h"
+
+size_t strpcpy(char **dest, size_t size, const char *src);
+size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4);
+size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_;
+size_t strscpy(char *dest, size_t size, const char *src);
+size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_;
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
new file mode 100644
index 0000000..042b88f
--- /dev/null
+++ b/src/basic/terminal-util.c
@@ -0,0 +1,1072 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <assert.h>
+#include <poll.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <linux/kd.h>
+
+#include "terminal-util.h"
+#include "time-util.h"
+#include "process-util.h"
+#include "util.h"
+#include "fileio.h"
+#include "path-util.h"
+
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
+int chvt(int vt) {
+ _cleanup_close_ int fd;
+
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (vt < 0) {
+ int tiocl[2] = {
+ TIOCL_GETKMSGREDIRECT,
+ 0
+ };
+
+ if (ioctl(fd, TIOCLINUX, tiocl) < 0)
+ return -errno;
+
+ vt = tiocl[0] <= 0 ? 1 : tiocl[0];
+ }
+
+ if (ioctl(fd, VT_ACTIVATE, vt) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
+ struct termios old_termios, new_termios;
+ char c, line[LINE_MAX];
+
+ assert(f);
+ assert(ret);
+
+ if (tcgetattr(fileno(f), &old_termios) >= 0) {
+ new_termios = old_termios;
+
+ new_termios.c_lflag &= ~ICANON;
+ new_termios.c_cc[VMIN] = 1;
+ new_termios.c_cc[VTIME] = 0;
+
+ if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
+ size_t k;
+
+ if (t != USEC_INFINITY) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ return -ETIMEDOUT;
+ }
+ }
+
+ k = fread(&c, 1, 1, f);
+
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+
+ if (k <= 0)
+ return -EIO;
+
+ if (need_nl)
+ *need_nl = c != '\n';
+
+ *ret = c;
+ return 0;
+ }
+ }
+
+ if (t != USEC_INFINITY) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+ return -ETIMEDOUT;
+ }
+
+ errno = 0;
+ if (!fgets(line, sizeof(line), f))
+ return errno ? -errno : -EIO;
+
+ truncate_nl(line);
+
+ if (strlen(line) != 1)
+ return -EBADMSG;
+
+ if (need_nl)
+ *need_nl = false;
+
+ *ret = line[0];
+ return 0;
+}
+
+int ask_char(char *ret, const char *replies, const char *text, ...) {
+ int r;
+
+ assert(ret);
+ assert(replies);
+ assert(text);
+
+ for (;;) {
+ va_list ap;
+ char c;
+ bool need_nl = true;
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
+ if (r < 0) {
+
+ if (r == -EBADMSG) {
+ puts("Bad input, please try again.");
+ continue;
+ }
+
+ putchar('\n');
+ return r;
+ }
+
+ if (need_nl)
+ putchar('\n');
+
+ if (strchr(replies, c)) {
+ *ret = c;
+ return 0;
+ }
+
+ puts("Read unexpected character, please try again.");
+ }
+}
+
+int ask_string(char **ret, const char *text, ...) {
+ assert(ret);
+ assert(text);
+
+ for (;;) {
+ char line[LINE_MAX];
+ va_list ap;
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ errno = 0;
+ if (!fgets(line, sizeof(line), stdin))
+ return errno ? -errno : -EIO;
+
+ if (!endswith(line, "\n"))
+ putchar('\n');
+ else {
+ char *s;
+
+ if (isempty(line))
+ continue;
+
+ truncate_nl(line);
+ s = strdup(line);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+ }
+ }
+}
+
+int reset_terminal_fd(int fd, bool switch_to_text) {
+ struct termios termios;
+ int r = 0;
+
+ /* Set terminal to some sane defaults */
+
+ assert(fd >= 0);
+
+ /* We leave locked terminal attributes untouched, so that
+ * Plymouth may set whatever it wants to set, and we don't
+ * interfere with that. */
+
+ /* Disable exclusive mode, just in case */
+ ioctl(fd, TIOCNXCL);
+
+ /* Switch to text mode */
+ if (switch_to_text)
+ ioctl(fd, KDSETMODE, KD_TEXT);
+
+ /* Enable console unicode mode */
+ ioctl(fd, KDSKBMODE, K_UNICODE);
+
+ if (tcgetattr(fd, &termios) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* We only reset the stuff that matters to the software. How
+ * hardware is set up we don't touch assuming that somebody
+ * else will do that for us */
+
+ termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+ termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+ termios.c_oflag |= ONLCR;
+ termios.c_cflag |= CREAD;
+ termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
+
+ termios.c_cc[VINTR] = 03; /* ^C */
+ termios.c_cc[VQUIT] = 034; /* ^\ */
+ termios.c_cc[VERASE] = 0177;
+ termios.c_cc[VKILL] = 025; /* ^X */
+ termios.c_cc[VEOF] = 04; /* ^D */
+ termios.c_cc[VSTART] = 021; /* ^Q */
+ termios.c_cc[VSTOP] = 023; /* ^S */
+ termios.c_cc[VSUSP] = 032; /* ^Z */
+ termios.c_cc[VLNEXT] = 026; /* ^V */
+ termios.c_cc[VWERASE] = 027; /* ^W */
+ termios.c_cc[VREPRINT] = 022; /* ^R */
+ termios.c_cc[VEOL] = 0;
+ termios.c_cc[VEOL2] = 0;
+
+ termios.c_cc[VTIME] = 0;
+ termios.c_cc[VMIN] = 1;
+
+ if (tcsetattr(fd, TCSANOW, &termios) < 0)
+ r = -errno;
+
+finish:
+ /* Just in case, flush all crap out */
+ tcflush(fd, TCIOFLUSH);
+
+ return r;
+}
+
+int reset_terminal(const char *name) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ return reset_terminal_fd(fd, true);
+}
+
+int open_terminal(const char *name, int mode) {
+ int fd, r;
+ unsigned c = 0;
+
+ /*
+ * If a TTY is in the process of being closed opening it might
+ * cause EIO. This is horribly awful, but unlikely to be
+ * changed in the kernel. Hence we work around this problem by
+ * retrying a couple of times.
+ *
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+ */
+
+ assert(!(mode & O_CREAT));
+
+ for (;;) {
+ fd = open(name, mode, 0);
+ if (fd >= 0)
+ break;
+
+ if (errno != EIO)
+ return -errno;
+
+ /* Max 1s in total */
+ if (c >= 20)
+ return -errno;
+
+ usleep(50 * USEC_PER_MSEC);
+ c++;
+ }
+
+ r = isatty(fd);
+ if (r < 0) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ if (!r) {
+ safe_close(fd);
+ return -ENOTTY;
+ }
+
+ return fd;
+}
+
+int acquire_terminal(
+ const char *name,
+ bool fail,
+ bool force,
+ bool ignore_tiocstty_eperm,
+ usec_t timeout) {
+
+ int fd = -1, notify = -1, r = 0, wd = -1;
+ usec_t ts = 0;
+
+ assert(name);
+
+ /* We use inotify to be notified when the tty is closed. We
+ * create the watch before checking if we can actually acquire
+ * it, so that we don't lose any event.
+ *
+ * Note: strictly speaking this actually watches for the
+ * device being closed, it does *not* really watch whether a
+ * tty loses its controlling process. However, unless some
+ * rogue process uses TIOCNOTTY on /dev/tty *after* closing
+ * its tty otherwise this will not become a problem. As long
+ * as the administrator makes sure not configure any service
+ * on the same tty as an untrusted user this should not be a
+ * problem. (Which he probably should not do anyway.) */
+
+ if (timeout != USEC_INFINITY)
+ ts = now(CLOCK_MONOTONIC);
+
+ if (!fail && !force) {
+ notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
+ if (notify < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ wd = inotify_add_watch(notify, name, IN_CLOSE);
+ if (wd < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+
+ for (;;) {
+ struct sigaction sa_old, sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
+ if (notify >= 0) {
+ r = flush_fd(notify);
+ if (r < 0)
+ goto fail;
+ }
+
+ /* We pass here O_NOCTTY only so that we can check the return
+ * value TIOCSCTTY and have a reliable way to figure out if we
+ * successfully became the controlling process of the tty */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * if we already own the tty. */
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ /* First, try to get the tty */
+ if (ioctl(fd, TIOCSCTTY, force) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ /* Sometimes it makes sense to ignore TIOCSCTTY
+ * returning EPERM, i.e. when very likely we already
+ * are have this controlling terminal. */
+ if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
+ r = 0;
+
+ if (r < 0 && (force || fail || r != -EPERM)) {
+ goto fail;
+ }
+
+ if (r >= 0)
+ break;
+
+ assert(!fail);
+ assert(!force);
+ assert(notify >= 0);
+
+ for (;;) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
+
+ if (timeout != USEC_INFINITY) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (ts + timeout < n) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+
+ r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+ if (r < 0)
+ goto fail;
+
+ if (r == 0) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+ }
+
+ l = read(notify, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ r = -errno;
+ goto fail;
+ }
+
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
+ if (e->wd != wd || !(e->mask & IN_CLOSE)) {
+ r = -EIO;
+ goto fail;
+ }
+ }
+
+ break;
+ }
+
+ /* We close the tty fd here since if the old session
+ * ended our handle will be dead. It's important that
+ * we do this after sleeping, so that we don't enter
+ * an endless loop. */
+ fd = safe_close(fd);
+ }
+
+ safe_close(notify);
+
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset terminal: %m");
+
+ return fd;
+
+fail:
+ safe_close(fd);
+ safe_close(notify);
+
+ return r;
+}
+
+int release_terminal(void) {
+ static const struct sigaction sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
+ _cleanup_close_ int fd = -1;
+ struct sigaction sa_old;
+ int r = 0;
+
+ fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * by our own TIOCNOTTY */
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ if (ioctl(fd, TIOCNOTTY) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ return r;
+}
+
+int terminal_vhangup_fd(int fd) {
+ assert(fd >= 0);
+
+ if (ioctl(fd, TIOCVHANGUP) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int terminal_vhangup(const char *name) {
+ _cleanup_close_ int fd;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ return terminal_vhangup_fd(fd);
+}
+
+int vt_disallocate(const char *name) {
+ int fd, r;
+ unsigned u;
+
+ /* Deallocate the VT if possible. If not possible
+ * (i.e. because it is the active one), at least clear it
+ * entirely (including the scrollback buffer) */
+
+ if (!startswith(name, "/dev/"))
+ return -EINVAL;
+
+ if (!tty_is_vc(name)) {
+ /* So this is not a VT. I guess we cannot deallocate
+ * it then. But let's at least clear the screen */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[2J", /* clear screen */
+ 10, false);
+ safe_close(fd);
+
+ return 0;
+ }
+
+ if (!startswith(name, "/dev/tty"))
+ return -EINVAL;
+
+ r = safe_atou(name+8, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0)
+ return -EINVAL;
+
+ /* Try to deallocate */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = ioctl(fd, VT_DISALLOCATE, u);
+ safe_close(fd);
+
+ if (r >= 0)
+ return 0;
+
+ if (errno != EBUSY)
+ return -errno;
+
+ /* Couldn't deallocate, so let's clear it fully with
+ * scrollback */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+ 10, false);
+ safe_close(fd);
+
+ return 0;
+}
+
+void warn_melody(void) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return;
+
+ /* Yeah, this is synchronous. Kinda sucks. But well... */
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/440));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, 0);
+}
+
+int make_console_stdio(void) {
+ int fd, r;
+
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+ fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to acquire terminal: %m");
+
+ r = make_stdio(fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to duplicate terminal fd: %m");
+
+ return 0;
+}
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
+ static const char status_indent[] = " "; /* "[" STATUS "] " */
+ _cleanup_free_ char *s = NULL;
+ _cleanup_close_ int fd = -1;
+ struct iovec iovec[6] = {};
+ int n = 0;
+ static bool prev_ephemeral;
+
+ assert(format);
+
+ /* This is independent of logging, as status messages are
+ * optional and go exclusively to the console. */
+
+ if (vasprintf(&s, format, ap) < 0)
+ return log_oom();
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ if (ellipse) {
+ char *e;
+ size_t emax, sl;
+ int c;
+
+ c = fd_columns(fd);
+ if (c <= 0)
+ c = 80;
+
+ sl = status ? sizeof(status_indent)-1 : 0;
+
+ emax = c - sl - 1;
+ if (emax < 3)
+ emax = 3;
+
+ e = ellipsize(s, emax, 50);
+ if (e) {
+ free(s);
+ s = e;
+ }
+ }
+
+ if (prev_ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = ephemeral;
+
+ if (status) {
+ if (!isempty(status)) {
+ IOVEC_SET_STRING(iovec[n++], "[");
+ IOVEC_SET_STRING(iovec[n++], status);
+ IOVEC_SET_STRING(iovec[n++], "] ");
+ } else
+ IOVEC_SET_STRING(iovec[n++], status_indent);
+ }
+
+ IOVEC_SET_STRING(iovec[n++], s);
+ if (!ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(fd, iovec, n) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
+ va_list ap;
+ int r;
+
+ assert(format);
+
+ va_start(ap, format);
+ r = status_vprintf(status, ellipse, ephemeral, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+bool tty_is_vc(const char *tty) {
+ assert(tty);
+
+ return vtnr_from_tty(tty) >= 0;
+}
+
+bool tty_is_console(const char *tty) {
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ return streq(tty, "console");
+}
+
+int vtnr_from_tty(const char *tty) {
+ int i, r;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (!startswith(tty, "tty") )
+ return -EINVAL;
+
+ if (tty[3] < '0' || tty[3] > '9')
+ return -EINVAL;
+
+ r = safe_atoi(tty+3, &i);
+ if (r < 0)
+ return r;
+
+ if (i < 0 || i > 63)
+ return -EINVAL;
+
+ return i;
+}
+
+char *resolve_dev_console(char **active) {
+ char *tty;
+
+ /* Resolve where /dev/console is pointing to, if /sys is actually ours
+ * (i.e. not read-only-mounted which is a sign for container setups) */
+
+ if (path_is_read_only_fs("/sys") > 0)
+ return NULL;
+
+ if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
+ return NULL;
+
+ /* If multiple log outputs are configured the last one is what
+ * /dev/console points to */
+ tty = strrchr(*active, ' ');
+ if (tty)
+ tty++;
+ else
+ tty = *active;
+
+ if (streq(tty, "tty0")) {
+ char *tmp;
+
+ /* Get the active VC (e.g. tty1) */
+ if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
+ free(*active);
+ tty = *active = tmp;
+ }
+ }
+
+ return tty;
+}
+
+bool tty_is_vc_resolve(const char *tty) {
+ _cleanup_free_ char *active = NULL;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (streq(tty, "console")) {
+ tty = resolve_dev_console(&active);
+ if (!tty)
+ return false;
+ }
+
+ return tty_is_vc(tty);
+}
+
+const char *default_term_for_tty(const char *tty) {
+ assert(tty);
+
+ return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+}
+
+int fd_columns(int fd) {
+ struct winsize ws = {};
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_col <= 0)
+ return -EIO;
+
+ return ws.ws_col;
+}
+
+unsigned columns(void) {
+ const char *e;
+ int c;
+
+ if (_likely_(cached_columns > 0))
+ return cached_columns;
+
+ c = 0;
+ e = getenv("COLUMNS");
+ if (e)
+ (void) safe_atoi(e, &c);
+
+ if (c <= 0)
+ c = fd_columns(STDOUT_FILENO);
+
+ if (c <= 0)
+ c = 80;
+
+ cached_columns = c;
+ return cached_columns;
+}
+
+int fd_lines(int fd) {
+ struct winsize ws = {};
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_row <= 0)
+ return -EIO;
+
+ return ws.ws_row;
+}
+
+unsigned lines(void) {
+ const char *e;
+ int l;
+
+ if (_likely_(cached_lines > 0))
+ return cached_lines;
+
+ l = 0;
+ e = getenv("LINES");
+ if (e)
+ (void) safe_atoi(e, &l);
+
+ if (l <= 0)
+ l = fd_lines(STDOUT_FILENO);
+
+ if (l <= 0)
+ l = 24;
+
+ cached_lines = l;
+ return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+ cached_columns = 0;
+ cached_lines = 0;
+}
+
+bool on_tty(void) {
+ static int cached_on_tty = -1;
+
+ if (_unlikely_(cached_on_tty < 0))
+ cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+ return cached_on_tty;
+}
+
+int make_stdio(int fd) {
+ int r, s, t;
+
+ assert(fd >= 0);
+
+ r = dup2(fd, STDIN_FILENO);
+ s = dup2(fd, STDOUT_FILENO);
+ t = dup2(fd, STDERR_FILENO);
+
+ if (fd >= 3)
+ safe_close(fd);
+
+ if (r < 0 || s < 0 || t < 0)
+ return -errno;
+
+ /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
+ * dup2() was a NOP and the bit hence possibly set. */
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_cloexec(STDERR_FILENO, false);
+
+ return 0;
+}
+
+int make_null_stdio(void) {
+ int null_fd;
+
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ if (null_fd < 0)
+ return -errno;
+
+ return make_stdio(null_fd);
+}
+
+int getttyname_malloc(int fd, char **ret) {
+ size_t l = 100;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ char path[l];
+
+ r = ttyname_r(fd, path, sizeof(path));
+ if (r == 0) {
+ const char *p;
+ char *c;
+
+ p = startswith(path, "/dev/");
+ c = strdup(p ?: path);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 0;
+ }
+
+ if (r != ERANGE)
+ return -r;
+
+ l *= 2;
+ }
+
+ return 0;
+}
+
+int getttyname_harder(int fd, char **r) {
+ int k;
+ char *s = NULL;
+
+ k = getttyname_malloc(fd, &s);
+ if (k < 0)
+ return k;
+
+ if (streq(s, "tty")) {
+ free(s);
+ return get_ctty(0, NULL, r);
+ }
+
+ *r = s;
+ return 0;
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ unsigned long ttynr;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%lu ", /* ttynr */
+ &ttynr) != 1)
+ return -EIO;
+
+ if (major(ttynr) == 0 && minor(ttynr) == 0)
+ return -ENXIO;
+
+ if (d)
+ *d = (dev_t) ttynr;
+
+ return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+ char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
+ _cleanup_free_ char *s = NULL;
+ const char *p;
+ dev_t devnr;
+ int k;
+
+ assert(r);
+
+ k = get_ctty_devnr(pid, &devnr);
+ if (k < 0)
+ return k;
+
+ sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
+
+ k = readlink_malloc(fn, &s);
+ if (k < 0) {
+
+ if (k != -ENOENT)
+ return k;
+
+ /* This is an ugly hack */
+ if (major(devnr) == 136) {
+ if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
+ return -ENOMEM;
+ } else {
+ /* Probably something like the ptys which have no
+ * symlink in /dev/char. Let's return something
+ * vaguely useful. */
+
+ b = strdup(fn + 5);
+ if (!b)
+ return -ENOMEM;
+ }
+ } else {
+ if (startswith(s, "/dev/"))
+ p = s + 5;
+ else if (startswith(s, "../"))
+ p = s + 3;
+ else
+ p = s;
+
+ b = strdup(p);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
new file mode 100644
index 0000000..188714f
--- /dev/null
+++ b/src/basic/terminal-util.h
@@ -0,0 +1,109 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_RED_ON "\x1B[31m"
+#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
+#define ANSI_GREEN_ON "\x1B[32m"
+#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
+#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
+#define ANSI_HIGHLIGHT_BLUE_ON "\x1B[1;34m"
+#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
+
+int reset_terminal_fd(int fd, bool switch_to_text);
+int reset_terminal(const char *name);
+
+int open_terminal(const char *name, int mode);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
+int release_terminal(void);
+
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int chvt(int vt);
+
+int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
+int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
+int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
+
+int vt_disallocate(const char *name);
+
+char *resolve_dev_console(char **active);
+bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(const char *tty);
+bool tty_is_console(const char *tty) _pure_;
+int vtnr_from_tty(const char *tty);
+const char *default_term_for_tty(const char *tty);
+
+void warn_melody(void);
+
+int make_stdio(int fd);
+int make_null_stdio(void);
+int make_console_stdio(void);
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
+
+int fd_columns(int fd);
+unsigned columns(void);
+int fd_lines(int fd);
+unsigned lines(void);
+void columns_lines_cache_reset(int _unused_ signum);
+
+bool on_tty(void);
+
+static inline const char *ansi_highlight(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_ON : "";
+}
+
+static inline const char *ansi_highlight_red(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
+}
+
+static inline const char *ansi_highlight_green(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
+}
+
+static inline const char *ansi_highlight_yellow(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
+}
+
+static inline const char *ansi_highlight_blue(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
+}
+
+static inline const char *ansi_highlight_off(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d);
+int get_ctty(pid_t, dev_t *_devnr, char **r);
+
+int getttyname_malloc(int fd, char **r);
+int getttyname_harder(int fd, char **r);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
new file mode 100644
index 0000000..12f1b19
--- /dev/null
+++ b/src/basic/time-util.c
@@ -0,0 +1,997 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <time.h>
+#include <string.h>
+#include <sys/timex.h>
+#include <sys/timerfd.h>
+
+#include "util.h"
+#include "time-util.h"
+#include "strv.h"
+
+usec_t now(clockid_t clock_id) {
+ struct timespec ts;
+
+ assert_se(clock_gettime(clock_id, &ts) == 0);
+
+ return timespec_load(&ts);
+}
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+ assert(ts);
+
+ ts->realtime = now(CLOCK_REALTIME);
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ return ts;
+}
+
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+ assert(ts);
+
+ if (u == USEC_INFINITY || u <= 0) {
+ ts->realtime = ts->monotonic = u;
+ return ts;
+ }
+
+ ts->realtime = u;
+
+ delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ if ((int64_t) ts->monotonic > delta)
+ ts->monotonic -= delta;
+ else
+ ts->monotonic = 0;
+
+ return ts;
+}
+
+dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+ assert(ts);
+
+ if (u == USEC_INFINITY) {
+ ts->realtime = ts->monotonic = USEC_INFINITY;
+ return ts;
+ }
+
+ ts->monotonic = u;
+ delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
+
+ ts->realtime = now(CLOCK_REALTIME);
+ if ((int64_t) ts->realtime > delta)
+ ts->realtime -= delta;
+ else
+ ts->realtime = 0;
+
+ return ts;
+}
+
+usec_t timespec_load(const struct timespec *ts) {
+ assert(ts);
+
+ if (ts->tv_sec == (time_t) -1 &&
+ ts->tv_nsec == (long) -1)
+ return USEC_INFINITY;
+
+ if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
+ return USEC_INFINITY;
+
+ return
+ (usec_t) ts->tv_sec * USEC_PER_SEC +
+ (usec_t) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+struct timespec *timespec_store(struct timespec *ts, usec_t u) {
+ assert(ts);
+
+ if (u == USEC_INFINITY) {
+ ts->tv_sec = (time_t) -1;
+ ts->tv_nsec = (long) -1;
+ return ts;
+ }
+
+ ts->tv_sec = (time_t) (u / USEC_PER_SEC);
+ ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+
+ return ts;
+}
+
+usec_t timeval_load(const struct timeval *tv) {
+ assert(tv);
+
+ if (tv->tv_sec == (time_t) -1 &&
+ tv->tv_usec == (suseconds_t) -1)
+ return USEC_INFINITY;
+
+ if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
+ return USEC_INFINITY;
+
+ return
+ (usec_t) tv->tv_sec * USEC_PER_SEC +
+ (usec_t) tv->tv_usec;
+}
+
+struct timeval *timeval_store(struct timeval *tv, usec_t u) {
+ assert(tv);
+
+ if (u == USEC_INFINITY) {
+ tv->tv_sec = (time_t) -1;
+ tv->tv_usec = (suseconds_t) -1;
+ } else {
+ tv->tv_sec = (time_t) (u / USEC_PER_SEC);
+ tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
+ }
+
+ return tv;
+}
+
+static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
+ struct tm tm;
+ time_t sec;
+
+ assert(buf);
+ assert(l > 0);
+
+ if (t <= 0 || t == USEC_INFINITY)
+ return NULL;
+
+ sec = (time_t) (t / USEC_PER_SEC);
+
+ if (utc)
+ gmtime_r(&sec, &tm);
+ else
+ localtime_r(&sec, &tm);
+ if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
+ return NULL;
+
+ return buf;
+}
+
+char *format_timestamp(char *buf, size_t l, usec_t t) {
+ return format_timestamp_internal(buf, l, t, false);
+}
+
+char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
+ return format_timestamp_internal(buf, l, t, true);
+}
+
+static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
+ struct tm tm;
+ time_t sec;
+
+ assert(buf);
+ assert(l > 0);
+
+ if (t <= 0 || t == USEC_INFINITY)
+ return NULL;
+
+ sec = (time_t) (t / USEC_PER_SEC);
+ if (utc)
+ gmtime_r(&sec, &tm);
+ else
+ localtime_r(&sec, &tm);
+
+ if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
+ return NULL;
+ snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+ if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
+ return NULL;
+
+ return buf;
+}
+
+char *format_timestamp_us(char *buf, size_t l, usec_t t) {
+ return format_timestamp_internal_us(buf, l, t, false);
+}
+
+char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
+ return format_timestamp_internal_us(buf, l, t, true);
+}
+
+char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
+ const char *s;
+ usec_t n, d;
+
+ if (t <= 0 || t == USEC_INFINITY)
+ return NULL;
+
+ n = now(CLOCK_REALTIME);
+ if (n > t) {
+ d = n - t;
+ s = "ago";
+ } else {
+ d = t - n;
+ s = "left";
+ }
+
+ if (d >= USEC_PER_YEAR)
+ snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
+ d / USEC_PER_YEAR,
+ (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
+ else if (d >= USEC_PER_MONTH)
+ snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
+ d / USEC_PER_MONTH,
+ (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
+ else if (d >= USEC_PER_WEEK)
+ snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
+ d / USEC_PER_WEEK,
+ (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
+ else if (d >= 2*USEC_PER_DAY)
+ snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+ else if (d >= 25*USEC_PER_HOUR)
+ snprintf(buf, l, "1 day " USEC_FMT "h %s",
+ (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
+ else if (d >= 6*USEC_PER_HOUR)
+ snprintf(buf, l, USEC_FMT "h %s",
+ d / USEC_PER_HOUR, s);
+ else if (d >= USEC_PER_HOUR)
+ snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+ d / USEC_PER_HOUR,
+ (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
+ else if (d >= 5*USEC_PER_MINUTE)
+ snprintf(buf, l, USEC_FMT "min %s",
+ d / USEC_PER_MINUTE, s);
+ else if (d >= USEC_PER_MINUTE)
+ snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+ d / USEC_PER_MINUTE,
+ (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
+ else if (d >= USEC_PER_SEC)
+ snprintf(buf, l, USEC_FMT "s %s",
+ d / USEC_PER_SEC, s);
+ else if (d >= USEC_PER_MSEC)
+ snprintf(buf, l, USEC_FMT "ms %s",
+ d / USEC_PER_MSEC, s);
+ else if (d > 0)
+ snprintf(buf, l, USEC_FMT"us %s",
+ d, s);
+ else
+ snprintf(buf, l, "now");
+
+ buf[l-1] = 0;
+ return buf;
+}
+
+char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
+ static const struct {
+ const char *suffix;
+ usec_t usec;
+ } table[] = {
+ { "y", USEC_PER_YEAR },
+ { "month", USEC_PER_MONTH },
+ { "w", USEC_PER_WEEK },
+ { "d", USEC_PER_DAY },
+ { "h", USEC_PER_HOUR },
+ { "min", USEC_PER_MINUTE },
+ { "s", USEC_PER_SEC },
+ { "ms", USEC_PER_MSEC },
+ { "us", 1 },
+ };
+
+ unsigned i;
+ char *p = buf;
+ bool something = false;
+
+ assert(buf);
+ assert(l > 0);
+
+ if (t == USEC_INFINITY) {
+ strncpy(p, "infinity", l-1);
+ p[l-1] = 0;
+ return p;
+ }
+
+ if (t <= 0) {
+ strncpy(p, "0", l-1);
+ p[l-1] = 0;
+ return p;
+ }
+
+ /* The result of this function can be parsed with parse_sec */
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+ int k = 0;
+ size_t n;
+ bool done = false;
+ usec_t a, b;
+
+ if (t <= 0)
+ break;
+
+ if (t < accuracy && something)
+ break;
+
+ if (t < table[i].usec)
+ continue;
+
+ if (l <= 1)
+ break;
+
+ a = t / table[i].usec;
+ b = t % table[i].usec;
+
+ /* Let's see if we should shows this in dot notation */
+ if (t < USEC_PER_MINUTE && b > 0) {
+ usec_t cc;
+ int j;
+
+ j = 0;
+ for (cc = table[i].usec; cc > 1; cc /= 10)
+ j++;
+
+ for (cc = accuracy; cc > 1; cc /= 10) {
+ b /= 10;
+ j--;
+ }
+
+ if (j > 0) {
+ k = snprintf(p, l,
+ "%s"USEC_FMT".%0*llu%s",
+ p > buf ? " " : "",
+ a,
+ j,
+ (unsigned long long) b,
+ table[i].suffix);
+
+ t = 0;
+ done = true;
+ }
+ }
+
+ /* No? Then let's show it normally */
+ if (!done) {
+ k = snprintf(p, l,
+ "%s"USEC_FMT"%s",
+ p > buf ? " " : "",
+ a,
+ table[i].suffix);
+
+ t = b;
+ }
+
+ n = MIN((size_t) k, l);
+
+ l -= n;
+ p += n;
+
+ something = true;
+ }
+
+ *p = 0;
+
+ return buf;
+}
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
+
+ assert(f);
+ assert(name);
+ assert(t);
+
+ if (!dual_timestamp_is_set(t))
+ return;
+
+ fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
+ name,
+ t->realtime,
+ t->monotonic);
+}
+
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+ unsigned long long a, b;
+
+ assert(value);
+ assert(t);
+
+ if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+ log_debug("Failed to parse finish timestamp value %s.", value);
+ return -EINVAL;
+ }
+
+ t->realtime = a;
+ t->monotonic = b;
+
+ return 0;
+}
+
+int parse_timestamp(const char *t, usec_t *usec) {
+ static const struct {
+ const char *name;
+ const int nr;
+ } day_nr[] = {
+ { "Sunday", 0 },
+ { "Sun", 0 },
+ { "Monday", 1 },
+ { "Mon", 1 },
+ { "Tuesday", 2 },
+ { "Tue", 2 },
+ { "Wednesday", 3 },
+ { "Wed", 3 },
+ { "Thursday", 4 },
+ { "Thu", 4 },
+ { "Friday", 5 },
+ { "Fri", 5 },
+ { "Saturday", 6 },
+ { "Sat", 6 },
+ };
+
+ const char *k;
+ struct tm tm, copy;
+ time_t x;
+ usec_t plus = 0, minus = 0, ret;
+ int r, weekday = -1;
+ unsigned i;
+
+ /*
+ * Allowed syntaxes:
+ *
+ * 2012-09-22 16:34:22
+ * 2012-09-22 16:34 (seconds will be set to 0)
+ * 2012-09-22 (time will be set to 00:00:00)
+ * 16:34:22 (date will be set to today)
+ * 16:34 (date will be set to today, seconds to 0)
+ * now
+ * yesterday (time is set to 00:00:00)
+ * today (time is set to 00:00:00)
+ * tomorrow (time is set to 00:00:00)
+ * +5min
+ * -5days
+ * @2147483647 (seconds since epoch)
+ *
+ */
+
+ assert(t);
+ assert(usec);
+
+ x = time(NULL);
+ assert_se(localtime_r(&x, &tm));
+ tm.tm_isdst = -1;
+
+ if (streq(t, "now"))
+ goto finish;
+
+ else if (streq(t, "today")) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (streq(t, "yesterday")) {
+ tm.tm_mday --;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (streq(t, "tomorrow")) {
+ tm.tm_mday ++;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (t[0] == '+') {
+ r = parse_sec(t+1, &plus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+
+ } else if (t[0] == '-') {
+ r = parse_sec(t+1, &minus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+
+ } else if (t[0] == '@')
+ return parse_sec(t + 1, usec);
+
+ else if (endswith(t, " ago")) {
+ _cleanup_free_ char *z;
+
+ z = strndup(t, strlen(t) - 4);
+ if (!z)
+ return -ENOMEM;
+
+ r = parse_sec(z, &minus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+ } else if (endswith(t, " left")) {
+ _cleanup_free_ char *z;
+
+ z = strndup(t, strlen(t) - 4);
+ if (!z)
+ return -ENOMEM;
+
+ r = parse_sec(z, &plus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+ }
+
+ for (i = 0; i < ELEMENTSOF(day_nr); i++) {
+ size_t skip;
+
+ if (!startswith_no_case(t, day_nr[i].name))
+ continue;
+
+ skip = strlen(day_nr[i].name);
+ if (t[skip] != ' ')
+ continue;
+
+ weekday = day_nr[i].nr;
+ t += skip + 1;
+ break;
+ }
+
+ copy = tm;
+ k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%y-%m-%d %H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%y-%m-%d", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ return -EINVAL;
+
+finish:
+ x = mktime(&tm);
+ if (x == (time_t) -1)
+ return -EINVAL;
+
+ if (weekday >= 0 && tm.tm_wday != weekday)
+ return -EINVAL;
+
+ ret = (usec_t) x * USEC_PER_SEC;
+
+ ret += plus;
+ if (ret > minus)
+ ret -= minus;
+ else
+ ret = 0;
+
+ *usec = ret;
+
+ return 0;
+}
+
+int parse_sec(const char *t, usec_t *usec) {
+ static const struct {
+ const char *suffix;
+ usec_t usec;
+ } table[] = {
+ { "seconds", USEC_PER_SEC },
+ { "second", USEC_PER_SEC },
+ { "sec", USEC_PER_SEC },
+ { "s", USEC_PER_SEC },
+ { "minutes", USEC_PER_MINUTE },
+ { "minute", USEC_PER_MINUTE },
+ { "min", USEC_PER_MINUTE },
+ { "months", USEC_PER_MONTH },
+ { "month", USEC_PER_MONTH },
+ { "msec", USEC_PER_MSEC },
+ { "ms", USEC_PER_MSEC },
+ { "m", USEC_PER_MINUTE },
+ { "hours", USEC_PER_HOUR },
+ { "hour", USEC_PER_HOUR },
+ { "hr", USEC_PER_HOUR },
+ { "h", USEC_PER_HOUR },
+ { "days", USEC_PER_DAY },
+ { "day", USEC_PER_DAY },
+ { "d", USEC_PER_DAY },
+ { "weeks", USEC_PER_WEEK },
+ { "week", USEC_PER_WEEK },
+ { "w", USEC_PER_WEEK },
+ { "years", USEC_PER_YEAR },
+ { "year", USEC_PER_YEAR },
+ { "y", USEC_PER_YEAR },
+ { "usec", 1ULL },
+ { "us", 1ULL },
+ { "", USEC_PER_SEC }, /* default is sec */
+ };
+
+ const char *p, *s;
+ usec_t r = 0;
+ bool something = false;
+
+ assert(t);
+ assert(usec);
+
+ p = t;
+
+ p += strspn(p, WHITESPACE);
+ s = startswith(p, "infinity");
+ if (s) {
+ s += strspn(s, WHITESPACE);
+ if (*s != 0)
+ return -EINVAL;
+
+ *usec = USEC_INFINITY;
+ return 0;
+ }
+
+ for (;;) {
+ long long l, z = 0;
+ char *e;
+ unsigned i, n = 0;
+
+ p += strspn(p, WHITESPACE);
+
+ if (*p == 0) {
+ if (!something)
+ return -EINVAL;
+
+ break;
+ }
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno > 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (*e == '.') {
+ char *b = e + 1;
+
+ errno = 0;
+ z = strtoll(b, &e, 10);
+ if (errno > 0)
+ return -errno;
+
+ if (z < 0)
+ return -ERANGE;
+
+ if (e == b)
+ return -EINVAL;
+
+ n = e - b;
+
+ } else if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ usec_t k = (usec_t) z * table[i].usec;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (usec_t) l * table[i].usec + k;
+ p = e + strlen(table[i].suffix);
+
+ something = true;
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ return -EINVAL;
+
+ }
+
+ *usec = r;
+
+ return 0;
+}
+
+int parse_nsec(const char *t, nsec_t *nsec) {
+ static const struct {
+ const char *suffix;
+ nsec_t nsec;
+ } table[] = {
+ { "seconds", NSEC_PER_SEC },
+ { "second", NSEC_PER_SEC },
+ { "sec", NSEC_PER_SEC },
+ { "s", NSEC_PER_SEC },
+ { "minutes", NSEC_PER_MINUTE },
+ { "minute", NSEC_PER_MINUTE },
+ { "min", NSEC_PER_MINUTE },
+ { "months", NSEC_PER_MONTH },
+ { "month", NSEC_PER_MONTH },
+ { "msec", NSEC_PER_MSEC },
+ { "ms", NSEC_PER_MSEC },
+ { "m", NSEC_PER_MINUTE },
+ { "hours", NSEC_PER_HOUR },
+ { "hour", NSEC_PER_HOUR },
+ { "hr", NSEC_PER_HOUR },
+ { "h", NSEC_PER_HOUR },
+ { "days", NSEC_PER_DAY },
+ { "day", NSEC_PER_DAY },
+ { "d", NSEC_PER_DAY },
+ { "weeks", NSEC_PER_WEEK },
+ { "week", NSEC_PER_WEEK },
+ { "w", NSEC_PER_WEEK },
+ { "years", NSEC_PER_YEAR },
+ { "year", NSEC_PER_YEAR },
+ { "y", NSEC_PER_YEAR },
+ { "usec", NSEC_PER_USEC },
+ { "us", NSEC_PER_USEC },
+ { "nsec", 1ULL },
+ { "ns", 1ULL },
+ { "", 1ULL }, /* default is nsec */
+ };
+
+ const char *p, *s;
+ nsec_t r = 0;
+ bool something = false;
+
+ assert(t);
+ assert(nsec);
+
+ p = t;
+
+ p += strspn(p, WHITESPACE);
+ s = startswith(p, "infinity");
+ if (s) {
+ s += strspn(s, WHITESPACE);
+ if (*s != 0)
+ return -EINVAL;
+
+ *nsec = NSEC_INFINITY;
+ return 0;
+ }
+
+ for (;;) {
+ long long l, z = 0;
+ char *e;
+ unsigned i, n = 0;
+
+ p += strspn(p, WHITESPACE);
+
+ if (*p == 0) {
+ if (!something)
+ return -EINVAL;
+
+ break;
+ }
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno > 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (*e == '.') {
+ char *b = e + 1;
+
+ errno = 0;
+ z = strtoll(b, &e, 10);
+ if (errno > 0)
+ return -errno;
+
+ if (z < 0)
+ return -ERANGE;
+
+ if (e == b)
+ return -EINVAL;
+
+ n = e - b;
+
+ } else if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ nsec_t k = (nsec_t) z * table[i].nsec;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (nsec_t) l * table[i].nsec + k;
+ p = e + strlen(table[i].suffix);
+
+ something = true;
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ return -EINVAL;
+
+ }
+
+ *nsec = r;
+
+ return 0;
+}
+
+bool ntp_synced(void) {
+ struct timex txc = {};
+
+ if (adjtimex(&txc) < 0)
+ return false;
+
+ if (txc.status & STA_UNSYNC)
+ return false;
+
+ return true;
+}
+
+int get_timezones(char ***ret) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **zones = NULL;
+ size_t n_zones = 0, n_allocated = 0;
+
+ assert(ret);
+
+ zones = strv_new("UTC", NULL);
+ if (!zones)
+ return -ENOMEM;
+
+ n_allocated = 2;
+ n_zones = 1;
+
+ f = fopen("/usr/share/zoneinfo/zone.tab", "re");
+ if (f) {
+ char l[LINE_MAX];
+
+ FOREACH_LINE(l, f, return -errno) {
+ char *p, *w;
+ size_t k;
+
+ p = strstrip(l);
+
+ if (isempty(p) || *p == '#')
+ continue;
+
+ /* Skip over country code */
+ p += strcspn(p, WHITESPACE);
+ p += strspn(p, WHITESPACE);
+
+ /* Skip over coordinates */
+ p += strcspn(p, WHITESPACE);
+ p += strspn(p, WHITESPACE);
+
+ /* Found timezone name */
+ k = strcspn(p, WHITESPACE);
+ if (k <= 0)
+ continue;
+
+ w = strndup(p, k);
+ if (!w)
+ return -ENOMEM;
+
+ if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
+ free(w);
+ return -ENOMEM;
+ }
+
+ zones[n_zones++] = w;
+ zones[n_zones] = NULL;
+ }
+
+ strv_sort(zones);
+
+ } else if (errno != ENOENT)
+ return -errno;
+
+ *ret = zones;
+ zones = NULL;
+
+ return 0;
+}
+
+bool timezone_is_valid(const char *name) {
+ bool slash = false;
+ const char *p, *t;
+ struct stat st;
+
+ if (!name || *name == 0 || *name == '/')
+ return false;
+
+ for (p = name; *p; p++) {
+ if (!(*p >= '0' && *p <= '9') &&
+ !(*p >= 'a' && *p <= 'z') &&
+ !(*p >= 'A' && *p <= 'Z') &&
+ !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
+ return false;
+
+ if (*p == '/') {
+
+ if (slash)
+ return false;
+
+ slash = true;
+ } else
+ slash = false;
+ }
+
+ if (slash)
+ return false;
+
+ t = strjoina("/usr/share/zoneinfo/", name);
+ if (stat(t, &st) < 0)
+ return false;
+
+ if (!S_ISREG(st.st_mode))
+ return false;
+
+ 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;
+ }
+
+ return clock;
+}
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
new file mode 100644
index 0000000..7a64d45
--- /dev/null
+++ b/src/basic/time-util.h
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdio.h>
+#include <inttypes.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#define NSEC_FMT "%" PRIu64
+#define USEC_FMT "%" PRIu64
+
+#include "macro.h"
+
+typedef struct dual_timestamp {
+ usec_t realtime;
+ usec_t monotonic;
+} dual_timestamp;
+
+#define USEC_INFINITY ((usec_t) -1)
+#define NSEC_INFINITY ((nsec_t) -1)
+
+#define MSEC_PER_SEC 1000ULL
+#define USEC_PER_SEC ((usec_t) 1000000ULL)
+#define USEC_PER_MSEC ((usec_t) 1000ULL)
+#define NSEC_PER_SEC ((nsec_t) 1000000000ULL)
+#define NSEC_PER_MSEC ((nsec_t) 1000000ULL)
+#define NSEC_PER_USEC ((nsec_t) 1000ULL)
+
+#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC))
+#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC))
+#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE))
+#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE))
+#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR))
+#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR))
+#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY))
+#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY))
+#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC))
+#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC))
+#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
+#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
+
+#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
+#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+
+#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
+
+#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
+
+usec_t now(clockid_t clock);
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
+dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
+
+static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
+ return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
+ (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
+}
+
+usec_t timespec_load(const struct timespec *ts) _pure_;
+struct timespec *timespec_store(struct timespec *ts, usec_t u);
+
+usec_t timeval_load(const struct timeval *tv) _pure_;
+struct timeval *timeval_store(struct timeval *tv, usec_t u);
+
+char *format_timestamp(char *buf, size_t l, usec_t t);
+char *format_timestamp_utc(char *buf, size_t l, usec_t t);
+char *format_timestamp_us(char *buf, size_t l, usec_t t);
+char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
+char *format_timestamp_relative(char *buf, size_t l, usec_t t);
+char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+
+int parse_timestamp(const char *t, usec_t *usec);
+
+int parse_sec(const char *t, usec_t *usec);
+int parse_nsec(const char *t, nsec_t *nsec);
+
+bool ntp_synced(void);
+
+int get_timezones(char ***l);
+bool timezone_is_valid(const char *name);
+
+clockid_t clock_boottime_or_monotonic(void);
+
+#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)
diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h
new file mode 100644
index 0000000..d6181dd
--- /dev/null
+++ b/src/basic/unaligned.h
@@ -0,0 +1,66 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Tom Gundersen
+
+ 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 <stdint.h>
+
+static inline uint16_t unaligned_read_be16(const void *_u) {
+ const uint8_t *u = _u;
+
+ return (((uint16_t) u[0]) << 8) |
+ ((uint16_t) u[1]);
+}
+
+static inline uint32_t unaligned_read_be32(const void *_u) {
+ const uint8_t *u = _u;
+
+ return (((uint32_t) unaligned_read_be16(u)) << 16) |
+ ((uint32_t) unaligned_read_be16(u + 2));
+}
+
+static inline uint64_t unaligned_read_be64(const void *_u) {
+ const uint8_t *u = _u;
+
+ return (((uint64_t) unaligned_read_be32(u)) << 32) |
+ ((uint64_t) unaligned_read_be32(u + 4));
+}
+
+static inline void unaligned_write_be16(void *_u, uint16_t a) {
+ uint8_t *u = _u;
+
+ u[0] = (uint8_t) (a >> 8);
+ u[1] = (uint8_t) a;
+}
+
+static inline void unaligned_write_be32(void *_u, uint32_t a) {
+ uint8_t *u = _u;
+
+ unaligned_write_be16(u, (uint16_t) (a >> 16));
+ unaligned_write_be16(u + 2, (uint16_t) a);
+}
+
+static inline void unaligned_write_be64(void *_u, uint64_t a) {
+ uint8_t *u = _u;
+
+ unaligned_write_be32(u, (uint32_t) (a >> 32));
+ unaligned_write_be32(u + 4, (uint32_t) a);
+}
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
new file mode 100644
index 0000000..bf52463
--- /dev/null
+++ b/src/basic/unit-name.c
@@ -0,0 +1,834 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <errno.h>
+#include <string.h>
+
+#include "path-util.h"
+#include "bus-label.h"
+#include "util.h"
+#include "unit-name.h"
+#include "def.h"
+#include "strv.h"
+
+#define VALID_CHARS \
+ DIGITS LETTERS \
+ ":-_.\\"
+
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
+ const char *e, *i, *at;
+
+ assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
+
+ if (_unlikely_(flags == 0))
+ return false;
+
+ if (isempty(n))
+ return false;
+
+ if (strlen(n) >= UNIT_NAME_MAX)
+ return false;
+
+ e = strrchr(n, '.');
+ if (!e || e == n)
+ return false;
+
+ if (unit_type_from_string(e + 1) < 0)
+ return false;
+
+ for (i = n, at = NULL; i < e; i++) {
+
+ if (*i == '@' && !at)
+ at = i;
+
+ if (!strchr("@" VALID_CHARS, *i))
+ return false;
+ }
+
+ if (at == n)
+ return false;
+
+ if (flags & UNIT_NAME_PLAIN)
+ if (!at)
+ return true;
+
+ if (flags & UNIT_NAME_INSTANCE)
+ if (at && e > at + 1)
+ return true;
+
+ if (flags & UNIT_NAME_TEMPLATE)
+ if (at && e == at + 1)
+ return true;
+
+ return false;
+}
+
+bool unit_prefix_is_valid(const char *p) {
+
+ /* We don't allow additional @ in the prefix string */
+
+ if (isempty(p))
+ return false;
+
+ return in_charset(p, VALID_CHARS);
+}
+
+bool unit_instance_is_valid(const char *i) {
+
+ /* The max length depends on the length of the string, so we
+ * don't really check this here. */
+
+ if (isempty(i))
+ return false;
+
+ /* We allow additional @ in the instance string, we do not
+ * allow them in the prefix! */
+
+ return in_charset(i, "@" VALID_CHARS);
+}
+
+bool unit_suffix_is_valid(const char *s) {
+ if (isempty(s))
+ return false;
+
+ if (s[0] != '.')
+ return false;
+
+ if (unit_type_from_string(s + 1) < 0)
+ return false;
+
+ return true;
+}
+
+int unit_name_to_prefix(const char *n, char **ret) {
+ const char *p;
+ char *s;
+
+ assert(n);
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ p = strchr(n, '@');
+ if (!p)
+ p = strrchr(n, '.');
+
+ assert_se(p);
+
+ s = strndup(n, p - n);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+int unit_name_to_instance(const char *n, char **instance) {
+ const char *p, *d;
+ char *i;
+
+ assert(n);
+ assert(instance);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ /* Everything past the first @ and before the last . is the instance */
+ p = strchr(n, '@');
+ if (!p) {
+ *instance = NULL;
+ return 0;
+ }
+
+ p++;
+
+ d = strrchr(p, '.');
+ if (!d)
+ return -EINVAL;
+
+ i = strndup(p, d-p);
+ if (!i)
+ return -ENOMEM;
+
+ *instance = i;
+ return 1;
+}
+
+int unit_name_to_prefix_and_instance(const char *n, char **ret) {
+ const char *d;
+ char *s;
+
+ assert(n);
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ d = strrchr(n, '.');
+ if (!d)
+ return -EINVAL;
+
+ s = strndup(n, d - n);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+UnitType unit_name_to_type(const char *n) {
+ const char *e;
+
+ assert(n);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return _UNIT_TYPE_INVALID;
+
+ assert_se(e = strrchr(n, '.'));
+
+ return unit_type_from_string(e + 1);
+}
+
+int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
+ char *e, *s;
+ size_t a, b;
+
+ assert(n);
+ assert(suffix);
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ assert_se(e = strrchr(n, '.'));
+
+ a = e - n;
+ b = strlen(suffix);
+
+ s = new(char, a + b + 1);
+ if (!s)
+ return -ENOMEM;
+
+ strcpy(mempcpy(s, n, a), suffix);
+ *ret = s;
+
+ return 0;
+}
+
+int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
+ char *s;
+
+ assert(prefix);
+ assert(suffix);
+ assert(ret);
+
+ if (!unit_prefix_is_valid(prefix))
+ return -EINVAL;
+
+ if (instance && !unit_instance_is_valid(instance))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ if (!instance)
+ s = strappend(prefix, suffix);
+ else
+ s = strjoin(prefix, "@", instance, suffix, NULL);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+static char *do_escape_char(char c, char *t) {
+ assert(t);
+
+ *(t++) = '\\';
+ *(t++) = 'x';
+ *(t++) = hexchar(c >> 4);
+ *(t++) = hexchar(c);
+
+ return t;
+}
+
+static char *do_escape(const char *f, char *t) {
+ assert(f);
+ assert(t);
+
+ /* do not create units with a leading '.', like for "/.dotdir" mount points */
+ if (*f == '.') {
+ t = do_escape_char(*f, t);
+ f++;
+ }
+
+ for (; *f; f++) {
+ if (*f == '/')
+ *(t++) = '-';
+ else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
+ t = do_escape_char(*f, t);
+ else
+ *(t++) = *f;
+ }
+
+ return t;
+}
+
+char *unit_name_escape(const char *f) {
+ char *r, *t;
+
+ assert(f);
+
+ r = new(char, strlen(f)*4+1);
+ if (!r)
+ return NULL;
+
+ t = do_escape(f, r);
+ *t = 0;
+
+ return r;
+}
+
+int unit_name_unescape(const char *f, char **ret) {
+ _cleanup_free_ char *r = NULL;
+ char *t;
+
+ assert(f);
+
+ r = strdup(f);
+ if (!r)
+ return -ENOMEM;
+
+ for (t = r; *f; f++) {
+ if (*f == '-')
+ *(t++) = '/';
+ else if (*f == '\\') {
+ int a, b;
+
+ if (f[1] != 'x')
+ return -EINVAL;
+
+ a = unhexchar(f[2]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(f[3]);
+ if (b < 0)
+ return -EINVAL;
+
+ *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
+ f += 3;
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ *ret = r;
+ r = NULL;
+
+ return 0;
+}
+
+int unit_name_path_escape(const char *f, char **ret) {
+ char *p, *s;
+
+ assert(f);
+ assert(ret);
+
+ p = strdupa(f);
+ if (!p)
+ return -ENOMEM;
+
+ path_kill_slashes(p);
+
+ if (STR_IN_SET(p, "/", ""))
+ s = strdup("-");
+ else {
+ char *e;
+
+ if (!path_is_safe(p))
+ return -EINVAL;
+
+ /* Truncate trailing slashes */
+ e = endswith(p, "/");
+ if (e)
+ *e = 0;
+
+ /* Truncate leading slashes */
+ if (p[0] == '/')
+ p++;
+
+ s = unit_name_escape(p);
+ }
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+int unit_name_path_unescape(const char *f, char **ret) {
+ char *s;
+ int r;
+
+ assert(f);
+
+ if (isempty(f))
+ return -EINVAL;
+
+ if (streq(f, "-")) {
+ s = strdup("/");
+ if (!s)
+ return -ENOMEM;
+ } else {
+ char *w;
+
+ r = unit_name_unescape(f, &w);
+ if (r < 0)
+ return r;
+
+ /* Don't accept trailing or leading slashes */
+ if (startswith(w, "/") || endswith(w, "/")) {
+ free(w);
+ return -EINVAL;
+ }
+
+ /* Prefix a slash again */
+ s = strappend("/", w);
+ free(w);
+ if (!s)
+ return -ENOMEM;
+
+ if (!path_is_safe(s)) {
+ free(s);
+ return -EINVAL;
+ }
+ }
+
+ if (ret)
+ *ret = s;
+ else
+ free(s);
+
+ return 0;
+}
+
+int unit_name_replace_instance(const char *f, const char *i, char **ret) {
+ const char *p, *e;
+ char *s;
+ size_t a, b;
+
+ assert(f);
+ assert(i);
+ assert(ret);
+
+ if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+ if (!unit_instance_is_valid(i))
+ return -EINVAL;
+
+ assert_se(p = strchr(f, '@'));
+ assert_se(e = strrchr(f, '.'));
+
+ a = p - f;
+ b = strlen(i);
+
+ s = new(char, a + 1 + b + strlen(e) + 1);
+ if (!s)
+ return -ENOMEM;
+
+ strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
+
+ *ret = s;
+ return 0;
+}
+
+int unit_name_template(const char *f, char **ret) {
+ const char *p, *e;
+ char *s;
+ size_t a;
+
+ assert(f);
+ assert(ret);
+
+ if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+
+ assert_se(p = strchr(f, '@'));
+ assert_se(e = strrchr(f, '.'));
+
+ a = p - f;
+
+ s = new(char, a + 1 + strlen(e) + 1);
+ if (!s)
+ return -ENOMEM;
+
+ strcpy(mempcpy(s, f, a + 1), e);
+
+ *ret = s;
+ return 0;
+}
+
+int unit_name_from_path(const char *path, const char *suffix, char **ret) {
+ _cleanup_free_ char *p = NULL;
+ char *s = NULL;
+ int r;
+
+ assert(path);
+ assert(suffix);
+ assert(ret);
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ r = unit_name_path_escape(path, &p);
+ if (r < 0)
+ return r;
+
+ s = strappend(p, suffix);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
+ _cleanup_free_ char *p = NULL;
+ char *s;
+ int r;
+
+ assert(prefix);
+ assert(path);
+ assert(suffix);
+ assert(ret);
+
+ if (!unit_prefix_is_valid(prefix))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ r = unit_name_path_escape(path, &p);
+ if (r < 0)
+ return r;
+
+ s = strjoin(prefix, "@", p, suffix, NULL);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+int unit_name_to_path(const char *name, char **ret) {
+ _cleanup_free_ char *prefix = NULL;
+ int r;
+
+ assert(name);
+
+ r = unit_name_to_prefix(name, &prefix);
+ if (r < 0)
+ return r;
+
+ return unit_name_path_unescape(prefix, ret);
+}
+
+char *unit_dbus_path_from_name(const char *name) {
+ _cleanup_free_ char *e = NULL;
+
+ assert(name);
+
+ e = bus_label_escape(name);
+ if (!e)
+ return NULL;
+
+ return strappend("/org/freedesktop/systemd1/unit/", e);
+}
+
+int unit_name_from_dbus_path(const char *path, char **name) {
+ const char *e;
+ char *n;
+
+ e = startswith(path, "/org/freedesktop/systemd1/unit/");
+ if (!e)
+ return -EINVAL;
+
+ n = bus_label_unescape(e);
+ if (!n)
+ return -ENOMEM;
+
+ *name = n;
+ return 0;
+}
+
+static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
+ const char *valid_chars;
+
+ assert(f);
+ assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
+ assert(t);
+
+ /* We'll only escape the obvious characters here, to play
+ * safe. */
+
+ valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
+
+ for (; *f; f++) {
+ if (*f == '/')
+ *(t++) = '-';
+ else if (!strchr(valid_chars, *f))
+ t = do_escape_char(*f, t);
+ else
+ *(t++) = *f;
+ }
+
+ return t;
+}
+
+/**
+ * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
+ * /blah/blah is converted to blah-blah.mount, anything else is left alone,
+ * except that @suffix is appended if a valid unit suffix is not present.
+ *
+ * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
+ */
+int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
+ char *s, *t;
+ int r;
+
+ assert(name);
+ assert(suffix);
+ assert(ret);
+
+ if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
+ /* No mangling necessary... */
+ s = strdup(name);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+ }
+
+ if (is_device_path(name)) {
+ r = unit_name_from_path(name, ".device", ret);
+ if (r >= 0)
+ return 1;
+ if (r != -EINVAL)
+ return r;
+ }
+
+ if (path_is_absolute(name)) {
+ r = unit_name_from_path(name, ".mount", ret);
+ if (r >= 0)
+ return 1;
+ if (r != -EINVAL)
+ return r;
+ }
+
+ s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
+ if (!s)
+ return -ENOMEM;
+
+ t = do_escape_mangle(name, allow_globs, s);
+ *t = 0;
+
+ if (unit_name_to_type(s) < 0)
+ strcpy(t, suffix);
+
+ *ret = s;
+ return 1;
+}
+
+int slice_build_parent_slice(const char *slice, char **ret) {
+ char *s, *dash;
+
+ assert(slice);
+ assert(ret);
+
+ if (!slice_name_is_valid(slice))
+ return -EINVAL;
+
+ if (streq(slice, "-.slice")) {
+ *ret = NULL;
+ return 0;
+ }
+
+ s = strdup(slice);
+ if (!s)
+ return -ENOMEM;
+
+ dash = strrchr(s, '-');
+ if (dash)
+ strcpy(dash, ".slice");
+ else {
+ free(s);
+
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
+ }
+
+ *ret = s;
+ return 1;
+}
+
+int slice_build_subslice(const char *slice, const char*name, char **ret) {
+ char *subslice;
+
+ assert(slice);
+ assert(name);
+ assert(ret);
+
+ if (!slice_name_is_valid(slice))
+ return -EINVAL;
+
+ if (!unit_prefix_is_valid(name))
+ return -EINVAL;
+
+ if (streq(slice, "-.slice"))
+ subslice = strappend(name, ".slice");
+ else {
+ char *e;
+
+ assert_se(e = endswith(slice, ".slice"));
+
+ subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
+ if (!subslice)
+ return -ENOMEM;
+
+ stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
+ }
+
+ *ret = subslice;
+ return 0;
+}
+
+bool slice_name_is_valid(const char *name) {
+ const char *p, *e;
+ bool dash = false;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
+ return false;
+
+ if (streq(name, "-.slice"))
+ return true;
+
+ e = endswith(name, ".slice");
+ if (!e)
+ return false;
+
+ for (p = name; p < e; p++) {
+
+ if (*p == '-') {
+
+ /* Don't allow initial dash */
+ if (p == name)
+ return false;
+
+ /* Don't allow multiple dashes */
+ if (dash)
+ return false;
+
+ dash = true;
+ } else
+ dash = false;
+ }
+
+ /* Don't allow trailing hash */
+ if (dash)
+ return false;
+
+ return true;
+}
+
+static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
+ [UNIT_SERVICE] = "service",
+ [UNIT_SOCKET] = "socket",
+ [UNIT_BUSNAME] = "busname",
+ [UNIT_TARGET] = "target",
+ [UNIT_SNAPSHOT] = "snapshot",
+ [UNIT_DEVICE] = "device",
+ [UNIT_MOUNT] = "mount",
+ [UNIT_AUTOMOUNT] = "automount",
+ [UNIT_SWAP] = "swap",
+ [UNIT_TIMER] = "timer",
+ [UNIT_PATH] = "path",
+ [UNIT_SLICE] = "slice",
+ [UNIT_SCOPE] = "scope"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
+
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+ [UNIT_STUB] = "stub",
+ [UNIT_LOADED] = "loaded",
+ [UNIT_NOT_FOUND] = "not-found",
+ [UNIT_ERROR] = "error",
+ [UNIT_MERGED] = "merged",
+ [UNIT_MASKED] = "masked"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+
+static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
+ [UNIT_REQUIRES] = "Requires",
+ [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
+ [UNIT_REQUISITE] = "Requisite",
+ [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
+ [UNIT_WANTS] = "Wants",
+ [UNIT_BINDS_TO] = "BindsTo",
+ [UNIT_PART_OF] = "PartOf",
+ [UNIT_REQUIRED_BY] = "RequiredBy",
+ [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
+ [UNIT_REQUISITE_OF] = "RequisiteOf",
+ [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
+ [UNIT_WANTED_BY] = "WantedBy",
+ [UNIT_BOUND_BY] = "BoundBy",
+ [UNIT_CONSISTS_OF] = "ConsistsOf",
+ [UNIT_CONFLICTS] = "Conflicts",
+ [UNIT_CONFLICTED_BY] = "ConflictedBy",
+ [UNIT_BEFORE] = "Before",
+ [UNIT_AFTER] = "After",
+ [UNIT_ON_FAILURE] = "OnFailure",
+ [UNIT_TRIGGERS] = "Triggers",
+ [UNIT_TRIGGERED_BY] = "TriggeredBy",
+ [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
+ [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
+ [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
+ [UNIT_REFERENCES] = "References",
+ [UNIT_REFERENCED_BY] = "ReferencedBy",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h
new file mode 100644
index 0000000..b2043d0
--- /dev/null
+++ b/src/basic/unit-name.h
@@ -0,0 +1,177 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <stdbool.h>
+
+#include "macro.h"
+
+#define UNIT_NAME_MAX 256
+
+typedef enum UnitType UnitType;
+typedef enum UnitLoadState UnitLoadState;
+typedef enum UnitDependency UnitDependency;
+
+enum UnitType {
+ UNIT_SERVICE = 0,
+ UNIT_SOCKET,
+ UNIT_BUSNAME,
+ UNIT_TARGET,
+ UNIT_SNAPSHOT,
+ UNIT_DEVICE,
+ UNIT_MOUNT,
+ UNIT_AUTOMOUNT,
+ UNIT_SWAP,
+ UNIT_TIMER,
+ UNIT_PATH,
+ UNIT_SLICE,
+ UNIT_SCOPE,
+ _UNIT_TYPE_MAX,
+ _UNIT_TYPE_INVALID = -1
+};
+
+enum UnitLoadState {
+ UNIT_STUB = 0,
+ UNIT_LOADED,
+ UNIT_NOT_FOUND,
+ UNIT_ERROR,
+ UNIT_MERGED,
+ UNIT_MASKED,
+ _UNIT_LOAD_STATE_MAX,
+ _UNIT_LOAD_STATE_INVALID = -1
+};
+
+enum UnitDependency {
+ /* Positive dependencies */
+ UNIT_REQUIRES,
+ UNIT_REQUIRES_OVERRIDABLE,
+ UNIT_REQUISITE,
+ UNIT_REQUISITE_OVERRIDABLE,
+ UNIT_WANTS,
+ UNIT_BINDS_TO,
+ UNIT_PART_OF,
+
+ /* Inverse of the above */
+ UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
+ UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' is 'required_by_overridable' */
+ UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */
+ UNIT_REQUISITE_OF_OVERRIDABLE,/* inverse of 'requisite_overridable' is 'requisite_of_overridable' */
+ UNIT_WANTED_BY, /* inverse of 'wants' */
+ UNIT_BOUND_BY, /* inverse of 'binds_to' */
+ UNIT_CONSISTS_OF, /* inverse of 'part_of' */
+
+ /* Negative dependencies */
+ UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
+ UNIT_CONFLICTED_BY,
+
+ /* Order */
+ UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
+ UNIT_AFTER,
+
+ /* On Failure */
+ UNIT_ON_FAILURE,
+
+ /* Triggers (i.e. a socket triggers a service) */
+ UNIT_TRIGGERS,
+ UNIT_TRIGGERED_BY,
+
+ /* Propagate reloads */
+ UNIT_PROPAGATES_RELOAD_TO,
+ UNIT_RELOAD_PROPAGATED_FROM,
+
+ /* Joins namespace of */
+ UNIT_JOINS_NAMESPACE_OF,
+
+ /* Reference information for GC logic */
+ UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
+ UNIT_REFERENCED_BY,
+
+ _UNIT_DEPENDENCY_MAX,
+ _UNIT_DEPENDENCY_INVALID = -1
+};
+
+typedef enum UnitNameFlags {
+ UNIT_NAME_PLAIN = 1, /* Allow foo.service */
+ UNIT_NAME_INSTANCE = 2, /* Allow foo at bar.service */
+ UNIT_NAME_TEMPLATE = 4, /* Allow foo at .service */
+ UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE,
+} UnitNameFlags;
+
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;
+bool unit_prefix_is_valid(const char *p) _pure_;
+bool unit_instance_is_valid(const char *i) _pure_;
+bool unit_suffix_is_valid(const char *s) _pure_;
+
+static inline int unit_prefix_and_instance_is_valid(const char *p) {
+ /* For prefix+instance and instance the same rules apply */
+ return unit_instance_is_valid(p);
+}
+
+int unit_name_to_prefix(const char *n, char **prefix);
+int unit_name_to_instance(const char *n, char **instance);
+int unit_name_to_prefix_and_instance(const char *n, char **ret);
+
+UnitType unit_name_to_type(const char *n) _pure_;
+
+int unit_name_change_suffix(const char *n, const char *suffix, char **ret);
+
+int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret);
+
+char *unit_name_escape(const char *f);
+int unit_name_unescape(const char *f, char **ret);
+int unit_name_path_escape(const char *f, char **ret);
+int unit_name_path_unescape(const char *f, char **ret);
+
+int unit_name_replace_instance(const char *f, const char *i, char **ret);
+
+int unit_name_template(const char *f, char **ret);
+
+int unit_name_from_path(const char *path, const char *suffix, char **ret);
+int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret);
+int unit_name_to_path(const char *name, char **ret);
+
+char *unit_dbus_path_from_name(const char *name);
+int unit_name_from_dbus_path(const char *path, char **name);
+
+typedef enum UnitNameMangle {
+ UNIT_NAME_NOGLOB,
+ UNIT_NAME_GLOB,
+} UnitNameMangle;
+
+int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret);
+
+static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) {
+ return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret);
+}
+
+int slice_build_parent_slice(const char *slice, char **ret);
+int slice_build_subslice(const char *slice, const char*name, char **subslice);
+bool slice_name_is_valid(const char *name);
+
+const char *unit_type_to_string(UnitType i) _const_;
+UnitType unit_type_from_string(const char *s) _pure_;
+
+const char *unit_load_state_to_string(UnitLoadState i) _const_;
+UnitLoadState unit_load_state_from_string(const char *s) _pure_;
+
+const char *unit_dependency_to_string(UnitDependency i) _const_;
+UnitDependency unit_dependency_from_string(const char *s) _pure_;
diff --git a/src/basic/utf8.c b/src/basic/utf8.c
new file mode 100644
index 0000000..800884f
--- /dev/null
+++ b/src/basic/utf8.c
@@ -0,0 +1,402 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2011 Kay Sievers
+ 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/>.
+***/
+
+/* Parts of this file are based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "utf8.h"
+#include "util.h"
+
+bool unichar_is_valid(uint32_t ch) {
+
+ if (ch >= 0x110000) /* End of unicode space */
+ return false;
+ if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+ return false;
+ if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+ return false;
+ if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+ return false;
+
+ return true;
+}
+
+static bool unichar_is_control(uint32_t ch) {
+
+ /*
+ 0 to ' '-1 is the C0 range.
+ DEL=0x7F, and DEL+1 to 0x9F is C1 range.
+ '\t' is in C0 range, but more or less harmless and commonly used.
+ */
+
+ return (ch < ' ' && ch != '\t' && ch != '\n') ||
+ (0x7F <= ch && ch <= 0x9F);
+}
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str) {
+ unsigned char c;
+
+ assert(str);
+
+ c = (unsigned char) str[0];
+ if (c < 0x80)
+ return 1;
+ if ((c & 0xe0) == 0xc0)
+ return 2;
+ if ((c & 0xf0) == 0xe0)
+ return 3;
+ if ((c & 0xf8) == 0xf0)
+ return 4;
+ if ((c & 0xfc) == 0xf8)
+ return 5;
+ if ((c & 0xfe) == 0xfc)
+ return 6;
+
+ return 0;
+}
+
+/* decode one unicode char */
+int utf8_encoded_to_unichar(const char *str) {
+ int unichar, len, i;
+
+ assert(str);
+
+ len = utf8_encoded_expected_len(str);
+
+ switch (len) {
+ case 1:
+ return (int)str[0];
+ case 2:
+ unichar = str[0] & 0x1f;
+ break;
+ case 3:
+ unichar = (int)str[0] & 0x0f;
+ break;
+ case 4:
+ unichar = (int)str[0] & 0x07;
+ break;
+ case 5:
+ unichar = (int)str[0] & 0x03;
+ break;
+ case 6:
+ unichar = (int)str[0] & 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 1; i < len; i++) {
+ if (((int)str[i] & 0xc0) != 0x80)
+ return -EINVAL;
+ unichar <<= 6;
+ unichar |= (int)str[i] & 0x3f;
+ }
+
+ return unichar;
+}
+
+bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
+ const char *p;
+
+ assert(str);
+
+ for (p = str; length;) {
+ int encoded_len, val;
+
+ encoded_len = utf8_encoded_valid_unichar(p);
+ if (encoded_len < 0 ||
+ (size_t) encoded_len > length)
+ return false;
+
+ val = utf8_encoded_to_unichar(p);
+ if (val < 0 ||
+ unichar_is_control(val) ||
+ (!newline && val == '\n'))
+ return false;
+
+ length -= encoded_len;
+ p += encoded_len;
+ }
+
+ return true;
+}
+
+const char *utf8_is_valid(const char *str) {
+ const uint8_t *p;
+
+ assert(str);
+
+ for (p = (const uint8_t*) str; *p; ) {
+ int len;
+
+ len = utf8_encoded_valid_unichar((const char *)p);
+ if (len < 0)
+ return NULL;
+
+ p += len;
+ }
+
+ return str;
+}
+
+char *utf8_escape_invalid(const char *str) {
+ char *p, *s;
+
+ assert(str);
+
+ p = s = malloc(strlen(str) * 4 + 1);
+ if (!p)
+ return NULL;
+
+ while (*str) {
+ int len;
+
+ len = utf8_encoded_valid_unichar(str);
+ if (len > 0) {
+ s = mempcpy(s, str, len);
+ str += len;
+ } else {
+ s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
+ str += 1;
+ }
+ }
+
+ *s = '\0';
+
+ return p;
+}
+
+char *utf8_escape_non_printable(const char *str) {
+ char *p, *s;
+
+ assert(str);
+
+ p = s = malloc(strlen(str) * 4 + 1);
+ if (!p)
+ return NULL;
+
+ while (*str) {
+ int len;
+
+ len = utf8_encoded_valid_unichar(str);
+ if (len > 0) {
+ if (utf8_is_printable(str, len)) {
+ s = mempcpy(s, str, len);
+ str += len;
+ } else {
+ while (len > 0) {
+ *(s++) = '\\';
+ *(s++) = 'x';
+ *(s++) = hexchar((int) *str >> 4);
+ *(s++) = hexchar((int) *str);
+
+ str += 1;
+ len --;
+ }
+ }
+ } else {
+ s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
+ str += 1;
+ }
+ }
+
+ *s = '\0';
+
+ return p;
+}
+
+char *ascii_is_valid(const char *str) {
+ const char *p;
+
+ assert(str);
+
+ for (p = str; *p; p++)
+ if ((unsigned char) *p >= 128)
+ return NULL;
+
+ return (char*) str;
+}
+
+/**
+ * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
+ * @out_utf8: output buffer of at least 4 bytes or NULL
+ * @g: UCS-4 character to encode
+ *
+ * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
+ * The length of the character is returned. It is not zero-terminated! If the
+ * output buffer is NULL, only the length is returned.
+ *
+ * Returns: The length in bytes that the UTF-8 representation does or would
+ * occupy.
+ */
+size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
+
+ if (g < (1 << 7)) {
+ if (out_utf8)
+ out_utf8[0] = g & 0x7f;
+ return 1;
+ } else if (g < (1 << 11)) {
+ if (out_utf8) {
+ out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
+ out_utf8[1] = 0x80 | (g & 0x3f);
+ }
+ return 2;
+ } else if (g < (1 << 16)) {
+ if (out_utf8) {
+ out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
+ out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
+ out_utf8[2] = 0x80 | (g & 0x3f);
+ }
+ return 3;
+ } else if (g < (1 << 21)) {
+ if (out_utf8) {
+ out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
+ out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
+ out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
+ out_utf8[3] = 0x80 | (g & 0x3f);
+ }
+ return 4;
+ }
+
+ return 0;
+}
+
+char *utf16_to_utf8(const void *s, size_t length) {
+ const uint8_t *f;
+ char *r, *t;
+
+ r = new(char, (length * 4 + 1) / 2 + 1);
+ if (!r)
+ return NULL;
+
+ f = s;
+ t = r;
+
+ while (f < (const uint8_t*) s + length) {
+ uint16_t w1, w2;
+
+ /* see RFC 2781 section 2.2 */
+
+ w1 = f[1] << 8 | f[0];
+ f += 2;
+
+ if (!utf16_is_surrogate(w1)) {
+ t += utf8_encode_unichar(t, w1);
+
+ continue;
+ }
+
+ if (utf16_is_trailing_surrogate(w1))
+ continue;
+ else if (f >= (const uint8_t*) s + length)
+ break;
+
+ w2 = f[1] << 8 | f[0];
+ f += 2;
+
+ if (!utf16_is_trailing_surrogate(w2)) {
+ f -= 2;
+ continue;
+ }
+
+ t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
+ }
+
+ *t = 0;
+ return r;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar) {
+
+ if (unichar < 0x80)
+ return 1;
+ if (unichar < 0x800)
+ return 2;
+ if (unichar < 0x10000)
+ return 3;
+ if (unichar < 0x200000)
+ return 4;
+ if (unichar < 0x4000000)
+ return 5;
+
+ return 6;
+}
+
+/* validate one encoded unicode char and return its length */
+int utf8_encoded_valid_unichar(const char *str) {
+ int len, unichar, i;
+
+ assert(str);
+
+ len = utf8_encoded_expected_len(str);
+ if (len == 0)
+ return -EINVAL;
+
+ /* ascii is valid */
+ if (len == 1)
+ return 1;
+
+ /* check if expected encoded chars are available */
+ for (i = 0; i < len; i++)
+ if ((str[i] & 0x80) != 0x80)
+ return -EINVAL;
+
+ unichar = utf8_encoded_to_unichar(str);
+
+ /* check if encoded length matches encoded value */
+ if (utf8_unichar_to_encoded_len(unichar) != len)
+ return -EINVAL;
+
+ /* check if value has valid range */
+ if (!unichar_is_valid(unichar))
+ return -EINVAL;
+
+ return len;
+}
diff --git a/src/basic/utf8.h b/src/basic/utf8.h
new file mode 100644
index 0000000..e745649
--- /dev/null
+++ b/src/basic/utf8.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <stdbool.h>
+
+#include "macro.h"
+
+#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
+
+bool unichar_is_valid(uint32_t c);
+
+const char *utf8_is_valid(const char *s) _pure_;
+char *ascii_is_valid(const char *s) _pure_;
+
+bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
+#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
+
+char *utf8_escape_invalid(const char *s);
+char *utf8_escape_non_printable(const char *str);
+
+size_t utf8_encode_unichar(char *out_utf8, uint32_t g);
+char *utf16_to_utf8(const void *s, size_t length);
+
+int utf8_encoded_valid_unichar(const char *str);
+int utf8_encoded_to_unichar(const char *str);
+
+static inline bool utf16_is_surrogate(uint16_t c) {
+ return (0xd800 <= c && c <= 0xdfff);
+}
+
+static inline bool utf16_is_trailing_surrogate(uint16_t c) {
+ return (0xdc00 <= c && c <= 0xdfff);
+}
+
+static inline uint32_t utf16_surrogate_pair_to_unichar(uint16_t lead, uint16_t trail) {
+ return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
+}
diff --git a/src/basic/util.c b/src/basic/util.c
new file mode 100644
index 0000000..6f6906f
--- /dev/null
+++ b/src/basic/util.c
@@ -0,0 +1,6048 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <linux/sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <stdarg.h>
+#include <poll.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <netinet/ip.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <glob.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include <linux/magic.h>
+#include <limits.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <sys/personality.h>
+#include <sys/xattr.h>
+#include <sys/statvfs.h>
+#include <sys/file.h>
+#include <linux/fs.h>
+
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the XDG
+ * version which is really broken. */
+#include <libgen.h>
+#undef basename
+
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+
+#include "config.h"
+#include "macro.h"
+#include "util.h"
+#include "ioprio.h"
+#include "missing.h"
+#include "log.h"
+#include "strv.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "exit-status.h"
+#include "hashmap.h"
+#include "env-util.h"
+#include "fileio.h"
+#include "device-nodes.h"
+#include "utf8.h"
+#include "gunicode.h"
+#include "virt.h"
+#include "def.h"
+#include "sparse-endian.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
+
+int saved_argc = 0;
+char **saved_argv = NULL;
+
+size_t page_size(void) {
+ static thread_local size_t pgsz = 0;
+ long r;
+
+ if (_likely_(pgsz > 0))
+ return pgsz;
+
+ r = sysconf(_SC_PAGESIZE);
+ assert(r > 0);
+
+ pgsz = (size_t) r;
+ return pgsz;
+}
+
+bool streq_ptr(const char *a, const char *b) {
+
+ /* Like streq(), but tries to make sense of NULL pointers */
+
+ if (a && b)
+ return streq(a, b);
+
+ if (!a && !b)
+ return true;
+
+ return false;
+}
+
+char* endswith(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (memcmp(s + sl - pl, postfix, pl) != 0)
+ return NULL;
+
+ return (char*) s + sl - pl;
+}
+
+char* endswith_no_case(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcasecmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (char*) s + sl - pl;
+}
+
+char* first_word(const char *s, const char *word) {
+ size_t sl, wl;
+ const char *p;
+
+ assert(s);
+ assert(word);
+
+ /* Checks if the string starts with the specified word, either
+ * followed by NUL or by whitespace. Returns a pointer to the
+ * NUL or the first character after the whitespace. */
+
+ sl = strlen(s);
+ wl = strlen(word);
+
+ if (sl < wl)
+ return NULL;
+
+ if (wl == 0)
+ return (char*) s;
+
+ if (memcmp(s, word, wl) != 0)
+ return NULL;
+
+ p = s + wl;
+ if (*p == 0)
+ return (char*) p;
+
+ if (!strchr(WHITESPACE, *p))
+ return NULL;
+
+ p += strspn(p, WHITESPACE);
+ return (char*) p;
+}
+
+size_t cescape_char(char c, char *buf) {
+ char * buf_old = buf;
+
+ switch (c) {
+
+ case '\a':
+ *(buf++) = '\\';
+ *(buf++) = 'a';
+ break;
+ case '\b':
+ *(buf++) = '\\';
+ *(buf++) = 'b';
+ break;
+ case '\f':
+ *(buf++) = '\\';
+ *(buf++) = 'f';
+ break;
+ case '\n':
+ *(buf++) = '\\';
+ *(buf++) = 'n';
+ break;
+ case '\r':
+ *(buf++) = '\\';
+ *(buf++) = 'r';
+ break;
+ case '\t':
+ *(buf++) = '\\';
+ *(buf++) = 't';
+ break;
+ case '\v':
+ *(buf++) = '\\';
+ *(buf++) = 'v';
+ break;
+ case '\\':
+ *(buf++) = '\\';
+ *(buf++) = '\\';
+ break;
+ case '"':
+ *(buf++) = '\\';
+ *(buf++) = '"';
+ break;
+ case '\'':
+ *(buf++) = '\\';
+ *(buf++) = '\'';
+ break;
+
+ default:
+ /* For special chars we prefer octal over
+ * hexadecimal encoding, simply because glib's
+ * g_strescape() does the same */
+ if ((c < ' ') || (c >= 127)) {
+ *(buf++) = '\\';
+ *(buf++) = octchar((unsigned char) c >> 6);
+ *(buf++) = octchar((unsigned char) c >> 3);
+ *(buf++) = octchar((unsigned char) c);
+ } else
+ *(buf++) = c;
+ break;
+ }
+
+ return buf - buf_old;
+}
+
+int close_nointr(int fd) {
+ assert(fd >= 0);
+
+ if (close(fd) >= 0)
+ return 0;
+
+ /*
+ * Just ignore EINTR; a retry loop is the wrong thing to do on
+ * Linux.
+ *
+ * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+ * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+ * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+ */
+ if (errno == EINTR)
+ return 0;
+
+ return -errno;
+}
+
+int safe_close(int fd) {
+
+ /*
+ * Like close_nointr() but cannot fail. Guarantees errno is
+ * unchanged. Is a NOP with negative fds passed, and returns
+ * -1, so that it can be used in this syntax:
+ *
+ * fd = safe_close(fd);
+ */
+
+ if (fd >= 0) {
+ PROTECT_ERRNO;
+
+ /* The kernel might return pretty much any error code
+ * via close(), but the fd will be closed anyway. The
+ * only condition we want to check for here is whether
+ * the fd was invalid at all... */
+
+ assert_se(close_nointr(fd) != -EBADF);
+ }
+
+ return -1;
+}
+
+void close_many(const int fds[], unsigned n_fd) {
+ unsigned i;
+
+ assert(fds || n_fd <= 0);
+
+ for (i = 0; i < n_fd; i++)
+ safe_close(fds[i]);
+}
+
+int unlink_noerrno(const char *path) {
+ PROTECT_ERRNO;
+ int r;
+
+ r = unlink(path);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int parse_boolean(const char *v) {
+ assert(v);
+
+ if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
+ return 1;
+ else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
+ return 0;
+
+ return -EINVAL;
+}
+
+int parse_pid(const char *s, pid_t* ret_pid) {
+ unsigned long ul = 0;
+ pid_t pid;
+ int r;
+
+ assert(s);
+ assert(ret_pid);
+
+ r = safe_atolu(s, &ul);
+ if (r < 0)
+ return r;
+
+ pid = (pid_t) ul;
+
+ if ((unsigned long) pid != ul)
+ return -ERANGE;
+
+ if (pid <= 0)
+ return -ERANGE;
+
+ *ret_pid = pid;
+ return 0;
+}
+
+int parse_uid(const char *s, uid_t* ret_uid) {
+ unsigned long ul = 0;
+ uid_t uid;
+ int r;
+
+ assert(s);
+
+ r = safe_atolu(s, &ul);
+ if (r < 0)
+ return r;
+
+ uid = (uid_t) ul;
+
+ if ((unsigned long) uid != ul)
+ return -ERANGE;
+
+ /* Some libc APIs use UID_INVALID as special placeholder */
+ if (uid == (uid_t) 0xFFFFFFFF)
+ return -ENXIO;
+
+ /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
+ if (uid == (uid_t) 0xFFFF)
+ return -ENXIO;
+
+ if (ret_uid)
+ *ret_uid = uid;
+
+ return 0;
+}
+
+int safe_atou(const char *s, unsigned *ret_u) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret_u);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((unsigned long) (unsigned) l != l)
+ return -ERANGE;
+
+ *ret_u = (unsigned) l;
+ return 0;
+}
+
+int safe_atoi(const char *s, int *ret_i) {
+ char *x = NULL;
+ long l;
+
+ assert(s);
+ assert(ret_i);
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((long) (int) l != l)
+ return -ERANGE;
+
+ *ret_i = (int) l;
+ return 0;
+}
+
+int safe_atou8(const char *s, uint8_t *ret) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((unsigned long) (uint8_t) l != l)
+ return -ERANGE;
+
+ *ret = (uint8_t) l;
+ return 0;
+}
+
+int safe_atou16(const char *s, uint16_t *ret) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((unsigned long) (uint16_t) l != l)
+ return -ERANGE;
+
+ *ret = (uint16_t) l;
+ return 0;
+}
+
+int safe_atoi16(const char *s, int16_t *ret) {
+ char *x = NULL;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((long) (int16_t) l != l)
+ return -ERANGE;
+
+ *ret = (int16_t) l;
+ return 0;
+}
+
+int safe_atollu(const char *s, long long unsigned *ret_llu) {
+ char *x = NULL;
+ unsigned long long l;
+
+ assert(s);
+ assert(ret_llu);
+
+ errno = 0;
+ l = strtoull(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ *ret_llu = l;
+ return 0;
+}
+
+int safe_atolli(const char *s, long long int *ret_lli) {
+ char *x = NULL;
+ long long l;
+
+ assert(s);
+ assert(ret_lli);
+
+ errno = 0;
+ l = strtoll(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ *ret_lli = l;
+ return 0;
+}
+
+int safe_atod(const char *s, double *ret_d) {
+ char *x = NULL;
+ double d = 0;
+ locale_t loc;
+
+ assert(s);
+ assert(ret_d);
+
+ loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
+ if (loc == (locale_t) 0)
+ return -errno;
+
+ errno = 0;
+ d = strtod_l(s, &x, loc);
+
+ if (!x || x == s || *x || errno) {
+ freelocale(loc);
+ return errno ? -errno : -EINVAL;
+ }
+
+ freelocale(loc);
+ *ret_d = (double) d;
+ return 0;
+}
+
+static size_t strcspn_escaped(const char *s, const char *reject) {
+ bool escaped = false;
+ int n;
+
+ for (n=0; s[n]; n++) {
+ if (escaped)
+ escaped = false;
+ else if (s[n] == '\\')
+ escaped = true;
+ else if (strchr(reject, s[n]))
+ break;
+ }
+
+ /* if s ends in \, return index of previous char */
+ return n - escaped;
+}
+
+/* Split a string into words. */
+const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
+ const char *current;
+
+ current = *state;
+
+ if (!*current) {
+ assert(**state == '\0');
+ return NULL;
+ }
+
+ current += strspn(current, separator);
+ if (!*current) {
+ *state = current;
+ return NULL;
+ }
+
+ if (quoted && strchr("\'\"", *current)) {
+ char quotechars[2] = {*current, '\0'};
+
+ *l = strcspn_escaped(current + 1, quotechars);
+ if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
+ (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
+ /* right quote missing or garbage at the end */
+ *state = current;
+ return NULL;
+ }
+ *state = current++ + *l + 2;
+ } else if (quoted) {
+ *l = strcspn_escaped(current, separator);
+ if (current[*l] && !strchr(separator, current[*l])) {
+ /* unfinished escape */
+ *state = current;
+ return NULL;
+ }
+ *state = current + *l;
+ } else {
+ *l = strcspn(current, separator);
+ *state = current + *l;
+ }
+
+ return current;
+}
+
+int fchmod_umask(int fd, mode_t m) {
+ mode_t u;
+ int r;
+
+ u = umask(0777);
+ r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+ umask(u);
+
+ return r;
+}
+
+char *truncate_nl(char *s) {
+ assert(s);
+
+ s[strcspn(s, NEWLINE)] = 0;
+ return s;
+}
+
+char *strnappend(const char *s, const char *suffix, size_t b) {
+ size_t a;
+ char *r;
+
+ if (!s && !suffix)
+ return strdup("");
+
+ if (!s)
+ return strndup(suffix, b);
+
+ if (!suffix)
+ return strdup(s);
+
+ assert(s);
+ assert(suffix);
+
+ a = strlen(s);
+ if (b > ((size_t) -1) - a)
+ return NULL;
+
+ r = new(char, a+b+1);
+ if (!r)
+ return NULL;
+
+ memcpy(r, s, a);
+ memcpy(r+a, suffix, b);
+ r[a+b] = 0;
+
+ return r;
+}
+
+char *strappend(const char *s, const char *suffix) {
+ return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+int readlinkat_malloc(int fd, const char *p, char **ret) {
+ size_t l = 100;
+ int r;
+
+ assert(p);
+ assert(ret);
+
+ for (;;) {
+ char *c;
+ ssize_t n;
+
+ c = new(char, l);
+ if (!c)
+ return -ENOMEM;
+
+ n = readlinkat(fd, p, c, l-1);
+ if (n < 0) {
+ r = -errno;
+ free(c);
+ return r;
+ }
+
+ if ((size_t) n < l-1) {
+ c[n] = 0;
+ *ret = c;
+ return 0;
+ }
+
+ free(c);
+ l *= 2;
+ }
+}
+
+int readlink_malloc(const char *p, char **ret) {
+ return readlinkat_malloc(AT_FDCWD, p, ret);
+}
+
+int readlink_value(const char *p, char **ret) {
+ _cleanup_free_ char *link = NULL;
+ char *value;
+ int r;
+
+ r = readlink_malloc(p, &link);
+ if (r < 0)
+ return r;
+
+ value = basename(link);
+ if (!value)
+ return -ENOENT;
+
+ value = strdup(value);
+ if (!value)
+ return -ENOMEM;
+
+ *ret = value;
+
+ return 0;
+}
+
+int readlink_and_make_absolute(const char *p, char **r) {
+ _cleanup_free_ char *target = NULL;
+ char *k;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ j = readlink_malloc(p, &target);
+ if (j < 0)
+ return j;
+
+ k = file_in_same_dir(p, target);
+ if (!k)
+ return -ENOMEM;
+
+ *r = k;
+ return 0;
+}
+
+int readlink_and_canonicalize(const char *p, char **r) {
+ char *t, *s;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ j = readlink_and_make_absolute(p, &t);
+ if (j < 0)
+ return j;
+
+ s = canonicalize_file_name(t);
+ if (s) {
+ free(t);
+ *r = s;
+ } else
+ *r = t;
+
+ path_kill_slashes(*r);
+
+ return 0;
+}
+
+char *strstrip(char *s) {
+ char *e;
+
+ /* Drops trailing whitespace. Modifies the string in
+ * place. Returns pointer to first non-space character */
+
+ s += strspn(s, WHITESPACE);
+
+ for (e = strchr(s, 0); e > s; e --)
+ if (!strchr(WHITESPACE, e[-1]))
+ break;
+
+ *e = 0;
+
+ return s;
+}
+
+char *delete_chars(char *s, const char *bad) {
+ char *f, *t;
+
+ /* Drops all whitespace, regardless where in the string */
+
+ for (f = s, t = s; *f; f++) {
+ if (strchr(bad, *f))
+ continue;
+
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return s;
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+ char *e, *ret;
+ size_t k;
+
+ assert(path);
+ assert(filename);
+
+ /* This removes the last component of path and appends
+ * filename, unless the latter is absolute anyway or the
+ * former isn't */
+
+ if (path_is_absolute(filename))
+ return strdup(filename);
+
+ e = strrchr(path, '/');
+ if (!e)
+ return strdup(filename);
+
+ k = strlen(filename);
+ ret = new(char, (e + 1 - path) + k + 1);
+ if (!ret)
+ return NULL;
+
+ memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
+ return ret;
+}
+
+int rmdir_parents(const char *path, const char *stop) {
+ size_t l;
+ int r = 0;
+
+ assert(path);
+ assert(stop);
+
+ l = strlen(path);
+
+ /* Skip trailing slashes */
+ while (l > 0 && path[l-1] == '/')
+ l--;
+
+ while (l > 0) {
+ char *t;
+
+ /* Skip last component */
+ while (l > 0 && path[l-1] != '/')
+ l--;
+
+ /* Skip trailing slashes */
+ while (l > 0 && path[l-1] == '/')
+ l--;
+
+ if (l <= 0)
+ break;
+
+ if (!(t = strndup(path, l)))
+ return -ENOMEM;
+
+ if (path_startswith(stop, t)) {
+ free(t);
+ return 0;
+ }
+
+ r = rmdir(t);
+ free(t);
+
+ if (r < 0)
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return 0;
+}
+
+char hexchar(int x) {
+ static const char table[16] = "0123456789abcdef";
+
+ return table[x & 15];
+}
+
+int unhexchar(char c) {
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -EINVAL;
+}
+
+char *hexmem(const void *p, size_t l) {
+ char *r, *z;
+ const uint8_t *x;
+
+ z = r = malloc(l * 2 + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < (const uint8_t*) p + l; x++) {
+ *(z++) = hexchar(*x >> 4);
+ *(z++) = hexchar(*x & 15);
+ }
+
+ *z = 0;
+ return r;
+}
+
+void *unhexmem(const char *p, size_t l) {
+ uint8_t *r, *z;
+ const char *x;
+
+ assert(p);
+
+ z = r = malloc((l + 1) / 2 + 1);
+ if (!r)
+ return NULL;
+
+ for (x = p; x < p + l; x += 2) {
+ int a, b;
+
+ a = unhexchar(x[0]);
+ if (x+1 < p + l)
+ b = unhexchar(x[1]);
+ else
+ b = 0;
+
+ *(z++) = (uint8_t) a << 4 | (uint8_t) b;
+ }
+
+ *z = 0;
+ return r;
+}
+
+char octchar(int x) {
+ return '0' + (x & 7);
+}
+
+int unoctchar(char c) {
+
+ if (c >= '0' && c <= '7')
+ return c - '0';
+
+ return -EINVAL;
+}
+
+char decchar(int x) {
+ return '0' + (x % 10);
+}
+
+int undecchar(char c) {
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ return -EINVAL;
+}
+
+char *cescape(const char *s) {
+ char *r, *t;
+ const char *f;
+
+ assert(s);
+
+ /* Does C style string escaping. May be reversed with
+ * cunescape(). */
+
+ r = new(char, strlen(s)*4 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; *f; f++)
+ t += cescape_char(*f, t);
+
+ *t = 0;
+
+ return r;
+}
+
+static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+ int r = 1;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ /* Unescapes C style. Returns the unescaped character in ret,
+ * unless we encountered a \u sequence in which case the full
+ * unicode character is returned in ret_unicode, instead. */
+
+ if (length != (size_t) -1 && length < 1)
+ return -EINVAL;
+
+ switch (p[0]) {
+
+ case 'a':
+ *ret = '\a';
+ break;
+ case 'b':
+ *ret = '\b';
+ break;
+ case 'f':
+ *ret = '\f';
+ break;
+ case 'n':
+ *ret = '\n';
+ break;
+ case 'r':
+ *ret = '\r';
+ break;
+ case 't':
+ *ret = '\t';
+ break;
+ case 'v':
+ *ret = '\v';
+ break;
+ case '\\':
+ *ret = '\\';
+ break;
+ case '"':
+ *ret = '"';
+ break;
+ case '\'':
+ *ret = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *ret = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unhexchar(p[1]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(p[2]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* Don't allow NUL bytes */
+ if (a == 0 && b == 0)
+ return -EINVAL;
+
+ *ret = (char) ((a << 4U) | b);
+ r = 3;
+ break;
+ }
+
+ case 'u': {
+ /* C++11 style 16bit unicode */
+
+ int a[4];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 5)
+ return -EINVAL;
+
+ for (i = 0; i < 4; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 5;
+ break;
+ }
+
+ case 'U': {
+ /* C++11 style 32bit unicode */
+
+ int a[8];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 9)
+ return -EINVAL;
+
+ for (i = 0; i < 8; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
+ ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ /* Don't allow invalid code points */
+ if (!unichar_is_valid(c))
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 9;
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c;
+ uint32_t m;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unoctchar(p[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unoctchar(p[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unoctchar(p[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ /* don't allow NUL bytes */
+ if (a == 0 && b == 0 && c == 0)
+ return -EINVAL;
+
+ /* Don't allow bytes above 255 */
+ m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
+ if (m > 255)
+ return -EINVAL;
+
+ *ret = m;
+ r = 3;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
+ char *r, *t;
+ const char *f;
+ size_t pl;
+
+ assert(s);
+ assert(ret);
+
+ /* Undoes C style string escaping, and optionally prefixes it. */
+
+ pl = prefix ? strlen(prefix) : 0;
+
+ r = new(char, pl+length+1);
+ if (!r)
+ return -ENOMEM;
+
+ if (prefix)
+ memcpy(r, prefix, pl);
+
+ for (f = s, t = r + pl; f < s + length; f++) {
+ size_t remaining;
+ uint32_t u;
+ char c;
+ int k;
+
+ remaining = s + length - f;
+ assert(remaining > 0);
+
+ if (*f != '\\') {
+ /* A literal literal, copy verbatim */
+ *(t++) = *f;
+ continue;
+ }
+
+ if (remaining == 1) {
+ if (flags & UNESCAPE_RELAX) {
+ /* A trailing backslash, copy verbatim */
+ *(t++) = *f;
+ continue;
+ }
+
+ free(r);
+ return -EINVAL;
+ }
+
+ k = cunescape_one(f + 1, remaining - 1, &c, &u);
+ if (k < 0) {
+ if (flags & UNESCAPE_RELAX) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ continue;
+ }
+
+ free(r);
+ return k;
+ }
+
+ if (c != 0)
+ /* Non-Unicode? Let's encode this directly */
+ *(t++) = c;
+ else
+ /* Unicode? Then let's encode this in UTF-8 */
+ t += utf8_encode_unichar(t, u);
+
+ f += k;
+ }
+
+ *t = 0;
+
+ *ret = r;
+ return t - r;
+}
+
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+ return cunescape_length(s, strlen(s), flags, ret);
+}
+
+char *xescape(const char *s, const char *bad) {
+ char *r, *t;
+ const char *f;
+
+ /* Escapes all chars in bad, in addition to \ and all special
+ * chars, in \xFF style escaping. May be reversed with
+ * cunescape(). */
+
+ r = new(char, strlen(s) * 4 + 1);
+ if (!r)
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ if ((*f < ' ') || (*f >= 127) ||
+ (*f == '\\') || strchr(bad, *f)) {
+ *(t++) = '\\';
+ *(t++) = 'x';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+char *ascii_strlower(char *t) {
+ char *p;
+
+ assert(t);
+
+ for (p = t; *p; p++)
+ if (*p >= 'A' && *p <= 'Z')
+ *p = *p - 'A' + 'a';
+
+ return t;
+}
+
+_pure_ static 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_file(const char *filename) {
+ assert(filename);
+
+ if (endswith(filename, "~"))
+ return true;
+
+ return hidden_file_allow_backup(filename);
+}
+
+int fd_nonblock(int fd, bool nonblock) {
+ int flags, nflags;
+
+ assert(fd >= 0);
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0)
+ return -errno;
+
+ if (nonblock)
+ nflags = flags | O_NONBLOCK;
+ else
+ nflags = flags & ~O_NONBLOCK;
+
+ if (nflags == flags)
+ return 0;
+
+ if (fcntl(fd, F_SETFL, nflags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fd_cloexec(int fd, bool cloexec) {
+ int flags, nflags;
+
+ assert(fd >= 0);
+
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0)
+ return -errno;
+
+ if (cloexec)
+ nflags = flags | FD_CLOEXEC;
+ else
+ nflags = flags & ~FD_CLOEXEC;
+
+ if (nflags == flags)
+ return 0;
+
+ if (fcntl(fd, F_SETFD, nflags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+ unsigned i;
+
+ assert(n_fdset == 0 || fdset);
+
+ for (i = 0; i < n_fdset; i++)
+ if (fdset[i] == fd)
+ return true;
+
+ return false;
+}
+
+int close_all_fds(const int except[], unsigned n_except) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0;
+
+ assert(n_except == 0 || except);
+
+ d = opendir("/proc/self/fd");
+ if (!d) {
+ int fd;
+ struct rlimit rl;
+
+ /* When /proc isn't available (for example in chroots)
+ * the fallback is brute forcing through the fd
+ * table */
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
+ for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+
+ if (fd_in_set(fd, except, n_except))
+ continue;
+
+ if (close_nointr(fd) < 0)
+ if (errno != EBADF && r == 0)
+ r = -errno;
+ }
+
+ return r;
+ }
+
+ while ((de = readdir(d))) {
+ int fd = -1;
+
+ if (hidden_file(de->d_name))
+ continue;
+
+ if (safe_atoi(de->d_name, &fd) < 0)
+ /* Let's better ignore this, just in case */
+ continue;
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ if (fd_in_set(fd, except, n_except))
+ continue;
+
+ if (close_nointr(fd) < 0) {
+ /* Valgrind has its own FD and doesn't want to have it closed */
+ if (errno != EBADF && r == 0)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
+
+bool chars_intersect(const char *a, const char *b) {
+ const char *p;
+
+ /* Returns true if any of the chars in a are in b. */
+ for (p = a; *p; p++)
+ if (strchr(b, *p))
+ return true;
+
+ return false;
+}
+
+bool fstype_is_network(const char *fstype) {
+ static const char table[] =
+ "afs\0"
+ "cifs\0"
+ "smbfs\0"
+ "sshfs\0"
+ "ncpfs\0"
+ "ncp\0"
+ "nfs\0"
+ "nfs4\0"
+ "gfs\0"
+ "gfs2\0"
+ "glusterfs\0";
+
+ const char *x;
+
+ x = startswith(fstype, "fuse.");
+ if (x)
+ fstype = x;
+
+ return nulstr_contains(table, fstype);
+}
+
+int flush_fd(int fd) {
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+
+ for (;;) {
+ char buf[LINE_MAX];
+ ssize_t l;
+ int r;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+
+ } else if (r == 0)
+ return 0;
+
+ l = read(fd, buf, sizeof(buf));
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ } else if (l == 0)
+ return 0;
+ }
+}
+
+void safe_close_pair(int p[]) {
+ assert(p);
+
+ if (p[0] == p[1]) {
+ /* Special case pairs which use the same fd in both
+ * directions... */
+ p[0] = p[1] = safe_close(p[0]);
+ return;
+ }
+
+ p[0] = safe_close(p[0]);
+ p[1] = safe_close(p[1]);
+}
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
+ uint8_t *p = buf;
+ ssize_t n = 0;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ while (nbytes > 0) {
+ ssize_t k;
+
+ k = read(fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN && do_poll) {
+
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via read() */
+
+ fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
+ continue;
+ }
+
+ return n > 0 ? n : -errno;
+ }
+
+ if (k == 0)
+ return n;
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ }
+
+ return n;
+}
+
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
+ ssize_t n;
+
+ n = loop_read(fd, buf, nbytes, do_poll);
+ if (n < 0)
+ return n;
+ if ((size_t) n != nbytes)
+ return -EIO;
+ return 0;
+}
+
+int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
+ const uint8_t *p = buf;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ errno = 0;
+
+ do {
+ ssize_t k;
+
+ k = write(fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN && do_poll) {
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via write() */
+
+ fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
+ continue;
+ }
+
+ return -errno;
+ }
+
+ if (nbytes > 0 && k == 0) /* Can't really happen */
+ return -EIO;
+
+ p += k;
+ nbytes -= k;
+ } while (nbytes > 0);
+
+ return 0;
+}
+
+int parse_size(const char *t, off_t base, off_t *size) {
+
+ /* Soo, sometimes we want to parse IEC binary suffixes, and
+ * sometimes SI decimal suffixes. This function can parse
+ * both. Which one is the right way depends on the
+ * context. Wikipedia suggests that SI is customary for
+ * hardware metrics and network speeds, while IEC is
+ * customary for most data sizes used by software and volatile
+ * (RAM) memory. Hence be careful which one you pick!
+ *
+ * In either case we use just K, M, G as suffix, and not Ki,
+ * Mi, Gi or so (as IEC would suggest). That's because that's
+ * frickin' ugly. But this means you really need to make sure
+ * to document which base you are parsing when you use this
+ * call. */
+
+ struct table {
+ const char *suffix;
+ unsigned long long factor;
+ };
+
+ static const struct table iec[] = {
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "K", 1024ULL },
+ { "B", 1 },
+ { "", 1 },
+ };
+
+ static const struct table si[] = {
+ { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+ { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
+ { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
+ { "G", 1000ULL*1000ULL*1000ULL },
+ { "M", 1000ULL*1000ULL },
+ { "K", 1000ULL },
+ { "B", 1 },
+ { "", 1 },
+ };
+
+ const struct table *table;
+ const char *p;
+ unsigned long long r = 0;
+ unsigned n_entries, start_pos = 0;
+
+ assert(t);
+ assert(base == 1000 || base == 1024);
+ assert(size);
+
+ if (base == 1000) {
+ table = si;
+ n_entries = ELEMENTSOF(si);
+ } else {
+ table = iec;
+ n_entries = ELEMENTSOF(iec);
+ }
+
+ p = t;
+ do {
+ long long l;
+ unsigned long long l2;
+ double frac = 0;
+ char *e;
+ unsigned i;
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno > 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (e == p)
+ return -EINVAL;
+
+ if (*e == '.') {
+ e++;
+ if (*e >= '0' && *e <= '9') {
+ char *e2;
+
+ /* strotoull itself would accept space/+/- */
+ l2 = strtoull(e, &e2, 10);
+
+ if (errno == ERANGE)
+ return -errno;
+
+ /* Ignore failure. E.g. 10.M is valid */
+ frac = l2;
+ for (; e < e2; e++)
+ frac /= 10;
+ }
+ }
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = start_pos; i < n_entries; i++)
+ if (startswith(e, table[i].suffix)) {
+ unsigned long long tmp;
+ if ((unsigned long long) l + (frac > 0) > ULLONG_MAX / table[i].factor)
+ return -ERANGE;
+ tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
+ if (tmp > ULLONG_MAX - r)
+ return -ERANGE;
+
+ r += tmp;
+ if ((unsigned long long) (off_t) r != r)
+ return -ERANGE;
+
+ p = e + strlen(table[i].suffix);
+
+ start_pos = i + 1;
+ break;
+ }
+
+ if (i >= n_entries)
+ return -EINVAL;
+
+ } while (*p);
+
+ *size = r;
+
+ return 0;
+}
+
+bool is_device_path(const char *path) {
+
+ /* Returns true on paths that refer to a device, either in
+ * sysfs or in /dev */
+
+ return
+ path_startswith(path, "/dev/") ||
+ path_startswith(path, "/sys/");
+}
+
+int dir_is_empty(const char *path) {
+ _cleanup_closedir_ DIR *d;
+
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ for (;;) {
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0)
+ return -errno;
+
+ if (!de)
+ return 1;
+
+ if (!hidden_file(de->d_name))
+ return 0;
+ }
+}
+
+char* dirname_malloc(const char *path) {
+ char *d, *dir, *dir2;
+
+ d = strdup(path);
+ if (!d)
+ return NULL;
+ dir = dirname(d);
+ assert(dir);
+
+ if (dir != d) {
+ dir2 = strdup(dir);
+ free(d);
+ return dir2;
+ }
+
+ return dir;
+}
+
+void rename_process(const char name[8]) {
+ assert(name);
+
+ /* This is a like a poor man's setproctitle(). It changes the
+ * comm field, argv[0], and also the glibc's internally used
+ * name of the process. For the first one a limit of 16 chars
+ * applies, to the second one usually one of 10 (i.e. length
+ * of "/sbin/init"), to the third one one of 7 (i.e. length of
+ * "systemd"). If you pass a longer string it will be
+ * truncated */
+
+ prctl(PR_SET_NAME, name);
+
+ if (program_invocation_name)
+ strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+ if (saved_argc > 0) {
+ int i;
+
+ if (saved_argv[0])
+ strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+ for (i = 1; i < saved_argc; i++) {
+ if (!saved_argv[i])
+ break;
+
+ memzero(saved_argv[i], strlen(saved_argv[i]));
+ }
+ }
+}
+
+char *lookup_uid(uid_t uid) {
+ long bufsize;
+ char *name;
+ _cleanup_free_ char *buf = NULL;
+ struct passwd pwbuf, *pw = NULL;
+
+ /* Shortcut things to avoid NSS lookups */
+ if (uid == 0)
+ return strdup("root");
+
+ bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (bufsize <= 0)
+ bufsize = 4096;
+
+ buf = malloc(bufsize);
+ if (!buf)
+ return NULL;
+
+ if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
+ return strdup(pw->pw_name);
+
+ if (asprintf(&name, UID_FMT, uid) < 0)
+ return NULL;
+
+ return name;
+}
+
+char* getlogname_malloc(void) {
+ uid_t uid;
+ struct stat st;
+
+ if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+ uid = st.st_uid;
+ else
+ uid = getuid();
+
+ return lookup_uid(uid);
+}
+
+char *getusername_malloc(void) {
+ const char *e;
+
+ e = getenv("USER");
+ if (e)
+ return strdup(e);
+
+ return lookup_uid(getuid());
+}
+
+bool is_temporary_fs(const struct statfs *s) {
+ assert(s);
+
+ return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
+ F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
+}
+
+int fd_is_temporary_fs(int fd) {
+ struct statfs s;
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
+
+ return is_temporary_fs(&s);
+}
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ assert(path);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (mode != MODE_INVALID)
+ if (chmod(path, mode) < 0)
+ return -errno;
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (chown(path, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
+ assert(fd >= 0);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (mode != MODE_INVALID)
+ if (fchmod(fd, mode) < 0)
+ return -errno;
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (fchown(fd, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
+ cpu_set_t *r;
+ unsigned n = 1024;
+
+ /* Allocates the cpuset in the right size */
+
+ for (;;) {
+ if (!(r = CPU_ALLOC(n)))
+ return NULL;
+
+ if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
+ CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
+
+ if (ncpus)
+ *ncpus = n;
+
+ return r;
+ }
+
+ CPU_FREE(r);
+
+ if (errno != EINVAL)
+ return NULL;
+
+ n *= 2;
+ }
+}
+
+int files_same(const char *filea, const char *fileb) {
+ struct stat a, b;
+
+ if (stat(filea, &a) < 0)
+ return -errno;
+
+ if (stat(fileb, &b) < 0)
+ return -errno;
+
+ return a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino;
+}
+
+int running_in_chroot(void) {
+ int ret;
+
+ ret = files_same("/proc/1/root", "/");
+ if (ret < 0)
+ return ret;
+
+ return ret == 0;
+}
+
+static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+ size_t x;
+ char *r;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(new_length >= 3);
+
+ if (old_length <= 3 || old_length <= new_length)
+ return strndup(s, old_length);
+
+ r = new0(char, new_length+1);
+ if (!r)
+ return NULL;
+
+ x = (new_length * percent) / 100;
+
+ if (x > new_length - 3)
+ x = new_length - 3;
+
+ memcpy(r, s, x);
+ r[x] = '.';
+ r[x+1] = '.';
+ r[x+2] = '.';
+ memcpy(r + x + 3,
+ s + old_length - (new_length - x - 3),
+ new_length - x - 3);
+
+ return r;
+}
+
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+ size_t x;
+ char *e;
+ const char *i, *j;
+ unsigned k, len, len2;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(new_length >= 3);
+
+ /* if no multibyte characters use ascii_ellipsize_mem for speed */
+ if (ascii_is_valid(s))
+ return ascii_ellipsize_mem(s, old_length, new_length, percent);
+
+ if (old_length <= 3 || old_length <= new_length)
+ return strndup(s, old_length);
+
+ x = (new_length * percent) / 100;
+
+ if (x > new_length - 3)
+ x = new_length - 3;
+
+ k = 0;
+ for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
+ int c;
+
+ c = utf8_encoded_to_unichar(i);
+ if (c < 0)
+ return NULL;
+ k += unichar_iswide(c) ? 2 : 1;
+ }
+
+ if (k > x) /* last character was wide and went over quota */
+ x ++;
+
+ for (j = s + old_length; k < new_length && j > i; ) {
+ int c;
+
+ j = utf8_prev_char(j);
+ c = utf8_encoded_to_unichar(j);
+ if (c < 0)
+ return NULL;
+ k += unichar_iswide(c) ? 2 : 1;
+ }
+ assert(i <= j);
+
+ /* we don't actually need to ellipsize */
+ if (i == j)
+ return memdup(s, old_length + 1);
+
+ /* make space for ellipsis */
+ j = utf8_next_char(j);
+
+ len = i - s;
+ len2 = s + old_length - j;
+ e = new(char, len + 3 + len2 + 1);
+ if (!e)
+ return NULL;
+
+ /*
+ printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+ old_length, new_length, x, len, len2, k);
+ */
+
+ memcpy(e, s, len);
+ e[len] = 0xe2; /* tri-dot ellipsis: … */
+ e[len + 1] = 0x80;
+ e[len + 2] = 0xa6;
+
+ memcpy(e + len + 3, j, len2 + 1);
+
+ return e;
+}
+
+char *ellipsize(const char *s, size_t length, unsigned percent) {
+ return ellipsize_mem(s, strlen(s), length, percent);
+}
+
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
+ _cleanup_close_ int fd;
+ int r;
+
+ assert(path);
+
+ if (parents)
+ mkdir_parents(path, 0755);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
+ if (fd < 0)
+ return -errno;
+
+ if (mode > 0) {
+ r = fchmod(fd, mode);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (uid != UID_INVALID || gid != GID_INVALID) {
+ r = fchown(fd, uid, gid);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (stamp != USEC_INFINITY) {
+ struct timespec ts[2];
+
+ timespec_store(&ts[0], stamp);
+ ts[1] = ts[0];
+ r = futimens(fd, ts);
+ } else
+ r = futimens(fd, NULL);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int touch(const char *path) {
+ return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
+}
+
+static char *unquote(const char *s, const char* quotes) {
+ size_t l;
+ assert(s);
+
+ /* This is rather stupid, simply removes the heading and
+ * trailing quotes if there is one. Doesn't care about
+ * escaping or anything.
+ *
+ * DON'T USE THIS FOR NEW CODE ANYMORE!*/
+
+ l = strlen(s);
+ if (l < 2)
+ return strdup(s);
+
+ if (strchr(quotes, s[0]) && s[l-1] == s[0])
+ return strndup(s+1, l-2);
+
+ return strdup(s);
+}
+
+noreturn void freeze(void) {
+
+ /* Make sure nobody waits for us on a socket anymore */
+ close_all_fds(NULL, 0);
+
+ sync();
+
+ for (;;)
+ pause();
+}
+
+bool null_or_empty(struct stat *st) {
+ assert(st);
+
+ if (S_ISREG(st->st_mode) && st->st_size <= 0)
+ return true;
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+ return true;
+
+ return false;
+}
+
+int null_or_empty_path(const char *fn) {
+ struct stat st;
+
+ assert(fn);
+
+ if (stat(fn, &st) < 0)
+ return -errno;
+
+ return null_or_empty(&st);
+}
+
+int null_or_empty_fd(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ return null_or_empty(&st);
+}
+
+DIR *xopendirat(int fd, const char *name, int flags) {
+ int nfd;
+ DIR *d;
+
+ assert(!(flags & O_CREAT));
+
+ nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
+ if (nfd < 0)
+ return NULL;
+
+ d = fdopendir(nfd);
+ if (!d) {
+ safe_close(nfd);
+ return NULL;
+ }
+
+ return d;
+}
+
+static char *tag_to_udev_node(const char *tagvalue, const char *by) {
+ _cleanup_free_ char *t = NULL, *u = NULL;
+ size_t enc_len;
+
+ u = unquote(tagvalue, QUOTES);
+ if (!u)
+ return NULL;
+
+ enc_len = strlen(u) * 4 + 1;
+ t = new(char, enc_len);
+ if (!t)
+ return NULL;
+
+ if (encode_devnode_name(u, t, enc_len) < 0)
+ return NULL;
+
+ return strjoin("/dev/disk/by-", by, "/", t, NULL);
+}
+
+char *fstab_node_to_udev_node(const char *p) {
+ assert(p);
+
+ if (startswith(p, "LABEL="))
+ return tag_to_udev_node(p+6, "label");
+
+ if (startswith(p, "UUID="))
+ return tag_to_udev_node(p+5, "uuid");
+
+ if (startswith(p, "PARTUUID="))
+ return tag_to_udev_node(p+9, "partuuid");
+
+ if (startswith(p, "PARTLABEL="))
+ return tag_to_udev_node(p+10, "partlabel");
+
+ return strdup(p);
+}
+
+bool dirent_is_file(const struct dirent *de) {
+ assert(de);
+
+ if (hidden_file(de->d_name))
+ return false;
+
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN)
+ return false;
+
+ return true;
+}
+
+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)
+ return false;
+
+ if (hidden_file_allow_backup(de->d_name))
+ return false;
+
+ return endswith(de->d_name, suffix);
+}
+
+static int do_execute(char **directories, usec_t timeout, char *argv[]) {
+ _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+ _cleanup_set_free_free_ Set *seen = NULL;
+ char **directory;
+
+ /* We fork this all off from a child process so that we can
+ * somewhat cleanly make use of SIGALRM to set a time limit */
+
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ pids = hashmap_new(NULL);
+ if (!pids)
+ return log_oom();
+
+ seen = set_new(&string_hash_ops);
+ if (!seen)
+ return log_oom();
+
+ STRV_FOREACH(directory, directories) {
+ _cleanup_closedir_ DIR *d;
+ struct dirent *de;
+
+ d = opendir(*directory);
+ if (!d) {
+ if (errno == ENOENT)
+ continue;
+
+ return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
+ }
+
+ FOREACH_DIRENT(de, d, break) {
+ _cleanup_free_ char *path = NULL;
+ pid_t pid;
+ int r;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (set_contains(seen, de->d_name)) {
+ log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
+ continue;
+ }
+
+ r = set_put_strdup(seen, de->d_name);
+ if (r < 0)
+ return log_oom();
+
+ path = strjoin(*directory, "/", de->d_name, NULL);
+ if (!path)
+ return log_oom();
+
+ if (null_or_empty_path(path)) {
+ log_debug("%s is empty (a mask).", path);
+ continue;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ log_error_errno(errno, "Failed to fork: %m");
+ continue;
+ } else if (pid == 0) {
+ char *_argv[2];
+
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ if (!argv) {
+ _argv[0] = path;
+ _argv[1] = NULL;
+ argv = _argv;
+ } else
+ argv[0] = path;
+
+ execv(path, argv);
+ return log_error_errno(errno, "Failed to execute %s: %m", path);
+ }
+
+ log_debug("Spawned %s as " PID_FMT ".", path, pid);
+
+ r = hashmap_put(pids, UINT_TO_PTR(pid), path);
+ if (r < 0)
+ return log_oom();
+ path = NULL;
+ }
+ }
+
+ /* Abort execution of this process after the timout. We simply
+ * rely on SIGALRM as default action terminating the process,
+ * and turn on alarm(). */
+
+ if (timeout != USEC_INFINITY)
+ alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+
+ while (!hashmap_isempty(pids)) {
+ _cleanup_free_ char *path = NULL;
+ pid_t pid;
+
+ pid = PTR_TO_UINT(hashmap_first_key(pids));
+ assert(pid > 0);
+
+ path = hashmap_remove(pids, UINT_TO_PTR(pid));
+ assert(path);
+
+ wait_for_terminate_and_warn(path, pid, true);
+ }
+
+ return 0;
+}
+
+void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
+ pid_t executor_pid;
+ int r;
+ char *name;
+ char **dirs = (char**) directories;
+
+ assert(!strv_isempty(dirs));
+
+ name = basename(dirs[0]);
+ assert(!isempty(name));
+
+ /* Executes all binaries in the directories in parallel and waits
+ * for them to finish. Optionally a timeout is applied. If a file
+ * with the same name exists in more than one directory, the
+ * earliest one wins. */
+
+ executor_pid = fork();
+ if (executor_pid < 0) {
+ log_error_errno(errno, "Failed to fork: %m");
+ return;
+
+ } else if (executor_pid == 0) {
+ r = do_execute(dirs, timeout, argv);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+
+ wait_for_terminate_and_warn(name, executor_pid, true);
+}
+
+bool nulstr_contains(const char*nulstr, const char *needle) {
+ const char *i;
+
+ if (!nulstr)
+ return false;
+
+ NULSTR_FOREACH(i, nulstr)
+ if (streq(i, needle))
+ return true;
+
+ return false;
+}
+
+bool plymouth_running(void) {
+ return access("/run/plymouth/pid", F_OK) >= 0;
+}
+
+char* strshorten(char *s, size_t l) {
+ assert(s);
+
+ if (l < strlen(s))
+ s[l] = 0;
+
+ return s;
+}
+
+bool machine_name_is_valid(const char *s) {
+
+ if (!hostname_is_valid(s))
+ return false;
+
+ /* Machine names should be useful hostnames, but also be
+ * useful in unit names, hence we enforce a stricter length
+ * limitation. */
+
+ if (strlen(s) > 64)
+ return false;
+
+ return true;
+}
+
+int pipe_eof(int fd) {
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN|POLLHUP,
+ };
+
+ int r;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents & POLLHUP;
+}
+
+int fd_wait_for_event(int fd, int event, usec_t t) {
+
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = event,
+ };
+
+ struct timespec ts;
+ int r;
+
+ r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents;
+}
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+ FILE *f;
+ char *t;
+ int r, fd;
+
+ assert(path);
+ assert(_f);
+ assert(_temp_path);
+
+ r = tempfn_xxxxxx(path, &t);
+ if (r < 0)
+ return r;
+
+ fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ free(t);
+ return -errno;
+ }
+
+ f = fdopen(fd, "we");
+ if (!f) {
+ unlink(t);
+ free(t);
+ return -errno;
+ }
+
+ *_f = f;
+ *_temp_path = t;
+
+ return 0;
+}
+
+int symlink_atomic(const char *from, const char *to) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ r = tempfn_random(to, &t);
+ if (r < 0)
+ return r;
+
+ if (symlink(from, t) < 0)
+ return -errno;
+
+ if (rename(t, to) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int symlink_idempotent(const char *from, const char *to) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ if (symlink(from, to) < 0) {
+ if (errno != EEXIST)
+ return -errno;
+
+ r = readlink_malloc(to, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq(p, from))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(path);
+
+ r = tempfn_random(path, &t);
+ if (r < 0)
+ return r;
+
+ if (mknod(t, mode, dev) < 0)
+ return -errno;
+
+ if (rename(t, path) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int mkfifo_atomic(const char *path, mode_t mode) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(path);
+
+ r = tempfn_random(path, &t);
+ if (r < 0)
+ return r;
+
+ if (mkfifo(t, mode) < 0)
+ return -errno;
+
+ if (rename(t, path) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
+bool display_is_local(const char *display) {
+ assert(display);
+
+ return
+ display[0] == ':' &&
+ display[1] >= '0' &&
+ display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+ size_t k;
+ char *f, *c;
+
+ assert(display);
+ assert(path);
+
+ if (!display_is_local(display))
+ return -EINVAL;
+
+ k = strspn(display+1, "0123456789");
+
+ f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
+ if (!f)
+ return -ENOMEM;
+
+ c = stpcpy(f, "/tmp/.X11-unix/X");
+ memcpy(c, display+1, k);
+ c[k] = 0;
+
+ *path = f;
+
+ return 0;
+}
+
+int get_user_creds(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell) {
+
+ struct passwd *p;
+ uid_t u;
+
+ assert(username);
+ assert(*username);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+
+ if (uid)
+ *uid = 0;
+
+ if (gid)
+ *gid = 0;
+
+ if (home)
+ *home = "/root";
+
+ if (shell)
+ *shell = "/bin/sh";
+
+ return 0;
+ }
+
+ if (parse_uid(*username, &u) >= 0) {
+ errno = 0;
+ p = getpwuid(u);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurrence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (uid)
+ *uid = p->pw_uid;
+
+ if (gid)
+ *gid = p->pw_gid;
+
+ if (home)
+ *home = p->pw_dir;
+
+ if (shell)
+ *shell = p->pw_shell;
+
+ return 0;
+}
+
+char* uid_to_name(uid_t uid) {
+ struct passwd *p;
+ char *r;
+
+ if (uid == 0)
+ return strdup("root");
+
+ p = getpwuid(uid);
+ if (p)
+ return strdup(p->pw_name);
+
+ if (asprintf(&r, UID_FMT, uid) < 0)
+ return NULL;
+
+ return r;
+}
+
+char* gid_to_name(gid_t gid) {
+ struct group *p;
+ char *r;
+
+ if (gid == 0)
+ return strdup("root");
+
+ p = getgrgid(gid);
+ if (p)
+ return strdup(p->gr_name);
+
+ if (asprintf(&r, GID_FMT, gid) < 0)
+ return NULL;
+
+ return r;
+}
+
+int get_group_creds(const char **groupname, gid_t *gid) {
+ struct group *g;
+ gid_t id;
+
+ assert(groupname);
+
+ /* We enforce some special rules for gid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*groupname, "root") || streq(*groupname, "0")) {
+ *groupname = "root";
+
+ if (gid)
+ *gid = 0;
+
+ return 0;
+ }
+
+ if (parse_gid(*groupname, &id) >= 0) {
+ errno = 0;
+ g = getgrgid(id);
+
+ if (g)
+ *groupname = g->gr_name;
+ } else {
+ errno = 0;
+ g = getgrnam(*groupname);
+ }
+
+ if (!g)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (gid)
+ *gid = g->gr_gid;
+
+ return 0;
+}
+
+int in_gid(gid_t gid) {
+ gid_t *gids;
+ int ngroups_max, r, i;
+
+ if (getgid() == gid)
+ return 1;
+
+ if (getegid() == gid)
+ return 1;
+
+ ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ assert(ngroups_max > 0);
+
+ gids = alloca(sizeof(gid_t) * ngroups_max);
+
+ r = getgroups(ngroups_max, gids);
+ if (r < 0)
+ return -errno;
+
+ for (i = 0; i < r; i++)
+ if (gids[i] == gid)
+ return 1;
+
+ return 0;
+}
+
+int in_group(const char *name) {
+ int r;
+ gid_t gid;
+
+ r = get_group_creds(&name, &gid);
+ if (r < 0)
+ return r;
+
+ return in_gid(gid);
+}
+
+int glob_exists(const char *path) {
+ _cleanup_globfree_ glob_t g = {};
+ int k;
+
+ assert(path);
+
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ return 0;
+ else if (k == GLOB_NOSPACE)
+ return -ENOMEM;
+ else if (k == 0)
+ return !strv_isempty(g.gl_pathv);
+ else
+ return errno ? -errno : -EIO;
+}
+
+int glob_extend(char ***strv, const char *path) {
+ _cleanup_globfree_ glob_t g = {};
+ int k;
+ char **p;
+
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ return -ENOENT;
+ else if (k == GLOB_NOSPACE)
+ return -ENOMEM;
+ else if (k != 0 || strv_isempty(g.gl_pathv))
+ return errno ? -errno : -EIO;
+
+ STRV_FOREACH(p, g.gl_pathv) {
+ k = strv_extend(strv, *p);
+ if (k < 0)
+ break;
+ }
+
+ return k;
+}
+
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+ struct stat st;
+
+ assert(d);
+ assert(de);
+
+ if (de->d_type != DT_UNKNOWN)
+ return 0;
+
+ if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ de->d_type =
+ S_ISREG(st.st_mode) ? DT_REG :
+ S_ISDIR(st.st_mode) ? DT_DIR :
+ S_ISLNK(st.st_mode) ? DT_LNK :
+ S_ISFIFO(st.st_mode) ? DT_FIFO :
+ S_ISSOCK(st.st_mode) ? DT_SOCK :
+ S_ISCHR(st.st_mode) ? DT_CHR :
+ S_ISBLK(st.st_mode) ? DT_BLK :
+ DT_UNKNOWN;
+
+ return 0;
+}
+
+int get_files_in_directory(const char *path, char ***list) {
+ _cleanup_closedir_ DIR *d = NULL;
+ size_t bufsize = 0, n = 0;
+ _cleanup_strv_free_ char **l = NULL;
+
+ assert(path);
+
+ /* Returns all files in a directory in *list, and the number
+ * of files as return value. If list is NULL returns only the
+ * number. */
+
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ for (;;) {
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0)
+ return -errno;
+ if (!de)
+ break;
+
+ dirent_ensure_type(d, de);
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (list) {
+ /* one extra slot is needed for the terminating NULL */
+ if (!GREEDY_REALLOC(l, bufsize, n + 2))
+ return -ENOMEM;
+
+ l[n] = strdup(de->d_name);
+ if (!l[n])
+ return -ENOMEM;
+
+ l[++n] = NULL;
+ } else
+ n++;
+ }
+
+ if (list) {
+ *list = l;
+ l = NULL; /* avoid freeing */
+ }
+
+ return n;
+}
+
+char *strjoin(const char *x, ...) {
+ va_list ap;
+ size_t l;
+ char *r, *p;
+
+ va_start(ap, x);
+
+ if (x) {
+ l = strlen(x);
+
+ for (;;) {
+ const char *t;
+ size_t n;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ n = strlen(t);
+ if (n > ((size_t) -1) - l) {
+ va_end(ap);
+ return NULL;
+ }
+
+ l += n;
+ }
+ } else
+ l = 0;
+
+ va_end(ap);
+
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ if (x) {
+ p = stpcpy(r, x);
+
+ va_start(ap, x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+
+ va_end(ap);
+ } else
+ r[0] = 0;
+
+ return r;
+}
+
+bool is_main_thread(void) {
+ static thread_local int cached = 0;
+
+ if (_unlikely_(cached == 0))
+ cached = getpid() == gettid() ? 1 : -1;
+
+ return cached > 0;
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+ char *p, *s;
+ int r;
+ unsigned n, m;
+
+ assert(ret);
+
+ /* If it has a queue this is good enough for us */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r >= 0) {
+ *ret = d;
+ return 0;
+ }
+
+ /* If it is a partition find the originating device */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r < 0)
+ return -ENOENT;
+
+ /* Get parent dev_t */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+
+ if (r < 0)
+ return r;
+
+ r = sscanf(s, "%u:%u", &m, &n);
+ free(s);
+
+ if (r != 2)
+ return -EINVAL;
+
+ /* Only return this if it is really good enough for us. */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r >= 0) {
+ *ret = makedev(m, n);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static const char *const ioprio_class_table[] = {
+ [IOPRIO_CLASS_NONE] = "none",
+ [IOPRIO_CLASS_RT] = "realtime",
+ [IOPRIO_CLASS_BE] = "best-effort",
+ [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
+
+static const char *const sigchld_code_table[] = {
+ [CLD_EXITED] = "exited",
+ [CLD_KILLED] = "killed",
+ [CLD_DUMPED] = "dumped",
+ [CLD_TRAPPED] = "trapped",
+ [CLD_STOPPED] = "stopped",
+ [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
+ [LOG_FAC(LOG_KERN)] = "kern",
+ [LOG_FAC(LOG_USER)] = "user",
+ [LOG_FAC(LOG_MAIL)] = "mail",
+ [LOG_FAC(LOG_DAEMON)] = "daemon",
+ [LOG_FAC(LOG_AUTH)] = "auth",
+ [LOG_FAC(LOG_SYSLOG)] = "syslog",
+ [LOG_FAC(LOG_LPR)] = "lpr",
+ [LOG_FAC(LOG_NEWS)] = "news",
+ [LOG_FAC(LOG_UUCP)] = "uucp",
+ [LOG_FAC(LOG_CRON)] = "cron",
+ [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+ [LOG_FAC(LOG_FTP)] = "ftp",
+ [LOG_FAC(LOG_LOCAL0)] = "local0",
+ [LOG_FAC(LOG_LOCAL1)] = "local1",
+ [LOG_FAC(LOG_LOCAL2)] = "local2",
+ [LOG_FAC(LOG_LOCAL3)] = "local3",
+ [LOG_FAC(LOG_LOCAL4)] = "local4",
+ [LOG_FAC(LOG_LOCAL5)] = "local5",
+ [LOG_FAC(LOG_LOCAL6)] = "local6",
+ [LOG_FAC(LOG_LOCAL7)] = "local7"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
+
+static const char *const log_level_table[] = {
+ [LOG_EMERG] = "emerg",
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_ERR] = "err",
+ [LOG_WARNING] = "warning",
+ [LOG_NOTICE] = "notice",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
+
+static const char* const sched_policy_table[] = {
+ [SCHED_OTHER] = "other",
+ [SCHED_BATCH] = "batch",
+ [SCHED_IDLE] = "idle",
+ [SCHED_FIFO] = "fifo",
+ [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
+
+static const char* const rlimit_table[_RLIMIT_MAX] = {
+ [RLIMIT_CPU] = "LimitCPU",
+ [RLIMIT_FSIZE] = "LimitFSIZE",
+ [RLIMIT_DATA] = "LimitDATA",
+ [RLIMIT_STACK] = "LimitSTACK",
+ [RLIMIT_CORE] = "LimitCORE",
+ [RLIMIT_RSS] = "LimitRSS",
+ [RLIMIT_NOFILE] = "LimitNOFILE",
+ [RLIMIT_AS] = "LimitAS",
+ [RLIMIT_NPROC] = "LimitNPROC",
+ [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
+ [RLIMIT_LOCKS] = "LimitLOCKS",
+ [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
+ [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
+ [RLIMIT_NICE] = "LimitNICE",
+ [RLIMIT_RTPRIO] = "LimitRTPRIO",
+ [RLIMIT_RTTIME] = "LimitRTTIME"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
+
+static const char* const ip_tos_table[] = {
+ [IPTOS_LOWDELAY] = "low-delay",
+ [IPTOS_THROUGHPUT] = "throughput",
+ [IPTOS_RELIABILITY] = "reliability",
+ [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+
+bool kexec_loaded(void) {
+ bool loaded = false;
+ char *s;
+
+ if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+ if (s[0] == '1')
+ loaded = true;
+ free(s);
+ }
+ return loaded;
+}
+
+int prot_from_flags(int flags) {
+
+ switch (flags & O_ACCMODE) {
+
+ case O_RDONLY:
+ return PROT_READ;
+
+ case O_WRONLY:
+ return PROT_WRITE;
+
+ case O_RDWR:
+ return PROT_READ|PROT_WRITE;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+char *format_bytes(char *buf, size_t l, off_t t) {
+ unsigned i;
+
+ static const struct {
+ const char *suffix;
+ off_t factor;
+ } table[] = {
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "K", 1024ULL },
+ };
+
+ if (t == (off_t) -1)
+ return NULL;
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+
+ if (t >= table[i].factor) {
+ snprintf(buf, l,
+ "%llu.%llu%s",
+ (unsigned long long) (t / table[i].factor),
+ (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
+ table[i].suffix);
+
+ goto finish;
+ }
+ }
+
+ snprintf(buf, l, "%lluB", (unsigned long long) t);
+
+finish:
+ buf[l-1] = 0;
+ return buf;
+
+}
+
+void* memdup(const void *p, size_t l) {
+ void *r;
+
+ assert(p);
+
+ r = malloc(l);
+ if (!r)
+ return NULL;
+
+ memcpy(r, p, l);
+ return r;
+}
+
+int fd_inc_sndbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+ if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+ return 0;
+
+ /* If we have the privileges we will ignore the kernel limit. */
+
+ value = (int) n;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+ if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+ return 0;
+
+ /* If we have the privileges we will ignore the kernel limit. */
+
+ value = (int) n;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+ return -errno;
+ return 1;
+}
+
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
+ bool stdout_is_tty, stderr_is_tty;
+ pid_t parent_pid, agent_pid;
+ sigset_t ss, saved_ss;
+ unsigned n, i;
+ va_list ap;
+ char **l;
+
+ assert(pid);
+ assert(path);
+
+ /* Spawns a temporary TTY agent, making sure it goes away when
+ * we go away */
+
+ parent_pid = getpid();
+
+ /* First we temporarily block all signals, so that the new
+ * child has them blocked initially. This way, we can be sure
+ * that SIGTERMs are not lost we might send to the agent. */
+ assert_se(sigfillset(&ss) >= 0);
+ assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
+
+ agent_pid = fork();
+ if (agent_pid < 0) {
+ assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
+ return -errno;
+ }
+
+ if (agent_pid != 0) {
+ assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
+ *pid = agent_pid;
+ return 0;
+ }
+
+ /* In the child:
+ *
+ * Make sure the agent goes away when the parent dies */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ _exit(EXIT_FAILURE);
+
+ /* Make sure we actually can kill the agent, if we need to, in
+ * case somebody invoked us from a shell script that trapped
+ * SIGTERM or so... */
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+
+ /* Check whether our parent died before we were able
+ * to set the death signal and unblock the signals */
+ if (getppid() != parent_pid)
+ _exit(EXIT_SUCCESS);
+
+ /* Don't leak fds to the agent */
+ close_all_fds(except, n_except);
+
+ stdout_is_tty = isatty(STDOUT_FILENO);
+ stderr_is_tty = isatty(STDERR_FILENO);
+
+ if (!stdout_is_tty || !stderr_is_tty) {
+ int fd;
+
+ /* Detach from stdout/stderr. and reopen
+ * /dev/tty for them. This is important to
+ * ensure that when systemctl is started via
+ * popen() or a similar call that expects to
+ * read EOF we actually do generate EOF and
+ * not delay this indefinitely by because we
+ * keep an unused copy of stdin around. */
+ fd = open("/dev/tty", O_WRONLY);
+ if (fd < 0) {
+ log_error_errno(errno, "Failed to open /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stdout_is_tty)
+ dup2(fd, STDOUT_FILENO);
+
+ if (!stderr_is_tty)
+ dup2(fd, STDERR_FILENO);
+
+ if (fd > 2)
+ close(fd);
+ }
+
+ /* Count arguments */
+ va_start(ap, path);
+ for (n = 0; va_arg(ap, char*); n++)
+ ;
+ va_end(ap);
+
+ /* Allocate strv */
+ l = alloca(sizeof(char *) * (n + 1));
+
+ /* Fill in arguments */
+ va_start(ap, path);
+ for (i = 0; i <= n; i++)
+ l[i] = va_arg(ap, char*);
+ va_end(ap);
+
+ execv(path, l);
+ _exit(EXIT_FAILURE);
+}
+
+int setrlimit_closest(int resource, const struct rlimit *rlim) {
+ struct rlimit highest, fixed;
+
+ assert(rlim);
+
+ if (setrlimit(resource, rlim) >= 0)
+ return 0;
+
+ if (errno != EPERM)
+ return -errno;
+
+ /* So we failed to set the desired setrlimit, then let's try
+ * to get as close as we can */
+ assert_se(getrlimit(resource, &highest) == 0);
+
+ fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
+ fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
+
+ if (setrlimit(resource, &fixed) < 0)
+ return -errno;
+
+ return 0;
+}
+
+bool http_etag_is_valid(const char *etag) {
+ if (isempty(etag))
+ return false;
+
+ if (!endswith(etag, "\""))
+ return false;
+
+ if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
+ return false;
+
+ return true;
+}
+
+bool http_url_is_valid(const char *url) {
+ const char *p;
+
+ if (isempty(url))
+ return false;
+
+ p = startswith(url, "http://");
+ if (!p)
+ p = startswith(url, "https://");
+ if (!p)
+ return false;
+
+ if (isempty(p))
+ return false;
+
+ return ascii_is_valid(p);
+}
+
+bool documentation_url_is_valid(const char *url) {
+ const char *p;
+
+ if (isempty(url))
+ return false;
+
+ if (http_url_is_valid(url))
+ return true;
+
+ p = startswith(url, "file:/");
+ if (!p)
+ p = startswith(url, "info:");
+ if (!p)
+ p = startswith(url, "man:");
+
+ if (isempty(p))
+ return false;
+
+ return ascii_is_valid(p);
+}
+
+bool in_initrd(void) {
+ static int saved = -1;
+ struct statfs s;
+
+ if (saved >= 0)
+ return saved;
+
+ /* We make two checks here:
+ *
+ * 1. the flag file /etc/initrd-release must exist
+ * 2. the root file system must be a memory file system
+ *
+ * The second check is extra paranoia, since misdetecting an
+ * initrd can have bad bad consequences due the initrd
+ * emptying when transititioning to the main systemd.
+ */
+
+ saved = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ is_temporary_fs(&s);
+
+ return saved;
+}
+
+int get_home_dir(char **_h) {
+ struct passwd *p;
+ const char *e;
+ char *h;
+ uid_t u;
+
+ assert(_h);
+
+ /* Take the user specified one */
+ e = secure_getenv("HOME");
+ if (e && path_is_absolute(e)) {
+ h = strdup(e);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ h = strdup("/root");
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_dir))
+ return -EINVAL;
+
+ h = strdup(p->pw_dir);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+}
+
+int get_shell(char **_s) {
+ struct passwd *p;
+ const char *e;
+ char *s;
+ uid_t u;
+
+ assert(_s);
+
+ /* Take the user specified one */
+ e = getenv("SHELL");
+ if (e) {
+ s = strdup(e);
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ s = strdup("/bin/sh");
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_shell))
+ return -EINVAL;
+
+ s = strdup(p->pw_shell);
+ if (!s)
+ return -ENOMEM;
+
+ *_s = s;
+ return 0;
+}
+
+bool filename_is_valid(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (strchr(p, '/'))
+ return false;
+
+ if (streq(p, "."))
+ return false;
+
+ if (streq(p, ".."))
+ return false;
+
+ if (strlen(p) > FILENAME_MAX)
+ return false;
+
+ return true;
+}
+
+bool string_is_safe(const char *p) {
+ const char *t;
+
+ if (!p)
+ return false;
+
+ for (t = p; *t; t++) {
+ if (*t > 0 && *t < ' ')
+ return false;
+
+ if (strchr("\\\"\'\0x7f", *t))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Check if a string contains control characters. If 'ok' is non-NULL
+ * it may be a string containing additional CCs to be considered OK.
+ */
+bool string_has_cc(const char *p, const char *ok) {
+ const char *t;
+
+ assert(p);
+
+ for (t = p; *t; t++) {
+ if (ok && strchr(ok, *t))
+ continue;
+
+ if (*t > 0 && *t < ' ')
+ return true;
+
+ if (*t == 127)
+ return true;
+ }
+
+ return false;
+}
+
+bool path_is_safe(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ return false;
+
+ if (strlen(p)+1 > PATH_MAX)
+ return false;
+
+ /* The following two checks are not really dangerous, but hey, they still are confusing */
+ if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+ return false;
+
+ if (strstr(p, "//"))
+ return false;
+
+ return true;
+}
+
+/* hey glibc, APIs with callbacks without a user pointer are so useless */
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+ int (*compar) (const void *, const void *, void *), void *arg) {
+ size_t l, u, idx;
+ const void *p;
+ int comparison;
+
+ l = 0;
+ u = nmemb;
+ while (l < u) {
+ idx = (l + u) / 2;
+ p = (void *)(((const char *) base) + (idx * size));
+ comparison = compar(key, p, arg);
+ if (comparison < 0)
+ u = idx;
+ else if (comparison > 0)
+ l = idx + 1;
+ else
+ return (void *)p;
+ }
+ return NULL;
+}
+
+void init_gettext(void) {
+ setlocale(LC_ALL, "");
+ textdomain(GETTEXT_PACKAGE);
+}
+
+bool is_locale_utf8(void) {
+ const char *set;
+ static int cached_answer = -1;
+
+ if (cached_answer >= 0)
+ goto out;
+
+ if (!setlocale(LC_ALL, "")) {
+ cached_answer = true;
+ goto out;
+ }
+
+ set = nl_langinfo(CODESET);
+ if (!set) {
+ cached_answer = true;
+ goto out;
+ }
+
+ if (streq(set, "UTF-8")) {
+ cached_answer = true;
+ goto out;
+ }
+
+ /* For LC_CTYPE=="C" return true, because CTYPE is effectly
+ * unset and everything can do to UTF-8 nowadays. */
+ set = setlocale(LC_CTYPE, NULL);
+ if (!set) {
+ cached_answer = true;
+ goto out;
+ }
+
+ /* Check result, but ignore the result if C was set
+ * explicitly. */
+ cached_answer =
+ streq(set, "C") &&
+ !getenv("LC_ALL") &&
+ !getenv("LC_CTYPE") &&
+ !getenv("LANG");
+
+out:
+ return (bool) cached_answer;
+}
+
+const char *draw_special_char(DrawSpecialChar ch) {
+ static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
+
+ /* UTF-8 */ {
+ [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */
+ [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
+ [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
+ [DRAW_TREE_SPACE] = " ", /* */
+ [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
+ [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */
+ [DRAW_ARROW] = "\342\206\222", /* → */
+ [DRAW_DASH] = "\342\200\223", /* – */
+ },
+
+ /* ASCII fallback */ {
+ [DRAW_TREE_VERTICAL] = "| ",
+ [DRAW_TREE_BRANCH] = "|-",
+ [DRAW_TREE_RIGHT] = "`-",
+ [DRAW_TREE_SPACE] = " ",
+ [DRAW_TRIANGULAR_BULLET] = ">",
+ [DRAW_BLACK_CIRCLE] = "*",
+ [DRAW_ARROW] = "->",
+ [DRAW_DASH] = "-",
+ }
+ };
+
+ return draw_table[!is_locale_utf8()][ch];
+}
+
+char *strreplace(const char *text, const char *old_string, const char *new_string) {
+ const char *f;
+ char *t, *r;
+ size_t l, old_len, new_len;
+
+ assert(text);
+ assert(old_string);
+ assert(new_string);
+
+ old_len = strlen(old_string);
+ new_len = strlen(new_string);
+
+ l = strlen(text);
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ f = text;
+ t = r;
+ while (*f) {
+ char *a;
+ size_t d, nl;
+
+ if (!startswith(f, old_string)) {
+ *(t++) = *(f++);
+ continue;
+ }
+
+ d = t - r;
+ nl = l - old_len + new_len;
+ a = realloc(r, nl + 1);
+ if (!a)
+ goto oom;
+
+ l = nl;
+ r = a;
+ t = r + d;
+
+ t = stpcpy(t, new_string);
+ f += old_len;
+ }
+
+ *t = 0;
+ return r;
+
+oom:
+ free(r);
+ return NULL;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+ const char *i, *begin = NULL;
+ enum {
+ STATE_OTHER,
+ STATE_ESCAPE,
+ STATE_BRACKET
+ } state = STATE_OTHER;
+ char *obuf = NULL;
+ size_t osz = 0, isz;
+ FILE *f;
+
+ assert(ibuf);
+ assert(*ibuf);
+
+ /* Strips ANSI color and replaces TABs by 8 spaces */
+
+ isz = _isz ? *_isz : strlen(*ibuf);
+
+ f = open_memstream(&obuf, &osz);
+ if (!f)
+ return NULL;
+
+ for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+
+ switch (state) {
+
+ case STATE_OTHER:
+ if (i >= *ibuf + isz) /* EOT */
+ break;
+ else if (*i == '\x1B')
+ state = STATE_ESCAPE;
+ else if (*i == '\t')
+ fputs(" ", f);
+ else
+ fputc(*i, f);
+ break;
+
+ case STATE_ESCAPE:
+ if (i >= *ibuf + isz) { /* EOT */
+ fputc('\x1B', f);
+ break;
+ } else if (*i == '[') {
+ state = STATE_BRACKET;
+ begin = i + 1;
+ } else {
+ fputc('\x1B', f);
+ fputc(*i, f);
+ state = STATE_OTHER;
+ }
+
+ break;
+
+ case STATE_BRACKET:
+
+ if (i >= *ibuf + isz || /* EOT */
+ (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
+ fputc('\x1B', f);
+ fputc('[', f);
+ state = STATE_OTHER;
+ i = begin-1;
+ } else if (*i == 'm')
+ state = STATE_OTHER;
+ break;
+ }
+ }
+
+ if (ferror(f)) {
+ fclose(f);
+ free(obuf);
+ return NULL;
+ }
+
+ fclose(f);
+
+ free(*ibuf);
+ *ibuf = obuf;
+
+ if (_isz)
+ *_isz = osz;
+
+ return obuf;
+}
+
+int on_ac_power(void) {
+ bool found_offline = false, found_online = false;
+ _cleanup_closedir_ DIR *d = NULL;
+
+ d = opendir("/sys/class/power_supply");
+ if (!d)
+ return errno == ENOENT ? true : -errno;
+
+ for (;;) {
+ struct dirent *de;
+ _cleanup_close_ int fd = -1, device = -1;
+ char contents[6];
+ ssize_t n;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de && errno != 0)
+ return -errno;
+
+ if (!de)
+ break;
+
+ if (hidden_file(de->d_name))
+ continue;
+
+ device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (device < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ continue;
+
+ return -errno;
+ }
+
+ fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ return -errno;
+ }
+
+ n = read(fd, contents, sizeof(contents));
+ if (n < 0)
+ return -errno;
+
+ if (n != 6 || memcmp(contents, "Mains\n", 6))
+ continue;
+
+ safe_close(fd);
+ fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ return -errno;
+ }
+
+ n = read(fd, contents, sizeof(contents));
+ if (n < 0)
+ return -errno;
+
+ if (n != 2 || contents[1] != '\n')
+ return -EIO;
+
+ if (contents[0] == '1') {
+ found_online = true;
+ break;
+ } else if (contents[0] == '0')
+ found_offline = true;
+ else
+ return -EIO;
+ }
+
+ return found_online || !found_offline;
+}
+
+static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
+ char **i;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (!path_strv_resolve_uniq(search, root))
+ return -ENOMEM;
+
+ STRV_FOREACH(i, search) {
+ _cleanup_free_ char *p = NULL;
+ FILE *f;
+
+ if (root)
+ p = strjoin(root, *i, "/", path, NULL);
+ else
+ p = strjoin(*i, "/", path, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ f = fopen(p, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, root, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ s = strv_split_nulstr(search);
+ if (!s)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, root, s, _f);
+}
+
+char *strextend(char **x, ...) {
+ va_list ap;
+ size_t f, l;
+ char *r, *p;
+
+ assert(x);
+
+ l = f = *x ? strlen(*x) : 0;
+
+ va_start(ap, x);
+ for (;;) {
+ const char *t;
+ size_t n;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ n = strlen(t);
+ if (n > ((size_t) -1) - l) {
+ va_end(ap);
+ return NULL;
+ }
+
+ l += n;
+ }
+ va_end(ap);
+
+ r = realloc(*x, l+1);
+ if (!r)
+ return NULL;
+
+ p = r + f;
+
+ va_start(ap, x);
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+ va_end(ap);
+
+ *p = 0;
+ *x = r;
+
+ return r + l;
+}
+
+char *strrep(const char *s, unsigned n) {
+ size_t l;
+ char *r, *p;
+ unsigned i;
+
+ assert(s);
+
+ l = strlen(s);
+ p = r = malloc(l * n + 1);
+ if (!r)
+ return NULL;
+
+ for (i = 0; i < n; i++)
+ p = stpcpy(p, s);
+
+ *p = 0;
+ return r;
+}
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
+ size_t a, newalloc;
+ void *q;
+
+ assert(p);
+ assert(allocated);
+
+ if (*allocated >= need)
+ return *p;
+
+ newalloc = MAX(need * 2, 64u / size);
+ a = newalloc * size;
+
+ /* check for overflows */
+ if (a < size * need)
+ return NULL;
+
+ q = realloc(*p, a);
+ if (!q)
+ return NULL;
+
+ *p = q;
+ *allocated = newalloc;
+ return q;
+}
+
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
+ size_t prev;
+ uint8_t *q;
+
+ assert(p);
+ assert(allocated);
+
+ prev = *allocated;
+
+ q = greedy_realloc(p, allocated, need, size);
+ if (!q)
+ return NULL;
+
+ if (*allocated > prev)
+ memzero(q + prev * size, (*allocated - prev) * size);
+
+ return q;
+}
+
+bool id128_is_valid(const char *s) {
+ size_t i, l;
+
+ l = strlen(s);
+ if (l == 32) {
+
+ /* Simple formatted 128bit hex string */
+
+ for (i = 0; i < l; i++) {
+ char c = s[i];
+
+ if (!(c >= '0' && c <= '9') &&
+ !(c >= 'a' && c <= 'z') &&
+ !(c >= 'A' && c <= 'Z'))
+ return false;
+ }
+
+ } else if (l == 36) {
+
+ /* Formatted UUID */
+
+ for (i = 0; i < l; i++) {
+ char c = s[i];
+
+ if ((i == 8 || i == 13 || i == 18 || i == 23)) {
+ if (c != '-')
+ return false;
+ } else {
+ if (!(c >= '0' && c <= '9') &&
+ !(c >= 'a' && c <= 'z') &&
+ !(c >= 'A' && c <= 'Z'))
+ return false;
+ }
+ }
+
+ } else
+ return false;
+
+ return true;
+}
+
+int split_pair(const char *s, const char *sep, char **l, char **r) {
+ char *x, *a, *b;
+
+ assert(s);
+ assert(sep);
+ assert(l);
+ assert(r);
+
+ if (isempty(sep))
+ return -EINVAL;
+
+ x = strstr(s, sep);
+ if (!x)
+ return -EINVAL;
+
+ a = strndup(s, x - s);
+ if (!a)
+ return -ENOMEM;
+
+ b = strdup(x + strlen(sep));
+ if (!b) {
+ free(a);
+ return -ENOMEM;
+ }
+
+ *l = a;
+ *r = b;
+
+ return 0;
+}
+
+int shall_restore_state(void) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ r = get_proc_cmdline_key("systemd.restore_state=", &value);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return true;
+
+ return parse_boolean(value) != 0;
+}
+
+int proc_cmdline(char **ret) {
+ assert(ret);
+
+ if (detect_container(NULL) > 0)
+ return get_process_cmdline(1, 0, false, ret);
+ else
+ return read_one_line_file("/proc/cmdline", ret);
+}
+
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ int r;
+
+ assert(parse_item);
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ p = line;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ char *value = NULL;
+
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* Filter out arguments that are intended only for the
+ * initrd */
+ if (!in_initrd() && startswith(word, "rd."))
+ continue;
+
+ value = strchr(word, '=');
+ if (value)
+ *(value++) = 0;
+
+ r = parse_item(word, value);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int get_proc_cmdline_key(const char *key, char **value) {
+ _cleanup_free_ char *line = NULL, *ret = NULL;
+ bool found = false;
+ const char *p;
+ int r;
+
+ assert(key);
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ p = line;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *e;
+
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* Filter out arguments that are intended only for the
+ * initrd */
+ if (!in_initrd() && startswith(word, "rd."))
+ continue;
+
+ if (value) {
+ e = startswith(word, key);
+ if (!e)
+ continue;
+
+ r = free_and_strdup(&ret, e);
+ if (r < 0)
+ return r;
+
+ found = true;
+ } else {
+ if (streq(word, key))
+ found = true;
+ }
+ }
+
+ if (value) {
+ *value = ret;
+ ret = NULL;
+ }
+
+ return found;
+
+}
+
+int container_get_leader(const char *machine, pid_t *pid) {
+ _cleanup_free_ char *s = NULL, *class = NULL;
+ const char *p;
+ pid_t leader;
+ int r;
+
+ assert(machine);
+ assert(pid);
+
+ p = strjoina("/run/systemd/machines/", machine);
+ r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
+ if (r == -ENOENT)
+ return -EHOSTDOWN;
+ if (r < 0)
+ return r;
+ if (!s)
+ return -EIO;
+
+ if (!streq_ptr(class, "container"))
+ return -EIO;
+
+ r = parse_pid(s, &leader);
+ if (r < 0)
+ return r;
+ if (leader <= 1)
+ return -EIO;
+
+ *pid = leader;
+ return 0;
+}
+
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1;
+ int rfd = -1;
+
+ assert(pid >= 0);
+
+ if (mntns_fd) {
+ const char *mntns;
+
+ mntns = procfs_file_alloca(pid, "ns/mnt");
+ mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (mntnsfd < 0)
+ return -errno;
+ }
+
+ if (pidns_fd) {
+ const char *pidns;
+
+ pidns = procfs_file_alloca(pid, "ns/pid");
+ pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (pidnsfd < 0)
+ return -errno;
+ }
+
+ if (netns_fd) {
+ const char *netns;
+
+ netns = procfs_file_alloca(pid, "ns/net");
+ netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (netnsfd < 0)
+ return -errno;
+ }
+
+ if (root_fd) {
+ const char *root;
+
+ root = procfs_file_alloca(pid, "root");
+ rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (rfd < 0)
+ return -errno;
+ }
+
+ if (pidns_fd)
+ *pidns_fd = pidnsfd;
+
+ if (mntns_fd)
+ *mntns_fd = mntnsfd;
+
+ if (netns_fd)
+ *netns_fd = netnsfd;
+
+ if (root_fd)
+ *root_fd = rfd;
+
+ pidnsfd = mntnsfd = netnsfd = -1;
+
+ return 0;
+}
+
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
+
+ if (pidns_fd >= 0)
+ if (setns(pidns_fd, CLONE_NEWPID) < 0)
+ return -errno;
+
+ if (mntns_fd >= 0)
+ if (setns(mntns_fd, CLONE_NEWNS) < 0)
+ return -errno;
+
+ if (netns_fd >= 0)
+ if (setns(netns_fd, CLONE_NEWNET) < 0)
+ return -errno;
+
+ if (root_fd >= 0) {
+ if (fchdir(root_fd) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+ }
+
+ return reset_uid_gid();
+}
+
+int getpeercred(int fd, struct ucred *ucred) {
+ socklen_t n = sizeof(struct ucred);
+ struct ucred u;
+ int r;
+
+ assert(fd >= 0);
+ assert(ucred);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
+ if (r < 0)
+ return -errno;
+
+ if (n != sizeof(struct ucred))
+ return -EIO;
+
+ /* Check if the data is actually useful and not suppressed due
+ * to namespacing issues */
+ if (u.pid <= 0)
+ return -ENODATA;
+ if (u.uid == UID_INVALID)
+ return -ENODATA;
+ if (u.gid == GID_INVALID)
+ return -ENODATA;
+
+ *ucred = u;
+ return 0;
+}
+
+int getpeersec(int fd, char **ret) {
+ socklen_t n = 64;
+ char *s;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ s = new0(char, n);
+ if (!s)
+ return -ENOMEM;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+ if (r < 0) {
+ free(s);
+
+ if (errno != ERANGE)
+ return -errno;
+
+ s = new0(char, n);
+ if (!s)
+ return -ENOMEM;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+ if (r < 0) {
+ free(s);
+ return -errno;
+ }
+ }
+
+ if (isempty(s)) {
+ free(s);
+ return -EOPNOTSUPP;
+ }
+
+ *ret = s;
+ return 0;
+}
+
+/* This is much like like mkostemp() but is subject to umask(). */
+int mkostemp_safe(char *pattern, int flags) {
+ _cleanup_umask_ mode_t u;
+ int fd;
+
+ assert(pattern);
+
+ u = umask(077);
+
+ fd = mkostemp(pattern, flags);
+ if (fd < 0)
+ return -errno;
+
+ 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 fd_warn_permissions(const char *path, int fd) {
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (st.st_mode & 0111)
+ log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
+
+ if (st.st_mode & 0002)
+ log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
+
+ if (getpid() == 1 && (st.st_mode & 0044) != 0044)
+ log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
+
+ return 0;
+}
+
+unsigned long personality_from_string(const char *p) {
+
+ /* Parse a personality specifier. We introduce our own
+ * identifiers that indicate specific ABIs, rather than just
+ * hints regarding the register size, since we want to keep
+ * things open for multiple locally supported ABIs for the
+ * same register size. We try to reuse the ABI identifiers
+ * used by libseccomp. */
+
+#if defined(__x86_64__)
+
+ if (streq(p, "x86"))
+ return PER_LINUX32;
+
+ if (streq(p, "x86-64"))
+ return PER_LINUX;
+
+#elif defined(__i386__)
+
+ if (streq(p, "x86"))
+ return PER_LINUX;
+#endif
+
+ return PERSONALITY_INVALID;
+}
+
+const char* personality_to_string(unsigned long p) {
+
+#if defined(__x86_64__)
+
+ if (p == PER_LINUX32)
+ return "x86";
+
+ if (p == PER_LINUX)
+ return "x86-64";
+
+#elif defined(__i386__)
+
+ if (p == PER_LINUX)
+ return "x86";
+#endif
+
+ return NULL;
+}
+
+uint64_t physical_memory(void) {
+ long mem;
+
+ /* We return this as uint64_t in case we are running as 32bit
+ * process on a 64bit kernel with huge amounts of memory */
+
+ mem = sysconf(_SC_PHYS_PAGES);
+ assert(mem > 0);
+
+ return (uint64_t) mem * (uint64_t) page_size();
+}
+
+void hexdump(FILE *f, const void *p, size_t s) {
+ const uint8_t *b = p;
+ unsigned n = 0;
+
+ assert(s == 0 || b);
+
+ while (s > 0) {
+ size_t i;
+
+ fprintf(f, "%04x ", n);
+
+ for (i = 0; i < 16; i++) {
+
+ if (i >= s)
+ fputs(" ", f);
+ else
+ fprintf(f, "%02x ", b[i]);
+
+ if (i == 7)
+ fputc(' ', f);
+ }
+
+ fputc(' ', f);
+
+ for (i = 0; i < 16; i++) {
+
+ if (i >= s)
+ fputc(' ', f);
+ else
+ fputc(isprint(b[i]) ? (char) b[i] : '.', f);
+ }
+
+ fputc('\n', f);
+
+ if (s < 16)
+ break;
+
+ n += 16;
+ b += 16;
+ s -= 16;
+ }
+}
+
+int update_reboot_param_file(const char *param) {
+ int r = 0;
+
+ if (param) {
+
+ r = write_string_file(REBOOT_PARAM_FILE, param);
+ if (r < 0)
+ log_error("Failed to write reboot param to "
+ REBOOT_PARAM_FILE": %s", strerror(-r));
+ } else
+ unlink(REBOOT_PARAM_FILE);
+
+ return r;
+}
+
+int umount_recursive(const char *prefix, int flags) {
+ bool again;
+ int n = 0, r;
+
+ /* Try to umount everything recursively below a
+ * directory. Also, take care of stacked mounts, and keep
+ * unmounting them until they are gone. */
+
+ do {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+
+ again = false;
+ r = 0;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *path = NULL, *p = NULL;
+ int k;
+
+ k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%*s " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path);
+ if (k != 1) {
+ if (k == EOF)
+ break;
+
+ continue;
+ }
+
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
+
+ if (!path_startswith(p, prefix))
+ continue;
+
+ if (umount2(p, flags) < 0) {
+ r = -errno;
+ continue;
+ }
+
+ again = true;
+ n++;
+
+ break;
+ }
+
+ } while (again);
+
+ return r ? r : n;
+}
+
+static int get_mount_flags(const char *path, unsigned long *flags) {
+ struct statvfs buf;
+
+ if (statvfs(path, &buf) < 0)
+ return -errno;
+ *flags = buf.f_flag;
+ return 0;
+}
+
+int bind_remount_recursive(const char *prefix, bool ro) {
+ _cleanup_set_free_free_ Set *done = NULL;
+ _cleanup_free_ char *cleaned = NULL;
+ int r;
+
+ /* Recursively remount a directory (and all its submounts)
+ * read-only or read-write. If the directory is already
+ * mounted, we reuse the mount and simply mark it
+ * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+ * operation). If it isn't we first make it one. Afterwards we
+ * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
+ * submounts we can access, too. When mounts are stacked on
+ * the same mount point we only care for each individual
+ * "top-level" mount on each point, as we cannot
+ * influence/access the underlying mounts anyway. We do not
+ * have any effect on future submounts that might get
+ * propagated, they migt be writable. This includes future
+ * submounts that have been triggered via autofs. */
+
+ cleaned = strdup(prefix);
+ if (!cleaned)
+ return -ENOMEM;
+
+ path_kill_slashes(cleaned);
+
+ done = set_new(&string_hash_ops);
+ if (!done)
+ return -ENOMEM;
+
+ for (;;) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ _cleanup_set_free_free_ Set *todo = NULL;
+ bool top_autofs = false;
+ char *x;
+ unsigned long orig_flags;
+
+ todo = set_new(&string_hash_ops);
+ if (!todo)
+ return -ENOMEM;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
+ int k;
+
+ k = fscanf(proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%*s" /* (6) mount options (superblock) */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%ms " /* (9) file system type */
+ "%*s" /* (10) mount source */
+ "%*s" /* (11) mount options (bind mount) */
+ "%*[^\n]", /* some rubbish at the end */
+ &path,
+ &type);
+ if (k != 2) {
+ if (k == EOF)
+ break;
+
+ continue;
+ }
+
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
+
+ /* Let's ignore autofs mounts. If they aren't
+ * triggered yet, we want to avoid triggering
+ * them, as we don't make any guarantees for
+ * future submounts anyway. If they are
+ * already triggered, then we will find
+ * another entry for this. */
+ if (streq(type, "autofs")) {
+ top_autofs = top_autofs || path_equal(cleaned, p);
+ continue;
+ }
+
+ if (path_startswith(p, cleaned) &&
+ !set_contains(done, p)) {
+
+ r = set_consume(todo, p);
+ p = NULL;
+
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
+
+ /* If we have no submounts to process anymore and if
+ * the root is either already done, or an autofs, we
+ * are done */
+ if (set_isempty(todo) &&
+ (top_autofs || set_contains(done, cleaned)))
+ return 0;
+
+ if (!set_contains(done, cleaned) &&
+ !set_contains(todo, cleaned)) {
+ /* The prefix directory itself is not yet a
+ * mount, make it one. */
+ if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
+ return -errno;
+
+ orig_flags = 0;
+ (void) get_mount_flags(cleaned, &orig_flags);
+ orig_flags &= ~MS_RDONLY;
+
+ if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
+ return -errno;
+
+ x = strdup(cleaned);
+ if (!x)
+ return -ENOMEM;
+
+ r = set_consume(done, x);
+ if (r < 0)
+ return r;
+ }
+
+ while ((x = set_steal_first(todo))) {
+
+ r = set_consume(done, x);
+ if (r == -EEXIST || r == 0)
+ continue;
+ if (r < 0)
+ return r;
+
+ /* Try to reuse the original flag set, but
+ * don't care for errors, in case of
+ * obstructed mounts */
+ orig_flags = 0;
+ (void) get_mount_flags(x, &orig_flags);
+ orig_flags &= ~MS_RDONLY;
+
+ if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) {
+
+ /* Deal with mount points that are
+ * obstructed by a later mount */
+
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ }
+ }
+}
+
+int fflush_and_check(FILE *f) {
+ assert(f);
+
+ errno = 0;
+ fflush(f);
+
+ if (ferror(f))
+ return errno ? -errno : -EIO;
+
+ return 0;
+}
+
+int tempfn_xxxxxx(const char *p, char **ret) {
+ const char *fn;
+ char *t;
+
+ assert(p);
+ assert(ret);
+
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#waldoXXXXXX
+ */
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ t = new(char, strlen(p) + 2 + 6 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX");
+
+ *ret = path_kill_slashes(t);
+ return 0;
+}
+
+int tempfn_random(const char *p, char **ret) {
+ const char *fn;
+ char *t, *x;
+ uint64_t u;
+ unsigned i;
+
+ assert(p);
+ assert(ret);
+
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#waldobaa2a261115984a9
+ */
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ t = new(char, strlen(p) + 2 + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn);
+
+ u = random_u64();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
+ }
+
+ *x = 0;
+
+ *ret = path_kill_slashes(t);
+ return 0;
+}
+
+int tempfn_random_child(const char *p, char **ret) {
+ char *t, *x;
+ uint64_t u;
+ unsigned i;
+
+ assert(p);
+ assert(ret);
+
+ /* Turns this:
+ * /foo/bar/waldo
+ * Into this:
+ * /foo/bar/waldo/.#3c2b6219aa75d7d0
+ */
+
+ t = new(char, strlen(p) + 3 + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ x = stpcpy(stpcpy(t, p), "/.#");
+
+ u = random_u64();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
+ }
+
+ *x = 0;
+
+ *ret = path_kill_slashes(t);
+ return 0;
+}
+
+int take_password_lock(const char *root) {
+
+ struct flock flock = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0,
+ };
+
+ const char *path;
+ int fd, r;
+
+ /* This is roughly the same as lckpwdf(), but not as awful. We
+ * don't want to use alarm() and signals, hence we implement
+ * our own trivial version of this.
+ *
+ * Note that shadow-utils also takes per-database locks in
+ * addition to lckpwdf(). However, we don't given that they
+ * are redundant as they they invoke lckpwdf() first and keep
+ * it during everything they do. The per-database locks are
+ * awfully racy, and thus we just won't do them. */
+
+ if (root)
+ path = strjoina(root, "/etc/.pwd.lock");
+ else
+ path = "/etc/.pwd.lock";
+
+ fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+ if (fd < 0)
+ return -errno;
+
+ r = fcntl(fd, F_SETLKW, &flock);
+ if (r < 0) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
+int is_symlink(const char *path) {
+ struct stat info;
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!S_ISLNK(info.st_mode);
+}
+
+int is_dir(const char* path, bool follow) {
+ struct stat st;
+ int r;
+
+ if (follow)
+ r = stat(path, &st);
+ else
+ r = lstat(path, &st);
+ if (r < 0)
+ return -errno;
+
+ return !!S_ISDIR(st.st_mode);
+}
+
+int is_device_node(const char *path) {
+ struct stat info;
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+}
+
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
+ _cleanup_free_ char *s = NULL;
+ size_t allocated = 0, sz = 0;
+ int r;
+
+ enum {
+ START,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE,
+ SINGLE_QUOTE_ESCAPE,
+ DOUBLE_QUOTE,
+ DOUBLE_QUOTE_ESCAPE,
+ SPACE,
+ } state = START;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ /* Parses the first word of a string, and returns it in
+ * *ret. Removes all quotes in the process. When parsing fails
+ * (because of an uneven number of quotes or similar), leaves
+ * the pointer *p at the first invalid character. */
+
+ for (;;) {
+ char c = **p;
+
+ switch (state) {
+
+ case START:
+ if (c == 0)
+ goto finish;
+ else if (strchr(WHITESPACE, c))
+ break;
+
+ state = VALUE;
+ /* fallthrough */
+
+ case VALUE:
+ if (c == 0)
+ goto finish;
+ else if (c == '\'')
+ state = SINGLE_QUOTE;
+ else if (c == '\\')
+ state = VALUE_ESCAPE;
+ else if (c == '\"')
+ state = DOUBLE_QUOTE;
+ else if (strchr(WHITESPACE, c))
+ state = SPACE;
+ else {
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+ }
+
+ break;
+
+ case VALUE_ESCAPE:
+ if (c == 0) {
+ if (flags & UNQUOTE_RELAX)
+ goto finish;
+ return -EINVAL;
+ }
+
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
+ return -ENOMEM;
+
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c; /* normal explicit char */
+ else
+ sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
+ } else
+ s[sz++] = c;
+
+ state = VALUE;
+ break;
+
+ case SINGLE_QUOTE:
+ if (c == 0) {
+ if (flags & UNQUOTE_RELAX)
+ goto finish;
+ return -EINVAL;
+ } else if (c == '\'')
+ state = VALUE;
+ else if (c == '\\')
+ state = SINGLE_QUOTE_ESCAPE;
+ else {
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+ }
+
+ break;
+
+ case SINGLE_QUOTE_ESCAPE:
+ if (c == 0) {
+ if (flags & UNQUOTE_RELAX)
+ goto finish;
+ return -EINVAL;
+ }
+
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
+ return -ENOMEM;
+
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ } else
+ s[sz++] = c;
+
+ state = SINGLE_QUOTE;
+ break;
+
+ case DOUBLE_QUOTE:
+ if (c == 0)
+ return -EINVAL;
+ else if (c == '\"')
+ state = VALUE;
+ else if (c == '\\')
+ state = DOUBLE_QUOTE_ESCAPE;
+ else {
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+ }
+
+ break;
+
+ case DOUBLE_QUOTE_ESCAPE:
+ if (c == 0) {
+ if (flags & UNQUOTE_RELAX)
+ goto finish;
+ return -EINVAL;
+ }
+
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
+ return -ENOMEM;
+
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ } else
+ s[sz++] = c;
+
+ state = DOUBLE_QUOTE;
+ break;
+
+ case SPACE:
+ if (c == 0)
+ goto finish;
+ if (!strchr(WHITESPACE, c))
+ goto finish;
+
+ break;
+ }
+
+ (*p) ++;
+ }
+
+finish:
+ if (!s) {
+ *ret = NULL;
+ return 0;
+ }
+
+ s[sz] = 0;
+ *ret = s;
+ s = NULL;
+
+ return 1;
+}
+
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
+ va_list ap;
+ char **l;
+ int n = 0, i, c, r;
+
+ /* Parses a number of words from a string, stripping any
+ * quotes if necessary. */
+
+ assert(p);
+
+ /* Count how many words are expected */
+ va_start(ap, flags);
+ for (;;) {
+ if (!va_arg(ap, char **))
+ break;
+ n++;
+ }
+ va_end(ap);
+
+ if (n <= 0)
+ return 0;
+
+ /* Read all words into a temporary array */
+ l = newa0(char*, n);
+ for (c = 0; c < n; c++) {
+
+ r = unquote_first_word(p, &l[c], flags);
+ if (r < 0) {
+ int j;
+
+ for (j = 0; j < c; j++)
+ free(l[j]);
+
+ return r;
+ }
+
+ if (r == 0)
+ break;
+ }
+
+ /* If we managed to parse all words, return them in the passed
+ * in parameters */
+ va_start(ap, flags);
+ for (i = 0; i < n; i++) {
+ char **v;
+
+ v = va_arg(ap, char **);
+ assert(v);
+
+ *v = l[i];
+ }
+ va_end(ap);
+
+ return c;
+}
+
+int free_and_strdup(char **p, const char *s) {
+ char *t;
+
+ assert(p);
+
+ /* Replaces a string pointer with an strdup()ed new string,
+ * possibly freeing the old one. */
+
+ if (streq_ptr(*p, s))
+ return 0;
+
+ if (s) {
+ t = strdup(s);
+ if (!t)
+ return -ENOMEM;
+ } else
+ t = NULL;
+
+ free(*p);
+ *p = t;
+
+ return 1;
+}
+
+int ptsname_malloc(int fd, char **ret) {
+ size_t l = 100;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ char *c;
+
+ c = new(char, l);
+ if (!c)
+ return -ENOMEM;
+
+ if (ptsname_r(fd, c, l) == 0) {
+ *ret = c;
+ return 0;
+ }
+ if (errno != ERANGE) {
+ free(c);
+ return -errno;
+ }
+
+ free(c);
+ l *= 2;
+ }
+}
+
+int openpt_in_namespace(pid_t pid, int flags) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ siginfo_t si;
+ pid_t child;
+ int r;
+
+ assert(pid > 0);
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return -errno;
+
+ if (child == 0) {
+ int master;
+
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ master = posix_openpt(flags);
+ if (master < 0)
+ _exit(EXIT_FAILURE);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
+
+ mh.msg_controllen = cmsg->cmsg_len;
+
+ if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return r;
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return -EIO;
+
+ if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
+ return -errno;
+
+ CMSG_FOREACH(cmsg, &mh)
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int *fds;
+ unsigned n_fds;
+
+ fds = (int*) CMSG_DATA(cmsg);
+ n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ if (n_fds != 1) {
+ close_many(fds, n_fds);
+ return -EIO;
+ }
+
+ return fds[0];
+ }
+
+ return -EIO;
+}
+
+ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
+ _cleanup_close_ int fd = -1;
+ ssize_t l;
+
+ /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
+
+ fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
+ if (fd < 0)
+ return -errno;
+
+ l = fgetxattr(fd, attribute, value, size);
+ if (l < 0)
+ return -errno;
+
+ return l;
+}
+
+static int parse_crtime(le64_t le, usec_t *usec) {
+ uint64_t u;
+
+ assert(usec);
+
+ u = le64toh(le);
+ if (u == 0 || u == (uint64_t) -1)
+ return -EIO;
+
+ *usec = (usec_t) u;
+ return 0;
+}
+
+int fd_getcrtime(int fd, usec_t *usec) {
+ le64_t le;
+ ssize_t n;
+
+ assert(fd >= 0);
+ assert(usec);
+
+ /* Until Linux gets a real concept of birthtime/creation time,
+ * let's fake one with xattrs */
+
+ n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
+ le64_t le;
+ ssize_t n;
+
+ n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags);
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int path_getcrtime(const char *p, usec_t *usec) {
+ le64_t le;
+ ssize_t n;
+
+ assert(p);
+ assert(usec);
+
+ n = getxattr(p, "user.crtime_usec", &le, sizeof(le));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int fd_setcrtime(int fd, usec_t usec) {
+ le64_t le;
+
+ assert(fd >= 0);
+
+ if (usec <= 0)
+ usec = now(CLOCK_REALTIME);
+
+ le = htole64((uint64_t) usec);
+ if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int same_fd(int a, int b) {
+ struct stat sta, stb;
+ pid_t pid;
+ int r, fa, fb;
+
+ assert(a >= 0);
+ assert(b >= 0);
+
+ /* Compares two file descriptors. Note that semantics are
+ * quite different depending on whether we have kcmp() or we
+ * don't. If we have kcmp() this will only return true for
+ * dup()ed file descriptors, but not otherwise. If we don't
+ * have kcmp() this will also return true for two fds of the same
+ * file, created by separate open() calls. Since we use this
+ * call mostly for filtering out duplicates in the fd store
+ * this difference hopefully doesn't matter too much. */
+
+ if (a == b)
+ return true;
+
+ /* Try to use kcmp() if we have it. */
+ pid = getpid();
+ r = kcmp(pid, pid, KCMP_FILE, a, b);
+ if (r == 0)
+ return true;
+ if (r > 0)
+ return false;
+ if (errno != ENOSYS)
+ return -errno;
+
+ /* We don't have kcmp(), use fstat() instead. */
+ if (fstat(a, &sta) < 0)
+ return -errno;
+
+ if (fstat(b, &stb) < 0)
+ return -errno;
+
+ if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
+ return false;
+
+ /* We consider all device fds different, since two device fds
+ * might refer to quite different device contexts even though
+ * they share the same inode and backing dev_t. */
+
+ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
+ return false;
+
+ if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
+ return false;
+
+ /* The fds refer to the same inode on disk, let's also check
+ * if they have the same fd flags. This is useful to
+ * distinguish the read and write side of a pipe created with
+ * pipe(). */
+ fa = fcntl(a, F_GETFL);
+ if (fa < 0)
+ return -errno;
+
+ fb = fcntl(b, F_GETFL);
+ if (fb < 0)
+ return -errno;
+
+ return fa == fb;
+}
+
+int chattr_fd(int fd, unsigned value, unsigned mask) {
+ unsigned old_attr, new_attr;
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ /* Explicitly check whether this is a regular file or
+ * directory. If it is anything else (such as a device node or
+ * fifo), then the ioctl will not hit the file systems but
+ * possibly drivers, where the ioctl might have different
+ * effects. Notably, DRM is using the same ioctl() number. */
+
+ if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
+ return -ENOTTY;
+
+ if (mask == 0)
+ return 0;
+
+ if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
+ return -errno;
+
+ new_attr = (old_attr & ~mask) | (value & mask);
+ if (new_attr == old_attr)
+ return 0;
+
+ if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int chattr_path(const char *p, unsigned value, unsigned mask) {
+ _cleanup_close_ int fd = -1;
+
+ assert(p);
+
+ if (mask == 0)
+ return 0;
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return chattr_fd(fd, value, mask);
+}
+
+int read_attr_fd(int fd, unsigned *ret) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
+ return -ENOTTY;
+
+ if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int read_attr_path(const char *p, unsigned *ret) {
+ _cleanup_close_ int fd = -1;
+
+ assert(p);
+ assert(ret);
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return read_attr_fd(fd, ret);
+}
+
+static size_t nul_length(const uint8_t *p, size_t sz) {
+ size_t n = 0;
+
+ while (sz > 0) {
+ if (*p != 0)
+ break;
+
+ n++;
+ p++;
+ sz--;
+ }
+
+ return n;
+}
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
+ const uint8_t *q, *w, *e;
+ ssize_t l;
+
+ q = w = p;
+ e = q + sz;
+ while (q < e) {
+ size_t n;
+
+ n = nul_length(q, e - q);
+
+ /* If there are more than the specified run length of
+ * NUL bytes, or if this is the beginning or the end
+ * of the buffer, then seek instead of write */
+ if ((n > run_length) ||
+ (n > 0 && q == p) ||
+ (n > 0 && q + n >= e)) {
+ if (q > w) {
+ l = write(fd, w, q - w);
+ if (l < 0)
+ return -errno;
+ if (l != q -w)
+ return -EIO;
+ }
+
+ if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+ return -errno;
+
+ q += n;
+ w = q;
+ } else if (n > 0)
+ q += n;
+ else
+ q ++;
+ }
+
+ if (q > w) {
+ l = write(fd, w, q - w);
+ if (l < 0)
+ return -errno;
+ if (l != q - w)
+ return -EIO;
+ }
+
+ return q - (const uint8_t*) p;
+}
+
+void sigkill_wait(pid_t *pid) {
+ if (!pid)
+ return;
+ if (*pid <= 1)
+ return;
+
+ if (kill(*pid, SIGKILL) > 0)
+ (void) wait_for_terminate(*pid, NULL);
+}
+
+int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
+ int a = 0, b = 0, c = 0;
+ int k;
+
+ assert(p);
+ assert(*p);
+ assert(priority);
+
+ if ((*p)[0] != '<')
+ return 0;
+
+ if (!strchr(*p, '>'))
+ return 0;
+
+ if ((*p)[2] == '>') {
+ c = undecchar((*p)[1]);
+ k = 3;
+ } else if ((*p)[3] == '>') {
+ b = undecchar((*p)[1]);
+ c = undecchar((*p)[2]);
+ k = 4;
+ } else if ((*p)[4] == '>') {
+ a = undecchar((*p)[1]);
+ b = undecchar((*p)[2]);
+ c = undecchar((*p)[3]);
+ k = 5;
+ } else
+ return 0;
+
+ if (a < 0 || b < 0 || c < 0 ||
+ (!with_facility && (a || b || c > 7)))
+ return 0;
+
+ if (with_facility)
+ *priority = a*100 + b*10 + c;
+ else
+ *priority = (*priority & LOG_FACMASK) | c;
+
+ *p += k;
+ return 1;
+}
+
+ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
+ size_t i;
+
+ if (!key)
+ return -1;
+
+ for (i = 0; i < len; ++i)
+ if (streq_ptr(table[i], key))
+ return (ssize_t)i;
+
+ return -1;
+}
+
+void cmsg_close_all(struct msghdr *mh) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ CMSG_FOREACH(cmsg, mh)
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+ close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+}
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ struct stat buf;
+ int ret;
+
+ ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
+ if (ret >= 0)
+ return 0;
+
+ /* Even though renameat2() exists since Linux 3.15, btrfs added
+ * support for it later. If it is not implemented, fallback to another
+ * method. */
+ if (errno != EINVAL)
+ return -errno;
+
+ /* The link()/unlink() fallback does not work on directories. But
+ * renameat() without RENAME_NOREPLACE gives the same semantics on
+ * directories, except when newpath is an *empty* directory. This is
+ * good enough. */
+ ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
+ if (ret >= 0 && S_ISDIR(buf.st_mode)) {
+ ret = renameat(olddirfd, oldpath, newdirfd, newpath);
+ return ret >= 0 ? 0 : -errno;
+ }
+
+ /* If it is not a directory, use the link()/unlink() fallback. */
+ ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
+ if (ret < 0)
+ return -errno;
+
+ ret = unlinkat(olddirfd, oldpath, 0);
+ if (ret < 0) {
+ /* backup errno before the following unlinkat() alters it */
+ ret = errno;
+ (void) unlinkat(newdirfd, newpath, 0);
+ errno = ret;
+ return -errno;
+ }
+
+ return 0;
+}
+
+char *shell_maybe_quote(const char *s) {
+ const char *p;
+ char *r, *t;
+
+ assert(s);
+
+ /* Encloses a string in double quotes if necessary to make it
+ * OK as shell string. */
+
+ for (p = s; *p; p++)
+ if (*p <= ' ' ||
+ *p >= 127 ||
+ strchr(SHELL_NEED_QUOTES, *p))
+ break;
+
+ if (!*p)
+ return strdup(s);
+
+ r = new(char, 1+strlen(s)*2+1+1);
+ if (!r)
+ return NULL;
+
+ t = r;
+ *(t++) = '"';
+ t = mempcpy(t, s, p - s);
+
+ for (; *p; p++) {
+
+ if (strchr(SHELL_NEED_ESCAPE, *p))
+ *(t++) = '\\';
+
+ *(t++) = *p;
+ }
+
+ *(t++)= '"';
+ *t = 0;
+
+ return r;
+}
+
+int parse_mode(const char *s, mode_t *ret) {
+ char *x;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 8);
+ if (errno != 0)
+ return -errno;
+
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (l < 0 || l > 07777)
+ return -ERANGE;
+
+ *ret = (mode_t) l;
+ return 0;
+}
+
+int mount_move_root(const char *path) {
+ assert(path);
+
+ if (chdir(path) < 0)
+ return -errno;
+
+ if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+
+ if (chdir("/") < 0)
+ return -errno;
+
+ return 0;
+}
+
+int reset_uid_gid(void) {
+
+ if (setgroups(0, NULL) < 0)
+ return -errno;
+
+ if (setresgid(0, 0, 0) < 0)
+ return -errno;
+
+ if (setresuid(0, 0, 0) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/basic/util.h b/src/basic/util.h
new file mode 100644
index 0000000..467ae23
--- /dev/null
+++ b/src/basic/util.h
@@ -0,0 +1,903 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 <alloca.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <locale.h>
+#include <mntent.h>
+#include <sys/inotify.h>
+#include <sys/statfs.h>
+
+#include "macro.h"
+#include "missing.h"
+#include "time-util.h"
+#include "formats-util.h"
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n\r"
+#define NEWLINE "\n\r"
+#define QUOTES "\"\'"
+#define COMMENTS "#;"
+#define GLOB_CHARS "*?["
+
+/* What characters are special in the shell? */
+/* must be escaped outside and inside double-quotes */
+#define SHELL_NEED_ESCAPE "\"\\`$"
+/* can be escaped or double-quoted */
+#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;"
+
+#define FORMAT_BYTES_MAX 8
+
+size_t page_size(void) _pure_;
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+bool streq_ptr(const char *a, const char *b) _pure_;
+
+#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n)))
+
+#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+
+#define malloc0(n) (calloc((n), 1))
+
+static inline const char* yes_no(bool b) {
+ return b ? "yes" : "no";
+}
+
+static inline const char* true_false(bool b) {
+ return b ? "true" : "false";
+}
+
+static inline const char* one_zero(bool b) {
+ return b ? "1" : "0";
+}
+
+static inline const char* strempty(const char *s) {
+ return s ? s : "";
+}
+
+static inline const char* strnull(const char *s) {
+ return s ? s : "(null)";
+}
+
+static inline const char *strna(const char *s) {
+ return s ? s : "n/a";
+}
+
+static inline bool isempty(const char *p) {
+ return !p || !p[0];
+}
+
+static inline char *startswith(const char *s, const char *prefix) {
+ size_t l;
+
+ l = strlen(prefix);
+ if (strncmp(s, prefix, l) == 0)
+ return (char*) s + l;
+
+ return NULL;
+}
+
+static inline char *startswith_no_case(const char *s, const char *prefix) {
+ size_t l;
+
+ l = strlen(prefix);
+ if (strncasecmp(s, prefix, l) == 0)
+ return (char*) s + l;
+
+ return NULL;
+}
+
+char *endswith(const char *s, const char *postfix) _pure_;
+char *endswith_no_case(const char *s, const char *postfix) _pure_;
+
+char *first_word(const char *s, const char *word) _pure_;
+
+int close_nointr(int fd);
+int safe_close(int fd);
+void safe_close_pair(int p[]);
+
+void close_many(const int fds[], unsigned n_fd);
+
+int parse_size(const char *t, off_t base, off_t *size);
+
+int parse_boolean(const char *v) _pure_;
+int parse_pid(const char *s, pid_t* ret_pid);
+int parse_uid(const char *s, uid_t* ret_uid);
+#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
+
+int safe_atou(const char *s, unsigned *ret_u);
+int safe_atoi(const char *s, int *ret_i);
+
+int safe_atollu(const char *s, unsigned long long *ret_u);
+int safe_atolli(const char *s, long long int *ret_i);
+
+int safe_atod(const char *s, double *ret_d);
+
+int safe_atou8(const char *s, uint8_t *ret);
+
+#if LONG_MAX == INT_MAX
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned));
+ return safe_atou(s, (unsigned*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+ assert_cc(sizeof(long int) == sizeof(int));
+ return safe_atoi(s, (int*) ret_u);
+}
+#else
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+ assert_cc(sizeof(long int) == sizeof(long long int));
+ return safe_atolli(s, (long long int*) ret_u);
+}
+#endif
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+ assert_cc(sizeof(uint32_t) == sizeof(unsigned));
+ return safe_atou(s, (unsigned*) ret_u);
+}
+
+static inline int safe_atoi32(const char *s, int32_t *ret_i) {
+ assert_cc(sizeof(int32_t) == sizeof(int));
+ return safe_atoi(s, (int*) ret_i);
+}
+
+static inline int safe_atou64(const char *s, uint64_t *ret_u) {
+ assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long*) ret_u);
+}
+
+static inline int safe_atoi64(const char *s, int64_t *ret_i) {
+ assert_cc(sizeof(int64_t) == sizeof(long long int));
+ return safe_atolli(s, (long long int*) ret_i);
+}
+
+int safe_atou16(const char *s, uint16_t *ret);
+int safe_atoi16(const char *s, int16_t *ret);
+
+const char* split(const char **state, size_t *l, const char *separator, bool quoted);
+
+#define FOREACH_WORD(word, length, s, state) \
+ _FOREACH_WORD(word, length, s, WHITESPACE, false, state)
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ _FOREACH_WORD(word, length, s, separator, false, state)
+
+#define FOREACH_WORD_QUOTED(word, length, s, state) \
+ _FOREACH_WORD(word, length, s, WHITESPACE, true, state)
+
+#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
+ for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+int readlinkat_malloc(int fd, const char *p, char **ret);
+int readlink_malloc(const char *p, char **r);
+int readlink_value(const char *p, char **ret);
+int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, char **r);
+
+char *strstrip(char *s);
+char *delete_chars(char *s, const char *bad);
+char *truncate_nl(char *s);
+
+char *file_in_same_dir(const char *path, const char *filename);
+
+int rmdir_parents(const char *path, const char *stop);
+
+char hexchar(int x) _const_;
+int unhexchar(char c) _const_;
+char octchar(int x) _const_;
+int unoctchar(char c) _const_;
+char decchar(int x) _const_;
+int undecchar(char c) _const_;
+
+char *cescape(const char *s);
+size_t cescape_char(char c, char *buf);
+
+typedef enum UnescapeFlags {
+ UNESCAPE_RELAX = 1,
+} UnescapeFlags;
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
+
+char *xescape(const char *s, const char *bad);
+
+char *ascii_strlower(char *path);
+
+bool dirent_is_file(const struct dirent *de) _pure_;
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
+
+bool hidden_file(const char *filename) _pure_;
+
+bool chars_intersect(const char *a, const char *b) _pure_;
+
+/* For basic lookup tables with strictly enumerated entries */
+#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
+ scope const char *name##_to_string(type i) { \
+ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
+ return NULL; \
+ return name##_table[i]; \
+ }
+
+ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
+
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
+ scope inline type name##_from_string(const char *s) { \
+ return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+ }
+
+#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
+ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
+
+/* For string conversions where numbers are also acceptable */
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
+ int name##_to_string_alloc(type i, char **str) { \
+ char *s; \
+ int r; \
+ if (i < 0 || i > max) \
+ return -ERANGE; \
+ if (i < (type) ELEMENTSOF(name##_table)) { \
+ s = strdup(name##_table[i]); \
+ if (!s) \
+ return log_oom(); \
+ } else { \
+ r = asprintf(&s, "%i", i); \
+ if (r < 0) \
+ return log_oom(); \
+ } \
+ *str = s; \
+ return 0; \
+ } \
+ type name##_from_string(const char *s) { \
+ type i; \
+ unsigned u = 0; \
+ assert(s); \
+ for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
+ if (name##_table[i] && \
+ streq(name##_table[i], s)) \
+ return i; \
+ if (safe_atou(s, &u) >= 0 && u <= max) \
+ return (type) u; \
+ return (type) -1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+int fd_nonblock(int fd, bool nonblock);
+int fd_cloexec(int fd, bool cloexec);
+
+int close_all_fds(const int except[], unsigned n_except);
+
+bool fstype_is_network(const char *fstype);
+
+int flush_fd(int fd);
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
+int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+bool is_device_path(const char *path);
+
+int dir_is_empty(const char *path);
+char* dirname_malloc(const char *path);
+
+char* lookup_uid(uid_t uid);
+char* getlogname_malloc(void);
+char* getusername_malloc(void);
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
+
+bool is_temporary_fs(const struct statfs *s) _pure_;
+int fd_is_temporary_fs(int fd);
+
+int pipe_eof(int fd);
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus);
+
+#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
+
+int files_same(const char *filea, const char *fileb);
+
+int running_in_chroot(void);
+
+char *ellipsize(const char *s, size_t length, unsigned percent);
+ /* bytes columns */
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent);
+
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
+int touch(const char *path);
+
+noreturn void freeze(void);
+
+bool null_or_empty(struct stat *st) _pure_;
+int null_or_empty_path(const char *fn);
+int null_or_empty_fd(int fd);
+
+DIR *xopendirat(int dirfd, const char *name, int flags);
+
+char *fstab_node_to_udev_node(const char *p);
+
+void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
+
+bool nulstr_contains(const char*nulstr, const char *needle);
+
+bool plymouth_running(void);
+
+bool machine_name_is_valid(const char *s) _pure_;
+
+char* strshorten(char *s, size_t l);
+
+int symlink_idempotent(const char *from, const char *to);
+
+int symlink_atomic(const char *from, const char *to);
+int mknod_atomic(const char *path, mode_t mode, dev_t dev);
+int mkfifo_atomic(const char *path, mode_t mode);
+
+int fchmod_umask(int fd, mode_t mode);
+
+bool display_is_local(const char *display) _pure_;
+int socket_from_display(const char *display, char **path);
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
+int get_group_creds(const char **groupname, gid_t *gid);
+
+int in_gid(gid_t gid);
+int in_group(const char *name);
+
+char* uid_to_name(uid_t uid);
+char* gid_to_name(gid_t gid);
+
+int glob_exists(const char *path);
+int glob_extend(char ***strv, const char *path);
+
+int dirent_ensure_type(DIR *d, struct dirent *de);
+
+int get_files_in_directory(const char *path, char ***list);
+
+char *strjoin(const char *x, ...) _sentinel_;
+
+bool is_main_thread(void);
+
+static inline bool _pure_ in_charset(const char *s, const char* charset) {
+ assert(s);
+ assert(charset);
+ return s[strspn(s, charset)] == '\0';
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret);
+
+#define NULSTR_FOREACH(i, l) \
+ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
+
+#define NULSTR_FOREACH_PAIR(i, j, l) \
+ for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
+
+int ioprio_class_to_string_alloc(int i, char **s);
+int ioprio_class_from_string(const char *s);
+
+const char *sigchld_code_to_string(int i) _const_;
+int sigchld_code_from_string(const char *s) _pure_;
+
+int log_facility_unshifted_to_string_alloc(int i, char **s);
+int log_facility_unshifted_from_string(const char *s);
+
+int log_level_to_string_alloc(int i, char **s);
+int log_level_from_string(const char *s);
+
+int sched_policy_to_string_alloc(int i, char **s);
+int sched_policy_from_string(const char *s);
+
+const char *rlimit_to_string(int i) _const_;
+int rlimit_from_string(const char *s) _pure_;
+
+int ip_tos_to_string_alloc(int i, char **s);
+int ip_tos_from_string(const char *s);
+
+extern int saved_argc;
+extern char **saved_argv;
+
+bool kexec_loaded(void);
+
+int prot_from_flags(int flags) _const_;
+
+char *format_bytes(char *buf, size_t l, off_t t);
+
+int fd_wait_for_event(int fd, int event, usec_t timeout);
+
+void* memdup(const void *p, size_t l) _alloc_(2);
+
+int fd_inc_sndbuf(int fd, size_t n);
+int fd_inc_rcvbuf(int fd, size_t n);
+
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
+
+int setrlimit_closest(int resource, const struct rlimit *rlim);
+
+bool http_url_is_valid(const char *url) _pure_;
+bool documentation_url_is_valid(const char *url) _pure_;
+
+bool http_etag_is_valid(const char *etag);
+
+bool in_initrd(void);
+
+int get_home_dir(char **ret);
+int get_shell(char **_ret);
+
+static inline void freep(void *p) {
+ free(*(void**) p);
+}
+
+static inline void closep(int *fd) {
+ safe_close(*fd);
+}
+
+static inline void umaskp(mode_t *u) {
+ umask(*u);
+}
+
+static inline void close_pairp(int (*p)[2]) {
+ safe_close_pair(*p);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, fclose);
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
+DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
+
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_umask_ _cleanup_(umaskp)
+#define _cleanup_globfree_ _cleanup_(globfree)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+#define _cleanup_endmntent_ _cleanup_(endmntentp)
+#define _cleanup_close_pair_ _cleanup_(close_pairp)
+
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
+ if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+ return NULL;
+
+ return malloc(a * b);
+}
+
+_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
+ if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+ return NULL;
+
+ return realloc(p, a * b);
+}
+
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
+ if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+ return NULL;
+
+ return memdup(p, a * b);
+}
+
+bool filename_is_valid(const char *p) _pure_;
+bool path_is_safe(const char *p) _pure_;
+bool string_is_safe(const char *p) _pure_;
+bool string_has_cc(const char *p, const char *ok) _pure_;
+
+/**
+ * Check if a string contains any glob patterns.
+ */
+_pure_ static inline bool string_is_glob(const char *p) {
+ return !!strpbrk(p, GLOB_CHARS);
+}
+
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+ int (*compar) (const void *, const void *, void *),
+ void *arg);
+
+#define _(String) gettext (String)
+void init_gettext(void);
+bool is_locale_utf8(void);
+
+typedef enum DrawSpecialChar {
+ DRAW_TREE_VERTICAL,
+ DRAW_TREE_BRANCH,
+ DRAW_TREE_RIGHT,
+ DRAW_TREE_SPACE,
+ DRAW_TRIANGULAR_BULLET,
+ DRAW_BLACK_CIRCLE,
+ DRAW_ARROW,
+ DRAW_DASH,
+ _DRAW_SPECIAL_CHAR_MAX
+} DrawSpecialChar;
+
+const char *draw_special_char(DrawSpecialChar ch);
+
+char *strreplace(const char *text, const char *old_string, const char *new_string);
+
+char *strip_tab_ansi(char **p, size_t *l);
+
+int on_ac_power(void);
+
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
+
+#define FOREACH_LINE(line, f, on_error) \
+ for (;;) \
+ if (!fgets(line, sizeof(line), f)) { \
+ if (ferror(f)) { \
+ on_error; \
+ } \
+ break; \
+ } else
+
+#define FOREACH_DIRENT(de, d, on_error) \
+ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
+ if (!de) { \
+ if (errno > 0) { \
+ on_error; \
+ } \
+ break; \
+ } else if (hidden_file((de)->d_name)) \
+ continue; \
+ else
+
+#define FOREACH_DIRENT_ALL(de, d, on_error) \
+ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
+ if (!de) { \
+ if (errno > 0) { \
+ on_error; \
+ } \
+ break; \
+ } else
+
+static inline void *mempset(void *s, int c, size_t n) {
+ memset(s, c, n);
+ return (uint8_t*)s + n;
+}
+
+char *hexmem(const void *p, size_t l);
+void *unhexmem(const char *p, size_t l);
+
+char *strextend(char **x, ...) _sentinel_;
+char *strrep(const char *s, unsigned n);
+
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
+#define GREEDY_REALLOC(array, allocated, need) \
+ greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
+
+#define GREEDY_REALLOC0(array, allocated, need) \
+ greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
+
+static inline void _reset_errno_(int *saved_errno) {
+ errno = *saved_errno;
+}
+
+#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno
+
+static inline int negative_errno(void) {
+ /* This helper should be used to shut up gcc if you know 'errno' is
+ * negative. Instead of "return -errno;", use "return negative_errno();"
+ * It will suppress bogus gcc warnings in case it assumes 'errno' might
+ * be 0 and thus the caller's error-handling might not be triggered. */
+ assert_return(errno > 0, -EINVAL);
+ return -errno;
+}
+
+struct _umask_struct_ {
+ mode_t mask;
+ bool quit;
+};
+
+static inline void _reset_umask_(struct _umask_struct_ *s) {
+ umask(s->mask);
+};
+
+#define RUN_WITH_UMASK(mask) \
+ for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
+ !_saved_umask_.quit ; \
+ _saved_umask_.quit = true)
+
+static inline unsigned u64log2(uint64_t n) {
+#if __SIZEOF_LONG_LONG__ == 8
+ return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
+#else
+#error "Wut?"
+#endif
+}
+
+static inline unsigned u32ctz(uint32_t n) {
+#if __SIZEOF_INT__ == 4
+ return __builtin_ctz(n);
+#else
+#error "Wut?"
+#endif
+}
+
+static inline unsigned log2i(int x) {
+ assert(x > 0);
+
+ return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
+}
+
+static inline unsigned log2u(unsigned x) {
+ assert(x > 0);
+
+ return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
+}
+
+static inline unsigned log2u_round_up(unsigned x) {
+ assert(x > 0);
+
+ if (x == 1)
+ return 0;
+
+ return log2u(x - 1) + 1;
+}
+
+static inline bool logind_running(void) {
+ return access("/run/systemd/seats/", F_OK) >= 0;
+}
+
+#define DECIMAL_STR_WIDTH(x) \
+ ({ \
+ typeof(x) _x_ = (x); \
+ unsigned ans = 1; \
+ while (_x_ /= 10) \
+ ans++; \
+ ans; \
+ })
+
+int unlink_noerrno(const char *path);
+
+#define alloca0(n) \
+ ({ \
+ char *_new_; \
+ size_t _len_ = n; \
+ _new_ = alloca(_len_); \
+ (void *) memset(_new_, 0, _len_); \
+ })
+
+/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
+#define alloca_align(size, align) \
+ ({ \
+ void *_ptr_; \
+ size_t _mask_ = (align) - 1; \
+ _ptr_ = alloca((size) + _mask_); \
+ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
+ })
+
+#define alloca0_align(size, align) \
+ ({ \
+ void *_new_; \
+ size_t _size_ = (size); \
+ _new_ = alloca_align(_size_, (align)); \
+ (void*)memset(_new_, 0, _size_); \
+ })
+
+#define strjoina(a, ...) \
+ ({ \
+ const char *_appendees_[] = { a, __VA_ARGS__ }; \
+ char *_d_, *_p_; \
+ int _len_ = 0; \
+ unsigned _i_; \
+ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
+ _len_ += strlen(_appendees_[_i_]); \
+ _p_ = _d_ = alloca(_len_ + 1); \
+ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
+ _p_ = stpcpy(_p_, _appendees_[_i_]); \
+ *_p_ = 0; \
+ _d_; \
+ })
+
+bool id128_is_valid(const char *s) _pure_;
+
+int split_pair(const char *s, const char *sep, char **l, char **r);
+
+int shall_restore_state(void);
+
+/**
+ * Normal qsort requires base to be nonnull. Here were require
+ * that only if nmemb > 0.
+ */
+static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
+ if (nmemb <= 1)
+ return;
+
+ assert(base);
+ qsort(base, nmemb, size, compar);
+}
+
+/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
+static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+
+ if (needlelen <= 0)
+ return (void*) haystack;
+
+ if (haystacklen < needlelen)
+ return NULL;
+
+ assert(haystack);
+ assert(needle);
+
+ return memmem(haystack, haystacklen, needle, needlelen);
+}
+
+int proc_cmdline(char **ret);
+int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
+int get_proc_cmdline_key(const char *parameter, char **value);
+
+int container_get_leader(const char *machine, pid_t *pid);
+
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
+
+int getpeercred(int fd, struct ucred *ucred);
+int getpeersec(int fd, char **ret);
+
+int writev_safe(int fd, const struct iovec *w, int j);
+
+int mkostemp_safe(char *pattern, int flags);
+int open_tmpfile(const char *path, int flags);
+
+int fd_warn_permissions(const char *path, int fd);
+
+#ifndef PERSONALITY_INVALID
+/* personality(7) documents that 0xffffffffUL is used for querying the
+ * current personality, hence let's use that here as error
+ * indicator. */
+#define PERSONALITY_INVALID 0xffffffffLU
+#endif
+
+unsigned long personality_from_string(const char *p);
+const char *personality_to_string(unsigned long);
+
+uint64_t physical_memory(void);
+
+void hexdump(FILE *f, const void *p, size_t s);
+
+union file_handle_union {
+ struct file_handle handle;
+ char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
+};
+#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
+
+int update_reboot_param_file(const char *param);
+
+int umount_recursive(const char *target, int flags);
+
+int bind_remount_recursive(const char *prefix, bool ro);
+
+int fflush_and_check(FILE *f);
+
+int tempfn_xxxxxx(const char *p, char **ret);
+int tempfn_random(const char *p, char **ret);
+int tempfn_random_child(const char *p, char **ret);
+
+int take_password_lock(const char *root);
+
+int is_symlink(const char *path);
+int is_dir(const char *path, bool follow);
+int is_device_node(const char *path);
+
+typedef enum UnquoteFlags {
+ UNQUOTE_RELAX = 1,
+ UNQUOTE_CUNESCAPE = 2,
+} UnquoteFlags;
+
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
+
+int free_and_strdup(char **p, const char *s);
+
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
+
+#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
+ for ((e) = &buffer.ev; \
+ (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
+ (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
+
+union inotify_event_buffer {
+ struct inotify_event ev;
+ uint8_t raw[INOTIFY_EVENT_MAX];
+};
+
+#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
+
+int ptsname_malloc(int fd, char **ret);
+
+int openpt_in_namespace(pid_t pid, int flags);
+
+ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags);
+
+int fd_setcrtime(int fd, usec_t usec);
+int fd_getcrtime(int fd, usec_t *usec);
+int path_getcrtime(const char *p, usec_t *usec);
+int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags);
+
+int same_fd(int a, int b);
+
+int chattr_fd(int fd, unsigned value, unsigned mask);
+int chattr_path(const char *p, unsigned value, unsigned mask);
+
+int read_attr_fd(int fd, unsigned *ret);
+int read_attr_path(const char *p, unsigned *ret);
+
+#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
+
+void sigkill_wait(pid_t *pid);
+#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
+
+int syslog_parse_priority(const char **p, int *priority, bool with_facility);
+
+void cmsg_close_all(struct msghdr *mh);
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+
+char *shell_maybe_quote(const char *s);
+
+int parse_mode(const char *s, mode_t *ret);
+
+int mount_move_root(const char *path);
+
+int reset_uid_gid(void);
diff --git a/src/basic/verbs.c b/src/basic/verbs.c
new file mode 100644
index 0000000..c7beccc
--- /dev/null
+++ b/src/basic/verbs.c
@@ -0,0 +1,90 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 "util.h"
+#include "verbs.h"
+
+int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
+ const Verb *verb;
+ const char *name;
+ unsigned i;
+ int left;
+
+ assert(verbs);
+ assert(verbs[0].dispatch);
+ assert(argc >= 0);
+ assert(argv);
+ assert(argc >= optind);
+
+ left = argc - optind;
+ name = argv[optind];
+
+ for (i = 0;; i++) {
+ bool found;
+
+ /* At the end of the list? */
+ if (!verbs[i].dispatch) {
+ if (name)
+ log_error("Unknown operation %s.", name);
+ else
+ log_error("Requires operation parameter.");
+ return -EINVAL;
+ }
+
+ if (name)
+ found = streq(name, verbs[i].verb);
+ else
+ found = !!(verbs[i].flags & VERB_DEFAULT);
+
+ if (found) {
+ verb = &verbs[i];
+ break;
+ }
+ }
+
+ assert(verb);
+
+ if (!name)
+ left = 1;
+
+ if (verb->min_args != VERB_ANY &&
+ (unsigned) left < verb->min_args) {
+ log_error("Too few arguments.");
+ return -EINVAL;
+ }
+
+ if (verb->max_args != VERB_ANY &&
+ (unsigned) left > verb->max_args) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ if (name)
+ return verb->dispatch(left, argv + optind, userdata);
+ else {
+ char* fake[2] = {
+ (char*) verb->verb,
+ NULL
+ };
+
+ return verb->dispatch(1, fake, userdata);
+ }
+}
diff --git a/src/basic/verbs.h b/src/basic/verbs.h
new file mode 100644
index 0000000..d59e4d5
--- /dev/null
+++ b/src/basic/verbs.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+***/
+
+#define VERB_ANY ((unsigned) -1)
+#define VERB_DEFAULT 1
+
+typedef struct {
+ const char *verb;
+ unsigned min_args, max_args;
+ unsigned flags;
+ int (* const dispatch)(int argc, char *argv[], void *userdata);
+} Verb;
+
+int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
diff --git a/src/basic/virt.c b/src/basic/virt.c
new file mode 100644
index 0000000..1299a75
--- /dev/null
+++ b/src/basic/virt.c
@@ -0,0 +1,406 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "process-util.h"
+#include "virt.h"
+#include "fileio.h"
+
+static int detect_vm_cpuid(const char **_id) {
+
+ /* Both CPUID and DMI are x86 specific interfaces... */
+#if defined(__i386__) || defined(__x86_64__)
+
+ static const char cpuid_vendor_table[] =
+ "XenVMMXenVMM\0" "xen\0"
+ "KVMKVMKVM\0" "kvm\0"
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ "VMwareVMware\0" "vmware\0"
+ /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
+ "Microsoft Hv\0" "microsoft\0";
+
+ uint32_t eax, ecx;
+ union {
+ uint32_t sig32[3];
+ char text[13];
+ } sig = {};
+ const char *j, *k;
+ bool hypervisor;
+
+ /* http://lwn.net/Articles/301888/ */
+
+#if defined (__i386__)
+#define REG_a "eax"
+#define REG_b "ebx"
+#elif defined (__amd64__)
+#define REG_a "rax"
+#define REG_b "rbx"
+#endif
+
+ /* First detect whether there is a hypervisor */
+ eax = 1;
+ __asm__ __volatile__ (
+ /* ebx/rbx is being used for PIC! */
+ " push %%"REG_b" \n\t"
+ " cpuid \n\t"
+ " pop %%"REG_b" \n\t"
+
+ : "=a" (eax), "=c" (ecx)
+ : "0" (eax)
+ );
+
+ hypervisor = !!(ecx & 0x80000000U);
+
+ if (hypervisor) {
+
+ /* There is a hypervisor, see what it is */
+ eax = 0x40000000U;
+ __asm__ __volatile__ (
+ /* ebx/rbx is being used for PIC! */
+ " push %%"REG_b" \n\t"
+ " cpuid \n\t"
+ " mov %%ebx, %1 \n\t"
+ " pop %%"REG_b" \n\t"
+
+ : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+ : "0" (eax)
+ );
+
+ NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
+ if (streq(sig.text, j)) {
+ *_id = k;
+ return 1;
+ }
+
+ *_id = "other";
+ return 0;
+ }
+#endif
+
+ return 0;
+}
+
+static int detect_vm_devicetree(const char **_id) {
+#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
+ _cleanup_free_ char *hvtype = NULL;
+ int r;
+
+ r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
+ if (r >= 0) {
+ if (streq(hvtype, "linux,kvm")) {
+ *_id = "kvm";
+ return 1;
+ } else if (strstr(hvtype, "xen")) {
+ *_id = "xen";
+ return 1;
+ }
+ } else if (r == -ENOENT) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *dent;
+
+ dir = opendir("/proc/device-tree");
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ FOREACH_DIRENT(dent, dir, return -errno) {
+ if (strstr(dent->d_name, "fw-cfg")) {
+ *_id = "qemu";
+ return 1;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+static int detect_vm_dmi(const char **_id) {
+
+ /* Both CPUID and DMI are x86 specific interfaces... */
+#if defined(__i386__) || defined(__x86_64__)
+
+ static const char *const dmi_vendors[] = {
+ "/sys/class/dmi/id/sys_vendor",
+ "/sys/class/dmi/id/board_vendor",
+ "/sys/class/dmi/id/bios_vendor"
+ };
+
+ static const char dmi_vendor_table[] =
+ "QEMU\0" "qemu\0"
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ "VMware\0" "vmware\0"
+ "VMW\0" "vmware\0"
+ "innotek GmbH\0" "oracle\0"
+ "Xen\0" "xen\0"
+ "Bochs\0" "bochs\0";
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
+ _cleanup_free_ char *s = NULL;
+ const char *j, *k;
+ int r;
+
+ r = read_one_line_file(dmi_vendors[i], &s);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
+
+ continue;
+ }
+
+ NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
+ if (startswith(s, j)) {
+ *_id = k;
+ return 1;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/* Returns a short identifier for the various VM implementations */
+int detect_vm(const char **id) {
+ _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
+ static thread_local int cached_found = -1;
+ static thread_local const char *cached_id = NULL;
+ const char *_id = NULL;
+ int r;
+
+ if (_likely_(cached_found >= 0)) {
+
+ if (id)
+ *id = cached_id;
+
+ return cached_found;
+ }
+
+ /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
+ *
+ * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
+ r = read_one_line_file("/proc/xen/capabilities", &domcap);
+ if (r >= 0) {
+ char *cap, *i = domcap;
+
+ while ((cap = strsep(&i, ",")))
+ if (streq(cap, "control_d"))
+ break;
+
+ if (!cap) {
+ _id = "xen";
+ r = 1;
+ }
+
+ goto finish;
+
+ } else if (r == -ENOENT) {
+ _cleanup_free_ char *hvtype = NULL;
+
+ r = read_one_line_file("/sys/hypervisor/type", &hvtype);
+ if (r >= 0) {
+ if (streq(hvtype, "xen")) {
+ _id = "xen";
+ r = 1;
+ goto finish;
+ }
+ } else if (r != -ENOENT)
+ return r;
+ } else
+ return r;
+
+ /* this will set _id to "other" and return 0 for unknown hypervisors */
+ r = detect_vm_cpuid(&_id);
+ if (r != 0)
+ goto finish;
+
+ r = detect_vm_dmi(&_id);
+ if (r != 0)
+ goto finish;
+
+ r = detect_vm_devicetree(&_id);
+ if (r != 0)
+ goto finish;
+
+ if (_id) {
+ /* "other" */
+ r = 1;
+ goto finish;
+ }
+
+ /* Detect User-Mode Linux by reading /proc/cpuinfo */
+ r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
+ if (r < 0)
+ return r;
+ if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
+ _id = "uml";
+ r = 1;
+ goto finish;
+ }
+
+#if defined(__s390__)
+ {
+ _cleanup_free_ char *t = NULL;
+
+ r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
+ if (r >= 0) {
+ if (streq(t, "z/VM"))
+ _id = "zvm";
+ else
+ _id = "kvm";
+ r = 1;
+
+ goto finish;
+ }
+ }
+#endif
+
+ r = 0;
+
+finish:
+ cached_found = r;
+
+ cached_id = _id;
+ if (id)
+ *id = _id;
+
+ return r;
+}
+
+int detect_container(const char **id) {
+
+ static thread_local int cached_found = -1;
+ static thread_local const char *cached_id = NULL;
+
+ _cleanup_free_ char *m = NULL;
+ const char *_id = NULL, *e = NULL;
+ int r;
+
+ if (_likely_(cached_found >= 0)) {
+
+ if (id)
+ *id = cached_id;
+
+ return cached_found;
+ }
+
+ /* /proc/vz exists in container and outside of the container,
+ * /proc/bc only outside of the container. */
+ if (access("/proc/vz", F_OK) >= 0 &&
+ access("/proc/bc", F_OK) < 0) {
+ _id = "openvz";
+ r = 1;
+ goto finish;
+ }
+
+ if (getpid() == 1) {
+ /* If we are PID 1 we can just check our own
+ * environment variable */
+
+ e = getenv("container");
+ if (isempty(e)) {
+ r = 0;
+ goto finish;
+ }
+ } else {
+
+ /* Otherwise, PID 1 dropped this information into a
+ * file in /run. This is better than accessing
+ * /proc/1/environ, since we don't need CAP_SYS_PTRACE
+ * for that. */
+
+ r = read_one_line_file("/run/systemd/container", &m);
+ if (r == -ENOENT) {
+
+ /* Fallback for cases where PID 1 was not
+ * systemd (for example, cases where
+ * init=/bin/sh is used. */
+
+ r = getenv_for_pid(1, "container", &m);
+ if (r <= 0) {
+
+ /* If that didn't work, give up,
+ * assume no container manager.
+ *
+ * Note: This means we still cannot
+ * detect containers if init=/bin/sh
+ * is passed but privileges dropped,
+ * as /proc/1/environ is only readable
+ * with privileges. */
+
+ r = 0;
+ goto finish;
+ }
+ }
+ if (r < 0)
+ return r;
+
+ e = m;
+ }
+
+ /* We only recognize a selected few here, since we want to
+ * enforce a redacted namespace */
+ if (streq(e, "lxc"))
+ _id ="lxc";
+ else if (streq(e, "lxc-libvirt"))
+ _id = "lxc-libvirt";
+ else if (streq(e, "systemd-nspawn"))
+ _id = "systemd-nspawn";
+ else if (streq(e, "docker"))
+ _id = "docker";
+ else
+ _id = "other";
+
+ r = 1;
+
+finish:
+ cached_found = r;
+
+ cached_id = _id;
+ if (id)
+ *id = _id;
+
+ return r;
+}
+
+/* Returns a short identifier for the various VM/container implementations */
+int detect_virtualization(const char **id) {
+ int r;
+
+ r = detect_container(id);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return VIRTUALIZATION_CONTAINER;
+
+ r = detect_vm(id);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return VIRTUALIZATION_VM;
+
+ return VIRTUALIZATION_NONE;
+}
diff --git a/src/basic/virt.h b/src/basic/virt.h
new file mode 100644
index 0000000..7194ab2
--- /dev/null
+++ b/src/basic/virt.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 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/>.
+***/
+
+int detect_vm(const char **id);
+int detect_container(const char **id);
+
+enum {
+ VIRTUALIZATION_NONE = 0,
+ VIRTUALIZATION_VM,
+ VIRTUALIZATION_CONTAINER,
+ _VIRTUALIZATION_MAX,
+ _VIRTUALIZATION_INVALID = -1
+};
+
+int detect_virtualization(const char **id);
diff --git a/src/basic/xml.c b/src/basic/xml.c
new file mode 100644
index 0000000..15c629b
--- /dev/null
+++ b/src/basic/xml.c
@@ -0,0 +1,254 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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 <string.h>
+
+#include "util.h"
+#include "xml.h"
+
+enum {
+ STATE_NULL,
+ STATE_TEXT,
+ STATE_TAG,
+ STATE_ATTRIBUTE,
+};
+
+static void inc_lines(unsigned *line, const char *s, size_t n) {
+ const char *p = s;
+
+ if (!line)
+ return;
+
+ for (;;) {
+ const char *f;
+
+ f = memchr(p, '\n', n);
+ if (!f)
+ return;
+
+ n -= (f - p) + 1;
+ p = f + 1;
+ (*line)++;
+ }
+}
+
+/* We don't actually do real XML here. We only read a simplistic
+ * subset, that is a bit less strict that XML and lacks all the more
+ * complex features, like entities, or namespaces. However, we do
+ * support some HTML5-like simplifications */
+
+int xml_tokenize(const char **p, char **name, void **state, unsigned *line) {
+ const char *c, *e, *b;
+ char *ret;
+ int t;
+
+ assert(p);
+ assert(*p);
+ assert(name);
+ assert(state);
+
+ t = PTR_TO_INT(*state);
+ c = *p;
+
+ if (t == STATE_NULL) {
+ if (line)
+ *line = 1;
+ t = STATE_TEXT;
+ }
+
+ for (;;) {
+ if (*c == 0)
+ return XML_END;
+
+ switch (t) {
+
+ case STATE_TEXT: {
+ int x;
+
+ e = strchrnul(c, '<');
+ if (e > c) {
+ /* More text... */
+ ret = strndup(c, e - c);
+ if (!ret)
+ return -ENOMEM;
+
+ inc_lines(line, c, e - c);
+
+ *name = ret;
+ *p = e;
+ *state = INT_TO_PTR(STATE_TEXT);
+
+ return XML_TEXT;
+ }
+
+ assert(*e == '<');
+ b = c + 1;
+
+ if (startswith(b, "!--")) {
+ /* A comment */
+ e = strstr(b + 3, "-->");
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, b, e + 3 - b);
+
+ c = e + 3;
+ continue;
+ }
+
+ if (*b == '?') {
+ /* Processing instruction */
+
+ e = strstr(b + 1, "?>");
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, b, e + 2 - b);
+
+ c = e + 2;
+ continue;
+ }
+
+ if (*b == '!') {
+ /* DTD */
+
+ e = strchr(b + 1, '>');
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, b, e + 1 - b);
+
+ c = e + 1;
+ continue;
+ }
+
+ if (*b == '/') {
+ /* A closing tag */
+ x = XML_TAG_CLOSE;
+ b++;
+ } else
+ x = XML_TAG_OPEN;
+
+ e = strpbrk(b, WHITESPACE "/>");
+ if (!e)
+ return -EINVAL;
+
+ ret = strndup(b, e - b);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = e;
+ *state = INT_TO_PTR(STATE_TAG);
+
+ return x;
+ }
+
+ case STATE_TAG:
+
+ b = c + strspn(c, WHITESPACE);
+ if (*b == 0)
+ return -EINVAL;
+
+ inc_lines(line, c, b - c);
+
+ e = b + strcspn(b, WHITESPACE "=/>");
+ if (e > b) {
+ /* An attribute */
+
+ ret = strndup(b, e - b);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = e;
+ *state = INT_TO_PTR(STATE_ATTRIBUTE);
+
+ return XML_ATTRIBUTE_NAME;
+ }
+
+ if (startswith(b, "/>")) {
+ /* An empty tag */
+
+ *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */
+ *p = b + 2;
+ *state = INT_TO_PTR(STATE_TEXT);
+
+ return XML_TAG_CLOSE_EMPTY;
+ }
+
+ if (*b != '>')
+ return -EINVAL;
+
+ c = b + 1;
+ t = STATE_TEXT;
+ continue;
+
+ case STATE_ATTRIBUTE:
+
+ if (*c == '=') {
+ c++;
+
+ if (*c == '\'' || *c == '\"') {
+ /* Tag with a quoted value */
+
+ e = strchr(c+1, *c);
+ if (!e)
+ return -EINVAL;
+
+ inc_lines(line, c, e - c);
+
+ ret = strndup(c+1, e - c - 1);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = e + 1;
+ *state = INT_TO_PTR(STATE_TAG);
+
+ return XML_ATTRIBUTE_VALUE;
+
+ }
+
+ /* Tag with a value without quotes */
+
+ b = strpbrk(c, WHITESPACE ">");
+ if (!b)
+ b = c;
+
+ ret = strndup(c, b - c);
+ if (!ret)
+ return -ENOMEM;
+
+ *name = ret;
+ *p = b;
+ *state = INT_TO_PTR(STATE_TAG);
+ return XML_ATTRIBUTE_VALUE;
+ }
+
+ t = STATE_TAG;
+ continue;
+ }
+
+ }
+
+ assert_not_reached("Bad state");
+}
diff --git a/src/basic/xml.h b/src/basic/xml.h
new file mode 100644
index 0000000..b256b0b
--- /dev/null
+++ b/src/basic/xml.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/>.
+***/
+
+enum {
+ XML_END,
+ XML_TEXT,
+ XML_TAG_OPEN,
+ XML_TAG_CLOSE,
+ XML_TAG_CLOSE_EMPTY,
+ XML_ATTRIBUTE_NAME,
+ XML_ATTRIBUTE_VALUE,
+};
+
+int xml_tokenize(const char **p, char **name, void **state, unsigned *line);
diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index 4cee95f..30c1a72 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -30,7 +30,7 @@
#include <unistd.h>
/* Yuck! DRM headers need system headers included first.. but we have to
- * include it before shared/missing.h to avoid redefining ioctl bits */
+ * include it before util/missing.h to avoid redefining ioctl bits */
#include <drm.h>
#include <drm_fourcc.h>
#include <drm_mode.h>
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index c386e65..71f0bd3 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -25,7 +25,7 @@
#include "sd-bus.h"
#include "sd-event.h"
#include "hashmap.h"
-#include "login-shared.h"
+#include "login-util.h"
#include "macro.h"
#include "util.h"
#include "grdev.h"
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
index b187934..b92a393 100644
--- a/src/libsystemd-terminal/idev.c
+++ b/src/libsystemd-terminal/idev.c
@@ -25,7 +25,7 @@
#include "sd-bus.h"
#include "sd-event.h"
#include "hashmap.h"
-#include "login-shared.h"
+#include "login-util.h"
#include "macro.h"
#include "util.h"
#include "idev.h"
diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c
index 37793e4..fea796c 100644
--- a/src/libsystemd/sd-bus/bus-internal.c
+++ b/src/libsystemd/sd-bus/bus-internal.c
@@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "bus-message.h"
#include "bus-internal.h"
bool object_path_is_valid(const char *p) {
@@ -345,3 +346,28 @@ char *bus_address_escape(const char *v) {
*b = 0;
return r;
}
+
+int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
+ assert(m);
+
+ if (r < 0) {
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
+ sd_bus_reply_method_errno(m, r, error);
+
+ } else if (sd_bus_error_is_set(error)) {
+ if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
+ sd_bus_reply_method_error(m, error);
+ } else
+ return r;
+
+ log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
+ bus_message_type_to_string(m->header->type),
+ strna(m->sender),
+ strna(m->path),
+ strna(m->interface),
+ strna(m->member),
+ strna(m->root_container.signature),
+ bus_error_message(error, r));
+
+ return 1;
+}
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 2ee0eab..88c0588 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -390,3 +390,5 @@ int bus_set_address_system_machine(sd_bus *b, const char *machine);
int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
int bus_get_root_path(sd_bus *bus);
+
+int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
deleted file mode 100644
index 9993779..0000000
--- a/src/libsystemd/sd-bus/bus-util.c
+++ /dev/null
@@ -1,2085 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/socket.h>
-
-#include "sd-daemon.h"
-#include "sd-event.h"
-#include "util.h"
-#include "strv.h"
-#include "macro.h"
-#include "def.h"
-#include "path-util.h"
-#include "missing.h"
-#include "set.h"
-#include "signal-util.h"
-#include "unit-name.h"
-
-#include "sd-bus.h"
-#include "bus-error.h"
-#include "bus-label.h"
-#include "bus-message.h"
-#include "bus-util.h"
-#include "bus-internal.h"
-
-static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- sd_event *e = userdata;
-
- assert(m);
- assert(e);
-
- sd_bus_close(sd_bus_message_get_bus(m));
- sd_event_exit(e, 0);
-
- return 1;
-}
-
-int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
- _cleanup_free_ char *match = NULL;
- const char *unique;
- int r;
-
- assert(e);
- assert(bus);
- assert(name);
-
- /* We unregister the name here and then wait for the
- * NameOwnerChanged signal for this event to arrive before we
- * quit. We do this in order to make sure that any queued
- * requests are still processed before we really exit. */
-
- r = sd_bus_get_unique_name(bus, &unique);
- if (r < 0)
- return r;
-
- r = asprintf(&match,
- "sender='org.freedesktop.DBus',"
- "type='signal',"
- "interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged',"
- "path='/org/freedesktop/DBus',"
- "arg0='%s',"
- "arg1='%s',"
- "arg2=''", name, unique);
- if (r < 0)
- return -ENOMEM;
-
- r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
- if (r < 0)
- return r;
-
- r = sd_bus_release_name(bus, name);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int bus_event_loop_with_idle(
- sd_event *e,
- sd_bus *bus,
- const char *name,
- usec_t timeout,
- check_idle_t check_idle,
- void *userdata) {
- bool exiting = false;
- int r, code;
-
- assert(e);
- assert(bus);
- assert(name);
-
- for (;;) {
- bool idle;
-
- r = sd_event_get_state(e);
- if (r < 0)
- return r;
- if (r == SD_EVENT_FINISHED)
- break;
-
- if (check_idle)
- idle = check_idle(userdata);
- else
- idle = true;
-
- r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
- if (r < 0)
- return r;
-
- if (r == 0 && !exiting && idle) {
-
- r = sd_bus_try_close(bus);
- if (r == -EBUSY)
- continue;
-
- /* Fallback for dbus1 connections: we
- * unregister the name and wait for the
- * response to come through for it */
- if (r == -EOPNOTSUPP) {
-
- /* Inform the service manager that we
- * are going down, so that it will
- * queue all further start requests,
- * instead of assuming we are already
- * running. */
- sd_notify(false, "STOPPING=1");
-
- r = bus_async_unregister_and_exit(e, bus, name);
- if (r < 0)
- return r;
-
- exiting = true;
- continue;
- }
-
- if (r < 0)
- return r;
-
- sd_event_exit(e, 0);
- break;
- }
- }
-
- r = sd_event_get_exit_code(e, &code);
- if (r < 0)
- return r;
-
- return code;
-}
-
-int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
- int r, has_owner = 0;
-
- assert(c);
- assert(name);
-
- r = sd_bus_call_method(c,
- "org.freedesktop.DBus",
- "/org/freedesktop/dbus",
- "org.freedesktop.DBus",
- "NameHasOwner",
- error,
- &rep,
- "s",
- name);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_basic(rep, 'b', &has_owner);
- if (r < 0)
- return sd_bus_error_set_errno(error, r);
-
- return has_owner;
-}
-
-static int check_good_user(sd_bus_message *m, uid_t good_user) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- uid_t sender_uid;
- int r;
-
- assert(m);
-
- if (good_user == UID_INVALID)
- return 0;
-
- r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
- if (r < 0)
- return r;
-
- /* Don't trust augmented credentials for authorization */
- assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
-
- r = sd_bus_creds_get_euid(creds, &sender_uid);
- if (r < 0)
- return r;
-
- return sender_uid == good_user;
-}
-
-int bus_test_polkit(
- sd_bus_message *call,
- int capability,
- const char *action,
- uid_t good_user,
- bool *_challenge,
- sd_bus_error *e) {
-
- int r;
-
- assert(call);
- assert(action);
-
- /* Tests non-interactively! */
-
- r = check_good_user(call, good_user);
- if (r != 0)
- return r;
-
- r = sd_bus_query_sender_privilege(call, capability);
- if (r < 0)
- return r;
- else if (r > 0)
- return 1;
-#ifdef ENABLE_POLKIT
- else {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- int authorized = false, challenge = false;
- const char *sender;
-
- sender = sd_bus_message_get_sender(call);
- if (!sender)
- return -EBADMSG;
-
- r = sd_bus_call_method(
- call->bus,
- "org.freedesktop.PolicyKit1",
- "/org/freedesktop/PolicyKit1/Authority",
- "org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization",
- e,
- &reply,
- "(sa{sv})sa{ss}us",
- "system-bus-name", 1, "name", "s", sender,
- action,
- 0,
- 0,
- "");
-
- if (r < 0) {
- /* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
- sd_bus_error_free(e);
- return -EACCES;
- }
-
- return r;
- }
-
- r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
- if (r < 0)
- return r;
-
- if (authorized)
- return 1;
-
- if (_challenge) {
- *_challenge = challenge;
- return 0;
- }
- }
-#endif
-
- return -EACCES;
-}
-
-#ifdef ENABLE_POLKIT
-
-typedef struct AsyncPolkitQuery {
- sd_bus_message *request, *reply;
- sd_bus_message_handler_t callback;
- void *userdata;
- sd_bus_slot *slot;
- Hashmap *registry;
-} AsyncPolkitQuery;
-
-static void async_polkit_query_free(AsyncPolkitQuery *q) {
-
- if (!q)
- return;
-
- sd_bus_slot_unref(q->slot);
-
- if (q->registry && q->request)
- hashmap_remove(q->registry, q->request);
-
- sd_bus_message_unref(q->request);
- sd_bus_message_unref(q->reply);
-
- free(q);
-}
-
-static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- AsyncPolkitQuery *q = userdata;
- int r;
-
- assert(reply);
- assert(q);
-
- q->slot = sd_bus_slot_unref(q->slot);
- q->reply = sd_bus_message_ref(reply);
-
- r = sd_bus_message_rewind(q->request, true);
- if (r < 0) {
- r = sd_bus_reply_method_errno(q->request, r, NULL);
- goto finish;
- }
-
- r = q->callback(q->request, q->userdata, &error_buffer);
- r = bus_maybe_reply_error(q->request, r, &error_buffer);
-
-finish:
- async_polkit_query_free(q);
-
- return r;
-}
-
-#endif
-
-int bus_verify_polkit_async(
- sd_bus_message *call,
- int capability,
- const char *action,
- bool interactive,
- uid_t good_user,
- Hashmap **registry,
- sd_bus_error *error) {
-
-#ifdef ENABLE_POLKIT
- _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
- AsyncPolkitQuery *q;
- const char *sender;
- sd_bus_message_handler_t callback;
- void *userdata;
- int c;
-#endif
- int r;
-
- assert(call);
- assert(action);
- assert(registry);
-
- r = check_good_user(call, good_user);
- if (r != 0)
- return r;
-
-#ifdef ENABLE_POLKIT
- q = hashmap_get(*registry, call);
- if (q) {
- int authorized, challenge;
-
- /* This is the second invocation of this function, and
- * there's already a response from polkit, let's
- * process it */
- assert(q->reply);
-
- if (sd_bus_message_is_method_error(q->reply, NULL)) {
- const sd_bus_error *e;
-
- /* Copy error from polkit reply */
- e = sd_bus_message_get_error(q->reply);
- sd_bus_error_copy(error, e);
-
- /* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
- return -EACCES;
-
- return -sd_bus_error_get_errno(e);
- }
-
- r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
- if (r >= 0)
- r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
-
- if (r < 0)
- return r;
-
- if (authorized)
- return 1;
-
- if (challenge)
- return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
-
- return -EACCES;
- }
-#endif
-
- r = sd_bus_query_sender_privilege(call, capability);
- if (r < 0)
- return r;
- else if (r > 0)
- return 1;
-
-#ifdef ENABLE_POLKIT
- if (sd_bus_get_current_message(call->bus) != call)
- return -EINVAL;
-
- callback = sd_bus_get_current_handler(call->bus);
- if (!callback)
- return -EINVAL;
-
- userdata = sd_bus_get_current_userdata(call->bus);
-
- sender = sd_bus_message_get_sender(call);
- if (!sender)
- return -EBADMSG;
-
- c = sd_bus_message_get_allow_interactive_authorization(call);
- if (c < 0)
- return c;
- if (c > 0)
- interactive = true;
-
- r = hashmap_ensure_allocated(registry, NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_message_new_method_call(
- call->bus,
- &pk,
- "org.freedesktop.PolicyKit1",
- "/org/freedesktop/PolicyKit1/Authority",
- "org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(
- pk,
- "(sa{sv})sa{ss}us",
- "system-bus-name", 1, "name", "s", sender,
- action,
- 0,
- !!interactive,
- NULL);
- if (r < 0)
- return r;
-
- q = new0(AsyncPolkitQuery, 1);
- if (!q)
- return -ENOMEM;
-
- q->request = sd_bus_message_ref(call);
- q->callback = callback;
- q->userdata = userdata;
-
- r = hashmap_put(*registry, call, q);
- if (r < 0) {
- async_polkit_query_free(q);
- return r;
- }
-
- q->registry = *registry;
-
- r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
- if (r < 0) {
- async_polkit_query_free(q);
- return r;
- }
-
- return 0;
-#endif
-
- return -EACCES;
-}
-
-void bus_verify_polkit_async_registry_free(Hashmap *registry) {
-#ifdef ENABLE_POLKIT
- AsyncPolkitQuery *q;
-
- while ((q = hashmap_steal_first(registry)))
- async_polkit_query_free(q);
-
- hashmap_free(registry);
-#endif
-}
-
-int bus_check_peercred(sd_bus *c) {
- struct ucred ucred;
- socklen_t l;
- int fd;
-
- assert(c);
-
- fd = sd_bus_get_fd(c);
- if (fd < 0)
- return fd;
-
- l = sizeof(struct ucred);
- if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
- return -errno;
-
- if (l != sizeof(struct ucred))
- return -E2BIG;
-
- if (ucred.uid != 0 && ucred.uid != geteuid())
- return -EPERM;
-
- return 1;
-}
-
-int bus_open_system_systemd(sd_bus **_bus) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- int r;
-
- assert(_bus);
-
- if (geteuid() != 0)
- return sd_bus_open_system(_bus);
-
- /* If we are root and kdbus is not available, then let's talk
- * directly to the system instance, instead of going via the
- * bus */
-
-#ifdef ENABLE_KDBUS
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_ADDRESS);
- if (r < 0)
- return r;
-
- bus->bus_client = true;
-
- r = sd_bus_start(bus);
- if (r >= 0) {
- *_bus = bus;
- bus = NULL;
- return 0;
- }
-
- bus = sd_bus_unref(bus);
-#endif
-
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
- if (r < 0)
- return r;
-
- r = sd_bus_start(bus);
- if (r < 0)
- return sd_bus_open_system(_bus);
-
- r = bus_check_peercred(bus);
- if (r < 0)
- return r;
-
- *_bus = bus;
- bus = NULL;
-
- return 0;
-}
-
-int bus_open_user_systemd(sd_bus **_bus) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
- _cleanup_free_ char *ee = NULL;
- const char *e;
- int r;
-
- /* Try via kdbus first, and then directly */
-
- assert(_bus);
-
-#ifdef ENABLE_KDBUS
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- if (asprintf(&bus->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()) < 0)
- return -ENOMEM;
-
- bus->bus_client = true;
-
- r = sd_bus_start(bus);
- if (r >= 0) {
- *_bus = bus;
- bus = NULL;
- return 0;
- }
-
- bus = sd_bus_unref(bus);
-#endif
-
- e = secure_getenv("XDG_RUNTIME_DIR");
- if (!e)
- return sd_bus_open_user(_bus);
-
- ee = bus_address_escape(e);
- if (!ee)
- return -ENOMEM;
-
- r = sd_bus_new(&bus);
- if (r < 0)
- return r;
-
- bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
- if (!bus->address)
- return -ENOMEM;
-
- r = sd_bus_start(bus);
- if (r < 0)
- return sd_bus_open_user(_bus);
-
- r = bus_check_peercred(bus);
- if (r < 0)
- return r;
-
- *_bus = bus;
- bus = NULL;
-
- return 0;
-}
-
-int bus_print_property(const char *name, sd_bus_message *property, bool all) {
- char type;
- const char *contents;
- int r;
-
- assert(name);
- assert(property);
-
- r = sd_bus_message_peek_type(property, &type, &contents);
- if (r < 0)
- return r;
-
- switch (type) {
-
- case SD_BUS_TYPE_STRING: {
- const char *s;
-
- r = sd_bus_message_read_basic(property, type, &s);
- if (r < 0)
- return r;
-
- if (all || !isempty(s)) {
- _cleanup_free_ char *escaped = NULL;
-
- escaped = xescape(s, "\n");
- if (!escaped)
- return -ENOMEM;
-
- printf("%s=%s\n", name, escaped);
- }
-
- return 1;
- }
-
- case SD_BUS_TYPE_BOOLEAN: {
- int b;
-
- r = sd_bus_message_read_basic(property, type, &b);
- if (r < 0)
- return r;
-
- printf("%s=%s\n", name, yes_no(b));
-
- return 1;
- }
-
- case SD_BUS_TYPE_UINT64: {
- uint64_t u;
-
- r = sd_bus_message_read_basic(property, type, &u);
- if (r < 0)
- return r;
-
- /* Yes, heuristics! But we can change this check
- * should it turn out to not be sufficient */
-
- if (endswith(name, "Timestamp")) {
- char timestamp[FORMAT_TIMESTAMP_MAX], *t;
-
- t = format_timestamp(timestamp, sizeof(timestamp), u);
- if (t || all)
- printf("%s=%s\n", name, strempty(t));
-
- } else if (strstr(name, "USec")) {
- char timespan[FORMAT_TIMESPAN_MAX];
-
- printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
- } else
- printf("%s=%llu\n", name, (unsigned long long) u);
-
- return 1;
- }
-
- case SD_BUS_TYPE_INT64: {
- int64_t i;
-
- r = sd_bus_message_read_basic(property, type, &i);
- if (r < 0)
- return r;
-
- printf("%s=%lld\n", name, (long long) i);
-
- return 1;
- }
-
- case SD_BUS_TYPE_UINT32: {
- uint32_t u;
-
- r = sd_bus_message_read_basic(property, type, &u);
- if (r < 0)
- return r;
-
- if (strstr(name, "UMask") || strstr(name, "Mode"))
- printf("%s=%04o\n", name, u);
- else
- printf("%s=%u\n", name, (unsigned) u);
-
- return 1;
- }
-
- case SD_BUS_TYPE_INT32: {
- int32_t i;
-
- r = sd_bus_message_read_basic(property, type, &i);
- if (r < 0)
- return r;
-
- printf("%s=%i\n", name, (int) i);
- return 1;
- }
-
- case SD_BUS_TYPE_DOUBLE: {
- double d;
-
- r = sd_bus_message_read_basic(property, type, &d);
- if (r < 0)
- return r;
-
- printf("%s=%g\n", name, d);
- return 1;
- }
-
- case SD_BUS_TYPE_ARRAY:
- if (streq(contents, "s")) {
- bool first = true;
- const char *str;
-
- r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
- if (r < 0)
- return r;
-
- while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
- _cleanup_free_ char *escaped = NULL;
-
- if (first)
- printf("%s=", name);
-
- escaped = xescape(str, "\n ");
- if (!escaped)
- return -ENOMEM;
-
- printf("%s%s", first ? "" : " ", escaped);
-
- first = false;
- }
- if (r < 0)
- return r;
-
- if (first && all)
- printf("%s=", name);
- if (!first || all)
- puts("");
-
- r = sd_bus_message_exit_container(property);
- if (r < 0)
- return r;
-
- return 1;
-
- } else if (streq(contents, "y")) {
- const uint8_t *u;
- size_t n;
-
- r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
- if (r < 0)
- return r;
-
- if (all || n > 0) {
- unsigned int i;
-
- printf("%s=", name);
-
- for (i = 0; i < n; i++)
- printf("%02x", u[i]);
-
- puts("");
- }
-
- return 1;
-
- } else if (streq(contents, "u")) {
- uint32_t *u;
- size_t n;
-
- r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
- if (r < 0)
- return r;
-
- if (all || n > 0) {
- unsigned int i;
-
- printf("%s=", name);
-
- for (i = 0; i < n; i++)
- printf("%08x", u[i]);
-
- puts("");
- }
-
- return 1;
- }
-
- break;
- }
-
- return 0;
-}
-
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- assert(bus);
- assert(path);
-
- r = sd_bus_call_method(bus,
- dest,
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- &error,
- &reply,
- "s", "");
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return r;
-
- while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
- const char *name;
- const char *contents;
-
- r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
- if (r < 0)
- return r;
-
- if (!filter || strv_find(filter, name)) {
- r = sd_bus_message_peek_type(reply, NULL, &contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
- if (r < 0)
- return r;
-
- r = bus_print_property(name, reply, all);
- if (r < 0)
- return r;
- if (r == 0) {
- if (all)
- printf("%s=[unprintable]\n", name);
- /* skip what we didn't read */
- r = sd_bus_message_skip(reply, contents);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
- } else {
- r = sd_bus_message_skip(reply, "v");
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
- }
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- sd_id128_t *p = userdata;
- const void *v;
- size_t n;
- int r;
-
- r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
- if (r < 0)
- return r;
-
- if (n == 0)
- *p = SD_ID128_NULL;
- else if (n == 16)
- memcpy((*p).bytes, v, n);
- else
- return -EINVAL;
-
- return 0;
-}
-
-static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- char type;
- int r;
-
- r = sd_bus_message_peek_type(m, &type, NULL);
- if (r < 0)
- return r;
-
- switch (type) {
- case SD_BUS_TYPE_STRING: {
- const char *s;
- char **p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &s);
- if (r < 0)
- break;
-
- if (isempty(s))
- break;
-
- r = free_and_strdup(p, s);
- break;
- }
-
- case SD_BUS_TYPE_ARRAY: {
- _cleanup_strv_free_ char **l = NULL;
- char ***p = userdata;
-
- r = bus_message_read_strv_extend(m, &l);
- if (r < 0)
- break;
-
- strv_free(*p);
- *p = l;
- l = NULL;
-
- break;
- }
-
- case SD_BUS_TYPE_BOOLEAN: {
- unsigned b;
- bool *p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &b);
- if (r < 0)
- break;
-
- *p = b;
-
- break;
- }
-
- case SD_BUS_TYPE_UINT32: {
- uint64_t u;
- uint32_t *p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &u);
- if (r < 0)
- break;
-
- *p = u;
-
- break;
- }
-
- case SD_BUS_TYPE_UINT64: {
- uint64_t t;
- uint64_t *p = userdata;
-
- r = sd_bus_message_read_basic(m, type, &t);
- if (r < 0)
- break;
-
- *p = t;
-
- break;
- }
-
- default:
- break;
- }
-
- return r;
-}
-
-int bus_message_map_all_properties(
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- assert(m);
- assert(map);
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return r;
-
- while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
- const struct bus_properties_map *prop;
- const char *member;
- const char *contents;
- void *v;
- unsigned i;
-
- r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
- if (r < 0)
- return r;
-
- for (i = 0, prop = NULL; map[i].member; i++)
- if (streq(map[i].member, member)) {
- prop = &map[i];
- break;
- }
-
- if (prop) {
- r = sd_bus_message_peek_type(m, NULL, &contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
- if (r < 0)
- return r;
-
- v = (uint8_t *)userdata + prop->offset;
- if (map[i].set)
- r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v);
- else
- r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- } else {
- r = sd_bus_message_skip(m, "v");
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- }
- if (r < 0)
- return r;
-
- return sd_bus_message_exit_container(m);
-}
-
-int bus_message_map_properties_changed(
- sd_bus_message *m,
- const struct bus_properties_map *map,
- void *userdata) {
-
- const char *member;
- int r, invalidated, i;
-
- assert(m);
- assert(map);
-
- r = bus_message_map_all_properties(m, map, userdata);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
- if (r < 0)
- return r;
-
- invalidated = 0;
- while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
- for (i = 0; map[i].member; i++)
- if (streq(map[i].member, member)) {
- ++invalidated;
- break;
- }
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return invalidated;
-}
-
-int bus_map_all_properties(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const struct bus_properties_map *map,
- void *userdata) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- assert(bus);
- assert(destination);
- assert(path);
- assert(map);
-
- r = sd_bus_call_method(
- bus,
- destination,
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- &error,
- &m,
- "s", "");
- if (r < 0)
- return r;
-
- return bus_message_map_all_properties(m, map, userdata);
-}
-
-int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
- int r;
-
- assert(transport >= 0);
- assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
-
- assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
-
- switch (transport) {
-
- case BUS_TRANSPORT_LOCAL:
- if (user)
- r = sd_bus_default_user(bus);
- else
- r = sd_bus_default_system(bus);
-
- break;
-
- case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
- break;
-
- case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
- break;
-
- default:
- assert_not_reached("Hmm, unknown transport type.");
- }
-
- return r;
-}
-
-int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
- int r;
-
- assert(transport >= 0);
- assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
-
- assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
- assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
-
- switch (transport) {
-
- case BUS_TRANSPORT_LOCAL:
- if (user)
- r = bus_open_user_systemd(bus);
- else
- r = bus_open_system_systemd(bus);
-
- break;
-
- case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
- break;
-
- case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
- break;
-
- default:
- assert_not_reached("Hmm, unknown transport type.");
- }
-
- return r;
-}
-
-int bus_property_get_bool(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- int b = *(bool*) userdata;
-
- return sd_bus_message_append_basic(reply, 'b', &b);
-}
-
-#if __SIZEOF_SIZE_T__ != 8
-int bus_property_get_size(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- uint64_t sz = *(size_t*) userdata;
-
- return sd_bus_message_append_basic(reply, 't', &sz);
-}
-#endif
-
-#if __SIZEOF_LONG__ != 8
-int bus_property_get_long(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- int64_t l = *(long*) userdata;
-
- return sd_bus_message_append_basic(reply, 'x', &l);
-}
-
-int bus_property_get_ulong(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- uint64_t ul = *(unsigned long*) userdata;
-
- return sd_bus_message_append_basic(reply, 't', &ul);
-}
-#endif
-
-int bus_log_parse_error(int r) {
- return log_error_errno(r, "Failed to parse bus message: %m");
-}
-
-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_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
- assert(m);
-
- if (r < 0) {
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
- sd_bus_reply_method_errno(m, r, error);
-
- } else if (sd_bus_error_is_set(error)) {
- if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
- sd_bus_reply_method_error(m, error);
- } else
- return r;
-
- log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
- bus_message_type_to_string(m->header->type),
- strna(m->sender),
- strna(m->path),
- strna(m->interface),
- strna(m->member),
- strna(m->root_container.signature),
- bus_error_message(error, r));
-
- return 1;
-}
-
-int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
- const char *eq, *field;
- int r;
-
- assert(m);
- assert(assignment);
-
- eq = strchr(assignment, '=');
- if (!eq) {
- log_error("Not an assignment: %s", assignment);
- return -EINVAL;
- }
-
- field = strndupa(assignment, eq - assignment);
- eq ++;
-
- if (streq(field, "CPUQuota")) {
-
- if (isempty(eq)) {
-
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "v", "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_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
- } else {
- log_error("CPU quota needs to be in percent.");
- return -EINVAL;
- }
-
- if (r < 0)
- return bus_log_create_error(r);
-
- return 0;
- }
-
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
- if (r < 0)
- return bus_log_create_error(r);
-
- if (STR_IN_SET(field,
- "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
- "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies")) {
-
- r = parse_boolean(eq);
- if (r < 0) {
- log_error("Failed to parse boolean assignment %s.", assignment);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "b", r);
-
- } else if (streq(field, "MemoryLimit")) {
- off_t bytes;
-
- 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", (uint64_t) bytes);
-
- } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
- uint64_t u;
-
- r = safe_atou64(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"))
- r = sd_bus_message_append(m, "v", "s", eq);
-
- 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;
- off_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, (uint64_t) 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 (rlimit_from_string(field) >= 0) {
- uint64_t rl;
-
- if (streq(eq, "infinity"))
- rl = (uint64_t) -1;
- else {
- r = safe_atou64(eq, &rl);
- if (r < 0) {
- log_error("Invalid resource limit: %s", eq);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", rl);
-
- } 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 (streq(field, "Environment")) {
-
- r = sd_bus_message_append(m, "v", "as", 1, eq);
-
- } 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, "AccuracySec")) {
- usec_t u;
-
- r = parse_sec(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 {
- log_error("Unknown assignment %s.", assignment);
- return -EINVAL;
- }
-
- 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) {
- _cleanup_free_ char *service_shell_quoted = NULL;
-
- assert(service);
-
- service_shell_quoted = shell_maybe_quote(service);
-
- 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. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
- service,
- explanations[i].explanation,
- strna(service_shell_quoted));
-
- goto finish;
- }
- }
-
- log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
- service,
- strna(service_shell_quoted));
-
-finish:
- /* For some results maybe additional explanation is required */
- if (streq_ptr(result, "start-limit"))
- log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
- strna(service_shell_quoted));
-}
-
-static int check_wait_response(BusWaitForJobs *d, bool quiet) {
- 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("Job for %s invalid.", 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);
- } 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) {
- 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);
- /* 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));
- }
-
- free(d->name);
- d->name = NULL;
-
- free(d->result);
- d->result = NULL;
- }
-
- 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);
-}
-
-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) {
- if (!quiet) {
- if (streq(type, "symlink"))
- log_info("Created symlink from %s to %s.", path, source);
- else
- log_info("Removed symlink %s.", path);
- }
-
- r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, 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);
-
- return 0;
-}
-
-/**
- * bus_path_encode_unique() - encode unique object path
- * @b: bus connection or NULL
- * @prefix: object path prefix
- * @sender_id: unique-name of client, or NULL
- * @external_id: external ID to be chosen by client, or NULL
- * @ret_path: storage for encoded object path pointer
- *
- * Whenever we provide a bus API that allows clients to create and manage
- * server-side objects, we need to provide a unique name for these objects. If
- * we let the server choose the name, we suffer from a race condition: If a
- * client creates an object asynchronously, it cannot destroy that object until
- * it received the method reply. It cannot know the name of the new object,
- * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
- *
- * Therefore, many APIs allow the client to choose the unique name for newly
- * created objects. There're two problems to solve, though:
- * 1) Object names are usually defined via dbus object paths, which are
- * usually globally namespaced. Therefore, multiple clients must be able
- * to choose unique object names without interference.
- * 2) If multiple libraries share the same bus connection, they must be
- * able to choose unique object names without interference.
- * The first problem is solved easily by prefixing a name with the
- * unique-bus-name of a connection. The server side must enforce this and
- * reject any other name. The second problem is solved by providing unique
- * suffixes from within sd-bus.
- *
- * This helper allows clients to create unique object-paths. It uses the
- * template '/prefix/sender_id/external_id' and returns the new path in
- * @ret_path (must be freed by the caller).
- * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
- * NULL, this function allocates a unique suffix via @b (by requesting a new
- * cookie). If both @sender_id and @external_id are given, @b can be passed as
- * NULL.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
- _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
- char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
- int r;
-
- assert_return(b || (sender_id && external_id), -EINVAL);
- assert_return(object_path_is_valid(prefix), -EINVAL);
- assert_return(ret_path, -EINVAL);
-
- if (!sender_id) {
- r = sd_bus_get_unique_name(b, &sender_id);
- if (r < 0)
- return r;
- }
-
- if (!external_id) {
- xsprintf(external_buf, "%"PRIu64, ++b->cookie);
- external_id = external_buf;
- }
-
- sender_label = bus_label_escape(sender_id);
- if (!sender_label)
- return -ENOMEM;
-
- external_label = bus_label_escape(external_id);
- if (!external_label)
- return -ENOMEM;
-
- p = strjoin(prefix, "/", sender_label, "/", external_label, NULL);
- if (!p)
- return -ENOMEM;
-
- *ret_path = p;
- return 0;
-}
-
-/**
- * bus_path_decode_unique() - decode unique object path
- * @path: object path to decode
- * @prefix: object path prefix
- * @ret_sender: output parameter for sender-id label
- * @ret_external: output parameter for external-id label
- *
- * This does the reverse of bus_path_encode_unique() (see its description for
- * details). Both trailing labels, sender-id and external-id, are unescaped and
- * returned in the given output parameters (the caller must free them).
- *
- * Note that this function returns 0 if the path does not match the template
- * (see bus_path_encode_unique()), 1 if it matched.
- *
- * Returns: Negative error code on failure, 0 if the given object path does not
- * match the template (return parameters are set to NULL), 1 if it was
- * parsed successfully (return parameters contain allocated labels).
- */
-int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
- const char *p, *q;
- char *sender, *external;
-
- assert(object_path_is_valid(path));
- assert(object_path_is_valid(prefix));
- assert(ret_sender);
- assert(ret_external);
-
- p = object_path_startswith(path, prefix);
- if (!p) {
- *ret_sender = NULL;
- *ret_external = NULL;
- return 0;
- }
-
- q = strchr(p, '/');
- if (!q) {
- *ret_sender = NULL;
- *ret_external = NULL;
- return 0;
- }
-
- sender = bus_label_unescape_n(p, q - p);
- external = bus_label_unescape(q + 1);
- if (!sender || !external) {
- free(sender);
- free(external);
- return -ENOMEM;
- }
-
- *ret_sender = sender;
- *ret_external = external;
- return 1;
-}
-
-bool is_kdbus_wanted(void) {
- _cleanup_free_ char *value = NULL;
- int r;
-
- if (get_proc_cmdline_key("kdbus", NULL) <= 0) {
- r = get_proc_cmdline_key("kdbus=", &value);
- if (r <= 0 || parse_boolean(value) != 1)
- return false;
- }
-
- return true;
-}
-
-bool is_kdbus_available(void) {
- _cleanup_close_ int fd = -1;
- struct kdbus_cmd cmd = { .size = sizeof(cmd), .flags = KDBUS_FLAG_NEGOTIATE };
-
- if (!is_kdbus_wanted())
- return false;
-
- fd = open("/sys/fs/kdbus/control", O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
- if (fd < 0)
- return false;
-
- return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0;
-}
diff --git a/src/libsystemd/sd-bus/bus-util.h b/src/libsystemd/sd-bus/bus-util.h
deleted file mode 100644
index 999a372..0000000
--- a/src/libsystemd/sd-bus/bus-util.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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-event.h"
-#include "sd-bus.h"
-#include "hashmap.h"
-#include "install.h"
-#include "time-util.h"
-
-typedef enum BusTransport {
- BUS_TRANSPORT_LOCAL,
- BUS_TRANSPORT_REMOTE,
- BUS_TRANSPORT_MACHINE,
- _BUS_TRANSPORT_MAX,
- _BUS_TRANSPORT_INVALID = -1
-} BusTransport;
-
-typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-
-struct bus_properties_map {
- const char *member;
- const char *signature;
- bus_property_set_t set;
- size_t offset;
-};
-
-int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-
-int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
-int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
-int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata);
-
-int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
-
-typedef bool (*check_idle_t)(void *userdata);
-
-int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout, check_idle_t check_idle, void *userdata);
-
-int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
-
-int bus_check_peercred(sd_bus *c);
-
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, uid_t good_user, bool *_challenge, sd_bus_error *e);
-
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
-void bus_verify_polkit_async_registry_free(Hashmap *registry);
-
-int bus_open_system_systemd(sd_bus **_bus);
-int bus_open_user_systemd(sd_bus **_bus);
-
-int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
-int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
-
-int bus_print_property(const char *name, sd_bus_message *property, bool all);
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all);
-
-int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-
-#define bus_property_get_usec ((sd_bus_property_get_t) NULL)
-#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
-
-assert_cc(sizeof(int) == sizeof(int32_t));
-#define bus_property_get_int ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(unsigned) == sizeof(unsigned));
-#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL)
-
-/* On 64bit machines we can use the default serializer for size_t and
- * friends, otherwise we need to cast this manually */
-#if __SIZEOF_SIZE_T__ == 8
-#define bus_property_get_size ((sd_bus_property_get_t) NULL)
-#else
-int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-#endif
-
-#if __SIZEOF_LONG__ == 8
-#define bus_property_get_long ((sd_bus_property_get_t) NULL)
-#define bus_property_get_ulong ((sd_bus_property_get_t) NULL)
-#else
-int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-#endif
-
-/* uid_t and friends on Linux 32 bit. This means we can just use the
- * default serializer for 32bit unsigned, for serializing it, and map
- * it to NULL here */
-assert_cc(sizeof(uid_t) == sizeof(uint32_t));
-#define bus_property_get_uid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(gid_t) == sizeof(uint32_t));
-#define bus_property_get_gid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(pid_t) == sizeof(uint32_t));
-#define bus_property_get_pid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(mode_t) == sizeof(uint32_t));
-#define bus_property_get_mode ((sd_bus_property_get_t) NULL)
-
-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);
-
-static inline void sd_bus_close_unrefp(sd_bus **bus) {
- if (*bus) {
- sd_bus_flush(*bus);
- sd_bus_close(*bus);
- sd_bus_unref(*bus);
- }
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_slot*, sd_bus_slot_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, sd_bus_message_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_creds*, sd_bus_creds_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_track*, sd_bus_track_unref);
-
-#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp)
-#define _cleanup_bus_close_unref_ _cleanup_(sd_bus_close_unrefp)
-#define _cleanup_bus_slot_unref_ _cleanup_(sd_bus_slot_unrefp)
-#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp)
-#define _cleanup_bus_creds_unref_ _cleanup_(sd_bus_creds_unrefp)
-#define _cleanup_bus_track_unref_ _cleanup_(sd_bus_slot_unrefp)
-#define _cleanup_bus_error_free_ _cleanup_(sd_bus_error_free)
-
-#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
- int function(sd_bus *bus, \
- const char *path, \
- const char *interface, \
- const char *property, \
- sd_bus_message *reply, \
- void *userdata, \
- sd_bus_error *error) { \
- \
- const char *value; \
- type *field = userdata; \
- int r; \
- \
- assert(bus); \
- assert(reply); \
- assert(field); \
- \
- value = strempty(name##_to_string(*field)); \
- \
- r = sd_bus_message_append_basic(reply, 's', value); \
- if (r < 0) \
- return r; \
- \
- return 1; \
- } \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \
- 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_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
-
-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);
-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);
-
-bool is_kdbus_wanted(void);
-bool is_kdbus_available(void);
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index ed8aa09..e3885ec 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -30,7 +30,7 @@
#include "macro.h"
#include "strv.h"
#include "fileio.h"
-#include "login-shared.h"
+#include "login-util.h"
#include "formats-util.h"
#include "sd-login.h"
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index 7a329b9..4bf739a 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -26,7 +26,7 @@ typedef enum KillWho KillWho;
#include "list.h"
#include "logind-user.h"
-#include "login-shared.h"
+#include "login-util.h"
typedef enum SessionState {
SESSION_OPENING, /* Session scope is being created */
diff --git a/src/login/test-login-shared.c b/src/login/test-login-shared.c
index d29d7e7..4c4275d 100644
--- a/src/login/test-login-shared.c
+++ b/src/login/test-login-shared.c
@@ -20,7 +20,7 @@
***/
#include "macro.h"
-#include "login-shared.h"
+#include "login-util.h"
static void test_session_id_valid(void) {
assert_se(session_id_valid("c1"));
diff --git a/src/shared/.gitignore b/src/shared/.gitignore
deleted file mode 100644
index e22411e..0000000
--- a/src/shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-/cap-from-name.gperf
-/cap-from-name.h
-/cap-list.txt
-/cap-to-name.h
-/errno-from-name.gperf
-/errno-from-name.h
-/errno-list.txt
-/errno-to-name.h
-/af-from-name.gperf
-/af-from-name.h
-/af-list.txt
-/af-to-name.h
-/arphrd-from-name.gperf
-/arphrd-from-name.h
-/arphrd-list.txt
-/arphrd-to-name.h
diff --git a/src/shared/Makefile b/src/shared/Makefile
deleted file mode 120000
index d0b0e8e..0000000
--- a/src/shared/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/shared/MurmurHash2.c b/src/shared/MurmurHash2.c
deleted file mode 100644
index 2f4149d..0000000
--- a/src/shared/MurmurHash2.c
+++ /dev/null
@@ -1,86 +0,0 @@
-//-----------------------------------------------------------------------------
-// MurmurHash2 was written by Austin Appleby, and is placed in the public
-// domain. The author hereby disclaims copyright to this source code.
-
-// Note - This code makes a few assumptions about how your machine behaves -
-
-// 1. We can read a 4-byte value from any address without crashing
-// 2. sizeof(int) == 4
-
-// And it has a few limitations -
-
-// 1. It will not work incrementally.
-// 2. It will not produce the same results on little-endian and big-endian
-// machines.
-
-#include "MurmurHash2.h"
-
-//-----------------------------------------------------------------------------
-// Platform-specific functions and macros
-
-// Microsoft Visual Studio
-
-#if defined(_MSC_VER)
-
-#define BIG_CONSTANT(x) (x)
-
-// Other compilers
-
-#else // defined(_MSC_VER)
-
-#define BIG_CONSTANT(x) (x##LLU)
-
-#endif // !defined(_MSC_VER)
-
-//-----------------------------------------------------------------------------
-
-uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
-{
- // 'm' and 'r' are mixing constants generated offline.
- // They're not really 'magic', they just happen to work well.
-
- const uint32_t m = 0x5bd1e995;
- const int r = 24;
-
- // Initialize the hash to a 'random' value
-
- uint32_t h = seed ^ len;
-
- // Mix 4 bytes at a time into the hash
-
- const unsigned char * data = (const unsigned char *)key;
-
- while(len >= 4)
- {
- uint32_t k = *(uint32_t*)data;
-
- k *= m;
- k ^= k >> r;
- k *= m;
-
- h *= m;
- h ^= k;
-
- data += 4;
- len -= 4;
- }
-
- // Handle the last few bytes of the input array
-
- switch(len)
- {
- case 3: h ^= data[2] << 16;
- case 2: h ^= data[1] << 8;
- case 1: h ^= data[0];
- h *= m;
- };
-
- // Do a few final mixes of the hash to ensure the last few
- // bytes are well-incorporated.
-
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
-
- return h;
-}
diff --git a/src/shared/MurmurHash2.h b/src/shared/MurmurHash2.h
deleted file mode 100644
index 93362dd..0000000
--- a/src/shared/MurmurHash2.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//-----------------------------------------------------------------------------
-// MurmurHash2 was written by Austin Appleby, and is placed in the public
-// domain. The author hereby disclaims copyright to this source code.
-
-#ifndef _MURMURHASH2_H_
-#define _MURMURHASH2_H_
-
-//-----------------------------------------------------------------------------
-// Platform-specific functions and macros
-
-// Microsoft Visual Studio
-
-#if defined(_MSC_VER)
-
-typedef unsigned char uint8_t;
-typedef unsigned long uint32_t;
-typedef unsigned __int64 uint64_t;
-
-// Other compilers
-
-#else // defined(_MSC_VER)
-
-#include <stdint.h>
-
-#endif // !defined(_MSC_VER)
-
-//-----------------------------------------------------------------------------
-
-uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed );
-
-//-----------------------------------------------------------------------------
-
-#endif // _MURMURHASH2_H_
diff --git a/src/shared/af-list.c b/src/shared/af-list.c
deleted file mode 100644
index f396115..0000000
--- a/src/shared/af-list.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/socket.h>
-#include <string.h>
-
-#include "util.h"
-#include "af-list.h"
-
-static const struct af_name* lookup_af(register const char *str, register unsigned int len);
-
-#include "af-to-name.h"
-#include "af-from-name.h"
-
-const char *af_to_name(int id) {
-
- if (id <= 0)
- return NULL;
-
- if (id >= (int) ELEMENTSOF(af_names))
- return NULL;
-
- return af_names[id];
-}
-
-int af_from_name(const char *name) {
- const struct af_name *sc;
-
- assert(name);
-
- sc = lookup_af(name, strlen(name));
- if (!sc)
- return AF_UNSPEC;
-
- return sc->id;
-}
-
-int af_max(void) {
- return ELEMENTSOF(af_names);
-}
diff --git a/src/shared/af-list.h b/src/shared/af-list.h
deleted file mode 100644
index e346ab8..0000000
--- a/src/shared/af-list.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-const char *af_to_name(int id);
-int af_from_name(const char *name);
-
-int af_max(void);
diff --git a/src/shared/arphrd-list.c b/src/shared/arphrd-list.c
deleted file mode 100644
index 284043c..0000000
--- a/src/shared/arphrd-list.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <net/if_arp.h>
-#include <string.h>
-
-#include "util.h"
-#include "arphrd-list.h"
-
-static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
-
-#include "arphrd-to-name.h"
-#include "arphrd-from-name.h"
-
-const char *arphrd_to_name(int id) {
-
- if (id <= 0)
- return NULL;
-
- if (id >= (int) ELEMENTSOF(arphrd_names))
- return NULL;
-
- return arphrd_names[id];
-}
-
-int arphrd_from_name(const char *name) {
- const struct arphrd_name *sc;
-
- assert(name);
-
- sc = lookup_arphrd(name, strlen(name));
- if (!sc)
- return 0;
-
- return sc->id;
-}
-
-int arphrd_max(void) {
- return ELEMENTSOF(arphrd_names);
-}
diff --git a/src/shared/arphrd-list.h b/src/shared/arphrd-list.h
deleted file mode 100644
index 5ca182c..0000000
--- a/src/shared/arphrd-list.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-const char *arphrd_to_name(int id);
-int arphrd_from_name(const char *name);
-
-int arphrd_max(void);
diff --git a/src/shared/async.c b/src/shared/async.c
deleted file mode 100644
index 7725e6d..0000000
--- a/src/shared/async.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 <pthread.h>
-#include <unistd.h>
-
-#include "async.h"
-#include "log.h"
-#include "util.h"
-
-int asynchronous_job(void* (*func)(void *p), void *arg) {
- pthread_attr_t a;
- pthread_t t;
- int r;
-
- /* It kinda sucks that we have to resort to threads to
- * implement an asynchronous sync(), but well, such is
- * life.
- *
- * Note that issuing this command right before exiting a
- * process will cause the process to wait for the sync() to
- * complete. This function hence is nicely asynchronous really
- * only in long running processes. */
-
- r = pthread_attr_init(&a);
- if (r > 0)
- return -r;
-
- r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
- if (r > 0)
- goto finish;
-
- r = pthread_create(&t, &a, func, arg);
-
-finish:
- pthread_attr_destroy(&a);
- return -r;
-}
-
-static void *sync_thread(void *p) {
- sync();
- return NULL;
-}
-
-int asynchronous_sync(void) {
- log_debug("Spawning new thread for sync");
-
- return asynchronous_job(sync_thread, NULL);
-}
-
-static void *close_thread(void *p) {
- assert_se(close_nointr(PTR_TO_INT(p)) != -EBADF);
- return NULL;
-}
-
-int asynchronous_close(int fd) {
- int r;
-
- /* This is supposed to behave similar to safe_close(), but
- * actually invoke close() asynchronously, so that it will
- * never block. Ideally the kernel would have an API for this,
- * but it doesn't, so we work around it, and hide this as a
- * far away as we can. */
-
- if (fd >= 0) {
- PROTECT_ERRNO;
-
- r = asynchronous_job(close_thread, INT_TO_PTR(fd));
- if (r < 0)
- assert_se(close_nointr(fd) != -EBADF);
- }
-
- return -1;
-}
diff --git a/src/shared/async.h b/src/shared/async.h
deleted file mode 100644
index 7f1ef79..0000000
--- a/src/shared/async.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/>.
-***/
-
-int asynchronous_job(void* (*func)(void *p), void *arg);
-
-int asynchronous_sync(void);
-int asynchronous_close(int fd);
diff --git a/src/shared/audit.c b/src/shared/audit.c
deleted file mode 100644
index 54148fc..0000000
--- a/src/shared/audit.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <errno.h>
-#include <stdio.h>
-
-#include "macro.h"
-#include "audit.h"
-#include "util.h"
-#include "process-util.h"
-#include "fileio.h"
-
-int audit_session_from_pid(pid_t pid, uint32_t *id) {
- _cleanup_free_ char *s = NULL;
- const char *p;
- uint32_t u;
- int r;
-
- assert(id);
-
- p = procfs_file_alloca(pid, "sessionid");
-
- r = read_one_line_file(p, &s);
- if (r < 0)
- return r;
-
- r = safe_atou32(s, &u);
- if (r < 0)
- return r;
-
- if (u == AUDIT_SESSION_INVALID || u <= 0)
- return -ENXIO;
-
- *id = u;
- return 0;
-}
-
-int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
- _cleanup_free_ char *s = NULL;
- const char *p;
- uid_t u;
- int r;
-
- assert(uid);
-
- p = procfs_file_alloca(pid, "loginuid");
-
- r = read_one_line_file(p, &s);
- if (r < 0)
- return r;
-
- r = parse_uid(s, &u);
- if (r < 0)
- return r;
-
- *uid = (uid_t) u;
- return 0;
-}
-
-bool use_audit(void) {
- static int cached_use = -1;
-
- if (cached_use < 0) {
- int fd;
-
- fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
- if (fd < 0)
- cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT;
- else {
- cached_use = true;
- safe_close(fd);
- }
- }
-
- return cached_use;
-}
diff --git a/src/shared/audit.h b/src/shared/audit.h
deleted file mode 100644
index 6de331c..0000000
--- a/src/shared/audit.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdint.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#define AUDIT_SESSION_INVALID ((uint32_t) -1)
-
-int audit_session_from_pid(pid_t pid, uint32_t *id);
-int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
-
-bool use_audit(void);
diff --git a/src/shared/barrier.c b/src/shared/barrier.c
deleted file mode 100644
index 436ba95..0000000
--- a/src/shared/barrier.c
+++ /dev/null
@@ -1,416 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
-
- 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 <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/eventfd.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "barrier.h"
-#include "macro.h"
-#include "util.h"
-
-/**
- * Barriers
- * This barrier implementation provides a simple synchronization method based
- * on file-descriptors that can safely be used between threads and processes. A
- * barrier object contains 2 shared counters based on eventfd. Both processes
- * can now place barriers and wait for the other end to reach a random or
- * specific barrier.
- * Barriers are numbered, so you can either wait for the other end to reach any
- * barrier or the last barrier that you placed. This way, you can use barriers
- * for one-way *and* full synchronization. Note that even-though barriers are
- * numbered, these numbers are internal and recycled once both sides reached the
- * same barrier (implemented as a simple signed counter). It is thus not
- * possible to address barriers by their ID.
- *
- * Barrier-API: Both ends can place as many barriers via barrier_place() as
- * they want and each pair of barriers on both sides will be implicitly linked.
- * Each side can use the barrier_wait/sync_*() family of calls to wait for the
- * other side to place a specific barrier. barrier_wait_next() waits until the
- * other side calls barrier_place(). No links between the barriers are
- * considered and this simply serves as most basic asynchronous barrier.
- * barrier_sync_next() is like barrier_wait_next() and waits for the other side
- * to place their next barrier via barrier_place(). However, it only waits for
- * barriers that are linked to a barrier we already placed. If the other side
- * already placed more barriers than we did, barrier_sync_next() returns
- * immediately.
- * barrier_sync() extends barrier_sync_next() and waits until the other end
- * placed as many barriers via barrier_place() as we did. If they already placed
- * as many as we did (or more), it returns immediately.
- *
- * Additionally to basic barriers, an abortion event is available.
- * barrier_abort() places an abortion event that cannot be undone. An abortion
- * immediately cancels all placed barriers and replaces them. Any running and
- * following wait/sync call besides barrier_wait_abortion() will immediately
- * return false on both sides (otherwise, they always return true).
- * barrier_abort() can be called multiple times on both ends and will be a
- * no-op if already called on this side.
- * barrier_wait_abortion() can be used to wait for the other side to call
- * barrier_abort() and is the only wait/sync call that does not return
- * immediately if we aborted outself. It only returns once the other side
- * called barrier_abort().
- *
- * Barriers can be used for in-process and inter-process synchronization.
- * However, for in-process synchronization you could just use mutexes.
- * Therefore, main target is IPC and we require both sides to *not* share the FD
- * table. If that's given, barriers provide target tracking: If the remote side
- * exit()s, an abortion event is implicitly queued on the other side. This way,
- * a sync/wait call will be woken up if the remote side crashed or exited
- * unexpectedly. However, note that these abortion events are only queued if the
- * barrier-queue has been drained. Therefore, it is safe to place a barrier and
- * exit. The other side can safely wait on the barrier even though the exit
- * queued an abortion event. Usually, the abortion event would overwrite the
- * barrier, however, that's not true for exit-abortion events. Those are only
- * queued if the barrier-queue is drained (thus, the receiving side has placed
- * more barriers than the remote side).
- */
-
-/**
- * barrier_create() - Initialize a barrier object
- * @obj: barrier to initialize
- *
- * This initializes a barrier object. The caller is responsible of allocating
- * the memory and keeping it valid. The memory does not have to be zeroed
- * beforehand.
- * Two eventfd objects are allocated for each barrier. If allocation fails, an
- * error is returned.
- *
- * If this function fails, the barrier is reset to an invalid state so it is
- * safe to call barrier_destroy() on the object regardless whether the
- * initialization succeeded or not.
- *
- * The caller is responsible to destroy the object via barrier_destroy() before
- * releasing the underlying memory.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int barrier_create(Barrier *b) {
- _cleanup_(barrier_destroyp) Barrier *staging = b;
- int r;
-
- assert(b);
-
- b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
- if (b->me < 0)
- return -errno;
-
- b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
- if (b->them < 0)
- return -errno;
-
- r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK);
- if (r < 0)
- return -errno;
-
- staging = NULL;
- return 0;
-}
-
-/**
- * barrier_destroy() - Destroy a barrier object
- * @b: barrier to destroy or NULL
- *
- * This destroys a barrier object that has previously been passed to
- * barrier_create(). The object is released and reset to invalid
- * state. Therefore, it is safe to call barrier_destroy() multiple
- * times or even if barrier_create() failed. However, barrier must be
- * always initialized with BARRIER_NULL.
- *
- * If @b is NULL, this is a no-op.
- */
-void barrier_destroy(Barrier *b) {
- if (!b)
- return;
-
- b->me = safe_close(b->me);
- b->them = safe_close(b->them);
- safe_close_pair(b->pipe);
- b->barriers = 0;
-}
-
-/**
- * barrier_set_role() - Set the local role of the barrier
- * @b: barrier to operate on
- * @role: role to set on the barrier
- *
- * This sets the roles on a barrier object. This is needed to know
- * which side of the barrier you're on. Usually, the parent creates
- * the barrier via barrier_create() and then calls fork() or clone().
- * Therefore, the FDs are duplicated and the child retains the same
- * barrier object.
- *
- * Both sides need to call barrier_set_role() after fork() or clone()
- * are done. If this is not done, barriers will not work correctly.
- *
- * Note that barriers could be supported without fork() or clone(). However,
- * this is currently not needed so it hasn't been implemented.
- */
-void barrier_set_role(Barrier *b, unsigned int role) {
- int fd;
-
- assert(b);
- assert(role == BARRIER_PARENT || role == BARRIER_CHILD);
- /* make sure this is only called once */
- assert(b->pipe[0] >= 0 && b->pipe[1] >= 0);
-
- if (role == BARRIER_PARENT)
- b->pipe[1] = safe_close(b->pipe[1]);
- else {
- b->pipe[0] = safe_close(b->pipe[0]);
-
- /* swap me/them for children */
- fd = b->me;
- b->me = b->them;
- b->them = fd;
- }
-}
-
-/* places barrier; returns false if we aborted, otherwise true */
-static bool barrier_write(Barrier *b, uint64_t buf) {
- ssize_t len;
-
- /* prevent new sync-points if we already aborted */
- if (barrier_i_aborted(b))
- return false;
-
- do {
- len = write(b->me, &buf, sizeof(buf));
- } while (len < 0 && IN_SET(errno, EAGAIN, EINTR));
-
- if (len != sizeof(buf))
- goto error;
-
- /* lock if we aborted */
- if (buf >= (uint64_t)BARRIER_ABORTION) {
- if (barrier_they_aborted(b))
- b->barriers = BARRIER_WE_ABORTED;
- else
- b->barriers = BARRIER_I_ABORTED;
- } else if (!barrier_is_aborted(b))
- b->barriers += buf;
-
- return !barrier_i_aborted(b);
-
-error:
- /* If there is an unexpected error, we have to make this fatal. There
- * is no way we can recover from sync-errors. Therefore, we close the
- * pipe-ends and treat this as abortion. The other end will notice the
- * pipe-close and treat it as abortion, too. */
-
- safe_close_pair(b->pipe);
- b->barriers = BARRIER_WE_ABORTED;
- return false;
-}
-
-/* waits for barriers; returns false if they aborted, otherwise true */
-static bool barrier_read(Barrier *b, int64_t comp) {
- if (barrier_they_aborted(b))
- return false;
-
- while (b->barriers > comp) {
- struct pollfd pfd[2] = {
- { .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1],
- .events = POLLHUP },
- { .fd = b->them,
- .events = POLLIN }};
- uint64_t buf;
- int r;
-
- r = poll(pfd, 2, -1);
- if (r < 0 && IN_SET(errno, EAGAIN, EINTR))
- continue;
- else if (r < 0)
- goto error;
-
- if (pfd[1].revents) {
- ssize_t len;
-
- /* events on @them signal new data for us */
- len = read(b->them, &buf, sizeof(buf));
- if (len < 0 && IN_SET(errno, EAGAIN, EINTR))
- continue;
-
- if (len != sizeof(buf))
- goto error;
- } else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL))
- /* POLLHUP on the pipe tells us the other side exited.
- * We treat this as implicit abortion. But we only
- * handle it if there's no event on the eventfd. This
- * guarantees that exit-abortions do not overwrite real
- * barriers. */
- buf = BARRIER_ABORTION;
- else
- continue;
-
- /* lock if they aborted */
- if (buf >= (uint64_t)BARRIER_ABORTION) {
- if (barrier_i_aborted(b))
- b->barriers = BARRIER_WE_ABORTED;
- else
- b->barriers = BARRIER_THEY_ABORTED;
- } else if (!barrier_is_aborted(b))
- b->barriers -= buf;
- }
-
- return !barrier_they_aborted(b);
-
-error:
- /* If there is an unexpected error, we have to make this fatal. There
- * is no way we can recover from sync-errors. Therefore, we close the
- * pipe-ends and treat this as abortion. The other end will notice the
- * pipe-close and treat it as abortion, too. */
-
- safe_close_pair(b->pipe);
- b->barriers = BARRIER_WE_ABORTED;
- return false;
-}
-
-/**
- * barrier_place() - Place a new barrier
- * @b: barrier object
- *
- * This places a new barrier on the barrier object. If either side already
- * aborted, this is a no-op and returns "false". Otherwise, the barrier is
- * placed and this returns "true".
- *
- * Returns: true if barrier was placed, false if either side aborted.
- */
-bool barrier_place(Barrier *b) {
- assert(b);
-
- if (barrier_is_aborted(b))
- return false;
-
- barrier_write(b, BARRIER_SINGLE);
- return true;
-}
-
-/**
- * barrier_abort() - Abort the synchronization
- * @b: barrier object to abort
- *
- * This aborts the barrier-synchronization. If barrier_abort() was already
- * called on this side, this is a no-op. Otherwise, the barrier is put into the
- * ABORT-state and will stay there. The other side is notified about the
- * abortion. Any following attempt to place normal barriers or to wait on normal
- * barriers will return immediately as "false".
- *
- * You can wait for the other side to call barrier_abort(), too. Use
- * barrier_wait_abortion() for that.
- *
- * Returns: false if the other side already aborted, true otherwise.
- */
-bool barrier_abort(Barrier *b) {
- assert(b);
-
- barrier_write(b, BARRIER_ABORTION);
- return !barrier_they_aborted(b);
-}
-
-/**
- * barrier_wait_next() - Wait for the next barrier of the other side
- * @b: barrier to operate on
- *
- * This waits until the other side places its next barrier. This is independent
- * of any barrier-links and just waits for any next barrier of the other side.
- *
- * If either side aborted, this returns false.
- *
- * Returns: false if either side aborted, true otherwise.
- */
-bool barrier_wait_next(Barrier *b) {
- assert(b);
-
- if (barrier_is_aborted(b))
- return false;
-
- barrier_read(b, b->barriers - 1);
- return !barrier_is_aborted(b);
-}
-
-/**
- * barrier_wait_abortion() - Wait for the other side to abort
- * @b: barrier to operate on
- *
- * This waits until the other side called barrier_abort(). This can be called
- * regardless whether the local side already called barrier_abort() or not.
- *
- * If the other side has already aborted, this returns immediately.
- *
- * Returns: false if the local side aborted, true otherwise.
- */
-bool barrier_wait_abortion(Barrier *b) {
- assert(b);
-
- barrier_read(b, BARRIER_THEY_ABORTED);
- return !barrier_i_aborted(b);
-}
-
-/**
- * barrier_sync_next() - Wait for the other side to place a next linked barrier
- * @b: barrier to operate on
- *
- * This is like barrier_wait_next() and waits for the other side to call
- * barrier_place(). However, this only waits for linked barriers. That means, if
- * the other side already placed more barriers than (or as much as) we did, this
- * returns immediately instead of waiting.
- *
- * If either side aborted, this returns false.
- *
- * Returns: false if either side aborted, true otherwise.
- */
-bool barrier_sync_next(Barrier *b) {
- assert(b);
-
- if (barrier_is_aborted(b))
- return false;
-
- barrier_read(b, MAX((int64_t)0, b->barriers - 1));
- return !barrier_is_aborted(b);
-}
-
-/**
- * barrier_sync() - Wait for the other side to place as many barriers as we did
- * @b: barrier to operate on
- *
- * This is like barrier_sync_next() but waits for the other side to call
- * barrier_place() as often as we did (in total). If they already placed as much
- * as we did (or more), this returns immediately instead of waiting.
- *
- * If either side aborted, this returns false.
- *
- * Returns: false if either side aborted, true otherwise.
- */
-bool barrier_sync(Barrier *b) {
- assert(b);
-
- if (barrier_is_aborted(b))
- return false;
-
- barrier_read(b, 0);
- return !barrier_is_aborted(b);
-}
diff --git a/src/shared/barrier.h b/src/shared/barrier.h
deleted file mode 100644
index b895469..0000000
--- a/src/shared/barrier.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
-
- 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>
-
-#include "macro.h"
-
-/* See source file for an API description. */
-
-typedef struct Barrier Barrier;
-
-enum {
- BARRIER_SINGLE = 1LL,
- BARRIER_ABORTION = INT64_MAX,
-
- /* bias values to store state; keep @WE < @THEY < @I */
- BARRIER_BIAS = INT64_MIN,
- BARRIER_WE_ABORTED = BARRIER_BIAS + 1LL,
- BARRIER_THEY_ABORTED = BARRIER_BIAS + 2LL,
- BARRIER_I_ABORTED = BARRIER_BIAS + 3LL,
-};
-
-enum {
- BARRIER_PARENT,
- BARRIER_CHILD,
-};
-
-struct Barrier {
- int me;
- int them;
- int pipe[2];
- int64_t barriers;
-};
-
-#define BARRIER_NULL {-1, -1, {-1, -1}, 0}
-
-int barrier_create(Barrier *obj);
-void barrier_destroy(Barrier *b);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy);
-
-void barrier_set_role(Barrier *b, unsigned int role);
-
-bool barrier_place(Barrier *b);
-bool barrier_abort(Barrier *b);
-
-bool barrier_wait_next(Barrier *b);
-bool barrier_wait_abortion(Barrier *b);
-bool barrier_sync_next(Barrier *b);
-bool barrier_sync(Barrier *b);
-
-static inline bool barrier_i_aborted(Barrier *b) {
- return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED;
-}
-
-static inline bool barrier_they_aborted(Barrier *b) {
- return b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED;
-}
-
-static inline bool barrier_we_aborted(Barrier *b) {
- return b->barriers == BARRIER_WE_ABORTED;
-}
-
-static inline bool barrier_is_aborted(Barrier *b) {
- return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED;
-}
-
-static inline bool barrier_place_and_sync(Barrier *b) {
- (void) barrier_place(b);
- return barrier_sync(b);
-}
diff --git a/src/shared/blkid-util.h b/src/shared/blkid-util.h
deleted file mode 100644
index c689310..0000000
--- a/src/shared/blkid-util.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-#ifdef HAVE_BLKID
-#include <blkid/blkid.h>
-#endif
-
-#include "util.h"
-
-#ifdef HAVE_BLKID
-DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
-#define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep)
-#endif
diff --git a/src/shared/btrfs-ctree.h b/src/shared/btrfs-ctree.h
deleted file mode 100644
index d3ae573..0000000
--- a/src/shared/btrfs-ctree.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-#include "macro.h"
-#include "sparse-endian.h"
-
-/* Stolen from btrfs' ctree.h */
-
-struct btrfs_timespec {
- le64_t sec;
- le32_t nsec;
-} _packed_;
-
-struct btrfs_disk_key {
- le64_t objectid;
- uint8_t type;
- le64_t offset;
-} _packed_;
-
-struct btrfs_inode_item {
- le64_t generation;
- le64_t transid;
- le64_t size;
- le64_t nbytes;
- le64_t block_group;
- le32_t nlink;
- le32_t uid;
- le32_t gid;
- le32_t mode;
- le64_t rdev;
- le64_t flags;
- le64_t sequence;
- le64_t reserved[4];
- struct btrfs_timespec atime;
- struct btrfs_timespec ctime;
- struct btrfs_timespec mtime;
- struct btrfs_timespec otime;
-} _packed_;
-
-struct btrfs_root_item {
- struct btrfs_inode_item inode;
- le64_t generation;
- le64_t root_dirid;
- le64_t bytenr;
- le64_t byte_limit;
- le64_t bytes_used;
- le64_t last_snapshot;
- le64_t flags;
- le32_t refs;
- struct btrfs_disk_key drop_progress;
- uint8_t drop_level;
- uint8_t level;
- le64_t generation_v2;
- uint8_t uuid[BTRFS_UUID_SIZE];
- uint8_t parent_uuid[BTRFS_UUID_SIZE];
- uint8_t received_uuid[BTRFS_UUID_SIZE];
- le64_t ctransid;
- le64_t otransid;
- le64_t stransid;
- le64_t rtransid;
- struct btrfs_timespec ctime;
- struct btrfs_timespec otime;
- struct btrfs_timespec stime;
- struct btrfs_timespec rtime;
- le64_t reserved[8];
-} _packed_;
-
-#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
-
-struct btrfs_qgroup_info_item {
- le64_t generation;
- le64_t rfer;
- le64_t rfer_cmpr;
- le64_t excl;
- le64_t excl_cmpr;
-} _packed_;
-
-#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
-#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
-#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
-#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
-#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
-#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
-
-struct btrfs_qgroup_limit_item {
- le64_t flags;
- le64_t max_rfer;
- le64_t max_excl;
- le64_t rsv_rfer;
- le64_t rsv_excl;
-} _packed_;
-
-struct btrfs_root_ref {
- le64_t dirid;
- le64_t sequence;
- le16_t name_len;
-} _packed_;
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
deleted file mode 100644
index 49528db..0000000
--- a/src/shared/btrfs-util.c
+++ /dev/null
@@ -1,1152 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <sys/vfs.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_LINUX_BTRFS_H
-#include <linux/btrfs.h>
-#endif
-
-#include "missing.h"
-#include "util.h"
-#include "path-util.h"
-#include "macro.h"
-#include "copy.h"
-#include "selinux-util.h"
-#include "smack-util.h"
-#include "fileio.h"
-#include "btrfs-ctree.h"
-#include "btrfs-util.h"
-
-/* WARNING: Be careful with file system ioctls! When we get an fd, we
- * need to make sure it either refers to only a regular file or
- * directory, or that it is located on btrfs, before invoking any
- * btrfs ioctls. The ioctl numbers are reused by some device drivers
- * (such as DRM), and hence might have bad effects when invoked on
- * device nodes (that reference drivers) rather than fds to normal
- * files or directories. */
-
-static int validate_subvolume_name(const char *name) {
-
- if (!filename_is_valid(name))
- return -EINVAL;
-
- if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
- return -E2BIG;
-
- return 0;
-}
-
-static int open_parent(const char *path, int flags) {
- _cleanup_free_ char *parent = NULL;
- int r, fd;
-
- assert(path);
-
- r = path_get_parent(path, &parent);
- if (r < 0)
- return r;
-
- fd = open(parent, flags);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-static int extract_subvolume_name(const char *path, const char **subvolume) {
- const char *fn;
- int r;
-
- assert(path);
- assert(subvolume);
-
- fn = basename(path);
-
- r = validate_subvolume_name(fn);
- if (r < 0)
- return r;
-
- *subvolume = fn;
- return 0;
-}
-
-int btrfs_is_filesystem(int fd) {
- struct statfs sfs;
-
- assert(fd >= 0);
-
- if (fstatfs(fd, &sfs) < 0)
- return -errno;
-
- return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
-}
-
-int btrfs_is_subvol(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- /* On btrfs subvolumes always have the inode 256 */
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
- return 0;
-
- return btrfs_is_filesystem(fd);
-}
-
-int btrfs_subvol_make(const char *path) {
- struct btrfs_ioctl_vol_args args = {};
- _cleanup_close_ int fd = -1;
- const char *subvolume;
- int r;
-
- assert(path);
-
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return fd;
-
- strncpy(args.name, subvolume, sizeof(args.name)-1);
-
- if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_subvol_make_label(const char *path) {
- int r;
-
- assert(path);
-
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
- if (r < 0)
- return r;
-
- r = btrfs_subvol_make(path);
- mac_selinux_create_file_clear();
-
- if (r < 0)
- return r;
-
- return mac_smack_fix(path, false, false);
-}
-
-int btrfs_subvol_set_read_only_fd(int fd, bool b) {
- uint64_t flags, nflags;
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
- return -EINVAL;
-
- if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
- return -errno;
-
- if (b)
- nflags = flags | BTRFS_SUBVOL_RDONLY;
- else
- nflags = flags & ~BTRFS_SUBVOL_RDONLY;
-
- if (flags == nflags)
- return 0;
-
- if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_subvol_set_read_only(const char *path, bool b) {
- _cleanup_close_ int fd = -1;
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
-
- return btrfs_subvol_set_read_only_fd(fd, b);
-}
-
-int btrfs_subvol_get_read_only_fd(int fd) {
- uint64_t flags;
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
- return -EINVAL;
-
- if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
- return -errno;
-
- return !!(flags & BTRFS_SUBVOL_RDONLY);
-}
-
-int btrfs_reflink(int infd, int outfd) {
- struct stat st;
- int r;
-
- assert(infd >= 0);
- assert(outfd >= 0);
-
- /* Make sure we invoke the ioctl on a regular file, so that no
- * device driver accidentally gets it. */
-
- if (fstat(outfd, &st) < 0)
- return -errno;
-
- if (!S_ISREG(st.st_mode))
- return -EINVAL;
-
- r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
- struct btrfs_ioctl_clone_range_args args = {
- .src_fd = infd,
- .src_offset = in_offset,
- .src_length = sz,
- .dest_offset = out_offset,
- };
- struct stat st;
- int r;
-
- assert(infd >= 0);
- assert(outfd >= 0);
- assert(sz > 0);
-
- if (fstat(outfd, &st) < 0)
- return -errno;
-
- if (!S_ISREG(st.st_mode))
- return -EINVAL;
-
- r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_get_block_device_fd(int fd, dev_t *dev) {
- struct btrfs_ioctl_fs_info_args fsi = {};
- uint64_t id;
- int r;
-
- assert(fd >= 0);
- assert(dev);
-
- r = btrfs_is_filesystem(fd);
- if (r < 0)
- return r;
- if (!r)
- return -ENOTTY;
-
- if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
- return -errno;
-
- /* We won't do this for btrfs RAID */
- if (fsi.num_devices != 1)
- return 0;
-
- for (id = 1; id <= fsi.max_id; id++) {
- struct btrfs_ioctl_dev_info_args di = {
- .devid = id,
- };
- struct stat st;
-
- if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
- if (errno == ENODEV)
- continue;
-
- return -errno;
- }
-
- if (stat((char*) di.path, &st) < 0)
- return -errno;
-
- if (!S_ISBLK(st.st_mode))
- return -ENODEV;
-
- if (major(st.st_rdev) == 0)
- return -ENODEV;
-
- *dev = st.st_rdev;
- return 1;
- }
-
- return -ENODEV;
-}
-
-int btrfs_get_block_device(const char *path, dev_t *dev) {
- _cleanup_close_ int fd = -1;
-
- assert(path);
- assert(dev);
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return btrfs_get_block_device_fd(fd, dev);
-}
-
-int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
- struct btrfs_ioctl_ino_lookup_args args = {
- .objectid = BTRFS_FIRST_FREE_OBJECTID
- };
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- r = btrfs_is_filesystem(fd);
- if (r < 0)
- return r;
- if (!r)
- return -ENOTTY;
-
- if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
- return -errno;
-
- *ret = args.treeid;
- return 0;
-}
-
-static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
- assert(args);
-
- /* the objectid, type, offset together make up the btrfs key,
- * which is considered a single 136byte integer when
- * comparing. This call increases the counter by one, dealing
- * with the overflow between the overflows */
-
- if (args->key.min_offset < (uint64_t) -1) {
- args->key.min_offset++;
- return true;
- }
-
- if (args->key.min_type < (uint8_t) -1) {
- args->key.min_type++;
- args->key.min_offset = 0;
- return true;
- }
-
- if (args->key.min_objectid < (uint64_t) -1) {
- args->key.min_objectid++;
- args->key.min_offset = 0;
- args->key.min_type = 0;
- return true;
- }
-
- return 0;
-}
-
-static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
- assert(args);
- assert(h);
-
- args->key.min_objectid = h->objectid;
- args->key.min_type = h->type;
- args->key.min_offset = h->offset;
-}
-
-static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
- assert(args);
-
- /* Compare min and max */
-
- if (args->key.min_objectid < args->key.max_objectid)
- return -1;
- if (args->key.min_objectid > args->key.max_objectid)
- return 1;
-
- if (args->key.min_type < args->key.max_type)
- return -1;
- if (args->key.min_type > args->key.max_type)
- return 1;
-
- if (args->key.min_offset < args->key.max_offset)
- return -1;
- if (args->key.min_offset > args->key.max_offset)
- return 1;
-
- return 0;
-}
-
-#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
- for ((i) = 0, \
- (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
- (i) < (args).key.nr_items; \
- (i)++, \
- (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
-
-#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
- ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
-
-int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
- struct btrfs_ioctl_search_args args = {
- /* Tree of tree roots */
- .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
-
- /* Look precisely for the subvolume items */
- .key.min_type = BTRFS_ROOT_ITEM_KEY,
- .key.max_type = BTRFS_ROOT_ITEM_KEY,
-
- .key.min_offset = 0,
- .key.max_offset = (uint64_t) -1,
-
- /* No restrictions on the other components */
- .key.min_transid = 0,
- .key.max_transid = (uint64_t) -1,
- };
-
- uint64_t subvol_id;
- bool found = false;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- r = btrfs_subvol_get_id_fd(fd, &subvol_id);
- if (r < 0)
- return r;
-
- args.key.min_objectid = args.key.max_objectid = subvol_id;
-
- while (btrfs_ioctl_search_args_compare(&args) <= 0) {
- const struct btrfs_ioctl_search_header *sh;
- unsigned i;
-
- args.key.nr_items = 256;
- if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
- return -errno;
-
- if (args.key.nr_items <= 0)
- break;
-
- FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
-
- const struct btrfs_root_item *ri;
-
- /* Make sure we start the next search at least from this entry */
- btrfs_ioctl_search_args_set(&args, sh);
-
- if (sh->objectid != subvol_id)
- continue;
- if (sh->type != BTRFS_ROOT_ITEM_KEY)
- continue;
-
- /* Older versions of the struct lacked the otime setting */
- if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
- continue;
-
- ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
-
- ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
- (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
-
- ret->subvol_id = subvol_id;
- ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
-
- assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
- memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
- memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
-
- found = true;
- goto finish;
- }
-
- /* Increase search key by one, to read the next item, if we can. */
- if (!btrfs_ioctl_search_args_inc(&args))
- break;
- }
-
-finish:
- if (!found)
- return -ENODATA;
-
- return 0;
-}
-
-int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
-
- struct btrfs_ioctl_search_args args = {
- /* Tree of quota items */
- .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
-
- /* The object ID is always 0 */
- .key.min_objectid = 0,
- .key.max_objectid = 0,
-
- /* Look precisely for the quota items */
- .key.min_type = BTRFS_QGROUP_STATUS_KEY,
- .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
-
- /* No restrictions on the other components */
- .key.min_transid = 0,
- .key.max_transid = (uint64_t) -1,
- };
-
- uint64_t subvol_id;
- bool found_info = false, found_limit = false;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- r = btrfs_subvol_get_id_fd(fd, &subvol_id);
- if (r < 0)
- return r;
-
- args.key.min_offset = args.key.max_offset = subvol_id;
-
- while (btrfs_ioctl_search_args_compare(&args) <= 0) {
- const struct btrfs_ioctl_search_header *sh;
- unsigned i;
-
- args.key.nr_items = 256;
- if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
- return -errno;
-
- if (args.key.nr_items <= 0)
- break;
-
- FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
-
- /* Make sure we start the next search at least from this entry */
- btrfs_ioctl_search_args_set(&args, sh);
-
- if (sh->objectid != 0)
- continue;
- if (sh->offset != subvol_id)
- continue;
-
- if (sh->type == BTRFS_QGROUP_INFO_KEY) {
- const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
-
- ret->referenced = le64toh(qii->rfer);
- ret->exclusive = le64toh(qii->excl);
-
- found_info = true;
-
- } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
- const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
-
- ret->referenced_max = le64toh(qli->max_rfer);
- ret->exclusive_max = le64toh(qli->max_excl);
-
- if (ret->referenced_max == 0)
- ret->referenced_max = (uint64_t) -1;
- if (ret->exclusive_max == 0)
- ret->exclusive_max = (uint64_t) -1;
-
- found_limit = true;
- }
-
- if (found_info && found_limit)
- goto finish;
- }
-
- /* Increase search key by one, to read the next item, if we can. */
- if (!btrfs_ioctl_search_args_inc(&args))
- break;
- }
-
-finish:
- if (!found_limit && !found_info)
- return -ENODATA;
-
- if (!found_info) {
- ret->referenced = (uint64_t) -1;
- ret->exclusive = (uint64_t) -1;
- }
-
- if (!found_limit) {
- ret->referenced_max = (uint64_t) -1;
- ret->exclusive_max = (uint64_t) -1;
- }
-
- return 0;
-}
-
-int btrfs_defrag_fd(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISREG(st.st_mode))
- return -EINVAL;
-
- if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_defrag(const char *p) {
- _cleanup_close_ int fd = -1;
-
- fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return btrfs_defrag_fd(fd);
-}
-
-int btrfs_quota_enable_fd(int fd, bool b) {
- struct btrfs_ioctl_quota_ctl_args args = {
- .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
- };
- int r;
-
- assert(fd >= 0);
-
- r = btrfs_is_filesystem(fd);
- if (r < 0)
- return r;
- if (!r)
- return -ENOTTY;
-
- if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_quota_enable(const char *path, bool b) {
- _cleanup_close_ int fd = -1;
-
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return btrfs_quota_enable_fd(fd, b);
-}
-
-int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
- struct btrfs_ioctl_qgroup_limit_args args = {
- .lim.max_rfer =
- referenced_max == (uint64_t) -1 ? 0 :
- referenced_max == 0 ? 1 : referenced_max,
- .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
- };
- int r;
-
- assert(fd >= 0);
-
- r = btrfs_is_filesystem(fd);
- if (r < 0)
- return r;
- if (!r)
- return -ENOTTY;
-
- if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
- _cleanup_close_ int fd = -1;
-
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return btrfs_quota_limit_fd(fd, referenced_max);
-}
-
-int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
- struct btrfs_ioctl_vol_args args = {};
- _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
- _cleanup_close_ int loop_fd = -1, backing_fd = -1;
- struct stat st;
- dev_t dev = 0;
- int r;
-
- /* btrfs cannot handle file systems < 16M, hence use this as minimum */
- if (new_size < 16*1024*1024)
- new_size = 16*1024*1024;
-
- r = btrfs_get_block_device_fd(fd, &dev);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENODEV;
-
- if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
- return -ENOMEM;
- r = read_one_line_file(p, &backing);
- if (r == -ENOENT)
- return -ENODEV;
- if (r < 0)
- return r;
- if (isempty(backing) || !path_is_absolute(backing))
- return -ENODEV;
-
- backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (backing_fd < 0)
- return -errno;
-
- if (fstat(backing_fd, &st) < 0)
- return -errno;
- if (!S_ISREG(st.st_mode))
- return -ENODEV;
-
- if (new_size == (uint64_t) st.st_size)
- return 0;
-
- if (grow_only && new_size < (uint64_t) st.st_size)
- return -EINVAL;
-
- if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
- return -ENOMEM;
- loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (loop_fd < 0)
- return -errno;
-
- if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
- return -EINVAL;
-
- if (new_size < (uint64_t) st.st_size) {
- /* Decrease size: first decrease btrfs size, then shorten loopback */
- if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
- return -errno;
- }
-
- if (ftruncate(backing_fd, new_size) < 0)
- return -errno;
-
- if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
- return -errno;
-
- if (new_size > (uint64_t) st.st_size) {
- /* Increase size: first enlarge loopback, then increase btrfs size */
- if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
- return -errno;
- }
-
- /* Make sure the free disk space is correctly updated for both file systems */
- (void) fsync(fd);
- (void) fsync(backing_fd);
-
- return 1;
-}
-
-int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
- _cleanup_close_ int fd = -1;
-
- fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return btrfs_resize_loopback_fd(fd, new_size, grow_only);
-}
-
-static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
- struct btrfs_ioctl_search_args args = {
- .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
-
- .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
- .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
-
- .key.min_type = BTRFS_ROOT_BACKREF_KEY,
- .key.max_type = BTRFS_ROOT_BACKREF_KEY,
-
- .key.min_transid = 0,
- .key.max_transid = (uint64_t) -1,
- };
-
- struct btrfs_ioctl_vol_args vol_args = {};
- _cleanup_close_ int subvol_fd = -1;
- struct stat st;
- bool made_writable = false;
- int r;
-
- assert(fd >= 0);
- assert(subvolume);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode))
- return -EINVAL;
-
- /* First, try to remove the subvolume. If it happens to be
- * already empty, this will just work. */
- strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
- if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
- return 0;
- if (!recursive || errno != ENOTEMPTY)
- return -errno;
-
- /* OK, the subvolume is not empty, let's look for child
- * subvolumes, and remove them, first */
- subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (subvol_fd < 0)
- return -errno;
-
- if (subvol_id == 0) {
- r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
- if (r < 0)
- return r;
- }
-
- args.key.min_offset = args.key.max_offset = subvol_id;
-
- while (btrfs_ioctl_search_args_compare(&args) <= 0) {
- const struct btrfs_ioctl_search_header *sh;
- unsigned i;
-
- args.key.nr_items = 256;
- if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
- return -errno;
-
- if (args.key.nr_items <= 0)
- break;
-
- FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
- _cleanup_free_ char *p = NULL;
- const struct btrfs_root_ref *ref;
- struct btrfs_ioctl_ino_lookup_args ino_args;
-
- btrfs_ioctl_search_args_set(&args, sh);
-
- if (sh->type != BTRFS_ROOT_BACKREF_KEY)
- continue;
- if (sh->offset != subvol_id)
- continue;
-
- ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
-
- p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
- if (!p)
- return -ENOMEM;
-
- zero(ino_args);
- ino_args.treeid = subvol_id;
- ino_args.objectid = htole64(ref->dirid);
-
- if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
- return -errno;
-
- if (!made_writable) {
- r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
- if (r < 0)
- return r;
-
- made_writable = true;
- }
-
- if (isempty(ino_args.name))
- /* Subvolume is in the top-level
- * directory of the subvolume. */
- r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
- else {
- _cleanup_close_ int child_fd = -1;
-
- /* Subvolume is somewhere further down,
- * hence we need to open the
- * containing directory first */
-
- child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (child_fd < 0)
- return -errno;
-
- r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
- }
- if (r < 0)
- return r;
- }
-
- /* Increase search key by one, to read the next item, if we can. */
- if (!btrfs_ioctl_search_args_inc(&args))
- break;
- }
-
- /* OK, the child subvolumes should all be gone now, let's try
- * again to remove the subvolume */
- if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
- return -errno;
-
- return 0;
-}
-
-int btrfs_subvol_remove(const char *path, bool recursive) {
- _cleanup_close_ int fd = -1;
- const char *subvolume;
- int r;
-
- assert(path);
-
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return fd;
-
- return subvol_remove_children(fd, subvolume, 0, recursive);
-}
-
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
- return subvol_remove_children(fd, subvolume, 0, recursive);
-}
-
-static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
-
- struct btrfs_ioctl_search_args args = {
- .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
-
- .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
- .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
-
- .key.min_type = BTRFS_ROOT_BACKREF_KEY,
- .key.max_type = BTRFS_ROOT_BACKREF_KEY,
-
- .key.min_transid = 0,
- .key.max_transid = (uint64_t) -1,
- };
-
- struct btrfs_ioctl_vol_args_v2 vol_args = {
- .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
- .fd = old_fd,
- };
- int r;
- _cleanup_close_ int subvolume_fd = -1;
-
- assert(old_fd >= 0);
- assert(new_fd >= 0);
- assert(subvolume);
-
- strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
- vol_args.fd = old_fd;
-
- if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
- return -errno;
-
- if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
- return 0;
-
- if (subvol_id == 0) {
- r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
- if (r < 0)
- return r;
- }
-
- args.key.min_offset = args.key.max_offset = subvol_id;
-
- while (btrfs_ioctl_search_args_compare(&args) <= 0) {
- const struct btrfs_ioctl_search_header *sh;
- unsigned i;
-
- args.key.nr_items = 256;
- if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
- return -errno;
-
- if (args.key.nr_items <= 0)
- break;
-
- FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
- _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
- struct btrfs_ioctl_ino_lookup_args ino_args;
- const struct btrfs_root_ref *ref;
- _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
-
- btrfs_ioctl_search_args_set(&args, sh);
-
- if (sh->type != BTRFS_ROOT_BACKREF_KEY)
- continue;
- if (sh->offset != subvol_id)
- continue;
-
- ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
-
- p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
- if (!p)
- return -ENOMEM;
-
- zero(ino_args);
- ino_args.treeid = subvol_id;
- ino_args.objectid = htole64(ref->dirid);
-
- if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
- return -errno;
-
- /* The kernel returns an empty name if the
- * subvolume is in the top-level directory,
- * and otherwise appends a slash, so that we
- * can just concatenate easily here, without
- * adding a slash. */
- c = strappend(ino_args.name, p);
- if (!c)
- return -ENOMEM;
-
- old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (old_child_fd < 0)
- return -errno;
-
- np = strjoin(subvolume, "/", ino_args.name, NULL);
- if (!np)
- return -ENOMEM;
-
- new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (new_child_fd < 0)
- return -errno;
-
- if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
- /* If the snapshot is read-only we
- * need to mark it writable
- * temporarily, to put the subsnapshot
- * into place. */
-
- if (subvolume_fd < 0) {
- subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (subvolume_fd < 0)
- return -errno;
- }
-
- r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
- if (r < 0)
- return r;
- }
-
- /* When btrfs clones the subvolumes, child
- * subvolumes appear as directories. Remove
- * them, so that we can create a new snapshot
- * in their place */
- if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
- int k = -errno;
-
- if (flags & BTRFS_SNAPSHOT_READ_ONLY)
- (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
-
- return k;
- }
-
- r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
-
- /* Restore the readonly flag */
- if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
- int k;
-
- k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
- if (r >= 0 && k < 0)
- return k;
- }
-
- if (r < 0)
- return r;
- }
-
- /* Increase search key by one, to read the next item, if we can. */
- if (!btrfs_ioctl_search_args_inc(&args))
- break;
- }
-
- return 0;
-}
-
-int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
- _cleanup_close_ int new_fd = -1;
- const char *subvolume;
- int r;
-
- assert(old_fd >= 0);
- assert(new_path);
-
- r = btrfs_is_subvol(old_fd);
- if (r < 0)
- return r;
- if (r == 0) {
- if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
- return -EISDIR;
-
- r = btrfs_subvol_make(new_path);
- if (r < 0)
- return r;
-
- r = copy_directory_fd(old_fd, new_path, true);
- if (r < 0) {
- btrfs_subvol_remove(new_path, false);
- return r;
- }
-
- if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
- r = btrfs_subvol_set_read_only(new_path, true);
- if (r < 0) {
- btrfs_subvol_remove(new_path, false);
- return r;
- }
- }
-
- return 0;
- }
-
- r = extract_subvolume_name(new_path, &subvolume);
- if (r < 0)
- return r;
-
- new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (new_fd < 0)
- return new_fd;
-
- return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
-}
-
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
- _cleanup_close_ int old_fd = -1;
-
- assert(old_path);
- assert(new_path);
-
- old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (old_fd < 0)
- return -errno;
-
- return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
-}
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
deleted file mode 100644
index a7eb895..0000000
--- a/src/shared/btrfs-util.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-#include "time-util.h"
-
-typedef struct BtrfsSubvolInfo {
- uint64_t subvol_id;
- usec_t otime;
-
- sd_id128_t uuid;
- sd_id128_t parent_uuid;
-
- bool read_only;
-} BtrfsSubvolInfo;
-
-typedef struct BtrfsQuotaInfo {
- uint64_t referenced;
- uint64_t exclusive;
- uint64_t referenced_max;
- uint64_t exclusive_max;
-} BtrfsQuotaInfo;
-
-typedef enum BtrfsSnapshotFlags {
- BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
- BTRFS_SNAPSHOT_READ_ONLY = 2,
- BTRFS_SNAPSHOT_RECURSIVE = 4,
-} BtrfsSnapshotFlags;
-
-int btrfs_is_filesystem(int fd);
-int btrfs_is_subvol(int fd);
-
-int btrfs_subvol_make(const char *path);
-int btrfs_subvol_make_label(const char *path);
-
-int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
-
-int btrfs_subvol_set_read_only_fd(int fd, bool b);
-int btrfs_subvol_set_read_only(const char *path, bool b);
-int btrfs_subvol_get_read_only_fd(int fd);
-int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
-int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
-int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
-
-int btrfs_reflink(int infd, int outfd);
-int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
-
-int btrfs_get_block_device_fd(int fd, dev_t *dev);
-int btrfs_get_block_device(const char *path, dev_t *dev);
-
-int btrfs_defrag_fd(int fd);
-int btrfs_defrag(const char *p);
-
-int btrfs_quota_enable_fd(int fd, bool b);
-int btrfs_quota_enable(const char *path, bool b);
-
-int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
-int btrfs_quota_limit(const char *path, uint64_t referenced_max);
-
-int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
-int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
-
-int btrfs_subvol_remove(const char *path, bool recursive);
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
diff --git a/src/shared/build.h b/src/shared/build.h
deleted file mode 100644
index 24873ab..0000000
--- a/src/shared/build.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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/>.
-***/
-
-#ifdef HAVE_PAM
-#define _PAM_FEATURE_ "+PAM"
-#else
-#define _PAM_FEATURE_ "-PAM"
-#endif
-
-#ifdef HAVE_AUDIT
-#define _AUDIT_FEATURE_ "+AUDIT"
-#else
-#define _AUDIT_FEATURE_ "-AUDIT"
-#endif
-
-#ifdef HAVE_SELINUX
-#define _SELINUX_FEATURE_ "+SELINUX"
-#else
-#define _SELINUX_FEATURE_ "-SELINUX"
-#endif
-
-#ifdef HAVE_APPARMOR
-#define _APPARMOR_FEATURE_ "+APPARMOR"
-#else
-#define _APPARMOR_FEATURE_ "-APPARMOR"
-#endif
-
-#ifdef HAVE_IMA
-#define _IMA_FEATURE_ "+IMA"
-#else
-#define _IMA_FEATURE_ "-IMA"
-#endif
-
-#ifdef HAVE_SMACK
-#define _SMACK_FEATURE_ "+SMACK"
-#else
-#define _SMACK_FEATURE_ "-SMACK"
-#endif
-
-#ifdef HAVE_SYSV_COMPAT
-#define _SYSVINIT_FEATURE_ "+SYSVINIT"
-#else
-#define _SYSVINIT_FEATURE_ "-SYSVINIT"
-#endif
-
-#ifdef HAVE_UTMP
-#define _UTMP_FEATURE_ "+UTMP"
-#else
-#define _UTMP_FEATURE_ "-UTMP"
-#endif
-
-#ifdef HAVE_LIBCRYPTSETUP
-#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP"
-#else
-#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP"
-#endif
-
-#ifdef HAVE_GCRYPT
-#define _GCRYPT_FEATURE_ "+GCRYPT"
-#else
-#define _GCRYPT_FEATURE_ "-GCRYPT"
-#endif
-
-#ifdef HAVE_GNUTLS
-#define _GNUTLS_FEATURE_ "+GNUTLS"
-#else
-#define _GNUTLS_FEATURE_ "-GNUTLS"
-#endif
-
-#ifdef HAVE_ACL
-#define _ACL_FEATURE_ "+ACL"
-#else
-#define _ACL_FEATURE_ "-ACL"
-#endif
-
-#ifdef HAVE_XZ
-#define _XZ_FEATURE_ "+XZ"
-#else
-#define _XZ_FEATURE_ "-XZ"
-#endif
-
-#ifdef HAVE_LZ4
-#define _LZ4_FEATURE_ "+LZ4"
-#else
-#define _LZ4_FEATURE_ "-LZ4"
-#endif
-
-#ifdef HAVE_SECCOMP
-#define _SECCOMP_FEATURE_ "+SECCOMP"
-#else
-#define _SECCOMP_FEATURE_ "-SECCOMP"
-#endif
-
-#ifdef HAVE_BLKID
-#define _BLKID_FEATURE_ "+BLKID"
-#else
-#define _BLKID_FEATURE_ "-BLKID"
-#endif
-
-#ifdef HAVE_ELFUTILS
-#define _ELFUTILS_FEATURE_ "+ELFUTILS"
-#else
-#define _ELFUTILS_FEATURE_ "-ELFUTILS"
-#endif
-
-#ifdef HAVE_KMOD
-#define _KMOD_FEATURE_ "+KMOD"
-#else
-#define _KMOD_FEATURE_ "-KMOD"
-#endif
-
-#ifdef HAVE_LIBIDN
-#define _IDN_FEATURE_ "+IDN"
-#else
-#define _IDN_FEATURE_ "-IDN"
-#endif
-
-#define SYSTEMD_FEATURES \
- _PAM_FEATURE_ " " \
- _AUDIT_FEATURE_ " " \
- _SELINUX_FEATURE_ " " \
- _IMA_FEATURE_ " " \
- _APPARMOR_FEATURE_ " " \
- _SMACK_FEATURE_ " " \
- _SYSVINIT_FEATURE_ " " \
- _UTMP_FEATURE_ " " \
- _LIBCRYPTSETUP_FEATURE_ " " \
- _GCRYPT_FEATURE_ " " \
- _GNUTLS_FEATURE_ " " \
- _ACL_FEATURE_ " " \
- _XZ_FEATURE_ " " \
- _LZ4_FEATURE_ " " \
- _SECCOMP_FEATURE_ " " \
- _BLKID_FEATURE_ " " \
- _ELFUTILS_FEATURE_ " " \
- _KMOD_FEATURE_ " " \
- _IDN_FEATURE_
diff --git a/src/shared/bus-label.c b/src/shared/bus-label.c
deleted file mode 100644
index ccc9f2b..0000000
--- a/src/shared/bus-label.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 "util.h"
-#include "macro.h"
-
-#include "bus-label.h"
-
-char *bus_label_escape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert_return(s, NULL);
-
- /* Escapes all chars that D-Bus' object path cannot deal
- * with. Can be reversed with bus_path_unescape(). We special
- * case the empty string. */
-
- if (*s == 0)
- return strdup("_");
-
- r = new(char, strlen(s)*3 + 1);
- if (!r)
- return NULL;
-
- for (f = s, t = r; *f; f++) {
-
- /* Escape everything that is not a-zA-Z0-9. We also
- * escape 0-9 if it's the first character */
-
- if (!(*f >= 'A' && *f <= 'Z') &&
- !(*f >= 'a' && *f <= 'z') &&
- !(f > s && *f >= '0' && *f <= '9')) {
- *(t++) = '_';
- *(t++) = hexchar(*f >> 4);
- *(t++) = hexchar(*f);
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *bus_label_unescape_n(const char *f, size_t l) {
- char *r, *t;
- size_t i;
-
- assert_return(f, NULL);
-
- /* Special case for the empty string */
- if (l == 1 && *f == '_')
- return strdup("");
-
- r = new(char, l + 1);
- if (!r)
- return NULL;
-
- for (i = 0, t = r; i < l; ++i) {
- if (f[i] == '_') {
- int a, b;
-
- if (l - i < 3 ||
- (a = unhexchar(f[i + 1])) < 0 ||
- (b = unhexchar(f[i + 2])) < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '_';
- } else {
- *(t++) = (char) ((a << 4) | b);
- i += 2;
- }
- } else
- *(t++) = f[i];
- }
-
- *t = 0;
-
- return r;
-}
diff --git a/src/shared/bus-label.h b/src/shared/bus-label.h
deleted file mode 100644
index ed1dc4e..0000000
--- a/src/shared/bus-label.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 <string.h>
-
-char *bus_label_escape(const char *s);
-char *bus_label_unescape_n(const char *f, size_t l);
-
-static inline char *bus_label_unescape(const char *f) {
- return bus_label_unescape_n(f, f ? strlen(f) : 0);
-}
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
new file mode 100644
index 0000000..8fcc289
--- /dev/null
+++ b/src/shared/bus-util.c
@@ -0,0 +1,2060 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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/socket.h>
+
+#include "sd-daemon.h"
+#include "sd-event.h"
+#include "util.h"
+#include "strv.h"
+#include "macro.h"
+#include "def.h"
+#include "path-util.h"
+#include "missing.h"
+#include "set.h"
+#include "signal-util.h"
+#include "unit-name.h"
+
+#include "sd-bus.h"
+#include "bus-error.h"
+#include "bus-label.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "bus-internal.h"
+
+static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ sd_event *e = userdata;
+
+ assert(m);
+ assert(e);
+
+ sd_bus_close(sd_bus_message_get_bus(m));
+ sd_event_exit(e, 0);
+
+ return 1;
+}
+
+int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
+ _cleanup_free_ char *match = NULL;
+ const char *unique;
+ int r;
+
+ assert(e);
+ assert(bus);
+ assert(name);
+
+ /* We unregister the name here and then wait for the
+ * NameOwnerChanged signal for this event to arrive before we
+ * quit. We do this in order to make sure that any queued
+ * requests are still processed before we really exit. */
+
+ r = sd_bus_get_unique_name(bus, &unique);
+ if (r < 0)
+ return r;
+
+ r = asprintf(&match,
+ "sender='org.freedesktop.DBus',"
+ "type='signal',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "path='/org/freedesktop/DBus',"
+ "arg0='%s',"
+ "arg1='%s',"
+ "arg2=''", name, unique);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_release_name(bus, name);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int bus_event_loop_with_idle(
+ sd_event *e,
+ sd_bus *bus,
+ const char *name,
+ usec_t timeout,
+ check_idle_t check_idle,
+ void *userdata) {
+ bool exiting = false;
+ int r, code;
+
+ assert(e);
+ assert(bus);
+ assert(name);
+
+ for (;;) {
+ bool idle;
+
+ r = sd_event_get_state(e);
+ if (r < 0)
+ return r;
+ if (r == SD_EVENT_FINISHED)
+ break;
+
+ if (check_idle)
+ idle = check_idle(userdata);
+ else
+ idle = true;
+
+ r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
+ if (r < 0)
+ return r;
+
+ if (r == 0 && !exiting && idle) {
+
+ r = sd_bus_try_close(bus);
+ if (r == -EBUSY)
+ continue;
+
+ /* Fallback for dbus1 connections: we
+ * unregister the name and wait for the
+ * response to come through for it */
+ if (r == -EOPNOTSUPP) {
+
+ /* Inform the service manager that we
+ * are going down, so that it will
+ * queue all further start requests,
+ * instead of assuming we are already
+ * running. */
+ sd_notify(false, "STOPPING=1");
+
+ r = bus_async_unregister_and_exit(e, bus, name);
+ if (r < 0)
+ return r;
+
+ exiting = true;
+ continue;
+ }
+
+ if (r < 0)
+ return r;
+
+ sd_event_exit(e, 0);
+ break;
+ }
+ }
+
+ r = sd_event_get_exit_code(e, &code);
+ if (r < 0)
+ return r;
+
+ return code;
+}
+
+int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
+ _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
+ int r, has_owner = 0;
+
+ assert(c);
+ assert(name);
+
+ r = sd_bus_call_method(c,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/dbus",
+ "org.freedesktop.DBus",
+ "NameHasOwner",
+ error,
+ &rep,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(rep, 'b', &has_owner);
+ if (r < 0)
+ return sd_bus_error_set_errno(error, r);
+
+ return has_owner;
+}
+
+static int check_good_user(sd_bus_message *m, uid_t good_user) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ uid_t sender_uid;
+ int r;
+
+ assert(m);
+
+ if (good_user == UID_INVALID)
+ return 0;
+
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ /* Don't trust augmented credentials for authorization */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
+
+ r = sd_bus_creds_get_euid(creds, &sender_uid);
+ if (r < 0)
+ return r;
+
+ return sender_uid == good_user;
+}
+
+int bus_test_polkit(
+ sd_bus_message *call,
+ int capability,
+ const char *action,
+ uid_t good_user,
+ bool *_challenge,
+ sd_bus_error *e) {
+
+ int r;
+
+ assert(call);
+ assert(action);
+
+ /* Tests non-interactively! */
+
+ r = check_good_user(call, good_user);
+ if (r != 0)
+ return r;
+
+ r = sd_bus_query_sender_privilege(call, capability);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ return 1;
+#ifdef ENABLE_POLKIT
+ else {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ int authorized = false, challenge = false;
+ const char *sender;
+
+ sender = sd_bus_message_get_sender(call);
+ if (!sender)
+ return -EBADMSG;
+
+ r = sd_bus_call_method(
+ call->bus,
+ "org.freedesktop.PolicyKit1",
+ "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority",
+ "CheckAuthorization",
+ e,
+ &reply,
+ "(sa{sv})sa{ss}us",
+ "system-bus-name", 1, "name", "s", sender,
+ action,
+ 0,
+ 0,
+ "");
+
+ if (r < 0) {
+ /* Treat no PK available as access denied */
+ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
+ sd_bus_error_free(e);
+ return -EACCES;
+ }
+
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
+ if (r < 0)
+ return r;
+
+ if (authorized)
+ return 1;
+
+ if (_challenge) {
+ *_challenge = challenge;
+ return 0;
+ }
+ }
+#endif
+
+ return -EACCES;
+}
+
+#ifdef ENABLE_POLKIT
+
+typedef struct AsyncPolkitQuery {
+ sd_bus_message *request, *reply;
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ sd_bus_slot *slot;
+ Hashmap *registry;
+} AsyncPolkitQuery;
+
+static void async_polkit_query_free(AsyncPolkitQuery *q) {
+
+ if (!q)
+ return;
+
+ sd_bus_slot_unref(q->slot);
+
+ if (q->registry && q->request)
+ hashmap_remove(q->registry, q->request);
+
+ sd_bus_message_unref(q->request);
+ sd_bus_message_unref(q->reply);
+
+ free(q);
+}
+
+static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+ AsyncPolkitQuery *q = userdata;
+ int r;
+
+ assert(reply);
+ assert(q);
+
+ q->slot = sd_bus_slot_unref(q->slot);
+ q->reply = sd_bus_message_ref(reply);
+
+ r = sd_bus_message_rewind(q->request, true);
+ if (r < 0) {
+ r = sd_bus_reply_method_errno(q->request, r, NULL);
+ goto finish;
+ }
+
+ r = q->callback(q->request, q->userdata, &error_buffer);
+ r = bus_maybe_reply_error(q->request, r, &error_buffer);
+
+finish:
+ async_polkit_query_free(q);
+
+ return r;
+}
+
+#endif
+
+int bus_verify_polkit_async(
+ sd_bus_message *call,
+ int capability,
+ const char *action,
+ bool interactive,
+ uid_t good_user,
+ Hashmap **registry,
+ sd_bus_error *error) {
+
+#ifdef ENABLE_POLKIT
+ _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
+ AsyncPolkitQuery *q;
+ const char *sender;
+ sd_bus_message_handler_t callback;
+ void *userdata;
+ int c;
+#endif
+ int r;
+
+ assert(call);
+ assert(action);
+ assert(registry);
+
+ r = check_good_user(call, good_user);
+ if (r != 0)
+ return r;
+
+#ifdef ENABLE_POLKIT
+ q = hashmap_get(*registry, call);
+ if (q) {
+ int authorized, challenge;
+
+ /* This is the second invocation of this function, and
+ * there's already a response from polkit, let's
+ * process it */
+ assert(q->reply);
+
+ if (sd_bus_message_is_method_error(q->reply, NULL)) {
+ const sd_bus_error *e;
+
+ /* Copy error from polkit reply */
+ e = sd_bus_message_get_error(q->reply);
+ sd_bus_error_copy(error, e);
+
+ /* Treat no PK available as access denied */
+ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
+ return -EACCES;
+
+ return -sd_bus_error_get_errno(e);
+ }
+
+ r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
+ if (r >= 0)
+ r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
+
+ if (r < 0)
+ return r;
+
+ if (authorized)
+ return 1;
+
+ if (challenge)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
+
+ return -EACCES;
+ }
+#endif
+
+ r = sd_bus_query_sender_privilege(call, capability);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ return 1;
+
+#ifdef ENABLE_POLKIT
+ if (sd_bus_get_current_message(call->bus) != call)
+ return -EINVAL;
+
+ callback = sd_bus_get_current_handler(call->bus);
+ if (!callback)
+ return -EINVAL;
+
+ userdata = sd_bus_get_current_userdata(call->bus);
+
+ sender = sd_bus_message_get_sender(call);
+ if (!sender)
+ return -EBADMSG;
+
+ c = sd_bus_message_get_allow_interactive_authorization(call);
+ if (c < 0)
+ return c;
+ if (c > 0)
+ interactive = true;
+
+ r = hashmap_ensure_allocated(registry, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_call(
+ call->bus,
+ &pk,
+ "org.freedesktop.PolicyKit1",
+ "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority",
+ "CheckAuthorization");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ pk,
+ "(sa{sv})sa{ss}us",
+ "system-bus-name", 1, "name", "s", sender,
+ action,
+ 0,
+ !!interactive,
+ NULL);
+ if (r < 0)
+ return r;
+
+ q = new0(AsyncPolkitQuery, 1);
+ if (!q)
+ return -ENOMEM;
+
+ q->request = sd_bus_message_ref(call);
+ q->callback = callback;
+ q->userdata = userdata;
+
+ r = hashmap_put(*registry, call, q);
+ if (r < 0) {
+ async_polkit_query_free(q);
+ return r;
+ }
+
+ q->registry = *registry;
+
+ r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
+ if (r < 0) {
+ async_polkit_query_free(q);
+ return r;
+ }
+
+ return 0;
+#endif
+
+ return -EACCES;
+}
+
+void bus_verify_polkit_async_registry_free(Hashmap *registry) {
+#ifdef ENABLE_POLKIT
+ AsyncPolkitQuery *q;
+
+ while ((q = hashmap_steal_first(registry)))
+ async_polkit_query_free(q);
+
+ hashmap_free(registry);
+#endif
+}
+
+int bus_check_peercred(sd_bus *c) {
+ struct ucred ucred;
+ socklen_t l;
+ int fd;
+
+ assert(c);
+
+ fd = sd_bus_get_fd(c);
+ if (fd < 0)
+ return fd;
+
+ l = sizeof(struct ucred);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
+ return -errno;
+
+ if (l != sizeof(struct ucred))
+ return -E2BIG;
+
+ if (ucred.uid != 0 && ucred.uid != geteuid())
+ return -EPERM;
+
+ return 1;
+}
+
+int bus_open_system_systemd(sd_bus **_bus) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ int r;
+
+ assert(_bus);
+
+ if (geteuid() != 0)
+ return sd_bus_open_system(_bus);
+
+ /* If we are root and kdbus is not available, then let's talk
+ * directly to the system instance, instead of going via the
+ * bus */
+
+#ifdef ENABLE_KDBUS
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_ADDRESS);
+ if (r < 0)
+ return r;
+
+ bus->bus_client = true;
+
+ r = sd_bus_start(bus);
+ if (r >= 0) {
+ *_bus = bus;
+ bus = NULL;
+ return 0;
+ }
+
+ bus = sd_bus_unref(bus);
+#endif
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return sd_bus_open_system(_bus);
+
+ r = bus_check_peercred(bus);
+ if (r < 0)
+ return r;
+
+ *_bus = bus;
+ bus = NULL;
+
+ return 0;
+}
+
+int bus_open_user_systemd(sd_bus **_bus) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *ee = NULL;
+ const char *e;
+ int r;
+
+ /* Try via kdbus first, and then directly */
+
+ assert(_bus);
+
+#ifdef ENABLE_KDBUS
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&bus->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()) < 0)
+ return -ENOMEM;
+
+ bus->bus_client = true;
+
+ r = sd_bus_start(bus);
+ if (r >= 0) {
+ *_bus = bus;
+ bus = NULL;
+ return 0;
+ }
+
+ bus = sd_bus_unref(bus);
+#endif
+
+ e = secure_getenv("XDG_RUNTIME_DIR");
+ if (!e)
+ return sd_bus_open_user(_bus);
+
+ ee = bus_address_escape(e);
+ if (!ee)
+ return -ENOMEM;
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
+ if (!bus->address)
+ return -ENOMEM;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return sd_bus_open_user(_bus);
+
+ r = bus_check_peercred(bus);
+ if (r < 0)
+ return r;
+
+ *_bus = bus;
+ bus = NULL;
+
+ return 0;
+}
+
+int bus_print_property(const char *name, sd_bus_message *property, bool all) {
+ char type;
+ const char *contents;
+ int r;
+
+ assert(name);
+ assert(property);
+
+ r = sd_bus_message_peek_type(property, &type, &contents);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_STRING: {
+ const char *s;
+
+ r = sd_bus_message_read_basic(property, type, &s);
+ if (r < 0)
+ return r;
+
+ if (all || !isempty(s)) {
+ _cleanup_free_ char *escaped = NULL;
+
+ escaped = xescape(s, "\n");
+ if (!escaped)
+ return -ENOMEM;
+
+ printf("%s=%s\n", name, escaped);
+ }
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN: {
+ int b;
+
+ r = sd_bus_message_read_basic(property, type, &b);
+ if (r < 0)
+ return r;
+
+ printf("%s=%s\n", name, yes_no(b));
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t u;
+
+ r = sd_bus_message_read_basic(property, type, &u);
+ if (r < 0)
+ return r;
+
+ /* Yes, heuristics! But we can change this check
+ * should it turn out to not be sufficient */
+
+ if (endswith(name, "Timestamp")) {
+ char timestamp[FORMAT_TIMESTAMP_MAX], *t;
+
+ t = format_timestamp(timestamp, sizeof(timestamp), u);
+ if (t || all)
+ printf("%s=%s\n", name, strempty(t));
+
+ } else if (strstr(name, "USec")) {
+ char timespan[FORMAT_TIMESPAN_MAX];
+
+ printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
+ } else
+ printf("%s=%llu\n", name, (unsigned long long) u);
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_INT64: {
+ int64_t i;
+
+ r = sd_bus_message_read_basic(property, type, &i);
+ if (r < 0)
+ return r;
+
+ printf("%s=%lld\n", name, (long long) i);
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint32_t u;
+
+ r = sd_bus_message_read_basic(property, type, &u);
+ if (r < 0)
+ return r;
+
+ if (strstr(name, "UMask") || strstr(name, "Mode"))
+ printf("%s=%04o\n", name, u);
+ else
+ printf("%s=%u\n", name, (unsigned) u);
+
+ return 1;
+ }
+
+ case SD_BUS_TYPE_INT32: {
+ int32_t i;
+
+ r = sd_bus_message_read_basic(property, type, &i);
+ if (r < 0)
+ return r;
+
+ printf("%s=%i\n", name, (int) i);
+ return 1;
+ }
+
+ case SD_BUS_TYPE_DOUBLE: {
+ double d;
+
+ r = sd_bus_message_read_basic(property, type, &d);
+ if (r < 0)
+ return r;
+
+ printf("%s=%g\n", name, d);
+ return 1;
+ }
+
+ case SD_BUS_TYPE_ARRAY:
+ if (streq(contents, "s")) {
+ bool first = true;
+ const char *str;
+
+ r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
+ if (r < 0)
+ return r;
+
+ while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
+ _cleanup_free_ char *escaped = NULL;
+
+ if (first)
+ printf("%s=", name);
+
+ escaped = xescape(str, "\n ");
+ if (!escaped)
+ return -ENOMEM;
+
+ printf("%s%s", first ? "" : " ", escaped);
+
+ first = false;
+ }
+ if (r < 0)
+ return r;
+
+ if (first && all)
+ printf("%s=", name);
+ if (!first || all)
+ puts("");
+
+ r = sd_bus_message_exit_container(property);
+ if (r < 0)
+ return r;
+
+ return 1;
+
+ } else if (streq(contents, "y")) {
+ const uint8_t *u;
+ size_t n;
+
+ r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
+ if (r < 0)
+ return r;
+
+ if (all || n > 0) {
+ unsigned int i;
+
+ printf("%s=", name);
+
+ for (i = 0; i < n; i++)
+ printf("%02x", u[i]);
+
+ puts("");
+ }
+
+ return 1;
+
+ } else if (streq(contents, "u")) {
+ uint32_t *u;
+ size_t n;
+
+ r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
+ if (r < 0)
+ return r;
+
+ if (all || n > 0) {
+ unsigned int i;
+
+ printf("%s=", name);
+
+ for (i = 0; i < n; i++)
+ printf("%08x", u[i]);
+
+ puts("");
+ }
+
+ return 1;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(bus);
+ assert(path);
+
+ r = sd_bus_call_method(bus,
+ dest,
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &error,
+ &reply,
+ "s", "");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ const char *name;
+ const char *contents;
+
+ r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
+ if (r < 0)
+ return r;
+
+ if (!filter || strv_find(filter, name)) {
+ r = sd_bus_message_peek_type(reply, NULL, &contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
+ if (r < 0)
+ return r;
+
+ r = bus_print_property(name, reply, all);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (all)
+ printf("%s=[unprintable]\n", name);
+ /* skip what we didn't read */
+ r = sd_bus_message_skip(reply, contents);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(reply, "v");
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ sd_id128_t *p = userdata;
+ const void *v;
+ size_t n;
+ int r;
+
+ r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
+ if (r < 0)
+ return r;
+
+ if (n == 0)
+ *p = SD_ID128_NULL;
+ else if (n == 16)
+ memcpy((*p).bytes, v, n);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ char type;
+ int r;
+
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return r;
+
+ switch (type) {
+ case SD_BUS_TYPE_STRING: {
+ const char *s;
+ char **p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &s);
+ if (r < 0)
+ break;
+
+ if (isempty(s))
+ break;
+
+ r = free_and_strdup(p, s);
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ _cleanup_strv_free_ char **l = NULL;
+ char ***p = userdata;
+
+ r = bus_message_read_strv_extend(m, &l);
+ if (r < 0)
+ break;
+
+ strv_free(*p);
+ *p = l;
+ l = NULL;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN: {
+ unsigned b;
+ bool *p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ break;
+
+ *p = b;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint64_t u;
+ uint32_t *p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &u);
+ if (r < 0)
+ break;
+
+ *p = u;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t t;
+ uint64_t *p = userdata;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ break;
+
+ *p = t;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return r;
+}
+
+int bus_message_map_all_properties(
+ sd_bus_message *m,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(m);
+ assert(map);
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ const struct bus_properties_map *prop;
+ const char *member;
+ const char *contents;
+ void *v;
+ unsigned i;
+
+ r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
+ if (r < 0)
+ return r;
+
+ for (i = 0, prop = NULL; map[i].member; i++)
+ if (streq(map[i].member, member)) {
+ prop = &map[i];
+ break;
+ }
+
+ if (prop) {
+ r = sd_bus_message_peek_type(m, NULL, &contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+ if (r < 0)
+ return r;
+
+ v = (uint8_t *)userdata + prop->offset;
+ if (map[i].set)
+ r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v);
+ else
+ r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_exit_container(m);
+}
+
+int bus_message_map_properties_changed(
+ sd_bus_message *m,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
+ const char *member;
+ int r, invalidated, i;
+
+ assert(m);
+ assert(map);
+
+ r = bus_message_map_all_properties(m, map, userdata);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
+ if (r < 0)
+ return r;
+
+ invalidated = 0;
+ while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
+ for (i = 0; map[i].member; i++)
+ if (streq(map[i].member, member)) {
+ ++invalidated;
+ break;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return invalidated;
+}
+
+int bus_map_all_properties(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const struct bus_properties_map *map,
+ void *userdata) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(bus);
+ assert(destination);
+ assert(path);
+ assert(map);
+
+ r = sd_bus_call_method(
+ bus,
+ destination,
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &error,
+ &m,
+ "s", "");
+ if (r < 0)
+ return r;
+
+ return bus_message_map_all_properties(m, map, userdata);
+}
+
+int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+ int r;
+
+ assert(transport >= 0);
+ assert(transport < _BUS_TRANSPORT_MAX);
+ assert(bus);
+
+ assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
+ assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
+
+ switch (transport) {
+
+ case BUS_TRANSPORT_LOCAL:
+ if (user)
+ r = sd_bus_default_user(bus);
+ else
+ r = sd_bus_default_system(bus);
+
+ break;
+
+ case BUS_TRANSPORT_REMOTE:
+ r = sd_bus_open_system_remote(bus, host);
+ break;
+
+ case BUS_TRANSPORT_MACHINE:
+ r = sd_bus_open_system_machine(bus, host);
+ break;
+
+ default:
+ assert_not_reached("Hmm, unknown transport type.");
+ }
+
+ return r;
+}
+
+int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+ int r;
+
+ assert(transport >= 0);
+ assert(transport < _BUS_TRANSPORT_MAX);
+ assert(bus);
+
+ assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
+ assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
+
+ switch (transport) {
+
+ case BUS_TRANSPORT_LOCAL:
+ if (user)
+ r = bus_open_user_systemd(bus);
+ else
+ r = bus_open_system_systemd(bus);
+
+ break;
+
+ case BUS_TRANSPORT_REMOTE:
+ r = sd_bus_open_system_remote(bus, host);
+ break;
+
+ case BUS_TRANSPORT_MACHINE:
+ r = sd_bus_open_system_machine(bus, host);
+ break;
+
+ default:
+ assert_not_reached("Hmm, unknown transport type.");
+ }
+
+ return r;
+}
+
+int bus_property_get_bool(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int b = *(bool*) userdata;
+
+ return sd_bus_message_append_basic(reply, 'b', &b);
+}
+
+#if __SIZEOF_SIZE_T__ != 8
+int bus_property_get_size(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t sz = *(size_t*) userdata;
+
+ return sd_bus_message_append_basic(reply, 't', &sz);
+}
+#endif
+
+#if __SIZEOF_LONG__ != 8
+int bus_property_get_long(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int64_t l = *(long*) userdata;
+
+ return sd_bus_message_append_basic(reply, 'x', &l);
+}
+
+int bus_property_get_ulong(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t ul = *(unsigned long*) userdata;
+
+ return sd_bus_message_append_basic(reply, 't', &ul);
+}
+#endif
+
+int bus_log_parse_error(int r) {
+ return log_error_errno(r, "Failed to parse bus message: %m");
+}
+
+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;
+
+ assert(m);
+ assert(assignment);
+
+ eq = strchr(assignment, '=');
+ if (!eq) {
+ log_error("Not an assignment: %s", assignment);
+ return -EINVAL;
+ }
+
+ field = strndupa(assignment, eq - assignment);
+ eq ++;
+
+ if (streq(field, "CPUQuota")) {
+
+ if (isempty(eq)) {
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "v", "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_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
+ } else {
+ log_error("CPU quota needs to be in percent.");
+ return -EINVAL;
+ }
+
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 0;
+ }
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ if (STR_IN_SET(field,
+ "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
+ "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies")) {
+
+ r = parse_boolean(eq);
+ if (r < 0) {
+ log_error("Failed to parse boolean assignment %s.", assignment);
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_append(m, "v", "b", r);
+
+ } else if (streq(field, "MemoryLimit")) {
+ off_t bytes;
+
+ 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", (uint64_t) bytes);
+
+ } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
+ uint64_t u;
+
+ r = safe_atou64(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"))
+ r = sd_bus_message_append(m, "v", "s", eq);
+
+ 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;
+ off_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, (uint64_t) 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 (rlimit_from_string(field) >= 0) {
+ uint64_t rl;
+
+ if (streq(eq, "infinity"))
+ rl = (uint64_t) -1;
+ else {
+ r = safe_atou64(eq, &rl);
+ if (r < 0) {
+ log_error("Invalid resource limit: %s", eq);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", rl);
+
+ } 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 (streq(field, "Environment")) {
+
+ r = sd_bus_message_append(m, "v", "as", 1, eq);
+
+ } 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, "AccuracySec")) {
+ usec_t u;
+
+ r = parse_sec(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 {
+ log_error("Unknown assignment %s.", assignment);
+ return -EINVAL;
+ }
+
+ 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) {
+ _cleanup_free_ char *service_shell_quoted = NULL;
+
+ assert(service);
+
+ service_shell_quoted = shell_maybe_quote(service);
+
+ 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. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ service,
+ explanations[i].explanation,
+ strna(service_shell_quoted));
+
+ goto finish;
+ }
+ }
+
+ log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ service,
+ strna(service_shell_quoted));
+
+finish:
+ /* For some results maybe additional explanation is required */
+ if (streq_ptr(result, "start-limit"))
+ log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
+ strna(service_shell_quoted));
+}
+
+static int check_wait_response(BusWaitForJobs *d, bool quiet) {
+ 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("Job for %s invalid.", 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);
+ } 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) {
+ 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);
+ /* 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));
+ }
+
+ free(d->name);
+ d->name = NULL;
+
+ free(d->result);
+ d->result = NULL;
+ }
+
+ 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);
+}
+
+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) {
+ if (!quiet) {
+ if (streq(type, "symlink"))
+ log_info("Created symlink from %s to %s.", path, source);
+ else
+ log_info("Removed symlink %s.", path);
+ }
+
+ r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, 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);
+
+ return 0;
+}
+
+/**
+ * bus_path_encode_unique() - encode unique object path
+ * @b: bus connection or NULL
+ * @prefix: object path prefix
+ * @sender_id: unique-name of client, or NULL
+ * @external_id: external ID to be chosen by client, or NULL
+ * @ret_path: storage for encoded object path pointer
+ *
+ * Whenever we provide a bus API that allows clients to create and manage
+ * server-side objects, we need to provide a unique name for these objects. If
+ * we let the server choose the name, we suffer from a race condition: If a
+ * client creates an object asynchronously, it cannot destroy that object until
+ * it received the method reply. It cannot know the name of the new object,
+ * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
+ *
+ * Therefore, many APIs allow the client to choose the unique name for newly
+ * created objects. There're two problems to solve, though:
+ * 1) Object names are usually defined via dbus object paths, which are
+ * usually globally namespaced. Therefore, multiple clients must be able
+ * to choose unique object names without interference.
+ * 2) If multiple libraries share the same bus connection, they must be
+ * able to choose unique object names without interference.
+ * The first problem is solved easily by prefixing a name with the
+ * unique-bus-name of a connection. The server side must enforce this and
+ * reject any other name. The second problem is solved by providing unique
+ * suffixes from within sd-bus.
+ *
+ * This helper allows clients to create unique object-paths. It uses the
+ * template '/prefix/sender_id/external_id' and returns the new path in
+ * @ret_path (must be freed by the caller).
+ * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
+ * NULL, this function allocates a unique suffix via @b (by requesting a new
+ * cookie). If both @sender_id and @external_id are given, @b can be passed as
+ * NULL.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
+ _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
+ char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
+ int r;
+
+ assert_return(b || (sender_id && external_id), -EINVAL);
+ assert_return(object_path_is_valid(prefix), -EINVAL);
+ assert_return(ret_path, -EINVAL);
+
+ if (!sender_id) {
+ r = sd_bus_get_unique_name(b, &sender_id);
+ if (r < 0)
+ return r;
+ }
+
+ if (!external_id) {
+ xsprintf(external_buf, "%"PRIu64, ++b->cookie);
+ external_id = external_buf;
+ }
+
+ sender_label = bus_label_escape(sender_id);
+ if (!sender_label)
+ return -ENOMEM;
+
+ external_label = bus_label_escape(external_id);
+ if (!external_label)
+ return -ENOMEM;
+
+ p = strjoin(prefix, "/", sender_label, "/", external_label, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ *ret_path = p;
+ return 0;
+}
+
+/**
+ * bus_path_decode_unique() - decode unique object path
+ * @path: object path to decode
+ * @prefix: object path prefix
+ * @ret_sender: output parameter for sender-id label
+ * @ret_external: output parameter for external-id label
+ *
+ * This does the reverse of bus_path_encode_unique() (see its description for
+ * details). Both trailing labels, sender-id and external-id, are unescaped and
+ * returned in the given output parameters (the caller must free them).
+ *
+ * Note that this function returns 0 if the path does not match the template
+ * (see bus_path_encode_unique()), 1 if it matched.
+ *
+ * Returns: Negative error code on failure, 0 if the given object path does not
+ * match the template (return parameters are set to NULL), 1 if it was
+ * parsed successfully (return parameters contain allocated labels).
+ */
+int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
+ const char *p, *q;
+ char *sender, *external;
+
+ assert(object_path_is_valid(path));
+ assert(object_path_is_valid(prefix));
+ assert(ret_sender);
+ assert(ret_external);
+
+ p = object_path_startswith(path, prefix);
+ if (!p) {
+ *ret_sender = NULL;
+ *ret_external = NULL;
+ return 0;
+ }
+
+ q = strchr(p, '/');
+ if (!q) {
+ *ret_sender = NULL;
+ *ret_external = NULL;
+ return 0;
+ }
+
+ sender = bus_label_unescape_n(p, q - p);
+ external = bus_label_unescape(q + 1);
+ if (!sender || !external) {
+ free(sender);
+ free(external);
+ return -ENOMEM;
+ }
+
+ *ret_sender = sender;
+ *ret_external = external;
+ return 1;
+}
+
+bool is_kdbus_wanted(void) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ if (get_proc_cmdline_key("kdbus", NULL) <= 0) {
+ r = get_proc_cmdline_key("kdbus=", &value);
+ if (r <= 0 || parse_boolean(value) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+bool is_kdbus_available(void) {
+ _cleanup_close_ int fd = -1;
+ struct kdbus_cmd cmd = { .size = sizeof(cmd), .flags = KDBUS_FLAG_NEGOTIATE };
+
+ if (!is_kdbus_wanted())
+ return false;
+
+ fd = open("/sys/fs/kdbus/control", O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
+ if (fd < 0)
+ return false;
+
+ return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0;
+}
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
new file mode 100644
index 0000000..d8dba58
--- /dev/null
+++ b/src/shared/bus-util.h
@@ -0,0 +1,209 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 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-event.h"
+#include "sd-bus.h"
+#include "hashmap.h"
+#include "install.h"
+#include "time-util.h"
+
+typedef enum BusTransport {
+ BUS_TRANSPORT_LOCAL,
+ BUS_TRANSPORT_REMOTE,
+ BUS_TRANSPORT_MACHINE,
+ _BUS_TRANSPORT_MAX,
+ _BUS_TRANSPORT_INVALID = -1
+} BusTransport;
+
+typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
+
+struct bus_properties_map {
+ const char *member;
+ const char *signature;
+ bus_property_set_t set;
+ size_t offset;
+};
+
+int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
+
+int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
+int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
+int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata);
+
+int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
+
+typedef bool (*check_idle_t)(void *userdata);
+
+int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout, check_idle_t check_idle, void *userdata);
+
+int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
+
+int bus_check_peercred(sd_bus *c);
+
+int bus_test_polkit(sd_bus_message *call, int capability, const char *action, uid_t good_user, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+void bus_verify_polkit_async_registry_free(Hashmap *registry);
+
+int bus_open_system_systemd(sd_bus **_bus);
+int bus_open_user_systemd(sd_bus **_bus);
+
+int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
+int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
+
+int bus_print_property(const char *name, sd_bus_message *property, bool all);
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all);
+
+int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+
+#define bus_property_get_usec ((sd_bus_property_get_t) NULL)
+#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
+
+assert_cc(sizeof(int) == sizeof(int32_t));
+#define bus_property_get_int ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(unsigned) == sizeof(unsigned));
+#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL)
+
+/* On 64bit machines we can use the default serializer for size_t and
+ * friends, otherwise we need to cast this manually */
+#if __SIZEOF_SIZE_T__ == 8
+#define bus_property_get_size ((sd_bus_property_get_t) NULL)
+#else
+int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif
+
+#if __SIZEOF_LONG__ == 8
+#define bus_property_get_long ((sd_bus_property_get_t) NULL)
+#define bus_property_get_ulong ((sd_bus_property_get_t) NULL)
+#else
+int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif
+
+/* uid_t and friends on Linux 32 bit. This means we can just use the
+ * default serializer for 32bit unsigned, for serializing it, and map
+ * it to NULL here */
+assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+#define bus_property_get_uid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(gid_t) == sizeof(uint32_t));
+#define bus_property_get_gid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(pid_t) == sizeof(uint32_t));
+#define bus_property_get_pid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(mode_t) == sizeof(uint32_t));
+#define bus_property_get_mode ((sd_bus_property_get_t) NULL)
+
+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);
+
+static inline void sd_bus_close_unrefp(sd_bus **bus) {
+ if (*bus) {
+ sd_bus_flush(*bus);
+ sd_bus_close(*bus);
+ sd_bus_unref(*bus);
+ }
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus*, sd_bus_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_slot*, sd_bus_slot_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, sd_bus_message_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_creds*, sd_bus_creds_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_track*, sd_bus_track_unref);
+
+#define _cleanup_bus_unref_ _cleanup_(sd_bus_unrefp)
+#define _cleanup_bus_close_unref_ _cleanup_(sd_bus_close_unrefp)
+#define _cleanup_bus_slot_unref_ _cleanup_(sd_bus_slot_unrefp)
+#define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp)
+#define _cleanup_bus_creds_unref_ _cleanup_(sd_bus_creds_unrefp)
+#define _cleanup_bus_track_unref_ _cleanup_(sd_bus_slot_unrefp)
+#define _cleanup_bus_error_free_ _cleanup_(sd_bus_error_free)
+
+#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \
+ int function(sd_bus *bus, \
+ const char *path, \
+ const char *interface, \
+ const char *property, \
+ sd_bus_message *reply, \
+ void *userdata, \
+ sd_bus_error *error) { \
+ \
+ const char *value; \
+ type *field = userdata; \
+ int r; \
+ \
+ assert(bus); \
+ assert(reply); \
+ assert(field); \
+ \
+ value = strempty(name##_to_string(*field)); \
+ \
+ r = sd_bus_message_append_basic(reply, 's', value); \
+ if (r < 0) \
+ return r; \
+ \
+ return 1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \
+ 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);
+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);
+
+bool is_kdbus_wanted(void);
+bool is_kdbus_available(void);
diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
deleted file mode 100644
index 2fde3e1..0000000
--- a/src/shared/calendarspec.c
+++ /dev/null
@@ -1,1006 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 <stdlib.h>
-#include <string.h>
-
-#include "calendarspec.h"
-
-#define BITS_WEEKDAYS 127
-
-static void free_chain(CalendarComponent *c) {
- CalendarComponent *n;
-
- while (c) {
- n = c->next;
- free(c);
- c = n;
- }
-}
-
-void calendar_spec_free(CalendarSpec *c) {
-
- if (!c)
- return;
-
- free_chain(c->year);
- free_chain(c->month);
- free_chain(c->day);
- free_chain(c->hour);
- free_chain(c->minute);
- free_chain(c->second);
-
- free(c);
-}
-
-static int component_compare(const void *_a, const void *_b) {
- CalendarComponent * const *a = _a, * const *b = _b;
-
- if ((*a)->value < (*b)->value)
- return -1;
- if ((*a)->value > (*b)->value)
- return 1;
-
- if ((*a)->repeat < (*b)->repeat)
- return -1;
- if ((*a)->repeat > (*b)->repeat)
- return 1;
-
- return 0;
-}
-
-static void sort_chain(CalendarComponent **c) {
- unsigned n = 0, k;
- CalendarComponent **b, *i, **j, *next;
-
- assert(c);
-
- for (i = *c; i; i = i->next)
- n++;
-
- if (n <= 1)
- return;
-
- j = b = alloca(sizeof(CalendarComponent*) * n);
- for (i = *c; i; i = i->next)
- *(j++) = i;
-
- qsort(b, n, sizeof(CalendarComponent*), component_compare);
-
- b[n-1]->next = NULL;
- next = b[n-1];
-
- /* Drop non-unique entries */
- for (k = n-1; k > 0; k--) {
- if (b[k-1]->value == next->value &&
- b[k-1]->repeat == next->repeat) {
- free(b[k-1]);
- continue;
- }
-
- b[k-1]->next = next;
- next = b[k-1];
- }
-
- *c = next;
-}
-
-static void fix_year(CalendarComponent *c) {
- /* Turns 12 → 2012, 89 → 1989 */
-
- while(c) {
- CalendarComponent *n = c->next;
-
- if (c->value >= 0 && c->value < 70)
- c->value += 2000;
-
- if (c->value >= 70 && c->value < 100)
- c->value += 1900;
-
- c = n;
- }
-}
-
-int calendar_spec_normalize(CalendarSpec *c) {
- assert(c);
-
- if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
- c->weekdays_bits = -1;
-
- fix_year(c->year);
-
- sort_chain(&c->year);
- sort_chain(&c->month);
- sort_chain(&c->day);
- sort_chain(&c->hour);
- sort_chain(&c->minute);
- sort_chain(&c->second);
-
- return 0;
-}
-
-_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
- if (!c)
- return true;
-
- if (c->value < from || c->value > to)
- return false;
-
- if (c->value + c->repeat > to)
- return false;
-
- if (c->next)
- return chain_valid(c->next, from, to);
-
- return true;
-}
-
-_pure_ bool calendar_spec_valid(CalendarSpec *c) {
- assert(c);
-
- if (c->weekdays_bits > BITS_WEEKDAYS)
- return false;
-
- if (!chain_valid(c->year, 1970, 2199))
- return false;
-
- if (!chain_valid(c->month, 1, 12))
- return false;
-
- if (!chain_valid(c->day, 1, 31))
- return false;
-
- if (!chain_valid(c->hour, 0, 23))
- return false;
-
- if (!chain_valid(c->minute, 0, 59))
- return false;
-
- if (!chain_valid(c->second, 0, 59))
- return false;
-
- return true;
-}
-
-static void format_weekdays(FILE *f, const CalendarSpec *c) {
- static const char *const days[] = {
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat",
- "Sun"
- };
-
- int l, x;
- bool need_colon = false;
-
- assert(f);
- assert(c);
- assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
-
- for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
-
- if (c->weekdays_bits & (1 << x)) {
-
- if (l < 0) {
- if (need_colon)
- fputc(',', f);
- else
- need_colon = true;
-
- fputs(days[x], f);
- l = x;
- }
-
- } else if (l >= 0) {
-
- if (x > l + 1) {
- fputc(x > l + 2 ? '-' : ',', f);
- fputs(days[x-1], f);
- }
-
- l = -1;
- }
- }
-
- if (l >= 0 && x > l + 1) {
- fputc(x > l + 2 ? '-' : ',', f);
- fputs(days[x-1], f);
- }
-}
-
-static void format_chain(FILE *f, int space, const CalendarComponent *c) {
- assert(f);
-
- if (!c) {
- fputc('*', f);
- return;
- }
-
- assert(c->value >= 0);
- fprintf(f, "%0*i", space, c->value);
-
- if (c->repeat > 0)
- fprintf(f, "/%i", c->repeat);
-
- if (c->next) {
- fputc(',', f);
- format_chain(f, space, c->next);
- }
-}
-
-int calendar_spec_to_string(const CalendarSpec *c, char **p) {
- char *buf = NULL;
- size_t sz = 0;
- FILE *f;
-
- assert(c);
- assert(p);
-
- f = open_memstream(&buf, &sz);
- if (!f)
- return -ENOMEM;
-
- if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
- format_weekdays(f, c);
- fputc(' ', f);
- }
-
- format_chain(f, 4, c->year);
- fputc('-', f);
- format_chain(f, 2, c->month);
- fputc('-', f);
- format_chain(f, 2, c->day);
- fputc(' ', f);
- format_chain(f, 2, c->hour);
- fputc(':', f);
- format_chain(f, 2, c->minute);
- fputc(':', f);
- format_chain(f, 2, c->second);
-
- fflush(f);
-
- if (ferror(f)) {
- free(buf);
- fclose(f);
- return -ENOMEM;
- }
-
- fclose(f);
-
- *p = buf;
- return 0;
-}
-
-static int parse_weekdays(const char **p, CalendarSpec *c) {
- static const struct {
- const char *name;
- const int nr;
- } day_nr[] = {
- { "Monday", 0 },
- { "Mon", 0 },
- { "Tuesday", 1 },
- { "Tue", 1 },
- { "Wednesday", 2 },
- { "Wed", 2 },
- { "Thursday", 3 },
- { "Thu", 3 },
- { "Friday", 4 },
- { "Fri", 4 },
- { "Saturday", 5 },
- { "Sat", 5 },
- { "Sunday", 6 },
- { "Sun", 6 }
- };
-
- int l = -1;
- bool first = true;
-
- assert(p);
- assert(*p);
- assert(c);
-
- for (;;) {
- unsigned i;
-
- if (!first && **p == ' ')
- return 0;
-
- for (i = 0; i < ELEMENTSOF(day_nr); i++) {
- size_t skip;
-
- if (!startswith_no_case(*p, day_nr[i].name))
- continue;
-
- skip = strlen(day_nr[i].name);
-
- if ((*p)[skip] != '-' &&
- (*p)[skip] != ',' &&
- (*p)[skip] != ' ' &&
- (*p)[skip] != 0)
- return -EINVAL;
-
- c->weekdays_bits |= 1 << day_nr[i].nr;
-
- if (l >= 0) {
- int j;
-
- if (l > day_nr[i].nr)
- return -EINVAL;
-
- for (j = l + 1; j < day_nr[i].nr; j++)
- c->weekdays_bits |= 1 << j;
- }
-
- *p += skip;
- break;
- }
-
- /* Couldn't find this prefix, so let's assume the
- weekday was not specified and let's continue with
- the date */
- if (i >= ELEMENTSOF(day_nr))
- return first ? 0 : -EINVAL;
-
- /* We reached the end of the string */
- if (**p == 0)
- return 0;
-
- /* We reached the end of the weekday spec part */
- if (**p == ' ') {
- *p += strspn(*p, " ");
- return 0;
- }
-
- if (**p == '-') {
- if (l >= 0)
- return -EINVAL;
-
- l = day_nr[i].nr;
- } else
- l = -1;
-
- *p += 1;
- first = false;
- }
-}
-
-static int prepend_component(const char **p, CalendarComponent **c) {
- unsigned long value, repeat = 0;
- char *e = NULL, *ee = NULL;
- CalendarComponent *cc;
-
- assert(p);
- assert(c);
-
- errno = 0;
- value = strtoul(*p, &e, 10);
- if (errno > 0)
- return -errno;
- if (e == *p)
- return -EINVAL;
- if ((unsigned long) (int) value != value)
- return -ERANGE;
-
- if (*e == '/') {
- repeat = strtoul(e+1, &ee, 10);
- if (errno > 0)
- return -errno;
- if (ee == e+1)
- return -EINVAL;
- if ((unsigned long) (int) repeat != repeat)
- return -ERANGE;
- if (repeat <= 0)
- return -ERANGE;
-
- e = ee;
- }
-
- if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
- return -EINVAL;
-
- cc = new0(CalendarComponent, 1);
- if (!cc)
- return -ENOMEM;
-
- cc->value = value;
- cc->repeat = repeat;
- cc->next = *c;
-
- *p = e;
- *c = cc;
-
- if (*e ==',') {
- *p += 1;
- return prepend_component(p, c);
- }
-
- return 0;
-}
-
-static int parse_chain(const char **p, CalendarComponent **c) {
- const char *t;
- CalendarComponent *cc = NULL;
- int r;
-
- assert(p);
- assert(c);
-
- t = *p;
-
- if (t[0] == '*') {
- *p = t + 1;
- *c = NULL;
- return 0;
- }
-
- r = prepend_component(&t, &cc);
- if (r < 0) {
- free_chain(cc);
- return r;
- }
-
- *p = t;
- *c = cc;
- return 0;
-}
-
-static int const_chain(int value, CalendarComponent **c) {
- CalendarComponent *cc = NULL;
-
- assert(c);
-
- cc = new0(CalendarComponent, 1);
- if (!cc)
- return -ENOMEM;
-
- cc->value = value;
- cc->repeat = 0;
- cc->next = *c;
-
- *c = cc;
-
- return 0;
-}
-
-static int parse_date(const char **p, CalendarSpec *c) {
- const char *t;
- int r;
- CalendarComponent *first, *second, *third;
-
- assert(p);
- assert(*p);
- assert(c);
-
- t = *p;
-
- if (*t == 0)
- return 0;
-
- r = parse_chain(&t, &first);
- if (r < 0)
- return r;
-
- /* Already the end? A ':' as separator? In that case this was a time, not a date */
- if (*t == 0 || *t == ':') {
- free_chain(first);
- return 0;
- }
-
- if (*t != '-') {
- free_chain(first);
- return -EINVAL;
- }
-
- t++;
- r = parse_chain(&t, &second);
- if (r < 0) {
- free_chain(first);
- return r;
- }
-
- /* Got two parts, hence it's month and day */
- if (*t == ' ' || *t == 0) {
- *p = t + strspn(t, " ");
- c->month = first;
- c->day = second;
- return 0;
- }
-
- if (*t != '-') {
- free_chain(first);
- free_chain(second);
- return -EINVAL;
- }
-
- t++;
- r = parse_chain(&t, &third);
- if (r < 0) {
- free_chain(first);
- free_chain(second);
- return r;
- }
-
- /* Got tree parts, hence it is year, month and day */
- if (*t == ' ' || *t == 0) {
- *p = t + strspn(t, " ");
- c->year = first;
- c->month = second;
- c->day = third;
- return 0;
- }
-
- free_chain(first);
- free_chain(second);
- free_chain(third);
- return -EINVAL;
-}
-
-static int parse_time(const char **p, CalendarSpec *c) {
- CalendarComponent *h = NULL, *m = NULL, *s = NULL;
- const char *t;
- int r;
-
- assert(p);
- assert(*p);
- assert(c);
-
- t = *p;
-
- if (*t == 0) {
- /* If no time is specified at all, but a date of some
- * kind, then this means 00:00:00 */
- if (c->day || c->weekdays_bits > 0)
- goto null_hour;
-
- goto finish;
- }
-
- r = parse_chain(&t, &h);
- if (r < 0)
- goto fail;
-
- if (*t != ':') {
- r = -EINVAL;
- goto fail;
- }
-
- t++;
- r = parse_chain(&t, &m);
- if (r < 0)
- goto fail;
-
- /* Already at the end? Then it's hours and minutes, and seconds are 0 */
- if (*t == 0) {
- if (m != NULL)
- goto null_second;
-
- goto finish;
- }
-
- if (*t != ':') {
- r = -EINVAL;
- goto fail;
- }
-
- t++;
- r = parse_chain(&t, &s);
- if (r < 0)
- goto fail;
-
- /* At the end? Then it's hours, minutes and seconds */
- if (*t == 0)
- goto finish;
-
- r = -EINVAL;
- goto fail;
-
-null_hour:
- r = const_chain(0, &h);
- if (r < 0)
- goto fail;
-
- r = const_chain(0, &m);
- if (r < 0)
- goto fail;
-
-null_second:
- r = const_chain(0, &s);
- if (r < 0)
- goto fail;
-
-finish:
- *p = t;
- c->hour = h;
- c->minute = m;
- c->second = s;
- return 0;
-
-fail:
- free_chain(h);
- free_chain(m);
- free_chain(s);
- return r;
-}
-
-int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
- CalendarSpec *c;
- int r;
-
- assert(p);
- assert(spec);
-
- if (isempty(p))
- return -EINVAL;
-
- c = new0(CalendarSpec, 1);
- if (!c)
- return -ENOMEM;
-
- if (strcaseeq(p, "minutely")) {
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "hourly")) {
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "daily")) {
- r = const_chain(0, &c->hour);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "monthly")) {
- r = const_chain(1, &c->day);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->hour);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "annually") ||
- strcaseeq(p, "yearly") ||
- strcaseeq(p, "anually") /* backwards compatibility */ ) {
-
- r = const_chain(1, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(1, &c->day);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->hour);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "weekly")) {
-
- c->weekdays_bits = 1;
-
- r = const_chain(0, &c->hour);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "quarterly")) {
-
- r = const_chain(1, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(4, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(7, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(10, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(1, &c->day);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->hour);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else if (strcaseeq(p, "biannually") ||
- strcaseeq(p, "bi-annually") ||
- strcaseeq(p, "semiannually") ||
- strcaseeq(p, "semi-annually")) {
-
- r = const_chain(1, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(7, &c->month);
- if (r < 0)
- goto fail;
- r = const_chain(1, &c->day);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->hour);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->minute);
- if (r < 0)
- goto fail;
- r = const_chain(0, &c->second);
- if (r < 0)
- goto fail;
-
- } else {
- r = parse_weekdays(&p, c);
- if (r < 0)
- goto fail;
-
- r = parse_date(&p, c);
- if (r < 0)
- goto fail;
-
- r = parse_time(&p, c);
- if (r < 0)
- goto fail;
-
- if (*p != 0) {
- r = -EINVAL;
- goto fail;
- }
- }
-
- r = calendar_spec_normalize(c);
- if (r < 0)
- goto fail;
-
- if (!calendar_spec_valid(c)) {
- r = -EINVAL;
- goto fail;
- }
-
- *spec = c;
- return 0;
-
-fail:
- calendar_spec_free(c);
- return r;
-}
-
-static int find_matching_component(const CalendarComponent *c, int *val) {
- const CalendarComponent *n;
- int d = -1;
- bool d_set = false;
- int r;
-
- assert(val);
-
- if (!c)
- return 0;
-
- while (c) {
- n = c->next;
-
- if (c->value >= *val) {
-
- if (!d_set || c->value < d) {
- d = c->value;
- d_set = true;
- }
-
- } else if (c->repeat > 0) {
- int k;
-
- k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
-
- if (!d_set || k < d) {
- d = k;
- d_set = true;
- }
- }
-
- c = n;
- }
-
- if (!d_set)
- return -ENOENT;
-
- r = *val != d;
- *val = d;
- return r;
-}
-
-static bool tm_out_of_bounds(const struct tm *tm) {
- struct tm t;
- assert(tm);
-
- t = *tm;
-
- if (mktime(&t) == (time_t) -1)
- return true;
-
- /* Did any normalization take place? If so, it was out of bounds before */
- return
- t.tm_year != tm->tm_year ||
- t.tm_mon != tm->tm_mon ||
- t.tm_mday != tm->tm_mday ||
- t.tm_hour != tm->tm_hour ||
- t.tm_min != tm->tm_min ||
- t.tm_sec != tm->tm_sec;
-}
-
-static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
- struct tm t;
- int k;
-
- if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
- return true;
-
- t = *tm;
- if (mktime(&t) == (time_t) -1)
- return false;
-
- k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
- return (weekdays_bits & (1 << k));
-}
-
-static int find_next(const CalendarSpec *spec, struct tm *tm) {
- struct tm c;
- int r;
-
- assert(spec);
- assert(tm);
-
- c = *tm;
-
- for (;;) {
- /* Normalize the current date */
- mktime(&c);
- c.tm_isdst = -1;
-
- c.tm_year += 1900;
- r = find_matching_component(spec->year, &c.tm_year);
- c.tm_year -= 1900;
-
- if (r > 0) {
- c.tm_mon = 0;
- c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- }
- if (r < 0 || tm_out_of_bounds(&c))
- return r;
-
- c.tm_mon += 1;
- r = find_matching_component(spec->month, &c.tm_mon);
- c.tm_mon -= 1;
-
- if (r > 0) {
- c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- }
- if (r < 0 || tm_out_of_bounds(&c)) {
- c.tm_year ++;
- c.tm_mon = 0;
- c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- continue;
- }
-
- r = find_matching_component(spec->day, &c.tm_mday);
- if (r > 0)
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- if (r < 0 || tm_out_of_bounds(&c)) {
- c.tm_mon ++;
- c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- continue;
- }
-
- if (!matches_weekday(spec->weekdays_bits, &c)) {
- c.tm_mday++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- continue;
- }
-
- r = find_matching_component(spec->hour, &c.tm_hour);
- if (r > 0)
- c.tm_min = c.tm_sec = 0;
- if (r < 0 || tm_out_of_bounds(&c)) {
- c.tm_mday ++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
- continue;
- }
-
- r = find_matching_component(spec->minute, &c.tm_min);
- if (r > 0)
- c.tm_sec = 0;
- if (r < 0 || tm_out_of_bounds(&c)) {
- c.tm_hour ++;
- c.tm_min = c.tm_sec = 0;
- continue;
- }
-
- r = find_matching_component(spec->second, &c.tm_sec);
- if (r < 0 || tm_out_of_bounds(&c)) {
- c.tm_min ++;
- c.tm_sec = 0;
- continue;
- }
-
-
- *tm = c;
- return 0;
- }
-}
-
-int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
- struct tm tm;
- time_t t;
- int r;
-
- assert(spec);
- assert(next);
-
- t = (time_t) (usec / USEC_PER_SEC) + 1;
- assert_se(localtime_r(&t, &tm));
-
- r = find_next(spec, &tm);
- if (r < 0)
- return r;
-
- t = mktime(&tm);
- if (t == (time_t) -1)
- return -EINVAL;
-
- *next = (usec_t) t * USEC_PER_SEC;
- return 0;
-}
diff --git a/src/shared/calendarspec.h b/src/shared/calendarspec.h
deleted file mode 100644
index 7baf318..0000000
--- a/src/shared/calendarspec.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- 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/>.
-***/
-
-/* A structure for specifying (possibly repetitive) points in calendar
- * time, a la cron */
-
-#include <stdbool.h>
-#include "util.h"
-
-typedef struct CalendarComponent {
- int value;
- int repeat;
-
- struct CalendarComponent *next;
-} CalendarComponent;
-
-typedef struct CalendarSpec {
- int weekdays_bits;
-
- CalendarComponent *year;
- CalendarComponent *month;
- CalendarComponent *day;
-
- CalendarComponent *hour;
- CalendarComponent *minute;
- CalendarComponent *second;
-} CalendarSpec;
-
-void calendar_spec_free(CalendarSpec *c);
-
-int calendar_spec_normalize(CalendarSpec *spec);
-bool calendar_spec_valid(CalendarSpec *spec);
-
-int calendar_spec_to_string(const CalendarSpec *spec, char **p);
-int calendar_spec_from_string(const char *p, CalendarSpec **spec);
-
-int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next);
diff --git a/src/shared/cap-list.c b/src/shared/cap-list.c
deleted file mode 100644
index bd5bffb..0000000
--- a/src/shared/cap-list.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <string.h>
-
-#include "util.h"
-#include "cap-list.h"
-#include "missing.h"
-
-static const struct capability_name* lookup_capability(register const char *str, register unsigned int len);
-
-#include "cap-to-name.h"
-#include "cap-from-name.h"
-
-const char *capability_to_name(int id) {
-
- if (id < 0)
- return NULL;
-
- if (id >= (int) ELEMENTSOF(capability_names))
- return NULL;
-
- return capability_names[id];
-}
-
-int capability_from_name(const char *name) {
- const struct capability_name *sc;
- int r, i;
-
- assert(name);
-
- /* Try to parse numeric capability */
- r = safe_atoi(name, &i);
- if (r >= 0 && i >= 0)
- return i;
-
- /* Try to parse string capability */
- sc = lookup_capability(name, strlen(name));
- if (!sc)
- return -EINVAL;
-
- return sc->id;
-}
-
-int capability_list_length(void) {
- return (int) ELEMENTSOF(capability_names);
-}
diff --git a/src/shared/cap-list.h b/src/shared/cap-list.h
deleted file mode 100644
index 9824fad..0000000
--- a/src/shared/cap-list.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-const char *capability_to_name(int id);
-int capability_from_name(const char *name);
-int capability_list_length(void);
diff --git a/src/shared/capability.c b/src/shared/capability.c
deleted file mode 100644
index 58f00e6..0000000
--- a/src/shared/capability.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <unistd.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/capability.h>
-#include <sys/prctl.h>
-#include "grp.h"
-
-#include "macro.h"
-#include "util.h"
-#include "log.h"
-#include "fileio.h"
-#include "capability.h"
-
-int have_effective_cap(int value) {
- _cleanup_cap_free_ cap_t cap;
- cap_flag_value_t fv;
-
- cap = cap_get_proc();
- if (!cap)
- return -errno;
-
- if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
- return -errno;
- else
- return fv == CAP_SET;
-}
-
-unsigned long cap_last_cap(void) {
- static thread_local unsigned long saved;
- static thread_local bool valid = false;
- _cleanup_free_ char *content = NULL;
- unsigned long p = 0;
- int r;
-
- if (valid)
- return saved;
-
- /* available since linux-3.2 */
- r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
- if (r >= 0) {
- r = safe_atolu(content, &p);
- if (r >= 0) {
- saved = p;
- valid = true;
- return p;
- }
- }
-
- /* fall back to syscall-probing for pre linux-3.2 */
- p = (unsigned long) CAP_LAST_CAP;
-
- if (prctl(PR_CAPBSET_READ, p) < 0) {
-
- /* Hmm, look downwards, until we find one that
- * works */
- for (p--; p > 0; p --)
- if (prctl(PR_CAPBSET_READ, p) >= 0)
- break;
-
- } else {
-
- /* Hmm, look upwards, until we find one that doesn't
- * work */
- for (;; p++)
- if (prctl(PR_CAPBSET_READ, p+1) < 0)
- break;
- }
-
- saved = p;
- valid = true;
-
- return p;
-}
-
-int capability_bounding_set_drop(uint64_t drop, bool right_now) {
- _cleanup_cap_free_ cap_t after_cap = NULL;
- cap_flag_value_t fv;
- unsigned long i;
- int r;
-
- /* If we are run as PID 1 we will lack CAP_SETPCAP by default
- * in the effective set (yes, the kernel drops that when
- * executing init!), so get it back temporarily so that we can
- * call PR_CAPBSET_DROP. */
-
- after_cap = cap_get_proc();
- if (!after_cap)
- return -errno;
-
- if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
- return -errno;
-
- if (fv != CAP_SET) {
- _cleanup_cap_free_ cap_t temp_cap = NULL;
- static const cap_value_t v = CAP_SETPCAP;
-
- temp_cap = cap_dup(after_cap);
- if (!temp_cap) {
- r = -errno;
- goto finish;
- }
-
- if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (cap_set_proc(temp_cap) < 0) {
- r = -errno;
- goto finish;
- }
- }
-
- for (i = 0; i <= cap_last_cap(); i++) {
-
- if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
- cap_value_t v;
-
- /* Drop it from the bounding set */
- if (prctl(PR_CAPBSET_DROP, i) < 0) {
- r = -errno;
- goto finish;
- }
- v = (cap_value_t) i;
-
- /* Also drop it from the inheritable set, so
- * that anything we exec() loses the
- * capability for good. */
- if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
- r = -errno;
- goto finish;
- }
-
- /* If we shall apply this right now drop it
- * also from our own capability sets. */
- if (right_now) {
- if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
- cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
- r = -errno;
- goto finish;
- }
- }
- }
- }
-
- r = 0;
-
-finish:
- if (cap_set_proc(after_cap) < 0)
- return -errno;
-
- return r;
-}
-
-static int drop_from_file(const char *fn, uint64_t drop) {
- int r, k;
- uint32_t hi, lo;
- uint64_t current, after;
- char *p;
-
- r = read_one_line_file(fn, &p);
- if (r < 0)
- return r;
-
- assert_cc(sizeof(hi) == sizeof(unsigned));
- assert_cc(sizeof(lo) == sizeof(unsigned));
-
- k = sscanf(p, "%u %u", &lo, &hi);
- free(p);
-
- if (k != 2)
- return -EIO;
-
- current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
- after = current & ~drop;
-
- if (current == after)
- return 0;
-
- lo = (unsigned) (after & 0xFFFFFFFFULL);
- hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
-
- if (asprintf(&p, "%u %u", lo, hi) < 0)
- return -ENOMEM;
-
- r = write_string_file(fn, p);
- free(p);
-
- return r;
-}
-
-int capability_bounding_set_drop_usermode(uint64_t drop) {
- int r;
-
- r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
- if (r < 0)
- return r;
-
- r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
- if (r < 0)
- return r;
-
- return r;
-}
-
-int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
- _cleanup_cap_free_ cap_t d = NULL;
- unsigned i, j = 0;
- int r;
-
- /* Unfortunately we cannot leave privilege dropping to PID 1
- * here, since we want to run as user but want to keep some
- * capabilities. Since file capabilities have been introduced
- * this cannot be done across exec() anymore, unless our
- * binary has the capability configured in the file system,
- * which we want to avoid. */
-
- if (setresgid(gid, gid, gid) < 0)
- return log_error_errno(errno, "Failed to change group ID: %m");
-
- if (setgroups(0, NULL) < 0)
- return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
-
- /* Ensure we keep the permitted caps across the setresuid() */
- if (prctl(PR_SET_KEEPCAPS, 1) < 0)
- return log_error_errno(errno, "Failed to enable keep capabilities flag: %m");
-
- r = setresuid(uid, uid, uid);
- if (r < 0)
- return log_error_errno(errno, "Failed to change user ID: %m");
-
- if (prctl(PR_SET_KEEPCAPS, 0) < 0)
- return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
-
- /* Drop all caps from the bounding set, except the ones we want */
- r = capability_bounding_set_drop(~keep_capabilities, true);
- if (r < 0)
- return log_error_errno(r, "Failed to drop capabilities: %m");
-
- /* Now upgrade the permitted caps we still kept to effective caps */
- d = cap_init();
- if (!d)
- return log_oom();
-
- if (keep_capabilities) {
- cap_value_t bits[u64log2(keep_capabilities) + 1];
-
- for (i = 0; i < ELEMENTSOF(bits); i++)
- if (keep_capabilities & (1ULL << i))
- bits[j++] = i;
-
- /* use enough bits */
- assert(i == 64 || (keep_capabilities >> i) == 0);
- /* don't use too many bits */
- assert(keep_capabilities & (1ULL << (i - 1)));
-
- if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) {
- log_error_errno(errno, "Failed to enable capabilities bits: %m");
- return -errno;
- }
-
- if (cap_set_proc(d) < 0)
- return log_error_errno(errno, "Failed to increase capabilities: %m");
- }
-
- return 0;
-}
-
-int drop_capability(cap_value_t cv) {
- _cleanup_cap_free_ cap_t tmp_cap = NULL;
-
- tmp_cap = cap_get_proc();
- if (!tmp_cap)
- return -errno;
-
- if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
- (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
- (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
- return -errno;
-
- if (cap_set_proc(tmp_cap) < 0)
- return -errno;
-
- return 0;
-}
diff --git a/src/shared/capability.h b/src/shared/capability.h
deleted file mode 100644
index 4eb5c2a..0000000
--- a/src/shared/capability.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <sys/capability.h>
-
-#include "util.h"
-
-unsigned long cap_last_cap(void);
-int have_effective_cap(int value);
-int capability_bounding_set_drop(uint64_t drop, bool right_now);
-int capability_bounding_set_drop_usermode(uint64_t drop);
-
-int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
-
-int drop_capability(cap_value_t cv);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free);
-#define _cleanup_cap_free_ _cleanup_(cap_freep)
-
-static inline void cap_free_charpp(char **p) {
- if (*p)
- cap_free(*p);
-}
-#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
deleted file mode 100644
index d83cdf7..0000000
--- a/src/shared/cgroup-util.c
+++ /dev/null
@@ -1,1917 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <errno.h>
-#include <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <ftw.h>
-
-#include "cgroup-util.h"
-#include "set.h"
-#include "macro.h"
-#include "util.h"
-#include "formats-util.h"
-#include "process-util.h"
-#include "path-util.h"
-#include "unit-name.h"
-#include "fileio.h"
-#include "special.h"
-#include "mkdir.h"
-#include "login-shared.h"
-
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
- _cleanup_free_ char *fs = NULL;
- FILE *f;
- int r;
-
- assert(_f);
-
- r = cg_get_path(controller, path, "cgroup.procs", &fs);
- if (r < 0)
- return r;
-
- f = fopen(fs, "re");
- if (!f)
- return -errno;
-
- *_f = f;
- return 0;
-}
-
-int cg_read_pid(FILE *f, pid_t *_pid) {
- unsigned long ul;
-
- /* Note that the cgroup.procs might contain duplicates! See
- * cgroups.txt for details. */
-
- assert(f);
- assert(_pid);
-
- errno = 0;
- if (fscanf(f, "%lu", &ul) != 1) {
-
- if (feof(f))
- return 0;
-
- return errno ? -errno : -EIO;
- }
-
- if (ul <= 0)
- return -EIO;
-
- *_pid = (pid_t) ul;
- return 1;
-}
-
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
- _cleanup_free_ char *fs = NULL;
- int r;
- DIR *d;
-
- assert(_d);
-
- /* This is not recursive! */
-
- r = cg_get_path(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- d = opendir(fs);
- if (!d)
- return -errno;
-
- *_d = d;
- return 0;
-}
-
-int cg_read_subgroup(DIR *d, char **fn) {
- struct dirent *de;
-
- assert(d);
- assert(fn);
-
- FOREACH_DIRENT(de, d, return -errno) {
- char *b;
-
- if (de->d_type != DT_DIR)
- continue;
-
- if (streq(de->d_name, ".") ||
- streq(de->d_name, ".."))
- continue;
-
- b = strdup(de->d_name);
- if (!b)
- return -ENOMEM;
-
- *fn = b;
- return 1;
- }
-
- return 0;
-}
-
-int cg_rmdir(const char *controller, const char *path) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- r = cg_get_path(controller, path, NULL, &p);
- if (r < 0)
- return r;
-
- r = rmdir(p);
- if (r < 0 && errno != ENOENT)
- return -errno;
-
- return 0;
-}
-
-int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
- _cleanup_set_free_ Set *allocated_set = NULL;
- bool done = false;
- int r, ret = 0;
- pid_t my_pid;
-
- assert(sig >= 0);
-
- /* This goes through the tasks list and kills them all. This
- * is repeated until no further processes are added to the
- * tasks list, to properly handle forking processes */
-
- if (!s) {
- s = allocated_set = set_new(NULL);
- if (!s)
- return -ENOMEM;
- }
-
- my_pid = getpid();
-
- do {
- _cleanup_fclose_ FILE *f = NULL;
- pid_t pid = 0;
- done = true;
-
- r = cg_enumerate_processes(controller, path, &f);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
-
- if (ignore_self && pid == my_pid)
- continue;
-
- if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
- continue;
-
- /* If we haven't killed this process yet, kill
- * it */
- if (kill(pid, sig) < 0) {
- if (ret >= 0 && errno != ESRCH)
- ret = -errno;
- } else {
- if (sigcont && sig != SIGKILL)
- kill(pid, SIGCONT);
-
- if (ret == 0)
- ret = 1;
- }
-
- done = false;
-
- r = set_put(s, LONG_TO_PTR(pid));
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
- }
-
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
-
- /* To avoid racing against processes which fork
- * quicker than we can kill them we repeat this until
- * no new pids need to be killed. */
-
- } while (!done);
-
- return ret;
-}
-
-int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
- _cleanup_set_free_ Set *allocated_set = NULL;
- _cleanup_closedir_ DIR *d = NULL;
- int r, ret = 0;
- char *fn;
-
- assert(path);
- assert(sig >= 0);
-
- if (!s) {
- s = allocated_set = set_new(NULL);
- if (!s)
- return -ENOMEM;
- }
-
- ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
-
- r = cg_enumerate_subgroups(controller, path, &d);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = strjoin(path, "/", fn, NULL);
- free(fn);
- if (!p)
- return -ENOMEM;
-
- r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
- if (ret >= 0 && r != 0)
- ret = r;
- }
-
- if (ret >= 0 && r < 0)
- ret = r;
-
- if (rem) {
- r = cg_rmdir(controller, path);
- if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
- return r;
- }
-
- return ret;
-}
-
-int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
- bool done = false;
- _cleanup_set_free_ Set *s = NULL;
- int r, ret = 0;
- pid_t my_pid;
-
- assert(cfrom);
- assert(pfrom);
- assert(cto);
- assert(pto);
-
- s = set_new(NULL);
- if (!s)
- return -ENOMEM;
-
- my_pid = getpid();
-
- do {
- _cleanup_fclose_ FILE *f = NULL;
- pid_t pid = 0;
- done = true;
-
- r = cg_enumerate_processes(cfrom, pfrom, &f);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
-
- /* This might do weird stuff if we aren't a
- * single-threaded program. However, we
- * luckily know we are not */
- if (ignore_self && pid == my_pid)
- continue;
-
- if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
- continue;
-
- r = cg_attach(cto, pto, pid);
- if (r < 0) {
- if (ret >= 0 && r != -ESRCH)
- ret = r;
- } else if (ret == 0)
- ret = 1;
-
- done = false;
-
- r = set_put(s, LONG_TO_PTR(pid));
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
- }
-
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
- } while (!done);
-
- return ret;
-}
-
-int cg_migrate_recursive(
- const char *cfrom,
- const char *pfrom,
- const char *cto,
- const char *pto,
- bool ignore_self,
- bool rem) {
-
- _cleanup_closedir_ DIR *d = NULL;
- int r, ret = 0;
- char *fn;
-
- assert(cfrom);
- assert(pfrom);
- assert(cto);
- assert(pto);
-
- ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
-
- r = cg_enumerate_subgroups(cfrom, pfrom, &d);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = strjoin(pfrom, "/", fn, NULL);
- free(fn);
- if (!p) {
- if (ret >= 0)
- return -ENOMEM;
-
- return ret;
- }
-
- r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
- if (r != 0 && ret >= 0)
- ret = r;
- }
-
- if (r < 0 && ret >= 0)
- ret = r;
-
- if (rem) {
- r = cg_rmdir(cfrom, pfrom);
- if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
- return r;
- }
-
- return ret;
-}
-
-int cg_migrate_recursive_fallback(
- const char *cfrom,
- const char *pfrom,
- const char *cto,
- const char *pto,
- bool ignore_self,
- bool rem) {
-
- int r;
-
- assert(cfrom);
- assert(pfrom);
- assert(cto);
- assert(pto);
-
- r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
- if (r < 0) {
- char prefix[strlen(pto) + 1];
-
- /* This didn't work? Then let's try all prefixes of the destination */
-
- PATH_FOREACH_PREFIX(prefix, pto) {
- r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
- if (r >= 0)
- break;
- }
- }
-
- return 0;
-}
-
-static const char *normalize_controller(const char *controller) {
-
- assert(controller);
-
- if (startswith(controller, "name="))
- return controller + 5;
- else
- return controller;
-}
-
-static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
- char *t = NULL;
-
- if (!isempty(controller)) {
- if (!isempty(path) && !isempty(suffix))
- t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
- else if (!isempty(path))
- t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
- else if (!isempty(suffix))
- t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
- else
- t = strappend("/sys/fs/cgroup/", controller);
- } else {
- if (!isempty(path) && !isempty(suffix))
- t = strjoin(path, "/", suffix, NULL);
- else if (!isempty(path))
- t = strdup(path);
- else
- return -EINVAL;
- }
-
- if (!t)
- return -ENOMEM;
-
- *fs = path_kill_slashes(t);
- return 0;
-}
-
-int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
- const char *p;
- static thread_local bool good = false;
-
- assert(fs);
-
- if (controller && !cg_controller_is_valid(controller))
- return -EINVAL;
-
- if (_unlikely_(!good)) {
- int r;
-
- r = path_is_mount_point("/sys/fs/cgroup", 0);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOENT;
-
- /* Cache this to save a few stat()s */
- good = true;
- }
-
- p = controller ? normalize_controller(controller) : NULL;
-
- return join_path(p, path, suffix, fs);
-}
-
-static int check_hierarchy(const char *p) {
- const char *cc;
-
- assert(p);
-
- if (!filename_is_valid(p))
- return 0;
-
- /* Check if this controller actually really exists */
- cc = strjoina("/sys/fs/cgroup/", p);
- if (laccess(cc, F_OK) < 0)
- return -errno;
-
- return 0;
-}
-
-int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
- const char *p;
- int r;
-
- assert(fs);
-
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
-
- /* Normalize the controller syntax */
- p = normalize_controller(controller);
-
- /* Check if this controller actually really exists */
- r = check_hierarchy(p);
- if (r < 0)
- return r;
-
- return join_path(p, path, suffix, fs);
-}
-
-static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
- assert(path);
- assert(sb);
- assert(ftwbuf);
-
- if (typeflag != FTW_DP)
- return 0;
-
- if (ftwbuf->level < 1)
- return 0;
-
- rmdir(path);
- return 0;
-}
-
-int cg_trim(const char *controller, const char *path, bool delete_root) {
- _cleanup_free_ char *fs = NULL;
- int r = 0;
-
- assert(path);
-
- r = cg_get_path(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- errno = 0;
- if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
- r = errno ? -errno : -EIO;
-
- if (delete_root) {
- if (rmdir(fs) < 0 && errno != ENOENT)
- return -errno;
- }
-
- return r;
-}
-
-int cg_delete(const char *controller, const char *path) {
- _cleanup_free_ char *parent = NULL;
- int r;
-
- assert(path);
-
- r = path_get_parent(path, &parent);
- if (r < 0)
- return r;
-
- r = cg_migrate_recursive(controller, path, controller, parent, false, true);
- return r == -ENOENT ? 0 : r;
-}
-
-int cg_create(const char *controller, const char *path) {
- _cleanup_free_ char *fs = NULL;
- int r;
-
- r = cg_get_path_and_check(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- r = mkdir_parents(fs, 0755);
- if (r < 0)
- return r;
-
- if (mkdir(fs, 0755) < 0) {
-
- if (errno == EEXIST)
- return 0;
-
- return -errno;
- }
-
- return 1;
-}
-
-int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
- int r, q;
-
- assert(pid >= 0);
-
- r = cg_create(controller, path);
- if (r < 0)
- return r;
-
- q = cg_attach(controller, path, pid);
- if (q < 0)
- return q;
-
- /* This does not remove the cgroup on failure */
- return r;
-}
-
-int cg_attach(const char *controller, const char *path, pid_t pid) {
- _cleanup_free_ char *fs = NULL;
- char c[DECIMAL_STR_MAX(pid_t) + 2];
- int r;
-
- assert(path);
- assert(pid >= 0);
-
- r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
- if (r < 0)
- return r;
-
- if (pid == 0)
- pid = getpid();
-
- snprintf(c, sizeof(c), PID_FMT"\n", pid);
-
- return write_string_file_no_create(fs, c);
-}
-
-int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
- int r;
-
- assert(controller);
- assert(path);
- assert(pid >= 0);
-
- r = cg_attach(controller, path, pid);
- if (r < 0) {
- char prefix[strlen(path) + 1];
-
- /* This didn't work? Then let's try all prefixes of
- * the destination */
-
- PATH_FOREACH_PREFIX(prefix, path) {
- r = cg_attach(controller, prefix, pid);
- if (r >= 0)
- break;
- }
- }
-
- return 0;
-}
-
-int cg_set_group_access(
- const char *controller,
- const char *path,
- mode_t mode,
- uid_t uid,
- gid_t gid) {
-
- _cleanup_free_ char *fs = NULL;
- int r;
-
- assert(path);
-
- if (mode != MODE_INVALID)
- mode &= 0777;
-
- r = cg_get_path(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- return chmod_and_chown(fs, mode, uid, gid);
-}
-
-int cg_set_task_access(
- const char *controller,
- const char *path,
- mode_t mode,
- uid_t uid,
- gid_t gid) {
-
- _cleanup_free_ char *fs = NULL, *procs = NULL;
- int r;
-
- assert(path);
-
- if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
- return 0;
-
- if (mode != MODE_INVALID)
- mode &= 0666;
-
- r = cg_get_path(controller, path, "cgroup.procs", &fs);
- if (r < 0)
- return r;
-
- r = chmod_and_chown(fs, mode, uid, gid);
- if (r < 0)
- return r;
-
- /* Compatibility, Always keep values for "tasks" in sync with
- * "cgroup.procs" */
- r = cg_get_path(controller, path, "tasks", &procs);
- if (r < 0)
- return r;
-
- return chmod_and_chown(procs, mode, uid, gid);
-}
-
-int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- const char *fs;
- size_t cs;
-
- assert(path);
- assert(pid >= 0);
-
- if (controller) {
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
-
- controller = normalize_controller(controller);
- } else
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
- fs = procfs_file_alloca(pid, "cgroup");
-
- f = fopen(fs, "re");
- if (!f)
- return errno == ENOENT ? -ESRCH : -errno;
-
- cs = strlen(controller);
-
- FOREACH_LINE(line, f, return -errno) {
- char *l, *p, *e;
- size_t k;
- const char *word, *state;
- bool found = false;
-
- truncate_nl(line);
-
- l = strchr(line, ':');
- if (!l)
- continue;
-
- l++;
- e = strchr(l, ':');
- if (!e)
- continue;
-
- *e = 0;
-
- FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
-
- if (k == cs && memcmp(word, controller, cs) == 0) {
- found = true;
- break;
- }
-
- if (k == 5 + cs &&
- memcmp(word, "name=", 5) == 0 &&
- memcmp(word+5, controller, cs) == 0) {
- found = true;
- break;
- }
- }
-
- if (!found)
- continue;
-
- p = strdup(e + 1);
- if (!p)
- return -ENOMEM;
-
- *path = p;
- return 0;
- }
-
- return -ENOENT;
-}
-
-int cg_install_release_agent(const char *controller, const char *agent) {
- _cleanup_free_ char *fs = NULL, *contents = NULL;
- char *sc;
- int r;
-
- assert(agent);
-
- r = cg_get_path(controller, NULL, "release_agent", &fs);
- if (r < 0)
- return r;
-
- r = read_one_line_file(fs, &contents);
- if (r < 0)
- return r;
-
- sc = strstrip(contents);
- if (sc[0] == 0) {
- r = write_string_file_no_create(fs, agent);
- if (r < 0)
- return r;
- } else if (!streq(sc, agent))
- return -EEXIST;
-
- free(fs);
- fs = NULL;
- r = cg_get_path(controller, NULL, "notify_on_release", &fs);
- if (r < 0)
- return r;
-
- free(contents);
- contents = NULL;
- r = read_one_line_file(fs, &contents);
- if (r < 0)
- return r;
-
- sc = strstrip(contents);
- if (streq(sc, "0")) {
- r = write_string_file_no_create(fs, "1");
- if (r < 0)
- return r;
-
- return 1;
- }
-
- if (!streq(sc, "1"))
- return -EIO;
-
- return 0;
-}
-
-int cg_uninstall_release_agent(const char *controller) {
- _cleanup_free_ char *fs = NULL;
- int r;
-
- r = cg_get_path(controller, NULL, "notify_on_release", &fs);
- if (r < 0)
- return r;
-
- r = write_string_file_no_create(fs, "0");
- if (r < 0)
- return r;
-
- free(fs);
- fs = NULL;
-
- r = cg_get_path(controller, NULL, "release_agent", &fs);
- if (r < 0)
- return r;
-
- r = write_string_file_no_create(fs, "");
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
- _cleanup_fclose_ FILE *f = NULL;
- pid_t pid = 0, self_pid;
- bool found = false;
- int r;
-
- assert(path);
-
- r = cg_enumerate_processes(controller, path, &f);
- if (r < 0)
- return r == -ENOENT ? 1 : r;
-
- self_pid = getpid();
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
-
- if (ignore_self && pid == self_pid)
- continue;
-
- found = true;
- break;
- }
-
- if (r < 0)
- return r;
-
- return !found;
-}
-
-int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
- _cleanup_closedir_ DIR *d = NULL;
- char *fn;
- int r;
-
- assert(path);
-
- r = cg_is_empty(controller, path, ignore_self);
- if (r <= 0)
- return r;
-
- r = cg_enumerate_subgroups(controller, path, &d);
- if (r < 0)
- return r == -ENOENT ? 1 : r;
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = strjoin(path, "/", fn, NULL);
- free(fn);
- if (!p)
- return -ENOMEM;
-
- r = cg_is_empty_recursive(controller, p, ignore_self);
- if (r <= 0)
- return r;
- }
-
- if (r < 0)
- return r;
-
- return 1;
-}
-
-int cg_split_spec(const char *spec, char **controller, char **path) {
- const char *e;
- char *t = NULL, *u = NULL;
- _cleanup_free_ char *v = NULL;
-
- assert(spec);
-
- if (*spec == '/') {
- if (!path_is_safe(spec))
- return -EINVAL;
-
- if (path) {
- t = strdup(spec);
- if (!t)
- return -ENOMEM;
-
- *path = path_kill_slashes(t);
- }
-
- if (controller)
- *controller = NULL;
-
- return 0;
- }
-
- e = strchr(spec, ':');
- if (!e) {
- if (!cg_controller_is_valid(spec))
- return -EINVAL;
-
- if (controller) {
- t = strdup(normalize_controller(spec));
- if (!t)
- return -ENOMEM;
-
- *controller = t;
- }
-
- if (path)
- *path = NULL;
-
- return 0;
- }
-
- v = strndup(spec, e-spec);
- if (!v)
- return -ENOMEM;
- t = strdup(normalize_controller(v));
- if (!t)
- return -ENOMEM;
- if (!cg_controller_is_valid(t)) {
- free(t);
- return -EINVAL;
- }
-
- if (streq(e+1, "")) {
- u = strdup("/");
- if (!u) {
- free(t);
- return -ENOMEM;
- }
- } else {
- u = strdup(e+1);
- if (!u) {
- free(t);
- return -ENOMEM;
- }
-
- if (!path_is_safe(u) ||
- !path_is_absolute(u)) {
- free(t);
- free(u);
- return -EINVAL;
- }
-
- path_kill_slashes(u);
- }
-
- if (controller)
- *controller = t;
- else
- free(t);
-
- if (path)
- *path = u;
- else
- free(u);
-
- return 0;
-}
-
-int cg_mangle_path(const char *path, char **result) {
- _cleanup_free_ char *c = NULL, *p = NULL;
- char *t;
- int r;
-
- assert(path);
- assert(result);
-
- /* First, check if it already is a filesystem path */
- if (path_startswith(path, "/sys/fs/cgroup")) {
-
- t = strdup(path);
- if (!t)
- return -ENOMEM;
-
- *result = path_kill_slashes(t);
- return 0;
- }
-
- /* Otherwise, treat it as cg spec */
- r = cg_split_spec(path, &c, &p);
- if (r < 0)
- return r;
-
- return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
-}
-
-int cg_get_root_path(char **path) {
- char *p, *e;
- int r;
-
- assert(path);
-
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
- if (r < 0)
- return r;
-
- e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
- if (e)
- *e = 0;
-
- *path = p;
- return 0;
-}
-
-int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
- _cleanup_free_ char *rt = NULL;
- char *p;
- int r;
-
- assert(cgroup);
- assert(shifted);
-
- if (!root) {
- /* If the root was specified let's use that, otherwise
- * let's determine it from PID 1 */
-
- r = cg_get_root_path(&rt);
- if (r < 0)
- return r;
-
- root = rt;
- }
-
- p = path_startswith(cgroup, root);
- if (p)
- *shifted = p - 1;
- else
- *shifted = cgroup;
-
- return 0;
-}
-
-int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
- _cleanup_free_ char *raw = NULL;
- const char *c;
- int r;
-
- assert(pid >= 0);
- assert(cgroup);
-
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
- if (r < 0)
- return r;
-
- r = cg_shift_path(raw, root, &c);
- if (r < 0)
- return r;
-
- if (c == raw) {
- *cgroup = raw;
- raw = NULL;
- } else {
- char *n;
-
- n = strdup(c);
- if (!n)
- return -ENOMEM;
-
- *cgroup = n;
- }
-
- return 0;
-}
-
-int cg_path_decode_unit(const char *cgroup, char **unit){
- char *c, *s;
- size_t n;
-
- assert(cgroup);
- assert(unit);
-
- n = strcspn(cgroup, "/");
- if (n < 3)
- return -ENXIO;
-
- c = strndupa(cgroup, n);
- c = cg_unescape(c);
-
- if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
- return -ENXIO;
-
- s = strdup(c);
- if (!s)
- return -ENOMEM;
-
- *unit = s;
- return 0;
-}
-
-static bool valid_slice_name(const char *p, size_t n) {
-
- if (!p)
- return false;
-
- if (n < strlen("x.slice"))
- return false;
-
- if (memcmp(p + n - 6, ".slice", 6) == 0) {
- char buf[n+1], *c;
-
- memcpy(buf, p, n);
- buf[n] = 0;
-
- c = cg_unescape(buf);
-
- return unit_name_is_valid(c, UNIT_NAME_PLAIN);
- }
-
- return false;
-}
-
-static const char *skip_slices(const char *p) {
- assert(p);
-
- /* Skips over all slice assignments */
-
- for (;;) {
- size_t n;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (!valid_slice_name(p, n))
- return p;
-
- p += n;
- }
-}
-
-int cg_path_get_unit(const char *path, char **ret) {
- const char *e;
- char *unit;
- int r;
-
- assert(path);
- assert(ret);
-
- e = skip_slices(path);
-
- r = cg_path_decode_unit(e, &unit);
- if (r < 0)
- return r;
-
- /* We skipped over the slices, don't accept any now */
- if (endswith(unit, ".slice")) {
- free(unit);
- return -ENXIO;
- }
-
- *ret = unit;
- return 0;
-}
-
-int cg_pid_get_unit(pid_t pid, char **unit) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(unit);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_unit(cgroup, unit);
-}
-
-/**
- * Skip session-*.scope, but require it to be there.
- */
-static const char *skip_session(const char *p) {
- size_t n;
-
- if (isempty(p))
- return NULL;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (n < strlen("session-x.scope"))
- return NULL;
-
- if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
- char buf[n - 8 - 6 + 1];
-
- memcpy(buf, p + 8, n - 8 - 6);
- buf[n - 8 - 6] = 0;
-
- /* Note that session scopes never need unescaping,
- * since they cannot conflict with the kernel's own
- * names, hence we don't need to call cg_unescape()
- * here. */
-
- if (!session_id_valid(buf))
- return false;
-
- p += n;
- p += strspn(p, "/");
- return p;
- }
-
- return NULL;
-}
-
-/**
- * Skip user@*.service, but require it to be there.
- */
-static const char *skip_user_manager(const char *p) {
- size_t n;
-
- if (isempty(p))
- return NULL;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (n < strlen("user at x.service"))
- return NULL;
-
- if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
- char buf[n - 5 - 8 + 1];
-
- memcpy(buf, p + 5, n - 5 - 8);
- buf[n - 5 - 8] = 0;
-
- /* Note that user manager services never need unescaping,
- * since they cannot conflict with the kernel's own
- * names, hence we don't need to call cg_unescape()
- * here. */
-
- if (parse_uid(buf, NULL) < 0)
- return NULL;
-
- p += n;
- p += strspn(p, "/");
-
- return p;
- }
-
- return NULL;
-}
-
-static const char *skip_user_prefix(const char *path) {
- const char *e, *t;
-
- assert(path);
-
- /* Skip slices, if there are any */
- e = skip_slices(path);
-
- /* Skip the user manager, if it's in the path now... */
- t = skip_user_manager(e);
- if (t)
- return t;
-
- /* Alternatively skip the user session if it is in the path... */
- return skip_session(e);
-}
-
-int cg_path_get_user_unit(const char *path, char **ret) {
- const char *t;
-
- assert(path);
- assert(ret);
-
- t = skip_user_prefix(path);
- if (!t)
- return -ENXIO;
-
- /* And from here on it looks pretty much the same as for a
- * system unit, hence let's use the same parser from here
- * on. */
- return cg_path_get_unit(t, ret);
-}
-
-int cg_pid_get_user_unit(pid_t pid, char **unit) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(unit);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_user_unit(cgroup, unit);
-}
-
-int cg_path_get_machine_name(const char *path, char **machine) {
- _cleanup_free_ char *u = NULL, *sl = NULL;
- int r;
-
- r = cg_path_get_unit(path, &u);
- if (r < 0)
- return r;
-
- sl = strjoin("/run/systemd/machines/unit:", u, NULL);
- if (!sl)
- return -ENOMEM;
-
- return readlink_malloc(sl, machine);
-}
-
-int cg_pid_get_machine_name(pid_t pid, char **machine) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(machine);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_machine_name(cgroup, machine);
-}
-
-int cg_path_get_session(const char *path, char **session) {
- _cleanup_free_ char *unit = NULL;
- char *start, *end;
- int r;
-
- assert(path);
-
- r = cg_path_get_unit(path, &unit);
- if (r < 0)
- return r;
-
- start = startswith(unit, "session-");
- if (!start)
- return -ENXIO;
- end = endswith(start, ".scope");
- if (!end)
- return -ENXIO;
-
- *end = 0;
- if (!session_id_valid(start))
- return -ENXIO;
-
- if (session) {
- char *rr;
-
- rr = strdup(start);
- if (!rr)
- return -ENOMEM;
-
- *session = rr;
- }
-
- return 0;
-}
-
-int cg_pid_get_session(pid_t pid, char **session) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_session(cgroup, session);
-}
-
-int cg_path_get_owner_uid(const char *path, uid_t *uid) {
- _cleanup_free_ char *slice = NULL;
- char *start, *end;
- int r;
-
- assert(path);
-
- r = cg_path_get_slice(path, &slice);
- if (r < 0)
- return r;
-
- start = startswith(slice, "user-");
- if (!start)
- return -ENXIO;
- end = endswith(start, ".slice");
- if (!end)
- return -ENXIO;
-
- *end = 0;
- if (parse_uid(start, uid) < 0)
- return -ENXIO;
-
- return 0;
-}
-
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_owner_uid(cgroup, uid);
-}
-
-int cg_path_get_slice(const char *p, char **slice) {
- const char *e = NULL;
-
- assert(p);
- assert(slice);
-
- /* Finds the right-most slice unit from the beginning, but
- * stops before we come to the first non-slice unit. */
-
- for (;;) {
- size_t n;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (!valid_slice_name(p, n)) {
-
- if (!e) {
- char *s;
-
- s = strdup("-.slice");
- if (!s)
- return -ENOMEM;
-
- *slice = s;
- return 0;
- }
-
- return cg_path_decode_unit(e, slice);
- }
-
- e = p;
- p += n;
- }
-}
-
-int cg_pid_get_slice(pid_t pid, char **slice) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(slice);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_slice(cgroup, slice);
-}
-
-int cg_path_get_user_slice(const char *p, char **slice) {
- const char *t;
- assert(p);
- assert(slice);
-
- t = skip_user_prefix(p);
- if (!t)
- return -ENXIO;
-
- /* And now it looks pretty much the same as for a system
- * slice, so let's just use the same parser from here on. */
- return cg_path_get_slice(t, slice);
-}
-
-int cg_pid_get_user_slice(pid_t pid, char **slice) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(slice);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_user_slice(cgroup, slice);
-}
-
-char *cg_escape(const char *p) {
- bool need_prefix = false;
-
- /* This implements very minimal escaping for names to be used
- * as file names in the cgroup tree: any name which might
- * conflict with a kernel name or is prefixed with '_' is
- * prefixed with a '_'. That way, when reading cgroup names it
- * is sufficient to remove a single prefixing underscore if
- * there is one. */
-
- /* The return value of this function (unlike cg_unescape())
- * needs free()! */
-
- if (p[0] == 0 ||
- p[0] == '_' ||
- p[0] == '.' ||
- streq(p, "notify_on_release") ||
- streq(p, "release_agent") ||
- streq(p, "tasks"))
- need_prefix = true;
- else {
- const char *dot;
-
- dot = strrchr(p, '.');
- if (dot) {
-
- if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
- need_prefix = true;
- else {
- char *n;
-
- n = strndupa(p, dot - p);
-
- if (check_hierarchy(n) >= 0)
- need_prefix = true;
- }
- }
- }
-
- if (need_prefix)
- return strappend("_", p);
- else
- return strdup(p);
-}
-
-char *cg_unescape(const char *p) {
- assert(p);
-
- /* The return value of this function (unlike cg_escape())
- * doesn't need free()! */
-
- if (p[0] == '_')
- return (char*) p+1;
-
- return (char*) p;
-}
-
-#define CONTROLLER_VALID \
- DIGITS LETTERS \
- "_"
-
-bool cg_controller_is_valid(const char *p) {
- const char *t, *s;
-
- if (!p)
- return false;
-
- s = startswith(p, "name=");
- if (s)
- p = s;
-
- if (*p == 0 || *p == '_')
- return false;
-
- for (t = p; *t; t++)
- if (!strchr(CONTROLLER_VALID, *t))
- return false;
-
- if (t - p > FILENAME_MAX)
- return false;
-
- return true;
-}
-
-int cg_slice_to_path(const char *unit, char **ret) {
- _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
- const char *dash;
- int r;
-
- assert(unit);
- assert(ret);
-
- if (streq(unit, "-.slice")) {
- char *x;
-
- x = strdup("");
- if (!x)
- return -ENOMEM;
- *ret = x;
- return 0;
- }
-
- if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
- return -EINVAL;
-
- if (!endswith(unit, ".slice"))
- return -EINVAL;
-
- r = unit_name_to_prefix(unit, &p);
- if (r < 0)
- return r;
-
- dash = strchr(p, '-');
-
- /* Don't allow initial dashes */
- if (dash == p)
- return -EINVAL;
-
- while (dash) {
- _cleanup_free_ char *escaped = NULL;
- char n[dash - p + sizeof(".slice")];
-
- /* Don't allow trailing or double dashes */
- if (dash[1] == 0 || dash[1] == '-')
- return -EINVAL;
-
- strcpy(stpncpy(n, p, dash - p), ".slice");
- if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
- return -EINVAL;
-
- escaped = cg_escape(n);
- if (!escaped)
- return -ENOMEM;
-
- if (!strextend(&s, escaped, "/", NULL))
- return -ENOMEM;
-
- dash = strchr(dash+1, '-');
- }
-
- e = cg_escape(unit);
- if (!e)
- return -ENOMEM;
-
- if (!strextend(&s, e, NULL))
- return -ENOMEM;
-
- *ret = s;
- s = NULL;
-
- return 0;
-}
-
-int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- r = cg_get_path(controller, path, attribute, &p);
- if (r < 0)
- return r;
-
- return write_string_file_no_create(p, value);
-}
-
-int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- r = cg_get_path(controller, path, attribute, &p);
- if (r < 0)
- return r;
-
- return read_one_line_file(p, ret);
-}
-
-static const char mask_names[] =
- "cpu\0"
- "cpuacct\0"
- "blkio\0"
- "memory\0"
- "devices\0";
-
-int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
-
- /* This one will create a cgroup in our private tree, but also
- * duplicate it in the trees specified in mask, and remove it
- * in all others */
-
- /* First create the cgroup in our own hierarchy. */
- r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
- if (r < 0)
- return r;
-
- /* Then, do the same in the other hierarchies */
- NULSTR_FOREACH(n, mask_names) {
- if (mask & bit)
- cg_create(n, path);
- else if (supported & bit)
- cg_trim(n, path, true);
-
- bit <<= 1;
- }
-
- return 0;
-}
-
-int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
-
- r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
- if (r < 0)
- return r;
-
- NULSTR_FOREACH(n, mask_names) {
-
- if (supported & bit) {
- const char *p = NULL;
-
- if (path_callback)
- p = path_callback(bit, userdata);
-
- if (!p)
- p = path;
-
- cg_attach_fallback(n, path, pid);
- }
-
- bit <<= 1;
- }
-
- return 0;
-}
-
-int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
- Iterator i;
- void *pidp;
- int r = 0;
-
- SET_FOREACH(pidp, pids, i) {
- pid_t pid = PTR_TO_LONG(pidp);
- int q;
-
- q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
- if (q < 0)
- r = q;
- }
-
- return r;
-}
-
-int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
-
- if (!path_equal(from, to)) {
- r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
- if (r < 0)
- return r;
- }
-
- NULSTR_FOREACH(n, mask_names) {
- if (supported & bit) {
- const char *p = NULL;
-
- if (to_callback)
- p = to_callback(bit, userdata);
-
- if (!p)
- p = to;
-
- cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
- }
-
- bit <<= 1;
- }
-
- return 0;
-}
-
-int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
-
- r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
- if (r < 0)
- return r;
-
- NULSTR_FOREACH(n, mask_names) {
- if (supported & bit)
- cg_trim(n, path, delete_root);
-
- bit <<= 1;
- }
-
- return 0;
-}
-
-CGroupControllerMask cg_mask_supported(void) {
- CGroupControllerMask bit = 1, mask = 0;
- const char *n;
-
- NULSTR_FOREACH(n, mask_names) {
- if (check_hierarchy(n) >= 0)
- mask |= bit;
-
- bit <<= 1;
- }
-
- return mask;
-}
-
-int cg_kernel_controllers(Set *controllers) {
- _cleanup_fclose_ FILE *f = NULL;
- char buf[LINE_MAX];
- int r;
-
- assert(controllers);
-
- f = fopen("/proc/cgroups", "re");
- if (!f) {
- if (errno == ENOENT)
- return 0;
- return -errno;
- }
-
- /* Ignore the header line */
- (void) fgets(buf, sizeof(buf), f);
-
- for (;;) {
- char *controller;
- int enabled = 0;
-
- errno = 0;
- if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
-
- if (feof(f))
- break;
-
- if (ferror(f) && errno)
- return -errno;
-
- return -EBADMSG;
- }
-
- if (!enabled) {
- free(controller);
- continue;
- }
-
- if (!filename_is_valid(controller)) {
- free(controller);
- return -EBADMSG;
- }
-
- r = set_consume(controllers, controller);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
deleted file mode 100644
index fd72e9e..0000000
--- a/src/shared/cgroup-util.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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>
-#include <stdio.h>
-#include <dirent.h>
-
-#include "set.h"
-#include "def.h"
-
-/* A bit mask of well known cgroup controllers */
-typedef enum CGroupControllerMask {
- CGROUP_CPU = 1,
- CGROUP_CPUACCT = 2,
- CGROUP_BLKIO = 4,
- CGROUP_MEMORY = 8,
- CGROUP_DEVICE = 16,
- _CGROUP_CONTROLLER_MASK_ALL = 31
-} CGroupControllerMask;
-
-/*
- * General rules:
- *
- * We accept named hierarchies in the syntax "foo" and "name=foo".
- *
- * We expect that named hierarchies do not conflict in name with a
- * kernel hierarchy, modulo the "name=" prefix.
- *
- * We always generate "normalized" controller names, i.e. without the
- * "name=" prefix.
- *
- * We require absolute cgroup paths. When returning, we will always
- * generate paths with multiple adjacent / removed.
- */
-
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
-int cg_read_pid(FILE *f, pid_t *_pid);
-
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
-int cg_read_subgroup(DIR *d, char **fn);
-
-int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s);
-int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool remove, Set *s);
-
-int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self);
-int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool remove);
-int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem);
-
-int cg_split_spec(const char *spec, char **controller, char **path);
-int cg_mangle_path(const char *path, char **result);
-
-int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
-int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
-
-int cg_pid_get_path(const char *controller, pid_t pid, char **path);
-
-int cg_trim(const char *controller, const char *path, bool delete_root);
-
-int cg_rmdir(const char *controller, const char *path);
-int cg_delete(const char *controller, const char *path);
-
-int cg_create(const char *controller, const char *path);
-int cg_attach(const char *controller, const char *path, pid_t pid);
-int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
-int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
-
-int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
-int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
-
-int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
-int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
-
-int cg_install_release_agent(const char *controller, const char *agent);
-int cg_uninstall_release_agent(const char *controller);
-
-int cg_is_empty(const char *controller, const char *path, bool ignore_self);
-int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
-
-int cg_get_root_path(char **path);
-
-int cg_path_get_session(const char *path, char **session);
-int cg_path_get_owner_uid(const char *path, uid_t *uid);
-int cg_path_get_unit(const char *path, char **unit);
-int cg_path_get_user_unit(const char *path, char **unit);
-int cg_path_get_machine_name(const char *path, char **machine);
-int cg_path_get_slice(const char *path, char **slice);
-int cg_path_get_user_slice(const char *path, char **slice);
-
-int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
-int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
-
-int cg_pid_get_session(pid_t pid, char **session);
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
-int cg_pid_get_unit(pid_t pid, char **unit);
-int cg_pid_get_user_unit(pid_t pid, char **unit);
-int cg_pid_get_machine_name(pid_t pid, char **machine);
-int cg_pid_get_slice(pid_t pid, char **slice);
-int cg_pid_get_user_slice(pid_t pid, char **slice);
-
-int cg_path_decode_unit(const char *cgroup, char **unit);
-
-char *cg_escape(const char *p);
-char *cg_unescape(const char *p) _pure_;
-
-bool cg_controller_is_valid(const char *p);
-
-int cg_slice_to_path(const char *unit, char **ret);
-
-typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata);
-
-int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path);
-int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
-int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
-int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
-int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
-
-CGroupControllerMask cg_mask_supported(void);
-
-int cg_kernel_controllers(Set *controllers);
diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c
deleted file mode 100644
index e4e03df..0000000
--- a/src/shared/clock-util.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010-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 <errno.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <linux/rtc.h>
-
-#include "macro.h"
-#include "util.h"
-#include "clock-util.h"
-
-int clock_get_hwclock(struct tm *tm) {
- _cleanup_close_ int fd = -1;
-
- assert(tm);
-
- fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- /* This leaves the timezone fields of struct tm
- * uninitialized! */
- if (ioctl(fd, RTC_RD_TIME, tm) < 0)
- return -errno;
-
- /* We don't know daylight saving, so we reset this in order not
- * to confuse mktime(). */
- tm->tm_isdst = -1;
-
- return 0;
-}
-
-int clock_set_hwclock(const struct tm *tm) {
- _cleanup_close_ int fd = -1;
-
- assert(tm);
-
- fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (ioctl(fd, RTC_SET_TIME, tm) < 0)
- return -errno;
-
- return 0;
-}
-
-int clock_is_localtime(void) {
- _cleanup_fclose_ FILE *f;
-
- /*
- * The third line of adjtime is "UTC" or "LOCAL" or nothing.
- * # /etc/adjtime
- * 0.0 0 0
- * 0
- * UTC
- */
- f = fopen("/etc/adjtime", "re");
- if (f) {
- char line[LINE_MAX];
- bool b;
-
- b = fgets(line, sizeof(line), f) &&
- fgets(line, sizeof(line), f) &&
- fgets(line, sizeof(line), f);
- if (!b)
- return -EIO;
-
- truncate_nl(line);
- return streq(line, "LOCAL");
-
- } else if (errno != ENOENT)
- return -errno;
-
- return 0;
-}
-
-int clock_set_timezone(int *min) {
- const struct timeval *tv_null = NULL;
- struct timespec ts;
- struct tm *tm;
- int minutesdelta;
- struct timezone tz;
-
- assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
- assert_se(tm = localtime(&ts.tv_sec));
- minutesdelta = tm->tm_gmtoff / 60;
-
- tz.tz_minuteswest = -minutesdelta;
- tz.tz_dsttime = 0; /* DST_NONE */
-
- /*
- * If the RTC does not run in UTC but in local time, the very first
- * call to settimeofday() will set the kernel's timezone and will warp the
- * system clock, so that it runs in UTC instead of the local time we
- * have read from the RTC.
- */
- if (settimeofday(tv_null, &tz) < 0)
- return -errno;
- if (min)
- *min = minutesdelta;
- return 0;
-}
-
-int clock_reset_timewarp(void) {
- const struct timeval *tv_null = NULL;
- struct timezone tz;
-
- tz.tz_minuteswest = 0;
- tz.tz_dsttime = 0; /* DST_NONE */
-
- /*
- * The very first call to settimeofday() does time warp magic. Do a
- * dummy call here, so the time warping is sealed and all later calls
- * behave as expected.
- */
- if (settimeofday(tv_null, &tz) < 0)
- return -errno;
-
- return 0;
-}
diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h
deleted file mode 100644
index 8c2d235..0000000
--- a/src/shared/clock-util.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010-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/>.
-***/
-
-
-int clock_is_localtime(void);
-int clock_set_timezone(int *min);
-int clock_reset_timewarp(void);
-int clock_get_hwclock(struct tm *tm);
-int clock_set_hwclock(const struct tm *tm);
diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
deleted file mode 100644
index da8745b..0000000
--- a/src/shared/conf-files.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <dirent.h>
-
-#include "macro.h"
-#include "util.h"
-#include "missing.h"
-#include "log.h"
-#include "strv.h"
-#include "path-util.h"
-#include "hashmap.h"
-#include "conf-files.h"
-
-static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
- _cleanup_closedir_ DIR *dir = NULL;
- const char *dirpath;
- int r;
-
- assert(path);
- assert(suffix);
-
- dirpath = prefix_roota(root, path);
-
- dir = opendir(dirpath);
- if (!dir) {
- if (errno == ENOENT)
- return 0;
- return -errno;
- }
-
- for (;;) {
- struct dirent *de;
- char *p;
-
- errno = 0;
- de = readdir(dir);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- break;
-
- if (!dirent_is_file_with_suffix(de, suffix))
- continue;
-
- p = strjoin(dirpath, "/", de->d_name, NULL);
- if (!p)
- return -ENOMEM;
-
- r = hashmap_put(h, basename(p), p);
- if (r == -EEXIST) {
- log_debug("Skipping overridden file: %s.", p);
- free(p);
- } else if (r < 0) {
- free(p);
- return r;
- } else if (r == 0) {
- log_debug("Duplicate file %s", p);
- free(p);
- }
- }
-
- return 0;
-}
-
-static int base_cmp(const void *a, const void *b) {
- const char *s1, *s2;
-
- s1 = *(char * const *)a;
- s2 = *(char * const *)b;
- return strcmp(basename(s1), basename(s2));
-}
-
-static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
- _cleanup_hashmap_free_ Hashmap *fh = NULL;
- char **files, **p;
- int r;
-
- assert(strv);
- assert(suffix);
-
- /* This alters the dirs string array */
- if (!path_strv_resolve_uniq(dirs, root))
- return -ENOMEM;
-
- fh = hashmap_new(&string_hash_ops);
- if (!fh)
- return -ENOMEM;
-
- STRV_FOREACH(p, dirs) {
- r = files_add(fh, root, *p, suffix);
- if (r == -ENOMEM) {
- return r;
- } else if (r < 0)
- log_debug_errno(r, "Failed to search for files in %s: %m",
- *p);
- }
-
- files = hashmap_get_strv(fh);
- if (files == NULL) {
- return -ENOMEM;
- }
-
- qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
- *strv = files;
-
- return 0;
-}
-
-int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) {
- _cleanup_strv_free_ char **copy = NULL;
-
- assert(strv);
- assert(suffix);
-
- copy = strv_copy((char**) dirs);
- if (!copy)
- return -ENOMEM;
-
- return conf_files_list_strv_internal(strv, suffix, root, copy);
-}
-
-int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) {
- _cleanup_strv_free_ char **dirs = NULL;
- va_list ap;
-
- assert(strv);
- assert(suffix);
-
- va_start(ap, dir);
- dirs = strv_new_ap(dir, ap);
- va_end(ap);
-
- if (!dirs)
- return -ENOMEM;
-
- return conf_files_list_strv_internal(strv, suffix, root, dirs);
-}
-
-int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) {
- _cleanup_strv_free_ char **dirs = NULL;
-
- assert(strv);
- assert(suffix);
-
- dirs = strv_split_nulstr(d);
- if (!dirs)
- return -ENOMEM;
-
- return conf_files_list_strv_internal(strv, suffix, root, dirs);
-}
diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h
deleted file mode 100644
index 3169a90..0000000
--- a/src/shared/conf-files.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010-2012 Lennart Poettering
- Copyright 2010-2012 Kay Sievers
-
- 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/>.
-***/
-
-
-int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
-int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
-int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs);
diff --git a/src/shared/copy.c b/src/shared/copy.c
deleted file mode 100644
index 1282cb8..0000000
--- a/src/shared/copy.c
+++ /dev/null
@@ -1,507 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/sendfile.h>
-#include <sys/xattr.h>
-
-#include "util.h"
-#include "btrfs-util.h"
-#include "copy.h"
-
-#define COPY_BUFFER_SIZE (16*1024)
-
-int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
- bool try_sendfile = true;
- int r;
-
- assert(fdf >= 0);
- assert(fdt >= 0);
-
- /* Try btrfs reflinks first. */
- if (try_reflink && max_bytes == (off_t) -1) {
- r = btrfs_reflink(fdf, fdt);
- if (r >= 0)
- return r;
- }
-
- for (;;) {
- size_t m = COPY_BUFFER_SIZE;
- ssize_t n;
-
- if (max_bytes != (off_t) -1) {
-
- if (max_bytes <= 0)
- return -EFBIG;
-
- if ((off_t) m > max_bytes)
- m = (size_t) max_bytes;
- }
-
- /* First try sendfile(), unless we already tried */
- if (try_sendfile) {
-
- n = sendfile(fdt, fdf, NULL, m);
- if (n < 0) {
- if (errno != EINVAL && errno != ENOSYS)
- return -errno;
-
- try_sendfile = false;
- /* use fallback below */
- } else if (n == 0) /* EOF */
- break;
- else if (n > 0)
- /* Succcess! */
- goto next;
- }
-
- /* As a fallback just copy bits by hand */
- {
- char buf[m];
-
- n = read(fdf, buf, m);
- if (n < 0)
- return -errno;
- if (n == 0) /* EOF */
- break;
-
- r = loop_write(fdt, buf, (size_t) n, false);
- if (r < 0)
- return r;
- }
-
- next:
- if (max_bytes != (off_t) -1) {
- assert(max_bytes >= n);
- max_bytes -= n;
- }
- }
-
- return 0;
-}
-
-static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
- _cleanup_free_ char *target = NULL;
- int r;
-
- assert(from);
- assert(st);
- assert(to);
-
- r = readlinkat_malloc(df, from, &target);
- if (r < 0)
- return r;
-
- if (symlinkat(target, dt, to) < 0)
- return -errno;
-
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
- return -errno;
-
- return 0;
-}
-
-static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
- _cleanup_close_ int fdf = -1, fdt = -1;
- struct timespec ts[2];
- int r, q;
-
- assert(from);
- assert(st);
- assert(to);
-
- fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fdf < 0)
- return -errno;
-
- fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
- if (fdt < 0)
- return -errno;
-
- r = copy_bytes(fdf, fdt, (off_t) -1, true);
- if (r < 0) {
- unlinkat(dt, to, 0);
- return r;
- }
-
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
- r = -errno;
-
- if (fchmod(fdt, st->st_mode & 07777) < 0)
- r = -errno;
-
- ts[0] = st->st_atim;
- ts[1] = st->st_mtim;
- (void) futimens(fdt, ts);
-
- (void) copy_xattr(fdf, fdt);
-
- q = close(fdt);
- fdt = -1;
-
- if (q < 0) {
- r = -errno;
- unlinkat(dt, to, 0);
- }
-
- return r;
-}
-
-static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
- int r;
-
- assert(from);
- assert(st);
- assert(to);
-
- r = mkfifoat(dt, to, st->st_mode & 07777);
- if (r < 0)
- return -errno;
-
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
- r = -errno;
-
- if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
- r = -errno;
-
- return r;
-}
-
-static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
- int r;
-
- assert(from);
- assert(st);
- assert(to);
-
- r = mknodat(dt, to, st->st_mode, st->st_rdev);
- if (r < 0)
- return -errno;
-
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
- r = -errno;
-
- if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
- r = -errno;
-
- return r;
-}
-
-static int fd_copy_directory(
- int df,
- const char *from,
- const struct stat *st,
- int dt,
- const char *to,
- dev_t original_device,
- bool merge) {
-
- _cleanup_close_ int fdf = -1, fdt = -1;
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- bool created;
- int r;
-
- assert(st);
- assert(to);
-
- if (from)
- fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- else
- fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
-
- d = fdopendir(fdf);
- if (!d)
- return -errno;
- fdf = -1;
-
- r = mkdirat(dt, to, st->st_mode & 07777);
- if (r >= 0)
- created = true;
- else if (errno == EEXIST && merge)
- created = false;
- else
- return -errno;
-
- fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fdt < 0)
- return -errno;
-
- r = 0;
-
- if (created) {
- struct timespec ut[2] = {
- st->st_atim,
- st->st_mtim
- };
-
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
- r = -errno;
-
- if (fchmod(fdt, st->st_mode & 07777) < 0)
- r = -errno;
-
- (void) futimens(fdt, ut);
- (void) copy_xattr(dirfd(d), fdt);
- }
-
- FOREACH_DIRENT(de, d, return -errno) {
- struct stat buf;
- int q;
-
- if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
- r = -errno;
- continue;
- }
-
- if (buf.st_dev != original_device)
- continue;
-
- if (S_ISREG(buf.st_mode))
- q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
- else if (S_ISDIR(buf.st_mode))
- q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
- else if (S_ISLNK(buf.st_mode))
- q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
- else if (S_ISFIFO(buf.st_mode))
- q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
- else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
- q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
- else
- q = -EOPNOTSUPP;
-
- if (q == -EEXIST && merge)
- q = 0;
-
- if (q < 0)
- r = q;
- }
-
- return r;
-}
-
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
- struct stat st;
-
- assert(from);
- assert(to);
-
- if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
- return -errno;
-
- if (S_ISREG(st.st_mode))
- return fd_copy_regular(fdf, from, &st, fdt, to);
- else if (S_ISDIR(st.st_mode))
- return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
- else if (S_ISLNK(st.st_mode))
- return fd_copy_symlink(fdf, from, &st, fdt, to);
- else if (S_ISFIFO(st.st_mode))
- return fd_copy_fifo(fdf, from, &st, fdt, to);
- else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
- return fd_copy_node(fdf, from, &st, fdt, to);
- else
- return -EOPNOTSUPP;
-}
-
-int copy_tree(const char *from, const char *to, bool merge) {
- return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
-}
-
-int copy_directory_fd(int dirfd, const char *to, bool merge) {
-
- struct stat st;
-
- assert(dirfd >= 0);
- assert(to);
-
- if (fstat(dirfd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode))
- return -ENOTDIR;
-
- return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
-}
-
-int copy_file_fd(const char *from, int fdt, bool try_reflink) {
- _cleanup_close_ int fdf = -1;
- int r;
-
- assert(from);
- assert(fdt >= 0);
-
- fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fdf < 0)
- return -errno;
-
- r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
-
- (void) copy_times(fdf, fdt);
- (void) copy_xattr(fdf, fdt);
-
- return r;
-}
-
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
- int fdt = -1, r;
-
- assert(from);
- assert(to);
-
- RUN_WITH_UMASK(0000) {
- fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
- if (fdt < 0)
- return -errno;
- }
-
- if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
-
- r = copy_file_fd(from, fdt, true);
- if (r < 0) {
- close(fdt);
- unlink(to);
- return r;
- }
-
- if (close(fdt) < 0) {
- unlink_noerrno(to);
- return -errno;
- }
-
- return 0;
-}
-
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(from);
- assert(to);
-
- r = tempfn_random(to, &t);
- if (r < 0)
- return r;
-
- r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
- if (r < 0)
- return r;
-
- if (replace) {
- r = renameat(AT_FDCWD, t, AT_FDCWD, to);
- if (r < 0)
- r = -errno;
- } else
- r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
- if (r < 0) {
- (void) unlink_noerrno(t);
- return r;
- }
-
- return 0;
-}
-
-int copy_times(int fdf, int fdt) {
- struct timespec ut[2];
- struct stat st;
- usec_t crtime = 0;
-
- assert(fdf >= 0);
- assert(fdt >= 0);
-
- if (fstat(fdf, &st) < 0)
- return -errno;
-
- ut[0] = st.st_atim;
- ut[1] = st.st_mtim;
-
- if (futimens(fdt, ut) < 0)
- return -errno;
-
- if (fd_getcrtime(fdf, &crtime) >= 0)
- (void) fd_setcrtime(fdt, crtime);
-
- return 0;
-}
-
-int copy_xattr(int fdf, int fdt) {
- _cleanup_free_ char *bufa = NULL, *bufb = NULL;
- size_t sza = 100, szb = 100;
- ssize_t n;
- int ret = 0;
- const char *p;
-
- for (;;) {
- bufa = malloc(sza);
- if (!bufa)
- return -ENOMEM;
-
- n = flistxattr(fdf, bufa, sza);
- if (n == 0)
- return 0;
- if (n > 0)
- break;
- if (errno != ERANGE)
- return -errno;
-
- sza *= 2;
-
- free(bufa);
- bufa = NULL;
- }
-
- p = bufa;
- while (n > 0) {
- size_t l;
-
- l = strlen(p);
- assert(l < (size_t) n);
-
- if (startswith(p, "user.")) {
- ssize_t m;
-
- if (!bufb) {
- bufb = malloc(szb);
- if (!bufb)
- return -ENOMEM;
- }
-
- m = fgetxattr(fdf, p, bufb, szb);
- if (m < 0) {
- if (errno == ERANGE) {
- szb *= 2;
- free(bufb);
- bufb = NULL;
- continue;
- }
-
- return -errno;
- }
-
- if (fsetxattr(fdt, p, bufb, m, 0) < 0)
- ret = -errno;
- }
-
- p += l + 1;
- n -= l + 1;
- }
-
- return ret;
-}
diff --git a/src/shared/copy.h b/src/shared/copy.h
deleted file mode 100644
index 8de0cfb..0000000
--- a/src/shared/copy.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <stdbool.h>
-#include <sys/types.h>
-
-int copy_file_fd(const char *from, int to, bool try_reflink);
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags);
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags);
-int copy_tree(const char *from, const char *to, bool merge);
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
-int copy_directory_fd(int dirfd, const char *to, bool merge);
-int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink);
-int copy_times(int fdf, int fdt);
-int copy_xattr(int fdf, int fdt);
diff --git a/src/shared/def.h b/src/shared/def.h
deleted file mode 100644
index 011c7c6..0000000
--- a/src/shared/def.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "util.h"
-
-#define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC)
-#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
-#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC)
-
-#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC)
-#define DEFAULT_START_LIMIT_BURST 5
-
-/* The default time after which exit-on-idle services exit. This
- * should be kept lower than the watchdog timeout, because otherwise
- * the watchdog pings will keep the loop busy. */
-#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
-
-#define SYSTEMD_CGROUP_CONTROLLER "systemd"
-
-#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
-#define SIGNALS_IGNORE SIGPIPE
-
-#define DIGITS "0123456789"
-#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
-#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
-#define ALPHANUMERICAL LETTERS DIGITS
-
-#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
-
-#ifdef HAVE_SPLIT_USR
-#define KBD_KEYMAP_DIRS \
- "/usr/share/keymaps/\0" \
- "/usr/share/kbd/keymaps/\0" \
- "/usr/lib/kbd/keymaps/\0" \
- "/lib/kbd/keymaps/\0"
-#else
-#define KBD_KEYMAP_DIRS \
- "/usr/share/keymaps/\0" \
- "/usr/share/kbd/keymaps/\0" \
- "/usr/lib/kbd/keymaps/\0"
-#endif
-
-#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
-#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus"
-
-#ifdef ENABLE_KDBUS
-# define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS
-#else
-# define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS
-#endif
-
-#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
-#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus"
-
-#define PLYMOUTH_SOCKET { \
- .un.sun_family = AF_UNIX, \
- .un.sun_path = "\0/org/freedesktop/plymouthd", \
- }
-
-#ifndef TTY_GID
-#define TTY_GID 5
-#endif
-
-#define NOTIFY_FD_MAX 768
-#define NOTIFY_BUFFER_MAX PIPE_BUF
diff --git a/src/shared/device-nodes.c b/src/shared/device-nodes.c
deleted file mode 100644
index 9d5af72..0000000
--- a/src/shared/device-nodes.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2008-2011 Kay Sievers
-
- 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 <stdio.h>
-
-#include "device-nodes.h"
-#include "utf8.h"
-
-int whitelisted_char_for_devnode(char c, const char *white) {
-
- if ((c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- strchr("#+-.:=@_", c) != NULL ||
- (white != NULL && strchr(white, c) != NULL))
- return 1;
-
- return 0;
-}
-
-int encode_devnode_name(const char *str, char *str_enc, size_t len) {
- size_t i, j;
-
- if (str == NULL || str_enc == NULL)
- return -EINVAL;
-
- for (i = 0, j = 0; str[i] != '\0'; i++) {
- int seqlen;
-
- seqlen = utf8_encoded_valid_unichar(&str[i]);
- if (seqlen > 1) {
-
- if (len-j < (size_t)seqlen)
- return -EINVAL;
-
- memcpy(&str_enc[j], &str[i], seqlen);
- j += seqlen;
- i += (seqlen-1);
-
- } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) {
-
- if (len-j < 4)
- return -EINVAL;
-
- sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
- j += 4;
-
- } else {
- if (len-j < 1)
- return -EINVAL;
-
- str_enc[j] = str[i];
- j++;
- }
- }
-
- if (len-j < 1)
- return -EINVAL;
-
- str_enc[j] = '\0';
- return 0;
-}
diff --git a/src/shared/device-nodes.h b/src/shared/device-nodes.h
deleted file mode 100644
index 04ba489..0000000
--- a/src/shared/device-nodes.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- 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/>.
-***/
-
-int encode_devnode_name(const char *str, char *str_enc, size_t len);
-int whitelisted_char_for_devnode(char c, const char *additional);
diff --git a/src/shared/env-util.c b/src/shared/env-util.c
deleted file mode 100644
index ac7bbdc..0000000
--- a/src/shared/env-util.c
+++ /dev/null
@@ -1,594 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 <limits.h>
-#include <unistd.h>
-
-#include "strv.h"
-#include "utf8.h"
-#include "util.h"
-#include "env-util.h"
-#include "def.h"
-
-#define VALID_CHARS_ENV_NAME \
- DIGITS LETTERS \
- "_"
-
-#ifndef ARG_MAX
-#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
-#endif
-
-static bool env_name_is_valid_n(const char *e, size_t n) {
- const char *p;
-
- if (!e)
- return false;
-
- if (n <= 0)
- return false;
-
- if (e[0] >= '0' && e[0] <= '9')
- return false;
-
- /* POSIX says the overall size of the environment block cannot
- * be > ARG_MAX, an individual assignment hence cannot be
- * either. Discounting the equal sign and trailing NUL this
- * hence leaves ARG_MAX-2 as longest possible variable
- * name. */
- if (n > ARG_MAX - 2)
- return false;
-
- for (p = e; p < e + n; p++)
- if (!strchr(VALID_CHARS_ENV_NAME, *p))
- return false;
-
- return true;
-}
-
-bool env_name_is_valid(const char *e) {
- if (!e)
- return false;
-
- return env_name_is_valid_n(e, strlen(e));
-}
-
-bool env_value_is_valid(const char *e) {
- if (!e)
- return false;
-
- if (!utf8_is_valid(e))
- return false;
-
- /* bash allows tabs in environment variables, and so should
- * we */
- if (string_has_cc(e, "\t"))
- return false;
-
- /* POSIX says the overall size of the environment block cannot
- * be > ARG_MAX, an individual assignment hence cannot be
- * either. Discounting the shortest possible variable name of
- * length 1, the equal sign and trailing NUL this hence leaves
- * ARG_MAX-3 as longest possible variable value. */
- if (strlen(e) > ARG_MAX - 3)
- return false;
-
- return true;
-}
-
-bool env_assignment_is_valid(const char *e) {
- const char *eq;
-
- eq = strchr(e, '=');
- if (!eq)
- return false;
-
- if (!env_name_is_valid_n(e, eq - e))
- return false;
-
- if (!env_value_is_valid(eq + 1))
- return false;
-
- /* POSIX says the overall size of the environment block cannot
- * be > ARG_MAX, hence the individual variable assignments
- * cannot be either, but let's leave room for one trailing NUL
- * byte. */
- if (strlen(e) > ARG_MAX - 1)
- return false;
-
- return true;
-}
-
-bool strv_env_is_valid(char **e) {
- char **p, **q;
-
- STRV_FOREACH(p, e) {
- size_t k;
-
- if (!env_assignment_is_valid(*p))
- return false;
-
- /* Check if there are duplicate assginments */
- k = strcspn(*p, "=");
- STRV_FOREACH(q, p + 1)
- if (strneq(*p, *q, k) && (*q)[k] == '=')
- return false;
- }
-
- return true;
-}
-
-bool strv_env_name_or_assignment_is_valid(char **l) {
- char **p, **q;
-
- STRV_FOREACH(p, l) {
- if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
- return false;
-
- STRV_FOREACH(q, p + 1)
- if (streq(*p, *q))
- return false;
- }
-
- return true;
-}
-
-static int env_append(char **r, char ***k, char **a) {
- assert(r);
- assert(k);
-
- if (!a)
- return 0;
-
- /* Add the entries of a to *k unless they already exist in *r
- * in which case they are overridden instead. This assumes
- * there is enough space in the r array. */
-
- for (; *a; a++) {
- char **j;
- size_t n;
-
- n = strcspn(*a, "=");
-
- if ((*a)[n] == '=')
- n++;
-
- for (j = r; j < *k; j++)
- if (strneq(*j, *a, n))
- break;
-
- if (j >= *k)
- (*k)++;
- else
- free(*j);
-
- *j = strdup(*a);
- if (!*j)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-char **strv_env_merge(unsigned n_lists, ...) {
- size_t n = 0;
- char **l, **k, **r;
- va_list ap;
- unsigned i;
-
- /* Merges an arbitrary number of environment sets */
-
- va_start(ap, n_lists);
- for (i = 0; i < n_lists; i++) {
- l = va_arg(ap, char**);
- n += strv_length(l);
- }
- va_end(ap);
-
- r = new(char*, n+1);
- if (!r)
- return NULL;
-
- k = r;
-
- va_start(ap, n_lists);
- for (i = 0; i < n_lists; i++) {
- l = va_arg(ap, char**);
- if (env_append(r, &k, l) < 0)
- goto fail;
- }
- va_end(ap);
-
- *k = NULL;
-
- return r;
-
-fail:
- va_end(ap);
- strv_free(r);
-
- return NULL;
-}
-
-_pure_ static bool env_match(const char *t, const char *pattern) {
- assert(t);
- assert(pattern);
-
- /* pattern a matches string a
- * a matches a=
- * a matches a=b
- * a= matches a=
- * a=b matches a=b
- * a= does not match a
- * a=b does not match a=
- * a=b does not match a
- * a=b does not match a=c */
-
- if (streq(t, pattern))
- return true;
-
- if (!strchr(pattern, '=')) {
- size_t l = strlen(pattern);
-
- return strneq(t, pattern, l) && t[l] == '=';
- }
-
- return false;
-}
-
-char **strv_env_delete(char **x, unsigned n_lists, ...) {
- size_t n, i = 0;
- char **k, **r;
- va_list ap;
-
- /* Deletes every entry from x that is mentioned in the other
- * string lists */
-
- n = strv_length(x);
-
- r = new(char*, n+1);
- if (!r)
- return NULL;
-
- STRV_FOREACH(k, x) {
- unsigned v;
-
- va_start(ap, n_lists);
- for (v = 0; v < n_lists; v++) {
- char **l, **j;
-
- l = va_arg(ap, char**);
- STRV_FOREACH(j, l)
- if (env_match(*k, *j))
- goto skip;
- }
- va_end(ap);
-
- r[i] = strdup(*k);
- if (!r[i]) {
- strv_free(r);
- return NULL;
- }
-
- i++;
- continue;
-
- skip:
- va_end(ap);
- }
-
- r[i] = NULL;
-
- assert(i <= n);
-
- return r;
-}
-
-char **strv_env_unset(char **l, const char *p) {
-
- char **f, **t;
-
- if (!l)
- return NULL;
-
- assert(p);
-
- /* Drops every occurrence of the env var setting p in the
- * string list. Edits in-place. */
-
- for (f = t = l; *f; f++) {
-
- if (env_match(*f, p)) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
-}
-
-char **strv_env_unset_many(char **l, ...) {
-
- char **f, **t;
-
- if (!l)
- return NULL;
-
- /* Like strv_env_unset() but applies many at once. Edits in-place. */
-
- for (f = t = l; *f; f++) {
- bool found = false;
- const char *p;
- va_list ap;
-
- va_start(ap, l);
-
- while ((p = va_arg(ap, const char*))) {
- if (env_match(*f, p)) {
- found = true;
- break;
- }
- }
-
- va_end(ap);
-
- if (found) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
-}
-
-char **strv_env_set(char **x, const char *p) {
-
- char **k, **r;
- char* m[2] = { (char*) p, NULL };
-
- /* Overrides the env var setting of p, returns a new copy */
-
- r = new(char*, strv_length(x)+2);
- if (!r)
- return NULL;
-
- k = r;
- if (env_append(r, &k, x) < 0)
- goto fail;
-
- if (env_append(r, &k, m) < 0)
- goto fail;
-
- *k = NULL;
-
- return r;
-
-fail:
- strv_free(r);
- return NULL;
-}
-
-char *strv_env_get_n(char **l, const char *name, size_t k) {
- char **i;
-
- assert(name);
-
- if (k <= 0)
- return NULL;
-
- STRV_FOREACH(i, l)
- if (strneq(*i, name, k) &&
- (*i)[k] == '=')
- return *i + k + 1;
-
- return NULL;
-}
-
-char *strv_env_get(char **l, const char *name) {
- assert(name);
-
- return strv_env_get_n(l, name, strlen(name));
-}
-
-char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
- char **p, **q;
- int k = 0;
-
- STRV_FOREACH(p, e) {
- size_t n;
- bool duplicate = false;
-
- if (!env_assignment_is_valid(*p)) {
- if (invalid_callback)
- invalid_callback(*p, userdata);
- free(*p);
- continue;
- }
-
- n = strcspn(*p, "=");
- STRV_FOREACH(q, p + 1)
- if (strneq(*p, *q, n) && (*q)[n] == '=') {
- duplicate = true;
- break;
- }
-
- if (duplicate) {
- free(*p);
- continue;
- }
-
- e[k++] = *p;
- }
-
- if (e)
- e[k] = NULL;
-
- return e;
-}
-
-char *replace_env(const char *format, char **env) {
- enum {
- WORD,
- CURLY,
- VARIABLE
- } state = WORD;
-
- const char *e, *word = format;
- char *r = NULL, *k;
-
- assert(format);
-
- for (e = format; *e; e ++) {
-
- switch (state) {
-
- case WORD:
- if (*e == '$')
- state = CURLY;
- break;
-
- case CURLY:
- if (*e == '{') {
- k = strnappend(r, word, e-word-1);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e-1;
- state = VARIABLE;
-
- } else if (*e == '$') {
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- } else
- state = WORD;
- break;
-
- case VARIABLE:
- if (*e == '}') {
- const char *t;
-
- t = strempty(strv_env_get_n(env, word+2, e-word-2));
-
- k = strappend(r, t);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- }
- break;
- }
- }
-
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
-
- free(r);
- return k;
-
-fail:
- free(r);
- return NULL;
-}
-
-char **replace_env_argv(char **argv, char **env) {
- char **ret, **i;
- unsigned k = 0, l = 0;
-
- l = strv_length(argv);
-
- ret = new(char*, l+1);
- if (!ret)
- return NULL;
-
- STRV_FOREACH(i, argv) {
-
- /* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && (*i)[1] != '{') {
- char *e;
- char **w, **m = NULL;
- unsigned q;
-
- e = strv_env_get(env, *i+1);
- if (e) {
- int r;
-
- r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
- if (r < 0) {
- ret[k] = NULL;
- strv_free(ret);
- return NULL;
- }
- } else
- m = NULL;
-
- q = strv_length(m);
- l = l + q - 1;
-
- w = realloc(ret, sizeof(char*) * (l+1));
- if (!w) {
- ret[k] = NULL;
- strv_free(ret);
- strv_free(m);
- return NULL;
- }
-
- ret = w;
- if (m) {
- memcpy(ret + k, m, q * sizeof(char*));
- free(m);
- }
-
- k += q;
- continue;
- }
-
- /* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env);
- if (!ret[k]) {
- strv_free(ret);
- return NULL;
- }
- k++;
- }
-
- ret[k] = NULL;
- return ret;
-}
diff --git a/src/shared/env-util.h b/src/shared/env-util.h
deleted file mode 100644
index 803aa61..0000000
--- a/src/shared/env-util.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 <stdbool.h>
-
-#include "macro.h"
-
-bool env_name_is_valid(const char *e);
-bool env_value_is_valid(const char *e);
-bool env_assignment_is_valid(const char *e);
-
-char *replace_env(const char *format, char **env);
-char **replace_env_argv(char **argv, char **env);
-
-bool strv_env_is_valid(char **e);
-#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
-char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
-
-bool strv_env_name_or_assignment_is_valid(char **l);
-
-char **strv_env_merge(unsigned n_lists, ...);
-char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */
-
-char **strv_env_set(char **x, const char *p); /* New copy ... */
-char **strv_env_unset(char **l, const char *p); /* In place ... */
-char **strv_env_unset_many(char **l, ...) _sentinel_;
-
-char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
-char *strv_env_get(char **x, const char *n) _pure_;
diff --git a/src/shared/errno-list.c b/src/shared/errno-list.c
deleted file mode 100644
index 34d1331..0000000
--- a/src/shared/errno-list.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 <string.h>
-
-#include "util.h"
-#include "errno-list.h"
-
-static const struct errno_name* lookup_errno(register const char *str,
- register unsigned int len);
-
-#include "errno-to-name.h"
-#include "errno-from-name.h"
-
-const char *errno_to_name(int id) {
-
- if (id < 0)
- id = -id;
-
- if (id >= (int) ELEMENTSOF(errno_names))
- return NULL;
-
- return errno_names[id];
-}
-
-int errno_from_name(const char *name) {
- const struct errno_name *sc;
-
- assert(name);
-
- sc = lookup_errno(name, strlen(name));
- if (!sc)
- return 0;
-
- return sc->id;
-}
-
-int errno_max(void) {
- return ELEMENTSOF(errno_names);
-}
diff --git a/src/shared/errno-list.h b/src/shared/errno-list.h
deleted file mode 100644
index ba53329..0000000
--- a/src/shared/errno-list.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/>.
-***/
-
-const char *errno_to_name(int id);
-int errno_from_name(const char *name);
-
-int errno_max(void);
diff --git a/src/shared/ether-addr-util.h b/src/shared/ether-addr-util.h
deleted file mode 100644
index 7033138..0000000
--- a/src/shared/ether-addr-util.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen
-
- 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 <net/ethernet.h>
-
-#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
-#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
deleted file mode 100644
index c09efdd..0000000
--- a/src/shared/exit-status.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "exit-status.h"
-#include "set.h"
-#include "macro.h"
-
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
-
- /* We cast to int here, so that -Wenum doesn't complain that
- * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
-
- switch ((int) status) {
-
- case EXIT_SUCCESS:
- return "SUCCESS";
-
- case EXIT_FAILURE:
- return "FAILURE";
- }
-
-
- if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
- switch ((int) status) {
-
- case EXIT_CHDIR:
- return "CHDIR";
-
- case EXIT_NICE:
- return "NICE";
-
- case EXIT_FDS:
- return "FDS";
-
- case EXIT_EXEC:
- return "EXEC";
-
- case EXIT_MEMORY:
- return "MEMORY";
-
- case EXIT_LIMITS:
- return "LIMITS";
-
- case EXIT_OOM_ADJUST:
- return "OOM_ADJUST";
-
- case EXIT_SIGNAL_MASK:
- return "SIGNAL_MASK";
-
- case EXIT_STDIN:
- return "STDIN";
-
- case EXIT_STDOUT:
- return "STDOUT";
-
- case EXIT_CHROOT:
- return "CHROOT";
-
- case EXIT_IOPRIO:
- return "IOPRIO";
-
- case EXIT_TIMERSLACK:
- return "TIMERSLACK";
-
- case EXIT_SECUREBITS:
- return "SECUREBITS";
-
- case EXIT_SETSCHEDULER:
- return "SETSCHEDULER";
-
- case EXIT_CPUAFFINITY:
- return "CPUAFFINITY";
-
- case EXIT_GROUP:
- return "GROUP";
-
- case EXIT_USER:
- return "USER";
-
- case EXIT_CAPABILITIES:
- return "CAPABILITIES";
-
- case EXIT_CGROUP:
- return "CGROUP";
-
- case EXIT_SETSID:
- return "SETSID";
-
- case EXIT_CONFIRM:
- return "CONFIRM";
-
- case EXIT_STDERR:
- return "STDERR";
-
- case EXIT_PAM:
- return "PAM";
-
- case EXIT_NETWORK:
- return "NETWORK";
-
- case EXIT_NAMESPACE:
- return "NAMESPACE";
-
- case EXIT_NO_NEW_PRIVILEGES:
- return "NO_NEW_PRIVILEGES";
-
- case EXIT_SECCOMP:
- return "SECCOMP";
-
- case EXIT_SELINUX_CONTEXT:
- return "SELINUX_CONTEXT";
-
- case EXIT_PERSONALITY:
- return "PERSONALITY";
-
- case EXIT_APPARMOR_PROFILE:
- return "APPARMOR";
-
- case EXIT_ADDRESS_FAMILIES:
- return "ADDRESS_FAMILIES";
-
- case EXIT_RUNTIME_DIRECTORY:
- return "RUNTIME_DIRECTORY";
-
- case EXIT_CHOWN:
- return "CHOWN";
-
- case EXIT_MAKE_STARTER:
- return "MAKE_STARTER";
-
- case EXIT_BUS_ENDPOINT:
- return "BUS_ENDPOINT";
- }
- }
-
- if (level == EXIT_STATUS_LSB) {
- switch ((int) status) {
-
- case EXIT_INVALIDARGUMENT:
- return "INVALIDARGUMENT";
-
- case EXIT_NOTIMPLEMENTED:
- return "NOTIMPLEMENTED";
-
- case EXIT_NOPERMISSION:
- return "NOPERMISSION";
-
- case EXIT_NOTINSTALLED:
- return "NOTINSTALLED";
-
- case EXIT_NOTCONFIGURED:
- return "NOTCONFIGURED";
-
- case EXIT_NOTRUNNING:
- return "NOTRUNNING";
- }
- }
-
- return NULL;
-}
-
-
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
-
- if (code == CLD_EXITED)
- return status == 0 ||
- (success_status &&
- set_contains(success_status->status, INT_TO_PTR(status)));
-
- /* If a daemon does not implement handlers for some of the
- * signals that's not considered an unclean shutdown */
- if (code == CLD_KILLED)
- return
- status == SIGHUP ||
- status == SIGINT ||
- status == SIGTERM ||
- status == SIGPIPE ||
- (success_status &&
- set_contains(success_status->signal, INT_TO_PTR(status)));
-
- return false;
-}
-
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
-
- if (is_clean_exit(code, status, success_status))
- return true;
-
- return
- code == CLD_EXITED &&
- (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
-}
-
-void exit_status_set_free(ExitStatusSet *x) {
- assert(x);
-
- set_free(x->status);
- set_free(x->signal);
- x->status = x->signal = NULL;
-}
-
-bool exit_status_set_is_empty(ExitStatusSet *x) {
- if (!x)
- return true;
-
- return set_isempty(x->status) && set_isempty(x->signal);
-}
-
-bool exit_status_set_test(ExitStatusSet *x, int code, int status) {
-
- if (exit_status_set_is_empty(x))
- return false;
-
- if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
- return true;
-
- if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status)))
- return true;
-
- return false;
-}
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
deleted file mode 100644
index 7259cd1..0000000
--- a/src/shared/exit-status.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-
-#include "set.h"
-
-typedef enum ExitStatus {
- /* EXIT_SUCCESS defined by libc */
- /* EXIT_FAILURE defined by libc */
- EXIT_INVALIDARGUMENT = 2,
- EXIT_NOTIMPLEMENTED = 3,
- EXIT_NOPERMISSION = 4,
- EXIT_NOTINSTALLED = 5,
- EXIT_NOTCONFIGURED = 6,
- EXIT_NOTRUNNING = 7,
-
- /* The LSB suggests that error codes >= 200 are "reserved". We
- * use them here under the assumption that they hence are
- * unused by init scripts.
- *
- * http://refspecs.linuxfoundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
-
- EXIT_CHDIR = 200,
- EXIT_NICE,
- EXIT_FDS,
- EXIT_EXEC,
- EXIT_MEMORY,
- EXIT_LIMITS,
- EXIT_OOM_ADJUST,
- EXIT_SIGNAL_MASK,
- EXIT_STDIN,
- EXIT_STDOUT,
- EXIT_CHROOT, /* 210 */
- EXIT_IOPRIO,
- EXIT_TIMERSLACK,
- EXIT_SECUREBITS,
- EXIT_SETSCHEDULER,
- EXIT_CPUAFFINITY,
- EXIT_GROUP,
- EXIT_USER,
- EXIT_CAPABILITIES,
- EXIT_CGROUP,
- EXIT_SETSID, /* 220 */
- EXIT_CONFIRM,
- EXIT_STDERR,
- _EXIT_RESERVED, /* used to be tcpwrap, don't reuse! */
- EXIT_PAM,
- EXIT_NETWORK,
- EXIT_NAMESPACE,
- EXIT_NO_NEW_PRIVILEGES,
- EXIT_SECCOMP,
- EXIT_SELINUX_CONTEXT,
- EXIT_PERSONALITY, /* 230 */
- EXIT_APPARMOR_PROFILE,
- EXIT_ADDRESS_FAMILIES,
- EXIT_RUNTIME_DIRECTORY,
- EXIT_MAKE_STARTER,
- EXIT_CHOWN,
- EXIT_BUS_ENDPOINT,
- EXIT_SMACK_PROCESS_LABEL,
-} ExitStatus;
-
-typedef enum ExitStatusLevel {
- EXIT_STATUS_MINIMAL,
- EXIT_STATUS_SYSTEMD,
- EXIT_STATUS_LSB,
- EXIT_STATUS_FULL = EXIT_STATUS_LSB
-} ExitStatusLevel;
-
-typedef struct ExitStatusSet {
- Set *status;
- Set *signal;
-} ExitStatusSet;
-
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
-
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
-
-void exit_status_set_free(ExitStatusSet *x);
-bool exit_status_set_is_empty(ExitStatusSet *x);
-bool exit_status_set_test(ExitStatusSet *x, int code, int status);
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
deleted file mode 100644
index 6101b62..0000000
--- a/src/shared/fdset.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-
-#include "set.h"
-#include "util.h"
-#include "macro.h"
-#include "fdset.h"
-#include "sd-daemon.h"
-
-#define MAKE_SET(s) ((Set*) s)
-#define MAKE_FDSET(s) ((FDSet*) s)
-
-/* Make sure we can distinguish fd 0 and NULL */
-#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
-#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
-
-FDSet *fdset_new(void) {
- return MAKE_FDSET(set_new(NULL));
-}
-
-int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds) {
- unsigned i;
- FDSet *s;
- int r;
-
- assert(ret);
-
- s = fdset_new();
- if (!s)
- return -ENOMEM;
-
- for (i = 0; i < n_fds; i++) {
-
- r = fdset_put(s, fds[i]);
- if (r < 0) {
- set_free(MAKE_SET(s));
- return r;
- }
- }
-
- *ret = s;
- return 0;
-}
-
-FDSet* fdset_free(FDSet *s) {
- void *p;
-
- while ((p = set_steal_first(MAKE_SET(s)))) {
- /* Valgrind's fd might have ended up in this set here,
- * due to fdset_new_fill(). We'll ignore all failures
- * here, so that the EBADFD that valgrind will return
- * us on close() doesn't influence us */
-
- /* When reloading duplicates of the private bus
- * connection fds and suchlike are closed here, which
- * has no effect at all, since they are only
- * duplicates. So don't be surprised about these log
- * messages. */
-
- log_debug("Closing left-over fd %i", PTR_TO_FD(p));
- close_nointr(PTR_TO_FD(p));
- }
-
- set_free(MAKE_SET(s));
- return NULL;
-}
-
-int fdset_put(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return set_put(MAKE_SET(s), FD_TO_PTR(fd));
-}
-
-int fdset_consume(FDSet *s, int fd) {
- int r;
-
- assert(s);
- assert(fd >= 0);
-
- r = fdset_put(s, fd);
- if (r <= 0)
- safe_close(fd);
-
- return r;
-}
-
-int fdset_put_dup(FDSet *s, int fd) {
- int copy, r;
-
- assert(s);
- assert(fd >= 0);
-
- copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (copy < 0)
- return -errno;
-
- r = fdset_put(s, copy);
- if (r < 0) {
- safe_close(copy);
- return r;
- }
-
- return copy;
-}
-
-bool fdset_contains(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
-}
-
-int fdset_remove(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
-}
-
-int fdset_new_fill(FDSet **_s) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int r = 0;
- FDSet *s;
-
- assert(_s);
-
- /* Creates an fdset and fills in all currently open file
- * descriptors. */
-
- d = opendir("/proc/self/fd");
- if (!d)
- return -errno;
-
- s = fdset_new();
- if (!s) {
- r = -ENOMEM;
- goto finish;
- }
-
- while ((de = readdir(d))) {
- int fd = -1;
-
- if (hidden_file(de->d_name))
- continue;
-
- r = safe_atoi(de->d_name, &fd);
- if (r < 0)
- goto finish;
-
- if (fd < 3)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- r = fdset_put(s, fd);
- if (r < 0)
- goto finish;
- }
-
- r = 0;
- *_s = s;
- s = NULL;
-
-finish:
- /* We won't close the fds here! */
- if (s)
- set_free(MAKE_SET(s));
-
- return r;
-}
-
-int fdset_cloexec(FDSet *fds, bool b) {
- Iterator i;
- void *p;
- int r;
-
- assert(fds);
-
- SET_FOREACH(p, MAKE_SET(fds), i)
- if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
- return r;
-
- return 0;
-}
-
-int fdset_new_listen_fds(FDSet **_s, bool unset) {
- int n, fd, r;
- FDSet *s;
-
- assert(_s);
-
- /* Creates an fdset and fills in all passed file descriptors */
-
- s = fdset_new();
- if (!s) {
- r = -ENOMEM;
- goto fail;
- }
-
- n = sd_listen_fds(unset);
- for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
- r = fdset_put(s, fd);
- if (r < 0)
- goto fail;
- }
-
- *_s = s;
- return 0;
-
-
-fail:
- if (s)
- set_free(MAKE_SET(s));
-
- return r;
-}
-
-int fdset_close_others(FDSet *fds) {
- void *e;
- Iterator i;
- int *a;
- unsigned j, m;
-
- j = 0, m = fdset_size(fds);
- a = alloca(sizeof(int) * m);
- SET_FOREACH(e, MAKE_SET(fds), i)
- a[j++] = PTR_TO_FD(e);
-
- assert(j == m);
-
- return close_all_fds(a, j);
-}
-
-unsigned fdset_size(FDSet *fds) {
- return set_size(MAKE_SET(fds));
-}
-
-bool fdset_isempty(FDSet *fds) {
- return set_isempty(MAKE_SET(fds));
-}
-
-int fdset_iterate(FDSet *s, Iterator *i) {
- void *p;
-
- p = set_iterate(MAKE_SET(s), i);
- if (!p)
- return -ENOENT;
-
- return PTR_TO_FD(p);
-}
-
-int fdset_steal_first(FDSet *fds) {
- void *p;
-
- p = set_steal_first(MAKE_SET(fds));
- if (!p)
- return -ENOENT;
-
- return PTR_TO_FD(p);
-}
diff --git a/src/shared/fdset.h b/src/shared/fdset.h
deleted file mode 100644
index 340438d..0000000
--- a/src/shared/fdset.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "set.h"
-
-typedef struct FDSet FDSet;
-
-FDSet* fdset_new(void);
-FDSet* fdset_free(FDSet *s);
-
-int fdset_put(FDSet *s, int fd);
-int fdset_put_dup(FDSet *s, int fd);
-int fdset_consume(FDSet *s, int fd);
-
-bool fdset_contains(FDSet *s, int fd);
-int fdset_remove(FDSet *s, int fd);
-
-int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds);
-int fdset_new_fill(FDSet **ret);
-int fdset_new_listen_fds(FDSet **ret, bool unset);
-
-int fdset_cloexec(FDSet *fds, bool b);
-
-int fdset_close_others(FDSet *fds);
-
-unsigned fdset_size(FDSet *fds);
-bool fdset_isempty(FDSet *fds);
-
-int fdset_iterate(FDSet *s, Iterator *i);
-
-int fdset_steal_first(FDSet *fds);
-
-#define FDSET_FOREACH(fd, fds, i) \
- for ((i) = ITERATOR_FIRST, (fd) = fdset_iterate((fds), &(i)); (fd) >= 0; (fd) = fdset_iterate((fds), &(i)))
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free);
-#define _cleanup_fdset_free_ _cleanup_(fdset_freep)
diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c
deleted file mode 100644
index bec988c..0000000
--- a/src/shared/fileio-label.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2010 Harald Hoyer
-
- 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 "util.h"
-#include "selinux-util.h"
-#include "fileio-label.h"
-
-int write_string_file_atomic_label(const char *fn, const char *line) {
- int r;
-
- r = mac_selinux_create_file_prepare(fn, S_IFREG);
- if (r < 0)
- return r;
-
- r = write_string_file_atomic(fn, line);
-
- mac_selinux_create_file_clear();
-
- return r;
-}
-
-int write_env_file_label(const char *fname, char **l) {
- int r;
-
- r = mac_selinux_create_file_prepare(fname, S_IFREG);
- if (r < 0)
- return r;
-
- r = write_env_file(fname, l);
-
- mac_selinux_create_file_clear();
-
- return r;
-}
-
-int fopen_temporary_label(const char *target,
- const char *path, FILE **f, char **temp_path) {
- int r;
-
- r = mac_selinux_create_file_prepare(target, S_IFREG);
- if (r < 0)
- return r;
-
- r = fopen_temporary(path, f, temp_path);
-
- mac_selinux_create_file_clear();
-
- return r;
-}
diff --git a/src/shared/fileio-label.h b/src/shared/fileio-label.h
deleted file mode 100644
index 25fa351..0000000
--- a/src/shared/fileio-label.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2010 Harald Hoyer
-
- 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 <stdio.h>
-#include "fileio.h"
-
-int write_string_file_atomic_label(const char *fn, const char *line);
-int write_env_file_label(const char *fname, char **l);
-int fopen_temporary_label(const char *target,
- const char *path, FILE **f, char **temp_path);
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
deleted file mode 100644
index ff6b1a7..0000000
--- a/src/shared/fileio.c
+++ /dev/null
@@ -1,817 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <unistd.h>
-
-#include "util.h"
-#include "strv.h"
-#include "utf8.h"
-#include "ctype.h"
-#include "fileio.h"
-
-int write_string_stream(FILE *f, const char *line) {
- assert(f);
- assert(line);
-
- errno = 0;
-
- fputs(line, f);
- if (!endswith(line, "\n"))
- fputc('\n', f);
-
- fflush(f);
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- return 0;
-}
-
-int write_string_file(const char *fn, const char *line) {
- _cleanup_fclose_ FILE *f = NULL;
-
- assert(fn);
- assert(line);
-
- f = fopen(fn, "we");
- if (!f)
- return -errno;
-
- return write_string_stream(f, line);
-}
-
-int write_string_file_no_create(const char *fn, const char *line) {
- _cleanup_fclose_ FILE *f = NULL;
- int fd;
-
- assert(fn);
- assert(line);
-
- /* We manually build our own version of fopen(..., "we") that
- * works without O_CREAT */
- fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return -errno;
-
- f = fdopen(fd, "we");
- if (!f) {
- safe_close(fd);
- return -errno;
- }
-
- return write_string_stream(f, line);
-}
-
-int write_string_file_atomic(const char *fn, const char *line) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *p = NULL;
- int r;
-
- assert(fn);
- assert(line);
-
- r = fopen_temporary(fn, &f, &p);
- if (r < 0)
- return r;
-
- fchmod_umask(fileno(f), 0644);
-
- r = write_string_stream(f, line);
- if (r >= 0) {
- if (rename(p, fn) < 0)
- r = -errno;
- }
-
- if (r < 0)
- unlink(p);
-
- return r;
-}
-
-int read_one_line_file(const char *fn, char **line) {
- _cleanup_fclose_ FILE *f = NULL;
- char t[LINE_MAX], *c;
-
- assert(fn);
- assert(line);
-
- f = fopen(fn, "re");
- if (!f)
- return -errno;
-
- if (!fgets(t, sizeof(t), f)) {
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- t[0] = 0;
- }
-
- c = strdup(t);
- if (!c)
- return -ENOMEM;
- truncate_nl(c);
-
- *line = c;
- return 0;
-}
-
-int read_full_stream(FILE *f, char **contents, size_t *size) {
- size_t n, l;
- _cleanup_free_ char *buf = NULL;
- struct stat st;
-
- assert(f);
- assert(contents);
-
- if (fstat(fileno(f), &st) < 0)
- return -errno;
-
- n = LINE_MAX;
-
- if (S_ISREG(st.st_mode)) {
-
- /* Safety check */
- if (st.st_size > 4*1024*1024)
- return -E2BIG;
-
- /* Start with the right file size, but be prepared for
- * files from /proc which generally report a file size
- * of 0 */
- if (st.st_size > 0)
- n = st.st_size;
- }
-
- l = 0;
- for (;;) {
- char *t;
- size_t k;
-
- t = realloc(buf, n+1);
- if (!t)
- return -ENOMEM;
-
- buf = t;
- k = fread(buf + l, 1, n - l, f);
-
- if (k <= 0) {
- if (ferror(f))
- return -errno;
-
- break;
- }
-
- l += k;
- n *= 2;
-
- /* Safety check */
- if (n > 4*1024*1024)
- return -E2BIG;
- }
-
- buf[l] = 0;
- *contents = buf;
- buf = NULL; /* do not free */
-
- if (size)
- *size = l;
-
- return 0;
-}
-
-int read_full_file(const char *fn, char **contents, size_t *size) {
- _cleanup_fclose_ FILE *f = NULL;
-
- assert(fn);
- assert(contents);
-
- f = fopen(fn, "re");
- if (!f)
- return -errno;
-
- return read_full_stream(f, contents, size);
-}
-
-static int parse_env_file_internal(
- FILE *f,
- const char *fname,
- const char *newline,
- int (*push) (const char *filename, unsigned line,
- const char *key, char *value, void *userdata, int *n_pushed),
- void *userdata,
- int *n_pushed) {
-
- _cleanup_free_ char *contents = NULL, *key = NULL;
- size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
- char *p, *value = NULL;
- int r;
- unsigned line = 1;
-
- enum {
- PRE_KEY,
- KEY,
- PRE_VALUE,
- VALUE,
- VALUE_ESCAPE,
- SINGLE_QUOTE_VALUE,
- SINGLE_QUOTE_VALUE_ESCAPE,
- DOUBLE_QUOTE_VALUE,
- DOUBLE_QUOTE_VALUE_ESCAPE,
- COMMENT,
- COMMENT_ESCAPE
- } state = PRE_KEY;
-
- assert(newline);
-
- if (f)
- r = read_full_stream(f, &contents, NULL);
- else
- r = read_full_file(fname, &contents, NULL);
- if (r < 0)
- return r;
-
- for (p = contents; *p; p++) {
- char c = *p;
-
- switch (state) {
-
- case PRE_KEY:
- if (strchr(COMMENTS, c))
- state = COMMENT;
- else if (!strchr(WHITESPACE, c)) {
- state = KEY;
- last_key_whitespace = (size_t) -1;
-
- if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- key[n_key++] = c;
- }
- break;
-
- case KEY:
- if (strchr(newline, c)) {
- state = PRE_KEY;
- line ++;
- n_key = 0;
- } else if (c == '=') {
- state = PRE_VALUE;
- last_value_whitespace = (size_t) -1;
- } else {
- if (!strchr(WHITESPACE, c))
- last_key_whitespace = (size_t) -1;
- else if (last_key_whitespace == (size_t) -1)
- last_key_whitespace = n_key;
-
- if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- key[n_key++] = c;
- }
-
- break;
-
- case PRE_VALUE:
- if (strchr(newline, c)) {
- state = PRE_KEY;
- line ++;
- key[n_key] = 0;
-
- if (value)
- value[n_value] = 0;
-
- /* strip trailing whitespace from key */
- if (last_key_whitespace != (size_t) -1)
- key[last_key_whitespace] = 0;
-
- r = push(fname, line, key, value, userdata, n_pushed);
- if (r < 0)
- goto fail;
-
- n_key = 0;
- value = NULL;
- value_alloc = n_value = 0;
-
- } else if (c == '\'')
- state = SINGLE_QUOTE_VALUE;
- else if (c == '\"')
- state = DOUBLE_QUOTE_VALUE;
- else if (c == '\\')
- state = VALUE_ESCAPE;
- else if (!strchr(WHITESPACE, c)) {
- state = VALUE;
-
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
-
- break;
-
- case VALUE:
- if (strchr(newline, c)) {
- state = PRE_KEY;
- line ++;
-
- key[n_key] = 0;
-
- if (value)
- value[n_value] = 0;
-
- /* Chomp off trailing whitespace from value */
- if (last_value_whitespace != (size_t) -1)
- value[last_value_whitespace] = 0;
-
- /* strip trailing whitespace from key */
- if (last_key_whitespace != (size_t) -1)
- key[last_key_whitespace] = 0;
-
- r = push(fname, line, key, value, userdata, n_pushed);
- if (r < 0)
- goto fail;
-
- n_key = 0;
- value = NULL;
- value_alloc = n_value = 0;
-
- } else if (c == '\\') {
- state = VALUE_ESCAPE;
- last_value_whitespace = (size_t) -1;
- } else {
- if (!strchr(WHITESPACE, c))
- last_value_whitespace = (size_t) -1;
- else if (last_value_whitespace == (size_t) -1)
- last_value_whitespace = n_value;
-
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
-
- break;
-
- case VALUE_ESCAPE:
- state = VALUE;
-
- if (!strchr(newline, c)) {
- /* Escaped newlines we eat up entirely */
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
- break;
-
- case SINGLE_QUOTE_VALUE:
- if (c == '\'')
- state = PRE_VALUE;
- else if (c == '\\')
- state = SINGLE_QUOTE_VALUE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
-
- break;
-
- case SINGLE_QUOTE_VALUE_ESCAPE:
- state = SINGLE_QUOTE_VALUE;
-
- if (!strchr(newline, c)) {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
- break;
-
- case DOUBLE_QUOTE_VALUE:
- if (c == '\"')
- state = PRE_VALUE;
- else if (c == '\\')
- state = DOUBLE_QUOTE_VALUE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
-
- break;
-
- case DOUBLE_QUOTE_VALUE_ESCAPE:
- state = DOUBLE_QUOTE_VALUE;
-
- if (!strchr(newline, c)) {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
- r = -ENOMEM;
- goto fail;
- }
-
- value[n_value++] = c;
- }
- break;
-
- case COMMENT:
- if (c == '\\')
- state = COMMENT_ESCAPE;
- else if (strchr(newline, c)) {
- state = PRE_KEY;
- line ++;
- }
- break;
-
- case COMMENT_ESCAPE:
- state = COMMENT;
- break;
- }
- }
-
- if (state == PRE_VALUE ||
- state == VALUE ||
- state == VALUE_ESCAPE ||
- state == SINGLE_QUOTE_VALUE ||
- state == SINGLE_QUOTE_VALUE_ESCAPE ||
- state == DOUBLE_QUOTE_VALUE ||
- state == DOUBLE_QUOTE_VALUE_ESCAPE) {
-
- key[n_key] = 0;
-
- if (value)
- value[n_value] = 0;
-
- if (state == VALUE)
- if (last_value_whitespace != (size_t) -1)
- value[last_value_whitespace] = 0;
-
- /* strip trailing whitespace from key */
- if (last_key_whitespace != (size_t) -1)
- key[last_key_whitespace] = 0;
-
- r = push(fname, line, key, value, userdata, n_pushed);
- if (r < 0)
- goto fail;
- }
-
- return 0;
-
-fail:
- free(value);
- return r;
-}
-
-static int parse_env_file_push(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
-
- const char *k;
- va_list aq, *ap = userdata;
-
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *p;
-
- p = utf8_escape_invalid(key);
- log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *p;
-
- p = utf8_escape_invalid(value);
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
- return -EINVAL;
- }
-
- va_copy(aq, *ap);
-
- while ((k = va_arg(aq, const char *))) {
- char **v;
-
- v = va_arg(aq, char **);
-
- if (streq(key, k)) {
- va_end(aq);
- free(*v);
- *v = value;
-
- if (n_pushed)
- (*n_pushed)++;
-
- return 1;
- }
- }
-
- va_end(aq);
- free(value);
-
- return 0;
-}
-
-int parse_env_file(
- const char *fname,
- const char *newline, ...) {
-
- va_list ap;
- int r, n_pushed = 0;
-
- if (!newline)
- newline = NEWLINE;
-
- va_start(ap, newline);
- r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
- va_end(ap);
-
- return r < 0 ? r : n_pushed;
-}
-
-static int load_env_file_push(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
- char ***m = userdata;
- char *p;
- int r;
-
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *t = utf8_escape_invalid(key);
-
- log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *t = utf8_escape_invalid(value);
-
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
- return -EINVAL;
- }
-
- p = strjoin(key, "=", strempty(value), NULL);
- if (!p)
- return -ENOMEM;
-
- r = strv_consume(m, p);
- if (r < 0)
- return r;
-
- if (n_pushed)
- (*n_pushed)++;
-
- free(value);
- return 0;
-}
-
-int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
- char **m = NULL;
- int r;
-
- if (!newline)
- newline = NEWLINE;
-
- r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
- if (r < 0) {
- strv_free(m);
- return r;
- }
-
- *rl = m;
- return 0;
-}
-
-static int load_env_file_push_pairs(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
- char ***m = userdata;
- int r;
-
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *t = utf8_escape_invalid(key);
-
- log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *t = utf8_escape_invalid(value);
-
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
- return -EINVAL;
- }
-
- r = strv_extend(m, key);
- if (r < 0)
- return -ENOMEM;
-
- if (!value) {
- r = strv_extend(m, "");
- if (r < 0)
- return -ENOMEM;
- } else {
- r = strv_push(m, value);
- if (r < 0)
- return r;
- }
-
- if (n_pushed)
- (*n_pushed)++;
-
- return 0;
-}
-
-int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
- char **m = NULL;
- int r;
-
- if (!newline)
- newline = NEWLINE;
-
- r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
- if (r < 0) {
- strv_free(m);
- return r;
- }
-
- *rl = m;
- return 0;
-}
-
-static void write_env_var(FILE *f, const char *v) {
- const char *p;
-
- p = strchr(v, '=');
- if (!p) {
- /* Fallback */
- fputs(v, f);
- fputc('\n', f);
- return;
- }
-
- p++;
- fwrite(v, 1, p-v, f);
-
- if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
- fputc('\"', f);
-
- for (; *p; p++) {
- if (strchr(SHELL_NEED_ESCAPE, *p))
- fputc('\\', f);
-
- fputc(*p, f);
- }
-
- fputc('\"', f);
- } else
- fputs(p, f);
-
- fputc('\n', f);
-}
-
-int write_env_file(const char *fname, char **l) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *p = NULL;
- char **i;
- int r;
-
- assert(fname);
-
- r = fopen_temporary(fname, &f, &p);
- if (r < 0)
- return r;
-
- fchmod_umask(fileno(f), 0644);
-
- STRV_FOREACH(i, l)
- write_env_var(f, *i);
-
- r = fflush_and_check(f);
- if (r >= 0) {
- if (rename(p, fname) >= 0)
- return 0;
-
- r = -errno;
- }
-
- unlink(p);
- return r;
-}
-
-int executable_is_script(const char *path, char **interpreter) {
- int r;
- _cleanup_free_ char *line = NULL;
- int len;
- char *ans;
-
- assert(path);
-
- r = read_one_line_file(path, &line);
- if (r < 0)
- return r;
-
- if (!startswith(line, "#!"))
- return 0;
-
- ans = strstrip(line + 2);
- len = strcspn(ans, " \t");
-
- if (len == 0)
- return 0;
-
- ans = strndup(ans, len);
- if (!ans)
- return -ENOMEM;
-
- *interpreter = ans;
- return 1;
-}
-
-/**
- * Retrieve one field from a file like /proc/self/status. pattern
- * should start with '\n' and end with a ':'. Whitespace and zeros
- * after the ':' will be skipped. field must be freed afterwards.
- */
-int get_status_field(const char *filename, const char *pattern, char **field) {
- _cleanup_free_ char *status = NULL;
- char *t;
- size_t len;
- int r;
-
- assert(filename);
- assert(pattern);
- assert(field);
-
- r = read_full_file(filename, &status, NULL);
- if (r < 0)
- return r;
-
- t = strstr(status, pattern);
- if (!t)
- return -ENOENT;
-
- t += strlen(pattern);
- if (*t) {
- t += strspn(t, " \t");
-
- /* Also skip zeros, because when this is used for
- * capabilities, we don't want the zeros. This way the
- * same capability set always maps to the same string,
- * irrespective of the total capability set size. For
- * other numbers it shouldn't matter. */
- t += strspn(t, "0");
- /* Back off one char if there's nothing but whitespace
- and zeros */
- if (!*t || isspace(*t))
- t --;
- }
-
- len = strcspn(t, WHITESPACE);
-
- *field = strndup(t, len);
- if (!*field)
- return -ENOMEM;
-
- return 0;
-}
diff --git a/src/shared/fileio.h b/src/shared/fileio.h
deleted file mode 100644
index 5ae51c1..0000000
--- a/src/shared/fileio.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stddef.h>
-#include <stdio.h>
-
-#include "macro.h"
-
-int write_string_stream(FILE *f, const char *line);
-int write_string_file(const char *fn, const char *line);
-int write_string_file_no_create(const char *fn, const char *line);
-int write_string_file_atomic(const char *fn, const char *line);
-
-int read_one_line_file(const char *fn, char **line);
-int read_full_file(const char *fn, char **contents, size_t *size);
-int read_full_stream(FILE *f, char **contents, size_t *size);
-
-int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
-int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
-int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
-
-int write_env_file(const char *fname, char **l);
-
-int executable_is_script(const char *path, char **interpreter);
-
-int get_status_field(const char *filename, const char *pattern, char **field);
diff --git a/src/shared/gunicode.c b/src/shared/gunicode.c
deleted file mode 100644
index d89a2f3..0000000
--- a/src/shared/gunicode.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/* gunicode.c - Unicode manipulation functions
- *
- * Copyright (C) 1999, 2000 Tom Tromey
- * Copyright 2000, 2005 Red Hat, Inc.
- */
-
-#include "gunicode.h"
-
-#define unichar uint32_t
-
-/**
- * g_utf8_prev_char:
- * @p: a pointer to a position within a UTF-8 encoded string
- *
- * Finds the previous UTF-8 character in the string before @p.
- *
- * @p does not have to be at the beginning of a UTF-8 character. No check
- * is made to see if the character found is actually valid other than
- * it starts with an appropriate byte. If @p might be the first
- * character of the string, you must use g_utf8_find_prev_char() instead.
- *
- * Return value: a pointer to the found character.
- **/
-char *
-utf8_prev_char (const char *p)
-{
- while (1)
- {
- p--;
- if ((*p & 0xc0) != 0x80)
- return (char *)p;
- }
-}
-
-struct Interval
-{
- unichar start, end;
-};
-
-static int
-interval_compare (const void *key, const void *elt)
-{
- unichar c = (unichar) (long) (key);
- struct Interval *interval = (struct Interval *)elt;
-
- if (c < interval->start)
- return -1;
- if (c > interval->end)
- return +1;
-
- return 0;
-}
-
-/*
- * NOTE:
- *
- * The tables for g_unichar_iswide() and g_unichar_iswide_cjk() are
- * generated from the Unicode Character Database's file
- * extracted/DerivedEastAsianWidth.txt using the gen-iswide-table.py
- * in this way:
- *
- * ./gen-iswide-table.py < path/to/ucd/extracted/DerivedEastAsianWidth.txt | fmt
- *
- * Last update for Unicode 6.0.
- */
-
-/**
- * g_unichar_iswide:
- * @c: a Unicode character
- *
- * Determines if a character is typically rendered in a double-width
- * cell.
- *
- * Return value: %TRUE if the character is wide
- **/
-bool
-unichar_iswide (unichar c)
-{
- /* See NOTE earlier for how to update this table. */
- static const struct Interval wide[] = {
- {0x1100, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3},
- {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096},
- {0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA},
- {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE},
- {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
- {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
- {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6},
- {0x1B000, 0x1B001}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23A},
- {0x1F240, 0x1F248}, {0x1F250, 0x1F251},
- {0x1F300, 0x1F567}, /* Miscellaneous Symbols and Pictographs */
- {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
- };
-
- if (bsearch ((void *)(uintptr_t)c, wide, (sizeof (wide) / sizeof ((wide)[0])), sizeof wide[0],
- interval_compare))
- return true;
-
- return false;
-}
-
-const char utf8_skip_data[256] = {
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
-};
diff --git a/src/shared/gunicode.h b/src/shared/gunicode.h
deleted file mode 100644
index e70818f..0000000
--- a/src/shared/gunicode.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* gunicode.h - Unicode manipulation functions
- *
- * Copyright (C) 1999, 2000 Tom Tromey
- * Copyright 2000, 2005 Red Hat, Inc.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-char *utf8_prev_char (const char *p);
-
-extern const char utf8_skip_data[256];
-
-/**
- * g_utf8_next_char:
- * @p: Pointer to the start of a valid UTF-8 character
- *
- * Skips to the next character in a UTF-8 string. The string must be
- * valid; this macro is as fast as possible, and has no error-checking.
- * You would use this macro to iterate over a string character by
- * character. The macro returns the start of the next UTF-8 character.
- * Before using this macro, use g_utf8_validate() to validate strings
- * that may contain invalid UTF-8.
- */
-#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)])
-
-bool unichar_iswide (uint32_t c);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
deleted file mode 100644
index 20d599d..0000000
--- a/src/shared/hashmap.c
+++ /dev/null
@@ -1,1854 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2014 Michal Schmidt
-
- 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 <errno.h>
-
-#include "util.h"
-#include "hashmap.h"
-#include "set.h"
-#include "macro.h"
-#include "siphash24.h"
-#include "strv.h"
-#include "mempool.h"
-#include "random-util.h"
-
-#ifdef ENABLE_DEBUG_HASHMAP
-#include "list.h"
-#endif
-
-/*
- * Implementation of hashmaps.
- * Addressing: open
- * - uses less RAM compared to closed addressing (chaining), because
- * our entries are small (especially in Sets, which tend to contain
- * the majority of entries in systemd).
- * Collision resolution: Robin Hood
- * - tends to equalize displacement of entries from their optimal buckets.
- * Probe sequence: linear
- * - though theoretically worse than random probing/uniform hashing/double
- * hashing, it is good for cache locality.
- *
- * References:
- * Celis, P. 1986. Robin Hood Hashing.
- * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
- * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
- * - The results are derived for random probing. Suggests deletion with
- * tombstones and two mean-centered search methods. None of that works
- * well for linear probing.
- *
- * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
- * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
- * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
- * http://www.math.uu.se/~svante/papers/sj157.pdf
- * - Applies to Robin Hood with linear probing. Contains remarks on
- * the unsuitability of mean-centered search with linear probing.
- *
- * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
- * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
- * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
- * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
- * in a successful search), and Janson writes about displacement. C = d + 1.
- *
- * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
- * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
- * - Explanation of backward shift deletion with pictures.
- *
- * Khuong, P. 2013. The Other Robin Hood Hashing.
- * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
- * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
- */
-
-/*
- * XXX Ideas for improvement:
- * For unordered hashmaps, randomize iteration order, similarly to Perl:
- * http://blog.booking.com/hardening-perls-hash-function.html
- */
-
-/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
- * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
-#define INV_KEEP_FREE 5U
-
-/* Fields common to entries of all hashmap/set types */
-struct hashmap_base_entry {
- const void *key;
-};
-
-/* Entry types for specific hashmap/set types
- * hashmap_base_entry must be at the beginning of each entry struct. */
-
-struct plain_hashmap_entry {
- struct hashmap_base_entry b;
- void *value;
-};
-
-struct ordered_hashmap_entry {
- struct plain_hashmap_entry p;
- unsigned iterate_next, iterate_previous;
-};
-
-struct set_entry {
- struct hashmap_base_entry b;
-};
-
-/* In several functions it is advantageous to have the hash table extended
- * virtually by a couple of additional buckets. We reserve special index values
- * for these "swap" buckets. */
-#define _IDX_SWAP_BEGIN (UINT_MAX - 3)
-#define IDX_PUT (_IDX_SWAP_BEGIN + 0)
-#define IDX_TMP (_IDX_SWAP_BEGIN + 1)
-#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2)
-
-#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */
-#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */
-
-assert_cc(IDX_FIRST == _IDX_SWAP_END);
-assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
-
-/* Storage space for the "swap" buckets.
- * All entry types can fit into a ordered_hashmap_entry. */
-struct swap_entries {
- struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
-};
-
-/* Distance from Initial Bucket */
-typedef uint8_t dib_raw_t;
-#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
-#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
-#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
-#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
-
-#define DIB_FREE UINT_MAX
-
-#ifdef ENABLE_DEBUG_HASHMAP
-struct hashmap_debug_info {
- LIST_FIELDS(struct hashmap_debug_info, debug_list);
- unsigned max_entries; /* high watermark of n_entries */
-
- /* who allocated this hashmap */
- int line;
- const char *file;
- const char *func;
-
- /* fields to detect modification while iterating */
- unsigned put_count; /* counts puts into the hashmap */
- unsigned rem_count; /* counts removals from hashmap */
- unsigned last_rem_idx; /* remembers last removal index */
-};
-
-/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
-static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
-
-#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
-
-#else /* !ENABLE_DEBUG_HASHMAP */
-#define HASHMAP_DEBUG_FIELDS
-#endif /* ENABLE_DEBUG_HASHMAP */
-
-enum HashmapType {
- HASHMAP_TYPE_PLAIN,
- HASHMAP_TYPE_ORDERED,
- HASHMAP_TYPE_SET,
- _HASHMAP_TYPE_MAX
-};
-
-struct _packed_ indirect_storage {
- char *storage; /* where buckets and DIBs are stored */
- uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */
-
- unsigned n_entries; /* number of stored entries */
- unsigned n_buckets; /* number of buckets */
-
- unsigned idx_lowest_entry; /* Index below which all buckets are free.
- Makes "while(hashmap_steal_first())" loops
- O(n) instead of O(n^2) for unordered hashmaps. */
- uint8_t _pad[3]; /* padding for the whole HashmapBase */
- /* The bitfields in HashmapBase complete the alignment of the whole thing. */
-};
-
-struct direct_storage {
- /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
- * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
- * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
- char storage[sizeof(struct indirect_storage)];
-};
-
-#define DIRECT_BUCKETS(entry_t) \
- (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
-
-/* We should be able to store at least one entry directly. */
-assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
-
-/* We have 3 bits for n_direct_entries. */
-assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
-
-/* Hashmaps with directly stored entries all use this shared hash key.
- * It's no big deal if the key is guessed, because there can be only
- * a handful of directly stored entries in a hashmap. When a hashmap
- * outgrows direct storage, it gets its own key for indirect storage. */
-static uint8_t shared_hash_key[HASH_KEY_SIZE];
-static bool shared_hash_key_initialized;
-
-/* Fields that all hashmap/set types must have */
-struct HashmapBase {
- const struct hash_ops *hash_ops; /* hash and compare ops to use */
-
- union _packed_ {
- struct indirect_storage indirect; /* if has_indirect */
- struct direct_storage direct; /* if !has_indirect */
- };
-
- enum HashmapType type:2; /* HASHMAP_TYPE_* */
- bool has_indirect:1; /* whether indirect storage is used */
- unsigned n_direct_entries:3; /* Number of entries in direct storage.
- * Only valid if !has_indirect. */
- bool from_pool:1; /* whether was allocated from mempool */
- HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
-};
-
-/* Specific hash types
- * HashmapBase must be at the beginning of each hashmap struct. */
-
-struct Hashmap {
- struct HashmapBase b;
-};
-
-struct OrderedHashmap {
- struct HashmapBase b;
- unsigned iterate_list_head, iterate_list_tail;
-};
-
-struct Set {
- struct HashmapBase b;
-};
-
-DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
-DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
-/* No need for a separate Set pool */
-assert_cc(sizeof(Hashmap) == sizeof(Set));
-
-struct hashmap_type_info {
- size_t head_size;
- size_t entry_size;
- struct mempool *mempool;
- unsigned n_direct_buckets;
-};
-
-static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
- [HASHMAP_TYPE_PLAIN] = {
- .head_size = sizeof(Hashmap),
- .entry_size = sizeof(struct plain_hashmap_entry),
- .mempool = &hashmap_pool,
- .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
- },
- [HASHMAP_TYPE_ORDERED] = {
- .head_size = sizeof(OrderedHashmap),
- .entry_size = sizeof(struct ordered_hashmap_entry),
- .mempool = &ordered_hashmap_pool,
- .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
- },
- [HASHMAP_TYPE_SET] = {
- .head_size = sizeof(Set),
- .entry_size = sizeof(struct set_entry),
- .mempool = &hashmap_pool,
- .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
- },
-};
-
-unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, p, strlen(p), hash_key);
- return (unsigned long) u;
-}
-
-int string_compare_func(const void *a, const void *b) {
- return strcmp(a, b);
-}
-
-const struct hash_ops string_hash_ops = {
- .hash = string_hash_func,
- .compare = string_compare_func
-};
-
-unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, &p, sizeof(p), hash_key);
- return (unsigned long) u;
-}
-
-int trivial_compare_func(const void *a, const void *b) {
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops trivial_hash_ops = {
- .hash = trivial_hash_func,
- .compare = trivial_compare_func
-};
-
-unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
- return (unsigned long) u;
-}
-
-int uint64_compare_func(const void *_a, const void *_b) {
- uint64_t a, b;
- a = *(const uint64_t*) _a;
- b = *(const uint64_t*) _b;
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops uint64_hash_ops = {
- .hash = uint64_hash_func,
- .compare = uint64_compare_func
-};
-
-#if SIZEOF_DEV_T != 8
-unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
- uint64_t u;
- siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
- return (unsigned long) u;
-}
-
-int devt_compare_func(const void *_a, const void *_b) {
- dev_t a, b;
- a = *(const dev_t*) _a;
- b = *(const dev_t*) _b;
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
-#endif
-
-static unsigned n_buckets(HashmapBase *h) {
- return h->has_indirect ? h->indirect.n_buckets
- : hashmap_type_info[h->type].n_direct_buckets;
-}
-
-static unsigned n_entries(HashmapBase *h) {
- return h->has_indirect ? h->indirect.n_entries
- : h->n_direct_entries;
-}
-
-static void n_entries_inc(HashmapBase *h) {
- if (h->has_indirect)
- h->indirect.n_entries++;
- else
- h->n_direct_entries++;
-}
-
-static void n_entries_dec(HashmapBase *h) {
- if (h->has_indirect)
- h->indirect.n_entries--;
- else
- h->n_direct_entries--;
-}
-
-static char *storage_ptr(HashmapBase *h) {
- return h->has_indirect ? h->indirect.storage
- : h->direct.storage;
-}
-
-static uint8_t *hash_key(HashmapBase *h) {
- return h->has_indirect ? h->indirect.hash_key
- : shared_hash_key;
-}
-
-static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
- return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h));
-}
-#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
-
-static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
- static uint8_t current[HASH_KEY_SIZE];
- static bool current_initialized = false;
-
- /* Returns a hash function key to use. In order to keep things
- * fast we will not generate a new key each time we allocate a
- * new hash table. Instead, we'll just reuse the most recently
- * generated one, except if we never generated one or when we
- * are rehashing an entire hash table because we reached a
- * fill level */
-
- if (!current_initialized || !reuse_is_ok) {
- random_bytes(current, sizeof(current));
- current_initialized = true;
- }
-
- memcpy(hash_key, current, sizeof(current));
-}
-
-static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
- return (struct hashmap_base_entry*)
- (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
-}
-
-static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
- return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
-}
-
-static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
- return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
-}
-
-static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
- return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
-}
-
-static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
- return &swap->e[idx - _IDX_SWAP_BEGIN];
-}
-
-/* Returns a pointer to the bucket at index idx.
- * Understands real indexes and swap indexes, hence "_virtual". */
-static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
- unsigned idx) {
- if (idx < _IDX_SWAP_BEGIN)
- return bucket_at(h, idx);
-
- if (idx < _IDX_SWAP_END)
- return &bucket_at_swap(swap, idx)->p.b;
-
- assert_not_reached("Invalid index");
-}
-
-static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
- return (dib_raw_t*)
- (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
-}
-
-static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
- return idx >= from ? idx - from
- : n_buckets(h) + idx - from;
-}
-
-static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
- unsigned initial_bucket;
-
- if (raw_dib == DIB_RAW_FREE)
- return DIB_FREE;
-
- if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
- return raw_dib;
-
- /*
- * Having an overflow DIB value is very unlikely. The hash function
- * would have to be bad. For example, in a table of size 2^24 filled
- * to load factor 0.9 the maximum observed DIB is only about 60.
- * In theory (assuming I used Maxima correctly), for an infinite size
- * hash table with load factor 0.8 the probability of a given entry
- * having DIB > 40 is 1.9e-8.
- * This returns the correct DIB value by recomputing the hash value in
- * the unlikely case. XXX Hitting this case could be a hint to rehash.
- */
- initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
- return bucket_distance(h, idx, initial_bucket);
-}
-
-static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
- dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
-}
-
-static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
- dib_raw_t *dibs;
-
- dibs = dib_raw_ptr(h);
-
- for ( ; idx < n_buckets(h); idx++)
- if (dibs[idx] != DIB_RAW_FREE)
- return idx;
-
- return IDX_NIL;
-}
-
-static void bucket_mark_free(HashmapBase *h, unsigned idx) {
- memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size);
- bucket_set_dib(h, idx, DIB_FREE);
-}
-
-static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
- unsigned from, unsigned to) {
- struct hashmap_base_entry *e_from, *e_to;
-
- assert(from != to);
-
- e_from = bucket_at_virtual(h, swap, from);
- e_to = bucket_at_virtual(h, swap, to);
-
- memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
-
- if (h->type == HASHMAP_TYPE_ORDERED) {
- OrderedHashmap *lh = (OrderedHashmap*) h;
- struct ordered_hashmap_entry *le, *le_to;
-
- le_to = (struct ordered_hashmap_entry*) e_to;
-
- if (le_to->iterate_next != IDX_NIL) {
- le = (struct ordered_hashmap_entry*)
- bucket_at_virtual(h, swap, le_to->iterate_next);
- le->iterate_previous = to;
- }
-
- if (le_to->iterate_previous != IDX_NIL) {
- le = (struct ordered_hashmap_entry*)
- bucket_at_virtual(h, swap, le_to->iterate_previous);
- le->iterate_next = to;
- }
-
- if (lh->iterate_list_head == from)
- lh->iterate_list_head = to;
- if (lh->iterate_list_tail == from)
- lh->iterate_list_tail = to;
- }
-}
-
-static unsigned next_idx(HashmapBase *h, unsigned idx) {
- return (idx + 1U) % n_buckets(h);
-}
-
-static unsigned prev_idx(HashmapBase *h, unsigned idx) {
- return (n_buckets(h) + idx - 1U) % n_buckets(h);
-}
-
-static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
- switch (h->type) {
-
- case HASHMAP_TYPE_PLAIN:
- case HASHMAP_TYPE_ORDERED:
- return ((struct plain_hashmap_entry*)e)->value;
-
- case HASHMAP_TYPE_SET:
- return (void*) e->key;
-
- default:
- assert_not_reached("Unknown hashmap type");
- }
-}
-
-static void base_remove_entry(HashmapBase *h, unsigned idx) {
- unsigned left, right, prev, dib;
- dib_raw_t raw_dib, *dibs;
-
- dibs = dib_raw_ptr(h);
- assert(dibs[idx] != DIB_RAW_FREE);
-
-#ifdef ENABLE_DEBUG_HASHMAP
- h->debug.rem_count++;
- h->debug.last_rem_idx = idx;
-#endif
-
- left = idx;
- /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
- for (right = next_idx(h, left); ; right = next_idx(h, right)) {
- raw_dib = dibs[right];
- if (raw_dib == 0 || raw_dib == DIB_RAW_FREE)
- break;
-
- /* The buckets are not supposed to be all occupied and with DIB > 0.
- * That would mean we could make everyone better off by shifting them
- * backward. This scenario is impossible. */
- assert(left != right);
- }
-
- if (h->type == HASHMAP_TYPE_ORDERED) {
- OrderedHashmap *lh = (OrderedHashmap*) h;
- struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
-
- if (le->iterate_next != IDX_NIL)
- ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
- else
- lh->iterate_list_tail = le->iterate_previous;
-
- if (le->iterate_previous != IDX_NIL)
- ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
- else
- lh->iterate_list_head = le->iterate_next;
- }
-
- /* Now shift all buckets in the interval (left, right) one step backwards */
- for (prev = left, left = next_idx(h, left); left != right;
- prev = left, left = next_idx(h, left)) {
- dib = bucket_calculate_dib(h, left, dibs[left]);
- assert(dib != 0);
- bucket_move_entry(h, NULL, left, prev);
- bucket_set_dib(h, prev, dib - 1);
- }
-
- bucket_mark_free(h, prev);
- n_entries_dec(h);
-}
-#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
-
-static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
- struct ordered_hashmap_entry *e;
- unsigned idx;
-
- assert(h);
- assert(i);
-
- if (i->idx == IDX_NIL)
- goto at_end;
-
- if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
- goto at_end;
-
- if (i->idx == IDX_FIRST) {
- idx = h->iterate_list_head;
- e = ordered_bucket_at(h, idx);
- } else {
- idx = i->idx;
- e = ordered_bucket_at(h, idx);
- /*
- * We allow removing the current entry while iterating, but removal may cause
- * a backward shift. The next entry may thus move one bucket to the left.
- * To detect when it happens, we remember the key pointer of the entry we were
- * going to iterate next. If it does not match, there was a backward shift.
- */
- if (e->p.b.key != i->next_key) {
- idx = prev_idx(HASHMAP_BASE(h), idx);
- e = ordered_bucket_at(h, idx);
- }
- assert(e->p.b.key == i->next_key);
- }
-
-#ifdef ENABLE_DEBUG_HASHMAP
- i->prev_idx = idx;
-#endif
-
- if (e->iterate_next != IDX_NIL) {
- struct ordered_hashmap_entry *n;
- i->idx = e->iterate_next;
- n = ordered_bucket_at(h, i->idx);
- i->next_key = n->p.b.key;
- } else
- i->idx = IDX_NIL;
-
- return idx;
-
-at_end:
- i->idx = IDX_NIL;
- return IDX_NIL;
-}
-
-static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
- unsigned idx;
-
- assert(h);
- assert(i);
-
- if (i->idx == IDX_NIL)
- goto at_end;
-
- if (i->idx == IDX_FIRST) {
- /* fast forward to the first occupied bucket */
- if (h->has_indirect) {
- i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
- h->indirect.idx_lowest_entry = i->idx;
- } else
- i->idx = skip_free_buckets(h, 0);
-
- if (i->idx == IDX_NIL)
- goto at_end;
- } else {
- struct hashmap_base_entry *e;
-
- assert(i->idx > 0);
-
- e = bucket_at(h, i->idx);
- /*
- * We allow removing the current entry while iterating, but removal may cause
- * a backward shift. The next entry may thus move one bucket to the left.
- * To detect when it happens, we remember the key pointer of the entry we were
- * going to iterate next. If it does not match, there was a backward shift.
- */
- if (e->key != i->next_key)
- e = bucket_at(h, --i->idx);
-
- assert(e->key == i->next_key);
- }
-
- idx = i->idx;
-#ifdef ENABLE_DEBUG_HASHMAP
- i->prev_idx = idx;
-#endif
-
- i->idx = skip_free_buckets(h, i->idx + 1);
- if (i->idx != IDX_NIL)
- i->next_key = bucket_at(h, i->idx)->key;
- else
- i->idx = IDX_NIL;
-
- return idx;
-
-at_end:
- i->idx = IDX_NIL;
- return IDX_NIL;
-}
-
-static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
- if (!h) {
- i->idx = IDX_NIL;
- return IDX_NIL;
- }
-
-#ifdef ENABLE_DEBUG_HASHMAP
- if (i->idx == IDX_FIRST) {
- i->put_count = h->debug.put_count;
- i->rem_count = h->debug.rem_count;
- } else {
- /* While iterating, must not add any new entries */
- assert(i->put_count == h->debug.put_count);
- /* ... or remove entries other than the current one */
- assert(i->rem_count == h->debug.rem_count ||
- (i->rem_count == h->debug.rem_count - 1 &&
- i->prev_idx == h->debug.last_rem_idx));
- /* Reset our removals counter */
- i->rem_count = h->debug.rem_count;
- }
-#endif
-
- return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
- : hashmap_iterate_in_internal_order(h, i);
-}
-
-void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key) {
- struct hashmap_base_entry *e;
- void *data;
- unsigned idx;
-
- idx = hashmap_iterate_entry(h, i);
- if (idx == IDX_NIL) {
- if (key)
- *key = NULL;
-
- return NULL;
- }
-
- e = bucket_at(h, idx);
- data = entry_value(h, e);
- if (key)
- *key = e->key;
-
- return data;
-}
-
-void *set_iterate(Set *s, Iterator *i) {
- return internal_hashmap_iterate(HASHMAP_BASE(s), i, NULL);
-}
-
-#define HASHMAP_FOREACH_IDX(idx, h, i) \
- for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
- (idx != IDX_NIL); \
- (idx) = hashmap_iterate_entry((h), &(i)))
-
-static void reset_direct_storage(HashmapBase *h) {
- const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
- void *p;
-
- assert(!h->has_indirect);
-
- p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
- memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
-}
-
-static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
- HashmapBase *h;
- const struct hashmap_type_info *hi = &hashmap_type_info[type];
- bool use_pool;
-
- use_pool = is_main_thread();
-
- h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
-
- if (!h)
- return NULL;
-
- h->type = type;
- h->from_pool = use_pool;
- h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
-
- if (type == HASHMAP_TYPE_ORDERED) {
- OrderedHashmap *lh = (OrderedHashmap*)h;
- lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
- }
-
- reset_direct_storage(h);
-
- if (!shared_hash_key_initialized) {
- random_bytes(shared_hash_key, sizeof(shared_hash_key));
- shared_hash_key_initialized= true;
- }
-
-#ifdef ENABLE_DEBUG_HASHMAP
- LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
- h->debug.func = func;
- h->debug.file = file;
- h->debug.line = line;
-#endif
-
- return h;
-}
-
-Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
-}
-
-OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
-}
-
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
-}
-
-static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
- enum HashmapType type HASHMAP_DEBUG_PARAMS) {
- HashmapBase *q;
-
- assert(h);
-
- if (*h)
- return 0;
-
- q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
- if (!q)
- return -ENOMEM;
-
- *h = q;
- return 0;
-}
-
-int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
-}
-
-int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
-}
-
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
-}
-
-static void hashmap_free_no_clear(HashmapBase *h) {
- assert(!h->has_indirect);
- assert(!h->n_direct_entries);
-
-#ifdef ENABLE_DEBUG_HASHMAP
- LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
-#endif
-
- if (h->from_pool)
- mempool_free_tile(hashmap_type_info[h->type].mempool, h);
- else
- free(h);
-}
-
-HashmapBase *internal_hashmap_free(HashmapBase *h) {
-
- /* Free the hashmap, but nothing in it */
-
- if (h) {
- internal_hashmap_clear(h);
- hashmap_free_no_clear(h);
- }
-
- return NULL;
-}
-
-HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
-
- /* Free the hashmap and all data objects in it, but not the
- * keys */
-
- if (h) {
- internal_hashmap_clear_free(h);
- hashmap_free_no_clear(h);
- }
-
- return NULL;
-}
-
-Hashmap *hashmap_free_free_free(Hashmap *h) {
-
- /* Free the hashmap and all data and key objects in it */
-
- if (h) {
- hashmap_clear_free_free(h);
- hashmap_free_no_clear(HASHMAP_BASE(h));
- }
-
- return NULL;
-}
-
-void internal_hashmap_clear(HashmapBase *h) {
- if (!h)
- return;
-
- if (h->has_indirect) {
- free(h->indirect.storage);
- h->has_indirect = false;
- }
-
- h->n_direct_entries = 0;
- reset_direct_storage(h);
-
- if (h->type == HASHMAP_TYPE_ORDERED) {
- OrderedHashmap *lh = (OrderedHashmap*) h;
- lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
- }
-}
-
-void internal_hashmap_clear_free(HashmapBase *h) {
- unsigned idx;
-
- if (!h)
- return;
-
- for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
- idx = skip_free_buckets(h, idx + 1))
- free(entry_value(h, bucket_at(h, idx)));
-
- internal_hashmap_clear(h);
-}
-
-void hashmap_clear_free_free(Hashmap *h) {
- unsigned idx;
-
- if (!h)
- return;
-
- for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
- idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
- struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
- free((void*)e->b.key);
- free(e->value);
- }
-
- internal_hashmap_clear(HASHMAP_BASE(h));
-}
-
-static int resize_buckets(HashmapBase *h, unsigned entries_add);
-
-/*
- * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
- * Performs Robin Hood swaps as it goes. The entry to put must be placed
- * by the caller into swap slot IDX_PUT.
- * If used for in-place resizing, may leave a displaced entry in swap slot
- * IDX_PUT. Caller must rehash it next.
- * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
- * false otherwise.
- */
-static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
- struct swap_entries *swap) {
- dib_raw_t raw_dib, *dibs;
- unsigned dib, distance;
-
-#ifdef ENABLE_DEBUG_HASHMAP
- h->debug.put_count++;
-#endif
-
- dibs = dib_raw_ptr(h);
-
- for (distance = 0; ; distance++) {
- raw_dib = dibs[idx];
- if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) {
- if (raw_dib == DIB_RAW_REHASH)
- bucket_move_entry(h, swap, idx, IDX_TMP);
-
- if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
- h->indirect.idx_lowest_entry = idx;
-
- bucket_set_dib(h, idx, distance);
- bucket_move_entry(h, swap, IDX_PUT, idx);
- if (raw_dib == DIB_RAW_REHASH) {
- bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
- return true;
- }
-
- return false;
- }
-
- dib = bucket_calculate_dib(h, idx, raw_dib);
-
- if (dib < distance) {
- /* Found a wealthier entry. Go Robin Hood! */
- bucket_set_dib(h, idx, distance);
-
- /* swap the entries */
- bucket_move_entry(h, swap, idx, IDX_TMP);
- bucket_move_entry(h, swap, IDX_PUT, idx);
- bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
-
- distance = dib;
- }
-
- idx = next_idx(h, idx);
- }
-}
-
-/*
- * Puts an entry into a hashmap, boldly - no check whether key already exists.
- * The caller must place the entry (only its key and value, not link indexes)
- * in swap slot IDX_PUT.
- * Caller must ensure: the key does not exist yet in the hashmap.
- * that resize is not needed if !may_resize.
- * Returns: 1 if entry was put successfully.
- * -ENOMEM if may_resize==true and resize failed with -ENOMEM.
- * Cannot return -ENOMEM if !may_resize.
- */
-static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
- struct swap_entries *swap, bool may_resize) {
- struct ordered_hashmap_entry *new_entry;
- int r;
-
- assert(idx < n_buckets(h));
-
- new_entry = bucket_at_swap(swap, IDX_PUT);
-
- if (may_resize) {
- r = resize_buckets(h, 1);
- if (r < 0)
- return r;
- if (r > 0)
- idx = bucket_hash(h, new_entry->p.b.key);
- }
- assert(n_entries(h) < n_buckets(h));
-
- if (h->type == HASHMAP_TYPE_ORDERED) {
- OrderedHashmap *lh = (OrderedHashmap*) h;
-
- new_entry->iterate_next = IDX_NIL;
- new_entry->iterate_previous = lh->iterate_list_tail;
-
- if (lh->iterate_list_tail != IDX_NIL) {
- struct ordered_hashmap_entry *old_tail;
-
- old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
- assert(old_tail->iterate_next == IDX_NIL);
- old_tail->iterate_next = IDX_PUT;
- }
-
- lh->iterate_list_tail = IDX_PUT;
- if (lh->iterate_list_head == IDX_NIL)
- lh->iterate_list_head = IDX_PUT;
- }
-
- assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
-
- n_entries_inc(h);
-#ifdef ENABLE_DEBUG_HASHMAP
- h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
-#endif
-
- return 1;
-}
-#define hashmap_put_boldly(h, idx, swap, may_resize) \
- hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
-
-/*
- * Returns 0 if resize is not needed.
- * 1 if successfully resized.
- * -ENOMEM on allocation failure.
- */
-static int resize_buckets(HashmapBase *h, unsigned entries_add) {
- struct swap_entries swap;
- char *new_storage;
- dib_raw_t *old_dibs, *new_dibs;
- const struct hashmap_type_info *hi;
- unsigned idx, optimal_idx;
- unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
- uint8_t new_shift;
- bool rehash_next;
-
- assert(h);
-
- hi = &hashmap_type_info[h->type];
- new_n_entries = n_entries(h) + entries_add;
-
- /* overflow? */
- if (_unlikely_(new_n_entries < entries_add))
- return -ENOMEM;
-
- /* For direct storage we allow 100% load, because it's tiny. */
- if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
- return 0;
-
- /*
- * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
- * From it follows: m = n + n/(INV_KEEP_FREE - 1)
- */
- new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
- /* overflow? */
- if (_unlikely_(new_n_buckets < new_n_entries))
- return -ENOMEM;
-
- if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
- return -ENOMEM;
-
- old_n_buckets = n_buckets(h);
-
- if (_likely_(new_n_buckets <= old_n_buckets))
- return 0;
-
- new_shift = log2u_round_up(MAX(
- new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
- 2 * sizeof(struct direct_storage)));
-
- /* Realloc storage (buckets and DIB array). */
- new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
- 1U << new_shift);
- if (!new_storage)
- return -ENOMEM;
-
- /* Must upgrade direct to indirect storage. */
- if (!h->has_indirect) {
- memcpy(new_storage, h->direct.storage,
- old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
- h->indirect.n_entries = h->n_direct_entries;
- h->indirect.idx_lowest_entry = 0;
- h->n_direct_entries = 0;
- }
-
- /* Get a new hash key. If we've just upgraded to indirect storage,
- * allow reusing a previously generated key. It's still a different key
- * from the shared one that we used for direct storage. */
- get_hash_key(h->indirect.hash_key, !h->has_indirect);
-
- h->has_indirect = true;
- h->indirect.storage = new_storage;
- h->indirect.n_buckets = (1U << new_shift) /
- (hi->entry_size + sizeof(dib_raw_t));
-
- old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
- new_dibs = dib_raw_ptr(h);
-
- /*
- * Move the DIB array to the new place, replacing valid DIB values with
- * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
- * Note: Overlap is not possible, because we have at least doubled the
- * number of buckets and dib_raw_t is smaller than any entry type.
- */
- for (idx = 0; idx < old_n_buckets; idx++) {
- assert(old_dibs[idx] != DIB_RAW_REHASH);
- new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
- : DIB_RAW_REHASH;
- }
-
- /* Zero the area of newly added entries (including the old DIB area) */
- memzero(bucket_at(h, old_n_buckets),
- (n_buckets(h) - old_n_buckets) * hi->entry_size);
-
- /* The upper half of the new DIB array needs initialization */
- memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
- (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
-
- /* Rehash entries that need it */
- n_rehashed = 0;
- for (idx = 0; idx < old_n_buckets; idx++) {
- if (new_dibs[idx] != DIB_RAW_REHASH)
- continue;
-
- optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
-
- /*
- * Not much to do if by luck the entry hashes to its current
- * location. Just set its DIB.
- */
- if (optimal_idx == idx) {
- new_dibs[idx] = 0;
- n_rehashed++;
- continue;
- }
-
- new_dibs[idx] = DIB_RAW_FREE;
- bucket_move_entry(h, &swap, idx, IDX_PUT);
- /* bucket_move_entry does not clear the source */
- memzero(bucket_at(h, idx), hi->entry_size);
-
- do {
- /*
- * Find the new bucket for the current entry. This may make
- * another entry homeless and load it into IDX_PUT.
- */
- rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
- n_rehashed++;
-
- /* Did the current entry displace another one? */
- if (rehash_next)
- optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
- } while (rehash_next);
- }
-
- assert(n_rehashed == n_entries(h));
-
- return 1;
-}
-
-/*
- * Finds an entry with a matching key
- * Returns: index of the found entry, or IDX_NIL if not found.
- */
-static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
- struct hashmap_base_entry *e;
- unsigned dib, distance;
- dib_raw_t *dibs = dib_raw_ptr(h);
-
- assert(idx < n_buckets(h));
-
- for (distance = 0; ; distance++) {
- if (dibs[idx] == DIB_RAW_FREE)
- return IDX_NIL;
-
- dib = bucket_calculate_dib(h, idx, dibs[idx]);
-
- if (dib < distance)
- return IDX_NIL;
- if (dib == distance) {
- e = bucket_at(h, idx);
- if (h->hash_ops->compare(e->key, key) == 0)
- return idx;
- }
-
- idx = next_idx(h, idx);
- }
-}
-#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
-
-int hashmap_put(Hashmap *h, const void *key, void *value) {
- struct swap_entries swap;
- struct plain_hashmap_entry *e;
- unsigned hash, idx;
-
- assert(h);
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx != IDX_NIL) {
- e = plain_bucket_at(h, idx);
- if (e->value == value)
- return 0;
- return -EEXIST;
- }
-
- e = &bucket_at_swap(&swap, IDX_PUT)->p;
- e->b.key = key;
- e->value = value;
- return hashmap_put_boldly(h, hash, &swap, true);
-}
-
-int set_put(Set *s, const void *key) {
- struct swap_entries swap;
- struct hashmap_base_entry *e;
- unsigned hash, idx;
-
- assert(s);
-
- hash = bucket_hash(s, key);
- idx = bucket_scan(s, hash, key);
- if (idx != IDX_NIL)
- return 0;
-
- e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
- e->key = key;
- return hashmap_put_boldly(s, hash, &swap, true);
-}
-
-int hashmap_replace(Hashmap *h, const void *key, void *value) {
- struct swap_entries swap;
- struct plain_hashmap_entry *e;
- unsigned hash, idx;
-
- assert(h);
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx != IDX_NIL) {
- e = plain_bucket_at(h, idx);
-#ifdef ENABLE_DEBUG_HASHMAP
- /* Although the key is equal, the key pointer may have changed,
- * and this would break our assumption for iterating. So count
- * this operation as incompatible with iteration. */
- if (e->b.key != key) {
- h->b.debug.put_count++;
- h->b.debug.rem_count++;
- h->b.debug.last_rem_idx = idx;
- }
-#endif
- e->b.key = key;
- e->value = value;
- return 0;
- }
-
- e = &bucket_at_swap(&swap, IDX_PUT)->p;
- e->b.key = key;
- e->value = value;
- return hashmap_put_boldly(h, hash, &swap, true);
-}
-
-int hashmap_update(Hashmap *h, const void *key, void *value) {
- struct plain_hashmap_entry *e;
- unsigned hash, idx;
-
- assert(h);
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL)
- return -ENOENT;
-
- e = plain_bucket_at(h, idx);
- e->value = value;
- return 0;
-}
-
-void *internal_hashmap_get(HashmapBase *h, const void *key) {
- struct hashmap_base_entry *e;
- unsigned hash, idx;
-
- if (!h)
- return NULL;
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
- return entry_value(h, e);
-}
-
-void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
- struct plain_hashmap_entry *e;
- unsigned hash, idx;
-
- if (!h)
- return NULL;
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL)
- return NULL;
-
- e = plain_bucket_at(h, idx);
- if (key2)
- *key2 = (void*) e->b.key;
-
- return e->value;
-}
-
-bool internal_hashmap_contains(HashmapBase *h, const void *key) {
- unsigned hash;
-
- if (!h)
- return false;
-
- hash = bucket_hash(h, key);
- return bucket_scan(h, hash, key) != IDX_NIL;
-}
-
-void *internal_hashmap_remove(HashmapBase *h, const void *key) {
- struct hashmap_base_entry *e;
- unsigned hash, idx;
- void *data;
-
- if (!h)
- return NULL;
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
- data = entry_value(h, e);
- remove_entry(h, idx);
-
- return data;
-}
-
-void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
- struct plain_hashmap_entry *e;
- unsigned hash, idx;
- void *data;
-
- if (!h) {
- if (rkey)
- *rkey = NULL;
- return NULL;
- }
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL) {
- if (rkey)
- *rkey = NULL;
- return NULL;
- }
-
- e = plain_bucket_at(h, idx);
- data = e->value;
- if (rkey)
- *rkey = (void*) e->b.key;
-
- remove_entry(h, idx);
-
- return data;
-}
-
-int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
- struct swap_entries swap;
- struct plain_hashmap_entry *e;
- unsigned old_hash, new_hash, idx;
-
- if (!h)
- return -ENOENT;
-
- old_hash = bucket_hash(h, old_key);
- idx = bucket_scan(h, old_hash, old_key);
- if (idx == IDX_NIL)
- return -ENOENT;
-
- new_hash = bucket_hash(h, new_key);
- if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
- return -EEXIST;
-
- remove_entry(h, idx);
-
- e = &bucket_at_swap(&swap, IDX_PUT)->p;
- e->b.key = new_key;
- e->value = value;
- assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
-
- return 0;
-}
-
-int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
- struct swap_entries swap;
- struct hashmap_base_entry *e;
- unsigned old_hash, new_hash, idx;
-
- if (!s)
- return -ENOENT;
-
- old_hash = bucket_hash(s, old_key);
- idx = bucket_scan(s, old_hash, old_key);
- if (idx == IDX_NIL)
- return -ENOENT;
-
- new_hash = bucket_hash(s, new_key);
- if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
- return -EEXIST;
-
- remove_entry(s, idx);
-
- e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
- e->key = new_key;
- assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
-
- return 0;
-}
-
-int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
- struct swap_entries swap;
- struct plain_hashmap_entry *e;
- unsigned old_hash, new_hash, idx_old, idx_new;
-
- if (!h)
- return -ENOENT;
-
- old_hash = bucket_hash(h, old_key);
- idx_old = bucket_scan(h, old_hash, old_key);
- if (idx_old == IDX_NIL)
- return -ENOENT;
-
- old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
-
- new_hash = bucket_hash(h, new_key);
- idx_new = bucket_scan(h, new_hash, new_key);
- if (idx_new != IDX_NIL)
- if (idx_old != idx_new) {
- remove_entry(h, idx_new);
- /* Compensate for a possible backward shift. */
- if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
- idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
- assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
- }
-
- remove_entry(h, idx_old);
-
- e = &bucket_at_swap(&swap, IDX_PUT)->p;
- e->b.key = new_key;
- e->value = value;
- assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
-
- return 0;
-}
-
-void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
- struct plain_hashmap_entry *e;
- unsigned hash, idx;
-
- if (!h)
- return NULL;
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL)
- return NULL;
-
- e = plain_bucket_at(h, idx);
- if (e->value != value)
- return NULL;
-
- remove_entry(h, idx);
-
- return value;
-}
-
-static unsigned find_first_entry(HashmapBase *h) {
- Iterator i = ITERATOR_FIRST;
-
- if (!h || !n_entries(h))
- return IDX_NIL;
-
- return hashmap_iterate_entry(h, &i);
-}
-
-void *internal_hashmap_first(HashmapBase *h) {
- unsigned idx;
-
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- return entry_value(h, bucket_at(h, idx));
-}
-
-void *internal_hashmap_first_key(HashmapBase *h) {
- struct hashmap_base_entry *e;
- unsigned idx;
-
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
- return (void*) e->key;
-}
-
-void *internal_hashmap_steal_first(HashmapBase *h) {
- struct hashmap_base_entry *e;
- void *data;
- unsigned idx;
-
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
- data = entry_value(h, e);
- remove_entry(h, idx);
-
- return data;
-}
-
-void *internal_hashmap_steal_first_key(HashmapBase *h) {
- struct hashmap_base_entry *e;
- void *key;
- unsigned idx;
-
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
- key = (void*) e->key;
- remove_entry(h, idx);
-
- return key;
-}
-
-unsigned internal_hashmap_size(HashmapBase *h) {
-
- if (!h)
- return 0;
-
- return n_entries(h);
-}
-
-unsigned internal_hashmap_buckets(HashmapBase *h) {
-
- if (!h)
- return 0;
-
- return n_buckets(h);
-}
-
-int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
- Iterator i;
- unsigned idx;
-
- assert(h);
-
- HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
- struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
- int r;
-
- r = hashmap_put(h, pe->b.key, pe->value);
- if (r < 0 && r != -EEXIST)
- return r;
- }
-
- return 0;
-}
-
-int set_merge(Set *s, Set *other) {
- Iterator i;
- unsigned idx;
-
- assert(s);
-
- HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
- struct set_entry *se = set_bucket_at(other, idx);
- int r;
-
- r = set_put(s, se->b.key);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
- int r;
-
- assert(h);
-
- r = resize_buckets(h, entries_add);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-/*
- * The same as hashmap_merge(), but every new item from other is moved to h.
- * Keys already in h are skipped and stay in other.
- * Returns: 0 on success.
- * -ENOMEM on alloc failure, in which case no move has been done.
- */
-int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
- struct swap_entries swap;
- struct hashmap_base_entry *e, *n;
- Iterator i;
- unsigned idx;
- int r;
-
- assert(h);
-
- if (!other)
- return 0;
-
- assert(other->type == h->type);
-
- /*
- * This reserves buckets for the worst case, where none of other's
- * entries are yet present in h. This is preferable to risking
- * an allocation failure in the middle of the moving and having to
- * rollback or return a partial result.
- */
- r = resize_buckets(h, n_entries(other));
- if (r < 0)
- return r;
-
- HASHMAP_FOREACH_IDX(idx, other, i) {
- unsigned h_hash;
-
- e = bucket_at(other, idx);
- h_hash = bucket_hash(h, e->key);
- if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
- continue;
-
- n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
- n->key = e->key;
- if (h->type != HASHMAP_TYPE_SET)
- ((struct plain_hashmap_entry*) n)->value =
- ((struct plain_hashmap_entry*) e)->value;
- assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
-
- remove_entry(other, idx);
- }
-
- return 0;
-}
-
-int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
- struct swap_entries swap;
- unsigned h_hash, other_hash, idx;
- struct hashmap_base_entry *e, *n;
- int r;
-
- assert(h);
-
- h_hash = bucket_hash(h, key);
- if (bucket_scan(h, h_hash, key) != IDX_NIL)
- return -EEXIST;
-
- if (!other)
- return -ENOENT;
-
- assert(other->type == h->type);
-
- other_hash = bucket_hash(other, key);
- idx = bucket_scan(other, other_hash, key);
- if (idx == IDX_NIL)
- return -ENOENT;
-
- e = bucket_at(other, idx);
-
- n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
- n->key = e->key;
- if (h->type != HASHMAP_TYPE_SET)
- ((struct plain_hashmap_entry*) n)->value =
- ((struct plain_hashmap_entry*) e)->value;
- r = hashmap_put_boldly(h, h_hash, &swap, true);
- if (r < 0)
- return r;
-
- remove_entry(other, idx);
- return 0;
-}
-
-HashmapBase *internal_hashmap_copy(HashmapBase *h) {
- HashmapBase *copy;
- int r;
-
- assert(h);
-
- copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS);
- if (!copy)
- return NULL;
-
- switch (h->type) {
- case HASHMAP_TYPE_PLAIN:
- case HASHMAP_TYPE_ORDERED:
- r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
- break;
- case HASHMAP_TYPE_SET:
- r = set_merge((Set*)copy, (Set*)h);
- break;
- default:
- assert_not_reached("Unknown hashmap type");
- }
-
- if (r < 0) {
- internal_hashmap_free(copy);
- return NULL;
- }
-
- return copy;
-}
-
-char **internal_hashmap_get_strv(HashmapBase *h) {
- char **sv;
- Iterator i;
- unsigned idx, n;
-
- sv = new(char*, n_entries(h)+1);
- if (!sv)
- return NULL;
-
- n = 0;
- HASHMAP_FOREACH_IDX(idx, h, i)
- sv[n++] = entry_value(h, bucket_at(h, idx));
- sv[n] = NULL;
-
- return sv;
-}
-
-void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
- struct ordered_hashmap_entry *e;
- unsigned hash, idx;
-
- assert(key);
-
- if (!h)
- return NULL;
-
- hash = bucket_hash(h, key);
- idx = bucket_scan(h, hash, key);
- if (idx == IDX_NIL)
- return NULL;
-
- e = ordered_bucket_at(h, idx);
- if (e->iterate_next == IDX_NIL)
- return NULL;
- return ordered_bucket_at(h, e->iterate_next)->p.value;
-}
-
-int set_consume(Set *s, void *value) {
- int r;
-
- r = set_put(s, value);
- if (r <= 0)
- free(value);
-
- return r;
-}
-
-int set_put_strdup(Set *s, const char *p) {
- char *c;
- int r;
-
- assert(s);
- assert(p);
-
- c = strdup(p);
- if (!c)
- return -ENOMEM;
-
- r = set_consume(s, c);
- if (r == -EEXIST)
- return 0;
-
- return r;
-}
-
-int set_put_strdupv(Set *s, char **l) {
- int n = 0, r;
- char **i;
-
- STRV_FOREACH(i, l) {
- r = set_put_strdup(s, *i);
- if (r < 0)
- return r;
-
- n += r;
- }
-
- return n;
-}
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
deleted file mode 100644
index a03ee58..0000000
--- a/src/shared/hashmap.h
+++ /dev/null
@@ -1,420 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2014 Michal Schmidt
-
- 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 <stdbool.h>
-
-#include "macro.h"
-#include "util.h"
-
-/*
- * A hash table implementation. As a minor optimization a NULL hashmap object
- * will be treated as empty hashmap for all read operations. That way it is not
- * necessary to instantiate an object for each Hashmap use.
- *
- * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
- * the implemention will:
- * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
- * - perform extra checks for invalid use of iterators
- */
-
-#define HASH_KEY_SIZE 16
-
-/* The base type for all hashmap and set types. Many functions in the
- * implementation take (HashmapBase*) parameters and are run-time polymorphic,
- * though the API is not meant to be polymorphic (do not call functions
- * internal_*() directly). */
-typedef struct HashmapBase HashmapBase;
-
-/* Specific hashmap/set types */
-typedef struct Hashmap Hashmap; /* Maps keys to values */
-typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
-typedef struct Set Set; /* Stores just keys */
-
-/* Ideally the Iterator would be an opaque struct, but it is instantiated
- * by hashmap users, so the definition has to be here. Do not use its fields
- * directly. */
-typedef struct {
- unsigned idx; /* index of an entry to be iterated next */
- const void *next_key; /* expected value of that entry's key pointer */
-#ifdef ENABLE_DEBUG_HASHMAP
- unsigned put_count; /* hashmap's put_count recorded at start of iteration */
- unsigned rem_count; /* hashmap's rem_count in previous iteration */
- unsigned prev_idx; /* idx in previous iteration */
-#endif
-} Iterator;
-
-#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
-#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
-
-typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
-typedef int (*compare_func_t)(const void *a, const void *b);
-
-struct hash_ops {
- hash_func_t hash;
- compare_func_t compare;
-};
-
-unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int string_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops string_hash_ops;
-
-/* This will compare the passed pointers directly, and will not
- * dereference them. This is hence not useful for strings or
- * suchlike. */
-unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int trivial_compare_func(const void *a, const void *b) _const_;
-extern const struct hash_ops trivial_hash_ops;
-
-/* 32bit values we can always just embedd in the pointer itself, but
- * in order to support 32bit archs we need store 64bit values
- * indirectly, since they don't fit in a pointer. */
-unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int uint64_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops uint64_hash_ops;
-
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
- * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
-#if SIZEOF_DEV_T != 8
-unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
-int devt_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
-#else
-#define devt_hash_func uint64_hash_func
-#define devt_compare_func uint64_compare_func
-#define devt_hash_ops uint64_hash_ops
-#endif
-
-/* Macros for type checking */
-#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
- (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
- __builtin_types_compatible_p(typeof(h), Hashmap*) || \
- __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
- __builtin_types_compatible_p(typeof(h), Set*))
-
-#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
- (__builtin_types_compatible_p(typeof(h), Hashmap*) || \
- __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
-
-#define HASHMAP_BASE(h) \
- __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
- (HashmapBase*)(h), \
- (void)0)
-
-#define PLAIN_HASHMAP(h) \
- __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
- (Hashmap*)(h), \
- (void)0)
-
-#ifdef ENABLE_DEBUG_HASHMAP
-# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
-# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
-# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
-#else
-# define HASHMAP_DEBUG_PARAMS
-# define HASHMAP_DEBUG_SRC_ARGS
-# define HASHMAP_DEBUG_PASS_ARGS
-#endif
-
-Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-
-HashmapBase *internal_hashmap_free(HashmapBase *h);
-static inline Hashmap *hashmap_free(Hashmap *h) {
- return (void*)internal_hashmap_free(HASHMAP_BASE(h));
-}
-static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
- return (void*)internal_hashmap_free(HASHMAP_BASE(h));
-}
-
-HashmapBase *internal_hashmap_free_free(HashmapBase *h);
-static inline Hashmap *hashmap_free_free(Hashmap *h) {
- return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
-}
-static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
- return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
-}
-
-Hashmap *hashmap_free_free_free(Hashmap *h);
-static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
- return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
-}
-
-HashmapBase *internal_hashmap_copy(HashmapBase *h);
-static inline Hashmap *hashmap_copy(Hashmap *h) {
- return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
-}
-static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
- return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
-}
-
-int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
-#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
-
-int hashmap_put(Hashmap *h, const void *key, void *value);
-static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
- return hashmap_put(PLAIN_HASHMAP(h), key, value);
-}
-
-int hashmap_update(Hashmap *h, const void *key, void *value);
-static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
- return hashmap_update(PLAIN_HASHMAP(h), key, value);
-}
-
-int hashmap_replace(Hashmap *h, const void *key, void *value);
-static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
- return hashmap_replace(PLAIN_HASHMAP(h), key, value);
-}
-
-void *internal_hashmap_get(HashmapBase *h, const void *key);
-static inline void *hashmap_get(Hashmap *h, const void *key) {
- return internal_hashmap_get(HASHMAP_BASE(h), key);
-}
-static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
- return internal_hashmap_get(HASHMAP_BASE(h), key);
-}
-
-void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
-static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
- return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
-}
-
-bool internal_hashmap_contains(HashmapBase *h, const void *key);
-static inline bool hashmap_contains(Hashmap *h, const void *key) {
- return internal_hashmap_contains(HASHMAP_BASE(h), key);
-}
-static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
- return internal_hashmap_contains(HASHMAP_BASE(h), key);
-}
-
-void *internal_hashmap_remove(HashmapBase *h, const void *key);
-static inline void *hashmap_remove(Hashmap *h, const void *key) {
- return internal_hashmap_remove(HASHMAP_BASE(h), key);
-}
-static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
- return internal_hashmap_remove(HASHMAP_BASE(h), key);
-}
-
-void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
-static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
- return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
-}
-
-void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
-static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
- return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
-}
-
-int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
-static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
- return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
-}
-
-int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
-static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
- return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
-}
-
-/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
- * should just work, allow this by having looser type-checking here. */
-int internal_hashmap_merge(Hashmap *h, Hashmap *other);
-#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
-#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
-
-int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
-static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
- return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
-}
-static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
- return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
-}
-
-int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
-/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
-static inline int hashmap_move(Hashmap *h, Hashmap *other) {
- return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
-}
-static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
- return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
-}
-
-int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
-static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
- return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
-}
-static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
- return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
-}
-
-unsigned internal_hashmap_size(HashmapBase *h) _pure_;
-static inline unsigned hashmap_size(Hashmap *h) {
- return internal_hashmap_size(HASHMAP_BASE(h));
-}
-static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
- return internal_hashmap_size(HASHMAP_BASE(h));
-}
-
-static inline bool hashmap_isempty(Hashmap *h) {
- return hashmap_size(h) == 0;
-}
-static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
- return ordered_hashmap_size(h) == 0;
-}
-
-unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
-static inline unsigned hashmap_buckets(Hashmap *h) {
- return internal_hashmap_buckets(HASHMAP_BASE(h));
-}
-static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
- return internal_hashmap_buckets(HASHMAP_BASE(h));
-}
-
-void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key);
-static inline void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
- return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
-}
-static inline void *ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, const void **key) {
- return internal_hashmap_iterate(HASHMAP_BASE(h), i, key);
-}
-
-void internal_hashmap_clear(HashmapBase *h);
-static inline void hashmap_clear(Hashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h));
-}
-static inline void ordered_hashmap_clear(OrderedHashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h));
-}
-
-void internal_hashmap_clear_free(HashmapBase *h);
-static inline void hashmap_clear_free(Hashmap *h) {
- internal_hashmap_clear_free(HASHMAP_BASE(h));
-}
-static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
- internal_hashmap_clear_free(HASHMAP_BASE(h));
-}
-
-void hashmap_clear_free_free(Hashmap *h);
-static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
- hashmap_clear_free_free(PLAIN_HASHMAP(h));
-}
-
-/*
- * Note about all *_first*() functions
- *
- * For plain Hashmaps and Sets the order of entries is undefined.
- * The functions find whatever entry is first in the implementation
- * internal order.
- *
- * Only for OrderedHashmaps the order is well defined and finding
- * the first entry is O(1).
- */
-
-void *internal_hashmap_steal_first(HashmapBase *h);
-static inline void *hashmap_steal_first(Hashmap *h) {
- return internal_hashmap_steal_first(HASHMAP_BASE(h));
-}
-static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
- return internal_hashmap_steal_first(HASHMAP_BASE(h));
-}
-
-void *internal_hashmap_steal_first_key(HashmapBase *h);
-static inline void *hashmap_steal_first_key(Hashmap *h) {
- return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
-}
-static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
- return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
-}
-
-void *internal_hashmap_first_key(HashmapBase *h) _pure_;
-static inline void *hashmap_first_key(Hashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h));
-}
-static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h));
-}
-
-void *internal_hashmap_first(HashmapBase *h) _pure_;
-static inline void *hashmap_first(Hashmap *h) {
- return internal_hashmap_first(HASHMAP_BASE(h));
-}
-static inline void *ordered_hashmap_first(OrderedHashmap *h) {
- return internal_hashmap_first(HASHMAP_BASE(h));
-}
-
-/* no hashmap_next */
-void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
-
-char **internal_hashmap_get_strv(HashmapBase *h);
-static inline char **hashmap_get_strv(Hashmap *h) {
- return internal_hashmap_get_strv(HASHMAP_BASE(h));
-}
-static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
- return internal_hashmap_get_strv(HASHMAP_BASE(h));
-}
-
-/*
- * Hashmaps are iterated in unpredictable order.
- * OrderedHashmaps are an exception to this. They are iterated in the order
- * the entries were inserted.
- * It is safe to remove the current entry.
- */
-#define HASHMAP_FOREACH(e, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); \
- (e); \
- (e) = hashmap_iterate((h), &(i), NULL))
-
-#define ORDERED_HASHMAP_FOREACH(e, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), NULL); \
- (e); \
- (e) = ordered_hashmap_iterate((h), &(i), NULL))
-
-#define HASHMAP_FOREACH_KEY(e, k, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); \
- (e); \
- (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
-
-#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)); \
- (e); \
- (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)))
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
-
-#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
-#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
-#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
-#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
-#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
-#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
diff --git a/src/shared/hostname-util.c b/src/shared/hostname-util.c
deleted file mode 100644
index e336f26..0000000
--- a/src/shared/hostname-util.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2015 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/utsname.h>
-#include <ctype.h>
-
-#include "util.h"
-#include "hostname-util.h"
-
-bool hostname_is_set(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (isempty(u.nodename))
- return false;
-
- /* This is the built-in kernel default host name */
- if (streq(u.nodename, "(none)"))
- return false;
-
- return true;
-}
-
-char* gethostname_malloc(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (isempty(u.nodename) || streq(u.nodename, "(none)"))
- return strdup(u.sysname);
-
- return strdup(u.nodename);
-}
-
-static bool hostname_valid_char(char c) {
- return
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '-' ||
- c == '_' ||
- c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
- const char *p;
- bool dot;
-
- if (isempty(s))
- return false;
-
- /* Doesn't accept empty hostnames, hostnames with trailing or
- * leading dots, and hostnames with multiple dots in a
- * sequence. Also ensures that the length stays below
- * HOST_NAME_MAX. */
-
- for (p = s, dot = true; *p; p++) {
- if (*p == '.') {
- if (dot)
- return false;
-
- dot = true;
- } else {
- if (!hostname_valid_char(*p))
- return false;
-
- dot = false;
- }
- }
-
- if (dot)
- return false;
-
- if (p-s > HOST_NAME_MAX)
- return false;
-
- return true;
-}
-
-char* hostname_cleanup(char *s, bool lowercase) {
- char *p, *d;
- bool dot;
-
- assert(s);
-
- for (p = s, d = s, dot = true; *p; p++) {
- if (*p == '.') {
- if (dot)
- continue;
-
- *(d++) = '.';
- dot = true;
- } else if (hostname_valid_char(*p)) {
- *(d++) = lowercase ? tolower(*p) : *p;
- dot = false;
- }
-
- }
-
- if (dot && d > s)
- d[-1] = 0;
- else
- *d = 0;
-
- strshorten(s, HOST_NAME_MAX);
-
- return s;
-}
-
-bool is_localhost(const char *hostname) {
- assert(hostname);
-
- /* This tries to identify local host and domain names
- * described in RFC6761 plus the redhatism of .localdomain */
-
- return streq(hostname, "localhost") ||
- streq(hostname, "localhost.") ||
- streq(hostname, "localdomain.") ||
- streq(hostname, "localdomain") ||
- endswith(hostname, ".localhost") ||
- endswith(hostname, ".localhost.") ||
- endswith(hostname, ".localdomain") ||
- endswith(hostname, ".localdomain.");
-}
-
-int sethostname_idempotent(const char *s) {
- char buf[HOST_NAME_MAX + 1] = {};
-
- assert(s);
-
- if (gethostname(buf, sizeof(buf)) < 0)
- return -errno;
-
- if (streq(buf, s))
- return 0;
-
- if (sethostname(s, strlen(s)) < 0)
- return -errno;
-
- return 1;
-}
-
-int read_hostname_config(const char *path, char **hostname) {
- _cleanup_fclose_ FILE *f = NULL;
- char l[LINE_MAX];
- char *name = NULL;
-
- assert(path);
- assert(hostname);
-
- f = fopen(path, "re");
- if (!f)
- return -errno;
-
- /* may have comments, ignore them */
- FOREACH_LINE(l, f, return -errno) {
- truncate_nl(l);
- if (l[0] != '\0' && l[0] != '#') {
- /* found line with value */
- name = hostname_cleanup(l, false);
- name = strdup(name);
- if (!name)
- return -ENOMEM;
- break;
- }
- }
-
- if (!name)
- /* no non-empty line found */
- return -ENOENT;
-
- *hostname = name;
- return 0;
-}
diff --git a/src/shared/hostname-util.h b/src/shared/hostname-util.h
deleted file mode 100644
index 0c4763c..0000000
--- a/src/shared/hostname-util.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010-2015 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 <stdbool.h>
-
-#include "macro.h"
-
-bool hostname_is_set(void);
-
-char* gethostname_malloc(void);
-
-bool hostname_is_valid(const char *s) _pure_;
-char* hostname_cleanup(char *s, bool lowercase);
-
-bool is_localhost(const char *hostname);
-
-int sethostname_idempotent(const char *s);
-
-int read_hostname_config(const char *path, char **hostname);
diff --git a/src/shared/in-addr-util.c b/src/shared/in-addr-util.c
deleted file mode 100644
index d88864b..0000000
--- a/src/shared/in-addr-util.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <arpa/inet.h>
-
-#include "in-addr-util.h"
-
-int in_addr_is_null(int family, const union in_addr_union *u) {
- assert(u);
-
- if (family == AF_INET)
- return u->in.s_addr == 0;
-
- if (family == AF_INET6)
- return
- u->in6.s6_addr32[0] == 0 &&
- u->in6.s6_addr32[1] == 0 &&
- u->in6.s6_addr32[2] == 0 &&
- u->in6.s6_addr32[3] == 0;
-
- return -EAFNOSUPPORT;
-}
-
-int in_addr_is_link_local(int family, const union in_addr_union *u) {
- assert(u);
-
- if (family == AF_INET)
- return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
-
- if (family == AF_INET6)
- return IN6_IS_ADDR_LINKLOCAL(&u->in6);
-
- return -EAFNOSUPPORT;
-}
-
-int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
- assert(a);
- assert(b);
-
- if (family == AF_INET)
- return a->in.s_addr == b->in.s_addr;
-
- if (family == AF_INET6)
- return
- a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
- a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
- a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
- a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
-
- return -EAFNOSUPPORT;
-}
-
-int in_addr_prefix_intersect(
- int family,
- const union in_addr_union *a,
- unsigned aprefixlen,
- const union in_addr_union *b,
- unsigned bprefixlen) {
-
- unsigned m;
-
- assert(a);
- assert(b);
-
- /* Checks whether there are any addresses that are in both
- * networks */
-
- m = MIN(aprefixlen, bprefixlen);
-
- if (family == AF_INET) {
- uint32_t x, nm;
-
- x = be32toh(a->in.s_addr ^ b->in.s_addr);
- nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
-
- return (x & nm) == 0;
- }
-
- if (family == AF_INET6) {
- unsigned i;
-
- if (m > 128)
- m = 128;
-
- for (i = 0; i < 16; i++) {
- uint8_t x, nm;
-
- x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
-
- if (m < 8)
- nm = 0xFF << (8 - m);
- else
- nm = 0xFF;
-
- if ((x & nm) != 0)
- return 0;
-
- if (m > 8)
- m -= 8;
- else
- m = 0;
- }
-
- return 1;
- }
-
- return -EAFNOSUPPORT;
-}
-
-int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
- assert(u);
-
- /* Increases the network part of an address by one. Returns
- * positive it that succeeds, or 0 if this overflows. */
-
- if (prefixlen <= 0)
- return 0;
-
- if (family == AF_INET) {
- uint32_t c, n;
-
- if (prefixlen > 32)
- prefixlen = 32;
-
- c = be32toh(u->in.s_addr);
- n = c + (1UL << (32 - prefixlen));
- if (n < c)
- return 0;
- n &= 0xFFFFFFFFUL << (32 - prefixlen);
-
- u->in.s_addr = htobe32(n);
- return 1;
- }
-
- if (family == AF_INET6) {
- struct in6_addr add = {}, result;
- uint8_t overflow = 0;
- unsigned i;
-
- if (prefixlen > 128)
- prefixlen = 128;
-
- /* First calculate what we have to add */
- add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
-
- for (i = 16; i > 0; i--) {
- unsigned j = i - 1;
-
- result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
- overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
- }
-
- if (overflow)
- return 0;
-
- u->in6 = result;
- return 1;
- }
-
- return -EAFNOSUPPORT;
-}
-
-int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
- char *x;
- size_t l;
-
- assert(u);
- assert(ret);
-
- if (family == AF_INET)
- l = INET_ADDRSTRLEN;
- else if (family == AF_INET6)
- l = INET6_ADDRSTRLEN;
- else
- return -EAFNOSUPPORT;
-
- x = new(char, l);
- if (!x)
- return -ENOMEM;
-
- errno = 0;
- if (!inet_ntop(family, u, x, l)) {
- free(x);
- return errno ? -errno : -EINVAL;
- }
-
- *ret = x;
- return 0;
-}
-
-int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
-
- assert(s);
- assert(ret);
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return -EAFNOSUPPORT;
-
- errno = 0;
- if (inet_pton(family, s, ret) <= 0)
- return errno ? -errno : -EINVAL;
-
- return 0;
-}
-
-int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
- int r;
-
- assert(s);
- assert(family);
- assert(ret);
-
- r = in_addr_from_string(AF_INET, s, ret);
- if (r >= 0) {
- *family = AF_INET;
- return 0;
- }
-
- r = in_addr_from_string(AF_INET6, s, ret);
- if (r >= 0) {
- *family = AF_INET6;
- return 0;
- }
-
- return -EINVAL;
-}
-
-unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
- assert(addr);
-
- return 32 - u32ctz(be32toh(addr->s_addr));
-}
-
-struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
- assert(addr);
- assert(prefixlen <= 32);
-
- /* Shifting beyond 32 is not defined, handle this specially. */
- if (prefixlen == 0)
- addr->s_addr = 0;
- else
- addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
-
- return addr;
-}
-
-int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
- uint8_t msb_octet = *(uint8_t*) addr;
-
- /* addr may not be aligned, so make sure we only access it byte-wise */
-
- assert(addr);
- assert(prefixlen);
-
- if (msb_octet < 128)
- /* class A, leading bits: 0 */
- *prefixlen = 8;
- else if (msb_octet < 192)
- /* class B, leading bits 10 */
- *prefixlen = 16;
- else if (msb_octet < 224)
- /* class C, leading bits 110 */
- *prefixlen = 24;
- else
- /* class D or E, no default prefixlen */
- return -ERANGE;
-
- return 0;
-}
-
-int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
- unsigned char prefixlen;
- int r;
-
- assert(addr);
- assert(mask);
-
- r = in_addr_default_prefixlen(addr, &prefixlen);
- if (r < 0)
- return r;
-
- in_addr_prefixlen_to_netmask(mask, prefixlen);
- return 0;
-}
-
-int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
- assert(addr);
-
- if (family == AF_INET) {
- struct in_addr mask;
-
- if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
- return -EINVAL;
-
- addr->in.s_addr &= mask.s_addr;
- return 0;
- }
-
- if (family == AF_INET6) {
- unsigned i;
-
- for (i = 0; i < 16; i++) {
- uint8_t mask;
-
- if (prefixlen >= 8) {
- mask = 0xFF;
- prefixlen -= 8;
- } else {
- mask = 0xFF << (8 - prefixlen);
- prefixlen = 0;
- }
-
- addr->in6.s6_addr[i] &= mask;
- }
-
- return 0;
- }
-
- return -EAFNOSUPPORT;
-}
diff --git a/src/shared/in-addr-util.h b/src/shared/in-addr-util.h
deleted file mode 100644
index 51af088..0000000
--- a/src/shared/in-addr-util.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <netinet/in.h>
-
-#include "macro.h"
-#include "util.h"
-
-union in_addr_union {
- struct in_addr in;
- struct in6_addr in6;
-};
-
-int in_addr_is_null(int family, const union in_addr_union *u);
-int in_addr_is_link_local(int family, const union in_addr_union *u);
-int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
-int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
-int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
-int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
-int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
-int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret);
-unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr);
-struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
-int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
-int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
-int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
-
-static inline size_t FAMILY_ADDRESS_SIZE(int family) {
- assert(family == AF_INET || family == AF_INET6);
- return family == AF_INET6 ? 16 : 4;
-}
-
-#define IN_ADDR_NULL ((union in_addr_union) {})
diff --git a/src/shared/ioprio.h b/src/shared/ioprio.h
deleted file mode 100644
index e5c71d0..0000000
--- a/src/shared/ioprio.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef IOPRIO_H
-#define IOPRIO_H
-
-/* This is minimal version of Linux' linux/ioprio.h header file, which
- * is licensed GPL2 */
-
-#include <unistd.h>
-#include <sys/syscall.h>
-
-/*
- * Gives us 8 prio classes with 13-bits of data for each class
- */
-#define IOPRIO_BITS (16)
-#define IOPRIO_CLASS_SHIFT (13)
-#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
-
-#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
-#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
-#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
-
-#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE)
-
-/*
- * These are the io priority groups as implemented by CFQ. RT is the realtime
- * class, it always gets premium service. BE is the best-effort scheduling
- * class, the default for any process. IDLE is the idle scheduling class, it
- * is only served when no one else is using the disk.
- */
-enum {
- IOPRIO_CLASS_NONE,
- IOPRIO_CLASS_RT,
- IOPRIO_CLASS_BE,
- IOPRIO_CLASS_IDLE,
-};
-
-/*
- * 8 best effort priority levels are supported
- */
-#define IOPRIO_BE_NR (8)
-
-enum {
- IOPRIO_WHO_PROCESS = 1,
- IOPRIO_WHO_PGRP,
- IOPRIO_WHO_USER,
-};
-
-static inline int ioprio_set(int which, int who, int ioprio) {
- return syscall(__NR_ioprio_set, which, who, ioprio);
-}
-
-static inline int ioprio_get(int which, int who) {
- return syscall(__NR_ioprio_get, which, who);
-}
-
-#endif
diff --git a/src/shared/json.c b/src/shared/json.c
deleted file mode 100644
index be40a0d..0000000
--- a/src/shared/json.c
+++ /dev/null
@@ -1,866 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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>
-#include <math.h>
-#include "macro.h"
-#include "utf8.h"
-#include "json.h"
-
-int json_variant_new(JsonVariant **ret, JsonVariantType type) {
- JsonVariant *v;
-
- v = new0(JsonVariant, 1);
- if (!v)
- return -ENOMEM;
- v->type = type;
- *ret = v;
- return 0;
-}
-
-static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
- int r;
-
- assert(ret);
- assert(variant);
-
- ret->type = variant->type;
- ret->size = variant->size;
-
- if (variant->type == JSON_VARIANT_STRING) {
- ret->string = memdup(variant->string, variant->size+1);
- if (!ret->string)
- return -ENOMEM;
- } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
- size_t i;
-
- ret->objects = new0(JsonVariant, variant->size);
- if (!ret->objects)
- return -ENOMEM;
-
- for (i = 0; i < variant->size; ++i) {
- r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
- if (r < 0)
- return r;
- }
- } else
- ret->value = variant->value;
-
- return 0;
-}
-
-static JsonVariant *json_object_unref(JsonVariant *variant);
-
-static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
- if (!variant)
- return NULL;
-
- if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
- return json_object_unref(variant);
- else if (variant->type == JSON_VARIANT_STRING)
- free(variant->string);
-
- return NULL;
-}
-
-static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
- if (!variant)
- return NULL;
-
- for (size_t i = 0; i < size; ++i)
- json_variant_unref_inner(&variant[i]);
-
- free(variant);
- return NULL;
-}
-
-static JsonVariant *json_object_unref(JsonVariant *variant) {
- size_t i;
-
- assert(variant);
-
- if (!variant->objects)
- return NULL;
-
- for (i = 0; i < variant->size; ++i)
- json_variant_unref_inner(&variant->objects[i]);
-
- free(variant->objects);
- return NULL;
-}
-
-static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
- size_t i = 0;
- JsonVariant *p = NULL;
-
- if (!variant)
- return NULL;
-
- while((p = (variant[i++])) != NULL) {
- if (p->type == JSON_VARIANT_STRING)
- free(p->string);
- free(p);
- }
-
- free(variant);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
-
-JsonVariant *json_variant_unref(JsonVariant *variant) {
- if (!variant)
- return NULL;
-
- if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
- json_object_unref(variant);
- else if (variant->type == JSON_VARIANT_STRING)
- free(variant->string);
-
- free(variant);
-
- return NULL;
-}
-
-char *json_variant_string(JsonVariant *variant){
- assert(variant);
- assert(variant->type == JSON_VARIANT_STRING);
-
- return variant->string;
-}
-
-bool json_variant_bool(JsonVariant *variant) {
- assert(variant);
- assert(variant->type == JSON_VARIANT_BOOLEAN);
-
- return variant->value.boolean;
-}
-
-intmax_t json_variant_integer(JsonVariant *variant) {
- assert(variant);
- assert(variant->type == JSON_VARIANT_INTEGER);
-
- return variant->value.integer;
-}
-
-double json_variant_real(JsonVariant *variant) {
- assert(variant);
- assert(variant->type == JSON_VARIANT_REAL);
-
- return variant->value.real;
-}
-
-JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
- assert(variant);
- assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
- assert(index < variant->size);
- assert(variant->objects);
-
- return &variant->objects[index];
-}
-
-JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
- size_t i;
-
- assert(variant);
- assert(variant->type == JSON_VARIANT_OBJECT);
- assert(variant->objects);
-
- for (i = 0; i < variant->size; i += 2) {
- JsonVariant *p = &variant->objects[i];
- if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
- return &variant->objects[i + 1];
- }
-
- return NULL;
-}
-
-static void inc_lines(unsigned *line, const char *s, size_t n) {
- const char *p = s;
-
- if (!line)
- return;
-
- for (;;) {
- const char *f;
-
- f = memchr(p, '\n', n);
- if (!f)
- return;
-
- n -= (f - p) + 1;
- p = f + 1;
- (*line)++;
- }
-}
-
-static int unhex_ucs2(const char *c, uint16_t *ret) {
- int aa, bb, cc, dd;
- uint16_t x;
-
- assert(c);
- assert(ret);
-
- aa = unhexchar(c[0]);
- if (aa < 0)
- return -EINVAL;
-
- bb = unhexchar(c[1]);
- if (bb < 0)
- return -EINVAL;
-
- cc = unhexchar(c[2]);
- if (cc < 0)
- return -EINVAL;
-
- dd = unhexchar(c[3]);
- if (dd < 0)
- return -EINVAL;
-
- x = ((uint16_t) aa << 12) |
- ((uint16_t) bb << 8) |
- ((uint16_t) cc << 4) |
- ((uint16_t) dd);
-
- if (x <= 0)
- return -EINVAL;
-
- *ret = x;
-
- return 0;
-}
-
-static int json_parse_string(const char **p, char **ret) {
- _cleanup_free_ char *s = NULL;
- size_t n = 0, allocated = 0;
- const char *c;
-
- assert(p);
- assert(*p);
- assert(ret);
-
- c = *p;
-
- if (*c != '"')
- return -EINVAL;
-
- c++;
-
- for (;;) {
- int len;
-
- /* Check for EOF */
- if (*c == 0)
- return -EINVAL;
-
- /* Check for control characters 0x00..0x1f */
- if (*c > 0 && *c < ' ')
- return -EINVAL;
-
- /* Check for control character 0x7f */
- if (*c == 0x7f)
- return -EINVAL;
-
- if (*c == '"') {
- if (!s) {
- s = strdup("");
- if (!s)
- return -ENOMEM;
- } else
- s[n] = 0;
-
- *p = c + 1;
-
- *ret = s;
- s = NULL;
- return JSON_STRING;
- }
-
- if (*c == '\\') {
- char ch = 0;
- c++;
-
- if (*c == 0)
- return -EINVAL;
-
- if (IN_SET(*c, '"', '\\', '/'))
- ch = *c;
- else if (*c == 'b')
- ch = '\b';
- else if (*c == 'f')
- ch = '\f';
- else if (*c == 'n')
- ch = '\n';
- else if (*c == 'r')
- ch = '\r';
- else if (*c == 't')
- ch = '\t';
- else if (*c == 'u') {
- uint16_t x;
- int r;
-
- r = unhex_ucs2(c + 1, &x);
- if (r < 0)
- return r;
-
- c += 5;
-
- if (!GREEDY_REALLOC(s, allocated, n + 4))
- return -ENOMEM;
-
- if (!utf16_is_surrogate(x))
- n += utf8_encode_unichar(s + n, x);
- else if (utf16_is_trailing_surrogate(x))
- return -EINVAL;
- else {
- uint16_t y;
-
- if (c[0] != '\\' || c[1] != 'u')
- return -EINVAL;
-
- r = unhex_ucs2(c + 2, &y);
- if (r < 0)
- return r;
-
- c += 6;
-
- if (!utf16_is_trailing_surrogate(y))
- return -EINVAL;
-
- n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
- }
-
- continue;
- } else
- return -EINVAL;
-
- if (!GREEDY_REALLOC(s, allocated, n + 2))
- return -ENOMEM;
-
- s[n++] = ch;
- c ++;
- continue;
- }
-
- len = utf8_encoded_valid_unichar(c);
- if (len < 0)
- return len;
-
- if (!GREEDY_REALLOC(s, allocated, n + len + 1))
- return -ENOMEM;
-
- memcpy(s + n, c, len);
- n += len;
- c += len;
- }
-}
-
-static int json_parse_number(const char **p, union json_value *ret) {
- bool negative = false, exponent_negative = false, is_double = false;
- double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
- intmax_t i = 0;
- const char *c;
-
- assert(p);
- assert(*p);
- assert(ret);
-
- c = *p;
-
- if (*c == '-') {
- negative = true;
- c++;
- }
-
- if (*c == '0')
- c++;
- else {
- if (!strchr("123456789", *c) || *c == 0)
- return -EINVAL;
-
- do {
- if (!is_double) {
- int64_t t;
-
- t = 10 * i + (*c - '0');
- if (t < i) /* overflow */
- is_double = false;
- else
- i = t;
- }
-
- x = 10.0 * x + (*c - '0');
- c++;
- } while (strchr("0123456789", *c) && *c != 0);
- }
-
- if (*c == '.') {
- is_double = true;
- c++;
-
- if (!strchr("0123456789", *c) || *c == 0)
- return -EINVAL;
-
- do {
- y = 10.0 * y + (*c - '0');
- shift = 10.0 * shift;
- c++;
- } while (strchr("0123456789", *c) && *c != 0);
- }
-
- if (*c == 'e' || *c == 'E') {
- is_double = true;
- c++;
-
- if (*c == '-') {
- exponent_negative = true;
- c++;
- } else if (*c == '+')
- c++;
-
- if (!strchr("0123456789", *c) || *c == 0)
- return -EINVAL;
-
- do {
- exponent = 10.0 * exponent + (*c - '0');
- c++;
- } while (strchr("0123456789", *c) && *c != 0);
- }
-
- *p = c;
-
- if (is_double) {
- ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent);
- return JSON_REAL;
- } else {
- ret->integer = negative ? -i : i;
- return JSON_INTEGER;
- }
-}
-
-int json_tokenize(
- const char **p,
- char **ret_string,
- union json_value *ret_value,
- void **state,
- unsigned *line) {
-
- const char *c;
- int t;
- int r;
-
- enum {
- STATE_NULL,
- STATE_VALUE,
- STATE_VALUE_POST,
- };
-
- assert(p);
- assert(*p);
- assert(ret_string);
- assert(ret_value);
- assert(state);
-
- t = PTR_TO_INT(*state);
- c = *p;
-
- if (t == STATE_NULL) {
- if (line)
- *line = 1;
- t = STATE_VALUE;
- }
-
- for (;;) {
- const char *b;
-
- b = c + strspn(c, WHITESPACE);
- if (*b == 0)
- return JSON_END;
-
- inc_lines(line, c, b - c);
- c = b;
-
- switch (t) {
-
- case STATE_VALUE:
-
- if (*c == '{') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE);
- return JSON_OBJECT_OPEN;
-
- } else if (*c == '}') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_OBJECT_CLOSE;
-
- } else if (*c == '[') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE);
- return JSON_ARRAY_OPEN;
-
- } else if (*c == ']') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_ARRAY_CLOSE;
-
- } else if (*c == '"') {
- r = json_parse_string(&c, ret_string);
- if (r < 0)
- return r;
-
- *ret_value = JSON_VALUE_NULL;
- *p = c;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return r;
-
- } else if (strchr("-0123456789", *c)) {
- r = json_parse_number(&c, ret_value);
- if (r < 0)
- return r;
-
- *ret_string = NULL;
- *p = c;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return r;
-
- } else if (startswith(c, "true")) {
- *ret_string = NULL;
- ret_value->boolean = true;
- *p = c + 4;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_BOOLEAN;
-
- } else if (startswith(c, "false")) {
- *ret_string = NULL;
- ret_value->boolean = false;
- *p = c + 5;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_BOOLEAN;
-
- } else if (startswith(c, "null")) {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 4;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_NULL;
-
- } else
- return -EINVAL;
-
- case STATE_VALUE_POST:
-
- if (*c == ':') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE);
- return JSON_COLON;
- } else if (*c == ',') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE);
- return JSON_COMMA;
- } else if (*c == '}') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_OBJECT_CLOSE;
- } else if (*c == ']') {
- *ret_string = NULL;
- *ret_value = JSON_VALUE_NULL;
- *p = c + 1;
- *state = INT_TO_PTR(STATE_VALUE_POST);
- return JSON_ARRAY_CLOSE;
- } else
- return -EINVAL;
- }
-
- }
-}
-
-static bool json_is_value(JsonVariant *var) {
- assert(var);
-
- return var->type != JSON_VARIANT_CONTROL;
-}
-
-static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
- bool arr = scope->type == JSON_VARIANT_ARRAY;
- int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
- size_t allocated = 0, size = 0;
- JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
- enum {
- STATE_KEY,
- STATE_COLON,
- STATE_COMMA,
- STATE_VALUE
- } state = arr ? STATE_VALUE : STATE_KEY;
-
- assert(tokens);
- assert(i);
- assert(scope);
-
- while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
- bool stopper;
- int r;
-
- stopper = !json_is_value(var) && var->value.integer == terminator;
-
- if (stopper) {
- if (state != STATE_COMMA && size > 0)
- goto error;
-
- goto out;
- }
-
- if (state == STATE_KEY) {
- if (var->type != JSON_VARIANT_STRING)
- goto error;
- else {
- key = var;
- state = STATE_COLON;
- }
- }
- else if (state == STATE_COLON) {
- if (key == NULL)
- goto error;
-
- if (json_is_value(var))
- goto error;
-
- if (var->value.integer != JSON_COLON)
- goto error;
-
- state = STATE_VALUE;
- }
- else if (state == STATE_VALUE) {
- _cleanup_json_variant_unref_ JsonVariant *v = NULL;
- size_t toadd = arr ? 1 : 2;
-
- if (!json_is_value(var)) {
- int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
-
- r = json_variant_new(&v, type);
- if (r < 0)
- goto error;
-
- r = json_scoped_parse(tokens, i, n, v);
- if (r < 0)
- goto error;
-
- value = v;
- }
- else
- value = var;
-
- if(!GREEDY_REALLOC(items, allocated, size + toadd))
- goto error;
-
- if (arr) {
- r = json_variant_deep_copy(&items[size], value);
- if (r < 0)
- goto error;
- } else {
- r = json_variant_deep_copy(&items[size], key);
- if (r < 0)
- goto error;
-
- r = json_variant_deep_copy(&items[size+1], value);
- if (r < 0)
- goto error;
- }
-
- size += toadd;
- state = STATE_COMMA;
- }
- else if (state == STATE_COMMA) {
- if (json_is_value(var))
- goto error;
-
- if (var->value.integer != JSON_COMMA)
- goto error;
-
- key = NULL;
- value = NULL;
-
- state = arr ? STATE_VALUE : STATE_KEY;
- }
- }
-
-error:
- json_raw_unref(items, size);
- return -EBADMSG;
-
-out:
- scope->size = size;
- scope->objects = items;
-
- return scope->type;
-}
-
-static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
- size_t it = 0;
- int r;
- JsonVariant *e;
- _cleanup_json_variant_unref_ JsonVariant *p = NULL;
-
- assert(tokens);
- assert(ntokens);
-
- e = tokens[it++];
- r = json_variant_new(&p, JSON_VARIANT_OBJECT);
- if (r < 0)
- return r;
-
- if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
- return -EBADMSG;
-
- r = json_scoped_parse(tokens, &it, ntokens, p);
- if (r < 0)
- return r;
-
- *rv = p;
- p = NULL;
-
- return 0;
-}
-
-static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
- _cleanup_free_ char *buf = NULL;
- _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
- union json_value v = {};
- void *json_state = NULL;
- const char *p;
- int t, r;
- size_t allocated = 0, s = 0;
-
- assert(string);
- assert(n);
-
- if (size <= 0)
- return -EBADMSG;
-
- buf = strndup(string, size);
- if (!buf)
- return -ENOMEM;
-
- p = buf;
- for (;;) {
- _cleanup_json_variant_unref_ JsonVariant *var = NULL;
- _cleanup_free_ char *rstr = NULL;
-
- t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
-
- if (t < 0)
- return t;
- else if (t == JSON_END)
- break;
-
- if (t <= JSON_ARRAY_CLOSE) {
- r = json_variant_new(&var, JSON_VARIANT_CONTROL);
- if (r < 0)
- return r;
- var->value.integer = t;
- } else {
- switch (t) {
- case JSON_STRING:
- r = json_variant_new(&var, JSON_VARIANT_STRING);
- if (r < 0)
- return r;
- var->size = strlen(rstr);
- var->string = strdup(rstr);
- if (!var->string) {
- return -ENOMEM;
- }
- break;
- case JSON_INTEGER:
- r = json_variant_new(&var, JSON_VARIANT_INTEGER);
- if (r < 0)
- return r;
- var->value = v;
- break;
- case JSON_REAL:
- r = json_variant_new(&var, JSON_VARIANT_REAL);
- if (r < 0)
- return r;
- var->value = v;
- break;
- case JSON_BOOLEAN:
- r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
- if (r < 0)
- return r;
- var->value = v;
- break;
- case JSON_NULL:
- r = json_variant_new(&var, JSON_VARIANT_NULL);
- if (r < 0)
- return r;
- break;
- }
- }
-
- if (!GREEDY_REALLOC(items, allocated, s+2))
- return -ENOMEM;
-
- items[s++] = var;
- items[s] = NULL;
- var = NULL;
- }
-
- *n = s;
- *tokens = items;
- items = NULL;
-
- return 0;
-}
-
-int json_parse(const char *string, JsonVariant **rv) {
- _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
- JsonVariant *v = NULL;
- size_t n = 0;
- int r;
-
- assert(string);
- assert(rv);
-
- r = json_tokens(string, strlen(string), &s, &n);
- if (r < 0)
- return r;
-
- r = json_parse_tokens(s, n, &v);
- if (r < 0)
- return r;
-
- *rv = v;
- return 0;
-}
diff --git a/src/shared/json.h b/src/shared/json.h
deleted file mode 100644
index e0b4d81..0000000
--- a/src/shared/json.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <stdbool.h>
-#include "util.h"
-
-enum {
- JSON_END,
- JSON_COLON,
- JSON_COMMA,
- JSON_OBJECT_OPEN,
- JSON_OBJECT_CLOSE,
- JSON_ARRAY_OPEN,
- JSON_ARRAY_CLOSE,
- JSON_STRING,
- JSON_REAL,
- JSON_INTEGER,
- JSON_BOOLEAN,
- JSON_NULL,
-};
-
-typedef enum {
- JSON_VARIANT_CONTROL,
- JSON_VARIANT_STRING,
- JSON_VARIANT_INTEGER,
- JSON_VARIANT_BOOLEAN,
- JSON_VARIANT_REAL,
- JSON_VARIANT_ARRAY,
- JSON_VARIANT_OBJECT,
- JSON_VARIANT_NULL
-} JsonVariantType;
-
-union json_value {
- bool boolean;
- double real;
- intmax_t integer;
-};
-
-typedef struct JsonVariant {
- JsonVariantType type;
- size_t size;
- union {
- char *string;
- struct JsonVariant *objects;
- union json_value value;
- };
-} JsonVariant;
-
-int json_variant_new(JsonVariant **ret, JsonVariantType type);
-JsonVariant *json_variant_unref(JsonVariant *v);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
-#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
-
-char *json_variant_string(JsonVariant *v);
-bool json_variant_bool(JsonVariant *v);
-intmax_t json_variant_integer(JsonVariant *v);
-double json_variant_real(JsonVariant *v);
-
-JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
-JsonVariant *json_variant_value(JsonVariant *v, const char *key);
-
-#define JSON_VALUE_NULL ((union json_value) {})
-
-int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
-
-int json_parse(const char *string, JsonVariant **rv);
-int json_parse_measure(const char *string, size_t *size);
diff --git a/src/shared/label.c b/src/shared/label.c
deleted file mode 100644
index 82f10b2..0000000
--- a/src/shared/label.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "selinux-util.h"
-#include "smack-util.h"
-#include "util.h"
-#include "label.h"
-
-int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
- int r, q;
-
- r = mac_selinux_fix(path, ignore_enoent, ignore_erofs);
- q = mac_smack_fix(path, ignore_enoent, ignore_erofs);
-
- if (r < 0)
- return r;
- if (q < 0)
- return q;
-
- return 0;
-}
-
-int mkdir_label(const char *path, mode_t mode) {
- int r;
-
- assert(path);
-
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
- if (r < 0)
- return r;
-
- if (mkdir(path, mode) < 0)
- r = -errno;
-
- mac_selinux_create_file_clear();
-
- if (r < 0)
- return r;
-
- return mac_smack_fix(path, false, false);
-}
-
-int symlink_label(const char *old_path, const char *new_path) {
- int r;
-
- assert(old_path);
- assert(new_path);
-
- r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
- if (r < 0)
- return r;
-
- if (symlink(old_path, new_path) < 0)
- r = -errno;
-
- mac_selinux_create_file_clear();
-
- if (r < 0)
- return r;
-
- return mac_smack_fix(new_path, false, false);
-}
diff --git a/src/shared/label.h b/src/shared/label.h
deleted file mode 100644
index 8070bcb..0000000
--- a/src/shared/label.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <sys/types.h>
-
-int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
-
-int mkdir_label(const char *path, mode_t mode);
-int symlink_label(const char *old_path, const char *new_path);
diff --git a/src/shared/linux/Makefile b/src/shared/linux/Makefile
deleted file mode 120000
index d0b0e8e..0000000
--- a/src/shared/linux/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/shared/list.h b/src/shared/list.h
deleted file mode 100644
index 2939216..0000000
--- a/src/shared/list.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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/>.
-***/
-
-/* The head of the linked list. Use this in the structure that shall
- * contain the head of the linked list */
-#define LIST_HEAD(t,name) \
- t *name
-
-/* The pointers in the linked list's items. Use this in the item structure */
-#define LIST_FIELDS(t,name) \
- t *name##_next, *name##_prev
-
-/* Initialize the list's head */
-#define LIST_HEAD_INIT(head) \
- do { \
- (head) = NULL; } \
- while(false)
-
-/* Initialize a list item */
-#define LIST_INIT(name,item) \
- do { \
- typeof(*(item)) *_item = (item); \
- assert(_item); \
- _item->name##_prev = _item->name##_next = NULL; \
- } while(false)
-
-/* Prepend an item to the list */
-#define LIST_PREPEND(name,head,item) \
- do { \
- typeof(*(head)) **_head = &(head), *_item = (item); \
- assert(_item); \
- if ((_item->name##_next = *_head)) \
- _item->name##_next->name##_prev = _item; \
- _item->name##_prev = NULL; \
- *_head = _item; \
- } while(false)
-
-/* Append an item to the list */
-#define LIST_APPEND(name,head,item) \
- do { \
- typeof(*(head)) *_tail; \
- LIST_FIND_TAIL(name,head,_tail); \
- LIST_INSERT_AFTER(name,head,_tail,item); \
- } while(false)
-
-/* Remove an item from the list */
-#define LIST_REMOVE(name,head,item) \
- do { \
- typeof(*(head)) **_head = &(head), *_item = (item); \
- assert(_item); \
- if (_item->name##_next) \
- _item->name##_next->name##_prev = _item->name##_prev; \
- if (_item->name##_prev) \
- _item->name##_prev->name##_next = _item->name##_next; \
- else { \
- assert(*_head == _item); \
- *_head = _item->name##_next; \
- } \
- _item->name##_next = _item->name##_prev = NULL; \
- } while(false)
-
-/* Find the head of the list */
-#define LIST_FIND_HEAD(name,item,head) \
- do { \
- typeof(*(item)) *_item = (item); \
- if (!_item) \
- (head) = NULL; \
- else { \
- while (_item->name##_prev) \
- _item = _item->name##_prev; \
- (head) = _item; \
- } \
- } while (false)
-
-/* Find the tail of the list */
-#define LIST_FIND_TAIL(name,item,tail) \
- do { \
- typeof(*(item)) *_item = (item); \
- if (!_item) \
- (tail) = NULL; \
- else { \
- while (_item->name##_next) \
- _item = _item->name##_next; \
- (tail) = _item; \
- } \
- } while (false)
-
-/* Insert an item after another one (a = where, b = what) */
-#define LIST_INSERT_AFTER(name,head,a,b) \
- do { \
- typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
- assert(_b); \
- if (!_a) { \
- if ((_b->name##_next = *_head)) \
- _b->name##_next->name##_prev = _b; \
- _b->name##_prev = NULL; \
- *_head = _b; \
- } else { \
- if ((_b->name##_next = _a->name##_next)) \
- _b->name##_next->name##_prev = _b; \
- _b->name##_prev = _a; \
- _a->name##_next = _b; \
- } \
- } while(false)
-
-#define LIST_JUST_US(name,item) \
- (!(item)->name##_prev && !(item)->name##_next) \
-
-#define LIST_FOREACH(name,i,head) \
- for ((i) = (head); (i); (i) = (i)->name##_next)
-
-#define LIST_FOREACH_SAFE(name,i,n,head) \
- for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
-
-#define LIST_FOREACH_BEFORE(name,i,p) \
- for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
-
-#define LIST_FOREACH_AFTER(name,i,p) \
- for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
-
-/* Iterate through all the members of the list p is included in, but skip over p */
-#define LIST_FOREACH_OTHERS(name,i,p) \
- for (({ \
- (i) = (p); \
- while ((i) && (i)->name##_prev) \
- (i) = (i)->name##_prev; \
- if ((i) == (p)) \
- (i) = (p)->name##_next; \
- }); \
- (i); \
- (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
-
-/* Loop starting from p->next until p->prev.
- p can be adjusted meanwhile. */
-#define LIST_LOOP_BUT_ONE(name,i,head,p) \
- for ((i) = (p)->name##_next ? (p)->name##_next : (head); \
- (i) != (p); \
- (i) = (i)->name##_next ? (i)->name##_next : (head))
diff --git a/src/shared/locale-util.c b/src/shared/locale-util.c
deleted file mode 100644
index 61db9a8..0000000
--- a/src/shared/locale-util.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/mman.h>
-
-#include "set.h"
-#include "util.h"
-#include "utf8.h"
-#include "strv.h"
-
-#include "locale-util.h"
-
-static int add_locales_from_archive(Set *locales) {
- /* Stolen from glibc... */
-
- struct locarhead {
- uint32_t magic;
- /* Serial number. */
- uint32_t serial;
- /* Name hash table. */
- uint32_t namehash_offset;
- uint32_t namehash_used;
- uint32_t namehash_size;
- /* String table. */
- uint32_t string_offset;
- uint32_t string_used;
- uint32_t string_size;
- /* Table with locale records. */
- uint32_t locrectab_offset;
- uint32_t locrectab_used;
- uint32_t locrectab_size;
- /* MD5 sum hash table. */
- uint32_t sumhash_offset;
- uint32_t sumhash_used;
- uint32_t sumhash_size;
- };
-
- struct namehashent {
- /* Hash value of the name. */
- uint32_t hashval;
- /* Offset of the name in the string table. */
- uint32_t name_offset;
- /* Offset of the locale record. */
- uint32_t locrec_offset;
- };
-
- const struct locarhead *h;
- const struct namehashent *e;
- const void *p = MAP_FAILED;
- _cleanup_close_ int fd = -1;
- size_t sz = 0;
- struct stat st;
- unsigned i;
- int r;
-
- fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return errno == ENOENT ? 0 : -errno;
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISREG(st.st_mode))
- return -EBADMSG;
-
- if (st.st_size < (off_t) sizeof(struct locarhead))
- return -EBADMSG;
-
- p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
- if (p == MAP_FAILED)
- return -errno;
-
- h = (const struct locarhead *) p;
- if (h->magic != 0xde020109 ||
- h->namehash_offset + h->namehash_size > st.st_size ||
- h->string_offset + h->string_size > st.st_size ||
- h->locrectab_offset + h->locrectab_size > st.st_size ||
- h->sumhash_offset + h->sumhash_size > st.st_size) {
- r = -EBADMSG;
- goto finish;
- }
-
- e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
- for (i = 0; i < h->namehash_size; i++) {
- char *z;
-
- if (e[i].locrec_offset == 0)
- continue;
-
- if (!utf8_is_valid((char*) p + e[i].name_offset))
- continue;
-
- z = strdup((char*) p + e[i].name_offset);
- if (!z) {
- r = -ENOMEM;
- goto finish;
- }
-
- r = set_consume(locales, z);
- if (r < 0)
- goto finish;
- }
-
- r = 0;
-
- finish:
- if (p != MAP_FAILED)
- munmap((void*) p, sz);
-
- return r;
-}
-
-static int add_locales_from_libdir (Set *locales) {
- _cleanup_closedir_ DIR *dir = NULL;
- struct dirent *entry;
- int r;
-
- dir = opendir("/usr/lib/locale");
- if (!dir)
- return errno == ENOENT ? 0 : -errno;
-
- FOREACH_DIRENT(entry, dir, return -errno) {
- char *z;
-
- if (entry->d_type != DT_DIR)
- continue;
-
- z = strdup(entry->d_name);
- if (!z)
- return -ENOMEM;
-
- r = set_consume(locales, z);
- if (r < 0 && r != -EEXIST)
- return r;
- }
-
- return 0;
-}
-
-int get_locales(char ***ret) {
- _cleanup_set_free_ Set *locales = NULL;
- _cleanup_strv_free_ char **l = NULL;
- int r;
-
- locales = set_new(&string_hash_ops);
- if (!locales)
- return -ENOMEM;
-
- r = add_locales_from_archive(locales);
- if (r < 0 && r != -ENOENT)
- return r;
-
- r = add_locales_from_libdir(locales);
- if (r < 0)
- return r;
-
- l = set_get_strv(locales);
- if (!l)
- return -ENOMEM;
-
- strv_sort(l);
-
- *ret = l;
- l = NULL;
-
- return 0;
-}
-
-bool locale_is_valid(const char *name) {
-
- if (isempty(name))
- return false;
-
- if (strlen(name) >= 128)
- return false;
-
- if (!utf8_is_valid(name))
- return false;
-
- if (!filename_is_valid(name))
- return false;
-
- if (!string_is_safe(name))
- return false;
-
- return true;
-}
-
-static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
- [VARIABLE_LANG] = "LANG",
- [VARIABLE_LANGUAGE] = "LANGUAGE",
- [VARIABLE_LC_CTYPE] = "LC_CTYPE",
- [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
- [VARIABLE_LC_TIME] = "LC_TIME",
- [VARIABLE_LC_COLLATE] = "LC_COLLATE",
- [VARIABLE_LC_MONETARY] = "LC_MONETARY",
- [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
- [VARIABLE_LC_PAPER] = "LC_PAPER",
- [VARIABLE_LC_NAME] = "LC_NAME",
- [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
- [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
- [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
- [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);
diff --git a/src/shared/locale-util.h b/src/shared/locale-util.h
deleted file mode 100644
index e48aa3d..0000000
--- a/src/shared/locale-util.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <stdbool.h>
-
-#include "macro.h"
-
-typedef enum LocaleVariable {
- /* We don't list LC_ALL here on purpose. People should be
- * using LANG instead. */
-
- VARIABLE_LANG,
- VARIABLE_LANGUAGE,
- VARIABLE_LC_CTYPE,
- VARIABLE_LC_NUMERIC,
- VARIABLE_LC_TIME,
- VARIABLE_LC_COLLATE,
- VARIABLE_LC_MONETARY,
- VARIABLE_LC_MESSAGES,
- VARIABLE_LC_PAPER,
- VARIABLE_LC_NAME,
- VARIABLE_LC_ADDRESS,
- VARIABLE_LC_TELEPHONE,
- VARIABLE_LC_MEASUREMENT,
- VARIABLE_LC_IDENTIFICATION,
- _VARIABLE_LC_MAX,
- _VARIABLE_LC_INVALID = -1
-} LocaleVariable;
-
-int get_locales(char ***l);
-bool locale_is_valid(const char *name);
-
-const char* locale_variable_to_string(LocaleVariable i) _const_;
-LocaleVariable locale_variable_from_string(const char *s) _pure_;
diff --git a/src/shared/lockfile-util.c b/src/shared/lockfile-util.c
deleted file mode 100644
index 05e16d1..0000000
--- a/src/shared/lockfile-util.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <sys/file.h>
-
-#include "util.h"
-#include "lockfile-util.h"
-#include "fileio.h"
-
-int make_lock_file(const char *p, int operation, LockFile *ret) {
- _cleanup_close_ int fd = -1;
- _cleanup_free_ char *t = NULL;
- int r;
-
- /*
- * We use UNPOSIX locks if they are available. They have nice
- * semantics, and are mostly compatible with NFS. However,
- * they are only available on new kernels. When we detect we
- * are running on an older kernel, then we fall back to good
- * old BSD locks. They also have nice semantics, but are
- * slightly problematic on NFS, where they are upgraded to
- * POSIX locks, even though locally they are orthogonal to
- * POSIX locks.
- */
-
- t = strdup(p);
- if (!t)
- return -ENOMEM;
-
- for (;;) {
- struct flock fl = {
- .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
- .l_whence = SEEK_SET,
- };
- struct stat st;
-
- fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
- if (fd < 0)
- return -errno;
-
- r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
- if (r < 0) {
-
- /* If the kernel is too old, use good old BSD locks */
- if (errno == EINVAL)
- r = flock(fd, operation);
-
- if (r < 0)
- return errno == EAGAIN ? -EBUSY : -errno;
- }
-
- /* If we acquired the lock, let's check if the file
- * still exists in the file system. If not, then the
- * previous exclusive owner removed it and then closed
- * it. In such a case our acquired lock is worthless,
- * hence try again. */
-
- r = fstat(fd, &st);
- if (r < 0)
- return -errno;
- if (st.st_nlink > 0)
- break;
-
- fd = safe_close(fd);
- }
-
- ret->path = t;
- ret->fd = fd;
- ret->operation = operation;
-
- fd = -1;
- t = NULL;
-
- return r;
-}
-
-int make_lock_file_for(const char *p, int operation, LockFile *ret) {
- const char *fn;
- char *t;
-
- assert(p);
- assert(ret);
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- t = newa(char, strlen(p) + 2 + 4 + 1);
- stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
-
- return make_lock_file(t, operation, ret);
-}
-
-void release_lock_file(LockFile *f) {
- int r;
-
- if (!f)
- return;
-
- if (f->path) {
-
- /* If we are the exclusive owner we can safely delete
- * the lock file itself. If we are not the exclusive
- * owner, we can try becoming it. */
-
- if (f->fd >= 0 &&
- (f->operation & ~LOCK_NB) == LOCK_SH) {
- static const struct flock fl = {
- .l_type = F_WRLCK,
- .l_whence = SEEK_SET,
- };
-
- r = fcntl(f->fd, F_OFD_SETLK, &fl);
- if (r < 0 && errno == EINVAL)
- r = flock(f->fd, LOCK_EX|LOCK_NB);
-
- if (r >= 0)
- f->operation = LOCK_EX|LOCK_NB;
- }
-
- if ((f->operation & ~LOCK_NB) == LOCK_EX)
- unlink_noerrno(f->path);
-
- free(f->path);
- f->path = NULL;
- }
-
- f->fd = safe_close(f->fd);
- f->operation = 0;
-}
diff --git a/src/shared/lockfile-util.h b/src/shared/lockfile-util.h
deleted file mode 100644
index 38d4709..0000000
--- a/src/shared/lockfile-util.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2011 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 "macro.h"
-#include "missing.h"
-
-typedef struct LockFile {
- char *path;
- int fd;
- int operation;
-} LockFile;
-
-int make_lock_file(const char *p, int operation, LockFile *ret);
-int make_lock_file_for(const char *p, int operation, LockFile *ret);
-void release_lock_file(LockFile *f);
-
-#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
-
-#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
diff --git a/src/shared/log.c b/src/shared/log.c
deleted file mode 100644
index b96afc4..0000000
--- a/src/shared/log.c
+++ /dev/null
@@ -1,1138 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stddef.h>
-#include <printf.h>
-
-#include "sd-messages.h"
-#include "log.h"
-#include "util.h"
-#include "missing.h"
-#include "macro.h"
-#include "socket-util.h"
-#include "formats-util.h"
-#include "process-util.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-
-#define SNDBUF_SIZE (8*1024*1024)
-
-static LogTarget log_target = LOG_TARGET_CONSOLE;
-static int log_max_level = LOG_INFO;
-static int log_facility = LOG_DAEMON;
-
-static int console_fd = STDERR_FILENO;
-static int syslog_fd = -1;
-static int kmsg_fd = -1;
-static int journal_fd = -1;
-
-static bool syslog_is_stream = false;
-
-static bool show_color = false;
-static bool show_location = false;
-
-static bool upgrade_syslog_to_journal = false;
-
-/* Akin to glibc's __abort_msg; which is private and we hence cannot
- * use here. */
-static char *log_abort_msg = NULL;
-
-void log_close_console(void) {
-
- if (console_fd < 0)
- return;
-
- if (getpid() == 1) {
- if (console_fd >= 3)
- safe_close(console_fd);
-
- console_fd = -1;
- }
-}
-
-static int log_open_console(void) {
-
- if (console_fd >= 0)
- return 0;
-
- if (getpid() == 1) {
- console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (console_fd < 0)
- return console_fd;
- } else
- console_fd = STDERR_FILENO;
-
- return 0;
-}
-
-void log_close_kmsg(void) {
- kmsg_fd = safe_close(kmsg_fd);
-}
-
-static int log_open_kmsg(void) {
-
- if (kmsg_fd >= 0)
- return 0;
-
- kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (kmsg_fd < 0)
- return -errno;
-
- return 0;
-}
-
-void log_close_syslog(void) {
- syslog_fd = safe_close(syslog_fd);
-}
-
-static int create_log_socket(int type) {
- struct timeval tv;
- int fd;
-
- fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
- if (fd < 0)
- return -errno;
-
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
-
- /* We need a blocking fd here since we'd otherwise lose
- messages way too early. However, let's not hang forever in the
- unlikely case of a deadlock. */
- if (getpid() == 1)
- timeval_store(&tv, 10 * USEC_PER_MSEC);
- else
- timeval_store(&tv, 10 * USEC_PER_SEC);
- (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
-
- return fd;
-}
-
-static int log_open_syslog(void) {
-
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/dev/log",
- };
-
- int r;
-
- if (syslog_fd >= 0)
- return 0;
-
- syslog_fd = create_log_socket(SOCK_DGRAM);
- if (syslog_fd < 0) {
- r = syslog_fd;
- goto fail;
- }
-
- if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- safe_close(syslog_fd);
-
- /* Some legacy syslog systems still use stream
- * sockets. They really shouldn't. But what can we
- * do... */
- syslog_fd = create_log_socket(SOCK_STREAM);
- if (syslog_fd < 0) {
- r = syslog_fd;
- goto fail;
- }
-
- if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- r = -errno;
- goto fail;
- }
-
- syslog_is_stream = true;
- } else
- syslog_is_stream = false;
-
- return 0;
-
-fail:
- log_close_syslog();
- return r;
-}
-
-void log_close_journal(void) {
- journal_fd = safe_close(journal_fd);
-}
-
-static int log_open_journal(void) {
-
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/socket",
- };
-
- int r;
-
- if (journal_fd >= 0)
- return 0;
-
- journal_fd = create_log_socket(SOCK_DGRAM);
- if (journal_fd < 0) {
- r = journal_fd;
- goto fail;
- }
-
- if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- r = -errno;
- goto fail;
- }
-
- return 0;
-
-fail:
- log_close_journal();
- return r;
-}
-
-int log_open(void) {
- int r;
-
- /* If we don't use the console we close it here, to not get
- * killed by SAK. If we don't use syslog we close it here so
- * that we are not confused by somebody deleting the socket in
- * the fs. If we don't use /dev/kmsg we still keep it open,
- * because there is no reason to close it. */
-
- if (log_target == LOG_TARGET_NULL) {
- log_close_journal();
- log_close_syslog();
- log_close_console();
- return 0;
- }
-
- if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) ||
- getpid() == 1 ||
- isatty(STDERR_FILENO) <= 0) {
-
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) {
- r = log_open_journal();
- if (r >= 0) {
- log_close_syslog();
- log_close_console();
- return r;
- }
- }
-
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
- r = log_open_syslog();
- if (r >= 0) {
- log_close_journal();
- log_close_console();
- return r;
- }
- }
-
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_SAFE ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG) {
- r = log_open_kmsg();
- if (r >= 0) {
- log_close_journal();
- log_close_syslog();
- log_close_console();
- return r;
- }
- }
- }
-
- log_close_journal();
- log_close_syslog();
-
- return log_open_console();
-}
-
-void log_set_target(LogTarget target) {
- assert(target >= 0);
- assert(target < _LOG_TARGET_MAX);
-
- if (upgrade_syslog_to_journal) {
- if (target == LOG_TARGET_SYSLOG)
- target = LOG_TARGET_JOURNAL;
- else if (target == LOG_TARGET_SYSLOG_OR_KMSG)
- target = LOG_TARGET_JOURNAL_OR_KMSG;
- }
-
- log_target = target;
-}
-
-void log_close(void) {
- log_close_journal();
- log_close_syslog();
- log_close_kmsg();
- log_close_console();
-}
-
-void log_forget_fds(void) {
- console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
-}
-
-void log_set_max_level(int level) {
- assert((level & LOG_PRIMASK) == level);
-
- log_max_level = level;
-}
-
-void log_set_facility(int facility) {
- log_facility = facility;
-}
-
-static int write_to_console(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *buffer) {
-
- char location[64], prefix[1 + DECIMAL_STR_MAX(int) + 2];
- struct iovec iovec[6] = {};
- unsigned n = 0;
- bool highlight;
-
- if (console_fd < 0)
- return 0;
-
- if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
- sprintf(prefix, "<%i>", level);
- IOVEC_SET_STRING(iovec[n++], prefix);
- }
-
- highlight = LOG_PRI(level) <= LOG_ERR && show_color;
-
- if (show_location) {
- snprintf(location, sizeof(location), "(%s:%i) ", file, line);
- IOVEC_SET_STRING(iovec[n++], location);
- }
-
- if (highlight)
- IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
- IOVEC_SET_STRING(iovec[n++], buffer);
- if (highlight)
- IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
- IOVEC_SET_STRING(iovec[n++], "\n");
-
- if (writev(console_fd, iovec, n) < 0) {
-
- if (errno == EIO && getpid() == 1) {
-
- /* If somebody tried to kick us from our
- * console tty (via vhangup() or suchlike),
- * try to reconnect */
-
- log_close_console();
- log_open_console();
-
- if (console_fd < 0)
- return 0;
-
- if (writev(console_fd, iovec, n) < 0)
- return -errno;
- } else
- return -errno;
- }
-
- return 1;
-}
-
-static int write_to_syslog(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *buffer) {
-
- char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
- header_time[64],
- header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
- struct iovec iovec[5] = {};
- struct msghdr msghdr = {
- .msg_iov = iovec,
- .msg_iovlen = ELEMENTSOF(iovec),
- };
- time_t t;
- struct tm *tm;
-
- if (syslog_fd < 0)
- return 0;
-
- xsprintf(header_priority, "<%i>", level);
-
- t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
- tm = localtime(&t);
- if (!tm)
- return -EINVAL;
-
- if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
- return -EINVAL;
-
- xsprintf(header_pid, "["PID_FMT"]: ", getpid());
-
- IOVEC_SET_STRING(iovec[0], header_priority);
- IOVEC_SET_STRING(iovec[1], header_time);
- IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
- IOVEC_SET_STRING(iovec[3], header_pid);
- IOVEC_SET_STRING(iovec[4], buffer);
-
- /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
- if (syslog_is_stream)
- iovec[4].iov_len++;
-
- for (;;) {
- ssize_t n;
-
- n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
- if (n < 0)
- return -errno;
-
- if (!syslog_is_stream ||
- (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
- break;
-
- IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
- }
-
- return 1;
-}
-
-static int write_to_kmsg(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *buffer) {
-
- char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
- header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
- struct iovec iovec[5] = {};
-
- if (kmsg_fd < 0)
- return 0;
-
- xsprintf(header_priority, "<%i>", level);
- xsprintf(header_pid, "["PID_FMT"]: ", getpid());
-
- IOVEC_SET_STRING(iovec[0], header_priority);
- IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
- IOVEC_SET_STRING(iovec[2], header_pid);
- IOVEC_SET_STRING(iovec[3], buffer);
- IOVEC_SET_STRING(iovec[4], "\n");
-
- if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
- return -errno;
-
- return 1;
-}
-
-static int log_do_header(
- char *header,
- size_t size,
- int level,
- int error,
- const char *file, int line, const char *func,
- const char *object_field, const char *object) {
-
- snprintf(header, size,
- "PRIORITY=%i\n"
- "SYSLOG_FACILITY=%i\n"
- "%s%s%s"
- "%s%.*i%s"
- "%s%s%s"
- "%s%.*i%s"
- "%s%s%s"
- "SYSLOG_IDENTIFIER=%s\n",
- LOG_PRI(level),
- LOG_FAC(level),
- isempty(file) ? "" : "CODE_FILE=",
- isempty(file) ? "" : file,
- isempty(file) ? "" : "\n",
- line ? "CODE_LINE=" : "",
- line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
- line ? "\n" : "",
- isempty(func) ? "" : "CODE_FUNCTION=",
- isempty(func) ? "" : func,
- isempty(func) ? "" : "\n",
- error ? "ERRNO=" : "",
- error ? 1 : 0, error,
- error ? "\n" : "",
- isempty(object) ? "" : object_field,
- isempty(object) ? "" : object,
- isempty(object) ? "" : "\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int write_to_journal(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *buffer) {
-
- char header[LINE_MAX];
- struct iovec iovec[4] = {};
- struct msghdr mh = {};
-
- if (journal_fd < 0)
- return 0;
-
- log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object);
-
- IOVEC_SET_STRING(iovec[0], header);
- IOVEC_SET_STRING(iovec[1], "MESSAGE=");
- IOVEC_SET_STRING(iovec[2], buffer);
- IOVEC_SET_STRING(iovec[3], "\n");
-
- mh.msg_iov = iovec;
- mh.msg_iovlen = ELEMENTSOF(iovec);
-
- if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
- return -errno;
-
- return 1;
-}
-
-static int log_dispatch(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- char *buffer) {
-
- assert(buffer);
-
- if (log_target == LOG_TARGET_NULL)
- return -error;
-
- /* Patch in LOG_DAEMON facility if necessary */
- if ((level & LOG_FACMASK) == 0)
- level = log_facility | LOG_PRI(level);
-
- if (error < 0)
- error = -error;
-
- do {
- char *e;
- int k = 0;
-
- buffer += strspn(buffer, NEWLINE);
-
- if (buffer[0] == 0)
- break;
-
- if ((e = strpbrk(buffer, NEWLINE)))
- *(e++) = 0;
-
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) {
-
- k = write_to_journal(level, error, file, line, func, object_field, object, buffer);
- if (k < 0) {
- if (k != -EAGAIN)
- log_close_journal();
- log_open_kmsg();
- }
- }
-
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
-
- k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
- if (k < 0) {
- if (k != -EAGAIN)
- log_close_syslog();
- log_open_kmsg();
- }
- }
-
- if (k <= 0 &&
- (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_SAFE ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_KMSG)) {
-
- k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
- if (k < 0) {
- log_close_kmsg();
- log_open_console();
- }
- }
-
- if (k <= 0)
- (void) write_to_console(level, error, file, line, func, object_field, object, buffer);
-
- buffer = e;
- } while (buffer);
-
- return -error;
-}
-
-int log_dump_internal(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- char *buffer) {
-
- PROTECT_ERRNO;
-
- /* This modifies the buffer... */
-
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return -error;
-
- return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
-}
-
-int log_internalv(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *format,
- va_list ap) {
-
- PROTECT_ERRNO;
- char buffer[LINE_MAX];
-
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return -error;
-
- /* Make sure that %m maps to the specified error */
- if (error != 0)
- errno = error;
-
- vsnprintf(buffer, sizeof(buffer), format, ap);
-
- return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
-}
-
-int log_internal(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *format, ...) {
-
- va_list ap;
- int r;
-
- va_start(ap, format);
- r = log_internalv(level, error, file, line, func, format, ap);
- va_end(ap);
-
- return r;
-}
-
-int log_object_internalv(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *format,
- va_list ap) {
-
- PROTECT_ERRNO;
- char *buffer, *b;
- size_t l;
-
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return -error;
-
- /* Make sure that %m maps to the specified error */
- if (error != 0)
- errno = error;
-
- /* Prepend the object name before the message */
- if (object) {
- size_t n;
-
- n = strlen(object);
- l = n + 2 + LINE_MAX;
-
- buffer = newa(char, l);
- b = stpcpy(stpcpy(buffer, object), ": ");
- } else {
- l = LINE_MAX;
- b = buffer = newa(char, l);
- }
-
- vsnprintf(b, l, format, ap);
-
- return log_dispatch(level, error, file, line, func, object_field, object, buffer);
-}
-
-int log_object_internal(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *format, ...) {
-
- va_list ap;
- int r;
-
- va_start(ap, format);
- r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap);
- va_end(ap);
-
- return r;
-}
-
-static void log_assert(
- int level,
- const char *text,
- const char *file,
- int line,
- const char *func,
- const char *format) {
-
- static char buffer[LINE_MAX];
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return;
-
- DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buffer, sizeof(buffer), format, text, file, line, func);
- REENABLE_WARNING;
-
- log_abort_msg = buffer;
-
- log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
-}
-
-noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
- log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
- abort();
-}
-
-noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) {
- log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
- abort();
-}
-
-void log_assert_failed_return(const char *text, const char *file, int line, const char *func) {
- PROTECT_ERRNO;
- log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.");
-}
-
-int log_oom_internal(const char *file, int line, const char *func) {
- log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory.");
- return -ENOMEM;
-}
-
-int log_struct_internal(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *format, ...) {
-
- char buf[LINE_MAX];
- bool found = false;
- PROTECT_ERRNO;
- va_list ap;
-
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return -error;
-
- if (log_target == LOG_TARGET_NULL)
- return -error;
-
- if ((level & LOG_FACMASK) == 0)
- level = log_facility | LOG_PRI(level);
-
- if ((log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) &&
- journal_fd >= 0) {
- char header[LINE_MAX];
- struct iovec iovec[17] = {};
- unsigned n = 0, i;
- struct msghdr mh = {
- .msg_iov = iovec,
- };
- static const char nl = '\n';
- bool fallback = false;
-
- /* If the journal is available do structured logging */
- log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL);
- IOVEC_SET_STRING(iovec[n++], header);
-
- va_start(ap, format);
- while (format && n + 1 < ELEMENTSOF(iovec)) {
- va_list aq;
- char *m;
-
- /* We need to copy the va_list structure,
- * since vasprintf() leaves it afterwards at
- * an undefined location */
-
- if (error != 0)
- errno = error;
-
- va_copy(aq, ap);
- if (vasprintf(&m, format, aq) < 0) {
- va_end(aq);
- fallback = true;
- goto finish;
- }
- va_end(aq);
-
- /* Now, jump enough ahead, so that we point to
- * the next format string */
- VA_FORMAT_ADVANCE(format, ap);
-
- IOVEC_SET_STRING(iovec[n++], m);
-
- iovec[n].iov_base = (char*) &nl;
- iovec[n].iov_len = 1;
- n++;
-
- format = va_arg(ap, char *);
- }
-
- mh.msg_iovlen = n;
-
- (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
-
- finish:
- va_end(ap);
- for (i = 1; i < n; i += 2)
- free(iovec[i].iov_base);
-
- if (!fallback)
- return -error;
- }
-
- /* Fallback if journal logging is not available or didn't work. */
-
- va_start(ap, format);
- while (format) {
- va_list aq;
-
- if (error != 0)
- errno = error;
-
- va_copy(aq, ap);
- vsnprintf(buf, sizeof(buf), format, aq);
- va_end(aq);
-
- if (startswith(buf, "MESSAGE=")) {
- found = true;
- break;
- }
-
- VA_FORMAT_ADVANCE(format, ap);
-
- format = va_arg(ap, char *);
- }
- va_end(ap);
-
- if (!found)
- return -error;
-
- return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8);
-}
-
-int log_set_target_from_string(const char *e) {
- LogTarget t;
-
- t = log_target_from_string(e);
- if (t < 0)
- return -EINVAL;
-
- log_set_target(t);
- return 0;
-}
-
-int log_set_max_level_from_string(const char *e) {
- int t;
-
- t = log_level_from_string(e);
- if (t < 0)
- return t;
-
- log_set_max_level(t);
- return 0;
-}
-
-static int parse_proc_cmdline_item(const char *key, const char *value) {
-
- /*
- * The systemd.log_xyz= settings are parsed by all tools, and
- * so is "debug".
- *
- * However, "quiet" is only parsed by PID 1, and only turns of
- * status output to /dev/console, but does not alter the log
- * level.
- */
-
- if (streq(key, "debug") && !value)
- log_set_max_level(LOG_DEBUG);
-
- else if (streq(key, "systemd.log_target") && value) {
-
- if (log_set_target_from_string(value) < 0)
- log_warning("Failed to parse log target '%s'. Ignoring.", value);
-
- } else if (streq(key, "systemd.log_level") && value) {
-
- if (log_set_max_level_from_string(value) < 0)
- log_warning("Failed to parse log level '%s'. Ignoring.", value);
-
- } else if (streq(key, "systemd.log_color") && value) {
-
- if (log_show_color_from_string(value) < 0)
- log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
-
- } else if (streq(key, "systemd.log_location") && value) {
-
- if (log_show_location_from_string(value) < 0)
- log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
- }
-
- return 0;
-}
-
-void log_parse_environment(void) {
- const char *e;
-
- if (get_ctty_devnr(0, NULL) < 0)
- /* Only try to read the command line in daemons.
- We assume that anything that has a controlling
- tty is user stuff. */
- (void) parse_proc_cmdline(parse_proc_cmdline_item);
-
- e = secure_getenv("SYSTEMD_LOG_TARGET");
- if (e && log_set_target_from_string(e) < 0)
- log_warning("Failed to parse log target '%s'. Ignoring.", e);
-
- e = secure_getenv("SYSTEMD_LOG_LEVEL");
- if (e && log_set_max_level_from_string(e) < 0)
- log_warning("Failed to parse log level '%s'. Ignoring.", e);
-
- e = secure_getenv("SYSTEMD_LOG_COLOR");
- if (e && log_show_color_from_string(e) < 0)
- log_warning("Failed to parse bool '%s'. Ignoring.", e);
-
- e = secure_getenv("SYSTEMD_LOG_LOCATION");
- if (e && log_show_location_from_string(e) < 0)
- log_warning("Failed to parse bool '%s'. Ignoring.", e);
-}
-
-LogTarget log_get_target(void) {
- return log_target;
-}
-
-int log_get_max_level(void) {
- return log_max_level;
-}
-
-void log_show_color(bool b) {
- show_color = b;
-}
-
-bool log_get_show_color(void) {
- return show_color;
-}
-
-void log_show_location(bool b) {
- show_location = b;
-}
-
-bool log_get_show_location(void) {
- return show_location;
-}
-
-int log_show_color_from_string(const char *e) {
- int t;
-
- t = parse_boolean(e);
- if (t < 0)
- return t;
-
- log_show_color(t);
- return 0;
-}
-
-int log_show_location_from_string(const char *e) {
- int t;
-
- t = parse_boolean(e);
- if (t < 0)
- return t;
-
- log_show_location(t);
- return 0;
-}
-
-bool log_on_console(void) {
- if (log_target == LOG_TARGET_CONSOLE ||
- log_target == LOG_TARGET_CONSOLE_PREFIXED)
- return true;
-
- return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
-}
-
-static const char *const log_target_table[_LOG_TARGET_MAX] = {
- [LOG_TARGET_CONSOLE] = "console",
- [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed",
- [LOG_TARGET_KMSG] = "kmsg",
- [LOG_TARGET_JOURNAL] = "journal",
- [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
- [LOG_TARGET_SYSLOG] = "syslog",
- [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
- [LOG_TARGET_AUTO] = "auto",
- [LOG_TARGET_SAFE] = "safe",
- [LOG_TARGET_NULL] = "null"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
-
-void log_received_signal(int level, const struct signalfd_siginfo *si) {
- if (si->ssi_pid > 0) {
- _cleanup_free_ char *p = NULL;
-
- get_process_comm(si->ssi_pid, &p);
-
- log_full(level,
- "Received SIG%s from PID %"PRIu32" (%s).",
- signal_to_string(si->ssi_signo),
- si->ssi_pid, strna(p));
- } else
- log_full(level,
- "Received SIG%s.",
- signal_to_string(si->ssi_signo));
-
-}
-
-void log_set_upgrade_syslog_to_journal(bool b) {
- upgrade_syslog_to_journal = b;
-}
-
-int log_syntax_internal(
- const char *unit,
- int level,
- const char *config_file,
- unsigned config_line,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *format, ...) {
-
- PROTECT_ERRNO;
- char buffer[LINE_MAX];
- int r;
- va_list ap;
-
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return -error;
-
- if (log_target == LOG_TARGET_NULL)
- return -error;
-
- if (error != 0)
- errno = error;
-
- va_start(ap, format);
- vsnprintf(buffer, sizeof(buffer), format, ap);
- va_end(ap);
-
- if (unit)
- r = log_struct_internal(
- level, error,
- file, line, func,
- getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
- NULL);
- else
- r = log_struct_internal(
- level, error,
- file, line, func,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
- NULL);
-
- return r;
-}
diff --git a/src/shared/log.h b/src/shared/log.h
deleted file mode 100644
index 569762d..0000000
--- a/src/shared/log.h
+++ /dev/null
@@ -1,229 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <syslog.h>
-#include <sys/signalfd.h>
-#include <errno.h>
-
-#include "sd-id128.h"
-#include "macro.h"
-
-typedef enum LogTarget{
- LOG_TARGET_CONSOLE,
- LOG_TARGET_CONSOLE_PREFIXED,
- LOG_TARGET_KMSG,
- LOG_TARGET_JOURNAL,
- LOG_TARGET_JOURNAL_OR_KMSG,
- LOG_TARGET_SYSLOG,
- LOG_TARGET_SYSLOG_OR_KMSG,
- LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
- LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
- LOG_TARGET_NULL,
- _LOG_TARGET_MAX,
- _LOG_TARGET_INVALID = -1
-} LogTarget;
-
-void log_set_target(LogTarget target);
-void log_set_max_level(int level);
-void log_set_facility(int facility);
-
-int log_set_target_from_string(const char *e);
-int log_set_max_level_from_string(const char *e);
-
-void log_show_color(bool b);
-bool log_get_show_color(void) _pure_;
-void log_show_location(bool b);
-bool log_get_show_location(void) _pure_;
-
-int log_show_color_from_string(const char *e);
-int log_show_location_from_string(const char *e);
-
-LogTarget log_get_target(void) _pure_;
-int log_get_max_level(void) _pure_;
-
-int log_open(void);
-void log_close(void);
-void log_forget_fds(void);
-
-void log_close_syslog(void);
-void log_close_journal(void);
-void log_close_kmsg(void);
-void log_close_console(void);
-
-void log_parse_environment(void);
-
-int log_internal(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *format, ...) _printf_(6,7);
-
-int log_internalv(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *format,
- va_list ap) _printf_(6,0);
-
-int log_object_internal(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *format, ...) _printf_(8,9);
-
-int log_object_internalv(
- int level,
- int error,
- const char*file,
- int line,
- const char *func,
- const char *object_field,
- const char *object,
- const char *format,
- va_list ap) _printf_(8,0);
-
-int log_struct_internal(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *format, ...) _printf_(6,0) _sentinel_;
-
-int log_oom_internal(
- const char *file,
- int line,
- const char *func);
-
-/* This modifies the buffer passed! */
-int log_dump_internal(
- int level,
- int error,
- const char *file,
- int line,
- const char *func,
- char *buffer);
-
-/* Logging for various assertions */
-noreturn void log_assert_failed(
- const char *text,
- const char *file,
- int line,
- const char *func);
-
-noreturn void log_assert_failed_unreachable(
- const char *text,
- const char *file,
- int line,
- const char *func);
-
-void log_assert_failed_return(
- const char *text,
- const char *file,
- int line,
- const char *func);
-
-/* Logging with level */
-#define log_full_errno(level, error, ...) \
- ({ \
- int _level = (level), _e = (error); \
- (log_get_max_level() >= LOG_PRI(_level)) \
- ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
- : -abs(_e); \
- })
-
-#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
-
-/* Normal logging */
-#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
-#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
-#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
-#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
-#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
-#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
-
-/* Logging triggered by an errno-like error */
-#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
-#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__)
-#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
-#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
-#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
-#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
-
-#ifdef LOG_TRACE
-# define log_trace(...) log_debug(__VA_ARGS__)
-#else
-# define log_trace(...) do {} while(0)
-#endif
-
-/* Structured logging */
-#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__)
-
-/* This modifies the buffer passed! */
-#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer)
-
-#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
-
-bool log_on_console(void) _pure_;
-
-const char *log_target_to_string(LogTarget target) _const_;
-LogTarget log_target_from_string(const char *s) _pure_;
-
-/* Helpers to prepare various fields for structured logging */
-#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
-#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
-
-void log_received_signal(int level, const struct signalfd_siginfo *si);
-
-void log_set_upgrade_syslog_to_journal(bool b);
-
-int log_syntax_internal(
- const char *unit,
- int level,
- const char *config_file,
- unsigned config_line,
- int error,
- const char *file,
- int line,
- const char *func,
- const char *format, ...) _printf_(9, 10);
-
-#define log_syntax(unit, level, config_file, config_line, error, ...) \
- ({ \
- int _level = (level), _e = (error); \
- (log_get_max_level() >= LOG_PRI(_level)) \
- ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
- : -abs(_e); \
- })
diff --git a/src/shared/login-shared.c b/src/shared/login-shared.c
deleted file mode 100644
index 64650a9..0000000
--- a/src/shared/login-shared.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Zbigniew Jędrzejewski-Szmek
-
- 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 "login-shared.h"
-#include "def.h"
-
-bool session_id_valid(const char *id) {
-
- if (isempty(id))
- return false;
-
- return id[strspn(id, LETTERS DIGITS)] == '\0';
-}
diff --git a/src/shared/login-shared.h b/src/shared/login-shared.h
deleted file mode 100644
index a79f20c..0000000
--- a/src/shared/login-shared.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Zbigniew Jędrzejewski-Szmek
-
- 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/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-
-bool session_id_valid(const char *id);
diff --git a/src/shared/macro.h b/src/shared/macro.h
deleted file mode 100644
index cc1c9e7..0000000
--- a/src/shared/macro.h
+++ /dev/null
@@ -1,473 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <assert.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <inttypes.h>
-
-#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
-#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
-#define _sentinel_ __attribute__ ((sentinel))
-#define _unused_ __attribute__ ((unused))
-#define _destructor_ __attribute__ ((destructor))
-#define _pure_ __attribute__ ((pure))
-#define _const_ __attribute__ ((const))
-#define _deprecated_ __attribute__ ((deprecated))
-#define _packed_ __attribute__ ((packed))
-#define _malloc_ __attribute__ ((malloc))
-#define _weak_ __attribute__ ((weak))
-#define _likely_(x) (__builtin_expect(!!(x),1))
-#define _unlikely_(x) (__builtin_expect(!!(x),0))
-#define _public_ __attribute__ ((visibility("default")))
-#define _hidden_ __attribute__ ((visibility("hidden")))
-#define _weakref_(x) __attribute__((weakref(#x)))
-#define _alignas_(x) __attribute__((aligned(__alignof(x))))
-#define _cleanup_(x) __attribute__((cleanup(x)))
-
-/* Temporarily disable some warnings */
-#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")
-
-#define DISABLE_WARNING_FORMAT_NONLITERAL \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
-
-#define DISABLE_WARNING_MISSING_PROTOTYPES \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
-
-#define DISABLE_WARNING_NONNULL \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wnonnull\"")
-
-#define DISABLE_WARNING_SHADOW \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wshadow\"")
-
-#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
-
-#define REENABLE_WARNING \
- _Pragma("GCC diagnostic pop")
-
-/* automake test harness */
-#define EXIT_TEST_SKIP 77
-
-#define XSTRINGIFY(x) #x
-#define STRINGIFY(x) XSTRINGIFY(x)
-
-#define XCONCATENATE(x, y) x ## y
-#define CONCATENATE(x, y) XCONCATENATE(x, y)
-
-#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
-#define UNIQ __COUNTER__
-
-/* Rounds up */
-
-#define ALIGN4(l) (((l) + 3) & ~3)
-#define ALIGN8(l) (((l) + 7) & ~7)
-
-#if __SIZEOF_POINTER__ == 8
-#define ALIGN(l) ALIGN8(l)
-#elif __SIZEOF_POINTER__ == 4
-#define ALIGN(l) ALIGN4(l)
-#else
-#error "Wut? Pointers are neither 4 nor 8 bytes long?"
-#endif
-
-#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p)))
-#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
-#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
-
-static inline size_t ALIGN_TO(size_t l, size_t ali) {
- return ((l + ali - 1) & ~(ali - 1));
-}
-
-#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
-
-/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
-static inline unsigned long ALIGN_POWER2(unsigned long u) {
- /* clz(0) is undefined */
- if (u == 1)
- return 1;
-
- /* left-shift overflow is undefined */
- if (__builtin_clzl(u - 1UL) < 1)
- return 0;
-
- return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
-}
-
-#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-
-/*
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- */
-#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member)
-#define __container_of(uniq, ptr, type, member) \
- __extension__ ({ \
- const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \
- (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \
- })
-
-#undef MAX
-#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
-#define __MAX(aq, a, bq, b) \
- __extension__ ({ \
- const typeof(a) UNIQ_T(A, aq) = (a); \
- const typeof(b) UNIQ_T(B, bq) = (b); \
- UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
- })
-
-/* evaluates to (void) if _A or _B are not constant or of different types */
-#define CONST_MAX(_A, _B) \
- __extension__ (__builtin_choose_expr( \
- __builtin_constant_p(_A) && \
- __builtin_constant_p(_B) && \
- __builtin_types_compatible_p(typeof(_A), typeof(_B)), \
- ((_A) > (_B)) ? (_A) : (_B), \
- (void)0))
-
-/* takes two types and returns the size of the larger one */
-#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
-
-#define MAX3(x,y,z) \
- __extension__ ({ \
- const typeof(x) _c = MAX(x,y); \
- MAX(_c, z); \
- })
-
-#undef MIN
-#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
-#define __MIN(aq, a, bq, b) \
- __extension__ ({ \
- const typeof(a) UNIQ_T(A, aq) = (a); \
- const typeof(b) UNIQ_T(B, bq) = (b); \
- UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
- })
-
-#define MIN3(x,y,z) \
- __extension__ ({ \
- const typeof(x) _c = MIN(x,y); \
- MIN(_c, z); \
- })
-
-#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
-#define __LESS_BY(aq, a, bq, b) \
- __extension__ ({ \
- const typeof(a) UNIQ_T(A, aq) = (a); \
- const typeof(b) UNIQ_T(B, bq) = (b); \
- UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \
- })
-
-#undef CLAMP
-#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
-#define __CLAMP(xq, x, lowq, low, highq, high) \
- __extension__ ({ \
- const typeof(x) UNIQ_T(X,xq) = (x); \
- const typeof(low) UNIQ_T(LOW,lowq) = (low); \
- const typeof(high) UNIQ_T(HIGH,highq) = (high); \
- UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \
- UNIQ_T(HIGH,highq) : \
- UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \
- UNIQ_T(LOW,lowq) : \
- UNIQ_T(X,xq); \
- })
-
-/* [(x + y - 1) / y] suffers from an integer overflow, even though the
- * computation should be possible in the given type. Therefore, we use
- * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
- * quotient and the remainder, so both should be equally fast. */
-#define DIV_ROUND_UP(_x, _y) \
- __extension__ ({ \
- const typeof(_x) __x = (_x); \
- const typeof(_y) __y = (_y); \
- (__x / __y + !!(__x % __y)); \
- })
-
-#define assert_se(expr) \
- do { \
- if (_unlikely_(!(expr))) \
- log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- } while (false) \
-
-/* We override the glibc assert() here. */
-#undef assert
-#ifdef NDEBUG
-#define assert(expr) do {} while(false)
-#else
-#define assert(expr) assert_se(expr)
-#endif
-
-#define assert_not_reached(t) \
- do { \
- log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- } while (false)
-
-#if defined(static_assert)
-/* static_assert() is sometimes defined in a way that trips up
- * -Wdeclaration-after-statement, hence let's temporarily turn off
- * this warning around it. */
-#define assert_cc(expr) \
- DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
- static_assert(expr, #expr); \
- REENABLE_WARNING
-#else
-#define assert_cc(expr) \
- DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
- struct CONCATENATE(_assert_struct_, __COUNTER__) { \
- char x[(expr) ? 0 : -1]; \
- }; \
- REENABLE_WARNING
-#endif
-
-#define assert_return(expr, r) \
- do { \
- if (_unlikely_(!(expr))) { \
- log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- return (r); \
- } \
- } while (false)
-
-#define assert_return_errno(expr, r, err) \
- do { \
- if (_unlikely_(!(expr))) { \
- log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- errno = err; \
- return (r); \
- } \
- } while (false)
-
-#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
-#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
-#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
-#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
-
-#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
-#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u)))
-#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
-#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u)))
-
-#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
-#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u)))
-#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
-#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u)))
-
-#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p)))
-#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u)))
-#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p)))
-#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u)))
-
-#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
-#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
-
-/* The following macros add 1 when converting things, since UID 0 is a
- * valid UID, while the pointer NULL is special */
-#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
-#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
-
-#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
-#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
-
-#define memzero(x,l) (memset((x), 0, (l)))
-#define zero(x) (memzero(&(x), sizeof(x)))
-
-#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
-
-#define char_array_0(x) x[sizeof(x)-1] = 0;
-
-#define IOVEC_SET_STRING(i, s) \
- do { \
- struct iovec *_i = &(i); \
- char *_s = (char *)(s); \
- _i->iov_base = _s; \
- _i->iov_len = strlen(_s); \
- } while(false)
-
-static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
- unsigned j;
- size_t r = 0;
-
- for (j = 0; j < n; j++)
- r += i[j].iov_len;
-
- return r;
-}
-
-static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
- unsigned j;
-
- for (j = 0; j < n; j++) {
- size_t sub;
-
- if (_unlikely_(k <= 0))
- break;
-
- sub = MIN(i[j].iov_len, k);
- i[j].iov_len -= sub;
- i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
- k -= sub;
- }
-
- return k;
-}
-
-#define VA_FORMAT_ADVANCE(format, ap) \
-do { \
- int _argtypes[128]; \
- size_t _i, _k; \
- _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
- assert(_k < ELEMENTSOF(_argtypes)); \
- for (_i = 0; _i < _k; _i++) { \
- if (_argtypes[_i] & PA_FLAG_PTR) { \
- (void) va_arg(ap, void*); \
- continue; \
- } \
- \
- switch (_argtypes[_i]) { \
- case PA_INT: \
- case PA_INT|PA_FLAG_SHORT: \
- case PA_CHAR: \
- (void) va_arg(ap, int); \
- break; \
- case PA_INT|PA_FLAG_LONG: \
- (void) va_arg(ap, long int); \
- break; \
- case PA_INT|PA_FLAG_LONG_LONG: \
- (void) va_arg(ap, long long int); \
- break; \
- case PA_WCHAR: \
- (void) va_arg(ap, wchar_t); \
- break; \
- case PA_WSTRING: \
- case PA_STRING: \
- case PA_POINTER: \
- (void) va_arg(ap, void*); \
- break; \
- case PA_FLOAT: \
- case PA_DOUBLE: \
- (void) va_arg(ap, double); \
- break; \
- case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
- (void) va_arg(ap, long double); \
- break; \
- default: \
- assert_not_reached("Unknown format string argument."); \
- } \
- } \
-} while(false)
-
- /* Because statfs.t_type can be int on some architectures, we have to cast
- * the const magic to the type, otherwise the compiler warns about
- * signed/unsigned comparison, because the magic can be 32 bit unsigned.
- */
-#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
-
-/* Returns the number of chars needed to format variables of the
- * specified type as a decimal string. Adds in extra space for a
- * negative '-' prefix (hence works correctly on signed
- * types). Includes space for the trailing NUL. */
-#define DECIMAL_STR_MAX(type) \
- (2+(sizeof(type) <= 1 ? 3 : \
- sizeof(type) <= 2 ? 5 : \
- sizeof(type) <= 4 ? 10 : \
- sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
-
-#define SET_FLAG(v, flag, b) \
- (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
-
-#define IN_SET(x, y, ...) \
- ({ \
- const typeof(y) _y = (y); \
- const typeof(_y) _x = (x); \
- unsigned _i; \
- bool _found = false; \
- for (_i = 0; _i < 1 + sizeof((const typeof(_x)[]) { __VA_ARGS__ })/sizeof(const typeof(_x)); _i++) \
- if (((const typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \
- _found = true; \
- break; \
- } \
- _found; \
- })
-
-/* Return a nulstr for a standard cascade of configuration directories,
- * suitable to pass to conf_files_list_nulstr or config_parse_many. */
-#define CONF_DIRS_NULSTR(n) \
- "/etc/" n ".d\0" \
- "/run/" n ".d\0" \
- "/usr/local/lib/" n ".d\0" \
- "/usr/lib/" n ".d\0" \
- CONF_DIR_SPLIT_USR(n)
-
-#ifdef HAVE_SPLIT_USR
-#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0"
-#else
-#define CONF_DIR_SPLIT_USR(n)
-#endif
-
-/* Define C11 thread_local attribute even on older gcc compiler
- * version */
-#ifndef thread_local
-/*
- * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
- * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
- */
-#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
-#define thread_local _Thread_local
-#else
-#define thread_local __thread
-#endif
-#endif
-
-/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
- * compiler versions */
-#ifndef noreturn
-#if __STDC_VERSION__ >= 201112L
-#define noreturn _Noreturn
-#else
-#define noreturn __attribute__((noreturn))
-#endif
-#endif
-
-#define UID_INVALID ((uid_t) -1)
-#define GID_INVALID ((gid_t) -1)
-#define MODE_INVALID ((mode_t) -1)
-
-#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
- static inline void func##p(type *p) { \
- if (*p) \
- func(*p); \
- } \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define CMSG_FOREACH(cmsg, mh) \
- for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
-
-#include "log.h"
diff --git a/src/shared/memfd-util.c b/src/shared/memfd-util.c
deleted file mode 100644
index e99a738..0000000
--- a/src/shared/memfd-util.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 <stdio.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-
-#ifdef HAVE_LINUX_MEMFD_H
-# include <linux/memfd.h>
-#endif
-
-#include "util.h"
-#include "memfd-util.h"
-#include "utf8.h"
-#include "missing.h"
-
-int memfd_new(const char *name) {
- _cleanup_free_ char *g = NULL;
- int fd;
-
- if (!name) {
- char pr[17] = {};
-
- /* If no name is specified we generate one. We include
- * a hint indicating our library implementation, and
- * add the thread name to it */
-
- assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
-
- if (isempty(pr))
- name = "sd";
- else {
- _cleanup_free_ char *e = NULL;
-
- e = utf8_escape_invalid(pr);
- if (!e)
- return -ENOMEM;
-
- g = strappend("sd-", e);
- if (!g)
- return -ENOMEM;
-
- name = g;
- }
- }
-
- fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-int memfd_map(int fd, uint64_t offset, size_t size, void **p) {
- void *q;
- int sealed;
-
- assert(fd >= 0);
- assert(size > 0);
- assert(p);
-
- sealed = memfd_get_sealed(fd);
- if (sealed < 0)
- return sealed;
-
- if (sealed)
- q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset);
- else
- q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
-
- if (q == MAP_FAILED)
- return -errno;
-
- *p = q;
- return 0;
-}
-
-int memfd_set_sealed(int fd) {
- int r;
-
- assert(fd >= 0);
-
- r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int memfd_get_sealed(int fd) {
- int r;
-
- assert(fd >= 0);
-
- r = fcntl(fd, F_GET_SEALS);
- if (r < 0)
- return -errno;
-
- return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
-}
-
-int memfd_get_size(int fd, uint64_t *sz) {
- struct stat stat;
- int r;
-
- assert(fd >= 0);
- assert(sz);
-
- r = fstat(fd, &stat);
- if (r < 0)
- return -errno;
-
- *sz = stat.st_size;
- return 0;
-}
-
-int memfd_set_size(int fd, uint64_t sz) {
- int r;
-
- assert(fd >= 0);
-
- r = ftruncate(fd, sz);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int memfd_new_and_map(const char *name, size_t sz, void **p) {
- _cleanup_close_ int fd = -1;
- int r;
-
- assert(sz > 0);
- assert(p);
-
- fd = memfd_new(name);
- if (fd < 0)
- return fd;
-
- r = memfd_set_size(fd, sz);
- if (r < 0)
- return r;
-
- r = memfd_map(fd, 0, sz, p);
- if (r < 0)
- return r;
-
- r = fd;
- fd = -1;
-
- return r;
-}
diff --git a/src/shared/memfd-util.h b/src/shared/memfd-util.h
deleted file mode 100644
index 3ed551f..0000000
--- a/src/shared/memfd-util.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/>.
-***/
-
-
-
-int memfd_new(const char *name);
-int memfd_new_and_map(const char *name, size_t sz, void **p);
-
-int memfd_map(int fd, uint64_t offset, size_t size, void **p);
-
-int memfd_set_sealed(int fd);
-int memfd_get_sealed(int fd);
-
-int memfd_get_size(int fd, uint64_t *sz);
-int memfd_set_size(int fd, uint64_t sz);
diff --git a/src/shared/mempool.c b/src/shared/mempool.c
deleted file mode 100644
index d5d98d8..0000000
--- a/src/shared/mempool.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010-2014 Lennart Poettering
- Copyright 2014 Michal Schmidt
-
- 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 "mempool.h"
-#include "macro.h"
-#include "util.h"
-
-struct pool {
- struct pool *next;
- unsigned n_tiles;
- unsigned n_used;
-};
-
-void* mempool_alloc_tile(struct mempool *mp) {
- unsigned i;
-
- /* When a tile is released we add it to the list and simply
- * place the next pointer at its offset 0. */
-
- assert(mp->tile_size >= sizeof(void*));
- assert(mp->at_least > 0);
-
- if (mp->freelist) {
- void *r;
-
- r = mp->freelist;
- mp->freelist = * (void**) mp->freelist;
- return r;
- }
-
- if (_unlikely_(!mp->first_pool) ||
- _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
- unsigned n;
- size_t size;
- struct pool *p;
-
- n = mp->first_pool ? mp->first_pool->n_tiles : 0;
- n = MAX(mp->at_least, n * 2);
- size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
- n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
-
- p = malloc(size);
- if (!p)
- return NULL;
-
- p->next = mp->first_pool;
- p->n_tiles = n;
- p->n_used = 0;
-
- mp->first_pool = p;
- }
-
- i = mp->first_pool->n_used++;
-
- return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
-}
-
-void* mempool_alloc0_tile(struct mempool *mp) {
- void *p;
-
- p = mempool_alloc_tile(mp);
- if (p)
- memzero(p, mp->tile_size);
- return p;
-}
-
-void mempool_free_tile(struct mempool *mp, void *p) {
- * (void**) p = mp->freelist;
- mp->freelist = p;
-}
-
-#ifdef VALGRIND
-
-void mempool_drop(struct mempool *mp) {
- struct pool *p = mp->first_pool;
- while (p) {
- struct pool *n;
- n = p->next;
- free(p);
- p = n;
- }
-}
-
-#endif
diff --git a/src/shared/mempool.h b/src/shared/mempool.h
deleted file mode 100644
index 42f473b..0000000
--- a/src/shared/mempool.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2011-2014 Lennart Poettering
- Copyright 2014 Michal Schmidt
-
- 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 <stddef.h>
-
-struct pool;
-
-struct mempool {
- struct pool *first_pool;
- void *freelist;
- size_t tile_size;
- unsigned at_least;
-};
-
-void* mempool_alloc_tile(struct mempool *mp);
-void* mempool_alloc0_tile(struct mempool *mp);
-void mempool_free_tile(struct mempool *mp, void *p);
-
-#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
-struct mempool pool_name = { \
- .tile_size = sizeof(tile_type), \
- .at_least = alloc_at_least, \
-}
-
-
-#ifdef VALGRIND
-void mempool_drop(struct mempool *mp);
-#endif
diff --git a/src/shared/missing.h b/src/shared/missing.h
deleted file mode 100644
index be7f618..0000000
--- a/src/shared/missing.h
+++ /dev/null
@@ -1,1001 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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/>.
-***/
-
-/* Missing glibc definitions to access certain kernel APIs */
-
-#include <sys/resource.h>
-#include <sys/syscall.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <linux/oom.h>
-#include <linux/input.h>
-#include <linux/if_link.h>
-#include <linux/loop.h>
-#include <linux/audit.h>
-#include <linux/capability.h>
-#include <linux/neighbour.h>
-
-#ifdef HAVE_AUDIT
-#include <libaudit.h>
-#endif
-
-#ifdef ARCH_MIPS
-#include <asm/sgidefs.h>
-#endif
-
-#ifdef HAVE_LINUX_BTRFS_H
-#include <linux/btrfs.h>
-#endif
-
-#include "macro.h"
-
-#ifndef RLIMIT_RTTIME
-#define RLIMIT_RTTIME 15
-#endif
-
-/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */
-#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS)
-
-#ifndef F_LINUX_SPECIFIC_BASE
-#define F_LINUX_SPECIFIC_BASE 1024
-#endif
-
-#ifndef F_SETPIPE_SZ
-#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
-#endif
-
-#ifndef F_GETPIPE_SZ
-#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
-#endif
-
-#ifndef F_ADD_SEALS
-#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
-#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
-
-#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
-#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
-#define F_SEAL_GROW 0x0004 /* prevent file from growing */
-#define F_SEAL_WRITE 0x0008 /* prevent writes */
-#endif
-
-#ifndef F_OFD_GETLK
-#define F_OFD_GETLK 36
-#define F_OFD_SETLK 37
-#define F_OFD_SETLKW 38
-#endif
-
-#ifndef MFD_ALLOW_SEALING
-#define MFD_ALLOW_SEALING 0x0002U
-#endif
-
-#ifndef MFD_CLOEXEC
-#define MFD_CLOEXEC 0x0001U
-#endif
-
-#ifndef IP_FREEBIND
-#define IP_FREEBIND 15
-#endif
-
-#ifndef OOM_SCORE_ADJ_MIN
-#define OOM_SCORE_ADJ_MIN (-1000)
-#endif
-
-#ifndef OOM_SCORE_ADJ_MAX
-#define OOM_SCORE_ADJ_MAX 1000
-#endif
-
-#ifndef AUDIT_SERVICE_START
-#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */
-#endif
-
-#ifndef AUDIT_SERVICE_STOP
-#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
-#endif
-
-#ifndef TIOCVHANGUP
-#define TIOCVHANGUP 0x5437
-#endif
-
-#ifndef IP_TRANSPARENT
-#define IP_TRANSPARENT 19
-#endif
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-#if !HAVE_DECL_PIVOT_ROOT
-static inline int pivot_root(const char *new_root, const char *put_old) {
- return syscall(SYS_pivot_root, new_root, put_old);
-}
-#endif
-
-#ifndef __NR_memfd_create
-# if defined __x86_64__
-# define __NR_memfd_create 319
-# elif defined __arm__
-# define __NR_memfd_create 385
-# elif defined __aarch64__
-# define __NR_memfd_create 279
-# elif defined _MIPS_SIM
-# if _MIPS_SIM == _MIPS_SIM_ABI32
-# define __NR_memfd_create 4354
-# endif
-# if _MIPS_SIM == _MIPS_SIM_NABI32
-# define __NR_memfd_create 6318
-# endif
-# if _MIPS_SIM == _MIPS_SIM_ABI64
-# define __NR_memfd_create 5314
-# endif
-# elif defined __i386__
-# define __NR_memfd_create 356
-# else
-# warning "__NR_memfd_create unknown for your architecture"
-# define __NR_memfd_create 0xffffffff
-# endif
-#endif
-
-#ifndef HAVE_MEMFD_CREATE
-static inline int memfd_create(const char *name, unsigned int flags) {
- return syscall(__NR_memfd_create, name, flags);
-}
-#endif
-
-#ifndef __NR_getrandom
-# if defined __x86_64__
-# define __NR_getrandom 318
-# elif defined(__i386__)
-# define __NR_getrandom 355
-# elif defined(__arm__)
-# define __NR_getrandom 384
-# elif defined(__aarch64__)
-# define __NR_getrandom 278
-# elif defined(__ia64__)
-# define __NR_getrandom 1339
-# elif defined(__m68k__)
-# define __NR_getrandom 352
-# elif defined(__s390x__)
-# define __NR_getrandom 349
-# elif defined(__powerpc__)
-# define __NR_getrandom 359
-# elif defined _MIPS_SIM
-# if _MIPS_SIM == _MIPS_SIM_ABI32
-# define __NR_getrandom 4353
-# endif
-# if _MIPS_SIM == _MIPS_SIM_NABI32
-# define __NR_getrandom 6317
-# endif
-# if _MIPS_SIM == _MIPS_SIM_ABI64
-# define __NR_getrandom 5313
-# endif
-# else
-# warning "__NR_getrandom unknown for your architecture"
-# define __NR_getrandom 0xffffffff
-# endif
-#endif
-
-#if !HAVE_DECL_GETRANDOM
-static inline int getrandom(void *buffer, size_t count, unsigned flags) {
- return syscall(__NR_getrandom, buffer, count, flags);
-}
-#endif
-
-#ifndef GRND_NONBLOCK
-#define GRND_NONBLOCK 0x0001
-#endif
-
-#ifndef GRND_RANDOM
-#define GRND_RANDOM 0x0002
-#endif
-
-#ifndef BTRFS_IOCTL_MAGIC
-#define BTRFS_IOCTL_MAGIC 0x94
-#endif
-
-#ifndef BTRFS_PATH_NAME_MAX
-#define BTRFS_PATH_NAME_MAX 4087
-#endif
-
-#ifndef BTRFS_DEVICE_PATH_NAME_MAX
-#define BTRFS_DEVICE_PATH_NAME_MAX 1024
-#endif
-
-#ifndef BTRFS_FSID_SIZE
-#define BTRFS_FSID_SIZE 16
-#endif
-
-#ifndef BTRFS_UUID_SIZE
-#define BTRFS_UUID_SIZE 16
-#endif
-
-#ifndef BTRFS_SUBVOL_RDONLY
-#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
-#endif
-
-#ifndef BTRFS_SUBVOL_NAME_MAX
-#define BTRFS_SUBVOL_NAME_MAX 4039
-#endif
-
-#ifndef BTRFS_INO_LOOKUP_PATH_MAX
-#define BTRFS_INO_LOOKUP_PATH_MAX 4080
-#endif
-
-#ifndef BTRFS_SEARCH_ARGS_BUFSIZE
-#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
-#endif
-
-#ifndef HAVE_LINUX_BTRFS_H
-struct btrfs_ioctl_vol_args {
- int64_t fd;
- char name[BTRFS_PATH_NAME_MAX + 1];
-};
-
-struct btrfs_qgroup_limit {
- __u64 flags;
- __u64 max_rfer;
- __u64 max_excl;
- __u64 rsv_rfer;
- __u64 rsv_excl;
-};
-
-struct btrfs_qgroup_inherit {
- __u64 flags;
- __u64 num_qgroups;
- __u64 num_ref_copies;
- __u64 num_excl_copies;
- struct btrfs_qgroup_limit lim;
- __u64 qgroups[0];
-};
-
-struct btrfs_ioctl_qgroup_limit_args {
- __u64 qgroupid;
- struct btrfs_qgroup_limit lim;
-};
-
-struct btrfs_ioctl_vol_args_v2 {
- __s64 fd;
- __u64 transid;
- __u64 flags;
- union {
- struct {
- __u64 size;
- struct btrfs_qgroup_inherit *qgroup_inherit;
- };
- __u64 unused[4];
- };
- char name[BTRFS_SUBVOL_NAME_MAX + 1];
-};
-
-struct btrfs_ioctl_dev_info_args {
- uint64_t devid; /* in/out */
- uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */
- uint64_t bytes_used; /* out */
- uint64_t total_bytes; /* out */
- uint64_t unused[379]; /* pad to 4k */
- char path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
-};
-
-struct btrfs_ioctl_fs_info_args {
- uint64_t max_id; /* out */
- uint64_t num_devices; /* out */
- uint8_t fsid[BTRFS_FSID_SIZE]; /* out */
- uint64_t reserved[124]; /* pad to 1k */
-};
-
-struct btrfs_ioctl_ino_lookup_args {
- __u64 treeid;
- __u64 objectid;
- char name[BTRFS_INO_LOOKUP_PATH_MAX];
-};
-
-struct btrfs_ioctl_search_key {
- /* which root are we searching. 0 is the tree of tree roots */
- __u64 tree_id;
-
- /* keys returned will be >= min and <= max */
- __u64 min_objectid;
- __u64 max_objectid;
-
- /* keys returned will be >= min and <= max */
- __u64 min_offset;
- __u64 max_offset;
-
- /* max and min transids to search for */
- __u64 min_transid;
- __u64 max_transid;
-
- /* keys returned will be >= min and <= max */
- __u32 min_type;
- __u32 max_type;
-
- /*
- * how many items did userland ask for, and how many are we
- * returning
- */
- __u32 nr_items;
-
- /* align to 64 bits */
- __u32 unused;
-
- /* some extra for later */
- __u64 unused1;
- __u64 unused2;
- __u64 unused3;
- __u64 unused4;
-};
-
-struct btrfs_ioctl_search_header {
- __u64 transid;
- __u64 objectid;
- __u64 offset;
- __u32 type;
- __u32 len;
-};
-
-
-struct btrfs_ioctl_search_args {
- struct btrfs_ioctl_search_key key;
- char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
-};
-
-struct btrfs_ioctl_clone_range_args {
- __s64 src_fd;
- __u64 src_offset, src_length;
- __u64 dest_offset;
-};
-
-#define BTRFS_QUOTA_CTL_ENABLE 1
-#define BTRFS_QUOTA_CTL_DISABLE 2
-#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
-struct btrfs_ioctl_quota_ctl_args {
- __u64 cmd;
- __u64 status;
-};
-#endif
-
-#ifndef BTRFS_IOC_DEFRAG
-#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_RESIZE
-#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_CLONE
-#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
-#endif
-
-#ifndef BTRFS_IOC_CLONE_RANGE
-#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
- struct btrfs_ioctl_clone_range_args)
-#endif
-
-#ifndef BTRFS_IOC_SUBVOL_CREATE
-#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_SNAP_DESTROY
-#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_TREE_SEARCH
-#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
- struct btrfs_ioctl_search_args)
-#endif
-
-#ifndef BTRFS_IOC_INO_LOOKUP
-#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
- struct btrfs_ioctl_ino_lookup_args)
-#endif
-
-#ifndef BTRFS_IOC_SNAP_CREATE_V2
-#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
- struct btrfs_ioctl_vol_args_v2)
-#endif
-
-#ifndef BTRFS_IOC_SUBVOL_GETFLAGS
-#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
-#endif
-
-#ifndef BTRFS_IOC_SUBVOL_SETFLAGS
-#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
-#endif
-
-#ifndef BTRFS_IOC_DEV_INFO
-#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
- struct btrfs_ioctl_dev_info_args)
-#endif
-
-#ifndef BTRFS_IOC_FS_INFO
-#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
- struct btrfs_ioctl_fs_info_args)
-#endif
-
-#ifndef BTRFS_IOC_DEVICES_READY
-#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_QUOTA_CTL
-#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
- struct btrfs_ioctl_quota_ctl_args)
-#endif
-
-#ifndef BTRFS_IOC_QGROUP_LIMIT
-#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
- struct btrfs_ioctl_qgroup_limit_args)
-#endif
-
-#ifndef BTRFS_FIRST_FREE_OBJECTID
-#define BTRFS_FIRST_FREE_OBJECTID 256
-#endif
-
-#ifndef BTRFS_LAST_FREE_OBJECTID
-#define BTRFS_LAST_FREE_OBJECTID -256ULL
-#endif
-
-#ifndef BTRFS_ROOT_TREE_OBJECTID
-#define BTRFS_ROOT_TREE_OBJECTID 1
-#endif
-
-#ifndef BTRFS_QUOTA_TREE_OBJECTID
-#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
-#endif
-
-#ifndef BTRFS_ROOT_ITEM_KEY
-#define BTRFS_ROOT_ITEM_KEY 132
-#endif
-
-#ifndef BTRFS_QGROUP_STATUS_KEY
-#define BTRFS_QGROUP_STATUS_KEY 240
-#endif
-
-#ifndef BTRFS_QGROUP_INFO_KEY
-#define BTRFS_QGROUP_INFO_KEY 242
-#endif
-
-#ifndef BTRFS_QGROUP_LIMIT_KEY
-#define BTRFS_QGROUP_LIMIT_KEY 244
-#endif
-
-#ifndef BTRFS_ROOT_BACKREF_KEY
-#define BTRFS_ROOT_BACKREF_KEY 144
-#endif
-
-#ifndef BTRFS_SUPER_MAGIC
-#define BTRFS_SUPER_MAGIC 0x9123683E
-#endif
-
-#ifndef MS_MOVE
-#define MS_MOVE 8192
-#endif
-
-#ifndef MS_PRIVATE
-#define MS_PRIVATE (1 << 18)
-#endif
-
-#if !HAVE_DECL_GETTID
-static inline pid_t gettid(void) {
- return (pid_t) syscall(SYS_gettid);
-}
-#endif
-
-#ifndef SCM_SECURITY
-#define SCM_SECURITY 0x03
-#endif
-
-#ifndef MS_STRICTATIME
-#define MS_STRICTATIME (1<<24)
-#endif
-
-#ifndef MS_REC
-#define MS_REC 16384
-#endif
-
-#ifndef MS_SHARED
-#define MS_SHARED (1<<20)
-#endif
-
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#endif
-
-#ifndef PR_SET_CHILD_SUBREAPER
-#define PR_SET_CHILD_SUBREAPER 36
-#endif
-
-#ifndef MAX_HANDLE_SZ
-#define MAX_HANDLE_SZ 128
-#endif
-
-#ifndef __NR_name_to_handle_at
-# if defined(__x86_64__)
-# define __NR_name_to_handle_at 303
-# elif defined(__i386__)
-# define __NR_name_to_handle_at 341
-# elif defined(__arm__)
-# define __NR_name_to_handle_at 370
-# elif defined(__powerpc__)
-# define __NR_name_to_handle_at 345
-# else
-# error "__NR_name_to_handle_at is not defined"
-# endif
-#endif
-
-#if !HAVE_DECL_NAME_TO_HANDLE_AT
-struct file_handle {
- unsigned int handle_bytes;
- int handle_type;
- unsigned char f_handle[0];
-};
-
-static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
- return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
-}
-#endif
-
-#ifndef HAVE_SECURE_GETENV
-# ifdef HAVE___SECURE_GETENV
-# define secure_getenv __secure_getenv
-# else
-# error "neither secure_getenv nor __secure_getenv are available"
-# endif
-#endif
-
-#ifndef CIFS_MAGIC_NUMBER
-# define CIFS_MAGIC_NUMBER 0xFF534D42
-#endif
-
-#ifndef TFD_TIMER_CANCEL_ON_SET
-# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
-#endif
-
-#ifndef SO_REUSEPORT
-# define SO_REUSEPORT 15
-#endif
-
-#ifndef EVIOCREVOKE
-# define EVIOCREVOKE _IOW('E', 0x91, int)
-#endif
-
-#ifndef DRM_IOCTL_SET_MASTER
-# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
-#endif
-
-#ifndef DRM_IOCTL_DROP_MASTER
-# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
-#endif
-
-#if defined(__i386__) || defined(__x86_64__)
-
-/* The precise definition of __O_TMPFILE is arch specific, so let's
- * just define this on x86 where we know the value. */
-
-#ifndef __O_TMPFILE
-#define __O_TMPFILE 020000000
-#endif
-
-/* a horrid kludge trying to make sure that this will fail on old kernels */
-#ifndef O_TMPFILE
-#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
-#endif
-
-#endif
-
-#ifndef __NR_setns
-# if defined(__x86_64__)
-# define __NR_setns 308
-# elif defined(__i386__)
-# define __NR_setns 346
-# else
-# error "__NR_setns is not defined"
-# endif
-#endif
-
-#if !HAVE_DECL_SETNS
-static inline int setns(int fd, int nstype) {
- return syscall(__NR_setns, fd, nstype);
-}
-#endif
-
-#if !HAVE_DECL_LO_FLAGS_PARTSCAN
-#define LO_FLAGS_PARTSCAN 8
-#endif
-
-#ifndef LOOP_CTL_REMOVE
-#define LOOP_CTL_REMOVE 0x4C81
-#endif
-
-#ifndef LOOP_CTL_GET_FREE
-#define LOOP_CTL_GET_FREE 0x4C82
-#endif
-
-#if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE
-#define IFLA_INET6_UNSPEC 0
-#define IFLA_INET6_FLAGS 1
-#define IFLA_INET6_CONF 2
-#define IFLA_INET6_STATS 3
-#define IFLA_INET6_MCAST 4
-#define IFLA_INET6_CACHEINFO 5
-#define IFLA_INET6_ICMP6STATS 6
-#define IFLA_INET6_TOKEN 7
-#define IFLA_INET6_ADDR_GEN_MODE 8
-#define __IFLA_INET6_MAX 9
-
-#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
-
-#define IN6_ADDR_GEN_MODE_EUI64 0
-#define IN6_ADDR_GEN_MODE_NONE 1
-#endif
-
-#if !HAVE_DECL_IFLA_MACVLAN_FLAGS
-#define IFLA_MACVLAN_UNSPEC 0
-#define IFLA_MACVLAN_MODE 1
-#define IFLA_MACVLAN_FLAGS 2
-#define __IFLA_MACVLAN_MAX 3
-
-#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_IPVLAN_MODE
-#define IFLA_IPVLAN_UNSPEC 0
-#define IFLA_IPVLAN_MODE 1
-#define __IFLA_IPVLAN_MAX 2
-
-#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
-
-#define IPVLAN_MODE_L2 0
-#define IPVLAN_MODE_L3 1
-#define IPVLAN_MAX 2
-#endif
-
-#if !HAVE_DECL_IFLA_VTI_REMOTE
-#define IFLA_VTI_UNSPEC 0
-#define IFLA_VTI_LINK 1
-#define IFLA_VTI_IKEY 2
-#define IFLA_VTI_OKEY 3
-#define IFLA_VTI_LOCAL 4
-#define IFLA_VTI_REMOTE 5
-#define __IFLA_VTI_MAX 6
-
-#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_PHYS_PORT_ID
-#undef IFLA_PROMISCUITY
-#define IFLA_PROMISCUITY 30
-#define IFLA_NUM_TX_QUEUES 31
-#define IFLA_NUM_RX_QUEUES 32
-#define IFLA_CARRIER 33
-#define IFLA_PHYS_PORT_ID 34
-#define __IFLA_MAX 35
-
-#define IFLA_MAX (__IFLA_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_BOND_AD_INFO
-#define IFLA_BOND_UNSPEC 0
-#define IFLA_BOND_MODE 1
-#define IFLA_BOND_ACTIVE_SLAVE 2
-#define IFLA_BOND_MIIMON 3
-#define IFLA_BOND_UPDELAY 4
-#define IFLA_BOND_DOWNDELAY 5
-#define IFLA_BOND_USE_CARRIER 6
-#define IFLA_BOND_ARP_INTERVAL 7
-#define IFLA_BOND_ARP_IP_TARGET 8
-#define IFLA_BOND_ARP_VALIDATE 9
-#define IFLA_BOND_ARP_ALL_TARGETS 10
-#define IFLA_BOND_PRIMARY 11
-#define IFLA_BOND_PRIMARY_RESELECT 12
-#define IFLA_BOND_FAIL_OVER_MAC 13
-#define IFLA_BOND_XMIT_HASH_POLICY 14
-#define IFLA_BOND_RESEND_IGMP 15
-#define IFLA_BOND_NUM_PEER_NOTIF 16
-#define IFLA_BOND_ALL_SLAVES_ACTIVE 17
-#define IFLA_BOND_MIN_LINKS 18
-#define IFLA_BOND_LP_INTERVAL 19
-#define IFLA_BOND_PACKETS_PER_SLAVE 20
-#define IFLA_BOND_AD_LACP_RATE 21
-#define IFLA_BOND_AD_SELECT 22
-#define IFLA_BOND_AD_INFO 23
-#define __IFLA_BOND_MAX 24
-
-#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_VLAN_PROTOCOL
-#define IFLA_VLAN_UNSPEC 0
-#define IFLA_VLAN_ID 1
-#define IFLA_VLAN_FLAGS 2
-#define IFLA_VLAN_EGRESS_QOS 3
-#define IFLA_VLAN_INGRESS_QOS 4
-#define IFLA_VLAN_PROTOCOL 5
-#define __IFLA_VLAN_MAX 6
-
-#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL
-#define IFLA_VXLAN_UNSPEC 0
-#define IFLA_VXLAN_ID 1
-#define IFLA_VXLAN_GROUP 2
-#define IFLA_VXLAN_LINK 3
-#define IFLA_VXLAN_LOCAL 4
-#define IFLA_VXLAN_TTL 5
-#define IFLA_VXLAN_TOS 6
-#define IFLA_VXLAN_LEARNING 7
-#define IFLA_VXLAN_AGEING 8
-#define IFLA_VXLAN_LIMIT 9
-#define IFLA_VXLAN_PORT_RANGE 10
-#define IFLA_VXLAN_PROXY 11
-#define IFLA_VXLAN_RSC 12
-#define IFLA_VXLAN_L2MISS 13
-#define IFLA_VXLAN_L3MISS 14
-#define IFLA_VXLAN_PORT 15
-#define IFLA_VXLAN_GROUP6 16
-#define IFLA_VXLAN_LOCAL6 17
-#define IFLA_VXLAN_UDP_CSUM 18
-#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
-#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
-#define IFLA_VXLAN_REMCSUM_TX 21
-#define IFLA_VXLAN_REMCSUM_RX 22
-#define IFLA_VXLAN_GBP 23
-#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
-#define __IFLA_VXLAN_MAX 25
-
-#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_IPTUN_6RD_RELAY_PREFIXLEN
-#define IFLA_IPTUN_UNSPEC 0
-#define IFLA_IPTUN_LINK 1
-#define IFLA_IPTUN_LOCAL 2
-#define IFLA_IPTUN_REMOTE 3
-#define IFLA_IPTUN_TTL 4
-#define IFLA_IPTUN_TOS 5
-#define IFLA_IPTUN_ENCAP_LIMIT 6
-#define IFLA_IPTUN_FLOWINFO 7
-#define IFLA_IPTUN_FLAGS 8
-#define IFLA_IPTUN_PROTO 9
-#define IFLA_IPTUN_PMTUDISC 10
-#define IFLA_IPTUN_6RD_PREFIX 11
-#define IFLA_IPTUN_6RD_RELAY_PREFIX 12
-#define IFLA_IPTUN_6RD_PREFIXLEN 13
-#define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14
-#define __IFLA_IPTUN_MAX 15
-
-#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO
-#define IFLA_BRIDGE_FLAGS 0
-#define IFLA_BRIDGE_MODE 1
-#define IFLA_BRIDGE_VLAN_INFO 2
-#define __IFLA_BRIDGE_MAX 3
-
-#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
-#endif
-
-#if !HAVE_DECL_IFLA_BRPORT_UNICAST_FLOOD
-#define IFLA_BRPORT_UNSPEC 0
-#define IFLA_BRPORT_STATE 1
-#define IFLA_BRPORT_PRIORITY 2
-#define IFLA_BRPORT_COST 3
-#define IFLA_BRPORT_MODE 4
-#define IFLA_BRPORT_GUARD 5
-#define IFLA_BRPORT_PROTECT 6
-#define IFLA_BRPORT_FAST_LEAVE 7
-#define IFLA_BRPORT_LEARNING 8
-#define IFLA_BRPORT_UNICAST_FLOOD 9
-#define __IFLA_BRPORT_MAX 10
-
-#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
-#endif
-
-#if !HAVE_DECL_NDA_IFINDEX
-#define NDA_UNSPEC 0
-#define NDA_DST 1
-#define NDA_LLADDR 2
-#define NDA_CACHEINFO 3
-#define NDA_PROBES 4
-#define NDA_VLAN 5
-#define NDA_PORT 6
-#define NDA_VNI 7
-#define NDA_IFINDEX 8
-#define __NDA_MAX 9
-
-#define NDA_MAX (__NDA_MAX - 1)
-#endif
-
-#ifndef IPV6_UNICAST_IF
-#define IPV6_UNICAST_IF 76
-#endif
-
-#ifndef IFF_MULTI_QUEUE
-#define IFF_MULTI_QUEUE 0x100
-#endif
-
-#ifndef IFF_LOWER_UP
-#define IFF_LOWER_UP 0x10000
-#endif
-
-#ifndef IFF_DORMANT
-#define IFF_DORMANT 0x20000
-#endif
-
-#ifndef BOND_XMIT_POLICY_ENCAP23
-#define BOND_XMIT_POLICY_ENCAP23 3
-#endif
-
-#ifndef BOND_XMIT_POLICY_ENCAP34
-#define BOND_XMIT_POLICY_ENCAP34 4
-#endif
-
-#ifndef NET_ADDR_RANDOM
-# define NET_ADDR_RANDOM 1
-#endif
-
-#ifndef NET_NAME_UNKNOWN
-# define NET_NAME_UNKNOWN 0
-#endif
-
-#ifndef NET_NAME_ENUM
-# define NET_NAME_ENUM 1
-#endif
-
-#ifndef NET_NAME_PREDICTABLE
-# define NET_NAME_PREDICTABLE 2
-#endif
-
-#ifndef NET_NAME_USER
-# define NET_NAME_USER 3
-#endif
-
-#ifndef NET_NAME_RENAMED
-# define NET_NAME_RENAMED 4
-#endif
-
-#ifndef BPF_XOR
-# define BPF_XOR 0xa0
-#endif
-
-/* Note that LOOPBACK_IFINDEX is currently not exported by the
- * kernel/glibc, but hardcoded internally by the kernel. However, as
- * it is exported to userspace indirectly via rtnetlink and the
- * ioctls, and made use of widely we define it here too, in a way that
- * is compatible with the kernel's internal definition. */
-#ifndef LOOPBACK_IFINDEX
-#define LOOPBACK_IFINDEX 1
-#endif
-
-#if !HAVE_DECL_IFA_FLAGS
-#define IFA_FLAGS 8
-#endif
-
-#ifndef IFA_F_NOPREFIXROUTE
-#define IFA_F_NOPREFIXROUTE 0x200
-#endif
-
-#ifndef MAX_AUDIT_MESSAGE_LENGTH
-#define MAX_AUDIT_MESSAGE_LENGTH 8970
-#endif
-
-#ifndef AUDIT_NLGRP_MAX
-#define AUDIT_NLGRP_READLOG 1
-#endif
-
-#ifndef CAP_MAC_OVERRIDE
-#define CAP_MAC_OVERRIDE 32
-#endif
-
-#ifndef CAP_MAC_ADMIN
-#define CAP_MAC_ADMIN 33
-#endif
-
-#ifndef CAP_SYSLOG
-#define CAP_SYSLOG 34
-#endif
-
-#ifndef CAP_WAKE_ALARM
-#define CAP_WAKE_ALARM 35
-#endif
-
-#ifndef CAP_BLOCK_SUSPEND
-#define CAP_BLOCK_SUSPEND 36
-#endif
-
-#ifndef CAP_AUDIT_READ
-#define CAP_AUDIT_READ 37
-#endif
-
-static inline int raw_clone(unsigned long flags, void *child_stack) {
-#if defined(__s390__) || defined(__CRIS__)
- /* On s390 and cris the order of the first and second arguments
- * of the raw clone() system call is reversed. */
- return (int) syscall(__NR_clone, child_stack, flags);
-#else
- return (int) syscall(__NR_clone, flags, child_stack);
-#endif
-}
-
-static inline pid_t raw_getpid(void) {
- return (pid_t) syscall(__NR_getpid);
-}
-
-#if !HAVE_DECL_RENAMEAT2
-
-#ifndef __NR_renameat2
-# if defined __x86_64__
-# define __NR_renameat2 316
-# elif defined __arm__
-# define __NR_renameat2 382
-# elif defined _MIPS_SIM
-# if _MIPS_SIM == _MIPS_SIM_ABI32
-# define __NR_renameat2 4351
-# endif
-# if _MIPS_SIM == _MIPS_SIM_NABI32
-# define __NR_renameat2 6315
-# endif
-# if _MIPS_SIM == _MIPS_SIM_ABI64
-# define __NR_renameat2 5311
-# endif
-# elif defined __i386__
-# define __NR_renameat2 353
-# else
-# warning "__NR_renameat2 unknown for your architecture"
-# define __NR_renameat2 0xffffffff
-# endif
-#endif
-
-static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
- return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
-}
-#endif
-
-#ifndef RENAME_NOREPLACE
-#define RENAME_NOREPLACE (1 << 0)
-#endif
-
-#if !HAVE_DECL_KCMP
-static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
- return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
-}
-#endif
-
-#ifndef KCMP_FILE
-#define KCMP_FILE 0
-#endif
-
-#ifndef INPUT_PROP_POINTING_STICK
-#define INPUT_PROP_POINTING_STICK 0x05
-#endif
-
-#ifndef INPUT_PROP_ACCELEROMETER
-#define INPUT_PROP_ACCELEROMETER 0x06
-#endif
diff --git a/src/shared/mkdir-label.c b/src/shared/mkdir-label.c
deleted file mode 100644
index 76bbc1e..0000000
--- a/src/shared/mkdir-label.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2013 Kay Sievers
-
- 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 <unistd.h>
-#include <stdio.h>
-
-#include "label.h"
-#include "mkdir.h"
-
-int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- return mkdir_safe_internal(path, mode, uid, gid, mkdir_label);
-}
-
-int mkdir_parents_label(const char *path, mode_t mode) {
- return mkdir_parents_internal(NULL, path, mode, mkdir_label);
-}
-
-int mkdir_p_label(const char *path, mode_t mode) {
- return mkdir_p_internal(NULL, path, mode, mkdir_label);
-}
diff --git a/src/shared/mkdir.c b/src/shared/mkdir.c
deleted file mode 100644
index 7ee4546..0000000
--- a/src/shared/mkdir.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <string.h>
-#include <errno.h>
-
-#include "util.h"
-#include "path-util.h"
-#include "mkdir.h"
-
-int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) {
- struct stat st;
-
- if (_mkdir(path, mode) >= 0)
- if (chmod_and_chown(path, mode, uid, gid) < 0)
- return -errno;
-
- if (lstat(path, &st) < 0)
- return -errno;
-
- if ((st.st_mode & 0007) > (mode & 0007) ||
- (st.st_mode & 0070) > (mode & 0070) ||
- (st.st_mode & 0700) > (mode & 0700) ||
- (uid != UID_INVALID && st.st_uid != uid) ||
- (gid != GID_INVALID && st.st_gid != gid) ||
- !S_ISDIR(st.st_mode))
- return -EEXIST;
-
- return 0;
-}
-
-int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- return mkdir_safe_internal(path, mode, uid, gid, mkdir);
-}
-
-int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
- const char *p, *e;
- int r;
-
- assert(path);
-
- if (prefix && !path_startswith(path, prefix))
- return -ENOTDIR;
-
- /* return immediately if directory exists */
- e = strrchr(path, '/');
- if (!e)
- return -EINVAL;
-
- if (e == path)
- return 0;
-
- p = strndupa(path, e - path);
- r = is_dir(p, true);
- if (r > 0)
- return 0;
- if (r == 0)
- return -ENOTDIR;
-
- /* create every parent directory in the path, except the last component */
- p = path + strspn(path, "/");
- for (;;) {
- char t[strlen(path) + 1];
-
- e = p + strcspn(p, "/");
- p = e + strspn(e, "/");
-
- /* Is this the last component? If so, then we're
- * done */
- if (*p == 0)
- return 0;
-
- memcpy(t, path, e - path);
- t[e-path] = 0;
-
- if (prefix && path_startswith(prefix, t))
- continue;
-
- r = _mkdir(t, mode);
- if (r < 0 && errno != EEXIST)
- return -errno;
- }
-}
-
-int mkdir_parents(const char *path, mode_t mode) {
- return mkdir_parents_internal(NULL, path, mode, mkdir);
-}
-
-int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
- int r;
-
- /* Like mkdir -p */
-
- r = mkdir_parents_internal(prefix, path, mode, _mkdir);
- if (r < 0)
- return r;
-
- r = _mkdir(path, mode);
- if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
- return -errno;
-
- return 0;
-}
-
-int mkdir_p(const char *path, mode_t mode) {
- return mkdir_p_internal(NULL, path, mode, mkdir);
-}
diff --git a/src/shared/mkdir.h b/src/shared/mkdir.h
deleted file mode 100644
index 2392d1f..0000000
--- a/src/shared/mkdir.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2013 Kay Sievers
-
- 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 mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int mkdir_parents(const char *path, mode_t mode);
-int mkdir_p(const char *path, mode_t mode);
-
-/* mandatory access control(MAC) versions */
-int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int mkdir_parents_label(const char *path, mode_t mode);
-int mkdir_p_label(const char *path, mode_t mode);
-
-/* internally used */
-typedef int (*mkdir_func_t)(const char *pathname, mode_t mode);
-int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir);
-int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
-int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
diff --git a/src/shared/ordered-set.h b/src/shared/ordered-set.h
deleted file mode 100644
index 766a1f2..0000000
--- a/src/shared/ordered-set.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 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 "hashmap.h"
-
-typedef struct OrderedSet OrderedSet;
-
-static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
- return (OrderedSet*) ordered_hashmap_new(ops);
-}
-
-static inline OrderedSet* ordered_set_free(OrderedSet *s) {
- ordered_hashmap_free((OrderedHashmap*) s);
- return NULL;
-}
-
-static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
- ordered_hashmap_free_free((OrderedHashmap*) s);
- return NULL;
-}
-
-static inline int ordered_set_put(OrderedSet *s, void *p) {
- return ordered_hashmap_put((OrderedHashmap*) s, p, p);
-}
-
-static inline bool ordered_set_isempty(OrderedSet *s) {
- return ordered_hashmap_isempty((OrderedHashmap*) s);
-}
-
-static inline void *ordered_set_iterate(OrderedSet *s, Iterator *i) {
- return ordered_hashmap_iterate((OrderedHashmap*) s, i, NULL);
-}
-
-#define ORDERED_SET_FOREACH(e, s, i) \
- for ((i) = ITERATOR_FIRST, (e) = ordered_set_iterate((s), &(i)); (e); (e) = ordered_set_iterate((s), &(i)))
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
-
-#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
deleted file mode 100644
index 5377054..0000000
--- a/src/shared/path-util.c
+++ /dev/null
@@ -1,853 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010-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 <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <sys/statvfs.h>
-
-#include "macro.h"
-#include "util.h"
-#include "log.h"
-#include "strv.h"
-#include "path-util.h"
-#include "missing.h"
-#include "fileio.h"
-
-bool path_is_absolute(const char *p) {
- return p[0] == '/';
-}
-
-bool is_path(const char *p) {
- return !!strchr(p, '/');
-}
-
-int path_get_parent(const char *path, char **_r) {
- const char *e, *a = NULL, *b = NULL, *p;
- char *r;
- bool slash = false;
-
- assert(path);
- assert(_r);
-
- if (!*path)
- return -EINVAL;
-
- for (e = path; *e; e++) {
-
- if (!slash && *e == '/') {
- a = b;
- b = e;
- slash = true;
- } else if (slash && *e != '/')
- slash = false;
- }
-
- if (*(e-1) == '/')
- p = a;
- else
- p = b;
-
- if (!p)
- return -EINVAL;
-
- if (p == path)
- r = strdup("/");
- else
- r = strndup(path, p-path);
-
- if (!r)
- return -ENOMEM;
-
- *_r = r;
- return 0;
-}
-
-char **path_split_and_make_absolute(const char *p) {
- char **l;
- assert(p);
-
- l = strv_split(p, ":");
- if (!l)
- return NULL;
-
- if (!path_strv_make_absolute_cwd(l)) {
- strv_free(l);
- return NULL;
- }
-
- return l;
-}
-
-char *path_make_absolute(const char *p, const char *prefix) {
- assert(p);
-
- /* Makes every item in the list an absolute path by prepending
- * the prefix, if specified and necessary */
-
- if (path_is_absolute(p) || !prefix)
- return strdup(p);
-
- return strjoin(prefix, "/", p, NULL);
-}
-
-char *path_make_absolute_cwd(const char *p) {
- _cleanup_free_ char *cwd = NULL;
-
- assert(p);
-
- /* Similar to path_make_absolute(), but prefixes with the
- * current working directory. */
-
- if (path_is_absolute(p))
- return strdup(p);
-
- cwd = get_current_dir_name();
- if (!cwd)
- return NULL;
-
- return strjoin(cwd, "/", p, NULL);
-}
-
-int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
- char *r, *p;
- unsigned n_parents;
-
- assert(from_dir);
- assert(to_path);
- assert(_r);
-
- /* Strips the common part, and adds ".." elements as necessary. */
-
- if (!path_is_absolute(from_dir))
- return -EINVAL;
-
- if (!path_is_absolute(to_path))
- return -EINVAL;
-
- /* Skip the common part. */
- for (;;) {
- size_t a;
- size_t b;
-
- from_dir += strspn(from_dir, "/");
- to_path += strspn(to_path, "/");
-
- if (!*from_dir) {
- if (!*to_path)
- /* from_dir equals to_path. */
- r = strdup(".");
- else
- /* from_dir is a parent directory of to_path. */
- r = strdup(to_path);
-
- if (!r)
- return -ENOMEM;
-
- path_kill_slashes(r);
-
- *_r = r;
- return 0;
- }
-
- if (!*to_path)
- break;
-
- a = strcspn(from_dir, "/");
- b = strcspn(to_path, "/");
-
- if (a != b)
- break;
-
- if (memcmp(from_dir, to_path, a) != 0)
- break;
-
- from_dir += a;
- to_path += b;
- }
-
- /* If we're here, then "from_dir" has one or more elements that need to
- * be replaced with "..". */
-
- /* Count the number of necessary ".." elements. */
- for (n_parents = 0;;) {
- from_dir += strspn(from_dir, "/");
-
- if (!*from_dir)
- break;
-
- from_dir += strcspn(from_dir, "/");
- n_parents++;
- }
-
- r = malloc(n_parents * 3 + strlen(to_path) + 1);
- if (!r)
- return -ENOMEM;
-
- for (p = r; n_parents > 0; n_parents--, p += 3)
- memcpy(p, "../", 3);
-
- strcpy(p, to_path);
- path_kill_slashes(r);
-
- *_r = r;
- return 0;
-}
-
-char **path_strv_make_absolute_cwd(char **l) {
- char **s;
-
- /* Goes through every item in the string list and makes it
- * absolute. This works in place and won't rollback any
- * changes on failure. */
-
- STRV_FOREACH(s, l) {
- char *t;
-
- t = path_make_absolute_cwd(*s);
- if (!t)
- return NULL;
-
- free(*s);
- *s = t;
- }
-
- return l;
-}
-
-char **path_strv_resolve(char **l, const char *prefix) {
- char **s;
- unsigned k = 0;
- bool enomem = false;
-
- if (strv_isempty(l))
- return l;
-
- /* Goes through every item in the string list and canonicalize
- * the path. This works in place and won't rollback any
- * changes on failure. */
-
- STRV_FOREACH(s, l) {
- char *t, *u;
- _cleanup_free_ char *orig = NULL;
-
- if (!path_is_absolute(*s)) {
- free(*s);
- continue;
- }
-
- if (prefix) {
- orig = *s;
- t = strappend(prefix, orig);
- if (!t) {
- enomem = true;
- continue;
- }
- } else
- t = *s;
-
- errno = 0;
- u = canonicalize_file_name(t);
- if (!u) {
- if (errno == ENOENT) {
- if (prefix) {
- u = orig;
- orig = NULL;
- free(t);
- } else
- u = t;
- } else {
- free(t);
- if (errno == ENOMEM || errno == 0)
- enomem = true;
-
- continue;
- }
- } else if (prefix) {
- char *x;
-
- free(t);
- x = path_startswith(u, prefix);
- if (x) {
- /* restore the slash if it was lost */
- if (!startswith(x, "/"))
- *(--x) = '/';
-
- t = strdup(x);
- free(u);
- if (!t) {
- enomem = true;
- continue;
- }
- u = t;
- } else {
- /* canonicalized path goes outside of
- * prefix, keep the original path instead */
- free(u);
- u = orig;
- orig = NULL;
- }
- } else
- free(t);
-
- l[k++] = u;
- }
-
- l[k] = NULL;
-
- if (enomem)
- return NULL;
-
- return l;
-}
-
-char **path_strv_resolve_uniq(char **l, const char *prefix) {
-
- if (strv_isempty(l))
- return l;
-
- if (!path_strv_resolve(l, prefix))
- return NULL;
-
- return strv_uniq(l);
-}
-
-char *path_kill_slashes(char *path) {
- char *f, *t;
- bool slash = false;
-
- /* Removes redundant inner and trailing slashes. Modifies the
- * passed string in-place.
- *
- * ///foo///bar/ becomes /foo/bar
- */
-
- for (f = path, t = path; *f; f++) {
-
- if (*f == '/') {
- slash = true;
- continue;
- }
-
- if (slash) {
- slash = false;
- *(t++) = '/';
- }
-
- *(t++) = *f;
- }
-
- /* Special rule, if we are talking of the root directory, a
- trailing slash is good */
-
- if (t == path && slash)
- *(t++) = '/';
-
- *t = 0;
- return path;
-}
-
-char* path_startswith(const char *path, const char *prefix) {
- assert(path);
- assert(prefix);
-
- if ((path[0] == '/') != (prefix[0] == '/'))
- return NULL;
-
- for (;;) {
- size_t a, b;
-
- path += strspn(path, "/");
- prefix += strspn(prefix, "/");
-
- if (*prefix == 0)
- return (char*) path;
-
- if (*path == 0)
- return NULL;
-
- a = strcspn(path, "/");
- b = strcspn(prefix, "/");
-
- if (a != b)
- return NULL;
-
- if (memcmp(path, prefix, a) != 0)
- return NULL;
-
- path += a;
- prefix += b;
- }
-}
-
-int path_compare(const char *a, const char *b) {
- int d;
-
- assert(a);
- assert(b);
-
- /* A relative path and an abolute path must not compare as equal.
- * Which one is sorted before the other does not really matter.
- * Here a relative path is ordered before an absolute path. */
- d = (a[0] == '/') - (b[0] == '/');
- if (d)
- return d;
-
- for (;;) {
- size_t j, k;
-
- a += strspn(a, "/");
- b += strspn(b, "/");
-
- if (*a == 0 && *b == 0)
- return 0;
-
- /* Order prefixes first: "/foo" before "/foo/bar" */
- if (*a == 0)
- return -1;
- if (*b == 0)
- return 1;
-
- j = strcspn(a, "/");
- k = strcspn(b, "/");
-
- /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
- d = memcmp(a, b, MIN(j, k));
- if (d)
- return (d > 0) - (d < 0); /* sign of d */
-
- /* Sort "/foo/a" before "/foo/aaa" */
- d = (j > k) - (j < k); /* sign of (j - k) */
- if (d)
- return d;
-
- a += j;
- b += k;
- }
-}
-
-bool path_equal(const char *a, const char *b) {
- return path_compare(a, b) == 0;
-}
-
-bool path_equal_or_files_same(const char *a, const char *b) {
- return path_equal(a, b) || files_same(a, b) > 0;
-}
-
-char* path_join(const char *root, const char *path, const char *rest) {
- assert(path);
-
- if (!isempty(root))
- return strjoin(root, endswith(root, "/") ? "" : "/",
- path[0] == '/' ? path+1 : path,
- rest ? (endswith(path, "/") ? "" : "/") : NULL,
- rest && rest[0] == '/' ? rest+1 : rest,
- NULL);
- else
- return strjoin(path,
- rest ? (endswith(path, "/") ? "" : "/") : NULL,
- rest && rest[0] == '/' ? rest+1 : rest,
- NULL);
-}
-
-static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
- char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
- _cleanup_free_ char *fdinfo = NULL;
- _cleanup_close_ int subfd = -1;
- char *p;
- int r;
-
- if ((flags & AT_EMPTY_PATH) && isempty(filename))
- xsprintf(path, "/proc/self/fdinfo/%i", fd);
- else {
- subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
- if (subfd < 0)
- return -errno;
-
- xsprintf(path, "/proc/self/fdinfo/%i", subfd);
- }
-
- r = read_full_file(path, &fdinfo, NULL);
- if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
- return -EOPNOTSUPP;
- if (r < 0)
- return -errno;
-
- p = startswith(fdinfo, "mnt_id:");
- if (!p) {
- p = strstr(fdinfo, "\nmnt_id:");
- if (!p) /* The mnt_id field is a relatively new addition */
- return -EOPNOTSUPP;
-
- p += 8;
- }
-
- p += strspn(p, WHITESPACE);
- p[strcspn(p, WHITESPACE)] = 0;
-
- return safe_atoi(p, mnt_id);
-}
-
-int fd_is_mount_point(int fd, const char *filename, int flags) {
- union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
- int mount_id = -1, mount_id_parent = -1;
- bool nosupp = false, check_st_dev = true;
- struct stat a, b;
- int r;
-
- assert(fd >= 0);
- assert(filename);
-
- /* First we will try the name_to_handle_at() syscall, which
- * tells us the mount id and an opaque file "handle". It is
- * not supported everywhere though (kernel compile-time
- * option, not all file systems are hooked up). If it works
- * the mount id is usually good enough to tell us whether
- * something is a mount point.
- *
- * If that didn't work we will try to read the mount id from
- * /proc/self/fdinfo/<fd>. This is almost as good as
- * name_to_handle_at(), however, does not return the the
- * opaque file handle. The opaque file handle is pretty useful
- * to detect the root directory, which we should always
- * consider a mount point. Hence we use this only as
- * fallback. Exporting the mnt_id in fdinfo is a pretty recent
- * kernel addition.
- *
- * As last fallback we do traditional fstat() based st_dev
- * comparisons. This is how things were traditionally done,
- * but unionfs breaks breaks this since it exposes file
- * systems with a variety of st_dev reported. Also, btrfs
- * subvolumes have different st_dev, even though they aren't
- * real mounts of their own. */
-
- r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
- if (r < 0) {
- if (errno == ENOSYS)
- /* This kernel does not support name_to_handle_at()
- * fall back to simpler logic. */
- goto fallback_fdinfo;
- else if (errno == EOPNOTSUPP)
- /* This kernel or file system does not support
- * name_to_handle_at(), hence let's see if the
- * upper fs supports it (in which case it is a
- * mount point), otherwise fallback to the
- * traditional stat() logic */
- nosupp = true;
- else
- return -errno;
- }
-
- r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
- if (r < 0) {
- if (errno == EOPNOTSUPP) {
- if (nosupp)
- /* Neither parent nor child do name_to_handle_at()?
- We have no choice but to fall back. */
- goto fallback_fdinfo;
- else
- /* The parent can't do name_to_handle_at() but the
- * directory we are interested in can?
- * If so, it must be a mount point. */
- return 1;
- } else
- return -errno;
- }
-
- /* The parent can do name_to_handle_at() but the
- * directory we are interested in can't? If so, it
- * must be a mount point. */
- if (nosupp)
- return 1;
-
- /* If the file handle for the directory we are
- * interested in and its parent are identical, we
- * assume this is the root directory, which is a mount
- * point. */
-
- if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
- h.handle.handle_type == h_parent.handle.handle_type &&
- memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
- return 1;
-
- return mount_id != mount_id_parent;
-
-fallback_fdinfo:
- r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
- if (r == -EOPNOTSUPP)
- goto fallback_fstat;
- if (r < 0)
- return r;
-
- r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
- if (r < 0)
- return r;
-
- if (mount_id != mount_id_parent)
- return 1;
-
- /* Hmm, so, the mount ids are the same. This leaves one
- * special case though for the root file system. For that,
- * let's see if the parent directory has the same inode as we
- * are interested in. Hence, let's also do fstat() checks now,
- * too, but avoid the st_dev comparisons, since they aren't
- * that useful on unionfs mounts. */
- check_st_dev = false;
-
-fallback_fstat:
- /* yay for fstatat() taking a different set of flags than the other
- * _at() above */
- if (flags & AT_SYMLINK_FOLLOW)
- flags &= ~AT_SYMLINK_FOLLOW;
- else
- flags |= AT_SYMLINK_NOFOLLOW;
- if (fstatat(fd, filename, &a, flags) < 0)
- return -errno;
-
- if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
- return -errno;
-
- /* A directory with same device and inode as its parent? Must
- * be the root directory */
- if (a.st_dev == b.st_dev &&
- a.st_ino == b.st_ino)
- return 1;
-
- return check_st_dev && (a.st_dev != b.st_dev);
-}
-
-/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, int flags) {
- _cleanup_close_ int fd = -1;
- _cleanup_free_ char *canonical = NULL, *parent = NULL;
- int r;
-
- assert(t);
-
- if (path_equal(t, "/"))
- return 1;
-
- /* we need to resolve symlinks manually, we can't just rely on
- * fd_is_mount_point() to do that for us; if we have a structure like
- * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
- * look at needs to be /usr, not /. */
- if (flags & AT_SYMLINK_FOLLOW) {
- canonical = canonicalize_file_name(t);
- if (!canonical)
- return -errno;
- }
-
- r = path_get_parent(canonical ?: t, &parent);
- if (r < 0)
- return r;
-
- fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
- if (fd < 0)
- return -errno;
-
- return fd_is_mount_point(fd, basename(canonical ?: t), flags);
-}
-
-int path_is_read_only_fs(const char *path) {
- struct statvfs st;
-
- assert(path);
-
- if (statvfs(path, &st) < 0)
- return -errno;
-
- if (st.f_flag & ST_RDONLY)
- return true;
-
- /* On NFS, statvfs() might not reflect whether we can actually
- * write to the remote share. Let's try again with
- * access(W_OK) which is more reliable, at least sometimes. */
- if (access(path, W_OK) < 0 && errno == EROFS)
- return true;
-
- return false;
-}
-
-int path_is_os_tree(const char *path) {
- char *p;
- int r;
-
- /* We use /usr/lib/os-release as flag file if something is an OS */
- p = strjoina(path, "/usr/lib/os-release");
- r = access(p, F_OK);
-
- if (r >= 0)
- return 1;
-
- /* Also check for the old location in /etc, just in case. */
- p = strjoina(path, "/etc/os-release");
- r = access(p, F_OK);
-
- return r >= 0;
-}
-
-int find_binary(const char *name, bool local, char **filename) {
- assert(name);
-
- if (is_path(name)) {
- if (local && access(name, X_OK) < 0)
- return -errno;
-
- if (filename) {
- char *p;
-
- p = path_make_absolute_cwd(name);
- if (!p)
- return -ENOMEM;
-
- *filename = p;
- }
-
- return 0;
- } else {
- const char *path;
- const char *word, *state;
- size_t l;
-
- /**
- * Plain getenv, not secure_getenv, because we want
- * to actually allow the user to pick the binary.
- */
- path = getenv("PATH");
- if (!path)
- path = DEFAULT_PATH;
-
- FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
- _cleanup_free_ char *p = NULL;
-
- if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
- return -ENOMEM;
-
- if (access(p, X_OK) < 0)
- continue;
-
- if (filename) {
- *filename = path_kill_slashes(p);
- p = NULL;
- }
-
- return 0;
- }
-
- return -ENOENT;
- }
-}
-
-bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
- bool changed = false;
- const char* const* i;
-
- assert(timestamp);
-
- if (paths == NULL)
- return false;
-
- STRV_FOREACH(i, paths) {
- struct stat stats;
- usec_t u;
-
- if (stat(*i, &stats) < 0)
- continue;
-
- u = timespec_load(&stats.st_mtim);
-
- /* first check */
- if (*timestamp >= u)
- continue;
-
- log_debug("timestamp of '%s' changed", *i);
-
- /* update timestamp */
- if (update) {
- *timestamp = u;
- changed = true;
- } else
- return true;
- }
-
- return changed;
-}
-
-int fsck_exists(const char *fstype) {
- _cleanup_free_ char *p = NULL, *d = NULL;
- const char *checker;
- int r;
-
- checker = strjoina("fsck.", fstype);
-
- r = find_binary(checker, true, &p);
- if (r < 0)
- return r;
-
- /* An fsck that is linked to /bin/true is a non-existent
- * fsck */
-
- r = readlink_malloc(p, &d);
- if (r >= 0 &&
- (path_equal(d, "/bin/true") ||
- path_equal(d, "/usr/bin/true") ||
- path_equal(d, "/dev/null")))
- return -ENOENT;
-
- return 0;
-}
-
-char *prefix_root(const char *root, const char *path) {
- char *n, *p;
- size_t l;
-
- /* If root is passed, prefixes path with it. Otherwise returns
- * it as is. */
-
- assert(path);
-
- /* First, drop duplicate prefixing slashes from the path */
- while (path[0] == '/' && path[1] == '/')
- path++;
-
- if (isempty(root) || path_equal(root, "/"))
- return strdup(path);
-
- l = strlen(root) + 1 + strlen(path) + 1;
-
- n = new(char, l);
- if (!n)
- return NULL;
-
- p = stpcpy(n, root);
-
- while (p > n && p[-1] == '/')
- p--;
-
- if (path[0] != '/')
- *(p++) = '/';
-
- strcpy(p, path);
- return n;
-}
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
deleted file mode 100644
index 1eac89c..0000000
--- a/src/shared/path-util.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010-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 <stdbool.h>
-
-#include "macro.h"
-#include "time-util.h"
-
-#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
-#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin"
-
-#ifdef HAVE_SPLIT_USR
-# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
-#else
-# define DEFAULT_PATH DEFAULT_PATH_NORMAL
-#endif
-
-bool is_path(const char *p) _pure_;
-char** path_split_and_make_absolute(const char *p);
-int path_get_parent(const char *path, char **parent);
-bool path_is_absolute(const char *p) _pure_;
-char* path_make_absolute(const char *p, const char *prefix);
-char* path_make_absolute_cwd(const char *p);
-int path_make_relative(const char *from_dir, const char *to_path, char **_r);
-char* path_kill_slashes(char *path);
-char* path_startswith(const char *path, const char *prefix) _pure_;
-int path_compare(const char *a, const char *b) _pure_;
-bool path_equal(const char *a, const char *b) _pure_;
-bool path_equal_or_files_same(const char *a, const char *b);
-char* path_join(const char *root, const char *path, const char *rest);
-
-char** path_strv_make_absolute_cwd(char **l);
-char** path_strv_resolve(char **l, const char *prefix);
-char** path_strv_resolve_uniq(char **l, const char *prefix);
-
-int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, int flags);
-int path_is_read_only_fs(const char *path);
-int path_is_os_tree(const char *path);
-
-int find_binary(const char *name, bool local, char **filename);
-
-bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
-
-int fsck_exists(const char *fstype);
-
-/* Iterates through the path prefixes of the specified path, going up
- * the tree, to root. Also returns "" (and not "/"!) for the root
- * directory. Excludes the specified directory itself */
-#define PATH_FOREACH_PREFIX(prefix, path) \
- for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
-
-/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
-#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
- for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
-
-char *prefix_root(const char *root, const char *path);
-
-/* Similar to prefix_root(), but returns an alloca() buffer, or
- * possibly a const pointer into the path parameter */
-#define prefix_roota(root, path) \
- ({ \
- const char* _path = (path), *_root = (root), *_ret; \
- char *_p, *_n; \
- size_t _l; \
- while (_path[0] == '/' && _path[1] == '/') \
- _path ++; \
- if (isempty(_root) || path_equal(_root, "/")) \
- _ret = _path; \
- else { \
- _l = strlen(_root) + 1 + strlen(_path) + 1; \
- _n = alloca(_l); \
- _p = stpcpy(_n, _root); \
- while (_p > _n && _p[-1] == '/') \
- _p--; \
- if (_path[0] != '/') \
- *(_p++) = '/'; \
- strcpy(_p, _path); \
- _ret = _n; \
- } \
- _ret; \
- })
diff --git a/src/shared/prioq.c b/src/shared/prioq.c
deleted file mode 100644
index b89888b..0000000
--- a/src/shared/prioq.c
+++ /dev/null
@@ -1,308 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 "util.h"
-#include "prioq.h"
-
-struct prioq_item {
- void *data;
- unsigned *idx;
-};
-
-struct Prioq {
- compare_func_t compare_func;
- unsigned n_items, n_allocated;
-
- struct prioq_item *items;
-};
-
-Prioq *prioq_new(compare_func_t compare_func) {
- Prioq *q;
-
- q = new0(Prioq, 1);
- if (!q)
- return q;
-
- q->compare_func = compare_func;
- return q;
-}
-
-Prioq* prioq_free(Prioq *q) {
- if (!q)
- return NULL;
-
- free(q->items);
- free(q);
-
- return NULL;
-}
-
-int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
- assert(q);
-
- if (*q)
- return 0;
-
- *q = prioq_new(compare_func);
- if (!*q)
- return -ENOMEM;
-
- return 0;
-}
-
-static void swap(Prioq *q, unsigned j, unsigned k) {
- void *saved_data;
- unsigned *saved_idx;
-
- assert(q);
- assert(j < q->n_items);
- assert(k < q->n_items);
-
- assert(!q->items[j].idx || *(q->items[j].idx) == j);
- assert(!q->items[k].idx || *(q->items[k].idx) == k);
-
- saved_data = q->items[j].data;
- saved_idx = q->items[j].idx;
- q->items[j].data = q->items[k].data;
- q->items[j].idx = q->items[k].idx;
- q->items[k].data = saved_data;
- q->items[k].idx = saved_idx;
-
- if (q->items[j].idx)
- *q->items[j].idx = j;
-
- if (q->items[k].idx)
- *q->items[k].idx = k;
-}
-
-static unsigned shuffle_up(Prioq *q, unsigned idx) {
- assert(q);
-
- while (idx > 0) {
- unsigned k;
-
- k = (idx-1)/2;
-
- if (q->compare_func(q->items[k].data, q->items[idx].data) < 0)
- break;
-
- swap(q, idx, k);
- idx = k;
- }
-
- return idx;
-}
-
-static unsigned shuffle_down(Prioq *q, unsigned idx) {
- assert(q);
-
- for (;;) {
- unsigned j, k, s;
-
- k = (idx+1)*2; /* right child */
- j = k-1; /* left child */
-
- if (j >= q->n_items)
- break;
-
- if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
-
- /* So our left child is smaller than we are, let's
- * remember this fact */
- s = j;
- else
- s = idx;
-
- if (k < q->n_items &&
- q->compare_func(q->items[k].data, q->items[s].data) < 0)
-
- /* So our right child is smaller than we are, let's
- * remember this fact */
- s = k;
-
- /* s now points to the smallest of the three items */
-
- if (s == idx)
- /* No swap necessary, we're done */
- break;
-
- swap(q, idx, s);
- idx = s;
- }
-
- return idx;
-}
-
-int prioq_put(Prioq *q, void *data, unsigned *idx) {
- struct prioq_item *i;
- unsigned k;
-
- assert(q);
-
- if (q->n_items >= q->n_allocated) {
- unsigned n;
- struct prioq_item *j;
-
- n = MAX((q->n_items+1) * 2, 16u);
- j = realloc(q->items, sizeof(struct prioq_item) * n);
- if (!j)
- return -ENOMEM;
-
- q->items = j;
- q->n_allocated = n;
- }
-
- k = q->n_items++;
- i = q->items + k;
- i->data = data;
- i->idx = idx;
-
- if (idx)
- *idx = k;
-
- shuffle_up(q, k);
-
- return 0;
-}
-
-static void remove_item(Prioq *q, struct prioq_item *i) {
- struct prioq_item *l;
-
- assert(q);
- assert(i);
-
- l = q->items + q->n_items - 1;
-
- if (i == l)
- /* Last entry, let's just remove it */
- q->n_items--;
- else {
- unsigned k;
-
- /* Not last entry, let's replace the last entry with
- * this one, and reshuffle */
-
- k = i - q->items;
-
- i->data = l->data;
- i->idx = l->idx;
- if (i->idx)
- *i->idx = k;
- q->n_items--;
-
- k = shuffle_down(q, k);
- shuffle_up(q, k);
- }
-}
-
-_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
- struct prioq_item *i;
-
- assert(q);
-
- if (idx) {
- if (*idx == PRIOQ_IDX_NULL ||
- *idx > q->n_items)
- return NULL;
-
- i = q->items + *idx;
- if (i->data != data)
- return NULL;
-
- return i;
- } else {
- for (i = q->items; i < q->items + q->n_items; i++)
- if (i->data == data)
- return i;
- return NULL;
- }
-}
-
-int prioq_remove(Prioq *q, void *data, unsigned *idx) {
- struct prioq_item *i;
-
- if (!q)
- return 0;
-
- i = find_item(q, data, idx);
- if (!i)
- return 0;
-
- remove_item(q, i);
- return 1;
-}
-
-int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
- struct prioq_item *i;
- unsigned k;
-
- assert(q);
-
- i = find_item(q, data, idx);
- if (!i)
- return 0;
-
- k = i - q->items;
- k = shuffle_down(q, k);
- shuffle_up(q, k);
- return 1;
-}
-
-void *prioq_peek(Prioq *q) {
-
- if (!q)
- return NULL;
-
- if (q->n_items <= 0)
- return NULL;
-
- return q->items[0].data;
-}
-
-void *prioq_pop(Prioq *q) {
- void *data;
-
- if (!q)
- return NULL;
-
- if (q->n_items <= 0)
- return NULL;
-
- data = q->items[0].data;
- remove_item(q, q->items);
- return data;
-}
-
-unsigned prioq_size(Prioq *q) {
-
- if (!q)
- return 0;
-
- return q->n_items;
-}
-
-bool prioq_isempty(Prioq *q) {
-
- if (!q)
- return true;
-
- return q->n_items <= 0;
-}
diff --git a/src/shared/prioq.h b/src/shared/prioq.h
deleted file mode 100644
index 1c044b1..0000000
--- a/src/shared/prioq.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 "hashmap.h"
-
-typedef struct Prioq Prioq;
-
-#define PRIOQ_IDX_NULL ((unsigned) -1)
-
-Prioq *prioq_new(compare_func_t compare);
-Prioq *prioq_free(Prioq *q);
-int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
-
-int prioq_put(Prioq *q, void *data, unsigned *idx);
-int prioq_remove(Prioq *q, void *data, unsigned *idx);
-int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
-
-void *prioq_peek(Prioq *q) _pure_;
-void *prioq_pop(Prioq *q);
-
-unsigned prioq_size(Prioq *q) _pure_;
-bool prioq_isempty(Prioq *q) _pure_;
diff --git a/src/shared/process-util.c b/src/shared/process-util.c
deleted file mode 100644
index cfc8765..0000000
--- a/src/shared/process-util.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <ctype.h>
-
-#include "fileio.h"
-#include "util.h"
-#include "log.h"
-#include "signal-util.h"
-#include "process-util.h"
-
-int get_process_state(pid_t pid) {
- const char *p;
- char state;
- int r;
- _cleanup_free_ char *line = NULL;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " %c", &state) != 1)
- return -EIO;
-
- return (unsigned char) state;
-}
-
-int get_process_comm(pid_t pid, char **name) {
- const char *p;
- int r;
-
- assert(name);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "comm");
-
- r = read_one_line_file(p, name);
- if (r == -ENOENT)
- return -ESRCH;
-
- return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- _cleanup_fclose_ FILE *f = NULL;
- char *r = NULL, *k;
- const char *p;
- int c;
-
- assert(line);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cmdline");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- if (max_length == 0) {
- size_t len = 0, allocated = 0;
-
- while ((c = getc(f)) != EOF) {
-
- if (!GREEDY_REALLOC(r, allocated, len+2)) {
- free(r);
- return -ENOMEM;
- }
-
- r[len++] = isprint(c) ? c : ' ';
- }
-
- if (len > 0)
- r[len-1] = 0;
-
- } else {
- bool space = false;
- size_t left;
-
- r = new(char, max_length);
- if (!r)
- return -ENOMEM;
-
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
- if (space) {
- if (left <= 4)
- break;
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 4)
- break;
-
- *(k++) = (char) c;
- left--;
- } else
- space = true;
- }
-
- if (left <= 4) {
- size_t n = MIN(left-1, 3U);
- memcpy(k, "...", n);
- k[n] = 0;
- } else
- *k = 0;
- }
-
- /* Kernel threads have no argv[] */
- if (isempty(r)) {
- _cleanup_free_ char *t = NULL;
- int h;
-
- free(r);
-
- if (!comm_fallback)
- return -ENOENT;
-
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- r = strjoin("[", t, "]", NULL);
- if (!r)
- return -ENOMEM;
- }
-
- *line = r;
- return 0;
-}
-
-int is_kernel_thread(pid_t pid) {
- const char *p;
- size_t count;
- char c;
- bool eof;
- FILE *f;
-
- if (pid == 0)
- return 0;
-
- assert(pid > 0);
-
- p = procfs_file_alloca(pid, "cmdline");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- count = fread(&c, 1, 1, f);
- eof = feof(f);
- fclose(f);
-
- /* Kernel threads have an empty cmdline */
-
- if (count <= 0)
- return eof ? 1 : -errno;
-
- return 0;
-}
-
-int get_process_capeff(pid_t pid, char **capeff) {
- const char *p;
-
- assert(capeff);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "status");
-
- return get_status_field(p, "\nCapEff:", capeff);
-}
-
-static int get_process_link_contents(const char *proc_file, char **name) {
- int r;
-
- assert(proc_file);
- assert(name);
-
- r = readlink_malloc(proc_file, name);
- if (r < 0)
- return r == -ENOENT ? -ESRCH : r;
-
- return 0;
-}
-
-int get_process_exe(pid_t pid, char **name) {
- const char *p;
- char *d;
- int r;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "exe");
- r = get_process_link_contents(p, name);
- if (r < 0)
- return r;
-
- d = endswith(*name, " (deleted)");
- if (d)
- *d = '\0';
-
- return 0;
-}
-
-static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- const char *p;
-
- assert(field);
- assert(uid);
-
- if (pid == 0)
- return getuid();
-
- p = procfs_file_alloca(pid, "status");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- FOREACH_LINE(line, f, return -errno) {
- char *l;
-
- l = strstrip(line);
-
- if (startswith(l, field)) {
- l += strlen(field);
- l += strspn(l, WHITESPACE);
-
- l[strcspn(l, WHITESPACE)] = 0;
-
- return parse_uid(l, uid);
- }
- }
-
- return -EIO;
-}
-
-int get_process_uid(pid_t pid, uid_t *uid) {
- return get_process_id(pid, "Uid:", uid);
-}
-
-int get_process_gid(pid_t pid, gid_t *gid) {
- assert_cc(sizeof(uid_t) == sizeof(gid_t));
- return get_process_id(pid, "Gid:", gid);
-}
-
-int get_process_cwd(pid_t pid, char **cwd) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cwd");
-
- return get_process_link_contents(p, cwd);
-}
-
-int get_process_root(pid_t pid, char **root) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "root");
-
- return get_process_link_contents(p, root);
-}
-
-int get_process_environ(pid_t pid, char **env) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *outcome = NULL;
- int c;
- const char *p;
- size_t allocated = 0, sz = 0;
-
- assert(pid >= 0);
- assert(env);
-
- p = procfs_file_alloca(pid, "environ");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- while ((c = fgetc(f)) != EOF) {
- if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
- return -ENOMEM;
-
- if (c == '\0')
- outcome[sz++] = '\n';
- else
- sz += cescape_char(c, outcome + sz);
- }
-
- outcome[sz] = '\0';
- *env = outcome;
- outcome = NULL;
-
- return 0;
-}
-
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
- int r;
- _cleanup_free_ char *line = NULL;
- long unsigned ppid;
- const char *p;
-
- assert(pid >= 0);
- assert(_ppid);
-
- if (pid == 0) {
- *_ppid = getppid();
- return 0;
- }
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%lu ", /* ppid */
- &ppid) != 1)
- return -EIO;
-
- if ((long unsigned) (pid_t) ppid != ppid)
- return -ERANGE;
-
- *_ppid = (pid_t) ppid;
-
- return 0;
-}
-
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
- siginfo_t dummy;
-
- assert(pid >= 1);
-
- if (!status)
- status = &dummy;
-
- for (;;) {
- zero(*status);
-
- if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- return 0;
- }
-}
-
-/*
- * Return values:
- * < 0 : wait_for_terminate() failed to get the state of the
- * process, the process was terminated by a signal, or
- * failed for an unknown reason.
- * >=0 : The process terminated normally, and its exit code is
- * returned.
- *
- * That is, success is indicated by a return value of zero, and an
- * error is indicated by a non-zero value.
- *
- * A warning is emitted if the process terminates abnormally,
- * and also if it returns non-zero unless check_exit_code is true.
- */
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
- siginfo_t status;
-
- assert(name);
- assert(pid > 1);
-
- r = wait_for_terminate(pid, &status);
- if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
-
- if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
- else
- log_debug("%s succeeded.", name);
-
- return status.si_status;
- } else if (status.si_code == CLD_KILLED ||
- status.si_code == CLD_DUMPED) {
-
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
- return -EPROTO;
- }
-
- log_warning("%s failed due to unknown reason.", name);
- return -EPROTO;
-}
-
-int kill_and_sigcont(pid_t pid, int sig) {
- int r;
-
- r = kill(pid, sig) < 0 ? -errno : 0;
-
- if (r >= 0)
- kill(pid, SIGCONT);
-
- return r;
-}
-
-int getenv_for_pid(pid_t pid, const char *field, char **_value) {
- _cleanup_fclose_ FILE *f = NULL;
- char *value = NULL;
- int r;
- bool done = false;
- size_t l;
- const char *path;
-
- assert(pid >= 0);
- assert(field);
- assert(_value);
-
- path = procfs_file_alloca(pid, "environ");
-
- f = fopen(path, "re");
- if (!f)
- return -errno;
-
- l = strlen(field);
- r = 0;
-
- do {
- char line[LINE_MAX];
- unsigned i;
-
- for (i = 0; i < sizeof(line)-1; i++) {
- int c;
-
- c = getc(f);
- if (_unlikely_(c == EOF)) {
- done = true;
- break;
- } else if (c == 0)
- break;
-
- line[i] = c;
- }
- line[i] = 0;
-
- if (memcmp(line, field, l) == 0 && line[l] == '=') {
- value = strdup(line + l + 1);
- if (!value)
- return -ENOMEM;
-
- r = 1;
- break;
- }
-
- } while (!done);
-
- *_value = value;
- return r;
-}
-
-bool pid_is_unwaited(pid_t pid) {
- /* Checks whether a PID is still valid at all, including a zombie */
-
- if (pid <= 0)
- return false;
-
- if (kill(pid, 0) >= 0)
- return true;
-
- return errno != ESRCH;
-}
-
-bool pid_is_alive(pid_t pid) {
- int r;
-
- /* Checks whether a PID is still valid and not a zombie */
-
- if (pid <= 0)
- return false;
-
- r = get_process_state(pid);
- if (r == -ENOENT || r == 'Z')
- return false;
-
- return true;
-}
diff --git a/src/shared/process-util.h b/src/shared/process-util.h
deleted file mode 100644
index 07431d0..0000000
--- a/src/shared/process-util.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <sys/types.h>
-#include <alloca.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-
-#include "formats-util.h"
-
-#define procfs_file_alloca(pid, field) \
- ({ \
- pid_t _pid_ = (pid); \
- const char *_r_; \
- if (_pid_ == 0) { \
- _r_ = ("/proc/self/" field); \
- } else { \
- _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
- sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
- } \
- _r_; \
- })
-
-int get_process_state(pid_t pid);
-int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
-int get_process_exe(pid_t pid, char **name);
-int get_process_uid(pid_t pid, uid_t *uid);
-int get_process_gid(pid_t pid, gid_t *gid);
-int get_process_capeff(pid_t pid, char **capeff);
-int get_process_cwd(pid_t pid, char **cwd);
-int get_process_root(pid_t pid, char **root);
-int get_process_environ(pid_t pid, char **environ);
-
-int wait_for_terminate(pid_t pid, siginfo_t *status);
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
-
-int kill_and_sigcont(pid_t pid, int sig);
-pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
-void rename_process(const char name[8]);
-int is_kernel_thread(pid_t pid);
-int getenv_for_pid(pid_t pid, const char *field, char **_value);
-
-bool pid_is_alive(pid_t pid);
-bool pid_is_unwaited(pid_t pid);
diff --git a/src/shared/random-util.c b/src/shared/random-util.c
deleted file mode 100644
index b230044..0000000
--- a/src/shared/random-util.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdint.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <time.h>
-#ifdef HAVE_SYS_AUXV_H
-#include <sys/auxv.h>
-#endif
-#include <linux/random.h>
-
-#include "random-util.h"
-#include "time-util.h"
-#include "missing.h"
-#include "util.h"
-
-int dev_urandom(void *p, size_t n) {
- static int have_syscall = -1;
-
- _cleanup_close_ int fd = -1;
- int r;
-
- /* Gathers some randomness from the kernel. This call will
- * never block, and will always return some data from the
- * kernel, regardless if the random pool is fully initialized
- * or not. It thus makes no guarantee for the quality of the
- * returned entropy, but is good enough for or usual usecases
- * of seeding the hash functions for hashtable */
-
- /* Use the getrandom() syscall unless we know we don't have
- * it, or when the requested size is too large for it. */
- if (have_syscall != 0 || (size_t) (int) n != n) {
- r = getrandom(p, n, GRND_NONBLOCK);
- if (r == (int) n) {
- have_syscall = true;
- return 0;
- }
-
- if (r < 0) {
- if (errno == ENOSYS)
- /* we lack the syscall, continue with
- * reading from /dev/urandom */
- have_syscall = false;
- else if (errno == EAGAIN)
- /* not enough entropy for now. Let's
- * remember to use the syscall the
- * next time, again, but also read
- * from /dev/urandom for now, which
- * doesn't care about the current
- * amount of entropy. */
- have_syscall = true;
- else
- return -errno;
- } else
- /* too short read? */
- return -ENODATA;
- }
-
- fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return errno == ENOENT ? -ENOSYS : -errno;
-
- return loop_read_exact(fd, p, n, true);
-}
-
-void initialize_srand(void) {
- static bool srand_called = false;
- unsigned x;
-#ifdef HAVE_SYS_AUXV_H
- void *auxv;
-#endif
-
- if (srand_called)
- return;
-
- x = 0;
-
-#ifdef HAVE_SYS_AUXV_H
- /* The kernel provides us with a bit of entropy in auxv, so
- * let's try to make use of that to seed the pseudo-random
- * generator. It's better than nothing... */
-
- auxv = (void*) getauxval(AT_RANDOM);
- if (auxv)
- x ^= *(unsigned*) auxv;
-#endif
-
- x ^= (unsigned) now(CLOCK_REALTIME);
- x ^= (unsigned) gettid();
-
- srand(x);
- srand_called = true;
-}
-
-void random_bytes(void *p, size_t n) {
- uint8_t *q;
- int r;
-
- r = dev_urandom(p, n);
- if (r >= 0)
- return;
-
- /* If some idiot made /dev/urandom unavailable to us, he'll
- * get a PRNG instead. */
-
- initialize_srand();
-
- for (q = p; q < (uint8_t*) p + n; q ++)
- *q = rand();
-}
diff --git a/src/shared/random-util.h b/src/shared/random-util.h
deleted file mode 100644
index f7862c8..0000000
--- a/src/shared/random-util.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdint.h>
-
-int dev_urandom(void *p, size_t n);
-void random_bytes(void *p, size_t n);
-void initialize_srand(void);
-
-static inline uint64_t random_u64(void) {
- uint64_t u;
- random_bytes(&u, sizeof(u));
- return u;
-}
-
-static inline uint32_t random_u32(void) {
- uint32_t u;
- random_bytes(&u, sizeof(u));
- return u;
-}
diff --git a/src/shared/ratelimit.c b/src/shared/ratelimit.c
deleted file mode 100644
index 81fc9c1..0000000
--- a/src/shared/ratelimit.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "ratelimit.h"
-
-/* Modelled after Linux' lib/ratelimit.c by Dave Young
- * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
-
-bool ratelimit_test(RateLimit *r) {
- usec_t ts;
-
- assert(r);
-
- if (r->interval <= 0 || r->burst <= 0)
- return true;
-
- ts = now(CLOCK_MONOTONIC);
-
- if (r->begin <= 0 ||
- r->begin + r->interval < ts) {
- r->begin = ts;
-
- /* Reset counter */
- r->num = 0;
- goto good;
- }
-
- if (r->num < r->burst)
- goto good;
-
- return false;
-
-good:
- r->num++;
- return true;
-}
diff --git a/src/shared/ratelimit.h b/src/shared/ratelimit.h
deleted file mode 100644
index 58efca7..0000000
--- a/src/shared/ratelimit.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "util.h"
-
-typedef struct RateLimit {
- usec_t interval;
- usec_t begin;
- unsigned burst;
- unsigned num;
-} RateLimit;
-
-#define RATELIMIT_DEFINE(_name, _interval, _burst) \
- RateLimit _name = { \
- .interval = (_interval), \
- .burst = (_burst), \
- .num = 0, \
- .begin = 0 \
- }
-
-#define RATELIMIT_INIT(v, _interval, _burst) \
- do { \
- RateLimit *_r = &(v); \
- _r->interval = (_interval); \
- _r->burst = (_burst); \
- _r->num = 0; \
- _r->begin = 0; \
- } while (false)
-
-#define RATELIMIT_RESET(v) \
- do { \
- RateLimit *_r = &(v); \
- _r->num = 0; \
- _r->begin = 0; \
- } while (false)
-
-bool ratelimit_test(RateLimit *r);
diff --git a/src/shared/refcnt.h b/src/shared/refcnt.h
deleted file mode 100644
index 0502c20..0000000
--- a/src/shared/refcnt.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/>.
-***/
-
-/* A type-safe atomic refcounter */
-
-typedef struct {
- volatile unsigned _value;
-} RefCount;
-
-#define REFCNT_GET(r) ((r)._value)
-#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
-#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
-
-#define REFCNT_INIT ((RefCount) { ._value = 1 })
diff --git a/src/shared/replace-var.c b/src/shared/replace-var.c
deleted file mode 100644
index 478fc43..0000000
--- a/src/shared/replace-var.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 <string.h>
-
-#include "macro.h"
-#include "util.h"
-#include "replace-var.h"
-#include "def.h"
-
-/*
- * Generic infrastructure for replacing @FOO@ style variables in
- * strings. Will call a callback for each replacement.
- */
-
-static int get_variable(const char *b, char **r) {
- size_t k;
- char *t;
-
- assert(b);
- assert(r);
-
- if (*b != '@')
- return 0;
-
- k = strspn(b + 1, UPPERCASE_LETTERS "_");
- if (k <= 0 || b[k+1] != '@')
- return 0;
-
- t = strndup(b + 1, k);
- if (!t)
- return -ENOMEM;
-
- *r = t;
- return 1;
-}
-
-char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) {
- char *r, *t;
- const char *f;
- size_t l;
-
- assert(text);
- assert(lookup);
-
- l = strlen(text);
- r = new(char, l+1);
- if (!r)
- return NULL;
-
- f = text;
- t = r;
- while (*f) {
- _cleanup_free_ char *v = NULL, *n = NULL;
- char *a;
- int k;
- size_t skip, d, nl;
-
- k = get_variable(f, &v);
- if (k < 0)
- goto oom;
- if (k == 0) {
- *(t++) = *(f++);
- continue;
- }
-
- n = lookup(v, userdata);
- if (!n)
- goto oom;
-
- skip = strlen(v) + 2;
-
- d = t - r;
- nl = l - skip + strlen(n);
- a = realloc(r, nl + 1);
- if (!a)
- goto oom;
-
- l = nl;
- r = a;
- t = r + d;
-
- t = stpcpy(t, n);
- f += skip;
- }
-
- *t = 0;
- return r;
-
-oom:
- free(r);
- return NULL;
-}
diff --git a/src/shared/replace-var.h b/src/shared/replace-var.h
deleted file mode 100644
index 7eaee93..0000000
--- a/src/shared/replace-var.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- 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/>.
-***/
-
-char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata);
diff --git a/src/shared/ring.c b/src/shared/ring.c
deleted file mode 100644
index 6814918..0000000
--- a/src/shared/ring.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
-
- 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 <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
-#include "macro.h"
-#include "ring.h"
-
-#define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1))
-
-void ring_flush(Ring *r) {
- assert(r);
-
- r->start = 0;
- r->used = 0;
-}
-
-void ring_clear(Ring *r) {
- assert(r);
-
- free(r->buf);
- zero(*r);
-}
-
-/*
- * Get data pointers for current ring-buffer data. @vec must be an array of 2
- * iovec objects. They are filled according to the data available in the
- * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects
- * that were filled (0 meaning buffer is empty).
- *
- * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this:
- * struct iovec {
- * void *iov_base;
- * size_t iov_len;
- * };
- */
-size_t ring_peek(Ring *r, struct iovec *vec) {
- assert(r);
-
- if (r->used == 0) {
- return 0;
- } else if (r->start + r->used <= r->size) {
- if (vec) {
- vec[0].iov_base = &r->buf[r->start];
- vec[0].iov_len = r->used;
- }
- return 1;
- } else {
- if (vec) {
- vec[0].iov_base = &r->buf[r->start];
- vec[0].iov_len = r->size - r->start;
- vec[1].iov_base = r->buf;
- vec[1].iov_len = r->used - (r->size - r->start);
- }
- return 2;
- }
-}
-
-/*
- * Copy data from the ring buffer into the linear external buffer @buf. Copy
- * at most @size bytes. If the ring buffer size is smaller, copy less bytes and
- * return the number of bytes copied.
- */
-size_t ring_copy(Ring *r, void *buf, size_t size) {
- size_t l;
-
- assert(r);
- assert(buf);
-
- if (size > r->used)
- size = r->used;
-
- if (size > 0) {
- l = r->size - r->start;
- if (size <= l) {
- memcpy(buf, &r->buf[r->start], size);
- } else {
- memcpy(buf, &r->buf[r->start], l);
- memcpy((uint8_t*)buf + l, r->buf, size - l);
- }
- }
-
- return size;
-}
-
-/*
- * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise
- * ring operations will behave incorrectly.
- */
-static int ring_resize(Ring *r, size_t nsize) {
- uint8_t *buf;
- size_t l;
-
- assert(r);
- assert(nsize > 0);
-
- buf = malloc(nsize);
- if (!buf)
- return -ENOMEM;
-
- if (r->used > 0) {
- l = r->size - r->start;
- if (r->used <= l) {
- memcpy(buf, &r->buf[r->start], r->used);
- } else {
- memcpy(buf, &r->buf[r->start], l);
- memcpy(&buf[l], r->buf, r->used - l);
- }
- }
-
- free(r->buf);
- r->buf = buf;
- r->size = nsize;
- r->start = 0;
-
- return 0;
-}
-
-/*
- * Resize ring-buffer to provide enough room for @add bytes of new data. This
- * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on
- * success.
- */
-static int ring_grow(Ring *r, size_t add) {
- size_t need;
-
- assert(r);
-
- if (r->size - r->used >= add)
- return 0;
-
- need = r->used + add;
- if (need <= r->used)
- return -ENOMEM;
- else if (need < 4096)
- need = 4096;
-
- need = ALIGN_POWER2(need);
- if (need == 0)
- return -ENOMEM;
-
- return ring_resize(r, need);
-}
-
-/*
- * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it
- * is too small. -ENOMEM is returned on OOM, 0 on success.
- */
-int ring_push(Ring *r, const void *u8, size_t size) {
- int err;
- size_t pos, l;
-
- assert(r);
- assert(u8);
-
- if (size == 0)
- return 0;
-
- err = ring_grow(r, size);
- if (err < 0)
- return err;
-
- pos = RING_MASK(r, r->start + r->used);
- l = r->size - pos;
- if (l >= size) {
- memcpy(&r->buf[pos], u8, size);
- } else {
- memcpy(&r->buf[pos], u8, l);
- memcpy(r->buf, (const uint8_t*)u8 + l, size - l);
- }
-
- r->used += size;
-
- return 0;
-}
-
-/*
- * Remove @len bytes from the start of the ring-buffer. Note that we protect
- * against overflows so removing more bytes than available is safe.
- */
-void ring_pull(Ring *r, size_t size) {
- assert(r);
-
- if (size > r->used)
- size = r->used;
-
- r->start = RING_MASK(r, r->start + size);
- r->used -= size;
-}
diff --git a/src/shared/ring.h b/src/shared/ring.h
deleted file mode 100644
index a7c44d1..0000000
--- a/src/shared/ring.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann at gmail.com>
-
- 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/>.
-***/
-
-
-typedef struct Ring Ring;
-
-struct Ring {
- uint8_t *buf; /* buffer or NULL */
- size_t size; /* actual size of @buf */
- size_t start; /* start position of ring */
- size_t used; /* number of actually used bytes */
-};
-
-/* flush buffer so it is empty again */
-void ring_flush(Ring *r);
-
-/* flush buffer, free allocated data and reset to initial state */
-void ring_clear(Ring *r);
-
-/* get pointers to buffer data and their length */
-size_t ring_peek(Ring *r, struct iovec *vec);
-
-/* copy data into external linear buffer */
-size_t ring_copy(Ring *r, void *buf, size_t size);
-
-/* push data to the end of the buffer */
-int ring_push(Ring *r, const void *u8, size_t size);
-
-/* pull data from the front of the buffer */
-void ring_pull(Ring *r, size_t size);
-
-/* return size of occupied buffer in bytes */
-static inline size_t ring_get_size(Ring *r)
-{
- return r->used;
-}
diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c
deleted file mode 100644
index bafd483..0000000
--- a/src/shared/rm-rf.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2015 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 "util.h"
-#include "path-util.h"
-#include "btrfs-util.h"
-#include "rm-rf.h"
-
-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- int ret = 0, r;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on. This closes the passed fd. */
-
- if (!(flags & REMOVE_PHYSICAL)) {
-
- r = fd_is_temporary_fs(fd);
- if (r < 0) {
- safe_close(fd);
- return r;
- }
-
- if (!r) {
- /* We refuse to clean physical file systems
- * with this call, unless explicitly
- * requested. This is extra paranoia just to
- * be sure we never ever remove non-state
- * data */
-
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
- }
-
- d = fdopendir(fd);
- if (!d) {
- safe_close(fd);
- return errno == ENOENT ? 0 : -errno;
- }
-
- for (;;) {
- struct dirent *de;
- bool is_dir;
- struct stat st;
-
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno != 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (de->d_type == DT_UNKNOWN ||
- (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
- } else
- is_dir = de->d_type == DT_DIR;
-
- if (is_dir) {
- int subdir_fd;
-
- /* if root_dev is set, remove subdirectories only if device is same */
- if (root_dev && st.st_dev != root_dev->st_dev)
- continue;
-
- subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- /* Stop at mount points */
- r = fd_is_mount_point(fd, de->d_name, 0);
- if (r < 0) {
- if (ret == 0 && r != -ENOENT)
- ret = r;
-
- safe_close(subdir_fd);
- continue;
- }
- if (r) {
- safe_close(subdir_fd);
- continue;
- }
-
- if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
-
- /* This could be a subvolume, try to remove it */
-
- r = btrfs_subvol_remove_fd(fd, de->d_name, true);
- if (r < 0) {
- if (r != -ENOTTY && r != -EINVAL) {
- if (ret == 0)
- ret = r;
-
- safe_close(subdir_fd);
- continue;
- }
-
- /* ENOTTY, then it wasn't a
- * btrfs subvolume, continue
- * below. */
- } else {
- /* It was a subvolume, continue. */
- safe_close(subdir_fd);
- continue;
- }
- }
-
- /* We pass REMOVE_PHYSICAL here, to avoid
- * doing the fstatfs() to check the file
- * system type again for each directory */
- r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
- if (r < 0 && ret == 0)
- ret = r;
-
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
-
- } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
-
- if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- }
- }
-}
-
-int rm_rf(const char *path, RemoveFlags flags) {
- int fd, r;
- struct statfs s;
-
- assert(path);
-
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
- if (path_equal(path, "/")) {
- log_error("Attempted to remove entire root file system, and we can't allow that.");
- return -EPERM;
- }
-
- if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
- /* Try to remove as subvolume first */
- r = btrfs_subvol_remove(path, true);
- if (r >= 0)
- return r;
-
- if (r != -ENOTTY && r != -EINVAL)
- return r;
-
- /* Not btrfs or not a subvolume */
- }
-
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (fd < 0) {
-
- if (errno != ENOTDIR && errno != ELOOP)
- return -errno;
-
- if (!(flags & REMOVE_PHYSICAL)) {
- if (statfs(path, &s) < 0)
- return -errno;
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- return -EPERM;
- }
- }
-
- if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
- if (unlink(path) < 0 && errno != ENOENT)
- return -errno;
-
- return 0;
- }
-
- r = rm_rf_children(fd, flags, NULL);
-
- if (flags & REMOVE_ROOT) {
- if (rmdir(path) < 0) {
- if (r == 0 && errno != ENOENT)
- r = -errno;
- }
- }
-
- return r;
-}
diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h
deleted file mode 100644
index 96579eb..0000000
--- a/src/shared/rm-rf.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 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/stat.h>
-
-typedef enum RemoveFlags {
- REMOVE_ONLY_DIRECTORIES = 1,
- REMOVE_ROOT = 2,
- REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */
- REMOVE_SUBVOLUME = 8,
-} RemoveFlags;
-
-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
-int rm_rf(const char *path, RemoveFlags flags);
diff --git a/src/shared/securebits.h b/src/shared/securebits.h
deleted file mode 100644
index 98fbe0d..0000000
--- a/src/shared/securebits.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _LINUX_SECUREBITS_H
-#define _LINUX_SECUREBITS_H 1
-
-/* This is minimal version of Linux' linux/securebits.h header file,
- * which is licensed GPL2 */
-
-#define SECUREBITS_DEFAULT 0x00000000
-
-/* When set UID 0 has no special privileges. When unset, we support
- inheritance of root-permissions and suid-root executable under
- compatibility mode. We raise the effective and inheritable bitmasks
- *of the executable file* if the effective uid of the new process is
- 0. If the real uid is 0, we raise the effective (legacy) bit of the
- executable file. */
-#define SECURE_NOROOT 0
-#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */
-
-/* When set, setuid to/from uid 0 does not trigger capability-"fixup".
- When unset, to provide compatibility with old programs relying on
- set*uid to gain/lose privilege, transitions to/from uid 0 cause
- capabilities to be gained/lost. */
-#define SECURE_NO_SETUID_FIXUP 2
-#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */
-
-/* When set, a process can retain its capabilities even after
- transitioning to a non-root user (the set-uid fixup suppressed by
- bit 2). Bit-4 is cleared when a process calls exec(); setting both
- bit 4 and 5 will create a barrier through exec that no exec()'d
- child can use this feature again. */
-#define SECURE_KEEP_CAPS 4
-#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */
-
-/* Each securesetting is implemented using two bits. One bit specifies
- whether the setting is on or off. The other bit specify whether the
- setting is locked or not. A setting which is locked cannot be
- changed from user-level. */
-#define issecure_mask(X) (1 << (X))
-#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
-
-#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
- issecure_mask(SECURE_NO_SETUID_FIXUP) | \
- issecure_mask(SECURE_KEEP_CAPS))
-#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
-
-#endif /* !_LINUX_SECUREBITS_H */
diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c
deleted file mode 100644
index 7c58985..0000000
--- a/src/shared/selinux-util.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <errno.h>
-#include <malloc.h>
-#include <sys/un.h>
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/context.h>
-#endif
-
-#include "strv.h"
-#include "path-util.h"
-#include "selinux-util.h"
-
-#ifdef HAVE_SELINUX
-DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
-DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
-
-#define _cleanup_security_context_free_ _cleanup_(freeconp)
-#define _cleanup_context_free_ _cleanup_(context_freep)
-
-static int cached_use = -1;
-static struct selabel_handle *label_hnd = NULL;
-
-#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
-#endif
-
-bool mac_selinux_use(void) {
-#ifdef HAVE_SELINUX
- if (cached_use < 0)
- cached_use = is_selinux_enabled() > 0;
-
- return cached_use;
-#else
- return false;
-#endif
-}
-
-void mac_selinux_retest(void) {
-#ifdef HAVE_SELINUX
- cached_use = -1;
-#endif
-}
-
-int mac_selinux_init(const char *prefix) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
- usec_t before_timestamp, after_timestamp;
- struct mallinfo before_mallinfo, after_mallinfo;
-
- if (!mac_selinux_use())
- return 0;
-
- if (label_hnd)
- return 0;
-
- before_mallinfo = mallinfo();
- before_timestamp = now(CLOCK_MONOTONIC);
-
- if (prefix) {
- struct selinux_opt options[] = {
- { .type = SELABEL_OPT_SUBSET, .value = prefix },
- };
-
- label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
- } else
- label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
-
- if (!label_hnd) {
- log_enforcing("Failed to initialize SELinux context: %m");
- r = security_getenforce() == 1 ? -errno : 0;
- } else {
- char timespan[FORMAT_TIMESPAN_MAX];
- int l;
-
- after_timestamp = now(CLOCK_MONOTONIC);
- after_mallinfo = mallinfo();
-
- l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
-
- log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
- format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
- (l+1023)/1024);
- }
-#endif
-
- return r;
-}
-
-void mac_selinux_finish(void) {
-
-#ifdef HAVE_SELINUX
- if (!label_hnd)
- return;
-
- selabel_close(label_hnd);
- label_hnd = NULL;
-#endif
-}
-
-int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
-
-#ifdef HAVE_SELINUX
- struct stat st;
- int r;
-
- assert(path);
-
- /* if mac_selinux_init() wasn't called before we are a NOOP */
- if (!label_hnd)
- return 0;
-
- r = lstat(path, &st);
- if (r >= 0) {
- _cleanup_security_context_free_ security_context_t fcon = NULL;
-
- r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
-
- /* If there's no label to set, then exit without warning */
- if (r < 0 && errno == ENOENT)
- return 0;
-
- if (r >= 0) {
- r = lsetfilecon(path, fcon);
-
- /* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == EOPNOTSUPP)
- return 0;
- }
- }
-
- if (r < 0) {
- /* Ignore ENOENT in some cases */
- if (ignore_enoent && errno == ENOENT)
- return 0;
-
- if (ignore_erofs && errno == EROFS)
- return 0;
-
- log_enforcing("Unable to fix SELinux security context of %s: %m", path);
- if (security_getenforce() == 1)
- return -errno;
- }
-#endif
-
- return 0;
-}
-
-int mac_selinux_apply(const char *path, const char *label) {
-
-#ifdef HAVE_SELINUX
- assert(path);
- assert(label);
-
- if (!mac_selinux_use())
- return 0;
-
- if (setfilecon(path, (security_context_t) label) < 0) {
- log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
- if (security_getenforce() == 1)
- return -errno;
- }
-#endif
- return 0;
-}
-
-int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
- int r = -EOPNOTSUPP;
-
-#ifdef HAVE_SELINUX
- _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
- security_class_t sclass;
-
- assert(exe);
- assert(label);
-
- if (!mac_selinux_use())
- return -EOPNOTSUPP;
-
- r = getcon(&mycon);
- if (r < 0)
- return -errno;
-
- r = getfilecon(exe, &fcon);
- if (r < 0)
- return -errno;
-
- sclass = string_to_security_class("process");
- r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-int mac_selinux_get_our_label(char **label) {
- int r = -EOPNOTSUPP;
-
- assert(label);
-
-#ifdef HAVE_SELINUX
- if (!mac_selinux_use())
- return -EOPNOTSUPP;
-
- r = getcon(label);
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
- int r = -EOPNOTSUPP;
-
-#ifdef HAVE_SELINUX
- _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
- _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
- security_class_t sclass;
- const char *range = NULL;
-
- assert(socket_fd >= 0);
- assert(exe);
- assert(label);
-
- if (!mac_selinux_use())
- return -EOPNOTSUPP;
-
- r = getcon(&mycon);
- if (r < 0)
- return -errno;
-
- r = getpeercon(socket_fd, &peercon);
- if (r < 0)
- return -errno;
-
- if (!exec_label) {
- /* If there is no context set for next exec let's use context
- of target executable */
- r = getfilecon(exe, &fcon);
- if (r < 0)
- return -errno;
- }
-
- bcon = context_new(mycon);
- if (!bcon)
- return -ENOMEM;
-
- pcon = context_new(peercon);
- if (!pcon)
- return -ENOMEM;
-
- range = context_range_get(pcon);
- if (!range)
- return -errno;
-
- r = context_range_set(bcon, range);
- if (r)
- return -errno;
-
- freecon(mycon);
- mycon = strdup(context_str(bcon));
- if (!mycon)
- return -ENOMEM;
-
- sclass = string_to_security_class("process");
- r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-void mac_selinux_free(char *label) {
-
-#ifdef HAVE_SELINUX
- if (!mac_selinux_use())
- return;
-
- freecon((security_context_t) label);
-#endif
-}
-
-int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
- _cleanup_security_context_free_ security_context_t filecon = NULL;
-
- assert(path);
-
- if (!label_hnd)
- return 0;
-
- if (path_is_absolute(path))
- r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
- else {
- _cleanup_free_ char *newpath;
-
- newpath = path_make_absolute_cwd(path);
- if (!newpath)
- return -ENOMEM;
-
- r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
- }
-
- /* No context specified by the policy? Proceed without setting it. */
- if (r < 0 && errno == ENOENT)
- return 0;
-
- if (r < 0)
- r = -errno;
- else {
- r = setfscreatecon(filecon);
- if (r < 0) {
- log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
- r = -errno;
- }
- }
-
- if (r < 0 && security_getenforce() == 0)
- r = 0;
-#endif
-
- return r;
-}
-
-void mac_selinux_create_file_clear(void) {
-
-#ifdef HAVE_SELINUX
- PROTECT_ERRNO;
-
- if (!mac_selinux_use())
- return;
-
- setfscreatecon(NULL);
-#endif
-}
-
-int mac_selinux_create_socket_prepare(const char *label) {
-
-#ifdef HAVE_SELINUX
- if (!mac_selinux_use())
- return 0;
-
- assert(label);
-
- if (setsockcreatecon((security_context_t) label) < 0) {
- log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
-
- if (security_getenforce() == 1)
- return -errno;
- }
-#endif
-
- return 0;
-}
-
-void mac_selinux_create_socket_clear(void) {
-
-#ifdef HAVE_SELINUX
- PROTECT_ERRNO;
-
- if (!mac_selinux_use())
- return;
-
- setsockcreatecon(NULL);
-#endif
-}
-
-int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
-
- /* Binds a socket and label its file system object according to the SELinux policy */
-
-#ifdef HAVE_SELINUX
- _cleanup_security_context_free_ security_context_t fcon = NULL;
- const struct sockaddr_un *un;
- char *path;
- int r;
-
- assert(fd >= 0);
- assert(addr);
- assert(addrlen >= sizeof(sa_family_t));
-
- if (!label_hnd)
- goto skipped;
-
- /* Filter out non-local sockets */
- if (addr->sa_family != AF_UNIX)
- goto skipped;
-
- /* Filter out anonymous sockets */
- if (addrlen < sizeof(sa_family_t) + 1)
- goto skipped;
-
- /* Filter out abstract namespace sockets */
- un = (const struct sockaddr_un*) addr;
- if (un->sun_path[0] == 0)
- goto skipped;
-
- path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
-
- if (path_is_absolute(path))
- r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
- else {
- _cleanup_free_ char *newpath;
-
- newpath = path_make_absolute_cwd(path);
- if (!newpath)
- return -ENOMEM;
-
- r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
- }
-
- if (r == 0)
- r = setfscreatecon(fcon);
-
- if (r < 0 && errno != ENOENT) {
- log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
-
- if (security_getenforce() == 1) {
- r = -errno;
- goto finish;
- }
- }
-
- r = bind(fd, addr, addrlen);
- if (r < 0)
- r = -errno;
-
-finish:
- setfscreatecon(NULL);
- return r;
-
-skipped:
-#endif
- return bind(fd, addr, addrlen) < 0 ? -errno : 0;
-}
diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h
deleted file mode 100644
index 8467185..0000000
--- a/src/shared/selinux-util.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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/socket.h>
-#include <stdbool.h>
-
-bool mac_selinux_use(void);
-void mac_selinux_retest(void);
-
-int mac_selinux_init(const char *prefix);
-void mac_selinux_finish(void);
-
-int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
-int mac_selinux_apply(const char *path, const char *label);
-
-int mac_selinux_get_create_label_from_exe(const char *exe, char **label);
-int mac_selinux_get_our_label(char **label);
-int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label);
-void mac_selinux_free(char *label);
-
-int mac_selinux_create_file_prepare(const char *path, mode_t mode);
-void mac_selinux_create_file_clear(void);
-
-int mac_selinux_create_socket_prepare(const char *label);
-void mac_selinux_create_socket_clear(void);
-
-int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
diff --git a/src/shared/set.h b/src/shared/set.h
deleted file mode 100644
index 4dffecd..0000000
--- a/src/shared/set.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 "hashmap.h"
-#include "macro.h"
-
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
-
-
-static inline void set_free(Set *s) {
- internal_hashmap_free(HASHMAP_BASE(s));
-}
-
-static inline void set_free_free(Set *s) {
- internal_hashmap_free_free(HASHMAP_BASE(s));
-}
-
-/* no set_free_free_free */
-
-static inline Set *set_copy(Set *s) {
- return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
-}
-
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
-
-int set_put(Set *s, const void *key);
-/* no set_update */
-/* no set_replace */
-static inline void *set_get(Set *s, void *key) {
- return internal_hashmap_get(HASHMAP_BASE(s), key);
-}
-/* no set_get2 */
-
-static inline bool set_contains(Set *s, const void *key) {
- return internal_hashmap_contains(HASHMAP_BASE(s), key);
-}
-
-static inline void *set_remove(Set *s, const void *key) {
- return internal_hashmap_remove(HASHMAP_BASE(s), key);
-}
-
-/* no set_remove2 */
-/* no set_remove_value */
-int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
-/* no set_remove_and_replace */
-int set_merge(Set *s, Set *other);
-
-static inline int set_reserve(Set *h, unsigned entries_add) {
- return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
-}
-
-static inline int set_move(Set *s, Set *other) {
- return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
-}
-
-static inline int set_move_one(Set *s, Set *other, const void *key) {
- return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
-}
-
-static inline unsigned set_size(Set *s) {
- return internal_hashmap_size(HASHMAP_BASE(s));
-}
-
-static inline bool set_isempty(Set *s) {
- return set_size(s) == 0;
-}
-
-static inline unsigned set_buckets(Set *s) {
- return internal_hashmap_buckets(HASHMAP_BASE(s));
-}
-
-void *set_iterate(Set *s, Iterator *i);
-
-static inline void set_clear(Set *s) {
- internal_hashmap_clear(HASHMAP_BASE(s));
-}
-
-static inline void set_clear_free(Set *s) {
- internal_hashmap_clear_free(HASHMAP_BASE(s));
-}
-
-/* no set_clear_free_free */
-
-static inline void *set_steal_first(Set *s) {
- return internal_hashmap_steal_first(HASHMAP_BASE(s));
-}
-
-/* no set_steal_first_key */
-/* no set_first_key */
-
-static inline void *set_first(Set *s) {
- return internal_hashmap_first(HASHMAP_BASE(s));
-}
-
-/* no set_next */
-
-static inline char **set_get_strv(Set *s) {
- return internal_hashmap_get_strv(HASHMAP_BASE(s));
-}
-
-int set_consume(Set *s, void *value);
-int set_put_strdup(Set *s, const char *p);
-int set_put_strdupv(Set *s, char **l);
-
-#define SET_FOREACH(e, s, i) \
- for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
-
-#define _cleanup_set_free_ _cleanup_(set_freep)
-#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
diff --git a/src/shared/sigbus.c b/src/shared/sigbus.c
deleted file mode 100644
index 0108603..0000000
--- a/src/shared/sigbus.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 <signal.h>
-#include <sys/mman.h>
-
-#include "macro.h"
-#include "util.h"
-#include "sigbus.h"
-
-#define SIGBUS_QUEUE_MAX 64
-
-static struct sigaction old_sigaction;
-static unsigned n_installed = 0;
-
-/* We maintain a fixed size list of page addresses that triggered a
- SIGBUS. We access with list with atomic operations, so that we
- don't have to deal with locks between signal handler and main
- programs in possibly multiple threads. */
-
-static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX];
-static volatile sig_atomic_t n_sigbus_queue = 0;
-
-static void sigbus_push(void *addr) {
- unsigned u;
-
- assert(addr);
-
- /* Find a free place, increase the number of entries and leave, if we can */
- for (u = 0; u < SIGBUS_QUEUE_MAX; u++)
- if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
- __sync_fetch_and_add(&n_sigbus_queue, 1);
- return;
- }
-
- /* If we can't, make sure the queue size is out of bounds, to
- * mark it as overflow */
- for (;;) {
- unsigned c;
-
- __sync_synchronize();
- c = n_sigbus_queue;
-
- if (c > SIGBUS_QUEUE_MAX) /* already overflow */
- return;
-
- if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX))
- return;
- }
-}
-
-int sigbus_pop(void **ret) {
- assert(ret);
-
- for (;;) {
- unsigned u, c;
-
- __sync_synchronize();
- c = n_sigbus_queue;
-
- if (_likely_(c == 0))
- return 0;
-
- if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
- return -EOVERFLOW;
-
- for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
- void *addr;
-
- addr = sigbus_queue[u];
- if (!addr)
- continue;
-
- if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
- __sync_fetch_and_sub(&n_sigbus_queue, 1);
- *ret = addr;
- return 1;
- }
- }
- }
-}
-
-static void sigbus_handler(int sn, siginfo_t *si, void *data) {
- unsigned long ul;
- void *aligned;
-
- assert(sn == SIGBUS);
- assert(si);
-
- if (si->si_code != BUS_ADRERR || !si->si_addr) {
- assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
- raise(SIGBUS);
- return;
- }
-
- ul = (unsigned long) si->si_addr;
- ul = ul / page_size();
- ul = ul * page_size();
- aligned = (void*) ul;
-
- /* Let's remember which address failed */
- sigbus_push(aligned);
-
- /* Replace mapping with an anonymous page, so that the
- * execution can continue, however with a zeroed out page */
- assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned);
-}
-
-void sigbus_install(void) {
- struct sigaction sa = {
- .sa_sigaction = sigbus_handler,
- .sa_flags = SA_SIGINFO,
- };
-
- n_installed++;
-
- if (n_installed == 1)
- assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0);
-
- return;
-}
-
-void sigbus_reset(void) {
-
- if (n_installed <= 0)
- return;
-
- n_installed--;
-
- if (n_installed == 0)
- assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0);
-
- return;
-}
diff --git a/src/shared/sigbus.h b/src/shared/sigbus.h
deleted file mode 100644
index 23edc6d..0000000
--- a/src/shared/sigbus.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-#pragma once
-
-void sigbus_install(void);
-void sigbus_reset(void);
-
-int sigbus_pop(void **ret);
diff --git a/src/shared/signal-util.c b/src/shared/signal-util.c
deleted file mode 100644
index 84cf42b..0000000
--- a/src/shared/signal-util.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2015 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 "util.h"
-#include "signal-util.h"
-
-int reset_all_signal_handlers(void) {
- static const struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
- int sig, r = 0;
-
- for (sig = 1; sig < _NSIG; sig++) {
-
- /* These two cannot be caught... */
- if (sig == SIGKILL || sig == SIGSTOP)
- continue;
-
- /* On Linux the first two RT signals are reserved by
- * glibc, and sigaction() will return EINVAL for them. */
- if ((sigaction(sig, &sa, NULL) < 0))
- if (errno != EINVAL && r >= 0)
- r = -errno;
- }
-
- return r;
-}
-
-int reset_signal_mask(void) {
- sigset_t ss;
-
- if (sigemptyset(&ss) < 0)
- return -errno;
-
- if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
-static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) {
- int r = 0;
-
- /* negative signal ends the list. 0 signal is skipped. */
-
- if (sig < 0)
- return 0;
-
- if (sig > 0) {
- if (sigaction(sig, sa, NULL) < 0)
- r = -errno;
- }
-
- while ((sig = va_arg(ap, int)) >= 0) {
-
- if (sig == 0)
- continue;
-
- if (sigaction(sig, sa, NULL) < 0) {
- if (r >= 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int sigaction_many(const struct sigaction *sa, ...) {
- va_list ap;
- int r;
-
- va_start(ap, sa);
- r = sigaction_many_ap(sa, 0, ap);
- va_end(ap);
-
- return r;
-}
-
-int ignore_signals(int sig, ...) {
-
- static const struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
-
- va_list ap;
- int r;
-
- va_start(ap, sig);
- r = sigaction_many_ap(&sa, sig, ap);
- va_end(ap);
-
- return r;
-}
-
-int default_signals(int sig, ...) {
-
- static const struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
-
- va_list ap;
- int r;
-
- va_start(ap, sig);
- r = sigaction_many_ap(&sa, sig, ap);
- va_end(ap);
-
- return r;
-}
-
-static int sigset_add_many_ap(sigset_t *ss, va_list ap) {
- int sig, r = 0;
-
- assert(ss);
-
- while ((sig = va_arg(ap, int)) >= 0) {
-
- if (sig == 0)
- continue;
-
- if (sigaddset(ss, sig) < 0) {
- if (r >= 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int sigset_add_many(sigset_t *ss, ...) {
- va_list ap;
- int r;
-
- va_start(ap, ss);
- r = sigset_add_many_ap(ss, ap);
- va_end(ap);
-
- return r;
-}
-
-int sigprocmask_many(int how, ...) {
- va_list ap;
- sigset_t ss;
- int r;
-
- if (sigemptyset(&ss) < 0)
- return -errno;
-
- va_start(ap, how);
- r = sigset_add_many_ap(&ss, ap);
- va_end(ap);
-
- if (r < 0)
- return r;
-
- if (sigprocmask(how, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
-static const char *const __signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
- [SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
- static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
- const char *name;
-
- name = __signal_to_string(signo);
- if (name)
- return name;
-
- if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
- else
- snprintf(buf, sizeof(buf), "%d", signo);
-
- return buf;
-}
-
-int signal_from_string(const char *s) {
- int signo;
- int offset = 0;
- unsigned u;
-
- signo = __signal_from_string(s);
- if (signo > 0)
- return signo;
-
- if (startswith(s, "RTMIN+")) {
- s += 6;
- offset = SIGRTMIN;
- }
- if (safe_atou(s, &u) >= 0) {
- signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
- return signo;
- }
- return -EINVAL;
-}
-
-int signal_from_string_try_harder(const char *s) {
- int signo;
- assert(s);
-
- signo = signal_from_string(s);
- if (signo <= 0)
- if (startswith(s, "SIG"))
- return signal_from_string(s+3);
-
- return signo;
-}
diff --git a/src/shared/signal-util.h b/src/shared/signal-util.h
deleted file mode 100644
index 9dc8a28..0000000
--- a/src/shared/signal-util.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010-2015 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 <signal.h>
-
-#include "macro.h"
-
-int reset_all_signal_handlers(void);
-int reset_signal_mask(void);
-
-int ignore_signals(int sig, ...);
-int default_signals(int sig, ...);
-int sigaction_many(const struct sigaction *sa, ...);
-
-int sigset_add_many(sigset_t *ss, ...);
-int sigprocmask_many(int how, ...);
-
-const char *signal_to_string(int i) _const_;
-int signal_from_string(const char *s) _pure_;
-
-int signal_from_string_try_harder(const char *s);
diff --git a/src/shared/siphash24.c b/src/shared/siphash24.c
deleted file mode 100644
index f68bd28..0000000
--- a/src/shared/siphash24.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- SipHash reference C implementation
-
- Written in 2012 by
- Jean-Philippe Aumasson <jeanphilippe.aumasson at gmail.com>
- Daniel J. Bernstein <djb at cr.yp.to>
-
- To the extent possible under law, the author(s) have dedicated all copyright
- and related and neighboring rights to this software to the public domain
- worldwide. This software is distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along with
- this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
-
- (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
-*/
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "siphash24.h"
-
-typedef uint64_t u64;
-typedef uint32_t u32;
-typedef uint8_t u8;
-
-#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
-
-#define U32TO8_LE(p, v) \
- (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
- (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
-
-#define U64TO8_LE(p, v) \
- U32TO8_LE((p), (u32)((v) )); \
- U32TO8_LE((p) + 4, (u32)((v) >> 32));
-
-#define U8TO64_LE(p) \
- (((u64)((p)[0]) ) | \
- ((u64)((p)[1]) << 8) | \
- ((u64)((p)[2]) << 16) | \
- ((u64)((p)[3]) << 24) | \
- ((u64)((p)[4]) << 32) | \
- ((u64)((p)[5]) << 40) | \
- ((u64)((p)[6]) << 48) | \
- ((u64)((p)[7]) << 56))
-
-#define SIPROUND \
- do { \
- v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
- v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
- v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
- v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
- } while(0)
-
-/* SipHash-2-4 */
-void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
-{
- /* "somepseudorandomlygeneratedbytes" */
- u64 v0 = 0x736f6d6570736575ULL;
- u64 v1 = 0x646f72616e646f6dULL;
- u64 v2 = 0x6c7967656e657261ULL;
- u64 v3 = 0x7465646279746573ULL;
- u64 b;
- u64 k0 = U8TO64_LE( k );
- u64 k1 = U8TO64_LE( k + 8 );
- u64 m;
- const u8 *in = _in;
- const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
- const int left = inlen & 7;
- b = ( ( u64 )inlen ) << 56;
- v3 ^= k1;
- v2 ^= k0;
- v1 ^= k1;
- v0 ^= k0;
-
- for ( ; in != end; in += 8 )
- {
- m = U8TO64_LE( in );
-#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
- printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
-#endif
- v3 ^= m;
- SIPROUND;
- SIPROUND;
- v0 ^= m;
- }
-
- switch( left )
- {
- case 7: b |= ( ( u64 )in[ 6] ) << 48;
-
- case 6: b |= ( ( u64 )in[ 5] ) << 40;
-
- case 5: b |= ( ( u64 )in[ 4] ) << 32;
-
- case 4: b |= ( ( u64 )in[ 3] ) << 24;
-
- case 3: b |= ( ( u64 )in[ 2] ) << 16;
-
- case 2: b |= ( ( u64 )in[ 1] ) << 8;
-
- case 1: b |= ( ( u64 )in[ 0] ); break;
-
- case 0: break;
- }
-
-#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
- printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
-#endif
- v3 ^= b;
- SIPROUND;
- SIPROUND;
- v0 ^= b;
-#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
-#endif
- v2 ^= 0xff;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- b = v0 ^ v1 ^ v2 ^ v3;
- U64TO8_LE( out, b );
-}
diff --git a/src/shared/siphash24.h b/src/shared/siphash24.h
deleted file mode 100644
index 62e1168..0000000
--- a/src/shared/siphash24.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-#include <inttypes.h>
-#include <sys/types.h>
-
-void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c
deleted file mode 100644
index 2e24b1e..0000000
--- a/src/shared/smack-util.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Intel Corporation
-
- Author: Auke Kok <auke-jan.h.kok at intel.com>
-
- 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/xattr.h>
-
-#include "util.h"
-#include "process-util.h"
-#include "path-util.h"
-#include "fileio.h"
-#include "smack-util.h"
-
-#define SMACK_FLOOR_LABEL "_"
-#define SMACK_STAR_LABEL "*"
-
-bool mac_smack_use(void) {
-#ifdef HAVE_SMACK
- static int cached_use = -1;
-
- if (cached_use < 0)
- cached_use = access("/sys/fs/smackfs/", F_OK) >= 0;
-
- return cached_use;
-#else
- return false;
-#endif
-}
-
-int mac_smack_apply(const char *path, const char *label) {
- int r = 0;
-
- assert(path);
-
-#ifdef HAVE_SMACK
- if (!mac_smack_use())
- return 0;
-
- if (label)
- r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
- else
- r = lremovexattr(path, "security.SMACK64");
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-int mac_smack_apply_fd(int fd, const char *label) {
- int r = 0;
-
- assert(fd >= 0);
-
-#ifdef HAVE_SMACK
- if (!mac_smack_use())
- return 0;
-
- if (label)
- r = fsetxattr(fd, "security.SMACK64", label, strlen(label), 0);
- else
- r = fremovexattr(fd, "security.SMACK64");
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-int mac_smack_apply_ip_out_fd(int fd, const char *label) {
- int r = 0;
-
- assert(fd >= 0);
-
-#ifdef HAVE_SMACK
- if (!mac_smack_use())
- return 0;
-
- if (label)
- r = fsetxattr(fd, "security.SMACK64IPOUT", label, strlen(label), 0);
- else
- r = fremovexattr(fd, "security.SMACK64IPOUT");
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-int mac_smack_apply_ip_in_fd(int fd, const char *label) {
- int r = 0;
-
- assert(fd >= 0);
-
-#ifdef HAVE_SMACK
- if (!mac_smack_use())
- return 0;
-
- if (label)
- r = fsetxattr(fd, "security.SMACK64IPIN", label, strlen(label), 0);
- else
- r = fremovexattr(fd, "security.SMACK64IPIN");
- if (r < 0)
- return -errno;
-#endif
-
- return r;
-}
-
-int mac_smack_apply_pid(pid_t pid, const char *label) {
-
-#ifdef HAVE_SMACK
- const char *p;
-#endif
- int r = 0;
-
- assert(label);
-
-#ifdef HAVE_SMACK
- if (!mac_smack_use())
- return 0;
-
- p = procfs_file_alloca(pid, "attr/current");
- r = write_string_file(p, label);
- if (r < 0)
- return r;
-#endif
-
- return r;
-}
-
-int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
-
-#ifdef HAVE_SMACK
- struct stat st;
-#endif
- int r = 0;
-
- assert(path);
-
-#ifdef HAVE_SMACK
- if (!mac_smack_use())
- return 0;
-
- /*
- * Path must be in /dev and must exist
- */
- if (!path_startswith(path, "/dev"))
- return 0;
-
- r = lstat(path, &st);
- if (r >= 0) {
- const char *label;
-
- /*
- * Label directories and character devices "*".
- * Label symlinks "_".
- * Don't change anything else.
- */
-
- if (S_ISDIR(st.st_mode))
- label = SMACK_STAR_LABEL;
- else if (S_ISLNK(st.st_mode))
- label = SMACK_FLOOR_LABEL;
- else if (S_ISCHR(st.st_mode))
- label = SMACK_STAR_LABEL;
- else
- return 0;
-
- r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
-
- /* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == EOPNOTSUPP)
- return 0;
- }
-
- if (r < 0) {
- /* Ignore ENOENT in some cases */
- if (ignore_enoent && errno == ENOENT)
- return 0;
-
- if (ignore_erofs && errno == EROFS)
- return 0;
-
- r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path);
- }
-#endif
-
- return r;
-}
diff --git a/src/shared/smack-util.h b/src/shared/smack-util.h
deleted file mode 100644
index 50f55b1..0000000
--- a/src/shared/smack-util.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Intel Corporation
-
- Author: Auke Kok <auke-jan.h.kok at intel.com>
-
- 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 <stdbool.h>
-
-bool mac_smack_use(void);
-
-int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
-
-int mac_smack_apply(const char *path, const char *label);
-int mac_smack_apply_fd(int fd, const char *label);
-int mac_smack_apply_pid(pid_t pid, const char *label);
-int mac_smack_apply_ip_in_fd(int fd, const char *label);
-int mac_smack_apply_ip_out_fd(int fd, const char *label);
diff --git a/src/shared/socket-label.c b/src/shared/socket-label.c
deleted file mode 100644
index cbe3ff2..0000000
--- a/src/shared/socket-label.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <stddef.h>
-
-#include "macro.h"
-#include "util.h"
-#include "mkdir.h"
-#include "missing.h"
-#include "selinux-util.h"
-#include "socket-util.h"
-
-int socket_address_listen(
- const SocketAddress *a,
- int flags,
- int backlog,
- SocketAddressBindIPv6Only only,
- const char *bind_to_device,
- bool free_bind,
- bool transparent,
- mode_t directory_mode,
- mode_t socket_mode,
- const char *label) {
-
- _cleanup_close_ int fd = -1;
- int r, one;
-
- assert(a);
-
- r = socket_address_verify(a);
- if (r < 0)
- return r;
-
- if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
- return -EAFNOSUPPORT;
-
- if (label) {
- r = mac_selinux_create_socket_prepare(label);
- if (r < 0)
- return r;
- }
-
- fd = socket(socket_address_family(a), a->type | flags, a->protocol);
- r = fd < 0 ? -errno : 0;
-
- if (label)
- mac_selinux_create_socket_clear();
-
- if (r < 0)
- return r;
-
- if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
- int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
- return -errno;
- }
-
- if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
- if (bind_to_device)
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
- return -errno;
-
- if (free_bind) {
- one = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
- log_warning_errno(errno, "IP_FREEBIND failed: %m");
- }
-
- if (transparent) {
- one = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
- log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
- }
- }
-
- one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
- return -errno;
-
- if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
- mode_t old_mask;
-
- /* Create parents */
- mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
-
- /* Enforce the right access mode for the socket */
- old_mask = umask(~ socket_mode);
-
- r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
-
- if (r < 0 && errno == EADDRINUSE) {
- /* Unlink and try again */
- unlink(a->sockaddr.un.sun_path);
- r = bind(fd, &a->sockaddr.sa, a->size);
- }
-
- umask(old_mask);
- } else
- r = bind(fd, &a->sockaddr.sa, a->size);
-
- if (r < 0)
- return -errno;
-
- if (socket_address_can_accept(a))
- if (listen(fd, backlog) < 0)
- return -errno;
-
- r = fd;
- fd = -1;
-
- return r;
-}
-
-int make_socket_fd(int log_level, const char* address, int flags) {
- SocketAddress a;
- int fd, r;
-
- r = socket_address_parse(&a, address);
- if (r < 0) {
- log_error("Failed to parse socket address \"%s\": %s",
- address, strerror(-r));
- return r;
- }
-
- fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
- NULL, false, false, 0755, 0644, NULL);
- if (fd < 0 || log_get_max_level() >= log_level) {
- _cleanup_free_ char *p = NULL;
-
- r = socket_address_print(&a, &p);
- if (r < 0)
- return log_error_errno(r, "socket_address_print(): %m");
-
- if (fd < 0)
- log_error_errno(fd, "Failed to listen on %s: %m", p);
- else
- log_full(log_level, "Listening on %s", p);
- }
-
- return fd;
-}
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
deleted file mode 100644
index e8bb10d..0000000
--- a/src/shared/socket-util.c
+++ /dev/null
@@ -1,769 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <stddef.h>
-#include <netdb.h>
-
-#include "macro.h"
-#include "path-util.h"
-#include "util.h"
-#include "socket-util.h"
-#include "missing.h"
-#include "fileio.h"
-#include "formats-util.h"
-
-int socket_address_parse(SocketAddress *a, const char *s) {
- char *e, *n;
- unsigned u;
- int r;
-
- assert(a);
- assert(s);
-
- zero(*a);
- a->type = SOCK_STREAM;
-
- if (*s == '[') {
- /* IPv6 in [x:.....:z]:p notation */
-
- e = strchr(s+1, ']');
- if (!e)
- return -EINVAL;
-
- n = strndupa(s+1, e-s-1);
-
- errno = 0;
- if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
- return errno > 0 ? -errno : -EINVAL;
-
- e++;
- if (*e != ':')
- return -EINVAL;
-
- e++;
- r = safe_atou(e, &u);
- if (r < 0)
- return r;
-
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->size = sizeof(struct sockaddr_in6);
-
- } else if (*s == '/') {
- /* AF_UNIX socket */
-
- size_t l;
-
- l = strlen(s);
- if (l >= sizeof(a->sockaddr.un.sun_path))
- return -EINVAL;
-
- a->sockaddr.un.sun_family = AF_UNIX;
- memcpy(a->sockaddr.un.sun_path, s, l);
- a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
-
- } else if (*s == '@') {
- /* Abstract AF_UNIX socket */
- size_t l;
-
- l = strlen(s+1);
- if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
- return -EINVAL;
-
- a->sockaddr.un.sun_family = AF_UNIX;
- memcpy(a->sockaddr.un.sun_path+1, s+1, l);
- a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
-
- } else {
- e = strchr(s, ':');
- if (e) {
- r = safe_atou(e+1, &u);
- if (r < 0)
- return r;
-
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
- n = strndupa(s, e-s);
-
- /* IPv4 in w.x.y.z:p notation? */
- r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
- if (r < 0)
- return -errno;
-
- if (r > 0) {
- /* Gotcha, it's a traditional IPv4 address */
- a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htons((uint16_t) u);
- a->size = sizeof(struct sockaddr_in);
- } else {
- unsigned idx;
-
- if (strlen(n) > IF_NAMESIZE-1)
- return -EINVAL;
-
- /* Uh, our last resort, an interface name */
- idx = if_nametoindex(n);
- if (idx == 0)
- return -EINVAL;
-
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->sockaddr.in6.sin6_scope_id = idx;
- a->sockaddr.in6.sin6_addr = in6addr_any;
- a->size = sizeof(struct sockaddr_in6);
- }
- } else {
-
- /* Just a port */
- r = safe_atou(s, &u);
- if (r < 0)
- return r;
-
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
- if (socket_ipv6_is_supported()) {
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->sockaddr.in6.sin6_addr = in6addr_any;
- a->size = sizeof(struct sockaddr_in6);
- } else {
- a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htons((uint16_t) u);
- a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
- a->size = sizeof(struct sockaddr_in);
- }
- }
- }
-
- return 0;
-}
-
-int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
- SocketAddress b;
- int r;
-
- /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
-
- r = socket_address_parse(&b, s);
- if (r < 0)
- return r;
-
- if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
- log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
- *a = b;
- return 0;
-}
-
-int socket_address_parse_netlink(SocketAddress *a, const char *s) {
- int family;
- unsigned group = 0;
- _cleanup_free_ char *sfamily = NULL;
- assert(a);
- assert(s);
-
- zero(*a);
- a->type = SOCK_RAW;
-
- errno = 0;
- if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
- return errno > 0 ? -errno : -EINVAL;
-
- family = netlink_family_from_string(sfamily);
- if (family < 0)
- return -EINVAL;
-
- a->sockaddr.nl.nl_family = AF_NETLINK;
- a->sockaddr.nl.nl_groups = group;
-
- a->type = SOCK_RAW;
- a->size = sizeof(struct sockaddr_nl);
- a->protocol = family;
-
- return 0;
-}
-
-int socket_address_verify(const SocketAddress *a) {
- assert(a);
-
- switch (socket_address_family(a)) {
-
- case AF_INET:
- if (a->size != sizeof(struct sockaddr_in))
- return -EINVAL;
-
- if (a->sockaddr.in.sin_port == 0)
- return -EINVAL;
-
- if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
- return -EINVAL;
-
- return 0;
-
- case AF_INET6:
- if (a->size != sizeof(struct sockaddr_in6))
- return -EINVAL;
-
- if (a->sockaddr.in6.sin6_port == 0)
- return -EINVAL;
-
- if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
- return -EINVAL;
-
- return 0;
-
- case AF_UNIX:
- if (a->size < offsetof(struct sockaddr_un, sun_path))
- return -EINVAL;
-
- if (a->size > offsetof(struct sockaddr_un, sun_path)) {
-
- if (a->sockaddr.un.sun_path[0] != 0) {
- char *e;
-
- /* path */
- e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
- if (!e)
- return -EINVAL;
-
- if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
- return -EINVAL;
- }
- }
-
- if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
- return -EINVAL;
-
- return 0;
-
- case AF_NETLINK:
-
- if (a->size != sizeof(struct sockaddr_nl))
- return -EINVAL;
-
- if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
- return -EINVAL;
-
- return 0;
-
- default:
- return -EAFNOSUPPORT;
- }
-}
-
-int socket_address_print(const SocketAddress *a, char **ret) {
- int r;
-
- assert(a);
- assert(ret);
-
- r = socket_address_verify(a);
- if (r < 0)
- return r;
-
- if (socket_address_family(a) == AF_NETLINK) {
- _cleanup_free_ char *sfamily = NULL;
-
- r = netlink_family_to_string_alloc(a->protocol, &sfamily);
- if (r < 0)
- return r;
-
- r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
- if (r < 0)
- return -ENOMEM;
-
- return 0;
- }
-
- return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
-}
-
-bool socket_address_can_accept(const SocketAddress *a) {
- assert(a);
-
- return
- a->type == SOCK_STREAM ||
- a->type == SOCK_SEQPACKET;
-}
-
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
- assert(a);
- assert(b);
-
- /* Invalid addresses are unequal to all */
- if (socket_address_verify(a) < 0 ||
- socket_address_verify(b) < 0)
- return false;
-
- if (a->type != b->type)
- return false;
-
- if (socket_address_family(a) != socket_address_family(b))
- return false;
-
- switch (socket_address_family(a)) {
-
- case AF_INET:
- if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
- return false;
-
- if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
- return false;
-
- break;
-
- case AF_INET6:
- if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
- return false;
-
- if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
- return false;
-
- break;
-
- case AF_UNIX:
- if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
- b->size <= offsetof(struct sockaddr_un, sun_path))
- return false;
-
- if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
- return false;
-
- if (a->sockaddr.un.sun_path[0]) {
- if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
- return false;
- } else {
- if (a->size != b->size)
- return false;
-
- if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
- return false;
- }
-
- break;
-
- case AF_NETLINK:
- if (a->protocol != b->protocol)
- return false;
-
- if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
- return false;
-
- break;
-
- default:
- /* Cannot compare, so we assume the addresses are different */
- return false;
- }
-
- return true;
-}
-
-bool socket_address_is(const SocketAddress *a, const char *s, int type) {
- struct SocketAddress b;
-
- assert(a);
- assert(s);
-
- if (socket_address_parse(&b, s) < 0)
- return false;
-
- b.type = type;
-
- return socket_address_equal(a, &b);
-}
-
-bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
- struct SocketAddress b;
-
- assert(a);
- assert(s);
-
- if (socket_address_parse_netlink(&b, s) < 0)
- return false;
-
- return socket_address_equal(a, &b);
-}
-
-const char* socket_address_get_path(const SocketAddress *a) {
- assert(a);
-
- if (socket_address_family(a) != AF_UNIX)
- return NULL;
-
- if (a->sockaddr.un.sun_path[0] == 0)
- return NULL;
-
- return a->sockaddr.un.sun_path;
-}
-
-bool socket_ipv6_is_supported(void) {
- _cleanup_free_ char *l = NULL;
-
- if (access("/sys/module/ipv6", F_OK) != 0)
- return false;
-
- /* If we can't check "disable" parameter, assume enabled */
- if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
- return true;
-
- /* If module was loaded with disable=1 no IPv6 available */
- return l[0] == '0';
-}
-
-bool socket_address_matches_fd(const SocketAddress *a, int fd) {
- SocketAddress b;
- socklen_t solen;
-
- assert(a);
- assert(fd >= 0);
-
- b.size = sizeof(b.sockaddr);
- if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
- return false;
-
- if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
- return false;
-
- solen = sizeof(b.type);
- if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
- return false;
-
- if (b.type != a->type)
- return false;
-
- if (a->protocol != 0) {
- solen = sizeof(b.protocol);
- if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
- return false;
-
- if (b.protocol != a->protocol)
- return false;
- }
-
- return socket_address_equal(a, &b);
-}
-
-int sockaddr_port(const struct sockaddr *_sa) {
- union sockaddr_union *sa = (union sockaddr_union*) _sa;
-
- assert(sa);
-
- if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
- return -EAFNOSUPPORT;
-
- return ntohs(sa->sa.sa_family == AF_INET6 ?
- sa->in6.sin6_port :
- sa->in.sin_port);
-}
-
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
- union sockaddr_union *sa = (union sockaddr_union*) _sa;
- char *p;
- int r;
-
- assert(sa);
- assert(salen >= sizeof(sa->sa.sa_family));
-
- switch (sa->sa.sa_family) {
-
- case AF_INET: {
- uint32_t a;
-
- a = ntohl(sa->in.sin_addr.s_addr);
-
- if (include_port)
- r = asprintf(&p,
- "%u.%u.%u.%u:%u",
- a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
- ntohs(sa->in.sin_port));
- else
- r = asprintf(&p,
- "%u.%u.%u.%u",
- a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
- if (r < 0)
- return -ENOMEM;
- break;
- }
-
- case AF_INET6: {
- static const unsigned char ipv4_prefix[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
- };
-
- if (translate_ipv6 &&
- memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
- const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
- if (include_port)
- r = asprintf(&p,
- "%u.%u.%u.%u:%u",
- a[0], a[1], a[2], a[3],
- ntohs(sa->in6.sin6_port));
- else
- r = asprintf(&p,
- "%u.%u.%u.%u",
- a[0], a[1], a[2], a[3]);
- if (r < 0)
- return -ENOMEM;
- } else {
- char a[INET6_ADDRSTRLEN];
-
- inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
-
- if (include_port) {
- r = asprintf(&p,
- "[%s]:%u",
- a,
- ntohs(sa->in6.sin6_port));
- if (r < 0)
- return -ENOMEM;
- } else {
- p = strdup(a);
- if (!p)
- return -ENOMEM;
- }
- }
-
- break;
- }
-
- case AF_UNIX:
- if (salen <= offsetof(struct sockaddr_un, sun_path)) {
- p = strdup("<unnamed>");
- if (!p)
- return -ENOMEM;
-
- } else if (sa->un.sun_path[0] == 0) {
- /* abstract */
-
- /* FIXME: We assume we can print the
- * socket path here and that it hasn't
- * more than one NUL byte. That is
- * actually an invalid assumption */
-
- p = new(char, sizeof(sa->un.sun_path)+1);
- if (!p)
- return -ENOMEM;
-
- p[0] = '@';
- memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
- p[sizeof(sa->un.sun_path)] = 0;
-
- } else {
- p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
- if (!ret)
- return -ENOMEM;
- }
-
- break;
-
- default:
- return -EOPNOTSUPP;
- }
-
-
- *ret = p;
- return 0;
-}
-
-int getpeername_pretty(int fd, char **ret) {
- union sockaddr_union sa;
- socklen_t salen = sizeof(sa);
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- if (getpeername(fd, &sa.sa, &salen) < 0)
- return -errno;
-
- if (sa.sa.sa_family == AF_UNIX) {
- struct ucred ucred = {};
-
- /* UNIX connection sockets are anonymous, so let's use
- * PID/UID as pretty credentials instead */
-
- r = getpeercred(fd, &ucred);
- if (r < 0)
- return r;
-
- if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
- return -ENOMEM;
-
- return 0;
- }
-
- /* For remote sockets we translate IPv6 addresses back to IPv4
- * if applicable, since that's nicer. */
-
- return sockaddr_pretty(&sa.sa, salen, true, true, ret);
-}
-
-int getsockname_pretty(int fd, char **ret) {
- union sockaddr_union sa;
- socklen_t salen = sizeof(sa);
-
- assert(fd >= 0);
- assert(ret);
-
- if (getsockname(fd, &sa.sa, &salen) < 0)
- return -errno;
-
- /* For local sockets we do not translate IPv6 addresses back
- * to IPv6 if applicable, since this is usually used for
- * listening sockets where the difference between IPv4 and
- * IPv6 matters. */
-
- return sockaddr_pretty(&sa.sa, salen, false, true, ret);
-}
-
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
- int r;
- char host[NI_MAXHOST], *ret;
-
- assert(_ret);
-
- r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
- NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
- if (r != 0) {
- int saved_errno = errno;
-
- r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
- if (r < 0)
- return log_error_errno(r, "sockadd_pretty() failed: %m");
-
- log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
- } else {
- ret = strdup(host);
- if (!ret)
- return log_oom();
- }
-
- *_ret = ret;
- return 0;
-}
-
-int getnameinfo_pretty(int fd, char **ret) {
- union sockaddr_union sa;
- socklen_t salen = sizeof(sa);
-
- assert(fd >= 0);
- assert(ret);
-
- if (getsockname(fd, &sa.sa, &salen) < 0)
- return log_error_errno(errno, "getsockname(%d) failed: %m", fd);
-
- return socknameinfo_pretty(&sa, salen, ret);
-}
-
-int socket_address_unlink(SocketAddress *a) {
- assert(a);
-
- if (socket_address_family(a) != AF_UNIX)
- return 0;
-
- if (a->sockaddr.un.sun_path[0] == 0)
- return 0;
-
- if (unlink(a->sockaddr.un.sun_path) < 0)
- return -errno;
-
- return 1;
-}
-
-static const char* const netlink_family_table[] = {
- [NETLINK_ROUTE] = "route",
- [NETLINK_FIREWALL] = "firewall",
- [NETLINK_INET_DIAG] = "inet-diag",
- [NETLINK_NFLOG] = "nflog",
- [NETLINK_XFRM] = "xfrm",
- [NETLINK_SELINUX] = "selinux",
- [NETLINK_ISCSI] = "iscsi",
- [NETLINK_AUDIT] = "audit",
- [NETLINK_FIB_LOOKUP] = "fib-lookup",
- [NETLINK_CONNECTOR] = "connector",
- [NETLINK_NETFILTER] = "netfilter",
- [NETLINK_IP6_FW] = "ip6-fw",
- [NETLINK_DNRTMSG] = "dnrtmsg",
- [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
- [NETLINK_GENERIC] = "generic",
- [NETLINK_SCSITRANSPORT] = "scsitransport",
- [NETLINK_ECRYPTFS] = "ecryptfs"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
-
-static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
- [SOCKET_ADDRESS_DEFAULT] = "default",
- [SOCKET_ADDRESS_BOTH] = "both",
- [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
-
-bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
- assert(a);
- assert(b);
-
- if (a->sa.sa_family != b->sa.sa_family)
- return false;
-
- if (a->sa.sa_family == AF_INET)
- return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
-
- if (a->sa.sa_family == AF_INET6)
- return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
-
- return false;
-}
-
-char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
- assert(addr);
- assert(buffer);
-
- /* Like ether_ntoa() but uses %02x instead of %x to print
- * ethernet addresses, which makes them look less funny. Also,
- * doesn't use a static buffer. */
-
- sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
- addr->ether_addr_octet[0],
- addr->ether_addr_octet[1],
- addr->ether_addr_octet[2],
- addr->ether_addr_octet[3],
- addr->ether_addr_octet[4],
- addr->ether_addr_octet[5]);
-
- return buffer;
-}
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
deleted file mode 100644
index 538cf59..0000000
--- a/src/shared/socket-util.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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/socket.h>
-#include <netinet/in.h>
-#include <netinet/ether.h>
-#include <sys/un.h>
-#include <linux/netlink.h>
-#include <linux/if_packet.h>
-
-#include "macro.h"
-#include "util.h"
-
-union sockaddr_union {
- struct sockaddr sa;
- struct sockaddr_in in;
- struct sockaddr_in6 in6;
- struct sockaddr_un un;
- struct sockaddr_nl nl;
- struct sockaddr_storage storage;
- struct sockaddr_ll ll;
-};
-
-typedef struct SocketAddress {
- union sockaddr_union sockaddr;
-
- /* We store the size here explicitly due to the weird
- * sockaddr_un semantics for abstract sockets */
- socklen_t size;
-
- /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
- int type;
-
- /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
- int protocol;
-} SocketAddress;
-
-typedef enum SocketAddressBindIPv6Only {
- SOCKET_ADDRESS_DEFAULT,
- SOCKET_ADDRESS_BOTH,
- SOCKET_ADDRESS_IPV6_ONLY,
- _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
- _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
-} SocketAddressBindIPv6Only;
-
-#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
-
-int socket_address_parse(SocketAddress *a, const char *s);
-int socket_address_parse_and_warn(SocketAddress *a, const char *s);
-int socket_address_parse_netlink(SocketAddress *a, const char *s);
-int socket_address_print(const SocketAddress *a, char **p);
-int socket_address_verify(const SocketAddress *a) _pure_;
-int socket_address_unlink(SocketAddress *a);
-
-bool socket_address_can_accept(const SocketAddress *a) _pure_;
-
-int socket_address_listen(
- const SocketAddress *a,
- int flags,
- int backlog,
- SocketAddressBindIPv6Only only,
- const char *bind_to_device,
- bool free_bind,
- bool transparent,
- mode_t directory_mode,
- mode_t socket_mode,
- const char *label);
-int make_socket_fd(int log_level, const char* address, int flags);
-
-bool socket_address_is(const SocketAddress *a, const char *s, int type);
-bool socket_address_is_netlink(const SocketAddress *a, const char *s);
-
-bool socket_address_matches_fd(const SocketAddress *a, int fd);
-
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
-
-const char* socket_address_get_path(const SocketAddress *a);
-
-bool socket_ipv6_is_supported(void);
-
-int sockaddr_port(const struct sockaddr *_sa) _pure_;
-
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
-int getpeername_pretty(int fd, char **ret);
-int getsockname_pretty(int fd, char **ret);
-
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
-int getnameinfo_pretty(int fd, char **ret);
-
-const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
-SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
-
-int netlink_family_to_string_alloc(int b, char **s);
-int netlink_family_from_string(const char *s) _pure_;
-
-bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
-
-#define ETHER_ADDR_TO_STRING_MAX (3*6)
-
-char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
diff --git a/src/shared/sparse-endian.h b/src/shared/sparse-endian.h
deleted file mode 100644
index c913fda..0000000
--- a/src/shared/sparse-endian.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Copyright (c) 2012 Josh Triplett <josh at joshtriplett.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#ifndef SPARSE_ENDIAN_H
-#define SPARSE_ENDIAN_H
-
-#include <byteswap.h>
-#include <endian.h>
-#include <stdint.h>
-
-#ifdef __CHECKER__
-#define __bitwise __attribute__((bitwise))
-#define __force __attribute__((force))
-#else
-#define __bitwise
-#define __force
-#endif
-
-typedef uint16_t __bitwise le16_t;
-typedef uint16_t __bitwise be16_t;
-typedef uint32_t __bitwise le32_t;
-typedef uint32_t __bitwise be32_t;
-typedef uint64_t __bitwise le64_t;
-typedef uint64_t __bitwise be64_t;
-
-#undef htobe16
-#undef htole16
-#undef be16toh
-#undef le16toh
-#undef htobe32
-#undef htole32
-#undef be32toh
-#undef le32toh
-#undef htobe64
-#undef htole64
-#undef be64toh
-#undef le64toh
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define bswap_16_on_le(x) __bswap_16(x)
-#define bswap_32_on_le(x) __bswap_32(x)
-#define bswap_64_on_le(x) __bswap_64(x)
-#define bswap_16_on_be(x) (x)
-#define bswap_32_on_be(x) (x)
-#define bswap_64_on_be(x) (x)
-#elif __BYTE_ORDER == __BIG_ENDIAN
-#define bswap_16_on_le(x) (x)
-#define bswap_32_on_le(x) (x)
-#define bswap_64_on_le(x) (x)
-#define bswap_16_on_be(x) __bswap_16(x)
-#define bswap_32_on_be(x) __bswap_32(x)
-#define bswap_64_on_be(x) __bswap_64(x)
-#endif
-
-static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); }
-static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); }
-static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); }
-
-static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); }
-static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); }
-static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); }
-
-static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); }
-static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); }
-static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); }
-
-static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); }
-static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); }
-static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); }
-
-#endif /* SPARSE_ENDIAN_H */
diff --git a/src/shared/special.h b/src/shared/special.h
deleted file mode 100644
index e51310e..0000000
--- a/src/shared/special.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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/>.
-***/
-
-#define SPECIAL_DEFAULT_TARGET "default.target"
-
-/* Shutdown targets */
-#define SPECIAL_UMOUNT_TARGET "umount.target"
-/* This is not really intended to be started by directly. This is
- * mostly so that other targets (reboot/halt/poweroff) can depend on
- * it to bring all services down that want to be brought down on
- * system shutdown. */
-#define SPECIAL_SHUTDOWN_TARGET "shutdown.target"
-#define SPECIAL_HALT_TARGET "halt.target"
-#define SPECIAL_POWEROFF_TARGET "poweroff.target"
-#define SPECIAL_REBOOT_TARGET "reboot.target"
-#define SPECIAL_KEXEC_TARGET "kexec.target"
-#define SPECIAL_EXIT_TARGET "exit.target"
-#define SPECIAL_SUSPEND_TARGET "suspend.target"
-#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
-#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
-
-/* Special boot targets */
-#define SPECIAL_RESCUE_TARGET "rescue.target"
-#define SPECIAL_EMERGENCY_TARGET "emergency.target"
-#define SPECIAL_MULTI_USER_TARGET "multi-user.target"
-#define SPECIAL_GRAPHICAL_TARGET "graphical.target"
-
-/* Early boot targets */
-#define SPECIAL_SYSINIT_TARGET "sysinit.target"
-#define SPECIAL_SOCKETS_TARGET "sockets.target"
-#define SPECIAL_BUSNAMES_TARGET "busnames.target"
-#define SPECIAL_TIMERS_TARGET "timers.target"
-#define SPECIAL_PATHS_TARGET "paths.target"
-#define SPECIAL_LOCAL_FS_TARGET "local-fs.target"
-#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target"
-#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target"
-#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target"
-#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
-#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
-#define SPECIAL_SWAP_TARGET "swap.target"
-#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target"
-#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
-#define SPECIAL_BASIC_TARGET "basic.target"
-
-/* LSB compatibility */
-#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
-#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
-#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
-
-/*
- * Rules regarding adding further high level targets like the above:
- *
- * - Be conservative, only add more of these when we really need
- * them. We need strong usecases for further additions.
- *
- * - When there can be multiple implementations running side-by-side,
- * it needs to be a .target unit which can pull in all
- * implementations.
- *
- * - If something can be implemented with socket activation, and
- * without, it needs to be a .target unit, so that it can pull in
- * the appropriate unit.
- *
- * - Otherwise, it should be a .service unit.
- *
- * - In some cases it is OK to have both a .service and a .target
- * unit, i.e. if there can be multiple parallel implementations, but
- * only one is the "system" one. Example: syslog.
- *
- * Or to put this in other words: .service symlinks can be used to
- * arbitrate between multiple implementations if there can be only one
- * of a kind. .target units can be used to support multiple
- * implementations that can run side-by-side.
- */
-
-/* Magic early boot services */
-#define SPECIAL_FSCK_SERVICE "systemd-fsck at .service"
-#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service"
-#define SPECIAL_QUOTAON_SERVICE "quotaon.service"
-#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
-
-/* Services systemd relies on */
-#define SPECIAL_DBUS_SERVICE "dbus.service"
-#define SPECIAL_DBUS_SOCKET "dbus.socket"
-#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket"
-#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service"
-
-/* Magic init signals */
-#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
-#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
-#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
-
-/* Where we add all our system units, users and machines by default */
-#define SPECIAL_SYSTEM_SLICE "system.slice"
-#define SPECIAL_USER_SLICE "user.slice"
-#define SPECIAL_MACHINE_SLICE "machine.slice"
-#define SPECIAL_ROOT_SLICE "-.slice"
diff --git a/src/shared/strbuf.c b/src/shared/strbuf.c
deleted file mode 100644
index 01a076c..0000000
--- a/src/shared/strbuf.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2012 Kay Sievers <kay at vrfy.org>
-
- 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 <string.h>
-
-#include "util.h"
-#include "strbuf.h"
-
-/*
- * Strbuf stores given strings in a single continuous allocated memory
- * area. Identical strings are de-duplicated and return the same offset
- * as the first string stored. If the tail of a string already exists
- * in the buffer, the tail is returned.
- *
- * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the
- * information about the stored strings.
- *
- * Example of udev rules:
- * $ ./udevadm test .
- * ...
- * read rules file: /usr/lib/udev/rules.d/99-systemd.rules
- * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings
- * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used
- * ...
- */
-
-struct strbuf *strbuf_new(void) {
- struct strbuf *str;
-
- str = new0(struct strbuf, 1);
- if (!str)
- return NULL;
-
- str->buf = new0(char, 1);
- if (!str->buf)
- goto err;
- str->len = 1;
-
- str->root = new0(struct strbuf_node, 1);
- if (!str->root)
- goto err;
- str->nodes_count = 1;
- return str;
-err:
- free(str->buf);
- free(str->root);
- free(str);
- return NULL;
-}
-
-static void strbuf_node_cleanup(struct strbuf_node *node) {
- size_t i;
-
- for (i = 0; i < node->children_count; i++)
- strbuf_node_cleanup(node->children[i].child);
- free(node->children);
- free(node);
-}
-
-/* clean up trie data, leave only the string buffer */
-void strbuf_complete(struct strbuf *str) {
- if (!str)
- return;
- if (str->root)
- strbuf_node_cleanup(str->root);
- str->root = NULL;
-}
-
-/* clean up everything */
-void strbuf_cleanup(struct strbuf *str) {
- if (!str)
- return;
- if (str->root)
- strbuf_node_cleanup(str->root);
- free(str->buf);
- free(str);
-}
-
-static int strbuf_children_cmp(const struct strbuf_child_entry *n1,
- const struct strbuf_child_entry *n2) {
- return n1->c - n2->c;
-}
-
-static void bubbleinsert(struct strbuf_node *node,
- uint8_t c,
- struct strbuf_node *node_child) {
-
- struct strbuf_child_entry new = {
- .c = c,
- .child = node_child,
- };
- int left = 0, right = node->children_count;
-
- while (right > left) {
- int middle = (right + left) / 2 ;
- if (strbuf_children_cmp(&node->children[middle], &new) <= 0)
- left = middle + 1;
- else
- right = middle;
- }
-
- memmove(node->children + left + 1, node->children + left,
- sizeof(struct strbuf_child_entry) * (node->children_count - left));
- node->children[left] = new;
-
- node->children_count ++;
-}
-
-/* add string, return the index/offset into the buffer */
-ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
- uint8_t c;
- struct strbuf_node *node;
- size_t depth;
- char *buf_new;
- struct strbuf_child_entry *child;
- struct strbuf_node *node_child;
- ssize_t off;
-
- if (!str->root)
- return -EINVAL;
-
- /* search string; start from last character to find possibly matching tails */
- if (len == 0)
- return 0;
- str->in_count++;
- str->in_len += len;
-
- node = str->root;
- c = s[len-1];
- for (depth = 0; depth <= len; depth++) {
- struct strbuf_child_entry search;
-
- /* match against current node */
- off = node->value_off + node->value_len - len;
- if (depth == len || (node->value_len >= len && memcmp(str->buf + off, s, len) == 0)) {
- str->dedup_len += len;
- str->dedup_count++;
- return off;
- }
-
- /* lookup child node */
- c = s[len - 1 - depth];
- search.c = c;
- child = bsearch(&search, node->children, node->children_count,
- sizeof(struct strbuf_child_entry),
- (__compar_fn_t) strbuf_children_cmp);
- if (!child)
- break;
- node = child->child;
- }
-
- /* add new string */
- buf_new = realloc(str->buf, str->len + len+1);
- if (!buf_new)
- return -ENOMEM;
- str->buf = buf_new;
- off = str->len;
- memcpy(str->buf + off, s, len);
- str->len += len;
- str->buf[str->len++] = '\0';
-
- /* new node */
- node_child = new0(struct strbuf_node, 1);
- if (!node_child)
- return -ENOMEM;
- node_child->value_off = off;
- node_child->value_len = len;
-
- /* extend array, add new entry, sort for bisection */
- child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry));
- if (!child) {
- free(node_child);
- return -ENOMEM;
- }
-
- str->nodes_count++;
-
- node->children = child;
- bubbleinsert(node, c, node_child);
-
- return off;
-}
diff --git a/src/shared/strbuf.h b/src/shared/strbuf.h
deleted file mode 100644
index fbc4e5f..0000000
--- a/src/shared/strbuf.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2012 Kay Sievers <kay at vrfy.org>
-
- 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 <stdint.h>
-
-struct strbuf {
- char *buf;
- size_t len;
- struct strbuf_node *root;
-
- size_t nodes_count;
- size_t in_count;
- size_t in_len;
- size_t dedup_len;
- size_t dedup_count;
-};
-
-struct strbuf_node {
- size_t value_off;
- size_t value_len;
-
- struct strbuf_child_entry *children;
- uint8_t children_count;
-};
-
-struct strbuf_child_entry {
- uint8_t c;
- struct strbuf_node *child;
-};
-
-struct strbuf *strbuf_new(void);
-ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len);
-void strbuf_complete(struct strbuf *str);
-void strbuf_cleanup(struct strbuf *str);
diff --git a/src/shared/strv.c b/src/shared/strv.c
deleted file mode 100644
index d44a72f..0000000
--- a/src/shared/strv.c
+++ /dev/null
@@ -1,704 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdarg.h>
-#include <string.h>
-#include <errno.h>
-
-#include "util.h"
-#include "strv.h"
-
-char *strv_find(char **l, const char *name) {
- char **i;
-
- assert(name);
-
- STRV_FOREACH(i, l)
- if (streq(*i, name))
- return *i;
-
- return NULL;
-}
-
-char *strv_find_prefix(char **l, const char *name) {
- char **i;
-
- assert(name);
-
- STRV_FOREACH(i, l)
- if (startswith(*i, name))
- return *i;
-
- return NULL;
-}
-
-char *strv_find_startswith(char **l, const char *name) {
- char **i, *e;
-
- assert(name);
-
- /* Like strv_find_prefix, but actually returns only the
- * suffix, not the whole item */
-
- STRV_FOREACH(i, l) {
- e = startswith(*i, name);
- if (e)
- return e;
- }
-
- return NULL;
-}
-
-void strv_clear(char **l) {
- char **k;
-
- if (!l)
- return;
-
- for (k = l; *k; k++)
- free(*k);
-
- *l = NULL;
-}
-
-char **strv_free(char **l) {
- strv_clear(l);
- free(l);
- return NULL;
-}
-
-char **strv_copy(char * const *l) {
- char **r, **k;
-
- k = r = new(char*, strv_length(l) + 1);
- if (!r)
- return NULL;
-
- if (l)
- for (; *l; k++, l++) {
- *k = strdup(*l);
- if (!*k) {
- strv_free(r);
- return NULL;
- }
- }
-
- *k = NULL;
- return r;
-}
-
-unsigned strv_length(char * const *l) {
- unsigned n = 0;
-
- if (!l)
- return 0;
-
- for (; *l; l++)
- n++;
-
- return n;
-}
-
-char **strv_new_ap(const char *x, va_list ap) {
- const char *s;
- char **a;
- unsigned n = 0, i = 0;
- va_list aq;
-
- /* As a special trick we ignore all listed strings that equal
- * (const char*) -1. This is supposed to be used with the
- * STRV_IFNOTNULL() macro to include possibly NULL strings in
- * the string list. */
-
- if (x) {
- n = x == (const char*) -1 ? 0 : 1;
-
- va_copy(aq, ap);
- while ((s = va_arg(aq, const char*))) {
- if (s == (const char*) -1)
- continue;
-
- n++;
- }
-
- va_end(aq);
- }
-
- a = new(char*, n+1);
- if (!a)
- return NULL;
-
- if (x) {
- if (x != (const char*) -1) {
- a[i] = strdup(x);
- if (!a[i])
- goto fail;
- i++;
- }
-
- while ((s = va_arg(ap, const char*))) {
-
- if (s == (const char*) -1)
- continue;
-
- a[i] = strdup(s);
- if (!a[i])
- goto fail;
-
- i++;
- }
- }
-
- a[i] = NULL;
-
- return a;
-
-fail:
- strv_free(a);
- return NULL;
-}
-
-char **strv_new(const char *x, ...) {
- char **r;
- va_list ap;
-
- va_start(ap, x);
- r = strv_new_ap(x, ap);
- va_end(ap);
-
- return r;
-}
-
-int strv_extend_strv(char ***a, char **b) {
- int r;
- char **s;
-
- STRV_FOREACH(s, b) {
- r = strv_extend(a, *s);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
- int r;
- char **s;
-
- STRV_FOREACH(s, b) {
- char *v;
-
- v = strappend(*s, suffix);
- if (!v)
- return -ENOMEM;
-
- r = strv_push(a, v);
- if (r < 0) {
- free(v);
- return r;
- }
- }
-
- return 0;
-}
-
-char **strv_split(const char *s, const char *separator) {
- const char *word, *state;
- size_t l;
- unsigned n, i;
- char **r;
-
- assert(s);
-
- n = 0;
- FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
- n++;
-
- r = new(char*, n+1);
- if (!r)
- return NULL;
-
- i = 0;
- FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
- r[i] = strndup(word, l);
- if (!r[i]) {
- strv_free(r);
- return NULL;
- }
-
- i++;
- }
-
- r[i] = NULL;
- return r;
-}
-
-char **strv_split_newlines(const char *s) {
- char **l;
- unsigned n;
-
- assert(s);
-
- /* Special version of strv_split() that splits on newlines and
- * suppresses an empty string at the end */
-
- l = strv_split(s, NEWLINE);
- if (!l)
- return NULL;
-
- n = strv_length(l);
- if (n <= 0)
- return l;
-
- if (isempty(l[n-1])) {
- free(l[n-1]);
- l[n-1] = NULL;
- }
-
- return l;
-}
-
-int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
- size_t n = 0, allocated = 0;
- _cleanup_strv_free_ char **l = NULL;
- int r;
-
- assert(t);
- assert(s);
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = unquote_first_word(&s, &word, flags);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- if (!GREEDY_REALLOC(l, allocated, n + 2))
- return -ENOMEM;
-
- l[n++] = word;
- word = NULL;
-
- l[n] = NULL;
- }
-
- if (!l)
- l = new0(char*, 1);
-
- *t = l;
- l = NULL;
-
- return 0;
-}
-
-char *strv_join(char **l, const char *separator) {
- char *r, *e;
- char **s;
- size_t n, k;
-
- if (!separator)
- separator = " ";
-
- k = strlen(separator);
-
- n = 0;
- STRV_FOREACH(s, l) {
- if (n != 0)
- n += k;
- n += strlen(*s);
- }
-
- r = new(char, n+1);
- if (!r)
- return NULL;
-
- e = r;
- STRV_FOREACH(s, l) {
- if (e != r)
- e = stpcpy(e, separator);
-
- e = stpcpy(e, *s);
- }
-
- *e = 0;
-
- return r;
-}
-
-char *strv_join_quoted(char **l) {
- char *buf = NULL;
- char **s;
- size_t allocated = 0, len = 0;
-
- STRV_FOREACH(s, l) {
- /* assuming here that escaped string cannot be more
- * than twice as long, and reserving space for the
- * separator and quotes.
- */
- _cleanup_free_ char *esc = NULL;
- size_t needed;
-
- if (!GREEDY_REALLOC(buf, allocated,
- len + strlen(*s) * 2 + 3))
- goto oom;
-
- esc = cescape(*s);
- if (!esc)
- goto oom;
-
- needed = snprintf(buf + len, allocated - len, "%s\"%s\"",
- len > 0 ? " " : "", esc);
- assert(needed < allocated - len);
- len += needed;
- }
-
- if (!buf)
- buf = malloc0(1);
-
- return buf;
-
- oom:
- free(buf);
- return NULL;
-}
-
-int strv_push(char ***l, char *value) {
- char **c;
- unsigned n, m;
-
- if (!value)
- return 0;
-
- n = strv_length(*l);
-
- /* Increase and check for overflow */
- m = n + 2;
- if (m < n)
- return -ENOMEM;
-
- c = realloc_multiply(*l, sizeof(char*), m);
- if (!c)
- return -ENOMEM;
-
- c[n] = value;
- c[n+1] = NULL;
-
- *l = c;
- return 0;
-}
-
-int strv_push_pair(char ***l, char *a, char *b) {
- char **c;
- unsigned n, m;
-
- if (!a && !b)
- return 0;
-
- n = strv_length(*l);
-
- /* increase and check for overflow */
- m = n + !!a + !!b + 1;
- if (m < n)
- return -ENOMEM;
-
- c = realloc_multiply(*l, sizeof(char*), m);
- if (!c)
- return -ENOMEM;
-
- if (a)
- c[n++] = a;
- if (b)
- c[n++] = b;
- c[n] = NULL;
-
- *l = c;
- return 0;
-}
-
-int strv_push_prepend(char ***l, char *value) {
- char **c;
- unsigned n, m, i;
-
- if (!value)
- return 0;
-
- n = strv_length(*l);
-
- /* increase and check for overflow */
- m = n + 2;
- if (m < n)
- return -ENOMEM;
-
- c = new(char*, m);
- if (!c)
- return -ENOMEM;
-
- for (i = 0; i < n; i++)
- c[i+1] = (*l)[i];
-
- c[0] = value;
- c[n+1] = NULL;
-
- free(*l);
- *l = c;
-
- return 0;
-}
-
-int strv_consume(char ***l, char *value) {
- int r;
-
- r = strv_push(l, value);
- if (r < 0)
- free(value);
-
- return r;
-}
-
-int strv_consume_pair(char ***l, char *a, char *b) {
- int r;
-
- r = strv_push_pair(l, a, b);
- if (r < 0) {
- free(a);
- free(b);
- }
-
- return r;
-}
-
-int strv_consume_prepend(char ***l, char *value) {
- int r;
-
- r = strv_push_prepend(l, value);
- if (r < 0)
- free(value);
-
- return r;
-}
-
-int strv_extend(char ***l, const char *value) {
- char *v;
-
- if (!value)
- return 0;
-
- v = strdup(value);
- if (!v)
- return -ENOMEM;
-
- return strv_consume(l, v);
-}
-
-char **strv_uniq(char **l) {
- char **i;
-
- /* Drops duplicate entries. The first identical string will be
- * kept, the others dropped */
-
- STRV_FOREACH(i, l)
- strv_remove(i+1, *i);
-
- return l;
-}
-
-bool strv_is_uniq(char **l) {
- char **i;
-
- STRV_FOREACH(i, l)
- if (strv_find(i+1, *i))
- return false;
-
- return true;
-}
-
-char **strv_remove(char **l, const char *s) {
- char **f, **t;
-
- if (!l)
- return NULL;
-
- assert(s);
-
- /* Drops every occurrence of s in the string list, edits
- * in-place. */
-
- for (f = t = l; *f; f++)
- if (streq(*f, s))
- free(*f);
- else
- *(t++) = *f;
-
- *t = NULL;
- return l;
-}
-
-char **strv_parse_nulstr(const char *s, size_t l) {
- const char *p;
- unsigned c = 0, i = 0;
- char **v;
-
- assert(s || l <= 0);
-
- if (l <= 0)
- return new0(char*, 1);
-
- for (p = s; p < s + l; p++)
- if (*p == 0)
- c++;
-
- if (s[l-1] != 0)
- c++;
-
- v = new0(char*, c+1);
- if (!v)
- return NULL;
-
- p = s;
- while (p < s + l) {
- const char *e;
-
- e = memchr(p, 0, s + l - p);
-
- v[i] = strndup(p, e ? e - p : s + l - p);
- if (!v[i]) {
- strv_free(v);
- return NULL;
- }
-
- i++;
-
- if (!e)
- break;
-
- p = e + 1;
- }
-
- assert(i == c);
-
- return v;
-}
-
-char **strv_split_nulstr(const char *s) {
- const char *i;
- char **r = NULL;
-
- NULSTR_FOREACH(i, s)
- if (strv_extend(&r, i) < 0) {
- strv_free(r);
- return NULL;
- }
-
- if (!r)
- return strv_new(NULL, NULL);
-
- return r;
-}
-
-bool strv_overlap(char **a, char **b) {
- char **i;
-
- STRV_FOREACH(i, a)
- if (strv_contains(b, *i))
- return true;
-
- return false;
-}
-
-static int str_compare(const void *_a, const void *_b) {
- const char **a = (const char**) _a, **b = (const char**) _b;
-
- return strcmp(*a, *b);
-}
-
-char **strv_sort(char **l) {
-
- if (strv_isempty(l))
- return l;
-
- qsort(l, strv_length(l), sizeof(char*), str_compare);
- return l;
-}
-
-bool strv_equal(char **a, char **b) {
- if (!a || !b)
- return a == b;
-
- for ( ; *a || *b; ++a, ++b)
- if (!streq_ptr(*a, *b))
- return false;
-
- return true;
-}
-
-void strv_print(char **l) {
- char **s;
-
- STRV_FOREACH(s, l)
- puts(*s);
-}
-
-int strv_extendf(char ***l, const char *format, ...) {
- va_list ap;
- char *x;
- int r;
-
- va_start(ap, format);
- r = vasprintf(&x, format, ap);
- va_end(ap);
-
- if (r < 0)
- return -ENOMEM;
-
- return strv_consume(l, x);
-}
-
-char **strv_reverse(char **l) {
- unsigned n, i;
-
- n = strv_length(l);
- if (n <= 1)
- return l;
-
- for (i = 0; i < n / 2; i++) {
- char *t;
-
- t = l[i];
- l[i] = l[n-1-i];
- l[n-1-i] = t;
- }
-
- return l;
-}
-
-bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
- char* const* p;
-
- STRV_FOREACH(p, patterns)
- if (fnmatch(*p, s, 0) == 0)
- return true;
-
- return false;
-}
diff --git a/src/shared/strv.h b/src/shared/strv.h
deleted file mode 100644
index 22f8f98..0000000
--- a/src/shared/strv.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdarg.h>
-#include <stdbool.h>
-#include <fnmatch.h>
-
-#include "util.h"
-
-char *strv_find(char **l, const char *name) _pure_;
-char *strv_find_prefix(char **l, const char *name) _pure_;
-char *strv_find_startswith(char **l, const char *name) _pure_;
-
-char **strv_free(char **l);
-DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
-#define _cleanup_strv_free_ _cleanup_(strv_freep)
-
-void strv_clear(char **l);
-
-char **strv_copy(char * const *l);
-unsigned strv_length(char * const *l) _pure_;
-
-int strv_extend_strv(char ***a, char **b);
-int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
-int strv_extend(char ***l, const char *value);
-int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
-int strv_push(char ***l, char *value);
-int strv_push_pair(char ***l, char *a, char *b);
-int strv_push_prepend(char ***l, char *value);
-int strv_consume(char ***l, char *value);
-int strv_consume_pair(char ***l, char *a, char *b);
-int strv_consume_prepend(char ***l, char *value);
-
-char **strv_remove(char **l, const char *s);
-char **strv_uniq(char **l);
-bool strv_is_uniq(char **l);
-
-bool strv_equal(char **a, char **b);
-
-#define strv_contains(l, s) (!!strv_find((l), (s)))
-
-char **strv_new(const char *x, ...) _sentinel_;
-char **strv_new_ap(const char *x, va_list ap);
-
-static inline const char* STRV_IFNOTNULL(const char *x) {
- return x ? x : (const char *) -1;
-}
-
-static inline bool strv_isempty(char * const *l) {
- return !l || !*l;
-}
-
-char **strv_split(const char *s, const char *separator);
-char **strv_split_newlines(const char *s);
-
-int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
-
-char *strv_join(char **l, const char *separator);
-char *strv_join_quoted(char **l);
-
-char **strv_parse_nulstr(const char *s, size_t l);
-char **strv_split_nulstr(const char *s);
-
-bool strv_overlap(char **a, char **b) _pure_;
-
-#define STRV_FOREACH(s, l) \
- for ((s) = (l); (s) && *(s); (s)++)
-
-#define STRV_FOREACH_BACKWARDS(s, l) \
- STRV_FOREACH(s, l) \
- ; \
- for ((s)--; (l) && ((s) >= (l)); (s)--)
-
-#define STRV_FOREACH_PAIR(x, y, l) \
- for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
-
-char **strv_sort(char **l);
-void strv_print(char **l);
-
-#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
-
-#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
-
-#define strv_from_stdarg_alloca(first) \
- ({ \
- char **_l; \
- \
- if (!first) \
- _l = (char**) &first; \
- else { \
- unsigned _n; \
- va_list _ap; \
- \
- _n = 1; \
- va_start(_ap, first); \
- while (va_arg(_ap, char*)) \
- _n++; \
- va_end(_ap); \
- \
- _l = newa(char*, _n+1); \
- _l[_n = 0] = (char*) first; \
- va_start(_ap, first); \
- for (;;) { \
- _l[++_n] = va_arg(_ap, char*); \
- if (!_l[_n]) \
- break; \
- } \
- va_end(_ap); \
- } \
- _l; \
- })
-
-#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
-
-#define FOREACH_STRING(x, ...) \
- for (char **_l = ({ \
- char **_ll = STRV_MAKE(__VA_ARGS__); \
- x = _ll ? _ll[0] : NULL; \
- _ll; \
- }); \
- _l && *_l; \
- x = ({ \
- _l ++; \
- _l[0]; \
- }))
-
-char **strv_reverse(char **l);
-
-bool strv_fnmatch(char* const* patterns, const char *s, int flags);
-
-static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
- assert(s);
- return strv_isempty(patterns) ||
- strv_fnmatch(patterns, s, flags);
-}
diff --git a/src/shared/strxcpyx.c b/src/shared/strxcpyx.c
deleted file mode 100644
index 6542c0a..0000000
--- a/src/shared/strxcpyx.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Kay Sievers
-
- 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/>.
-***/
-
-/*
- * Concatenates/copies strings. In any case, terminates in all cases
- * with '\0' * and moves the @dest pointer forward to the added '\0'.
- * Returns the * remaining size, and 0 if the string was truncated.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "strxcpyx.h"
-
-size_t strpcpy(char **dest, size_t size, const char *src) {
- size_t len;
-
- len = strlen(src);
- if (len >= size) {
- if (size > 1)
- *dest = mempcpy(*dest, src, size-1);
- size = 0;
- } else {
- if (len > 0) {
- *dest = mempcpy(*dest, src, len);
- size -= len;
- }
- }
- *dest[0] = '\0';
- return size;
-}
-
-size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
- va_list va;
- int i;
-
- va_start(va, src);
- i = vsnprintf(*dest, size, src, va);
- if (i < (int)size) {
- *dest += i;
- size -= i;
- } else {
- *dest += size;
- size = 0;
- }
- va_end(va);
- *dest[0] = '\0';
- return size;
-}
-
-size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
- va_list va;
-
- va_start(va, src);
- do {
- size = strpcpy(dest, size, src);
- src = va_arg(va, char *);
- } while (src != NULL);
- va_end(va);
- return size;
-}
-
-size_t strscpy(char *dest, size_t size, const char *src) {
- char *s;
-
- s = dest;
- return strpcpy(&s, size, src);
-}
-
-size_t strscpyl(char *dest, size_t size, const char *src, ...) {
- va_list va;
- char *s;
-
- va_start(va, src);
- s = dest;
- do {
- size = strpcpy(&s, size, src);
- src = va_arg(va, char *);
- } while (src != NULL);
- va_end(va);
-
- return size;
-}
diff --git a/src/shared/strxcpyx.h b/src/shared/strxcpyx.h
deleted file mode 100644
index ccc7e52..0000000
--- a/src/shared/strxcpyx.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Kay Sievers
-
- 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 "macro.h"
-
-size_t strpcpy(char **dest, size_t size, const char *src);
-size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4);
-size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_;
-size_t strscpy(char *dest, size_t size, const char *src);
-size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_;
diff --git a/src/shared/terminal-util.c b/src/shared/terminal-util.c
deleted file mode 100644
index 042b88f..0000000
--- a/src/shared/terminal-util.c
+++ /dev/null
@@ -1,1072 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2010 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/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <termios.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <assert.h>
-#include <poll.h>
-#include <linux/vt.h>
-#include <linux/tiocl.h>
-#include <linux/kd.h>
-
-#include "terminal-util.h"
-#include "time-util.h"
-#include "process-util.h"
-#include "util.h"
-#include "fileio.h"
-#include "path-util.h"
-
-static volatile unsigned cached_columns = 0;
-static volatile unsigned cached_lines = 0;
-
-int chvt(int vt) {
- _cleanup_close_ int fd;
-
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (vt < 0) {
- int tiocl[2] = {
- TIOCL_GETKMSGREDIRECT,
- 0
- };
-
- if (ioctl(fd, TIOCLINUX, tiocl) < 0)
- return -errno;
-
- vt = tiocl[0] <= 0 ? 1 : tiocl[0];
- }
-
- if (ioctl(fd, VT_ACTIVATE, vt) < 0)
- return -errno;
-
- return 0;
-}
-
-int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
- struct termios old_termios, new_termios;
- char c, line[LINE_MAX];
-
- assert(f);
- assert(ret);
-
- if (tcgetattr(fileno(f), &old_termios) >= 0) {
- new_termios = old_termios;
-
- new_termios.c_lflag &= ~ICANON;
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
-
- if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
- size_t k;
-
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
- return -ETIMEDOUT;
- }
- }
-
- k = fread(&c, 1, 1, f);
-
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-
- if (k <= 0)
- return -EIO;
-
- if (need_nl)
- *need_nl = c != '\n';
-
- *ret = c;
- return 0;
- }
- }
-
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
- return -ETIMEDOUT;
- }
-
- errno = 0;
- if (!fgets(line, sizeof(line), f))
- return errno ? -errno : -EIO;
-
- truncate_nl(line);
-
- if (strlen(line) != 1)
- return -EBADMSG;
-
- if (need_nl)
- *need_nl = false;
-
- *ret = line[0];
- return 0;
-}
-
-int ask_char(char *ret, const char *replies, const char *text, ...) {
- int r;
-
- assert(ret);
- assert(replies);
- assert(text);
-
- for (;;) {
- va_list ap;
- char c;
- bool need_nl = true;
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
- if (r < 0) {
-
- if (r == -EBADMSG) {
- puts("Bad input, please try again.");
- continue;
- }
-
- putchar('\n');
- return r;
- }
-
- if (need_nl)
- putchar('\n');
-
- if (strchr(replies, c)) {
- *ret = c;
- return 0;
- }
-
- puts("Read unexpected character, please try again.");
- }
-}
-
-int ask_string(char **ret, const char *text, ...) {
- assert(ret);
- assert(text);
-
- for (;;) {
- char line[LINE_MAX];
- va_list ap;
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- errno = 0;
- if (!fgets(line, sizeof(line), stdin))
- return errno ? -errno : -EIO;
-
- if (!endswith(line, "\n"))
- putchar('\n');
- else {
- char *s;
-
- if (isempty(line))
- continue;
-
- truncate_nl(line);
- s = strdup(line);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
- }
- }
-}
-
-int reset_terminal_fd(int fd, bool switch_to_text) {
- struct termios termios;
- int r = 0;
-
- /* Set terminal to some sane defaults */
-
- assert(fd >= 0);
-
- /* We leave locked terminal attributes untouched, so that
- * Plymouth may set whatever it wants to set, and we don't
- * interfere with that. */
-
- /* Disable exclusive mode, just in case */
- ioctl(fd, TIOCNXCL);
-
- /* Switch to text mode */
- if (switch_to_text)
- ioctl(fd, KDSETMODE, KD_TEXT);
-
- /* Enable console unicode mode */
- ioctl(fd, KDSKBMODE, K_UNICODE);
-
- if (tcgetattr(fd, &termios) < 0) {
- r = -errno;
- goto finish;
- }
-
- /* We only reset the stuff that matters to the software. How
- * hardware is set up we don't touch assuming that somebody
- * else will do that for us */
-
- termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
- termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
- termios.c_oflag |= ONLCR;
- termios.c_cflag |= CREAD;
- termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
-
- termios.c_cc[VINTR] = 03; /* ^C */
- termios.c_cc[VQUIT] = 034; /* ^\ */
- termios.c_cc[VERASE] = 0177;
- termios.c_cc[VKILL] = 025; /* ^X */
- termios.c_cc[VEOF] = 04; /* ^D */
- termios.c_cc[VSTART] = 021; /* ^Q */
- termios.c_cc[VSTOP] = 023; /* ^S */
- termios.c_cc[VSUSP] = 032; /* ^Z */
- termios.c_cc[VLNEXT] = 026; /* ^V */
- termios.c_cc[VWERASE] = 027; /* ^W */
- termios.c_cc[VREPRINT] = 022; /* ^R */
- termios.c_cc[VEOL] = 0;
- termios.c_cc[VEOL2] = 0;
-
- termios.c_cc[VTIME] = 0;
- termios.c_cc[VMIN] = 1;
-
- if (tcsetattr(fd, TCSANOW, &termios) < 0)
- r = -errno;
-
-finish:
- /* Just in case, flush all crap out */
- tcflush(fd, TCIOFLUSH);
-
- return r;
-}
-
-int reset_terminal(const char *name) {
- _cleanup_close_ int fd = -1;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- return reset_terminal_fd(fd, true);
-}
-
-int open_terminal(const char *name, int mode) {
- int fd, r;
- unsigned c = 0;
-
- /*
- * If a TTY is in the process of being closed opening it might
- * cause EIO. This is horribly awful, but unlikely to be
- * changed in the kernel. Hence we work around this problem by
- * retrying a couple of times.
- *
- * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
- */
-
- assert(!(mode & O_CREAT));
-
- for (;;) {
- fd = open(name, mode, 0);
- if (fd >= 0)
- break;
-
- if (errno != EIO)
- return -errno;
-
- /* Max 1s in total */
- if (c >= 20)
- return -errno;
-
- usleep(50 * USEC_PER_MSEC);
- c++;
- }
-
- r = isatty(fd);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!r) {
- safe_close(fd);
- return -ENOTTY;
- }
-
- return fd;
-}
-
-int acquire_terminal(
- const char *name,
- bool fail,
- bool force,
- bool ignore_tiocstty_eperm,
- usec_t timeout) {
-
- int fd = -1, notify = -1, r = 0, wd = -1;
- usec_t ts = 0;
-
- assert(name);
-
- /* We use inotify to be notified when the tty is closed. We
- * create the watch before checking if we can actually acquire
- * it, so that we don't lose any event.
- *
- * Note: strictly speaking this actually watches for the
- * device being closed, it does *not* really watch whether a
- * tty loses its controlling process. However, unless some
- * rogue process uses TIOCNOTTY on /dev/tty *after* closing
- * its tty otherwise this will not become a problem. As long
- * as the administrator makes sure not configure any service
- * on the same tty as an untrusted user this should not be a
- * problem. (Which he probably should not do anyway.) */
-
- if (timeout != USEC_INFINITY)
- ts = now(CLOCK_MONOTONIC);
-
- if (!fail && !force) {
- notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
- if (notify < 0) {
- r = -errno;
- goto fail;
- }
-
- wd = inotify_add_watch(notify, name, IN_CLOSE);
- if (wd < 0) {
- r = -errno;
- goto fail;
- }
- }
-
- for (;;) {
- struct sigaction sa_old, sa_new = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
-
- if (notify >= 0) {
- r = flush_fd(notify);
- if (r < 0)
- goto fail;
- }
-
- /* We pass here O_NOCTTY only so that we can check the return
- * value TIOCSCTTY and have a reliable way to figure out if we
- * successfully became the controlling process of the tty */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
- * if we already own the tty. */
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- /* First, try to get the tty */
- if (ioctl(fd, TIOCSCTTY, force) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- /* Sometimes it makes sense to ignore TIOCSCTTY
- * returning EPERM, i.e. when very likely we already
- * are have this controlling terminal. */
- if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
- r = 0;
-
- if (r < 0 && (force || fail || r != -EPERM)) {
- goto fail;
- }
-
- if (r >= 0)
- break;
-
- assert(!fail);
- assert(!force);
- assert(notify >= 0);
-
- for (;;) {
- union inotify_event_buffer buffer;
- struct inotify_event *e;
- ssize_t l;
-
- if (timeout != USEC_INFINITY) {
- usec_t n;
-
- n = now(CLOCK_MONOTONIC);
- if (ts + timeout < n) {
- r = -ETIMEDOUT;
- goto fail;
- }
-
- r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
- if (r < 0)
- goto fail;
-
- if (r == 0) {
- r = -ETIMEDOUT;
- goto fail;
- }
- }
-
- l = read(notify, &buffer, sizeof(buffer));
- if (l < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- r = -errno;
- goto fail;
- }
-
- FOREACH_INOTIFY_EVENT(e, buffer, l) {
- if (e->wd != wd || !(e->mask & IN_CLOSE)) {
- r = -EIO;
- goto fail;
- }
- }
-
- break;
- }
-
- /* We close the tty fd here since if the old session
- * ended our handle will be dead. It's important that
- * we do this after sleeping, so that we don't enter
- * an endless loop. */
- fd = safe_close(fd);
- }
-
- safe_close(notify);
-
- r = reset_terminal_fd(fd, true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal: %m");
-
- return fd;
-
-fail:
- safe_close(fd);
- safe_close(notify);
-
- return r;
-}
-
-int release_terminal(void) {
- static const struct sigaction sa_new = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
-
- _cleanup_close_ int fd = -1;
- struct sigaction sa_old;
- int r = 0;
-
- fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
- * by our own TIOCNOTTY */
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- if (ioctl(fd, TIOCNOTTY) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- return r;
-}
-
-int terminal_vhangup_fd(int fd) {
- assert(fd >= 0);
-
- if (ioctl(fd, TIOCVHANGUP) < 0)
- return -errno;
-
- return 0;
-}
-
-int terminal_vhangup(const char *name) {
- _cleanup_close_ int fd;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- return terminal_vhangup_fd(fd);
-}
-
-int vt_disallocate(const char *name) {
- int fd, r;
- unsigned u;
-
- /* Deallocate the VT if possible. If not possible
- * (i.e. because it is the active one), at least clear it
- * entirely (including the scrollback buffer) */
-
- if (!startswith(name, "/dev/"))
- return -EINVAL;
-
- if (!tty_is_vc(name)) {
- /* So this is not a VT. I guess we cannot deallocate
- * it then. But let's at least clear the screen */
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[2J", /* clear screen */
- 10, false);
- safe_close(fd);
-
- return 0;
- }
-
- if (!startswith(name, "/dev/tty"))
- return -EINVAL;
-
- r = safe_atou(name+8, &u);
- if (r < 0)
- return r;
-
- if (u <= 0)
- return -EINVAL;
-
- /* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- r = ioctl(fd, VT_DISALLOCATE, u);
- safe_close(fd);
-
- if (r >= 0)
- return 0;
-
- if (errno != EBUSY)
- return -errno;
-
- /* Couldn't deallocate, so let's clear it fully with
- * scrollback */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
- 10, false);
- safe_close(fd);
-
- return 0;
-}
-
-void warn_melody(void) {
- _cleanup_close_ int fd = -1;
-
- fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return;
-
- /* Yeah, this is synchronous. Kinda sucks. But well... */
-
- ioctl(fd, KIOCSOUND, (int)(1193180/440));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, 0);
-}
-
-int make_console_stdio(void) {
- int fd, r;
-
- /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
-
- fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
- if (fd < 0)
- return log_error_errno(fd, "Failed to acquire terminal: %m");
-
- r = make_stdio(fd);
- if (r < 0)
- return log_error_errno(r, "Failed to duplicate terminal fd: %m");
-
- return 0;
-}
-
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
- static const char status_indent[] = " "; /* "[" STATUS "] " */
- _cleanup_free_ char *s = NULL;
- _cleanup_close_ int fd = -1;
- struct iovec iovec[6] = {};
- int n = 0;
- static bool prev_ephemeral;
-
- assert(format);
-
- /* This is independent of logging, as status messages are
- * optional and go exclusively to the console. */
-
- if (vasprintf(&s, format, ap) < 0)
- return log_oom();
-
- fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- if (ellipse) {
- char *e;
- size_t emax, sl;
- int c;
-
- c = fd_columns(fd);
- if (c <= 0)
- c = 80;
-
- sl = status ? sizeof(status_indent)-1 : 0;
-
- emax = c - sl - 1;
- if (emax < 3)
- emax = 3;
-
- e = ellipsize(s, emax, 50);
- if (e) {
- free(s);
- s = e;
- }
- }
-
- if (prev_ephemeral)
- IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
- prev_ephemeral = ephemeral;
-
- if (status) {
- if (!isempty(status)) {
- IOVEC_SET_STRING(iovec[n++], "[");
- IOVEC_SET_STRING(iovec[n++], status);
- IOVEC_SET_STRING(iovec[n++], "] ");
- } else
- IOVEC_SET_STRING(iovec[n++], status_indent);
- }
-
- IOVEC_SET_STRING(iovec[n++], s);
- if (!ephemeral)
- IOVEC_SET_STRING(iovec[n++], "\n");
-
- if (writev(fd, iovec, n) < 0)
- return -errno;
-
- return 0;
-}
-
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
- va_list ap;
- int r;
-
- assert(format);
-
- va_start(ap, format);
- r = status_vprintf(status, ellipse, ephemeral, format, ap);
- va_end(ap);
-
- return r;
-}
-
-bool tty_is_vc(const char *tty) {
- assert(tty);
-
- return vtnr_from_tty(tty) >= 0;
-}
-
-bool tty_is_console(const char *tty) {
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- return streq(tty, "console");
-}
-
-int vtnr_from_tty(const char *tty) {
- int i, r;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (!startswith(tty, "tty") )
- return -EINVAL;
-
- if (tty[3] < '0' || tty[3] > '9')
- return -EINVAL;
-
- r = safe_atoi(tty+3, &i);
- if (r < 0)
- return r;
-
- if (i < 0 || i > 63)
- return -EINVAL;
-
- return i;
-}
-
-char *resolve_dev_console(char **active) {
- char *tty;
-
- /* Resolve where /dev/console is pointing to, if /sys is actually ours
- * (i.e. not read-only-mounted which is a sign for container setups) */
-
- if (path_is_read_only_fs("/sys") > 0)
- return NULL;
-
- if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
- return NULL;
-
- /* If multiple log outputs are configured the last one is what
- * /dev/console points to */
- tty = strrchr(*active, ' ');
- if (tty)
- tty++;
- else
- tty = *active;
-
- if (streq(tty, "tty0")) {
- char *tmp;
-
- /* Get the active VC (e.g. tty1) */
- if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
- free(*active);
- tty = *active = tmp;
- }
- }
-
- return tty;
-}
-
-bool tty_is_vc_resolve(const char *tty) {
- _cleanup_free_ char *active = NULL;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (streq(tty, "console")) {
- tty = resolve_dev_console(&active);
- if (!tty)
- return false;
- }
-
- return tty_is_vc(tty);
-}
-
-const char *default_term_for_tty(const char *tty) {
- assert(tty);
-
- return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
-}
-
-int fd_columns(int fd) {
- struct winsize ws = {};
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_col <= 0)
- return -EIO;
-
- return ws.ws_col;
-}
-
-unsigned columns(void) {
- const char *e;
- int c;
-
- if (_likely_(cached_columns > 0))
- return cached_columns;
-
- c = 0;
- e = getenv("COLUMNS");
- if (e)
- (void) safe_atoi(e, &c);
-
- if (c <= 0)
- c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
-
- cached_columns = c;
- return cached_columns;
-}
-
-int fd_lines(int fd) {
- struct winsize ws = {};
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_row <= 0)
- return -EIO;
-
- return ws.ws_row;
-}
-
-unsigned lines(void) {
- const char *e;
- int l;
-
- if (_likely_(cached_lines > 0))
- return cached_lines;
-
- l = 0;
- e = getenv("LINES");
- if (e)
- (void) safe_atoi(e, &l);
-
- if (l <= 0)
- l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
-
- cached_lines = l;
- return cached_lines;
-}
-
-/* intended to be used as a SIGWINCH sighandler */
-void columns_lines_cache_reset(int signum) {
- cached_columns = 0;
- cached_lines = 0;
-}
-
-bool on_tty(void) {
- static int cached_on_tty = -1;
-
- if (_unlikely_(cached_on_tty < 0))
- cached_on_tty = isatty(STDOUT_FILENO) > 0;
-
- return cached_on_tty;
-}
-
-int make_stdio(int fd) {
- int r, s, t;
-
- assert(fd >= 0);
-
- r = dup2(fd, STDIN_FILENO);
- s = dup2(fd, STDOUT_FILENO);
- t = dup2(fd, STDERR_FILENO);
-
- if (fd >= 3)
- safe_close(fd);
-
- if (r < 0 || s < 0 || t < 0)
- return -errno;
-
- /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
- * dup2() was a NOP and the bit hence possibly set. */
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
-
- return 0;
-}
-
-int make_null_stdio(void) {
- int null_fd;
-
- null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
- if (null_fd < 0)
- return -errno;
-
- return make_stdio(null_fd);
-}
-
-int getttyname_malloc(int fd, char **ret) {
- size_t l = 100;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- for (;;) {
- char path[l];
-
- r = ttyname_r(fd, path, sizeof(path));
- if (r == 0) {
- const char *p;
- char *c;
-
- p = startswith(path, "/dev/");
- c = strdup(p ?: path);
- if (!c)
- return -ENOMEM;
-
- *ret = c;
- return 0;
- }
-
- if (r != ERANGE)
- return -r;
-
- l *= 2;
- }
-
- return 0;
-}
-
-int getttyname_harder(int fd, char **r) {
- int k;
- char *s = NULL;
-
- k = getttyname_malloc(fd, &s);
- if (k < 0)
- return k;
-
- if (streq(s, "tty")) {
- free(s);
- return get_ctty(0, NULL, r);
- }
-
- *r = s;
- return 0;
-}
-
-int get_ctty_devnr(pid_t pid, dev_t *d) {
- int r;
- _cleanup_free_ char *line = NULL;
- const char *p;
- unsigned long ttynr;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%*d " /* ppid */
- "%*d " /* pgrp */
- "%*d " /* session */
- "%lu ", /* ttynr */
- &ttynr) != 1)
- return -EIO;
-
- if (major(ttynr) == 0 && minor(ttynr) == 0)
- return -ENXIO;
-
- if (d)
- *d = (dev_t) ttynr;
-
- return 0;
-}
-
-int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
- char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
- _cleanup_free_ char *s = NULL;
- const char *p;
- dev_t devnr;
- int k;
-
- assert(r);
-
- k = get_ctty_devnr(pid, &devnr);
- if (k < 0)
- return k;
-
- sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
-
- k = readlink_malloc(fn, &s);
- if (k < 0) {
-
- if (k != -ENOENT)
- return k;
-
- /* This is an ugly hack */
- if (major(devnr) == 136) {
- if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
- return -ENOMEM;
- } else {
- /* Probably something like the ptys which have no
- * symlink in /dev/char. Let's return something
- * vaguely useful. */
-
- b = strdup(fn + 5);
- if (!b)
- return -ENOMEM;
- }
- } else {
- if (startswith(s, "/dev/"))
- p = s + 5;
- else if (startswith(s, "../"))
- p = s + 3;
- else
- p = s;
-
- b = strdup(p);
- if (!b)
- return -ENOMEM;
- }
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
-}
diff --git a/src/shared/terminal-util.h b/src/shared/terminal-util.h
deleted file mode 100644
index 188714f..0000000
--- a/src/shared/terminal-util.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "macro.h"
-#include "time-util.h"
-
-#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
-#define ANSI_RED_ON "\x1B[31m"
-#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
-#define ANSI_GREEN_ON "\x1B[32m"
-#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
-#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
-#define ANSI_HIGHLIGHT_BLUE_ON "\x1B[1;34m"
-#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
-#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
-
-int reset_terminal_fd(int fd, bool switch_to_text);
-int reset_terminal(const char *name);
-
-int open_terminal(const char *name, int mode);
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
-int release_terminal(void);
-
-int terminal_vhangup_fd(int fd);
-int terminal_vhangup(const char *name);
-
-int chvt(int vt);
-
-int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
-int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
-int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
-
-int vt_disallocate(const char *name);
-
-char *resolve_dev_console(char **active);
-bool tty_is_vc(const char *tty);
-bool tty_is_vc_resolve(const char *tty);
-bool tty_is_console(const char *tty) _pure_;
-int vtnr_from_tty(const char *tty);
-const char *default_term_for_tty(const char *tty);
-
-void warn_melody(void);
-
-int make_stdio(int fd);
-int make_null_stdio(void);
-int make_console_stdio(void);
-
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
-
-int fd_columns(int fd);
-unsigned columns(void);
-int fd_lines(int fd);
-unsigned lines(void);
-void columns_lines_cache_reset(int _unused_ signum);
-
-bool on_tty(void);
-
-static inline const char *ansi_highlight(void) {
- return on_tty() ? ANSI_HIGHLIGHT_ON : "";
-}
-
-static inline const char *ansi_highlight_red(void) {
- return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
-}
-
-static inline const char *ansi_highlight_green(void) {
- return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
-}
-
-static inline const char *ansi_highlight_yellow(void) {
- return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
-}
-
-static inline const char *ansi_highlight_blue(void) {
- return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
-}
-
-static inline const char *ansi_highlight_off(void) {
- return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
-}
-
-int get_ctty_devnr(pid_t pid, dev_t *d);
-int get_ctty(pid_t, dev_t *_devnr, char **r);
-
-int getttyname_malloc(int fd, char **r);
-int getttyname_harder(int fd, char **r);
diff --git a/src/shared/time-util.c b/src/shared/time-util.c
deleted file mode 100644
index 12f1b19..0000000
--- a/src/shared/time-util.c
+++ /dev/null
@@ -1,997 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <time.h>
-#include <string.h>
-#include <sys/timex.h>
-#include <sys/timerfd.h>
-
-#include "util.h"
-#include "time-util.h"
-#include "strv.h"
-
-usec_t now(clockid_t clock_id) {
- struct timespec ts;
-
- assert_se(clock_gettime(clock_id, &ts) == 0);
-
- return timespec_load(&ts);
-}
-
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
- assert(ts);
-
- ts->realtime = now(CLOCK_REALTIME);
- ts->monotonic = now(CLOCK_MONOTONIC);
-
- return ts;
-}
-
-dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
- int64_t delta;
- assert(ts);
-
- if (u == USEC_INFINITY || u <= 0) {
- ts->realtime = ts->monotonic = u;
- return ts;
- }
-
- ts->realtime = u;
-
- delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
- ts->monotonic = now(CLOCK_MONOTONIC);
-
- if ((int64_t) ts->monotonic > delta)
- ts->monotonic -= delta;
- else
- ts->monotonic = 0;
-
- return ts;
-}
-
-dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
- int64_t delta;
- assert(ts);
-
- if (u == USEC_INFINITY) {
- ts->realtime = ts->monotonic = USEC_INFINITY;
- return ts;
- }
-
- ts->monotonic = u;
- delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
-
- ts->realtime = now(CLOCK_REALTIME);
- if ((int64_t) ts->realtime > delta)
- ts->realtime -= delta;
- else
- ts->realtime = 0;
-
- return ts;
-}
-
-usec_t timespec_load(const struct timespec *ts) {
- assert(ts);
-
- if (ts->tv_sec == (time_t) -1 &&
- ts->tv_nsec == (long) -1)
- return USEC_INFINITY;
-
- if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
- return USEC_INFINITY;
-
- return
- (usec_t) ts->tv_sec * USEC_PER_SEC +
- (usec_t) ts->tv_nsec / NSEC_PER_USEC;
-}
-
-struct timespec *timespec_store(struct timespec *ts, usec_t u) {
- assert(ts);
-
- if (u == USEC_INFINITY) {
- ts->tv_sec = (time_t) -1;
- ts->tv_nsec = (long) -1;
- return ts;
- }
-
- ts->tv_sec = (time_t) (u / USEC_PER_SEC);
- ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
-
- return ts;
-}
-
-usec_t timeval_load(const struct timeval *tv) {
- assert(tv);
-
- if (tv->tv_sec == (time_t) -1 &&
- tv->tv_usec == (suseconds_t) -1)
- return USEC_INFINITY;
-
- if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
- return USEC_INFINITY;
-
- return
- (usec_t) tv->tv_sec * USEC_PER_SEC +
- (usec_t) tv->tv_usec;
-}
-
-struct timeval *timeval_store(struct timeval *tv, usec_t u) {
- assert(tv);
-
- if (u == USEC_INFINITY) {
- tv->tv_sec = (time_t) -1;
- tv->tv_usec = (suseconds_t) -1;
- } else {
- tv->tv_sec = (time_t) (u / USEC_PER_SEC);
- tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
- }
-
- return tv;
-}
-
-static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
- struct tm tm;
- time_t sec;
-
- assert(buf);
- assert(l > 0);
-
- if (t <= 0 || t == USEC_INFINITY)
- return NULL;
-
- sec = (time_t) (t / USEC_PER_SEC);
-
- if (utc)
- gmtime_r(&sec, &tm);
- else
- localtime_r(&sec, &tm);
- if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
- return NULL;
-
- return buf;
-}
-
-char *format_timestamp(char *buf, size_t l, usec_t t) {
- return format_timestamp_internal(buf, l, t, false);
-}
-
-char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
- return format_timestamp_internal(buf, l, t, true);
-}
-
-static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
- struct tm tm;
- time_t sec;
-
- assert(buf);
- assert(l > 0);
-
- if (t <= 0 || t == USEC_INFINITY)
- return NULL;
-
- sec = (time_t) (t / USEC_PER_SEC);
- if (utc)
- gmtime_r(&sec, &tm);
- else
- localtime_r(&sec, &tm);
-
- if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
- return NULL;
- snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
- if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
- return NULL;
-
- return buf;
-}
-
-char *format_timestamp_us(char *buf, size_t l, usec_t t) {
- return format_timestamp_internal_us(buf, l, t, false);
-}
-
-char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
- return format_timestamp_internal_us(buf, l, t, true);
-}
-
-char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
- const char *s;
- usec_t n, d;
-
- if (t <= 0 || t == USEC_INFINITY)
- return NULL;
-
- n = now(CLOCK_REALTIME);
- if (n > t) {
- d = n - t;
- s = "ago";
- } else {
- d = t - n;
- s = "left";
- }
-
- if (d >= USEC_PER_YEAR)
- snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
- d / USEC_PER_YEAR,
- (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
- else if (d >= USEC_PER_MONTH)
- snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
- d / USEC_PER_MONTH,
- (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
- else if (d >= USEC_PER_WEEK)
- snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
- d / USEC_PER_WEEK,
- (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
- else if (d >= 2*USEC_PER_DAY)
- snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
- else if (d >= 25*USEC_PER_HOUR)
- snprintf(buf, l, "1 day " USEC_FMT "h %s",
- (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
- else if (d >= 6*USEC_PER_HOUR)
- snprintf(buf, l, USEC_FMT "h %s",
- d / USEC_PER_HOUR, s);
- else if (d >= USEC_PER_HOUR)
- snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
- d / USEC_PER_HOUR,
- (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
- else if (d >= 5*USEC_PER_MINUTE)
- snprintf(buf, l, USEC_FMT "min %s",
- d / USEC_PER_MINUTE, s);
- else if (d >= USEC_PER_MINUTE)
- snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
- d / USEC_PER_MINUTE,
- (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
- else if (d >= USEC_PER_SEC)
- snprintf(buf, l, USEC_FMT "s %s",
- d / USEC_PER_SEC, s);
- else if (d >= USEC_PER_MSEC)
- snprintf(buf, l, USEC_FMT "ms %s",
- d / USEC_PER_MSEC, s);
- else if (d > 0)
- snprintf(buf, l, USEC_FMT"us %s",
- d, s);
- else
- snprintf(buf, l, "now");
-
- buf[l-1] = 0;
- return buf;
-}
-
-char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
- static const struct {
- const char *suffix;
- usec_t usec;
- } table[] = {
- { "y", USEC_PER_YEAR },
- { "month", USEC_PER_MONTH },
- { "w", USEC_PER_WEEK },
- { "d", USEC_PER_DAY },
- { "h", USEC_PER_HOUR },
- { "min", USEC_PER_MINUTE },
- { "s", USEC_PER_SEC },
- { "ms", USEC_PER_MSEC },
- { "us", 1 },
- };
-
- unsigned i;
- char *p = buf;
- bool something = false;
-
- assert(buf);
- assert(l > 0);
-
- if (t == USEC_INFINITY) {
- strncpy(p, "infinity", l-1);
- p[l-1] = 0;
- return p;
- }
-
- if (t <= 0) {
- strncpy(p, "0", l-1);
- p[l-1] = 0;
- return p;
- }
-
- /* The result of this function can be parsed with parse_sec */
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
- int k = 0;
- size_t n;
- bool done = false;
- usec_t a, b;
-
- if (t <= 0)
- break;
-
- if (t < accuracy && something)
- break;
-
- if (t < table[i].usec)
- continue;
-
- if (l <= 1)
- break;
-
- a = t / table[i].usec;
- b = t % table[i].usec;
-
- /* Let's see if we should shows this in dot notation */
- if (t < USEC_PER_MINUTE && b > 0) {
- usec_t cc;
- int j;
-
- j = 0;
- for (cc = table[i].usec; cc > 1; cc /= 10)
- j++;
-
- for (cc = accuracy; cc > 1; cc /= 10) {
- b /= 10;
- j--;
- }
-
- if (j > 0) {
- k = snprintf(p, l,
- "%s"USEC_FMT".%0*llu%s",
- p > buf ? " " : "",
- a,
- j,
- (unsigned long long) b,
- table[i].suffix);
-
- t = 0;
- done = true;
- }
- }
-
- /* No? Then let's show it normally */
- if (!done) {
- k = snprintf(p, l,
- "%s"USEC_FMT"%s",
- p > buf ? " " : "",
- a,
- table[i].suffix);
-
- t = b;
- }
-
- n = MIN((size_t) k, l);
-
- l -= n;
- p += n;
-
- something = true;
- }
-
- *p = 0;
-
- return buf;
-}
-
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
-
- assert(f);
- assert(name);
- assert(t);
-
- if (!dual_timestamp_is_set(t))
- return;
-
- fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
- name,
- t->realtime,
- t->monotonic);
-}
-
-int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
- unsigned long long a, b;
-
- assert(value);
- assert(t);
-
- if (sscanf(value, "%llu %llu", &a, &b) != 2) {
- log_debug("Failed to parse finish timestamp value %s.", value);
- return -EINVAL;
- }
-
- t->realtime = a;
- t->monotonic = b;
-
- return 0;
-}
-
-int parse_timestamp(const char *t, usec_t *usec) {
- static const struct {
- const char *name;
- const int nr;
- } day_nr[] = {
- { "Sunday", 0 },
- { "Sun", 0 },
- { "Monday", 1 },
- { "Mon", 1 },
- { "Tuesday", 2 },
- { "Tue", 2 },
- { "Wednesday", 3 },
- { "Wed", 3 },
- { "Thursday", 4 },
- { "Thu", 4 },
- { "Friday", 5 },
- { "Fri", 5 },
- { "Saturday", 6 },
- { "Sat", 6 },
- };
-
- const char *k;
- struct tm tm, copy;
- time_t x;
- usec_t plus = 0, minus = 0, ret;
- int r, weekday = -1;
- unsigned i;
-
- /*
- * Allowed syntaxes:
- *
- * 2012-09-22 16:34:22
- * 2012-09-22 16:34 (seconds will be set to 0)
- * 2012-09-22 (time will be set to 00:00:00)
- * 16:34:22 (date will be set to today)
- * 16:34 (date will be set to today, seconds to 0)
- * now
- * yesterday (time is set to 00:00:00)
- * today (time is set to 00:00:00)
- * tomorrow (time is set to 00:00:00)
- * +5min
- * -5days
- * @2147483647 (seconds since epoch)
- *
- */
-
- assert(t);
- assert(usec);
-
- x = time(NULL);
- assert_se(localtime_r(&x, &tm));
- tm.tm_isdst = -1;
-
- if (streq(t, "now"))
- goto finish;
-
- else if (streq(t, "today")) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
-
- } else if (streq(t, "yesterday")) {
- tm.tm_mday --;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
-
- } else if (streq(t, "tomorrow")) {
- tm.tm_mday ++;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
-
- } else if (t[0] == '+') {
- r = parse_sec(t+1, &plus);
- if (r < 0)
- return r;
-
- goto finish;
-
- } else if (t[0] == '-') {
- r = parse_sec(t+1, &minus);
- if (r < 0)
- return r;
-
- goto finish;
-
- } else if (t[0] == '@')
- return parse_sec(t + 1, usec);
-
- else if (endswith(t, " ago")) {
- _cleanup_free_ char *z;
-
- z = strndup(t, strlen(t) - 4);
- if (!z)
- return -ENOMEM;
-
- r = parse_sec(z, &minus);
- if (r < 0)
- return r;
-
- goto finish;
- } else if (endswith(t, " left")) {
- _cleanup_free_ char *z;
-
- z = strndup(t, strlen(t) - 4);
- if (!z)
- return -ENOMEM;
-
- r = parse_sec(z, &plus);
- if (r < 0)
- return r;
-
- goto finish;
- }
-
- for (i = 0; i < ELEMENTSOF(day_nr); i++) {
- size_t skip;
-
- if (!startswith_no_case(t, day_nr[i].name))
- continue;
-
- skip = strlen(day_nr[i].name);
- if (t[skip] != ' ')
- continue;
-
- weekday = day_nr[i].nr;
- t += skip + 1;
- break;
- }
-
- copy = tm;
- k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
-
- tm = copy;
- k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
-
- tm = copy;
- k = strptime(t, "%y-%m-%d %H:%M", &tm);
- if (k && *k == 0) {
- tm.tm_sec = 0;
- goto finish;
- }
-
- tm = copy;
- k = strptime(t, "%Y-%m-%d %H:%M", &tm);
- if (k && *k == 0) {
- tm.tm_sec = 0;
- goto finish;
- }
-
- tm = copy;
- k = strptime(t, "%y-%m-%d", &tm);
- if (k && *k == 0) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
- }
-
- tm = copy;
- k = strptime(t, "%Y-%m-%d", &tm);
- if (k && *k == 0) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
- }
-
- tm = copy;
- k = strptime(t, "%H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
-
- tm = copy;
- k = strptime(t, "%H:%M", &tm);
- if (k && *k == 0) {
- tm.tm_sec = 0;
- goto finish;
- }
-
- return -EINVAL;
-
-finish:
- x = mktime(&tm);
- if (x == (time_t) -1)
- return -EINVAL;
-
- if (weekday >= 0 && tm.tm_wday != weekday)
- return -EINVAL;
-
- ret = (usec_t) x * USEC_PER_SEC;
-
- ret += plus;
- if (ret > minus)
- ret -= minus;
- else
- ret = 0;
-
- *usec = ret;
-
- return 0;
-}
-
-int parse_sec(const char *t, usec_t *usec) {
- static const struct {
- const char *suffix;
- usec_t usec;
- } table[] = {
- { "seconds", USEC_PER_SEC },
- { "second", USEC_PER_SEC },
- { "sec", USEC_PER_SEC },
- { "s", USEC_PER_SEC },
- { "minutes", USEC_PER_MINUTE },
- { "minute", USEC_PER_MINUTE },
- { "min", USEC_PER_MINUTE },
- { "months", USEC_PER_MONTH },
- { "month", USEC_PER_MONTH },
- { "msec", USEC_PER_MSEC },
- { "ms", USEC_PER_MSEC },
- { "m", USEC_PER_MINUTE },
- { "hours", USEC_PER_HOUR },
- { "hour", USEC_PER_HOUR },
- { "hr", USEC_PER_HOUR },
- { "h", USEC_PER_HOUR },
- { "days", USEC_PER_DAY },
- { "day", USEC_PER_DAY },
- { "d", USEC_PER_DAY },
- { "weeks", USEC_PER_WEEK },
- { "week", USEC_PER_WEEK },
- { "w", USEC_PER_WEEK },
- { "years", USEC_PER_YEAR },
- { "year", USEC_PER_YEAR },
- { "y", USEC_PER_YEAR },
- { "usec", 1ULL },
- { "us", 1ULL },
- { "", USEC_PER_SEC }, /* default is sec */
- };
-
- const char *p, *s;
- usec_t r = 0;
- bool something = false;
-
- assert(t);
- assert(usec);
-
- p = t;
-
- p += strspn(p, WHITESPACE);
- s = startswith(p, "infinity");
- if (s) {
- s += strspn(s, WHITESPACE);
- if (*s != 0)
- return -EINVAL;
-
- *usec = USEC_INFINITY;
- return 0;
- }
-
- for (;;) {
- long long l, z = 0;
- char *e;
- unsigned i, n = 0;
-
- p += strspn(p, WHITESPACE);
-
- if (*p == 0) {
- if (!something)
- return -EINVAL;
-
- break;
- }
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno > 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (*e == '.') {
- char *b = e + 1;
-
- errno = 0;
- z = strtoll(b, &e, 10);
- if (errno > 0)
- return -errno;
-
- if (z < 0)
- return -ERANGE;
-
- if (e == b)
- return -EINVAL;
-
- n = e - b;
-
- } else if (e == p)
- return -EINVAL;
-
- e += strspn(e, WHITESPACE);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- usec_t k = (usec_t) z * table[i].usec;
-
- for (; n > 0; n--)
- k /= 10;
-
- r += (usec_t) l * table[i].usec + k;
- p = e + strlen(table[i].suffix);
-
- something = true;
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
- }
-
- *usec = r;
-
- return 0;
-}
-
-int parse_nsec(const char *t, nsec_t *nsec) {
- static const struct {
- const char *suffix;
- nsec_t nsec;
- } table[] = {
- { "seconds", NSEC_PER_SEC },
- { "second", NSEC_PER_SEC },
- { "sec", NSEC_PER_SEC },
- { "s", NSEC_PER_SEC },
- { "minutes", NSEC_PER_MINUTE },
- { "minute", NSEC_PER_MINUTE },
- { "min", NSEC_PER_MINUTE },
- { "months", NSEC_PER_MONTH },
- { "month", NSEC_PER_MONTH },
- { "msec", NSEC_PER_MSEC },
- { "ms", NSEC_PER_MSEC },
- { "m", NSEC_PER_MINUTE },
- { "hours", NSEC_PER_HOUR },
- { "hour", NSEC_PER_HOUR },
- { "hr", NSEC_PER_HOUR },
- { "h", NSEC_PER_HOUR },
- { "days", NSEC_PER_DAY },
- { "day", NSEC_PER_DAY },
- { "d", NSEC_PER_DAY },
- { "weeks", NSEC_PER_WEEK },
- { "week", NSEC_PER_WEEK },
- { "w", NSEC_PER_WEEK },
- { "years", NSEC_PER_YEAR },
- { "year", NSEC_PER_YEAR },
- { "y", NSEC_PER_YEAR },
- { "usec", NSEC_PER_USEC },
- { "us", NSEC_PER_USEC },
- { "nsec", 1ULL },
- { "ns", 1ULL },
- { "", 1ULL }, /* default is nsec */
- };
-
- const char *p, *s;
- nsec_t r = 0;
- bool something = false;
-
- assert(t);
- assert(nsec);
-
- p = t;
-
- p += strspn(p, WHITESPACE);
- s = startswith(p, "infinity");
- if (s) {
- s += strspn(s, WHITESPACE);
- if (*s != 0)
- return -EINVAL;
-
- *nsec = NSEC_INFINITY;
- return 0;
- }
-
- for (;;) {
- long long l, z = 0;
- char *e;
- unsigned i, n = 0;
-
- p += strspn(p, WHITESPACE);
-
- if (*p == 0) {
- if (!something)
- return -EINVAL;
-
- break;
- }
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno > 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (*e == '.') {
- char *b = e + 1;
-
- errno = 0;
- z = strtoll(b, &e, 10);
- if (errno > 0)
- return -errno;
-
- if (z < 0)
- return -ERANGE;
-
- if (e == b)
- return -EINVAL;
-
- n = e - b;
-
- } else if (e == p)
- return -EINVAL;
-
- e += strspn(e, WHITESPACE);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- nsec_t k = (nsec_t) z * table[i].nsec;
-
- for (; n > 0; n--)
- k /= 10;
-
- r += (nsec_t) l * table[i].nsec + k;
- p = e + strlen(table[i].suffix);
-
- something = true;
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
- }
-
- *nsec = r;
-
- return 0;
-}
-
-bool ntp_synced(void) {
- struct timex txc = {};
-
- if (adjtimex(&txc) < 0)
- return false;
-
- if (txc.status & STA_UNSYNC)
- return false;
-
- return true;
-}
-
-int get_timezones(char ***ret) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_strv_free_ char **zones = NULL;
- size_t n_zones = 0, n_allocated = 0;
-
- assert(ret);
-
- zones = strv_new("UTC", NULL);
- if (!zones)
- return -ENOMEM;
-
- n_allocated = 2;
- n_zones = 1;
-
- f = fopen("/usr/share/zoneinfo/zone.tab", "re");
- if (f) {
- char l[LINE_MAX];
-
- FOREACH_LINE(l, f, return -errno) {
- char *p, *w;
- size_t k;
-
- p = strstrip(l);
-
- if (isempty(p) || *p == '#')
- continue;
-
- /* Skip over country code */
- p += strcspn(p, WHITESPACE);
- p += strspn(p, WHITESPACE);
-
- /* Skip over coordinates */
- p += strcspn(p, WHITESPACE);
- p += strspn(p, WHITESPACE);
-
- /* Found timezone name */
- k = strcspn(p, WHITESPACE);
- if (k <= 0)
- continue;
-
- w = strndup(p, k);
- if (!w)
- return -ENOMEM;
-
- if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
- free(w);
- return -ENOMEM;
- }
-
- zones[n_zones++] = w;
- zones[n_zones] = NULL;
- }
-
- strv_sort(zones);
-
- } else if (errno != ENOENT)
- return -errno;
-
- *ret = zones;
- zones = NULL;
-
- return 0;
-}
-
-bool timezone_is_valid(const char *name) {
- bool slash = false;
- const char *p, *t;
- struct stat st;
-
- if (!name || *name == 0 || *name == '/')
- return false;
-
- for (p = name; *p; p++) {
- if (!(*p >= '0' && *p <= '9') &&
- !(*p >= 'a' && *p <= 'z') &&
- !(*p >= 'A' && *p <= 'Z') &&
- !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
- return false;
-
- if (*p == '/') {
-
- if (slash)
- return false;
-
- slash = true;
- } else
- slash = false;
- }
-
- if (slash)
- return false;
-
- t = strjoina("/usr/share/zoneinfo/", name);
- if (stat(t, &st) < 0)
- return false;
-
- if (!S_ISREG(st.st_mode))
- return false;
-
- 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;
- }
-
- return clock;
-}
diff --git a/src/shared/time-util.h b/src/shared/time-util.h
deleted file mode 100644
index 7a64d45..0000000
--- a/src/shared/time-util.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdio.h>
-#include <inttypes.h>
-
-typedef uint64_t usec_t;
-typedef uint64_t nsec_t;
-
-#define NSEC_FMT "%" PRIu64
-#define USEC_FMT "%" PRIu64
-
-#include "macro.h"
-
-typedef struct dual_timestamp {
- usec_t realtime;
- usec_t monotonic;
-} dual_timestamp;
-
-#define USEC_INFINITY ((usec_t) -1)
-#define NSEC_INFINITY ((nsec_t) -1)
-
-#define MSEC_PER_SEC 1000ULL
-#define USEC_PER_SEC ((usec_t) 1000000ULL)
-#define USEC_PER_MSEC ((usec_t) 1000ULL)
-#define NSEC_PER_SEC ((nsec_t) 1000000000ULL)
-#define NSEC_PER_MSEC ((nsec_t) 1000000ULL)
-#define NSEC_PER_USEC ((nsec_t) 1000ULL)
-
-#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC))
-#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC))
-#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE))
-#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE))
-#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR))
-#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR))
-#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY))
-#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY))
-#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC))
-#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC))
-#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
-#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
-
-#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
-#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
-#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
-#define FORMAT_TIMESPAN_MAX 64
-
-#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
-
-#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
-
-usec_t now(clockid_t clock);
-
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
-dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
-dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
-
-static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
- return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
- (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
-}
-
-usec_t timespec_load(const struct timespec *ts) _pure_;
-struct timespec *timespec_store(struct timespec *ts, usec_t u);
-
-usec_t timeval_load(const struct timeval *tv) _pure_;
-struct timeval *timeval_store(struct timeval *tv, usec_t u);
-
-char *format_timestamp(char *buf, size_t l, usec_t t);
-char *format_timestamp_utc(char *buf, size_t l, usec_t t);
-char *format_timestamp_us(char *buf, size_t l, usec_t t);
-char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
-char *format_timestamp_relative(char *buf, size_t l, usec_t t);
-char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
-
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
-int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
-
-int parse_timestamp(const char *t, usec_t *usec);
-
-int parse_sec(const char *t, usec_t *usec);
-int parse_nsec(const char *t, nsec_t *nsec);
-
-bool ntp_synced(void);
-
-int get_timezones(char ***l);
-bool timezone_is_valid(const char *name);
-
-clockid_t clock_boottime_or_monotonic(void);
-
-#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)
diff --git a/src/shared/unaligned.h b/src/shared/unaligned.h
deleted file mode 100644
index d6181dd..0000000
--- a/src/shared/unaligned.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen
-
- 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 <stdint.h>
-
-static inline uint16_t unaligned_read_be16(const void *_u) {
- const uint8_t *u = _u;
-
- return (((uint16_t) u[0]) << 8) |
- ((uint16_t) u[1]);
-}
-
-static inline uint32_t unaligned_read_be32(const void *_u) {
- const uint8_t *u = _u;
-
- return (((uint32_t) unaligned_read_be16(u)) << 16) |
- ((uint32_t) unaligned_read_be16(u + 2));
-}
-
-static inline uint64_t unaligned_read_be64(const void *_u) {
- const uint8_t *u = _u;
-
- return (((uint64_t) unaligned_read_be32(u)) << 32) |
- ((uint64_t) unaligned_read_be32(u + 4));
-}
-
-static inline void unaligned_write_be16(void *_u, uint16_t a) {
- uint8_t *u = _u;
-
- u[0] = (uint8_t) (a >> 8);
- u[1] = (uint8_t) a;
-}
-
-static inline void unaligned_write_be32(void *_u, uint32_t a) {
- uint8_t *u = _u;
-
- unaligned_write_be16(u, (uint16_t) (a >> 16));
- unaligned_write_be16(u + 2, (uint16_t) a);
-}
-
-static inline void unaligned_write_be64(void *_u, uint64_t a) {
- uint8_t *u = _u;
-
- unaligned_write_be32(u, (uint32_t) (a >> 32));
- unaligned_write_be32(u + 4, (uint32_t) a);
-}
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
deleted file mode 100644
index bf52463..0000000
--- a/src/shared/unit-name.c
+++ /dev/null
@@ -1,834 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <errno.h>
-#include <string.h>
-
-#include "path-util.h"
-#include "bus-label.h"
-#include "util.h"
-#include "unit-name.h"
-#include "def.h"
-#include "strv.h"
-
-#define VALID_CHARS \
- DIGITS LETTERS \
- ":-_.\\"
-
-bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
- const char *e, *i, *at;
-
- assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
-
- if (_unlikely_(flags == 0))
- return false;
-
- if (isempty(n))
- return false;
-
- if (strlen(n) >= UNIT_NAME_MAX)
- return false;
-
- e = strrchr(n, '.');
- if (!e || e == n)
- return false;
-
- if (unit_type_from_string(e + 1) < 0)
- return false;
-
- for (i = n, at = NULL; i < e; i++) {
-
- if (*i == '@' && !at)
- at = i;
-
- if (!strchr("@" VALID_CHARS, *i))
- return false;
- }
-
- if (at == n)
- return false;
-
- if (flags & UNIT_NAME_PLAIN)
- if (!at)
- return true;
-
- if (flags & UNIT_NAME_INSTANCE)
- if (at && e > at + 1)
- return true;
-
- if (flags & UNIT_NAME_TEMPLATE)
- if (at && e == at + 1)
- return true;
-
- return false;
-}
-
-bool unit_prefix_is_valid(const char *p) {
-
- /* We don't allow additional @ in the prefix string */
-
- if (isempty(p))
- return false;
-
- return in_charset(p, VALID_CHARS);
-}
-
-bool unit_instance_is_valid(const char *i) {
-
- /* The max length depends on the length of the string, so we
- * don't really check this here. */
-
- if (isempty(i))
- return false;
-
- /* We allow additional @ in the instance string, we do not
- * allow them in the prefix! */
-
- return in_charset(i, "@" VALID_CHARS);
-}
-
-bool unit_suffix_is_valid(const char *s) {
- if (isempty(s))
- return false;
-
- if (s[0] != '.')
- return false;
-
- if (unit_type_from_string(s + 1) < 0)
- return false;
-
- return true;
-}
-
-int unit_name_to_prefix(const char *n, char **ret) {
- const char *p;
- char *s;
-
- assert(n);
- assert(ret);
-
- if (!unit_name_is_valid(n, UNIT_NAME_ANY))
- return -EINVAL;
-
- p = strchr(n, '@');
- if (!p)
- p = strrchr(n, '.');
-
- assert_se(p);
-
- s = strndup(n, p - n);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
-}
-
-int unit_name_to_instance(const char *n, char **instance) {
- const char *p, *d;
- char *i;
-
- assert(n);
- assert(instance);
-
- if (!unit_name_is_valid(n, UNIT_NAME_ANY))
- return -EINVAL;
-
- /* Everything past the first @ and before the last . is the instance */
- p = strchr(n, '@');
- if (!p) {
- *instance = NULL;
- return 0;
- }
-
- p++;
-
- d = strrchr(p, '.');
- if (!d)
- return -EINVAL;
-
- i = strndup(p, d-p);
- if (!i)
- return -ENOMEM;
-
- *instance = i;
- return 1;
-}
-
-int unit_name_to_prefix_and_instance(const char *n, char **ret) {
- const char *d;
- char *s;
-
- assert(n);
- assert(ret);
-
- if (!unit_name_is_valid(n, UNIT_NAME_ANY))
- return -EINVAL;
-
- d = strrchr(n, '.');
- if (!d)
- return -EINVAL;
-
- s = strndup(n, d - n);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
-}
-
-UnitType unit_name_to_type(const char *n) {
- const char *e;
-
- assert(n);
-
- if (!unit_name_is_valid(n, UNIT_NAME_ANY))
- return _UNIT_TYPE_INVALID;
-
- assert_se(e = strrchr(n, '.'));
-
- return unit_type_from_string(e + 1);
-}
-
-int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
- char *e, *s;
- size_t a, b;
-
- assert(n);
- assert(suffix);
- assert(ret);
-
- if (!unit_name_is_valid(n, UNIT_NAME_ANY))
- return -EINVAL;
-
- if (!unit_suffix_is_valid(suffix))
- return -EINVAL;
-
- assert_se(e = strrchr(n, '.'));
-
- a = e - n;
- b = strlen(suffix);
-
- s = new(char, a + b + 1);
- if (!s)
- return -ENOMEM;
-
- strcpy(mempcpy(s, n, a), suffix);
- *ret = s;
-
- return 0;
-}
-
-int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
- char *s;
-
- assert(prefix);
- assert(suffix);
- assert(ret);
-
- if (!unit_prefix_is_valid(prefix))
- return -EINVAL;
-
- if (instance && !unit_instance_is_valid(instance))
- return -EINVAL;
-
- if (!unit_suffix_is_valid(suffix))
- return -EINVAL;
-
- if (!instance)
- s = strappend(prefix, suffix);
- else
- s = strjoin(prefix, "@", instance, suffix, NULL);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
-}
-
-static char *do_escape_char(char c, char *t) {
- assert(t);
-
- *(t++) = '\\';
- *(t++) = 'x';
- *(t++) = hexchar(c >> 4);
- *(t++) = hexchar(c);
-
- return t;
-}
-
-static char *do_escape(const char *f, char *t) {
- assert(f);
- assert(t);
-
- /* do not create units with a leading '.', like for "/.dotdir" mount points */
- if (*f == '.') {
- t = do_escape_char(*f, t);
- f++;
- }
-
- for (; *f; f++) {
- if (*f == '/')
- *(t++) = '-';
- else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
- t = do_escape_char(*f, t);
- else
- *(t++) = *f;
- }
-
- return t;
-}
-
-char *unit_name_escape(const char *f) {
- char *r, *t;
-
- assert(f);
-
- r = new(char, strlen(f)*4+1);
- if (!r)
- return NULL;
-
- t = do_escape(f, r);
- *t = 0;
-
- return r;
-}
-
-int unit_name_unescape(const char *f, char **ret) {
- _cleanup_free_ char *r = NULL;
- char *t;
-
- assert(f);
-
- r = strdup(f);
- if (!r)
- return -ENOMEM;
-
- for (t = r; *f; f++) {
- if (*f == '-')
- *(t++) = '/';
- else if (*f == '\\') {
- int a, b;
-
- if (f[1] != 'x')
- return -EINVAL;
-
- a = unhexchar(f[2]);
- if (a < 0)
- return -EINVAL;
-
- b = unhexchar(f[3]);
- if (b < 0)
- return -EINVAL;
-
- *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
- f += 3;
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- *ret = r;
- r = NULL;
-
- return 0;
-}
-
-int unit_name_path_escape(const char *f, char **ret) {
- char *p, *s;
-
- assert(f);
- assert(ret);
-
- p = strdupa(f);
- if (!p)
- return -ENOMEM;
-
- path_kill_slashes(p);
-
- if (STR_IN_SET(p, "/", ""))
- s = strdup("-");
- else {
- char *e;
-
- if (!path_is_safe(p))
- return -EINVAL;
-
- /* Truncate trailing slashes */
- e = endswith(p, "/");
- if (e)
- *e = 0;
-
- /* Truncate leading slashes */
- if (p[0] == '/')
- p++;
-
- s = unit_name_escape(p);
- }
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
-}
-
-int unit_name_path_unescape(const char *f, char **ret) {
- char *s;
- int r;
-
- assert(f);
-
- if (isempty(f))
- return -EINVAL;
-
- if (streq(f, "-")) {
- s = strdup("/");
- if (!s)
- return -ENOMEM;
- } else {
- char *w;
-
- r = unit_name_unescape(f, &w);
- if (r < 0)
- return r;
-
- /* Don't accept trailing or leading slashes */
- if (startswith(w, "/") || endswith(w, "/")) {
- free(w);
- return -EINVAL;
- }
-
- /* Prefix a slash again */
- s = strappend("/", w);
- free(w);
- if (!s)
- return -ENOMEM;
-
- if (!path_is_safe(s)) {
- free(s);
- return -EINVAL;
- }
- }
-
- if (ret)
- *ret = s;
- else
- free(s);
-
- return 0;
-}
-
-int unit_name_replace_instance(const char *f, const char *i, char **ret) {
- const char *p, *e;
- char *s;
- size_t a, b;
-
- assert(f);
- assert(i);
- assert(ret);
-
- if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
- return -EINVAL;
- if (!unit_instance_is_valid(i))
- return -EINVAL;
-
- assert_se(p = strchr(f, '@'));
- assert_se(e = strrchr(f, '.'));
-
- a = p - f;
- b = strlen(i);
-
- s = new(char, a + 1 + b + strlen(e) + 1);
- if (!s)
- return -ENOMEM;
-
- strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
-
- *ret = s;
- return 0;
-}
-
-int unit_name_template(const char *f, char **ret) {
- const char *p, *e;
- char *s;
- size_t a;
-
- assert(f);
- assert(ret);
-
- if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
- return -EINVAL;
-
- assert_se(p = strchr(f, '@'));
- assert_se(e = strrchr(f, '.'));
-
- a = p - f;
-
- s = new(char, a + 1 + strlen(e) + 1);
- if (!s)
- return -ENOMEM;
-
- strcpy(mempcpy(s, f, a + 1), e);
-
- *ret = s;
- return 0;
-}
-
-int unit_name_from_path(const char *path, const char *suffix, char **ret) {
- _cleanup_free_ char *p = NULL;
- char *s = NULL;
- int r;
-
- assert(path);
- assert(suffix);
- assert(ret);
-
- if (!unit_suffix_is_valid(suffix))
- return -EINVAL;
-
- r = unit_name_path_escape(path, &p);
- if (r < 0)
- return r;
-
- s = strappend(p, suffix);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
-}
-
-int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
- _cleanup_free_ char *p = NULL;
- char *s;
- int r;
-
- assert(prefix);
- assert(path);
- assert(suffix);
- assert(ret);
-
- if (!unit_prefix_is_valid(prefix))
- return -EINVAL;
-
- if (!unit_suffix_is_valid(suffix))
- return -EINVAL;
-
- r = unit_name_path_escape(path, &p);
- if (r < 0)
- return r;
-
- s = strjoin(prefix, "@", p, suffix, NULL);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
-}
-
-int unit_name_to_path(const char *name, char **ret) {
- _cleanup_free_ char *prefix = NULL;
- int r;
-
- assert(name);
-
- r = unit_name_to_prefix(name, &prefix);
- if (r < 0)
- return r;
-
- return unit_name_path_unescape(prefix, ret);
-}
-
-char *unit_dbus_path_from_name(const char *name) {
- _cleanup_free_ char *e = NULL;
-
- assert(name);
-
- e = bus_label_escape(name);
- if (!e)
- return NULL;
-
- return strappend("/org/freedesktop/systemd1/unit/", e);
-}
-
-int unit_name_from_dbus_path(const char *path, char **name) {
- const char *e;
- char *n;
-
- e = startswith(path, "/org/freedesktop/systemd1/unit/");
- if (!e)
- return -EINVAL;
-
- n = bus_label_unescape(e);
- if (!n)
- return -ENOMEM;
-
- *name = n;
- return 0;
-}
-
-static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
- const char *valid_chars;
-
- assert(f);
- assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
- assert(t);
-
- /* We'll only escape the obvious characters here, to play
- * safe. */
-
- valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
-
- for (; *f; f++) {
- if (*f == '/')
- *(t++) = '-';
- else if (!strchr(valid_chars, *f))
- t = do_escape_char(*f, t);
- else
- *(t++) = *f;
- }
-
- return t;
-}
-
-/**
- * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
- * /blah/blah is converted to blah-blah.mount, anything else is left alone,
- * except that @suffix is appended if a valid unit suffix is not present.
- *
- * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
- */
-int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
- char *s, *t;
- int r;
-
- assert(name);
- assert(suffix);
- assert(ret);
-
- if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
- return -EINVAL;
-
- if (!unit_suffix_is_valid(suffix))
- return -EINVAL;
-
- if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
- /* No mangling necessary... */
- s = strdup(name);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
- }
-
- if (is_device_path(name)) {
- r = unit_name_from_path(name, ".device", ret);
- if (r >= 0)
- return 1;
- if (r != -EINVAL)
- return r;
- }
-
- if (path_is_absolute(name)) {
- r = unit_name_from_path(name, ".mount", ret);
- if (r >= 0)
- return 1;
- if (r != -EINVAL)
- return r;
- }
-
- s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
- if (!s)
- return -ENOMEM;
-
- t = do_escape_mangle(name, allow_globs, s);
- *t = 0;
-
- if (unit_name_to_type(s) < 0)
- strcpy(t, suffix);
-
- *ret = s;
- return 1;
-}
-
-int slice_build_parent_slice(const char *slice, char **ret) {
- char *s, *dash;
-
- assert(slice);
- assert(ret);
-
- if (!slice_name_is_valid(slice))
- return -EINVAL;
-
- if (streq(slice, "-.slice")) {
- *ret = NULL;
- return 0;
- }
-
- s = strdup(slice);
- if (!s)
- return -ENOMEM;
-
- dash = strrchr(s, '-');
- if (dash)
- strcpy(dash, ".slice");
- else {
- free(s);
-
- s = strdup("-.slice");
- if (!s)
- return -ENOMEM;
- }
-
- *ret = s;
- return 1;
-}
-
-int slice_build_subslice(const char *slice, const char*name, char **ret) {
- char *subslice;
-
- assert(slice);
- assert(name);
- assert(ret);
-
- if (!slice_name_is_valid(slice))
- return -EINVAL;
-
- if (!unit_prefix_is_valid(name))
- return -EINVAL;
-
- if (streq(slice, "-.slice"))
- subslice = strappend(name, ".slice");
- else {
- char *e;
-
- assert_se(e = endswith(slice, ".slice"));
-
- subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
- if (!subslice)
- return -ENOMEM;
-
- stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
- }
-
- *ret = subslice;
- return 0;
-}
-
-bool slice_name_is_valid(const char *name) {
- const char *p, *e;
- bool dash = false;
-
- if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
- return false;
-
- if (streq(name, "-.slice"))
- return true;
-
- e = endswith(name, ".slice");
- if (!e)
- return false;
-
- for (p = name; p < e; p++) {
-
- if (*p == '-') {
-
- /* Don't allow initial dash */
- if (p == name)
- return false;
-
- /* Don't allow multiple dashes */
- if (dash)
- return false;
-
- dash = true;
- } else
- dash = false;
- }
-
- /* Don't allow trailing hash */
- if (dash)
- return false;
-
- return true;
-}
-
-static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
- [UNIT_SERVICE] = "service",
- [UNIT_SOCKET] = "socket",
- [UNIT_BUSNAME] = "busname",
- [UNIT_TARGET] = "target",
- [UNIT_SNAPSHOT] = "snapshot",
- [UNIT_DEVICE] = "device",
- [UNIT_MOUNT] = "mount",
- [UNIT_AUTOMOUNT] = "automount",
- [UNIT_SWAP] = "swap",
- [UNIT_TIMER] = "timer",
- [UNIT_PATH] = "path",
- [UNIT_SLICE] = "slice",
- [UNIT_SCOPE] = "scope"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
-
-static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
- [UNIT_STUB] = "stub",
- [UNIT_LOADED] = "loaded",
- [UNIT_NOT_FOUND] = "not-found",
- [UNIT_ERROR] = "error",
- [UNIT_MERGED] = "merged",
- [UNIT_MASKED] = "masked"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
-
-static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
- [UNIT_REQUIRES] = "Requires",
- [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
- [UNIT_REQUISITE] = "Requisite",
- [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
- [UNIT_WANTS] = "Wants",
- [UNIT_BINDS_TO] = "BindsTo",
- [UNIT_PART_OF] = "PartOf",
- [UNIT_REQUIRED_BY] = "RequiredBy",
- [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
- [UNIT_REQUISITE_OF] = "RequisiteOf",
- [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
- [UNIT_WANTED_BY] = "WantedBy",
- [UNIT_BOUND_BY] = "BoundBy",
- [UNIT_CONSISTS_OF] = "ConsistsOf",
- [UNIT_CONFLICTS] = "Conflicts",
- [UNIT_CONFLICTED_BY] = "ConflictedBy",
- [UNIT_BEFORE] = "Before",
- [UNIT_AFTER] = "After",
- [UNIT_ON_FAILURE] = "OnFailure",
- [UNIT_TRIGGERS] = "Triggers",
- [UNIT_TRIGGERED_BY] = "TriggeredBy",
- [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
- [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
- [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
- [UNIT_REFERENCES] = "References",
- [UNIT_REFERENCED_BY] = "ReferencedBy",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
deleted file mode 100644
index b2043d0..0000000
--- a/src/shared/unit-name.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <stdbool.h>
-
-#include "macro.h"
-
-#define UNIT_NAME_MAX 256
-
-typedef enum UnitType UnitType;
-typedef enum UnitLoadState UnitLoadState;
-typedef enum UnitDependency UnitDependency;
-
-enum UnitType {
- UNIT_SERVICE = 0,
- UNIT_SOCKET,
- UNIT_BUSNAME,
- UNIT_TARGET,
- UNIT_SNAPSHOT,
- UNIT_DEVICE,
- UNIT_MOUNT,
- UNIT_AUTOMOUNT,
- UNIT_SWAP,
- UNIT_TIMER,
- UNIT_PATH,
- UNIT_SLICE,
- UNIT_SCOPE,
- _UNIT_TYPE_MAX,
- _UNIT_TYPE_INVALID = -1
-};
-
-enum UnitLoadState {
- UNIT_STUB = 0,
- UNIT_LOADED,
- UNIT_NOT_FOUND,
- UNIT_ERROR,
- UNIT_MERGED,
- UNIT_MASKED,
- _UNIT_LOAD_STATE_MAX,
- _UNIT_LOAD_STATE_INVALID = -1
-};
-
-enum UnitDependency {
- /* Positive dependencies */
- UNIT_REQUIRES,
- UNIT_REQUIRES_OVERRIDABLE,
- UNIT_REQUISITE,
- UNIT_REQUISITE_OVERRIDABLE,
- UNIT_WANTS,
- UNIT_BINDS_TO,
- UNIT_PART_OF,
-
- /* Inverse of the above */
- UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
- UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' is 'required_by_overridable' */
- UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */
- UNIT_REQUISITE_OF_OVERRIDABLE,/* inverse of 'requisite_overridable' is 'requisite_of_overridable' */
- UNIT_WANTED_BY, /* inverse of 'wants' */
- UNIT_BOUND_BY, /* inverse of 'binds_to' */
- UNIT_CONSISTS_OF, /* inverse of 'part_of' */
-
- /* Negative dependencies */
- UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
- UNIT_CONFLICTED_BY,
-
- /* Order */
- UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
- UNIT_AFTER,
-
- /* On Failure */
- UNIT_ON_FAILURE,
-
- /* Triggers (i.e. a socket triggers a service) */
- UNIT_TRIGGERS,
- UNIT_TRIGGERED_BY,
-
- /* Propagate reloads */
- UNIT_PROPAGATES_RELOAD_TO,
- UNIT_RELOAD_PROPAGATED_FROM,
-
- /* Joins namespace of */
- UNIT_JOINS_NAMESPACE_OF,
-
- /* Reference information for GC logic */
- UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
- UNIT_REFERENCED_BY,
-
- _UNIT_DEPENDENCY_MAX,
- _UNIT_DEPENDENCY_INVALID = -1
-};
-
-typedef enum UnitNameFlags {
- UNIT_NAME_PLAIN = 1, /* Allow foo.service */
- UNIT_NAME_INSTANCE = 2, /* Allow foo at bar.service */
- UNIT_NAME_TEMPLATE = 4, /* Allow foo at .service */
- UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE,
-} UnitNameFlags;
-
-bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;
-bool unit_prefix_is_valid(const char *p) _pure_;
-bool unit_instance_is_valid(const char *i) _pure_;
-bool unit_suffix_is_valid(const char *s) _pure_;
-
-static inline int unit_prefix_and_instance_is_valid(const char *p) {
- /* For prefix+instance and instance the same rules apply */
- return unit_instance_is_valid(p);
-}
-
-int unit_name_to_prefix(const char *n, char **prefix);
-int unit_name_to_instance(const char *n, char **instance);
-int unit_name_to_prefix_and_instance(const char *n, char **ret);
-
-UnitType unit_name_to_type(const char *n) _pure_;
-
-int unit_name_change_suffix(const char *n, const char *suffix, char **ret);
-
-int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret);
-
-char *unit_name_escape(const char *f);
-int unit_name_unescape(const char *f, char **ret);
-int unit_name_path_escape(const char *f, char **ret);
-int unit_name_path_unescape(const char *f, char **ret);
-
-int unit_name_replace_instance(const char *f, const char *i, char **ret);
-
-int unit_name_template(const char *f, char **ret);
-
-int unit_name_from_path(const char *path, const char *suffix, char **ret);
-int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret);
-int unit_name_to_path(const char *name, char **ret);
-
-char *unit_dbus_path_from_name(const char *name);
-int unit_name_from_dbus_path(const char *path, char **name);
-
-typedef enum UnitNameMangle {
- UNIT_NAME_NOGLOB,
- UNIT_NAME_GLOB,
-} UnitNameMangle;
-
-int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret);
-
-static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) {
- return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret);
-}
-
-int slice_build_parent_slice(const char *slice, char **ret);
-int slice_build_subslice(const char *slice, const char*name, char **subslice);
-bool slice_name_is_valid(const char *name);
-
-const char *unit_type_to_string(UnitType i) _const_;
-UnitType unit_type_from_string(const char *s) _pure_;
-
-const char *unit_load_state_to_string(UnitLoadState i) _const_;
-UnitLoadState unit_load_state_from_string(const char *s) _pure_;
-
-const char *unit_dependency_to_string(UnitDependency i) _const_;
-UnitDependency unit_dependency_from_string(const char *s) _pure_;
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
deleted file mode 100644
index 800884f..0000000
--- a/src/shared/utf8.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2008-2011 Kay Sievers
- 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/>.
-***/
-
-/* Parts of this file are based on the GLIB utf8 validation functions. The
- * original license text follows. */
-
-/* gutf8.c - Operations on UTF-8 strings.
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include "utf8.h"
-#include "util.h"
-
-bool unichar_is_valid(uint32_t ch) {
-
- if (ch >= 0x110000) /* End of unicode space */
- return false;
- if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
- return false;
- if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
- return false;
- if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
- return false;
-
- return true;
-}
-
-static bool unichar_is_control(uint32_t ch) {
-
- /*
- 0 to ' '-1 is the C0 range.
- DEL=0x7F, and DEL+1 to 0x9F is C1 range.
- '\t' is in C0 range, but more or less harmless and commonly used.
- */
-
- return (ch < ' ' && ch != '\t' && ch != '\n') ||
- (0x7F <= ch && ch <= 0x9F);
-}
-
-/* count of characters used to encode one unicode char */
-static int utf8_encoded_expected_len(const char *str) {
- unsigned char c;
-
- assert(str);
-
- c = (unsigned char) str[0];
- if (c < 0x80)
- return 1;
- if ((c & 0xe0) == 0xc0)
- return 2;
- if ((c & 0xf0) == 0xe0)
- return 3;
- if ((c & 0xf8) == 0xf0)
- return 4;
- if ((c & 0xfc) == 0xf8)
- return 5;
- if ((c & 0xfe) == 0xfc)
- return 6;
-
- return 0;
-}
-
-/* decode one unicode char */
-int utf8_encoded_to_unichar(const char *str) {
- int unichar, len, i;
-
- assert(str);
-
- len = utf8_encoded_expected_len(str);
-
- switch (len) {
- case 1:
- return (int)str[0];
- case 2:
- unichar = str[0] & 0x1f;
- break;
- case 3:
- unichar = (int)str[0] & 0x0f;
- break;
- case 4:
- unichar = (int)str[0] & 0x07;
- break;
- case 5:
- unichar = (int)str[0] & 0x03;
- break;
- case 6:
- unichar = (int)str[0] & 0x01;
- break;
- default:
- return -EINVAL;
- }
-
- for (i = 1; i < len; i++) {
- if (((int)str[i] & 0xc0) != 0x80)
- return -EINVAL;
- unichar <<= 6;
- unichar |= (int)str[i] & 0x3f;
- }
-
- return unichar;
-}
-
-bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
- const char *p;
-
- assert(str);
-
- for (p = str; length;) {
- int encoded_len, val;
-
- encoded_len = utf8_encoded_valid_unichar(p);
- if (encoded_len < 0 ||
- (size_t) encoded_len > length)
- return false;
-
- val = utf8_encoded_to_unichar(p);
- if (val < 0 ||
- unichar_is_control(val) ||
- (!newline && val == '\n'))
- return false;
-
- length -= encoded_len;
- p += encoded_len;
- }
-
- return true;
-}
-
-const char *utf8_is_valid(const char *str) {
- const uint8_t *p;
-
- assert(str);
-
- for (p = (const uint8_t*) str; *p; ) {
- int len;
-
- len = utf8_encoded_valid_unichar((const char *)p);
- if (len < 0)
- return NULL;
-
- p += len;
- }
-
- return str;
-}
-
-char *utf8_escape_invalid(const char *str) {
- char *p, *s;
-
- assert(str);
-
- p = s = malloc(strlen(str) * 4 + 1);
- if (!p)
- return NULL;
-
- while (*str) {
- int len;
-
- len = utf8_encoded_valid_unichar(str);
- if (len > 0) {
- s = mempcpy(s, str, len);
- str += len;
- } else {
- s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
- str += 1;
- }
- }
-
- *s = '\0';
-
- return p;
-}
-
-char *utf8_escape_non_printable(const char *str) {
- char *p, *s;
-
- assert(str);
-
- p = s = malloc(strlen(str) * 4 + 1);
- if (!p)
- return NULL;
-
- while (*str) {
- int len;
-
- len = utf8_encoded_valid_unichar(str);
- if (len > 0) {
- if (utf8_is_printable(str, len)) {
- s = mempcpy(s, str, len);
- str += len;
- } else {
- while (len > 0) {
- *(s++) = '\\';
- *(s++) = 'x';
- *(s++) = hexchar((int) *str >> 4);
- *(s++) = hexchar((int) *str);
-
- str += 1;
- len --;
- }
- }
- } else {
- s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
- str += 1;
- }
- }
-
- *s = '\0';
-
- return p;
-}
-
-char *ascii_is_valid(const char *str) {
- const char *p;
-
- assert(str);
-
- for (p = str; *p; p++)
- if ((unsigned char) *p >= 128)
- return NULL;
-
- return (char*) str;
-}
-
-/**
- * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
- * @out_utf8: output buffer of at least 4 bytes or NULL
- * @g: UCS-4 character to encode
- *
- * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
- * The length of the character is returned. It is not zero-terminated! If the
- * output buffer is NULL, only the length is returned.
- *
- * Returns: The length in bytes that the UTF-8 representation does or would
- * occupy.
- */
-size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
-
- if (g < (1 << 7)) {
- if (out_utf8)
- out_utf8[0] = g & 0x7f;
- return 1;
- } else if (g < (1 << 11)) {
- if (out_utf8) {
- out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
- out_utf8[1] = 0x80 | (g & 0x3f);
- }
- return 2;
- } else if (g < (1 << 16)) {
- if (out_utf8) {
- out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
- out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
- out_utf8[2] = 0x80 | (g & 0x3f);
- }
- return 3;
- } else if (g < (1 << 21)) {
- if (out_utf8) {
- out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
- out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
- out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
- out_utf8[3] = 0x80 | (g & 0x3f);
- }
- return 4;
- }
-
- return 0;
-}
-
-char *utf16_to_utf8(const void *s, size_t length) {
- const uint8_t *f;
- char *r, *t;
-
- r = new(char, (length * 4 + 1) / 2 + 1);
- if (!r)
- return NULL;
-
- f = s;
- t = r;
-
- while (f < (const uint8_t*) s + length) {
- uint16_t w1, w2;
-
- /* see RFC 2781 section 2.2 */
-
- w1 = f[1] << 8 | f[0];
- f += 2;
-
- if (!utf16_is_surrogate(w1)) {
- t += utf8_encode_unichar(t, w1);
-
- continue;
- }
-
- if (utf16_is_trailing_surrogate(w1))
- continue;
- else if (f >= (const uint8_t*) s + length)
- break;
-
- w2 = f[1] << 8 | f[0];
- f += 2;
-
- if (!utf16_is_trailing_surrogate(w2)) {
- f -= 2;
- continue;
- }
-
- t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
- }
-
- *t = 0;
- return r;
-}
-
-/* expected size used to encode one unicode char */
-static int utf8_unichar_to_encoded_len(int unichar) {
-
- if (unichar < 0x80)
- return 1;
- if (unichar < 0x800)
- return 2;
- if (unichar < 0x10000)
- return 3;
- if (unichar < 0x200000)
- return 4;
- if (unichar < 0x4000000)
- return 5;
-
- return 6;
-}
-
-/* validate one encoded unicode char and return its length */
-int utf8_encoded_valid_unichar(const char *str) {
- int len, unichar, i;
-
- assert(str);
-
- len = utf8_encoded_expected_len(str);
- if (len == 0)
- return -EINVAL;
-
- /* ascii is valid */
- if (len == 1)
- return 1;
-
- /* check if expected encoded chars are available */
- for (i = 0; i < len; i++)
- if ((str[i] & 0x80) != 0x80)
- return -EINVAL;
-
- unichar = utf8_encoded_to_unichar(str);
-
- /* check if encoded length matches encoded value */
- if (utf8_unichar_to_encoded_len(unichar) != len)
- return -EINVAL;
-
- /* check if value has valid range */
- if (!unichar_is_valid(unichar))
- return -EINVAL;
-
- return len;
-}
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
deleted file mode 100644
index e745649..0000000
--- a/src/shared/utf8.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- 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 <stdbool.h>
-
-#include "macro.h"
-
-#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
-
-bool unichar_is_valid(uint32_t c);
-
-const char *utf8_is_valid(const char *s) _pure_;
-char *ascii_is_valid(const char *s) _pure_;
-
-bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
-#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
-
-char *utf8_escape_invalid(const char *s);
-char *utf8_escape_non_printable(const char *str);
-
-size_t utf8_encode_unichar(char *out_utf8, uint32_t g);
-char *utf16_to_utf8(const void *s, size_t length);
-
-int utf8_encoded_valid_unichar(const char *str);
-int utf8_encoded_to_unichar(const char *str);
-
-static inline bool utf16_is_surrogate(uint16_t c) {
- return (0xd800 <= c && c <= 0xdfff);
-}
-
-static inline bool utf16_is_trailing_surrogate(uint16_t c) {
- return (0xdc00 <= c && c <= 0xdfff);
-}
-
-static inline uint32_t utf16_surrogate_pair_to_unichar(uint16_t lead, uint16_t trail) {
- return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
-}
diff --git a/src/shared/util.c b/src/shared/util.c
deleted file mode 100644
index 6f6906f..0000000
--- a/src/shared/util.c
+++ /dev/null
@@ -1,6048 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <sched.h>
-#include <sys/resource.h>
-#include <linux/sched.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <stdarg.h>
-#include <poll.h>
-#include <ctype.h>
-#include <sys/prctl.h>
-#include <sys/utsname.h>
-#include <pwd.h>
-#include <netinet/ip.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <glob.h>
-#include <grp.h>
-#include <sys/mman.h>
-#include <sys/vfs.h>
-#include <sys/mount.h>
-#include <linux/magic.h>
-#include <limits.h>
-#include <langinfo.h>
-#include <locale.h>
-#include <sys/personality.h>
-#include <sys/xattr.h>
-#include <sys/statvfs.h>
-#include <sys/file.h>
-#include <linux/fs.h>
-
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the XDG
- * version which is really broken. */
-#include <libgen.h>
-#undef basename
-
-#ifdef HAVE_SYS_AUXV_H
-#include <sys/auxv.h>
-#endif
-
-#include "config.h"
-#include "macro.h"
-#include "util.h"
-#include "ioprio.h"
-#include "missing.h"
-#include "log.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "exit-status.h"
-#include "hashmap.h"
-#include "env-util.h"
-#include "fileio.h"
-#include "device-nodes.h"
-#include "utf8.h"
-#include "gunicode.h"
-#include "virt.h"
-#include "def.h"
-#include "sparse-endian.h"
-#include "formats-util.h"
-#include "process-util.h"
-#include "random-util.h"
-#include "terminal-util.h"
-#include "hostname-util.h"
-#include "signal-util.h"
-
-/* Put this test here for a lack of better place */
-assert_cc(EAGAIN == EWOULDBLOCK);
-
-int saved_argc = 0;
-char **saved_argv = NULL;
-
-size_t page_size(void) {
- static thread_local size_t pgsz = 0;
- long r;
-
- if (_likely_(pgsz > 0))
- return pgsz;
-
- r = sysconf(_SC_PAGESIZE);
- assert(r > 0);
-
- pgsz = (size_t) r;
- return pgsz;
-}
-
-bool streq_ptr(const char *a, const char *b) {
-
- /* Like streq(), but tries to make sense of NULL pointers */
-
- if (a && b)
- return streq(a, b);
-
- if (!a && !b)
- return true;
-
- return false;
-}
-
-char* endswith(const char *s, const char *postfix) {
- size_t sl, pl;
-
- assert(s);
- assert(postfix);
-
- sl = strlen(s);
- pl = strlen(postfix);
-
- if (pl == 0)
- return (char*) s + sl;
-
- if (sl < pl)
- return NULL;
-
- if (memcmp(s + sl - pl, postfix, pl) != 0)
- return NULL;
-
- return (char*) s + sl - pl;
-}
-
-char* endswith_no_case(const char *s, const char *postfix) {
- size_t sl, pl;
-
- assert(s);
- assert(postfix);
-
- sl = strlen(s);
- pl = strlen(postfix);
-
- if (pl == 0)
- return (char*) s + sl;
-
- if (sl < pl)
- return NULL;
-
- if (strcasecmp(s + sl - pl, postfix) != 0)
- return NULL;
-
- return (char*) s + sl - pl;
-}
-
-char* first_word(const char *s, const char *word) {
- size_t sl, wl;
- const char *p;
-
- assert(s);
- assert(word);
-
- /* Checks if the string starts with the specified word, either
- * followed by NUL or by whitespace. Returns a pointer to the
- * NUL or the first character after the whitespace. */
-
- sl = strlen(s);
- wl = strlen(word);
-
- if (sl < wl)
- return NULL;
-
- if (wl == 0)
- return (char*) s;
-
- if (memcmp(s, word, wl) != 0)
- return NULL;
-
- p = s + wl;
- if (*p == 0)
- return (char*) p;
-
- if (!strchr(WHITESPACE, *p))
- return NULL;
-
- p += strspn(p, WHITESPACE);
- return (char*) p;
-}
-
-size_t cescape_char(char c, char *buf) {
- char * buf_old = buf;
-
- switch (c) {
-
- case '\a':
- *(buf++) = '\\';
- *(buf++) = 'a';
- break;
- case '\b':
- *(buf++) = '\\';
- *(buf++) = 'b';
- break;
- case '\f':
- *(buf++) = '\\';
- *(buf++) = 'f';
- break;
- case '\n':
- *(buf++) = '\\';
- *(buf++) = 'n';
- break;
- case '\r':
- *(buf++) = '\\';
- *(buf++) = 'r';
- break;
- case '\t':
- *(buf++) = '\\';
- *(buf++) = 't';
- break;
- case '\v':
- *(buf++) = '\\';
- *(buf++) = 'v';
- break;
- case '\\':
- *(buf++) = '\\';
- *(buf++) = '\\';
- break;
- case '"':
- *(buf++) = '\\';
- *(buf++) = '"';
- break;
- case '\'':
- *(buf++) = '\\';
- *(buf++) = '\'';
- break;
-
- default:
- /* For special chars we prefer octal over
- * hexadecimal encoding, simply because glib's
- * g_strescape() does the same */
- if ((c < ' ') || (c >= 127)) {
- *(buf++) = '\\';
- *(buf++) = octchar((unsigned char) c >> 6);
- *(buf++) = octchar((unsigned char) c >> 3);
- *(buf++) = octchar((unsigned char) c);
- } else
- *(buf++) = c;
- break;
- }
-
- return buf - buf_old;
-}
-
-int close_nointr(int fd) {
- assert(fd >= 0);
-
- if (close(fd) >= 0)
- return 0;
-
- /*
- * Just ignore EINTR; a retry loop is the wrong thing to do on
- * Linux.
- *
- * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
- * https://bugzilla.gnome.org/show_bug.cgi?id=682819
- * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
- * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
- */
- if (errno == EINTR)
- return 0;
-
- return -errno;
-}
-
-int safe_close(int fd) {
-
- /*
- * Like close_nointr() but cannot fail. Guarantees errno is
- * unchanged. Is a NOP with negative fds passed, and returns
- * -1, so that it can be used in this syntax:
- *
- * fd = safe_close(fd);
- */
-
- if (fd >= 0) {
- PROTECT_ERRNO;
-
- /* The kernel might return pretty much any error code
- * via close(), but the fd will be closed anyway. The
- * only condition we want to check for here is whether
- * the fd was invalid at all... */
-
- assert_se(close_nointr(fd) != -EBADF);
- }
-
- return -1;
-}
-
-void close_many(const int fds[], unsigned n_fd) {
- unsigned i;
-
- assert(fds || n_fd <= 0);
-
- for (i = 0; i < n_fd; i++)
- safe_close(fds[i]);
-}
-
-int unlink_noerrno(const char *path) {
- PROTECT_ERRNO;
- int r;
-
- r = unlink(path);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int parse_boolean(const char *v) {
- assert(v);
-
- if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
- return 1;
- else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
- return 0;
-
- return -EINVAL;
-}
-
-int parse_pid(const char *s, pid_t* ret_pid) {
- unsigned long ul = 0;
- pid_t pid;
- int r;
-
- assert(s);
- assert(ret_pid);
-
- r = safe_atolu(s, &ul);
- if (r < 0)
- return r;
-
- pid = (pid_t) ul;
-
- if ((unsigned long) pid != ul)
- return -ERANGE;
-
- if (pid <= 0)
- return -ERANGE;
-
- *ret_pid = pid;
- return 0;
-}
-
-int parse_uid(const char *s, uid_t* ret_uid) {
- unsigned long ul = 0;
- uid_t uid;
- int r;
-
- assert(s);
-
- r = safe_atolu(s, &ul);
- if (r < 0)
- return r;
-
- uid = (uid_t) ul;
-
- if ((unsigned long) uid != ul)
- return -ERANGE;
-
- /* Some libc APIs use UID_INVALID as special placeholder */
- if (uid == (uid_t) 0xFFFFFFFF)
- return -ENXIO;
-
- /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
- if (uid == (uid_t) 0xFFFF)
- return -ENXIO;
-
- if (ret_uid)
- *ret_uid = uid;
-
- return 0;
-}
-
-int safe_atou(const char *s, unsigned *ret_u) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret_u);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((unsigned long) (unsigned) l != l)
- return -ERANGE;
-
- *ret_u = (unsigned) l;
- return 0;
-}
-
-int safe_atoi(const char *s, int *ret_i) {
- char *x = NULL;
- long l;
-
- assert(s);
- assert(ret_i);
-
- errno = 0;
- l = strtol(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((long) (int) l != l)
- return -ERANGE;
-
- *ret_i = (int) l;
- return 0;
-}
-
-int safe_atou8(const char *s, uint8_t *ret) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((unsigned long) (uint8_t) l != l)
- return -ERANGE;
-
- *ret = (uint8_t) l;
- return 0;
-}
-
-int safe_atou16(const char *s, uint16_t *ret) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((unsigned long) (uint16_t) l != l)
- return -ERANGE;
-
- *ret = (uint16_t) l;
- return 0;
-}
-
-int safe_atoi16(const char *s, int16_t *ret) {
- char *x = NULL;
- long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtol(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno > 0 ? -errno : -EINVAL;
-
- if ((long) (int16_t) l != l)
- return -ERANGE;
-
- *ret = (int16_t) l;
- return 0;
-}
-
-int safe_atollu(const char *s, long long unsigned *ret_llu) {
- char *x = NULL;
- unsigned long long l;
-
- assert(s);
- assert(ret_llu);
-
- errno = 0;
- l = strtoull(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno ? -errno : -EINVAL;
-
- *ret_llu = l;
- return 0;
-}
-
-int safe_atolli(const char *s, long long int *ret_lli) {
- char *x = NULL;
- long long l;
-
- assert(s);
- assert(ret_lli);
-
- errno = 0;
- l = strtoll(s, &x, 0);
-
- if (!x || x == s || *x || errno)
- return errno ? -errno : -EINVAL;
-
- *ret_lli = l;
- return 0;
-}
-
-int safe_atod(const char *s, double *ret_d) {
- char *x = NULL;
- double d = 0;
- locale_t loc;
-
- assert(s);
- assert(ret_d);
-
- loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
- if (loc == (locale_t) 0)
- return -errno;
-
- errno = 0;
- d = strtod_l(s, &x, loc);
-
- if (!x || x == s || *x || errno) {
- freelocale(loc);
- return errno ? -errno : -EINVAL;
- }
-
- freelocale(loc);
- *ret_d = (double) d;
- return 0;
-}
-
-static size_t strcspn_escaped(const char *s, const char *reject) {
- bool escaped = false;
- int n;
-
- for (n=0; s[n]; n++) {
- if (escaped)
- escaped = false;
- else if (s[n] == '\\')
- escaped = true;
- else if (strchr(reject, s[n]))
- break;
- }
-
- /* if s ends in \, return index of previous char */
- return n - escaped;
-}
-
-/* Split a string into words. */
-const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
- const char *current;
-
- current = *state;
-
- if (!*current) {
- assert(**state == '\0');
- return NULL;
- }
-
- current += strspn(current, separator);
- if (!*current) {
- *state = current;
- return NULL;
- }
-
- if (quoted && strchr("\'\"", *current)) {
- char quotechars[2] = {*current, '\0'};
-
- *l = strcspn_escaped(current + 1, quotechars);
- if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
- (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
- /* right quote missing or garbage at the end */
- *state = current;
- return NULL;
- }
- *state = current++ + *l + 2;
- } else if (quoted) {
- *l = strcspn_escaped(current, separator);
- if (current[*l] && !strchr(separator, current[*l])) {
- /* unfinished escape */
- *state = current;
- return NULL;
- }
- *state = current + *l;
- } else {
- *l = strcspn(current, separator);
- *state = current + *l;
- }
-
- return current;
-}
-
-int fchmod_umask(int fd, mode_t m) {
- mode_t u;
- int r;
-
- u = umask(0777);
- r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
- umask(u);
-
- return r;
-}
-
-char *truncate_nl(char *s) {
- assert(s);
-
- s[strcspn(s, NEWLINE)] = 0;
- return s;
-}
-
-char *strnappend(const char *s, const char *suffix, size_t b) {
- size_t a;
- char *r;
-
- if (!s && !suffix)
- return strdup("");
-
- if (!s)
- return strndup(suffix, b);
-
- if (!suffix)
- return strdup(s);
-
- assert(s);
- assert(suffix);
-
- a = strlen(s);
- if (b > ((size_t) -1) - a)
- return NULL;
-
- r = new(char, a+b+1);
- if (!r)
- return NULL;
-
- memcpy(r, s, a);
- memcpy(r+a, suffix, b);
- r[a+b] = 0;
-
- return r;
-}
-
-char *strappend(const char *s, const char *suffix) {
- return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
-}
-
-int readlinkat_malloc(int fd, const char *p, char **ret) {
- size_t l = 100;
- int r;
-
- assert(p);
- assert(ret);
-
- for (;;) {
- char *c;
- ssize_t n;
-
- c = new(char, l);
- if (!c)
- return -ENOMEM;
-
- n = readlinkat(fd, p, c, l-1);
- if (n < 0) {
- r = -errno;
- free(c);
- return r;
- }
-
- if ((size_t) n < l-1) {
- c[n] = 0;
- *ret = c;
- return 0;
- }
-
- free(c);
- l *= 2;
- }
-}
-
-int readlink_malloc(const char *p, char **ret) {
- return readlinkat_malloc(AT_FDCWD, p, ret);
-}
-
-int readlink_value(const char *p, char **ret) {
- _cleanup_free_ char *link = NULL;
- char *value;
- int r;
-
- r = readlink_malloc(p, &link);
- if (r < 0)
- return r;
-
- value = basename(link);
- if (!value)
- return -ENOENT;
-
- value = strdup(value);
- if (!value)
- return -ENOMEM;
-
- *ret = value;
-
- return 0;
-}
-
-int readlink_and_make_absolute(const char *p, char **r) {
- _cleanup_free_ char *target = NULL;
- char *k;
- int j;
-
- assert(p);
- assert(r);
-
- j = readlink_malloc(p, &target);
- if (j < 0)
- return j;
-
- k = file_in_same_dir(p, target);
- if (!k)
- return -ENOMEM;
-
- *r = k;
- return 0;
-}
-
-int readlink_and_canonicalize(const char *p, char **r) {
- char *t, *s;
- int j;
-
- assert(p);
- assert(r);
-
- j = readlink_and_make_absolute(p, &t);
- if (j < 0)
- return j;
-
- s = canonicalize_file_name(t);
- if (s) {
- free(t);
- *r = s;
- } else
- *r = t;
-
- path_kill_slashes(*r);
-
- return 0;
-}
-
-char *strstrip(char *s) {
- char *e;
-
- /* Drops trailing whitespace. Modifies the string in
- * place. Returns pointer to first non-space character */
-
- s += strspn(s, WHITESPACE);
-
- for (e = strchr(s, 0); e > s; e --)
- if (!strchr(WHITESPACE, e[-1]))
- break;
-
- *e = 0;
-
- return s;
-}
-
-char *delete_chars(char *s, const char *bad) {
- char *f, *t;
-
- /* Drops all whitespace, regardless where in the string */
-
- for (f = s, t = s; *f; f++) {
- if (strchr(bad, *f))
- continue;
-
- *(t++) = *f;
- }
-
- *t = 0;
-
- return s;
-}
-
-char *file_in_same_dir(const char *path, const char *filename) {
- char *e, *ret;
- size_t k;
-
- assert(path);
- assert(filename);
-
- /* This removes the last component of path and appends
- * filename, unless the latter is absolute anyway or the
- * former isn't */
-
- if (path_is_absolute(filename))
- return strdup(filename);
-
- e = strrchr(path, '/');
- if (!e)
- return strdup(filename);
-
- k = strlen(filename);
- ret = new(char, (e + 1 - path) + k + 1);
- if (!ret)
- return NULL;
-
- memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
- return ret;
-}
-
-int rmdir_parents(const char *path, const char *stop) {
- size_t l;
- int r = 0;
-
- assert(path);
- assert(stop);
-
- l = strlen(path);
-
- /* Skip trailing slashes */
- while (l > 0 && path[l-1] == '/')
- l--;
-
- while (l > 0) {
- char *t;
-
- /* Skip last component */
- while (l > 0 && path[l-1] != '/')
- l--;
-
- /* Skip trailing slashes */
- while (l > 0 && path[l-1] == '/')
- l--;
-
- if (l <= 0)
- break;
-
- if (!(t = strndup(path, l)))
- return -ENOMEM;
-
- if (path_startswith(stop, t)) {
- free(t);
- return 0;
- }
-
- r = rmdir(t);
- free(t);
-
- if (r < 0)
- if (errno != ENOENT)
- return -errno;
- }
-
- return 0;
-}
-
-char hexchar(int x) {
- static const char table[16] = "0123456789abcdef";
-
- return table[x & 15];
-}
-
-int unhexchar(char c) {
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
-
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
-
- return -EINVAL;
-}
-
-char *hexmem(const void *p, size_t l) {
- char *r, *z;
- const uint8_t *x;
-
- z = r = malloc(l * 2 + 1);
- if (!r)
- return NULL;
-
- for (x = p; x < (const uint8_t*) p + l; x++) {
- *(z++) = hexchar(*x >> 4);
- *(z++) = hexchar(*x & 15);
- }
-
- *z = 0;
- return r;
-}
-
-void *unhexmem(const char *p, size_t l) {
- uint8_t *r, *z;
- const char *x;
-
- assert(p);
-
- z = r = malloc((l + 1) / 2 + 1);
- if (!r)
- return NULL;
-
- for (x = p; x < p + l; x += 2) {
- int a, b;
-
- a = unhexchar(x[0]);
- if (x+1 < p + l)
- b = unhexchar(x[1]);
- else
- b = 0;
-
- *(z++) = (uint8_t) a << 4 | (uint8_t) b;
- }
-
- *z = 0;
- return r;
-}
-
-char octchar(int x) {
- return '0' + (x & 7);
-}
-
-int unoctchar(char c) {
-
- if (c >= '0' && c <= '7')
- return c - '0';
-
- return -EINVAL;
-}
-
-char decchar(int x) {
- return '0' + (x % 10);
-}
-
-int undecchar(char c) {
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- return -EINVAL;
-}
-
-char *cescape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert(s);
-
- /* Does C style string escaping. May be reversed with
- * cunescape(). */
-
- r = new(char, strlen(s)*4 + 1);
- if (!r)
- return NULL;
-
- for (f = s, t = r; *f; f++)
- t += cescape_char(*f, t);
-
- *t = 0;
-
- return r;
-}
-
-static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
- int r = 1;
-
- assert(p);
- assert(*p);
- assert(ret);
-
- /* Unescapes C style. Returns the unescaped character in ret,
- * unless we encountered a \u sequence in which case the full
- * unicode character is returned in ret_unicode, instead. */
-
- if (length != (size_t) -1 && length < 1)
- return -EINVAL;
-
- switch (p[0]) {
-
- case 'a':
- *ret = '\a';
- break;
- case 'b':
- *ret = '\b';
- break;
- case 'f':
- *ret = '\f';
- break;
- case 'n':
- *ret = '\n';
- break;
- case 'r':
- *ret = '\r';
- break;
- case 't':
- *ret = '\t';
- break;
- case 'v':
- *ret = '\v';
- break;
- case '\\':
- *ret = '\\';
- break;
- case '"':
- *ret = '"';
- break;
- case '\'':
- *ret = '\'';
- break;
-
- case 's':
- /* This is an extension of the XDG syntax files */
- *ret = ' ';
- break;
-
- case 'x': {
- /* hexadecimal encoding */
- int a, b;
-
- if (length != (size_t) -1 && length < 3)
- return -EINVAL;
-
- a = unhexchar(p[1]);
- if (a < 0)
- return -EINVAL;
-
- b = unhexchar(p[2]);
- if (b < 0)
- return -EINVAL;
-
- /* Don't allow NUL bytes */
- if (a == 0 && b == 0)
- return -EINVAL;
-
- *ret = (char) ((a << 4U) | b);
- r = 3;
- break;
- }
-
- case 'u': {
- /* C++11 style 16bit unicode */
-
- int a[4];
- unsigned i;
- uint32_t c;
-
- if (length != (size_t) -1 && length < 5)
- return -EINVAL;
-
- for (i = 0; i < 4; i++) {
- a[i] = unhexchar(p[1 + i]);
- if (a[i] < 0)
- return a[i];
- }
-
- c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
-
- /* Don't allow 0 chars */
- if (c == 0)
- return -EINVAL;
-
- if (c < 128)
- *ret = c;
- else {
- if (!ret_unicode)
- return -EINVAL;
-
- *ret = 0;
- *ret_unicode = c;
- }
-
- r = 5;
- break;
- }
-
- case 'U': {
- /* C++11 style 32bit unicode */
-
- int a[8];
- unsigned i;
- uint32_t c;
-
- if (length != (size_t) -1 && length < 9)
- return -EINVAL;
-
- for (i = 0; i < 8; i++) {
- a[i] = unhexchar(p[1 + i]);
- if (a[i] < 0)
- return a[i];
- }
-
- c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
- ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
-
- /* Don't allow 0 chars */
- if (c == 0)
- return -EINVAL;
-
- /* Don't allow invalid code points */
- if (!unichar_is_valid(c))
- return -EINVAL;
-
- if (c < 128)
- *ret = c;
- else {
- if (!ret_unicode)
- return -EINVAL;
-
- *ret = 0;
- *ret_unicode = c;
- }
-
- r = 9;
- break;
- }
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': {
- /* octal encoding */
- int a, b, c;
- uint32_t m;
-
- if (length != (size_t) -1 && length < 3)
- return -EINVAL;
-
- a = unoctchar(p[0]);
- if (a < 0)
- return -EINVAL;
-
- b = unoctchar(p[1]);
- if (b < 0)
- return -EINVAL;
-
- c = unoctchar(p[2]);
- if (c < 0)
- return -EINVAL;
-
- /* don't allow NUL bytes */
- if (a == 0 && b == 0 && c == 0)
- return -EINVAL;
-
- /* Don't allow bytes above 255 */
- m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
- if (m > 255)
- return -EINVAL;
-
- *ret = m;
- r = 3;
- break;
- }
-
- default:
- return -EINVAL;
- }
-
- return r;
-}
-
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
- char *r, *t;
- const char *f;
- size_t pl;
-
- assert(s);
- assert(ret);
-
- /* Undoes C style string escaping, and optionally prefixes it. */
-
- pl = prefix ? strlen(prefix) : 0;
-
- r = new(char, pl+length+1);
- if (!r)
- return -ENOMEM;
-
- if (prefix)
- memcpy(r, prefix, pl);
-
- for (f = s, t = r + pl; f < s + length; f++) {
- size_t remaining;
- uint32_t u;
- char c;
- int k;
-
- remaining = s + length - f;
- assert(remaining > 0);
-
- if (*f != '\\') {
- /* A literal literal, copy verbatim */
- *(t++) = *f;
- continue;
- }
-
- if (remaining == 1) {
- if (flags & UNESCAPE_RELAX) {
- /* A trailing backslash, copy verbatim */
- *(t++) = *f;
- continue;
- }
-
- free(r);
- return -EINVAL;
- }
-
- k = cunescape_one(f + 1, remaining - 1, &c, &u);
- if (k < 0) {
- if (flags & UNESCAPE_RELAX) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- continue;
- }
-
- free(r);
- return k;
- }
-
- if (c != 0)
- /* Non-Unicode? Let's encode this directly */
- *(t++) = c;
- else
- /* Unicode? Then let's encode this in UTF-8 */
- t += utf8_encode_unichar(t, u);
-
- f += k;
- }
-
- *t = 0;
-
- *ret = r;
- return t - r;
-}
-
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
- return cunescape_length_with_prefix(s, length, NULL, flags, ret);
-}
-
-int cunescape(const char *s, UnescapeFlags flags, char **ret) {
- return cunescape_length(s, strlen(s), flags, ret);
-}
-
-char *xescape(const char *s, const char *bad) {
- char *r, *t;
- const char *f;
-
- /* Escapes all chars in bad, in addition to \ and all special
- * chars, in \xFF style escaping. May be reversed with
- * cunescape(). */
-
- r = new(char, strlen(s) * 4 + 1);
- if (!r)
- return NULL;
-
- for (f = s, t = r; *f; f++) {
-
- if ((*f < ' ') || (*f >= 127) ||
- (*f == '\\') || strchr(bad, *f)) {
- *(t++) = '\\';
- *(t++) = 'x';
- *(t++) = hexchar(*f >> 4);
- *(t++) = hexchar(*f);
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *ascii_strlower(char *t) {
- char *p;
-
- assert(t);
-
- for (p = t; *p; p++)
- if (*p >= 'A' && *p <= 'Z')
- *p = *p - 'A' + 'a';
-
- return t;
-}
-
-_pure_ static 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_file(const char *filename) {
- assert(filename);
-
- if (endswith(filename, "~"))
- return true;
-
- return hidden_file_allow_backup(filename);
-}
-
-int fd_nonblock(int fd, bool nonblock) {
- int flags, nflags;
-
- assert(fd >= 0);
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0)
- return -errno;
-
- if (nonblock)
- nflags = flags | O_NONBLOCK;
- else
- nflags = flags & ~O_NONBLOCK;
-
- if (nflags == flags)
- return 0;
-
- if (fcntl(fd, F_SETFL, nflags) < 0)
- return -errno;
-
- return 0;
-}
-
-int fd_cloexec(int fd, bool cloexec) {
- int flags, nflags;
-
- assert(fd >= 0);
-
- flags = fcntl(fd, F_GETFD, 0);
- if (flags < 0)
- return -errno;
-
- if (cloexec)
- nflags = flags | FD_CLOEXEC;
- else
- nflags = flags & ~FD_CLOEXEC;
-
- if (nflags == flags)
- return 0;
-
- if (fcntl(fd, F_SETFD, nflags) < 0)
- return -errno;
-
- return 0;
-}
-
-_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
- unsigned i;
-
- assert(n_fdset == 0 || fdset);
-
- for (i = 0; i < n_fdset; i++)
- if (fdset[i] == fd)
- return true;
-
- return false;
-}
-
-int close_all_fds(const int except[], unsigned n_except) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int r = 0;
-
- assert(n_except == 0 || except);
-
- d = opendir("/proc/self/fd");
- if (!d) {
- int fd;
- struct rlimit rl;
-
- /* When /proc isn't available (for example in chroots)
- * the fallback is brute forcing through the fd
- * table */
-
- assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
- for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
-
- if (fd_in_set(fd, except, n_except))
- continue;
-
- if (close_nointr(fd) < 0)
- if (errno != EBADF && r == 0)
- r = -errno;
- }
-
- return r;
- }
-
- while ((de = readdir(d))) {
- int fd = -1;
-
- if (hidden_file(de->d_name))
- continue;
-
- if (safe_atoi(de->d_name, &fd) < 0)
- /* Let's better ignore this, just in case */
- continue;
-
- if (fd < 3)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- if (fd_in_set(fd, except, n_except))
- continue;
-
- if (close_nointr(fd) < 0) {
- /* Valgrind has its own FD and doesn't want to have it closed */
- if (errno != EBADF && r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-bool chars_intersect(const char *a, const char *b) {
- const char *p;
-
- /* Returns true if any of the chars in a are in b. */
- for (p = a; *p; p++)
- if (strchr(b, *p))
- return true;
-
- return false;
-}
-
-bool fstype_is_network(const char *fstype) {
- static const char table[] =
- "afs\0"
- "cifs\0"
- "smbfs\0"
- "sshfs\0"
- "ncpfs\0"
- "ncp\0"
- "nfs\0"
- "nfs4\0"
- "gfs\0"
- "gfs2\0"
- "glusterfs\0";
-
- const char *x;
-
- x = startswith(fstype, "fuse.");
- if (x)
- fstype = x;
-
- return nulstr_contains(table, fstype);
-}
-
-int flush_fd(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN,
- };
-
- for (;;) {
- char buf[LINE_MAX];
- ssize_t l;
- int r;
-
- r = poll(&pollfd, 1, 0);
- if (r < 0) {
- if (errno == EINTR)
- continue;
-
- return -errno;
-
- } else if (r == 0)
- return 0;
-
- l = read(fd, buf, sizeof(buf));
- if (l < 0) {
-
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN)
- return 0;
-
- return -errno;
- } else if (l == 0)
- return 0;
- }
-}
-
-void safe_close_pair(int p[]) {
- assert(p);
-
- if (p[0] == p[1]) {
- /* Special case pairs which use the same fd in both
- * directions... */
- p[0] = p[1] = safe_close(p[0]);
- return;
- }
-
- p[0] = safe_close(p[0]);
- p[1] = safe_close(p[1]);
-}
-
-ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
- uint8_t *p = buf;
- ssize_t n = 0;
-
- assert(fd >= 0);
- assert(buf);
-
- while (nbytes > 0) {
- ssize_t k;
-
- k = read(fd, p, nbytes);
- if (k < 0) {
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN && do_poll) {
-
- /* We knowingly ignore any return value here,
- * and expect that any error/EOF is reported
- * via read() */
-
- fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
- continue;
- }
-
- return n > 0 ? n : -errno;
- }
-
- if (k == 0)
- return n;
-
- p += k;
- nbytes -= k;
- n += k;
- }
-
- return n;
-}
-
-int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
- ssize_t n;
-
- n = loop_read(fd, buf, nbytes, do_poll);
- if (n < 0)
- return n;
- if ((size_t) n != nbytes)
- return -EIO;
- return 0;
-}
-
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
- const uint8_t *p = buf;
-
- assert(fd >= 0);
- assert(buf);
-
- errno = 0;
-
- do {
- ssize_t k;
-
- k = write(fd, p, nbytes);
- if (k < 0) {
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN && do_poll) {
- /* We knowingly ignore any return value here,
- * and expect that any error/EOF is reported
- * via write() */
-
- fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
- continue;
- }
-
- return -errno;
- }
-
- if (nbytes > 0 && k == 0) /* Can't really happen */
- return -EIO;
-
- p += k;
- nbytes -= k;
- } while (nbytes > 0);
-
- return 0;
-}
-
-int parse_size(const char *t, off_t base, off_t *size) {
-
- /* Soo, sometimes we want to parse IEC binary suffixes, and
- * sometimes SI decimal suffixes. This function can parse
- * both. Which one is the right way depends on the
- * context. Wikipedia suggests that SI is customary for
- * hardware metrics and network speeds, while IEC is
- * customary for most data sizes used by software and volatile
- * (RAM) memory. Hence be careful which one you pick!
- *
- * In either case we use just K, M, G as suffix, and not Ki,
- * Mi, Gi or so (as IEC would suggest). That's because that's
- * frickin' ugly. But this means you really need to make sure
- * to document which base you are parsing when you use this
- * call. */
-
- struct table {
- const char *suffix;
- unsigned long long factor;
- };
-
- static const struct table iec[] = {
- { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
- { "G", 1024ULL*1024ULL*1024ULL },
- { "M", 1024ULL*1024ULL },
- { "K", 1024ULL },
- { "B", 1 },
- { "", 1 },
- };
-
- static const struct table si[] = {
- { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
- { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
- { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
- { "G", 1000ULL*1000ULL*1000ULL },
- { "M", 1000ULL*1000ULL },
- { "K", 1000ULL },
- { "B", 1 },
- { "", 1 },
- };
-
- const struct table *table;
- const char *p;
- unsigned long long r = 0;
- unsigned n_entries, start_pos = 0;
-
- assert(t);
- assert(base == 1000 || base == 1024);
- assert(size);
-
- if (base == 1000) {
- table = si;
- n_entries = ELEMENTSOF(si);
- } else {
- table = iec;
- n_entries = ELEMENTSOF(iec);
- }
-
- p = t;
- do {
- long long l;
- unsigned long long l2;
- double frac = 0;
- char *e;
- unsigned i;
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno > 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (e == p)
- return -EINVAL;
-
- if (*e == '.') {
- e++;
- if (*e >= '0' && *e <= '9') {
- char *e2;
-
- /* strotoull itself would accept space/+/- */
- l2 = strtoull(e, &e2, 10);
-
- if (errno == ERANGE)
- return -errno;
-
- /* Ignore failure. E.g. 10.M is valid */
- frac = l2;
- for (; e < e2; e++)
- frac /= 10;
- }
- }
-
- e += strspn(e, WHITESPACE);
-
- for (i = start_pos; i < n_entries; i++)
- if (startswith(e, table[i].suffix)) {
- unsigned long long tmp;
- if ((unsigned long long) l + (frac > 0) > ULLONG_MAX / table[i].factor)
- return -ERANGE;
- tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
- if (tmp > ULLONG_MAX - r)
- return -ERANGE;
-
- r += tmp;
- if ((unsigned long long) (off_t) r != r)
- return -ERANGE;
-
- p = e + strlen(table[i].suffix);
-
- start_pos = i + 1;
- break;
- }
-
- if (i >= n_entries)
- return -EINVAL;
-
- } while (*p);
-
- *size = r;
-
- return 0;
-}
-
-bool is_device_path(const char *path) {
-
- /* Returns true on paths that refer to a device, either in
- * sysfs or in /dev */
-
- return
- path_startswith(path, "/dev/") ||
- path_startswith(path, "/sys/");
-}
-
-int dir_is_empty(const char *path) {
- _cleanup_closedir_ DIR *d;
-
- d = opendir(path);
- if (!d)
- return -errno;
-
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- return 1;
-
- if (!hidden_file(de->d_name))
- return 0;
- }
-}
-
-char* dirname_malloc(const char *path) {
- char *d, *dir, *dir2;
-
- d = strdup(path);
- if (!d)
- return NULL;
- dir = dirname(d);
- assert(dir);
-
- if (dir != d) {
- dir2 = strdup(dir);
- free(d);
- return dir2;
- }
-
- return dir;
-}
-
-void rename_process(const char name[8]) {
- assert(name);
-
- /* This is a like a poor man's setproctitle(). It changes the
- * comm field, argv[0], and also the glibc's internally used
- * name of the process. For the first one a limit of 16 chars
- * applies, to the second one usually one of 10 (i.e. length
- * of "/sbin/init"), to the third one one of 7 (i.e. length of
- * "systemd"). If you pass a longer string it will be
- * truncated */
-
- prctl(PR_SET_NAME, name);
-
- if (program_invocation_name)
- strncpy(program_invocation_name, name, strlen(program_invocation_name));
-
- if (saved_argc > 0) {
- int i;
-
- if (saved_argv[0])
- strncpy(saved_argv[0], name, strlen(saved_argv[0]));
-
- for (i = 1; i < saved_argc; i++) {
- if (!saved_argv[i])
- break;
-
- memzero(saved_argv[i], strlen(saved_argv[i]));
- }
- }
-}
-
-char *lookup_uid(uid_t uid) {
- long bufsize;
- char *name;
- _cleanup_free_ char *buf = NULL;
- struct passwd pwbuf, *pw = NULL;
-
- /* Shortcut things to avoid NSS lookups */
- if (uid == 0)
- return strdup("root");
-
- bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize <= 0)
- bufsize = 4096;
-
- buf = malloc(bufsize);
- if (!buf)
- return NULL;
-
- if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
- return strdup(pw->pw_name);
-
- if (asprintf(&name, UID_FMT, uid) < 0)
- return NULL;
-
- return name;
-}
-
-char* getlogname_malloc(void) {
- uid_t uid;
- struct stat st;
-
- if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
- uid = st.st_uid;
- else
- uid = getuid();
-
- return lookup_uid(uid);
-}
-
-char *getusername_malloc(void) {
- const char *e;
-
- e = getenv("USER");
- if (e)
- return strdup(e);
-
- return lookup_uid(getuid());
-}
-
-bool is_temporary_fs(const struct statfs *s) {
- assert(s);
-
- return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
- F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
-}
-
-int fd_is_temporary_fs(int fd) {
- struct statfs s;
-
- if (fstatfs(fd, &s) < 0)
- return -errno;
-
- return is_temporary_fs(&s);
-}
-
-int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- assert(path);
-
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
-
- if (mode != MODE_INVALID)
- if (chmod(path, mode) < 0)
- return -errno;
-
- if (uid != UID_INVALID || gid != GID_INVALID)
- if (chown(path, uid, gid) < 0)
- return -errno;
-
- return 0;
-}
-
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
- assert(fd >= 0);
-
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
-
- if (mode != MODE_INVALID)
- if (fchmod(fd, mode) < 0)
- return -errno;
-
- if (uid != UID_INVALID || gid != GID_INVALID)
- if (fchown(fd, uid, gid) < 0)
- return -errno;
-
- return 0;
-}
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
- cpu_set_t *r;
- unsigned n = 1024;
-
- /* Allocates the cpuset in the right size */
-
- for (;;) {
- if (!(r = CPU_ALLOC(n)))
- return NULL;
-
- if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
- CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
-
- if (ncpus)
- *ncpus = n;
-
- return r;
- }
-
- CPU_FREE(r);
-
- if (errno != EINVAL)
- return NULL;
-
- n *= 2;
- }
-}
-
-int files_same(const char *filea, const char *fileb) {
- struct stat a, b;
-
- if (stat(filea, &a) < 0)
- return -errno;
-
- if (stat(fileb, &b) < 0)
- return -errno;
-
- return a.st_dev == b.st_dev &&
- a.st_ino == b.st_ino;
-}
-
-int running_in_chroot(void) {
- int ret;
-
- ret = files_same("/proc/1/root", "/");
- if (ret < 0)
- return ret;
-
- return ret == 0;
-}
-
-static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
- size_t x;
- char *r;
-
- assert(s);
- assert(percent <= 100);
- assert(new_length >= 3);
-
- if (old_length <= 3 || old_length <= new_length)
- return strndup(s, old_length);
-
- r = new0(char, new_length+1);
- if (!r)
- return NULL;
-
- x = (new_length * percent) / 100;
-
- if (x > new_length - 3)
- x = new_length - 3;
-
- memcpy(r, s, x);
- r[x] = '.';
- r[x+1] = '.';
- r[x+2] = '.';
- memcpy(r + x + 3,
- s + old_length - (new_length - x - 3),
- new_length - x - 3);
-
- return r;
-}
-
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
- size_t x;
- char *e;
- const char *i, *j;
- unsigned k, len, len2;
-
- assert(s);
- assert(percent <= 100);
- assert(new_length >= 3);
-
- /* if no multibyte characters use ascii_ellipsize_mem for speed */
- if (ascii_is_valid(s))
- return ascii_ellipsize_mem(s, old_length, new_length, percent);
-
- if (old_length <= 3 || old_length <= new_length)
- return strndup(s, old_length);
-
- x = (new_length * percent) / 100;
-
- if (x > new_length - 3)
- x = new_length - 3;
-
- k = 0;
- for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
- int c;
-
- c = utf8_encoded_to_unichar(i);
- if (c < 0)
- return NULL;
- k += unichar_iswide(c) ? 2 : 1;
- }
-
- if (k > x) /* last character was wide and went over quota */
- x ++;
-
- for (j = s + old_length; k < new_length && j > i; ) {
- int c;
-
- j = utf8_prev_char(j);
- c = utf8_encoded_to_unichar(j);
- if (c < 0)
- return NULL;
- k += unichar_iswide(c) ? 2 : 1;
- }
- assert(i <= j);
-
- /* we don't actually need to ellipsize */
- if (i == j)
- return memdup(s, old_length + 1);
-
- /* make space for ellipsis */
- j = utf8_next_char(j);
-
- len = i - s;
- len2 = s + old_length - j;
- e = new(char, len + 3 + len2 + 1);
- if (!e)
- return NULL;
-
- /*
- printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
- old_length, new_length, x, len, len2, k);
- */
-
- memcpy(e, s, len);
- e[len] = 0xe2; /* tri-dot ellipsis: … */
- e[len + 1] = 0x80;
- e[len + 2] = 0xa6;
-
- memcpy(e + len + 3, j, len2 + 1);
-
- return e;
-}
-
-char *ellipsize(const char *s, size_t length, unsigned percent) {
- return ellipsize_mem(s, strlen(s), length, percent);
-}
-
-int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
- _cleanup_close_ int fd;
- int r;
-
- assert(path);
-
- if (parents)
- mkdir_parents(path, 0755);
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
- if (fd < 0)
- return -errno;
-
- if (mode > 0) {
- r = fchmod(fd, mode);
- if (r < 0)
- return -errno;
- }
-
- if (uid != UID_INVALID || gid != GID_INVALID) {
- r = fchown(fd, uid, gid);
- if (r < 0)
- return -errno;
- }
-
- if (stamp != USEC_INFINITY) {
- struct timespec ts[2];
-
- timespec_store(&ts[0], stamp);
- ts[1] = ts[0];
- r = futimens(fd, ts);
- } else
- r = futimens(fd, NULL);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int touch(const char *path) {
- return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
-}
-
-static char *unquote(const char *s, const char* quotes) {
- size_t l;
- assert(s);
-
- /* This is rather stupid, simply removes the heading and
- * trailing quotes if there is one. Doesn't care about
- * escaping or anything.
- *
- * DON'T USE THIS FOR NEW CODE ANYMORE!*/
-
- l = strlen(s);
- if (l < 2)
- return strdup(s);
-
- if (strchr(quotes, s[0]) && s[l-1] == s[0])
- return strndup(s+1, l-2);
-
- return strdup(s);
-}
-
-noreturn void freeze(void) {
-
- /* Make sure nobody waits for us on a socket anymore */
- close_all_fds(NULL, 0);
-
- sync();
-
- for (;;)
- pause();
-}
-
-bool null_or_empty(struct stat *st) {
- assert(st);
-
- if (S_ISREG(st->st_mode) && st->st_size <= 0)
- return true;
-
- if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
- return true;
-
- return false;
-}
-
-int null_or_empty_path(const char *fn) {
- struct stat st;
-
- assert(fn);
-
- if (stat(fn, &st) < 0)
- return -errno;
-
- return null_or_empty(&st);
-}
-
-int null_or_empty_fd(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- return null_or_empty(&st);
-}
-
-DIR *xopendirat(int fd, const char *name, int flags) {
- int nfd;
- DIR *d;
-
- assert(!(flags & O_CREAT));
-
- nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
- if (nfd < 0)
- return NULL;
-
- d = fdopendir(nfd);
- if (!d) {
- safe_close(nfd);
- return NULL;
- }
-
- return d;
-}
-
-static char *tag_to_udev_node(const char *tagvalue, const char *by) {
- _cleanup_free_ char *t = NULL, *u = NULL;
- size_t enc_len;
-
- u = unquote(tagvalue, QUOTES);
- if (!u)
- return NULL;
-
- enc_len = strlen(u) * 4 + 1;
- t = new(char, enc_len);
- if (!t)
- return NULL;
-
- if (encode_devnode_name(u, t, enc_len) < 0)
- return NULL;
-
- return strjoin("/dev/disk/by-", by, "/", t, NULL);
-}
-
-char *fstab_node_to_udev_node(const char *p) {
- assert(p);
-
- if (startswith(p, "LABEL="))
- return tag_to_udev_node(p+6, "label");
-
- if (startswith(p, "UUID="))
- return tag_to_udev_node(p+5, "uuid");
-
- if (startswith(p, "PARTUUID="))
- return tag_to_udev_node(p+9, "partuuid");
-
- if (startswith(p, "PARTLABEL="))
- return tag_to_udev_node(p+10, "partlabel");
-
- return strdup(p);
-}
-
-bool dirent_is_file(const struct dirent *de) {
- assert(de);
-
- if (hidden_file(de->d_name))
- return false;
-
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
- return false;
-
- return true;
-}
-
-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)
- return false;
-
- if (hidden_file_allow_backup(de->d_name))
- return false;
-
- return endswith(de->d_name, suffix);
-}
-
-static int do_execute(char **directories, usec_t timeout, char *argv[]) {
- _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
- _cleanup_set_free_free_ Set *seen = NULL;
- char **directory;
-
- /* We fork this all off from a child process so that we can
- * somewhat cleanly make use of SIGALRM to set a time limit */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- pids = hashmap_new(NULL);
- if (!pids)
- return log_oom();
-
- seen = set_new(&string_hash_ops);
- if (!seen)
- return log_oom();
-
- STRV_FOREACH(directory, directories) {
- _cleanup_closedir_ DIR *d;
- struct dirent *de;
-
- d = opendir(*directory);
- if (!d) {
- if (errno == ENOENT)
- continue;
-
- return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
- }
-
- FOREACH_DIRENT(de, d, break) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
- int r;
-
- if (!dirent_is_file(de))
- continue;
-
- if (set_contains(seen, de->d_name)) {
- log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
- continue;
- }
-
- r = set_put_strdup(seen, de->d_name);
- if (r < 0)
- return log_oom();
-
- path = strjoin(*directory, "/", de->d_name, NULL);
- if (!path)
- return log_oom();
-
- if (null_or_empty_path(path)) {
- log_debug("%s is empty (a mask).", path);
- continue;
- }
-
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- continue;
- } else if (pid == 0) {
- char *_argv[2];
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- if (!argv) {
- _argv[0] = path;
- _argv[1] = NULL;
- argv = _argv;
- } else
- argv[0] = path;
-
- execv(path, argv);
- return log_error_errno(errno, "Failed to execute %s: %m", path);
- }
-
- log_debug("Spawned %s as " PID_FMT ".", path, pid);
-
- r = hashmap_put(pids, UINT_TO_PTR(pid), path);
- if (r < 0)
- return log_oom();
- path = NULL;
- }
- }
-
- /* Abort execution of this process after the timout. We simply
- * rely on SIGALRM as default action terminating the process,
- * and turn on alarm(). */
-
- if (timeout != USEC_INFINITY)
- alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
-
- while (!hashmap_isempty(pids)) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
-
- pid = PTR_TO_UINT(hashmap_first_key(pids));
- assert(pid > 0);
-
- path = hashmap_remove(pids, UINT_TO_PTR(pid));
- assert(path);
-
- wait_for_terminate_and_warn(path, pid, true);
- }
-
- return 0;
-}
-
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
- pid_t executor_pid;
- int r;
- char *name;
- char **dirs = (char**) directories;
-
- assert(!strv_isempty(dirs));
-
- name = basename(dirs[0]);
- assert(!isempty(name));
-
- /* Executes all binaries in the directories in parallel and waits
- * for them to finish. Optionally a timeout is applied. If a file
- * with the same name exists in more than one directory, the
- * earliest one wins. */
-
- executor_pid = fork();
- if (executor_pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- return;
-
- } else if (executor_pid == 0) {
- r = do_execute(dirs, timeout, argv);
- _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-
- wait_for_terminate_and_warn(name, executor_pid, true);
-}
-
-bool nulstr_contains(const char*nulstr, const char *needle) {
- const char *i;
-
- if (!nulstr)
- return false;
-
- NULSTR_FOREACH(i, nulstr)
- if (streq(i, needle))
- return true;
-
- return false;
-}
-
-bool plymouth_running(void) {
- return access("/run/plymouth/pid", F_OK) >= 0;
-}
-
-char* strshorten(char *s, size_t l) {
- assert(s);
-
- if (l < strlen(s))
- s[l] = 0;
-
- return s;
-}
-
-bool machine_name_is_valid(const char *s) {
-
- if (!hostname_is_valid(s))
- return false;
-
- /* Machine names should be useful hostnames, but also be
- * useful in unit names, hence we enforce a stricter length
- * limitation. */
-
- if (strlen(s) > 64)
- return false;
-
- return true;
-}
-
-int pipe_eof(int fd) {
- struct pollfd pollfd = {
- .fd = fd,
- .events = POLLIN|POLLHUP,
- };
-
- int r;
-
- r = poll(&pollfd, 1, 0);
- if (r < 0)
- return -errno;
-
- if (r == 0)
- return 0;
-
- return pollfd.revents & POLLHUP;
-}
-
-int fd_wait_for_event(int fd, int event, usec_t t) {
-
- struct pollfd pollfd = {
- .fd = fd,
- .events = event,
- };
-
- struct timespec ts;
- int r;
-
- r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
- if (r < 0)
- return -errno;
-
- if (r == 0)
- return 0;
-
- return pollfd.revents;
-}
-
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
- FILE *f;
- char *t;
- int r, fd;
-
- assert(path);
- assert(_f);
- assert(_temp_path);
-
- r = tempfn_xxxxxx(path, &t);
- if (r < 0)
- return r;
-
- fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
- if (fd < 0) {
- free(t);
- return -errno;
- }
-
- f = fdopen(fd, "we");
- if (!f) {
- unlink(t);
- free(t);
- return -errno;
- }
-
- *_f = f;
- *_temp_path = t;
-
- return 0;
-}
-
-int symlink_atomic(const char *from, const char *to) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(from);
- assert(to);
-
- r = tempfn_random(to, &t);
- if (r < 0)
- return r;
-
- if (symlink(from, t) < 0)
- return -errno;
-
- if (rename(t, to) < 0) {
- unlink_noerrno(t);
- return -errno;
- }
-
- return 0;
-}
-
-int symlink_idempotent(const char *from, const char *to) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- assert(from);
- assert(to);
-
- if (symlink(from, to) < 0) {
- if (errno != EEXIST)
- return -errno;
-
- r = readlink_malloc(to, &p);
- if (r < 0)
- return r;
-
- if (!streq(p, from))
- return -EINVAL;
- }
-
- return 0;
-}
-
-int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(path);
-
- r = tempfn_random(path, &t);
- if (r < 0)
- return r;
-
- if (mknod(t, mode, dev) < 0)
- return -errno;
-
- if (rename(t, path) < 0) {
- unlink_noerrno(t);
- return -errno;
- }
-
- return 0;
-}
-
-int mkfifo_atomic(const char *path, mode_t mode) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- assert(path);
-
- r = tempfn_random(path, &t);
- if (r < 0)
- return r;
-
- if (mkfifo(t, mode) < 0)
- return -errno;
-
- if (rename(t, path) < 0) {
- unlink_noerrno(t);
- return -errno;
- }
-
- return 0;
-}
-
-bool display_is_local(const char *display) {
- assert(display);
-
- return
- display[0] == ':' &&
- display[1] >= '0' &&
- display[1] <= '9';
-}
-
-int socket_from_display(const char *display, char **path) {
- size_t k;
- char *f, *c;
-
- assert(display);
- assert(path);
-
- if (!display_is_local(display))
- return -EINVAL;
-
- k = strspn(display+1, "0123456789");
-
- f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
- if (!f)
- return -ENOMEM;
-
- c = stpcpy(f, "/tmp/.X11-unix/X");
- memcpy(c, display+1, k);
- c[k] = 0;
-
- *path = f;
-
- return 0;
-}
-
-int get_user_creds(
- const char **username,
- uid_t *uid, gid_t *gid,
- const char **home,
- const char **shell) {
-
- struct passwd *p;
- uid_t u;
-
- assert(username);
- assert(*username);
-
- /* We enforce some special rules for uid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*username, "root") || streq(*username, "0")) {
- *username = "root";
-
- if (uid)
- *uid = 0;
-
- if (gid)
- *gid = 0;
-
- if (home)
- *home = "/root";
-
- if (shell)
- *shell = "/bin/sh";
-
- return 0;
- }
-
- if (parse_uid(*username, &u) >= 0) {
- errno = 0;
- p = getpwuid(u);
-
- /* If there are multiple users with the same id, make
- * sure to leave $USER to the configured value instead
- * of the first occurrence in the database. However if
- * the uid was configured by a numeric uid, then let's
- * pick the real username from /etc/passwd. */
- if (p)
- *username = p->pw_name;
- } else {
- errno = 0;
- p = getpwnam(*username);
- }
-
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
-
- if (uid)
- *uid = p->pw_uid;
-
- if (gid)
- *gid = p->pw_gid;
-
- if (home)
- *home = p->pw_dir;
-
- if (shell)
- *shell = p->pw_shell;
-
- return 0;
-}
-
-char* uid_to_name(uid_t uid) {
- struct passwd *p;
- char *r;
-
- if (uid == 0)
- return strdup("root");
-
- p = getpwuid(uid);
- if (p)
- return strdup(p->pw_name);
-
- if (asprintf(&r, UID_FMT, uid) < 0)
- return NULL;
-
- return r;
-}
-
-char* gid_to_name(gid_t gid) {
- struct group *p;
- char *r;
-
- if (gid == 0)
- return strdup("root");
-
- p = getgrgid(gid);
- if (p)
- return strdup(p->gr_name);
-
- if (asprintf(&r, GID_FMT, gid) < 0)
- return NULL;
-
- return r;
-}
-
-int get_group_creds(const char **groupname, gid_t *gid) {
- struct group *g;
- gid_t id;
-
- assert(groupname);
-
- /* We enforce some special rules for gid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*groupname, "root") || streq(*groupname, "0")) {
- *groupname = "root";
-
- if (gid)
- *gid = 0;
-
- return 0;
- }
-
- if (parse_gid(*groupname, &id) >= 0) {
- errno = 0;
- g = getgrgid(id);
-
- if (g)
- *groupname = g->gr_name;
- } else {
- errno = 0;
- g = getgrnam(*groupname);
- }
-
- if (!g)
- return errno > 0 ? -errno : -ESRCH;
-
- if (gid)
- *gid = g->gr_gid;
-
- return 0;
-}
-
-int in_gid(gid_t gid) {
- gid_t *gids;
- int ngroups_max, r, i;
-
- if (getgid() == gid)
- return 1;
-
- if (getegid() == gid)
- return 1;
-
- ngroups_max = sysconf(_SC_NGROUPS_MAX);
- assert(ngroups_max > 0);
-
- gids = alloca(sizeof(gid_t) * ngroups_max);
-
- r = getgroups(ngroups_max, gids);
- if (r < 0)
- return -errno;
-
- for (i = 0; i < r; i++)
- if (gids[i] == gid)
- return 1;
-
- return 0;
-}
-
-int in_group(const char *name) {
- int r;
- gid_t gid;
-
- r = get_group_creds(&name, &gid);
- if (r < 0)
- return r;
-
- return in_gid(gid);
-}
-
-int glob_exists(const char *path) {
- _cleanup_globfree_ glob_t g = {};
- int k;
-
- assert(path);
-
- errno = 0;
- k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-
- if (k == GLOB_NOMATCH)
- return 0;
- else if (k == GLOB_NOSPACE)
- return -ENOMEM;
- else if (k == 0)
- return !strv_isempty(g.gl_pathv);
- else
- return errno ? -errno : -EIO;
-}
-
-int glob_extend(char ***strv, const char *path) {
- _cleanup_globfree_ glob_t g = {};
- int k;
- char **p;
-
- errno = 0;
- k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-
- if (k == GLOB_NOMATCH)
- return -ENOENT;
- else if (k == GLOB_NOSPACE)
- return -ENOMEM;
- else if (k != 0 || strv_isempty(g.gl_pathv))
- return errno ? -errno : -EIO;
-
- STRV_FOREACH(p, g.gl_pathv) {
- k = strv_extend(strv, *p);
- if (k < 0)
- break;
- }
-
- return k;
-}
-
-int dirent_ensure_type(DIR *d, struct dirent *de) {
- struct stat st;
-
- assert(d);
- assert(de);
-
- if (de->d_type != DT_UNKNOWN)
- return 0;
-
- if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
- return -errno;
-
- de->d_type =
- S_ISREG(st.st_mode) ? DT_REG :
- S_ISDIR(st.st_mode) ? DT_DIR :
- S_ISLNK(st.st_mode) ? DT_LNK :
- S_ISFIFO(st.st_mode) ? DT_FIFO :
- S_ISSOCK(st.st_mode) ? DT_SOCK :
- S_ISCHR(st.st_mode) ? DT_CHR :
- S_ISBLK(st.st_mode) ? DT_BLK :
- DT_UNKNOWN;
-
- return 0;
-}
-
-int get_files_in_directory(const char *path, char ***list) {
- _cleanup_closedir_ DIR *d = NULL;
- size_t bufsize = 0, n = 0;
- _cleanup_strv_free_ char **l = NULL;
-
- assert(path);
-
- /* Returns all files in a directory in *list, and the number
- * of files as return value. If list is NULL returns only the
- * number. */
-
- d = opendir(path);
- if (!d)
- return -errno;
-
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
- if (!de)
- break;
-
- dirent_ensure_type(d, de);
-
- if (!dirent_is_file(de))
- continue;
-
- if (list) {
- /* one extra slot is needed for the terminating NULL */
- if (!GREEDY_REALLOC(l, bufsize, n + 2))
- return -ENOMEM;
-
- l[n] = strdup(de->d_name);
- if (!l[n])
- return -ENOMEM;
-
- l[++n] = NULL;
- } else
- n++;
- }
-
- if (list) {
- *list = l;
- l = NULL; /* avoid freeing */
- }
-
- return n;
-}
-
-char *strjoin(const char *x, ...) {
- va_list ap;
- size_t l;
- char *r, *p;
-
- va_start(ap, x);
-
- if (x) {
- l = strlen(x);
-
- for (;;) {
- const char *t;
- size_t n;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- n = strlen(t);
- if (n > ((size_t) -1) - l) {
- va_end(ap);
- return NULL;
- }
-
- l += n;
- }
- } else
- l = 0;
-
- va_end(ap);
-
- r = new(char, l+1);
- if (!r)
- return NULL;
-
- if (x) {
- p = stpcpy(r, x);
-
- va_start(ap, x);
-
- for (;;) {
- const char *t;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- p = stpcpy(p, t);
- }
-
- va_end(ap);
- } else
- r[0] = 0;
-
- return r;
-}
-
-bool is_main_thread(void) {
- static thread_local int cached = 0;
-
- if (_unlikely_(cached == 0))
- cached = getpid() == gettid() ? 1 : -1;
-
- return cached > 0;
-}
-
-int block_get_whole_disk(dev_t d, dev_t *ret) {
- char *p, *s;
- int r;
- unsigned n, m;
-
- assert(ret);
-
- /* If it has a queue this is good enough for us */
- if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r >= 0) {
- *ret = d;
- return 0;
- }
-
- /* If it is a partition find the originating device */
- if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r < 0)
- return -ENOENT;
-
- /* Get parent dev_t */
- if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, &s);
- free(p);
-
- if (r < 0)
- return r;
-
- r = sscanf(s, "%u:%u", &m, &n);
- free(s);
-
- if (r != 2)
- return -EINVAL;
-
- /* Only return this if it is really good enough for us. */
- if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r >= 0) {
- *ret = makedev(m, n);
- return 0;
- }
-
- return -ENOENT;
-}
-
-static const char *const ioprio_class_table[] = {
- [IOPRIO_CLASS_NONE] = "none",
- [IOPRIO_CLASS_RT] = "realtime",
- [IOPRIO_CLASS_BE] = "best-effort",
- [IOPRIO_CLASS_IDLE] = "idle"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
-
-static const char *const sigchld_code_table[] = {
- [CLD_EXITED] = "exited",
- [CLD_KILLED] = "killed",
- [CLD_DUMPED] = "dumped",
- [CLD_TRAPPED] = "trapped",
- [CLD_STOPPED] = "stopped",
- [CLD_CONTINUED] = "continued",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
-
-static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
- [LOG_FAC(LOG_KERN)] = "kern",
- [LOG_FAC(LOG_USER)] = "user",
- [LOG_FAC(LOG_MAIL)] = "mail",
- [LOG_FAC(LOG_DAEMON)] = "daemon",
- [LOG_FAC(LOG_AUTH)] = "auth",
- [LOG_FAC(LOG_SYSLOG)] = "syslog",
- [LOG_FAC(LOG_LPR)] = "lpr",
- [LOG_FAC(LOG_NEWS)] = "news",
- [LOG_FAC(LOG_UUCP)] = "uucp",
- [LOG_FAC(LOG_CRON)] = "cron",
- [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
- [LOG_FAC(LOG_FTP)] = "ftp",
- [LOG_FAC(LOG_LOCAL0)] = "local0",
- [LOG_FAC(LOG_LOCAL1)] = "local1",
- [LOG_FAC(LOG_LOCAL2)] = "local2",
- [LOG_FAC(LOG_LOCAL3)] = "local3",
- [LOG_FAC(LOG_LOCAL4)] = "local4",
- [LOG_FAC(LOG_LOCAL5)] = "local5",
- [LOG_FAC(LOG_LOCAL6)] = "local6",
- [LOG_FAC(LOG_LOCAL7)] = "local7"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0));
-
-static const char *const log_level_table[] = {
- [LOG_EMERG] = "emerg",
- [LOG_ALERT] = "alert",
- [LOG_CRIT] = "crit",
- [LOG_ERR] = "err",
- [LOG_WARNING] = "warning",
- [LOG_NOTICE] = "notice",
- [LOG_INFO] = "info",
- [LOG_DEBUG] = "debug"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
-
-static const char* const sched_policy_table[] = {
- [SCHED_OTHER] = "other",
- [SCHED_BATCH] = "batch",
- [SCHED_IDLE] = "idle",
- [SCHED_FIFO] = "fifo",
- [SCHED_RR] = "rr"
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
-
-static const char* const rlimit_table[_RLIMIT_MAX] = {
- [RLIMIT_CPU] = "LimitCPU",
- [RLIMIT_FSIZE] = "LimitFSIZE",
- [RLIMIT_DATA] = "LimitDATA",
- [RLIMIT_STACK] = "LimitSTACK",
- [RLIMIT_CORE] = "LimitCORE",
- [RLIMIT_RSS] = "LimitRSS",
- [RLIMIT_NOFILE] = "LimitNOFILE",
- [RLIMIT_AS] = "LimitAS",
- [RLIMIT_NPROC] = "LimitNPROC",
- [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
- [RLIMIT_LOCKS] = "LimitLOCKS",
- [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
- [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
- [RLIMIT_NICE] = "LimitNICE",
- [RLIMIT_RTPRIO] = "LimitRTPRIO",
- [RLIMIT_RTTIME] = "LimitRTTIME"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
-
-static const char* const ip_tos_table[] = {
- [IPTOS_LOWDELAY] = "low-delay",
- [IPTOS_THROUGHPUT] = "throughput",
- [IPTOS_RELIABILITY] = "reliability",
- [IPTOS_LOWCOST] = "low-cost",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
-
-bool kexec_loaded(void) {
- bool loaded = false;
- char *s;
-
- if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
- if (s[0] == '1')
- loaded = true;
- free(s);
- }
- return loaded;
-}
-
-int prot_from_flags(int flags) {
-
- switch (flags & O_ACCMODE) {
-
- case O_RDONLY:
- return PROT_READ;
-
- case O_WRONLY:
- return PROT_WRITE;
-
- case O_RDWR:
- return PROT_READ|PROT_WRITE;
-
- default:
- return -EINVAL;
- }
-}
-
-char *format_bytes(char *buf, size_t l, off_t t) {
- unsigned i;
-
- static const struct {
- const char *suffix;
- off_t factor;
- } table[] = {
- { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
- { "G", 1024ULL*1024ULL*1024ULL },
- { "M", 1024ULL*1024ULL },
- { "K", 1024ULL },
- };
-
- if (t == (off_t) -1)
- return NULL;
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
-
- if (t >= table[i].factor) {
- snprintf(buf, l,
- "%llu.%llu%s",
- (unsigned long long) (t / table[i].factor),
- (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
- table[i].suffix);
-
- goto finish;
- }
- }
-
- snprintf(buf, l, "%lluB", (unsigned long long) t);
-
-finish:
- buf[l-1] = 0;
- return buf;
-
-}
-
-void* memdup(const void *p, size_t l) {
- void *r;
-
- assert(p);
-
- r = malloc(l);
- if (!r)
- return NULL;
-
- memcpy(r, p, l);
- return r;
-}
-
-int fd_inc_sndbuf(int fd, size_t n) {
- int r, value;
- socklen_t l = sizeof(value);
-
- r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
- if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
- return 0;
-
- /* If we have the privileges we will ignore the kernel limit. */
-
- value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- return -errno;
-
- return 1;
-}
-
-int fd_inc_rcvbuf(int fd, size_t n) {
- int r, value;
- socklen_t l = sizeof(value);
-
- r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
- if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
- return 0;
-
- /* If we have the privileges we will ignore the kernel limit. */
-
- value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- return -errno;
- return 1;
-}
-
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
- bool stdout_is_tty, stderr_is_tty;
- pid_t parent_pid, agent_pid;
- sigset_t ss, saved_ss;
- unsigned n, i;
- va_list ap;
- char **l;
-
- assert(pid);
- assert(path);
-
- /* Spawns a temporary TTY agent, making sure it goes away when
- * we go away */
-
- parent_pid = getpid();
-
- /* First we temporarily block all signals, so that the new
- * child has them blocked initially. This way, we can be sure
- * that SIGTERMs are not lost we might send to the agent. */
- assert_se(sigfillset(&ss) >= 0);
- assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
-
- agent_pid = fork();
- if (agent_pid < 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- return -errno;
- }
-
- if (agent_pid != 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- *pid = agent_pid;
- return 0;
- }
-
- /* In the child:
- *
- * Make sure the agent goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Make sure we actually can kill the agent, if we need to, in
- * case somebody invoked us from a shell script that trapped
- * SIGTERM or so... */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- /* Check whether our parent died before we were able
- * to set the death signal and unblock the signals */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
- /* Don't leak fds to the agent */
- close_all_fds(except, n_except);
-
- stdout_is_tty = isatty(STDOUT_FILENO);
- stderr_is_tty = isatty(STDERR_FILENO);
-
- if (!stdout_is_tty || !stderr_is_tty) {
- int fd;
-
- /* Detach from stdout/stderr. and reopen
- * /dev/tty for them. This is important to
- * ensure that when systemctl is started via
- * popen() or a similar call that expects to
- * read EOF we actually do generate EOF and
- * not delay this indefinitely by because we
- * keep an unused copy of stdin around. */
- fd = open("/dev/tty", O_WRONLY);
- if (fd < 0) {
- log_error_errno(errno, "Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stdout_is_tty)
- dup2(fd, STDOUT_FILENO);
-
- if (!stderr_is_tty)
- dup2(fd, STDERR_FILENO);
-
- if (fd > 2)
- close(fd);
- }
-
- /* Count arguments */
- va_start(ap, path);
- for (n = 0; va_arg(ap, char*); n++)
- ;
- va_end(ap);
-
- /* Allocate strv */
- l = alloca(sizeof(char *) * (n + 1));
-
- /* Fill in arguments */
- va_start(ap, path);
- for (i = 0; i <= n; i++)
- l[i] = va_arg(ap, char*);
- va_end(ap);
-
- execv(path, l);
- _exit(EXIT_FAILURE);
-}
-
-int setrlimit_closest(int resource, const struct rlimit *rlim) {
- struct rlimit highest, fixed;
-
- assert(rlim);
-
- if (setrlimit(resource, rlim) >= 0)
- return 0;
-
- if (errno != EPERM)
- return -errno;
-
- /* So we failed to set the desired setrlimit, then let's try
- * to get as close as we can */
- assert_se(getrlimit(resource, &highest) == 0);
-
- fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
- fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
-
- if (setrlimit(resource, &fixed) < 0)
- return -errno;
-
- return 0;
-}
-
-bool http_etag_is_valid(const char *etag) {
- if (isempty(etag))
- return false;
-
- if (!endswith(etag, "\""))
- return false;
-
- if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
- return false;
-
- return true;
-}
-
-bool http_url_is_valid(const char *url) {
- const char *p;
-
- if (isempty(url))
- return false;
-
- p = startswith(url, "http://");
- if (!p)
- p = startswith(url, "https://");
- if (!p)
- return false;
-
- if (isempty(p))
- return false;
-
- return ascii_is_valid(p);
-}
-
-bool documentation_url_is_valid(const char *url) {
- const char *p;
-
- if (isempty(url))
- return false;
-
- if (http_url_is_valid(url))
- return true;
-
- p = startswith(url, "file:/");
- if (!p)
- p = startswith(url, "info:");
- if (!p)
- p = startswith(url, "man:");
-
- if (isempty(p))
- return false;
-
- return ascii_is_valid(p);
-}
-
-bool in_initrd(void) {
- static int saved = -1;
- struct statfs s;
-
- if (saved >= 0)
- return saved;
-
- /* We make two checks here:
- *
- * 1. the flag file /etc/initrd-release must exist
- * 2. the root file system must be a memory file system
- *
- * The second check is extra paranoia, since misdetecting an
- * initrd can have bad bad consequences due the initrd
- * emptying when transititioning to the main systemd.
- */
-
- saved = access("/etc/initrd-release", F_OK) >= 0 &&
- statfs("/", &s) >= 0 &&
- is_temporary_fs(&s);
-
- return saved;
-}
-
-int get_home_dir(char **_h) {
- struct passwd *p;
- const char *e;
- char *h;
- uid_t u;
-
- assert(_h);
-
- /* Take the user specified one */
- e = secure_getenv("HOME");
- if (e && path_is_absolute(e)) {
- h = strdup(e);
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
- }
-
- /* Hardcode home directory for root to avoid NSS */
- u = getuid();
- if (u == 0) {
- h = strdup("/root");
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
- }
-
- /* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
-
- if (!path_is_absolute(p->pw_dir))
- return -EINVAL;
-
- h = strdup(p->pw_dir);
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
-}
-
-int get_shell(char **_s) {
- struct passwd *p;
- const char *e;
- char *s;
- uid_t u;
-
- assert(_s);
-
- /* Take the user specified one */
- e = getenv("SHELL");
- if (e) {
- s = strdup(e);
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
- }
-
- /* Hardcode home directory for root to avoid NSS */
- u = getuid();
- if (u == 0) {
- s = strdup("/bin/sh");
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
- }
-
- /* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
-
- if (!path_is_absolute(p->pw_shell))
- return -EINVAL;
-
- s = strdup(p->pw_shell);
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
-}
-
-bool filename_is_valid(const char *p) {
-
- if (isempty(p))
- return false;
-
- if (strchr(p, '/'))
- return false;
-
- if (streq(p, "."))
- return false;
-
- if (streq(p, ".."))
- return false;
-
- if (strlen(p) > FILENAME_MAX)
- return false;
-
- return true;
-}
-
-bool string_is_safe(const char *p) {
- const char *t;
-
- if (!p)
- return false;
-
- for (t = p; *t; t++) {
- if (*t > 0 && *t < ' ')
- return false;
-
- if (strchr("\\\"\'\0x7f", *t))
- return false;
- }
-
- return true;
-}
-
-/**
- * Check if a string contains control characters. If 'ok' is non-NULL
- * it may be a string containing additional CCs to be considered OK.
- */
-bool string_has_cc(const char *p, const char *ok) {
- const char *t;
-
- assert(p);
-
- for (t = p; *t; t++) {
- if (ok && strchr(ok, *t))
- continue;
-
- if (*t > 0 && *t < ' ')
- return true;
-
- if (*t == 127)
- return true;
- }
-
- return false;
-}
-
-bool path_is_safe(const char *p) {
-
- if (isempty(p))
- return false;
-
- if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
- return false;
-
- if (strlen(p)+1 > PATH_MAX)
- return false;
-
- /* The following two checks are not really dangerous, but hey, they still are confusing */
- if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
- return false;
-
- if (strstr(p, "//"))
- return false;
-
- return true;
-}
-
-/* hey glibc, APIs with callbacks without a user pointer are so useless */
-void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
- int (*compar) (const void *, const void *, void *), void *arg) {
- size_t l, u, idx;
- const void *p;
- int comparison;
-
- l = 0;
- u = nmemb;
- while (l < u) {
- idx = (l + u) / 2;
- p = (void *)(((const char *) base) + (idx * size));
- comparison = compar(key, p, arg);
- if (comparison < 0)
- u = idx;
- else if (comparison > 0)
- l = idx + 1;
- else
- return (void *)p;
- }
- return NULL;
-}
-
-void init_gettext(void) {
- setlocale(LC_ALL, "");
- textdomain(GETTEXT_PACKAGE);
-}
-
-bool is_locale_utf8(void) {
- const char *set;
- static int cached_answer = -1;
-
- if (cached_answer >= 0)
- goto out;
-
- if (!setlocale(LC_ALL, "")) {
- cached_answer = true;
- goto out;
- }
-
- set = nl_langinfo(CODESET);
- if (!set) {
- cached_answer = true;
- goto out;
- }
-
- if (streq(set, "UTF-8")) {
- cached_answer = true;
- goto out;
- }
-
- /* For LC_CTYPE=="C" return true, because CTYPE is effectly
- * unset and everything can do to UTF-8 nowadays. */
- set = setlocale(LC_CTYPE, NULL);
- if (!set) {
- cached_answer = true;
- goto out;
- }
-
- /* Check result, but ignore the result if C was set
- * explicitly. */
- cached_answer =
- streq(set, "C") &&
- !getenv("LC_ALL") &&
- !getenv("LC_CTYPE") &&
- !getenv("LANG");
-
-out:
- return (bool) cached_answer;
-}
-
-const char *draw_special_char(DrawSpecialChar ch) {
- static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
-
- /* UTF-8 */ {
- [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */
- [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
- [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
- [DRAW_TREE_SPACE] = " ", /* */
- [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
- [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */
- [DRAW_ARROW] = "\342\206\222", /* → */
- [DRAW_DASH] = "\342\200\223", /* – */
- },
-
- /* ASCII fallback */ {
- [DRAW_TREE_VERTICAL] = "| ",
- [DRAW_TREE_BRANCH] = "|-",
- [DRAW_TREE_RIGHT] = "`-",
- [DRAW_TREE_SPACE] = " ",
- [DRAW_TRIANGULAR_BULLET] = ">",
- [DRAW_BLACK_CIRCLE] = "*",
- [DRAW_ARROW] = "->",
- [DRAW_DASH] = "-",
- }
- };
-
- return draw_table[!is_locale_utf8()][ch];
-}
-
-char *strreplace(const char *text, const char *old_string, const char *new_string) {
- const char *f;
- char *t, *r;
- size_t l, old_len, new_len;
-
- assert(text);
- assert(old_string);
- assert(new_string);
-
- old_len = strlen(old_string);
- new_len = strlen(new_string);
-
- l = strlen(text);
- r = new(char, l+1);
- if (!r)
- return NULL;
-
- f = text;
- t = r;
- while (*f) {
- char *a;
- size_t d, nl;
-
- if (!startswith(f, old_string)) {
- *(t++) = *(f++);
- continue;
- }
-
- d = t - r;
- nl = l - old_len + new_len;
- a = realloc(r, nl + 1);
- if (!a)
- goto oom;
-
- l = nl;
- r = a;
- t = r + d;
-
- t = stpcpy(t, new_string);
- f += old_len;
- }
-
- *t = 0;
- return r;
-
-oom:
- free(r);
- return NULL;
-}
-
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
- const char *i, *begin = NULL;
- enum {
- STATE_OTHER,
- STATE_ESCAPE,
- STATE_BRACKET
- } state = STATE_OTHER;
- char *obuf = NULL;
- size_t osz = 0, isz;
- FILE *f;
-
- assert(ibuf);
- assert(*ibuf);
-
- /* Strips ANSI color and replaces TABs by 8 spaces */
-
- isz = _isz ? *_isz : strlen(*ibuf);
-
- f = open_memstream(&obuf, &osz);
- if (!f)
- return NULL;
-
- for (i = *ibuf; i < *ibuf + isz + 1; i++) {
-
- switch (state) {
-
- case STATE_OTHER:
- if (i >= *ibuf + isz) /* EOT */
- break;
- else if (*i == '\x1B')
- state = STATE_ESCAPE;
- else if (*i == '\t')
- fputs(" ", f);
- else
- fputc(*i, f);
- break;
-
- case STATE_ESCAPE:
- if (i >= *ibuf + isz) { /* EOT */
- fputc('\x1B', f);
- break;
- } else if (*i == '[') {
- state = STATE_BRACKET;
- begin = i + 1;
- } else {
- fputc('\x1B', f);
- fputc(*i, f);
- state = STATE_OTHER;
- }
-
- break;
-
- case STATE_BRACKET:
-
- if (i >= *ibuf + isz || /* EOT */
- (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
- fputc('\x1B', f);
- fputc('[', f);
- state = STATE_OTHER;
- i = begin-1;
- } else if (*i == 'm')
- state = STATE_OTHER;
- break;
- }
- }
-
- if (ferror(f)) {
- fclose(f);
- free(obuf);
- return NULL;
- }
-
- fclose(f);
-
- free(*ibuf);
- *ibuf = obuf;
-
- if (_isz)
- *_isz = osz;
-
- return obuf;
-}
-
-int on_ac_power(void) {
- bool found_offline = false, found_online = false;
- _cleanup_closedir_ DIR *d = NULL;
-
- d = opendir("/sys/class/power_supply");
- if (!d)
- return errno == ENOENT ? true : -errno;
-
- for (;;) {
- struct dirent *de;
- _cleanup_close_ int fd = -1, device = -1;
- char contents[6];
- ssize_t n;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno != 0)
- return -errno;
-
- if (!de)
- break;
-
- if (hidden_file(de->d_name))
- continue;
-
- device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (device < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- continue;
-
- return -errno;
- }
-
- fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- if (errno == ENOENT)
- continue;
-
- return -errno;
- }
-
- n = read(fd, contents, sizeof(contents));
- if (n < 0)
- return -errno;
-
- if (n != 6 || memcmp(contents, "Mains\n", 6))
- continue;
-
- safe_close(fd);
- fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- if (errno == ENOENT)
- continue;
-
- return -errno;
- }
-
- n = read(fd, contents, sizeof(contents));
- if (n < 0)
- return -errno;
-
- if (n != 2 || contents[1] != '\n')
- return -EIO;
-
- if (contents[0] == '1') {
- found_online = true;
- break;
- } else if (contents[0] == '0')
- found_offline = true;
- else
- return -EIO;
- }
-
- return found_online || !found_offline;
-}
-
-static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
- char **i;
-
- assert(path);
- assert(mode);
- assert(_f);
-
- if (!path_strv_resolve_uniq(search, root))
- return -ENOMEM;
-
- STRV_FOREACH(i, search) {
- _cleanup_free_ char *p = NULL;
- FILE *f;
-
- if (root)
- p = strjoin(root, *i, "/", path, NULL);
- else
- p = strjoin(*i, "/", path, NULL);
- if (!p)
- return -ENOMEM;
-
- f = fopen(p, mode);
- if (f) {
- *_f = f;
- return 0;
- }
-
- if (errno != ENOENT)
- return -errno;
- }
-
- return -ENOENT;
-}
-
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
- _cleanup_strv_free_ char **copy = NULL;
-
- assert(path);
- assert(mode);
- assert(_f);
-
- if (path_is_absolute(path)) {
- FILE *f;
-
- f = fopen(path, mode);
- if (f) {
- *_f = f;
- return 0;
- }
-
- return -errno;
- }
-
- copy = strv_copy((char**) search);
- if (!copy)
- return -ENOMEM;
-
- return search_and_fopen_internal(path, mode, root, copy, _f);
-}
-
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
- _cleanup_strv_free_ char **s = NULL;
-
- if (path_is_absolute(path)) {
- FILE *f;
-
- f = fopen(path, mode);
- if (f) {
- *_f = f;
- return 0;
- }
-
- return -errno;
- }
-
- s = strv_split_nulstr(search);
- if (!s)
- return -ENOMEM;
-
- return search_and_fopen_internal(path, mode, root, s, _f);
-}
-
-char *strextend(char **x, ...) {
- va_list ap;
- size_t f, l;
- char *r, *p;
-
- assert(x);
-
- l = f = *x ? strlen(*x) : 0;
-
- va_start(ap, x);
- for (;;) {
- const char *t;
- size_t n;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- n = strlen(t);
- if (n > ((size_t) -1) - l) {
- va_end(ap);
- return NULL;
- }
-
- l += n;
- }
- va_end(ap);
-
- r = realloc(*x, l+1);
- if (!r)
- return NULL;
-
- p = r + f;
-
- va_start(ap, x);
- for (;;) {
- const char *t;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- p = stpcpy(p, t);
- }
- va_end(ap);
-
- *p = 0;
- *x = r;
-
- return r + l;
-}
-
-char *strrep(const char *s, unsigned n) {
- size_t l;
- char *r, *p;
- unsigned i;
-
- assert(s);
-
- l = strlen(s);
- p = r = malloc(l * n + 1);
- if (!r)
- return NULL;
-
- for (i = 0; i < n; i++)
- p = stpcpy(p, s);
-
- *p = 0;
- return r;
-}
-
-void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
- size_t a, newalloc;
- void *q;
-
- assert(p);
- assert(allocated);
-
- if (*allocated >= need)
- return *p;
-
- newalloc = MAX(need * 2, 64u / size);
- a = newalloc * size;
-
- /* check for overflows */
- if (a < size * need)
- return NULL;
-
- q = realloc(*p, a);
- if (!q)
- return NULL;
-
- *p = q;
- *allocated = newalloc;
- return q;
-}
-
-void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
- size_t prev;
- uint8_t *q;
-
- assert(p);
- assert(allocated);
-
- prev = *allocated;
-
- q = greedy_realloc(p, allocated, need, size);
- if (!q)
- return NULL;
-
- if (*allocated > prev)
- memzero(q + prev * size, (*allocated - prev) * size);
-
- return q;
-}
-
-bool id128_is_valid(const char *s) {
- size_t i, l;
-
- l = strlen(s);
- if (l == 32) {
-
- /* Simple formatted 128bit hex string */
-
- for (i = 0; i < l; i++) {
- char c = s[i];
-
- if (!(c >= '0' && c <= '9') &&
- !(c >= 'a' && c <= 'z') &&
- !(c >= 'A' && c <= 'Z'))
- return false;
- }
-
- } else if (l == 36) {
-
- /* Formatted UUID */
-
- for (i = 0; i < l; i++) {
- char c = s[i];
-
- if ((i == 8 || i == 13 || i == 18 || i == 23)) {
- if (c != '-')
- return false;
- } else {
- if (!(c >= '0' && c <= '9') &&
- !(c >= 'a' && c <= 'z') &&
- !(c >= 'A' && c <= 'Z'))
- return false;
- }
- }
-
- } else
- return false;
-
- return true;
-}
-
-int split_pair(const char *s, const char *sep, char **l, char **r) {
- char *x, *a, *b;
-
- assert(s);
- assert(sep);
- assert(l);
- assert(r);
-
- if (isempty(sep))
- return -EINVAL;
-
- x = strstr(s, sep);
- if (!x)
- return -EINVAL;
-
- a = strndup(s, x - s);
- if (!a)
- return -ENOMEM;
-
- b = strdup(x + strlen(sep));
- if (!b) {
- free(a);
- return -ENOMEM;
- }
-
- *l = a;
- *r = b;
-
- return 0;
-}
-
-int shall_restore_state(void) {
- _cleanup_free_ char *value = NULL;
- int r;
-
- r = get_proc_cmdline_key("systemd.restore_state=", &value);
- if (r < 0)
- return r;
- if (r == 0)
- return true;
-
- return parse_boolean(value) != 0;
-}
-
-int proc_cmdline(char **ret) {
- assert(ret);
-
- if (detect_container(NULL) > 0)
- return get_process_cmdline(1, 0, false, ret);
- else
- return read_one_line_file("/proc/cmdline", ret);
-}
-
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
- _cleanup_free_ char *line = NULL;
- const char *p;
- int r;
-
- assert(parse_item);
-
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
-
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- char *value = NULL;
-
- r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- /* Filter out arguments that are intended only for the
- * initrd */
- if (!in_initrd() && startswith(word, "rd."))
- continue;
-
- value = strchr(word, '=');
- if (value)
- *(value++) = 0;
-
- r = parse_item(word, value);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int get_proc_cmdline_key(const char *key, char **value) {
- _cleanup_free_ char *line = NULL, *ret = NULL;
- bool found = false;
- const char *p;
- int r;
-
- assert(key);
-
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
-
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- const char *e;
-
- r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- /* Filter out arguments that are intended only for the
- * initrd */
- if (!in_initrd() && startswith(word, "rd."))
- continue;
-
- if (value) {
- e = startswith(word, key);
- if (!e)
- continue;
-
- r = free_and_strdup(&ret, e);
- if (r < 0)
- return r;
-
- found = true;
- } else {
- if (streq(word, key))
- found = true;
- }
- }
-
- if (value) {
- *value = ret;
- ret = NULL;
- }
-
- return found;
-
-}
-
-int container_get_leader(const char *machine, pid_t *pid) {
- _cleanup_free_ char *s = NULL, *class = NULL;
- const char *p;
- pid_t leader;
- int r;
-
- assert(machine);
- assert(pid);
-
- p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
- if (r == -ENOENT)
- return -EHOSTDOWN;
- if (r < 0)
- return r;
- if (!s)
- return -EIO;
-
- if (!streq_ptr(class, "container"))
- return -EIO;
-
- r = parse_pid(s, &leader);
- if (r < 0)
- return r;
- if (leader <= 1)
- return -EIO;
-
- *pid = leader;
- return 0;
-}
-
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1;
- int rfd = -1;
-
- assert(pid >= 0);
-
- if (mntns_fd) {
- const char *mntns;
-
- mntns = procfs_file_alloca(pid, "ns/mnt");
- mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (mntnsfd < 0)
- return -errno;
- }
-
- if (pidns_fd) {
- const char *pidns;
-
- pidns = procfs_file_alloca(pid, "ns/pid");
- pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (pidnsfd < 0)
- return -errno;
- }
-
- if (netns_fd) {
- const char *netns;
-
- netns = procfs_file_alloca(pid, "ns/net");
- netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (netnsfd < 0)
- return -errno;
- }
-
- if (root_fd) {
- const char *root;
-
- root = procfs_file_alloca(pid, "root");
- rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (rfd < 0)
- return -errno;
- }
-
- if (pidns_fd)
- *pidns_fd = pidnsfd;
-
- if (mntns_fd)
- *mntns_fd = mntnsfd;
-
- if (netns_fd)
- *netns_fd = netnsfd;
-
- if (root_fd)
- *root_fd = rfd;
-
- pidnsfd = mntnsfd = netnsfd = -1;
-
- return 0;
-}
-
-int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
-
- if (pidns_fd >= 0)
- if (setns(pidns_fd, CLONE_NEWPID) < 0)
- return -errno;
-
- if (mntns_fd >= 0)
- if (setns(mntns_fd, CLONE_NEWNS) < 0)
- return -errno;
-
- if (netns_fd >= 0)
- if (setns(netns_fd, CLONE_NEWNET) < 0)
- return -errno;
-
- if (root_fd >= 0) {
- if (fchdir(root_fd) < 0)
- return -errno;
-
- if (chroot(".") < 0)
- return -errno;
- }
-
- return reset_uid_gid();
-}
-
-int getpeercred(int fd, struct ucred *ucred) {
- socklen_t n = sizeof(struct ucred);
- struct ucred u;
- int r;
-
- assert(fd >= 0);
- assert(ucred);
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
- if (r < 0)
- return -errno;
-
- if (n != sizeof(struct ucred))
- return -EIO;
-
- /* Check if the data is actually useful and not suppressed due
- * to namespacing issues */
- if (u.pid <= 0)
- return -ENODATA;
- if (u.uid == UID_INVALID)
- return -ENODATA;
- if (u.gid == GID_INVALID)
- return -ENODATA;
-
- *ucred = u;
- return 0;
-}
-
-int getpeersec(int fd, char **ret) {
- socklen_t n = 64;
- char *s;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
-
- if (errno != ERANGE)
- return -errno;
-
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
- return -errno;
- }
- }
-
- if (isempty(s)) {
- free(s);
- return -EOPNOTSUPP;
- }
-
- *ret = s;
- return 0;
-}
-
-/* This is much like like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern, int flags) {
- _cleanup_umask_ mode_t u;
- int fd;
-
- assert(pattern);
-
- u = umask(077);
-
- fd = mkostemp(pattern, flags);
- if (fd < 0)
- return -errno;
-
- 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 fd_warn_permissions(const char *path, int fd) {
- struct stat st;
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (st.st_mode & 0111)
- log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
-
- if (st.st_mode & 0002)
- log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
-
- if (getpid() == 1 && (st.st_mode & 0044) != 0044)
- log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
-
- return 0;
-}
-
-unsigned long personality_from_string(const char *p) {
-
- /* Parse a personality specifier. We introduce our own
- * identifiers that indicate specific ABIs, rather than just
- * hints regarding the register size, since we want to keep
- * things open for multiple locally supported ABIs for the
- * same register size. We try to reuse the ABI identifiers
- * used by libseccomp. */
-
-#if defined(__x86_64__)
-
- if (streq(p, "x86"))
- return PER_LINUX32;
-
- if (streq(p, "x86-64"))
- return PER_LINUX;
-
-#elif defined(__i386__)
-
- if (streq(p, "x86"))
- return PER_LINUX;
-#endif
-
- return PERSONALITY_INVALID;
-}
-
-const char* personality_to_string(unsigned long p) {
-
-#if defined(__x86_64__)
-
- if (p == PER_LINUX32)
- return "x86";
-
- if (p == PER_LINUX)
- return "x86-64";
-
-#elif defined(__i386__)
-
- if (p == PER_LINUX)
- return "x86";
-#endif
-
- return NULL;
-}
-
-uint64_t physical_memory(void) {
- long mem;
-
- /* We return this as uint64_t in case we are running as 32bit
- * process on a 64bit kernel with huge amounts of memory */
-
- mem = sysconf(_SC_PHYS_PAGES);
- assert(mem > 0);
-
- return (uint64_t) mem * (uint64_t) page_size();
-}
-
-void hexdump(FILE *f, const void *p, size_t s) {
- const uint8_t *b = p;
- unsigned n = 0;
-
- assert(s == 0 || b);
-
- while (s > 0) {
- size_t i;
-
- fprintf(f, "%04x ", n);
-
- for (i = 0; i < 16; i++) {
-
- if (i >= s)
- fputs(" ", f);
- else
- fprintf(f, "%02x ", b[i]);
-
- if (i == 7)
- fputc(' ', f);
- }
-
- fputc(' ', f);
-
- for (i = 0; i < 16; i++) {
-
- if (i >= s)
- fputc(' ', f);
- else
- fputc(isprint(b[i]) ? (char) b[i] : '.', f);
- }
-
- fputc('\n', f);
-
- if (s < 16)
- break;
-
- n += 16;
- b += 16;
- s -= 16;
- }
-}
-
-int update_reboot_param_file(const char *param) {
- int r = 0;
-
- if (param) {
-
- r = write_string_file(REBOOT_PARAM_FILE, param);
- if (r < 0)
- log_error("Failed to write reboot param to "
- REBOOT_PARAM_FILE": %s", strerror(-r));
- } else
- unlink(REBOOT_PARAM_FILE);
-
- return r;
-}
-
-int umount_recursive(const char *prefix, int flags) {
- bool again;
- int n = 0, r;
-
- /* Try to umount everything recursively below a
- * directory. Also, take care of stacked mounts, and keep
- * unmounting them until they are gone. */
-
- do {
- _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
-
- again = false;
- r = 0;
-
- proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!proc_self_mountinfo)
- return -errno;
-
- for (;;) {
- _cleanup_free_ char *path = NULL, *p = NULL;
- int k;
-
- k = fscanf(proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%*s" /* (6) mount options */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%*s " /* (9) file system type */
- "%*s" /* (10) mount source */
- "%*s" /* (11) mount options 2 */
- "%*[^\n]", /* some rubbish at the end */
- &path);
- if (k != 1) {
- if (k == EOF)
- break;
-
- continue;
- }
-
- r = cunescape(path, UNESCAPE_RELAX, &p);
- if (r < 0)
- return r;
-
- if (!path_startswith(p, prefix))
- continue;
-
- if (umount2(p, flags) < 0) {
- r = -errno;
- continue;
- }
-
- again = true;
- n++;
-
- break;
- }
-
- } while (again);
-
- return r ? r : n;
-}
-
-static int get_mount_flags(const char *path, unsigned long *flags) {
- struct statvfs buf;
-
- if (statvfs(path, &buf) < 0)
- return -errno;
- *flags = buf.f_flag;
- return 0;
-}
-
-int bind_remount_recursive(const char *prefix, bool ro) {
- _cleanup_set_free_free_ Set *done = NULL;
- _cleanup_free_ char *cleaned = NULL;
- int r;
-
- /* Recursively remount a directory (and all its submounts)
- * read-only or read-write. If the directory is already
- * mounted, we reuse the mount and simply mark it
- * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
- * operation). If it isn't we first make it one. Afterwards we
- * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
- * submounts we can access, too. When mounts are stacked on
- * the same mount point we only care for each individual
- * "top-level" mount on each point, as we cannot
- * influence/access the underlying mounts anyway. We do not
- * have any effect on future submounts that might get
- * propagated, they migt be writable. This includes future
- * submounts that have been triggered via autofs. */
-
- cleaned = strdup(prefix);
- if (!cleaned)
- return -ENOMEM;
-
- path_kill_slashes(cleaned);
-
- done = set_new(&string_hash_ops);
- if (!done)
- return -ENOMEM;
-
- for (;;) {
- _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
- _cleanup_set_free_free_ Set *todo = NULL;
- bool top_autofs = false;
- char *x;
- unsigned long orig_flags;
-
- todo = set_new(&string_hash_ops);
- if (!todo)
- return -ENOMEM;
-
- proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!proc_self_mountinfo)
- return -errno;
-
- for (;;) {
- _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
- int k;
-
- k = fscanf(proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%*s" /* (6) mount options (superblock) */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%ms " /* (9) file system type */
- "%*s" /* (10) mount source */
- "%*s" /* (11) mount options (bind mount) */
- "%*[^\n]", /* some rubbish at the end */
- &path,
- &type);
- if (k != 2) {
- if (k == EOF)
- break;
-
- continue;
- }
-
- r = cunescape(path, UNESCAPE_RELAX, &p);
- if (r < 0)
- return r;
-
- /* Let's ignore autofs mounts. If they aren't
- * triggered yet, we want to avoid triggering
- * them, as we don't make any guarantees for
- * future submounts anyway. If they are
- * already triggered, then we will find
- * another entry for this. */
- if (streq(type, "autofs")) {
- top_autofs = top_autofs || path_equal(cleaned, p);
- continue;
- }
-
- if (path_startswith(p, cleaned) &&
- !set_contains(done, p)) {
-
- r = set_consume(todo, p);
- p = NULL;
-
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- /* If we have no submounts to process anymore and if
- * the root is either already done, or an autofs, we
- * are done */
- if (set_isempty(todo) &&
- (top_autofs || set_contains(done, cleaned)))
- return 0;
-
- if (!set_contains(done, cleaned) &&
- !set_contains(todo, cleaned)) {
- /* The prefix directory itself is not yet a
- * mount, make it one. */
- if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
- return -errno;
-
- orig_flags = 0;
- (void) get_mount_flags(cleaned, &orig_flags);
- orig_flags &= ~MS_RDONLY;
-
- if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
- return -errno;
-
- x = strdup(cleaned);
- if (!x)
- return -ENOMEM;
-
- r = set_consume(done, x);
- if (r < 0)
- return r;
- }
-
- while ((x = set_steal_first(todo))) {
-
- r = set_consume(done, x);
- if (r == -EEXIST || r == 0)
- continue;
- if (r < 0)
- return r;
-
- /* Try to reuse the original flag set, but
- * don't care for errors, in case of
- * obstructed mounts */
- orig_flags = 0;
- (void) get_mount_flags(x, &orig_flags);
- orig_flags &= ~MS_RDONLY;
-
- if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) {
-
- /* Deal with mount points that are
- * obstructed by a later mount */
-
- if (errno != ENOENT)
- return -errno;
- }
-
- }
- }
-}
-
-int fflush_and_check(FILE *f) {
- assert(f);
-
- errno = 0;
- fflush(f);
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- return 0;
-}
-
-int tempfn_xxxxxx(const char *p, char **ret) {
- const char *fn;
- char *t;
-
- assert(p);
- assert(ret);
-
- /*
- * Turns this:
- * /foo/bar/waldo
- *
- * Into this:
- * /foo/bar/.#waldoXXXXXX
- */
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- t = new(char, strlen(p) + 2 + 6 + 1);
- if (!t)
- return -ENOMEM;
-
- strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX");
-
- *ret = path_kill_slashes(t);
- return 0;
-}
-
-int tempfn_random(const char *p, char **ret) {
- const char *fn;
- char *t, *x;
- uint64_t u;
- unsigned i;
-
- assert(p);
- assert(ret);
-
- /*
- * Turns this:
- * /foo/bar/waldo
- *
- * Into this:
- * /foo/bar/.#waldobaa2a261115984a9
- */
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- t = new(char, strlen(p) + 2 + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn);
-
- u = random_u64();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(u & 0xF);
- u >>= 4;
- }
-
- *x = 0;
-
- *ret = path_kill_slashes(t);
- return 0;
-}
-
-int tempfn_random_child(const char *p, char **ret) {
- char *t, *x;
- uint64_t u;
- unsigned i;
-
- assert(p);
- assert(ret);
-
- /* Turns this:
- * /foo/bar/waldo
- * Into this:
- * /foo/bar/waldo/.#3c2b6219aa75d7d0
- */
-
- t = new(char, strlen(p) + 3 + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- x = stpcpy(stpcpy(t, p), "/.#");
-
- u = random_u64();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(u & 0xF);
- u >>= 4;
- }
-
- *x = 0;
-
- *ret = path_kill_slashes(t);
- return 0;
-}
-
-int take_password_lock(const char *root) {
-
- struct flock flock = {
- .l_type = F_WRLCK,
- .l_whence = SEEK_SET,
- .l_start = 0,
- .l_len = 0,
- };
-
- const char *path;
- int fd, r;
-
- /* This is roughly the same as lckpwdf(), but not as awful. We
- * don't want to use alarm() and signals, hence we implement
- * our own trivial version of this.
- *
- * Note that shadow-utils also takes per-database locks in
- * addition to lckpwdf(). However, we don't given that they
- * are redundant as they they invoke lckpwdf() first and keep
- * it during everything they do. The per-database locks are
- * awfully racy, and thus we just won't do them. */
-
- if (root)
- path = strjoina(root, "/etc/.pwd.lock");
- else
- path = "/etc/.pwd.lock";
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
- if (fd < 0)
- return -errno;
-
- r = fcntl(fd, F_SETLKW, &flock);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- return fd;
-}
-
-int is_symlink(const char *path) {
- struct stat info;
-
- if (lstat(path, &info) < 0)
- return -errno;
-
- return !!S_ISLNK(info.st_mode);
-}
-
-int is_dir(const char* path, bool follow) {
- struct stat st;
- int r;
-
- if (follow)
- r = stat(path, &st);
- else
- r = lstat(path, &st);
- if (r < 0)
- return -errno;
-
- return !!S_ISDIR(st.st_mode);
-}
-
-int is_device_node(const char *path) {
- struct stat info;
-
- if (lstat(path, &info) < 0)
- return -errno;
-
- return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
-}
-
-int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
- _cleanup_free_ char *s = NULL;
- size_t allocated = 0, sz = 0;
- int r;
-
- enum {
- START,
- VALUE,
- VALUE_ESCAPE,
- SINGLE_QUOTE,
- SINGLE_QUOTE_ESCAPE,
- DOUBLE_QUOTE,
- DOUBLE_QUOTE_ESCAPE,
- SPACE,
- } state = START;
-
- assert(p);
- assert(*p);
- assert(ret);
-
- /* Parses the first word of a string, and returns it in
- * *ret. Removes all quotes in the process. When parsing fails
- * (because of an uneven number of quotes or similar), leaves
- * the pointer *p at the first invalid character. */
-
- for (;;) {
- char c = **p;
-
- switch (state) {
-
- case START:
- if (c == 0)
- goto finish;
- else if (strchr(WHITESPACE, c))
- break;
-
- state = VALUE;
- /* fallthrough */
-
- case VALUE:
- if (c == 0)
- goto finish;
- else if (c == '\'')
- state = SINGLE_QUOTE;
- else if (c == '\\')
- state = VALUE_ESCAPE;
- else if (c == '\"')
- state = DOUBLE_QUOTE;
- else if (strchr(WHITESPACE, c))
- state = SPACE;
- else {
- if (!GREEDY_REALLOC(s, allocated, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
- }
-
- break;
-
- case VALUE_ESCAPE:
- if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
- return -EINVAL;
- }
-
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
- if (flags & UNQUOTE_CUNESCAPE) {
- uint32_t u;
-
- r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0)
- return -EINVAL;
-
- (*p) += r - 1;
-
- if (c != 0)
- s[sz++] = c; /* normal explicit char */
- else
- sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
- } else
- s[sz++] = c;
-
- state = VALUE;
- break;
-
- case SINGLE_QUOTE:
- if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
- return -EINVAL;
- } else if (c == '\'')
- state = VALUE;
- else if (c == '\\')
- state = SINGLE_QUOTE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(s, allocated, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
- }
-
- break;
-
- case SINGLE_QUOTE_ESCAPE:
- if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
- return -EINVAL;
- }
-
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
- if (flags & UNQUOTE_CUNESCAPE) {
- uint32_t u;
-
- r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0)
- return -EINVAL;
-
- (*p) += r - 1;
-
- if (c != 0)
- s[sz++] = c;
- else
- sz += utf8_encode_unichar(s + sz, u);
- } else
- s[sz++] = c;
-
- state = SINGLE_QUOTE;
- break;
-
- case DOUBLE_QUOTE:
- if (c == 0)
- return -EINVAL;
- else if (c == '\"')
- state = VALUE;
- else if (c == '\\')
- state = DOUBLE_QUOTE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(s, allocated, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
- }
-
- break;
-
- case DOUBLE_QUOTE_ESCAPE:
- if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
- return -EINVAL;
- }
-
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
- if (flags & UNQUOTE_CUNESCAPE) {
- uint32_t u;
-
- r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0)
- return -EINVAL;
-
- (*p) += r - 1;
-
- if (c != 0)
- s[sz++] = c;
- else
- sz += utf8_encode_unichar(s + sz, u);
- } else
- s[sz++] = c;
-
- state = DOUBLE_QUOTE;
- break;
-
- case SPACE:
- if (c == 0)
- goto finish;
- if (!strchr(WHITESPACE, c))
- goto finish;
-
- break;
- }
-
- (*p) ++;
- }
-
-finish:
- if (!s) {
- *ret = NULL;
- return 0;
- }
-
- s[sz] = 0;
- *ret = s;
- s = NULL;
-
- return 1;
-}
-
-int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
- va_list ap;
- char **l;
- int n = 0, i, c, r;
-
- /* Parses a number of words from a string, stripping any
- * quotes if necessary. */
-
- assert(p);
-
- /* Count how many words are expected */
- va_start(ap, flags);
- for (;;) {
- if (!va_arg(ap, char **))
- break;
- n++;
- }
- va_end(ap);
-
- if (n <= 0)
- return 0;
-
- /* Read all words into a temporary array */
- l = newa0(char*, n);
- for (c = 0; c < n; c++) {
-
- r = unquote_first_word(p, &l[c], flags);
- if (r < 0) {
- int j;
-
- for (j = 0; j < c; j++)
- free(l[j]);
-
- return r;
- }
-
- if (r == 0)
- break;
- }
-
- /* If we managed to parse all words, return them in the passed
- * in parameters */
- va_start(ap, flags);
- for (i = 0; i < n; i++) {
- char **v;
-
- v = va_arg(ap, char **);
- assert(v);
-
- *v = l[i];
- }
- va_end(ap);
-
- return c;
-}
-
-int free_and_strdup(char **p, const char *s) {
- char *t;
-
- assert(p);
-
- /* Replaces a string pointer with an strdup()ed new string,
- * possibly freeing the old one. */
-
- if (streq_ptr(*p, s))
- return 0;
-
- if (s) {
- t = strdup(s);
- if (!t)
- return -ENOMEM;
- } else
- t = NULL;
-
- free(*p);
- *p = t;
-
- return 1;
-}
-
-int ptsname_malloc(int fd, char **ret) {
- size_t l = 100;
-
- assert(fd >= 0);
- assert(ret);
-
- for (;;) {
- char *c;
-
- c = new(char, l);
- if (!c)
- return -ENOMEM;
-
- if (ptsname_r(fd, c, l) == 0) {
- *ret = c;
- return 0;
- }
- if (errno != ERANGE) {
- free(c);
- return -errno;
- }
-
- free(c);
- l *= 2;
- }
-}
-
-int openpt_in_namespace(pid_t pid, int flags) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
- _cleanup_close_pair_ int pair[2] = { -1, -1 };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- siginfo_t si;
- pid_t child;
- int r;
-
- assert(pid > 0);
-
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
- if (r < 0)
- return r;
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
- return -errno;
-
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
- int master;
-
- pair[0] = safe_close(pair[0]);
-
- r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- master = posix_openpt(flags);
- if (master < 0)
- _exit(EXIT_FAILURE);
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
- }
-
- pair[1] = safe_close(pair[1]);
-
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return r;
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return -EIO;
-
- if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
- return -errno;
-
- CMSG_FOREACH(cmsg, &mh)
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- int *fds;
- unsigned n_fds;
-
- fds = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-
- if (n_fds != 1) {
- close_many(fds, n_fds);
- return -EIO;
- }
-
- return fds[0];
- }
-
- return -EIO;
-}
-
-ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
- _cleanup_close_ int fd = -1;
- ssize_t l;
-
- /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
-
- fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
- if (fd < 0)
- return -errno;
-
- l = fgetxattr(fd, attribute, value, size);
- if (l < 0)
- return -errno;
-
- return l;
-}
-
-static int parse_crtime(le64_t le, usec_t *usec) {
- uint64_t u;
-
- assert(usec);
-
- u = le64toh(le);
- if (u == 0 || u == (uint64_t) -1)
- return -EIO;
-
- *usec = (usec_t) u;
- return 0;
-}
-
-int fd_getcrtime(int fd, usec_t *usec) {
- le64_t le;
- ssize_t n;
-
- assert(fd >= 0);
- assert(usec);
-
- /* Until Linux gets a real concept of birthtime/creation time,
- * let's fake one with xattrs */
-
- n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le));
- if (n < 0)
- return -errno;
- if (n != sizeof(le))
- return -EIO;
-
- return parse_crtime(le, usec);
-}
-
-int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
- le64_t le;
- ssize_t n;
-
- n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags);
- if (n < 0)
- return -errno;
- if (n != sizeof(le))
- return -EIO;
-
- return parse_crtime(le, usec);
-}
-
-int path_getcrtime(const char *p, usec_t *usec) {
- le64_t le;
- ssize_t n;
-
- assert(p);
- assert(usec);
-
- n = getxattr(p, "user.crtime_usec", &le, sizeof(le));
- if (n < 0)
- return -errno;
- if (n != sizeof(le))
- return -EIO;
-
- return parse_crtime(le, usec);
-}
-
-int fd_setcrtime(int fd, usec_t usec) {
- le64_t le;
-
- assert(fd >= 0);
-
- if (usec <= 0)
- usec = now(CLOCK_REALTIME);
-
- le = htole64((uint64_t) usec);
- if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
- return -errno;
-
- return 0;
-}
-
-int same_fd(int a, int b) {
- struct stat sta, stb;
- pid_t pid;
- int r, fa, fb;
-
- assert(a >= 0);
- assert(b >= 0);
-
- /* Compares two file descriptors. Note that semantics are
- * quite different depending on whether we have kcmp() or we
- * don't. If we have kcmp() this will only return true for
- * dup()ed file descriptors, but not otherwise. If we don't
- * have kcmp() this will also return true for two fds of the same
- * file, created by separate open() calls. Since we use this
- * call mostly for filtering out duplicates in the fd store
- * this difference hopefully doesn't matter too much. */
-
- if (a == b)
- return true;
-
- /* Try to use kcmp() if we have it. */
- pid = getpid();
- r = kcmp(pid, pid, KCMP_FILE, a, b);
- if (r == 0)
- return true;
- if (r > 0)
- return false;
- if (errno != ENOSYS)
- return -errno;
-
- /* We don't have kcmp(), use fstat() instead. */
- if (fstat(a, &sta) < 0)
- return -errno;
-
- if (fstat(b, &stb) < 0)
- return -errno;
-
- if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
- return false;
-
- /* We consider all device fds different, since two device fds
- * might refer to quite different device contexts even though
- * they share the same inode and backing dev_t. */
-
- if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
- return false;
-
- if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
- return false;
-
- /* The fds refer to the same inode on disk, let's also check
- * if they have the same fd flags. This is useful to
- * distinguish the read and write side of a pipe created with
- * pipe(). */
- fa = fcntl(a, F_GETFL);
- if (fa < 0)
- return -errno;
-
- fb = fcntl(b, F_GETFL);
- if (fb < 0)
- return -errno;
-
- return fa == fb;
-}
-
-int chattr_fd(int fd, unsigned value, unsigned mask) {
- unsigned old_attr, new_attr;
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- /* Explicitly check whether this is a regular file or
- * directory. If it is anything else (such as a device node or
- * fifo), then the ioctl will not hit the file systems but
- * possibly drivers, where the ioctl might have different
- * effects. Notably, DRM is using the same ioctl() number. */
-
- if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
- return -ENOTTY;
-
- if (mask == 0)
- return 0;
-
- if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
- return -errno;
-
- new_attr = (old_attr & ~mask) | (value & mask);
- if (new_attr == old_attr)
- return 0;
-
- if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
- return -errno;
-
- return 1;
-}
-
-int chattr_path(const char *p, unsigned value, unsigned mask) {
- _cleanup_close_ int fd = -1;
-
- assert(p);
-
- if (mask == 0)
- return 0;
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return chattr_fd(fd, value, mask);
-}
-
-int read_attr_fd(int fd, unsigned *ret) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
- return -ENOTTY;
-
- if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0)
- return -errno;
-
- return 0;
-}
-
-int read_attr_path(const char *p, unsigned *ret) {
- _cleanup_close_ int fd = -1;
-
- assert(p);
- assert(ret);
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return read_attr_fd(fd, ret);
-}
-
-static size_t nul_length(const uint8_t *p, size_t sz) {
- size_t n = 0;
-
- while (sz > 0) {
- if (*p != 0)
- break;
-
- n++;
- p++;
- sz--;
- }
-
- return n;
-}
-
-ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
- const uint8_t *q, *w, *e;
- ssize_t l;
-
- q = w = p;
- e = q + sz;
- while (q < e) {
- size_t n;
-
- n = nul_length(q, e - q);
-
- /* If there are more than the specified run length of
- * NUL bytes, or if this is the beginning or the end
- * of the buffer, then seek instead of write */
- if ((n > run_length) ||
- (n > 0 && q == p) ||
- (n > 0 && q + n >= e)) {
- if (q > w) {
- l = write(fd, w, q - w);
- if (l < 0)
- return -errno;
- if (l != q -w)
- return -EIO;
- }
-
- if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
- return -errno;
-
- q += n;
- w = q;
- } else if (n > 0)
- q += n;
- else
- q ++;
- }
-
- if (q > w) {
- l = write(fd, w, q - w);
- if (l < 0)
- return -errno;
- if (l != q - w)
- return -EIO;
- }
-
- return q - (const uint8_t*) p;
-}
-
-void sigkill_wait(pid_t *pid) {
- if (!pid)
- return;
- if (*pid <= 1)
- return;
-
- if (kill(*pid, SIGKILL) > 0)
- (void) wait_for_terminate(*pid, NULL);
-}
-
-int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
- int a = 0, b = 0, c = 0;
- int k;
-
- assert(p);
- assert(*p);
- assert(priority);
-
- if ((*p)[0] != '<')
- return 0;
-
- if (!strchr(*p, '>'))
- return 0;
-
- if ((*p)[2] == '>') {
- c = undecchar((*p)[1]);
- k = 3;
- } else if ((*p)[3] == '>') {
- b = undecchar((*p)[1]);
- c = undecchar((*p)[2]);
- k = 4;
- } else if ((*p)[4] == '>') {
- a = undecchar((*p)[1]);
- b = undecchar((*p)[2]);
- c = undecchar((*p)[3]);
- k = 5;
- } else
- return 0;
-
- if (a < 0 || b < 0 || c < 0 ||
- (!with_facility && (a || b || c > 7)))
- return 0;
-
- if (with_facility)
- *priority = a*100 + b*10 + c;
- else
- *priority = (*priority & LOG_FACMASK) | c;
-
- *p += k;
- return 1;
-}
-
-ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
- size_t i;
-
- if (!key)
- return -1;
-
- for (i = 0; i < len; ++i)
- if (streq_ptr(table[i], key))
- return (ssize_t)i;
-
- return -1;
-}
-
-void cmsg_close_all(struct msghdr *mh) {
- struct cmsghdr *cmsg;
-
- assert(mh);
-
- CMSG_FOREACH(cmsg, mh)
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
- close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
-}
-
-int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
- struct stat buf;
- int ret;
-
- ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
- if (ret >= 0)
- return 0;
-
- /* Even though renameat2() exists since Linux 3.15, btrfs added
- * support for it later. If it is not implemented, fallback to another
- * method. */
- if (errno != EINVAL)
- return -errno;
-
- /* The link()/unlink() fallback does not work on directories. But
- * renameat() without RENAME_NOREPLACE gives the same semantics on
- * directories, except when newpath is an *empty* directory. This is
- * good enough. */
- ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
- if (ret >= 0 && S_ISDIR(buf.st_mode)) {
- ret = renameat(olddirfd, oldpath, newdirfd, newpath);
- return ret >= 0 ? 0 : -errno;
- }
-
- /* If it is not a directory, use the link()/unlink() fallback. */
- ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
- if (ret < 0)
- return -errno;
-
- ret = unlinkat(olddirfd, oldpath, 0);
- if (ret < 0) {
- /* backup errno before the following unlinkat() alters it */
- ret = errno;
- (void) unlinkat(newdirfd, newpath, 0);
- errno = ret;
- return -errno;
- }
-
- return 0;
-}
-
-char *shell_maybe_quote(const char *s) {
- const char *p;
- char *r, *t;
-
- assert(s);
-
- /* Encloses a string in double quotes if necessary to make it
- * OK as shell string. */
-
- for (p = s; *p; p++)
- if (*p <= ' ' ||
- *p >= 127 ||
- strchr(SHELL_NEED_QUOTES, *p))
- break;
-
- if (!*p)
- return strdup(s);
-
- r = new(char, 1+strlen(s)*2+1+1);
- if (!r)
- return NULL;
-
- t = r;
- *(t++) = '"';
- t = mempcpy(t, s, p - s);
-
- for (; *p; p++) {
-
- if (strchr(SHELL_NEED_ESCAPE, *p))
- *(t++) = '\\';
-
- *(t++) = *p;
- }
-
- *(t++)= '"';
- *t = 0;
-
- return r;
-}
-
-int parse_mode(const char *s, mode_t *ret) {
- char *x;
- long l;
-
- assert(s);
- assert(ret);
-
- errno = 0;
- l = strtol(s, &x, 8);
- if (errno != 0)
- return -errno;
-
- if (!x || x == s || *x)
- return -EINVAL;
- if (l < 0 || l > 07777)
- return -ERANGE;
-
- *ret = (mode_t) l;
- return 0;
-}
-
-int mount_move_root(const char *path) {
- assert(path);
-
- if (chdir(path) < 0)
- return -errno;
-
- if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
- return -errno;
-
- if (chroot(".") < 0)
- return -errno;
-
- if (chdir("/") < 0)
- return -errno;
-
- return 0;
-}
-
-int reset_uid_gid(void) {
-
- if (setgroups(0, NULL) < 0)
- return -errno;
-
- if (setresgid(0, 0, 0) < 0)
- return -errno;
-
- if (setresuid(0, 0, 0) < 0)
- return -errno;
-
- return 0;
-}
diff --git a/src/shared/util.h b/src/shared/util.h
deleted file mode 100644
index 467ae23..0000000
--- a/src/shared/util.h
+++ /dev/null
@@ -1,903 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <alloca.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <time.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <sched.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <locale.h>
-#include <mntent.h>
-#include <sys/inotify.h>
-#include <sys/statfs.h>
-
-#include "macro.h"
-#include "missing.h"
-#include "time-util.h"
-#include "formats-util.h"
-
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n\r"
-#define NEWLINE "\n\r"
-#define QUOTES "\"\'"
-#define COMMENTS "#;"
-#define GLOB_CHARS "*?["
-
-/* What characters are special in the shell? */
-/* must be escaped outside and inside double-quotes */
-#define SHELL_NEED_ESCAPE "\"\\`$"
-/* can be escaped or double-quoted */
-#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;"
-
-#define FORMAT_BYTES_MAX 8
-
-size_t page_size(void) _pure_;
-#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-
-#define streq(a,b) (strcmp((a),(b)) == 0)
-#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
-#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
-#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
-
-bool streq_ptr(const char *a, const char *b) _pure_;
-
-#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
-
-#define new0(t, n) ((t*) calloc((n), sizeof(t)))
-
-#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
-
-#define newa0(t, n) ((t*) alloca0(sizeof(t)*(n)))
-
-#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
-
-#define malloc0(n) (calloc((n), 1))
-
-static inline const char* yes_no(bool b) {
- return b ? "yes" : "no";
-}
-
-static inline const char* true_false(bool b) {
- return b ? "true" : "false";
-}
-
-static inline const char* one_zero(bool b) {
- return b ? "1" : "0";
-}
-
-static inline const char* strempty(const char *s) {
- return s ? s : "";
-}
-
-static inline const char* strnull(const char *s) {
- return s ? s : "(null)";
-}
-
-static inline const char *strna(const char *s) {
- return s ? s : "n/a";
-}
-
-static inline bool isempty(const char *p) {
- return !p || !p[0];
-}
-
-static inline char *startswith(const char *s, const char *prefix) {
- size_t l;
-
- l = strlen(prefix);
- if (strncmp(s, prefix, l) == 0)
- return (char*) s + l;
-
- return NULL;
-}
-
-static inline char *startswith_no_case(const char *s, const char *prefix) {
- size_t l;
-
- l = strlen(prefix);
- if (strncasecmp(s, prefix, l) == 0)
- return (char*) s + l;
-
- return NULL;
-}
-
-char *endswith(const char *s, const char *postfix) _pure_;
-char *endswith_no_case(const char *s, const char *postfix) _pure_;
-
-char *first_word(const char *s, const char *word) _pure_;
-
-int close_nointr(int fd);
-int safe_close(int fd);
-void safe_close_pair(int p[]);
-
-void close_many(const int fds[], unsigned n_fd);
-
-int parse_size(const char *t, off_t base, off_t *size);
-
-int parse_boolean(const char *v) _pure_;
-int parse_pid(const char *s, pid_t* ret_pid);
-int parse_uid(const char *s, uid_t* ret_uid);
-#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
-
-int safe_atou(const char *s, unsigned *ret_u);
-int safe_atoi(const char *s, int *ret_i);
-
-int safe_atollu(const char *s, unsigned long long *ret_u);
-int safe_atolli(const char *s, long long int *ret_i);
-
-int safe_atod(const char *s, double *ret_d);
-
-int safe_atou8(const char *s, uint8_t *ret);
-
-#if LONG_MAX == INT_MAX
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
- assert_cc(sizeof(unsigned long) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
-}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(int));
- return safe_atoi(s, (int*) ret_u);
-}
-#else
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
- assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
- return safe_atollu(s, (unsigned long long*) ret_u);
-}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_u);
-}
-#endif
-
-static inline int safe_atou32(const char *s, uint32_t *ret_u) {
- assert_cc(sizeof(uint32_t) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
-}
-
-static inline int safe_atoi32(const char *s, int32_t *ret_i) {
- assert_cc(sizeof(int32_t) == sizeof(int));
- return safe_atoi(s, (int*) ret_i);
-}
-
-static inline int safe_atou64(const char *s, uint64_t *ret_u) {
- assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
- return safe_atollu(s, (unsigned long long*) ret_u);
-}
-
-static inline int safe_atoi64(const char *s, int64_t *ret_i) {
- assert_cc(sizeof(int64_t) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_i);
-}
-
-int safe_atou16(const char *s, uint16_t *ret);
-int safe_atoi16(const char *s, int16_t *ret);
-
-const char* split(const char **state, size_t *l, const char *separator, bool quoted);
-
-#define FOREACH_WORD(word, length, s, state) \
- _FOREACH_WORD(word, length, s, WHITESPACE, false, state)
-
-#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
- _FOREACH_WORD(word, length, s, separator, false, state)
-
-#define FOREACH_WORD_QUOTED(word, length, s, state) \
- _FOREACH_WORD(word, length, s, WHITESPACE, true, state)
-
-#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
- for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
-
-char *strappend(const char *s, const char *suffix);
-char *strnappend(const char *s, const char *suffix, size_t length);
-
-int readlinkat_malloc(int fd, const char *p, char **ret);
-int readlink_malloc(const char *p, char **r);
-int readlink_value(const char *p, char **ret);
-int readlink_and_make_absolute(const char *p, char **r);
-int readlink_and_canonicalize(const char *p, char **r);
-
-char *strstrip(char *s);
-char *delete_chars(char *s, const char *bad);
-char *truncate_nl(char *s);
-
-char *file_in_same_dir(const char *path, const char *filename);
-
-int rmdir_parents(const char *path, const char *stop);
-
-char hexchar(int x) _const_;
-int unhexchar(char c) _const_;
-char octchar(int x) _const_;
-int unoctchar(char c) _const_;
-char decchar(int x) _const_;
-int undecchar(char c) _const_;
-
-char *cescape(const char *s);
-size_t cescape_char(char c, char *buf);
-
-typedef enum UnescapeFlags {
- UNESCAPE_RELAX = 1,
-} UnescapeFlags;
-
-int cunescape(const char *s, UnescapeFlags flags, char **ret);
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
-
-char *xescape(const char *s, const char *bad);
-
-char *ascii_strlower(char *path);
-
-bool dirent_is_file(const struct dirent *de) _pure_;
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
-
-bool hidden_file(const char *filename) _pure_;
-
-bool chars_intersect(const char *a, const char *b) _pure_;
-
-/* For basic lookup tables with strictly enumerated entries */
-#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
- scope const char *name##_to_string(type i) { \
- if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
- return NULL; \
- return name##_table[i]; \
- }
-
-ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
-
-#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
- scope inline type name##_from_string(const char *s) { \
- return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
- }
-
-#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
- _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
-
-/* For string conversions where numbers are also acceptable */
-#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
- int name##_to_string_alloc(type i, char **str) { \
- char *s; \
- int r; \
- if (i < 0 || i > max) \
- return -ERANGE; \
- if (i < (type) ELEMENTSOF(name##_table)) { \
- s = strdup(name##_table[i]); \
- if (!s) \
- return log_oom(); \
- } else { \
- r = asprintf(&s, "%i", i); \
- if (r < 0) \
- return log_oom(); \
- } \
- *str = s; \
- return 0; \
- } \
- type name##_from_string(const char *s) { \
- type i; \
- unsigned u = 0; \
- assert(s); \
- for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
- if (name##_table[i] && \
- streq(name##_table[i], s)) \
- return i; \
- if (safe_atou(s, &u) >= 0 && u <= max) \
- return (type) u; \
- return (type) -1; \
- } \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-int fd_nonblock(int fd, bool nonblock);
-int fd_cloexec(int fd, bool cloexec);
-
-int close_all_fds(const int except[], unsigned n_except);
-
-bool fstype_is_network(const char *fstype);
-
-int flush_fd(int fd);
-
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
-
-ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
-int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
-
-bool is_device_path(const char *path);
-
-int dir_is_empty(const char *path);
-char* dirname_malloc(const char *path);
-
-char* lookup_uid(uid_t uid);
-char* getlogname_malloc(void);
-char* getusername_malloc(void);
-
-int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
-
-bool is_temporary_fs(const struct statfs *s) _pure_;
-int fd_is_temporary_fs(int fd);
-
-int pipe_eof(int fd);
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-
-#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
-
-int files_same(const char *filea, const char *fileb);
-
-int running_in_chroot(void);
-
-char *ellipsize(const char *s, size_t length, unsigned percent);
- /* bytes columns */
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent);
-
-int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
-int touch(const char *path);
-
-noreturn void freeze(void);
-
-bool null_or_empty(struct stat *st) _pure_;
-int null_or_empty_path(const char *fn);
-int null_or_empty_fd(int fd);
-
-DIR *xopendirat(int dirfd, const char *name, int flags);
-
-char *fstab_node_to_udev_node(const char *p);
-
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
-
-bool nulstr_contains(const char*nulstr, const char *needle);
-
-bool plymouth_running(void);
-
-bool machine_name_is_valid(const char *s) _pure_;
-
-char* strshorten(char *s, size_t l);
-
-int symlink_idempotent(const char *from, const char *to);
-
-int symlink_atomic(const char *from, const char *to);
-int mknod_atomic(const char *path, mode_t mode, dev_t dev);
-int mkfifo_atomic(const char *path, mode_t mode);
-
-int fchmod_umask(int fd, mode_t mode);
-
-bool display_is_local(const char *display) _pure_;
-int socket_from_display(const char *display, char **path);
-
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
-int get_group_creds(const char **groupname, gid_t *gid);
-
-int in_gid(gid_t gid);
-int in_group(const char *name);
-
-char* uid_to_name(uid_t uid);
-char* gid_to_name(gid_t gid);
-
-int glob_exists(const char *path);
-int glob_extend(char ***strv, const char *path);
-
-int dirent_ensure_type(DIR *d, struct dirent *de);
-
-int get_files_in_directory(const char *path, char ***list);
-
-char *strjoin(const char *x, ...) _sentinel_;
-
-bool is_main_thread(void);
-
-static inline bool _pure_ in_charset(const char *s, const char* charset) {
- assert(s);
- assert(charset);
- return s[strspn(s, charset)] == '\0';
-}
-
-int block_get_whole_disk(dev_t d, dev_t *ret);
-
-#define NULSTR_FOREACH(i, l) \
- for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
-
-#define NULSTR_FOREACH_PAIR(i, j, l) \
- for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
-
-int ioprio_class_to_string_alloc(int i, char **s);
-int ioprio_class_from_string(const char *s);
-
-const char *sigchld_code_to_string(int i) _const_;
-int sigchld_code_from_string(const char *s) _pure_;
-
-int log_facility_unshifted_to_string_alloc(int i, char **s);
-int log_facility_unshifted_from_string(const char *s);
-
-int log_level_to_string_alloc(int i, char **s);
-int log_level_from_string(const char *s);
-
-int sched_policy_to_string_alloc(int i, char **s);
-int sched_policy_from_string(const char *s);
-
-const char *rlimit_to_string(int i) _const_;
-int rlimit_from_string(const char *s) _pure_;
-
-int ip_tos_to_string_alloc(int i, char **s);
-int ip_tos_from_string(const char *s);
-
-extern int saved_argc;
-extern char **saved_argv;
-
-bool kexec_loaded(void);
-
-int prot_from_flags(int flags) _const_;
-
-char *format_bytes(char *buf, size_t l, off_t t);
-
-int fd_wait_for_event(int fd, int event, usec_t timeout);
-
-void* memdup(const void *p, size_t l) _alloc_(2);
-
-int fd_inc_sndbuf(int fd, size_t n);
-int fd_inc_rcvbuf(int fd, size_t n);
-
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
-
-int setrlimit_closest(int resource, const struct rlimit *rlim);
-
-bool http_url_is_valid(const char *url) _pure_;
-bool documentation_url_is_valid(const char *url) _pure_;
-
-bool http_etag_is_valid(const char *etag);
-
-bool in_initrd(void);
-
-int get_home_dir(char **ret);
-int get_shell(char **_ret);
-
-static inline void freep(void *p) {
- free(*(void**) p);
-}
-
-static inline void closep(int *fd) {
- safe_close(*fd);
-}
-
-static inline void umaskp(mode_t *u) {
- umask(*u);
-}
-
-static inline void close_pairp(int (*p)[2]) {
- safe_close_pair(*p);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, fclose);
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
-
-#define _cleanup_free_ _cleanup_(freep)
-#define _cleanup_close_ _cleanup_(closep)
-#define _cleanup_umask_ _cleanup_(umaskp)
-#define _cleanup_globfree_ _cleanup_(globfree)
-#define _cleanup_fclose_ _cleanup_(fclosep)
-#define _cleanup_pclose_ _cleanup_(pclosep)
-#define _cleanup_closedir_ _cleanup_(closedirp)
-#define _cleanup_endmntent_ _cleanup_(endmntentp)
-#define _cleanup_close_pair_ _cleanup_(close_pairp)
-
-_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
- if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
- return NULL;
-
- return malloc(a * b);
-}
-
-_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
- if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
- return NULL;
-
- return realloc(p, a * b);
-}
-
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
- if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
- return NULL;
-
- return memdup(p, a * b);
-}
-
-bool filename_is_valid(const char *p) _pure_;
-bool path_is_safe(const char *p) _pure_;
-bool string_is_safe(const char *p) _pure_;
-bool string_has_cc(const char *p, const char *ok) _pure_;
-
-/**
- * Check if a string contains any glob patterns.
- */
-_pure_ static inline bool string_is_glob(const char *p) {
- return !!strpbrk(p, GLOB_CHARS);
-}
-
-void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
- int (*compar) (const void *, const void *, void *),
- void *arg);
-
-#define _(String) gettext (String)
-void init_gettext(void);
-bool is_locale_utf8(void);
-
-typedef enum DrawSpecialChar {
- DRAW_TREE_VERTICAL,
- DRAW_TREE_BRANCH,
- DRAW_TREE_RIGHT,
- DRAW_TREE_SPACE,
- DRAW_TRIANGULAR_BULLET,
- DRAW_BLACK_CIRCLE,
- DRAW_ARROW,
- DRAW_DASH,
- _DRAW_SPECIAL_CHAR_MAX
-} DrawSpecialChar;
-
-const char *draw_special_char(DrawSpecialChar ch);
-
-char *strreplace(const char *text, const char *old_string, const char *new_string);
-
-char *strip_tab_ansi(char **p, size_t *l);
-
-int on_ac_power(void);
-
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
-
-#define FOREACH_LINE(line, f, on_error) \
- for (;;) \
- if (!fgets(line, sizeof(line), f)) { \
- if (ferror(f)) { \
- on_error; \
- } \
- break; \
- } else
-
-#define FOREACH_DIRENT(de, d, on_error) \
- for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
- if (!de) { \
- if (errno > 0) { \
- on_error; \
- } \
- break; \
- } else if (hidden_file((de)->d_name)) \
- continue; \
- else
-
-#define FOREACH_DIRENT_ALL(de, d, on_error) \
- for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
- if (!de) { \
- if (errno > 0) { \
- on_error; \
- } \
- break; \
- } else
-
-static inline void *mempset(void *s, int c, size_t n) {
- memset(s, c, n);
- return (uint8_t*)s + n;
-}
-
-char *hexmem(const void *p, size_t l);
-void *unhexmem(const char *p, size_t l);
-
-char *strextend(char **x, ...) _sentinel_;
-char *strrep(const char *s, unsigned n);
-
-void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
-void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
-#define GREEDY_REALLOC(array, allocated, need) \
- greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
-
-#define GREEDY_REALLOC0(array, allocated, need) \
- greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
-
-static inline void _reset_errno_(int *saved_errno) {
- errno = *saved_errno;
-}
-
-#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno
-
-static inline int negative_errno(void) {
- /* This helper should be used to shut up gcc if you know 'errno' is
- * negative. Instead of "return -errno;", use "return negative_errno();"
- * It will suppress bogus gcc warnings in case it assumes 'errno' might
- * be 0 and thus the caller's error-handling might not be triggered. */
- assert_return(errno > 0, -EINVAL);
- return -errno;
-}
-
-struct _umask_struct_ {
- mode_t mask;
- bool quit;
-};
-
-static inline void _reset_umask_(struct _umask_struct_ *s) {
- umask(s->mask);
-};
-
-#define RUN_WITH_UMASK(mask) \
- for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \
- !_saved_umask_.quit ; \
- _saved_umask_.quit = true)
-
-static inline unsigned u64log2(uint64_t n) {
-#if __SIZEOF_LONG_LONG__ == 8
- return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
-#else
-#error "Wut?"
-#endif
-}
-
-static inline unsigned u32ctz(uint32_t n) {
-#if __SIZEOF_INT__ == 4
- return __builtin_ctz(n);
-#else
-#error "Wut?"
-#endif
-}
-
-static inline unsigned log2i(int x) {
- assert(x > 0);
-
- return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
-}
-
-static inline unsigned log2u(unsigned x) {
- assert(x > 0);
-
- return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
-}
-
-static inline unsigned log2u_round_up(unsigned x) {
- assert(x > 0);
-
- if (x == 1)
- return 0;
-
- return log2u(x - 1) + 1;
-}
-
-static inline bool logind_running(void) {
- return access("/run/systemd/seats/", F_OK) >= 0;
-}
-
-#define DECIMAL_STR_WIDTH(x) \
- ({ \
- typeof(x) _x_ = (x); \
- unsigned ans = 1; \
- while (_x_ /= 10) \
- ans++; \
- ans; \
- })
-
-int unlink_noerrno(const char *path);
-
-#define alloca0(n) \
- ({ \
- char *_new_; \
- size_t _len_ = n; \
- _new_ = alloca(_len_); \
- (void *) memset(_new_, 0, _len_); \
- })
-
-/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
-#define alloca_align(size, align) \
- ({ \
- void *_ptr_; \
- size_t _mask_ = (align) - 1; \
- _ptr_ = alloca((size) + _mask_); \
- (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
- })
-
-#define alloca0_align(size, align) \
- ({ \
- void *_new_; \
- size_t _size_ = (size); \
- _new_ = alloca_align(_size_, (align)); \
- (void*)memset(_new_, 0, _size_); \
- })
-
-#define strjoina(a, ...) \
- ({ \
- const char *_appendees_[] = { a, __VA_ARGS__ }; \
- char *_d_, *_p_; \
- int _len_ = 0; \
- unsigned _i_; \
- for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
- _len_ += strlen(_appendees_[_i_]); \
- _p_ = _d_ = alloca(_len_ + 1); \
- for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
- _p_ = stpcpy(_p_, _appendees_[_i_]); \
- *_p_ = 0; \
- _d_; \
- })
-
-bool id128_is_valid(const char *s) _pure_;
-
-int split_pair(const char *s, const char *sep, char **l, char **r);
-
-int shall_restore_state(void);
-
-/**
- * Normal qsort requires base to be nonnull. Here were require
- * that only if nmemb > 0.
- */
-static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
- if (nmemb <= 1)
- return;
-
- assert(base);
- qsort(base, nmemb, size, compar);
-}
-
-/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
-static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
-
- if (needlelen <= 0)
- return (void*) haystack;
-
- if (haystacklen < needlelen)
- return NULL;
-
- assert(haystack);
- assert(needle);
-
- return memmem(haystack, haystacklen, needle, needlelen);
-}
-
-int proc_cmdline(char **ret);
-int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
-int get_proc_cmdline_key(const char *parameter, char **value);
-
-int container_get_leader(const char *machine, pid_t *pid);
-
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd);
-int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
-
-int getpeercred(int fd, struct ucred *ucred);
-int getpeersec(int fd, char **ret);
-
-int writev_safe(int fd, const struct iovec *w, int j);
-
-int mkostemp_safe(char *pattern, int flags);
-int open_tmpfile(const char *path, int flags);
-
-int fd_warn_permissions(const char *path, int fd);
-
-#ifndef PERSONALITY_INVALID
-/* personality(7) documents that 0xffffffffUL is used for querying the
- * current personality, hence let's use that here as error
- * indicator. */
-#define PERSONALITY_INVALID 0xffffffffLU
-#endif
-
-unsigned long personality_from_string(const char *p);
-const char *personality_to_string(unsigned long);
-
-uint64_t physical_memory(void);
-
-void hexdump(FILE *f, const void *p, size_t s);
-
-union file_handle_union {
- struct file_handle handle;
- char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
-};
-#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
-
-int update_reboot_param_file(const char *param);
-
-int umount_recursive(const char *target, int flags);
-
-int bind_remount_recursive(const char *prefix, bool ro);
-
-int fflush_and_check(FILE *f);
-
-int tempfn_xxxxxx(const char *p, char **ret);
-int tempfn_random(const char *p, char **ret);
-int tempfn_random_child(const char *p, char **ret);
-
-int take_password_lock(const char *root);
-
-int is_symlink(const char *path);
-int is_dir(const char *path, bool follow);
-int is_device_node(const char *path);
-
-typedef enum UnquoteFlags {
- UNQUOTE_RELAX = 1,
- UNQUOTE_CUNESCAPE = 2,
-} UnquoteFlags;
-
-int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
-int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
-
-int free_and_strdup(char **p, const char *s);
-
-#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
-
-#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
- for ((e) = &buffer.ev; \
- (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
- (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
-
-union inotify_event_buffer {
- struct inotify_event ev;
- uint8_t raw[INOTIFY_EVENT_MAX];
-};
-
-#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
-
-int ptsname_malloc(int fd, char **ret);
-
-int openpt_in_namespace(pid_t pid, int flags);
-
-ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags);
-
-int fd_setcrtime(int fd, usec_t usec);
-int fd_getcrtime(int fd, usec_t *usec);
-int path_getcrtime(const char *p, usec_t *usec);
-int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags);
-
-int same_fd(int a, int b);
-
-int chattr_fd(int fd, unsigned value, unsigned mask);
-int chattr_path(const char *p, unsigned value, unsigned mask);
-
-int read_attr_fd(int fd, unsigned *ret);
-int read_attr_path(const char *p, unsigned *ret);
-
-#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
-
-ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
-
-void sigkill_wait(pid_t *pid);
-#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
-
-int syslog_parse_priority(const char **p, int *priority, bool with_facility);
-
-void cmsg_close_all(struct msghdr *mh);
-
-int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
-
-char *shell_maybe_quote(const char *s);
-
-int parse_mode(const char *s, mode_t *ret);
-
-int mount_move_root(const char *path);
-
-int reset_uid_gid(void);
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
deleted file mode 100644
index c7beccc..0000000
--- a/src/shared/verbs.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 "util.h"
-#include "verbs.h"
-
-int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
- const Verb *verb;
- const char *name;
- unsigned i;
- int left;
-
- assert(verbs);
- assert(verbs[0].dispatch);
- assert(argc >= 0);
- assert(argv);
- assert(argc >= optind);
-
- left = argc - optind;
- name = argv[optind];
-
- for (i = 0;; i++) {
- bool found;
-
- /* At the end of the list? */
- if (!verbs[i].dispatch) {
- if (name)
- log_error("Unknown operation %s.", name);
- else
- log_error("Requires operation parameter.");
- return -EINVAL;
- }
-
- if (name)
- found = streq(name, verbs[i].verb);
- else
- found = !!(verbs[i].flags & VERB_DEFAULT);
-
- if (found) {
- verb = &verbs[i];
- break;
- }
- }
-
- assert(verb);
-
- if (!name)
- left = 1;
-
- if (verb->min_args != VERB_ANY &&
- (unsigned) left < verb->min_args) {
- log_error("Too few arguments.");
- return -EINVAL;
- }
-
- if (verb->max_args != VERB_ANY &&
- (unsigned) left > verb->max_args) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- if (name)
- return verb->dispatch(left, argv + optind, userdata);
- else {
- char* fake[2] = {
- (char*) verb->verb,
- NULL
- };
-
- return verb->dispatch(1, fake, userdata);
- }
-}
diff --git a/src/shared/verbs.h b/src/shared/verbs.h
deleted file mode 100644
index d59e4d5..0000000
--- a/src/shared/verbs.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
-***/
-
-#define VERB_ANY ((unsigned) -1)
-#define VERB_DEFAULT 1
-
-typedef struct {
- const char *verb;
- unsigned min_args, max_args;
- unsigned flags;
- int (* const dispatch)(int argc, char *argv[], void *userdata);
-} Verb;
-
-int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
diff --git a/src/shared/virt.c b/src/shared/virt.c
deleted file mode 100644
index 1299a75..0000000
--- a/src/shared/virt.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2011 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 <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "process-util.h"
-#include "virt.h"
-#include "fileio.h"
-
-static int detect_vm_cpuid(const char **_id) {
-
- /* Both CPUID and DMI are x86 specific interfaces... */
-#if defined(__i386__) || defined(__x86_64__)
-
- static const char cpuid_vendor_table[] =
- "XenVMMXenVMM\0" "xen\0"
- "KVMKVMKVM\0" "kvm\0"
- /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
- "VMwareVMware\0" "vmware\0"
- /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
- "Microsoft Hv\0" "microsoft\0";
-
- uint32_t eax, ecx;
- union {
- uint32_t sig32[3];
- char text[13];
- } sig = {};
- const char *j, *k;
- bool hypervisor;
-
- /* http://lwn.net/Articles/301888/ */
-
-#if defined (__i386__)
-#define REG_a "eax"
-#define REG_b "ebx"
-#elif defined (__amd64__)
-#define REG_a "rax"
-#define REG_b "rbx"
-#endif
-
- /* First detect whether there is a hypervisor */
- eax = 1;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=c" (ecx)
- : "0" (eax)
- );
-
- hypervisor = !!(ecx & 0x80000000U);
-
- if (hypervisor) {
-
- /* There is a hypervisor, see what it is */
- eax = 0x40000000U;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " mov %%ebx, %1 \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
- : "0" (eax)
- );
-
- NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
- if (streq(sig.text, j)) {
- *_id = k;
- return 1;
- }
-
- *_id = "other";
- return 0;
- }
-#endif
-
- return 0;
-}
-
-static int detect_vm_devicetree(const char **_id) {
-#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
- _cleanup_free_ char *hvtype = NULL;
- int r;
-
- r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
- if (r >= 0) {
- if (streq(hvtype, "linux,kvm")) {
- *_id = "kvm";
- return 1;
- } else if (strstr(hvtype, "xen")) {
- *_id = "xen";
- return 1;
- }
- } else if (r == -ENOENT) {
- _cleanup_closedir_ DIR *dir = NULL;
- struct dirent *dent;
-
- dir = opendir("/proc/device-tree");
- if (!dir) {
- if (errno == ENOENT)
- return 0;
- return -errno;
- }
-
- FOREACH_DIRENT(dent, dir, return -errno) {
- if (strstr(dent->d_name, "fw-cfg")) {
- *_id = "qemu";
- return 1;
- }
- }
- }
-#endif
- return 0;
-}
-
-static int detect_vm_dmi(const char **_id) {
-
- /* Both CPUID and DMI are x86 specific interfaces... */
-#if defined(__i386__) || defined(__x86_64__)
-
- static const char *const dmi_vendors[] = {
- "/sys/class/dmi/id/sys_vendor",
- "/sys/class/dmi/id/board_vendor",
- "/sys/class/dmi/id/bios_vendor"
- };
-
- static const char dmi_vendor_table[] =
- "QEMU\0" "qemu\0"
- /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
- "VMware\0" "vmware\0"
- "VMW\0" "vmware\0"
- "innotek GmbH\0" "oracle\0"
- "Xen\0" "xen\0"
- "Bochs\0" "bochs\0";
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
- _cleanup_free_ char *s = NULL;
- const char *j, *k;
- int r;
-
- r = read_one_line_file(dmi_vendors[i], &s);
- if (r < 0) {
- if (r != -ENOENT)
- return r;
-
- continue;
- }
-
- NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
- if (startswith(s, j)) {
- *_id = k;
- return 1;
- }
- }
-#endif
-
- return 0;
-}
-
-/* Returns a short identifier for the various VM implementations */
-int detect_vm(const char **id) {
- _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
- static thread_local int cached_found = -1;
- static thread_local const char *cached_id = NULL;
- const char *_id = NULL;
- int r;
-
- if (_likely_(cached_found >= 0)) {
-
- if (id)
- *id = cached_id;
-
- return cached_found;
- }
-
- /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
- *
- * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
- r = read_one_line_file("/proc/xen/capabilities", &domcap);
- if (r >= 0) {
- char *cap, *i = domcap;
-
- while ((cap = strsep(&i, ",")))
- if (streq(cap, "control_d"))
- break;
-
- if (!cap) {
- _id = "xen";
- r = 1;
- }
-
- goto finish;
-
- } else if (r == -ENOENT) {
- _cleanup_free_ char *hvtype = NULL;
-
- r = read_one_line_file("/sys/hypervisor/type", &hvtype);
- if (r >= 0) {
- if (streq(hvtype, "xen")) {
- _id = "xen";
- r = 1;
- goto finish;
- }
- } else if (r != -ENOENT)
- return r;
- } else
- return r;
-
- /* this will set _id to "other" and return 0 for unknown hypervisors */
- r = detect_vm_cpuid(&_id);
- if (r != 0)
- goto finish;
-
- r = detect_vm_dmi(&_id);
- if (r != 0)
- goto finish;
-
- r = detect_vm_devicetree(&_id);
- if (r != 0)
- goto finish;
-
- if (_id) {
- /* "other" */
- r = 1;
- goto finish;
- }
-
- /* Detect User-Mode Linux by reading /proc/cpuinfo */
- r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
- if (r < 0)
- return r;
- if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
- _id = "uml";
- r = 1;
- goto finish;
- }
-
-#if defined(__s390__)
- {
- _cleanup_free_ char *t = NULL;
-
- r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
- if (r >= 0) {
- if (streq(t, "z/VM"))
- _id = "zvm";
- else
- _id = "kvm";
- r = 1;
-
- goto finish;
- }
- }
-#endif
-
- r = 0;
-
-finish:
- cached_found = r;
-
- cached_id = _id;
- if (id)
- *id = _id;
-
- return r;
-}
-
-int detect_container(const char **id) {
-
- static thread_local int cached_found = -1;
- static thread_local const char *cached_id = NULL;
-
- _cleanup_free_ char *m = NULL;
- const char *_id = NULL, *e = NULL;
- int r;
-
- if (_likely_(cached_found >= 0)) {
-
- if (id)
- *id = cached_id;
-
- return cached_found;
- }
-
- /* /proc/vz exists in container and outside of the container,
- * /proc/bc only outside of the container. */
- if (access("/proc/vz", F_OK) >= 0 &&
- access("/proc/bc", F_OK) < 0) {
- _id = "openvz";
- r = 1;
- goto finish;
- }
-
- if (getpid() == 1) {
- /* If we are PID 1 we can just check our own
- * environment variable */
-
- e = getenv("container");
- if (isempty(e)) {
- r = 0;
- goto finish;
- }
- } else {
-
- /* Otherwise, PID 1 dropped this information into a
- * file in /run. This is better than accessing
- * /proc/1/environ, since we don't need CAP_SYS_PTRACE
- * for that. */
-
- r = read_one_line_file("/run/systemd/container", &m);
- if (r == -ENOENT) {
-
- /* Fallback for cases where PID 1 was not
- * systemd (for example, cases where
- * init=/bin/sh is used. */
-
- r = getenv_for_pid(1, "container", &m);
- if (r <= 0) {
-
- /* If that didn't work, give up,
- * assume no container manager.
- *
- * Note: This means we still cannot
- * detect containers if init=/bin/sh
- * is passed but privileges dropped,
- * as /proc/1/environ is only readable
- * with privileges. */
-
- r = 0;
- goto finish;
- }
- }
- if (r < 0)
- return r;
-
- e = m;
- }
-
- /* We only recognize a selected few here, since we want to
- * enforce a redacted namespace */
- if (streq(e, "lxc"))
- _id ="lxc";
- else if (streq(e, "lxc-libvirt"))
- _id = "lxc-libvirt";
- else if (streq(e, "systemd-nspawn"))
- _id = "systemd-nspawn";
- else if (streq(e, "docker"))
- _id = "docker";
- else
- _id = "other";
-
- r = 1;
-
-finish:
- cached_found = r;
-
- cached_id = _id;
- if (id)
- *id = _id;
-
- return r;
-}
-
-/* Returns a short identifier for the various VM/container implementations */
-int detect_virtualization(const char **id) {
- int r;
-
- r = detect_container(id);
- if (r < 0)
- return r;
- if (r > 0)
- return VIRTUALIZATION_CONTAINER;
-
- r = detect_vm(id);
- if (r < 0)
- return r;
- if (r > 0)
- return VIRTUALIZATION_VM;
-
- return VIRTUALIZATION_NONE;
-}
diff --git a/src/shared/virt.h b/src/shared/virt.h
deleted file mode 100644
index 7194ab2..0000000
--- a/src/shared/virt.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2011 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/>.
-***/
-
-int detect_vm(const char **id);
-int detect_container(const char **id);
-
-enum {
- VIRTUALIZATION_NONE = 0,
- VIRTUALIZATION_VM,
- VIRTUALIZATION_CONTAINER,
- _VIRTUALIZATION_MAX,
- _VIRTUALIZATION_INVALID = -1
-};
-
-int detect_virtualization(const char **id);
diff --git a/src/shared/xml.c b/src/shared/xml.c
deleted file mode 100644
index 15c629b..0000000
--- a/src/shared/xml.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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 <string.h>
-
-#include "util.h"
-#include "xml.h"
-
-enum {
- STATE_NULL,
- STATE_TEXT,
- STATE_TAG,
- STATE_ATTRIBUTE,
-};
-
-static void inc_lines(unsigned *line, const char *s, size_t n) {
- const char *p = s;
-
- if (!line)
- return;
-
- for (;;) {
- const char *f;
-
- f = memchr(p, '\n', n);
- if (!f)
- return;
-
- n -= (f - p) + 1;
- p = f + 1;
- (*line)++;
- }
-}
-
-/* We don't actually do real XML here. We only read a simplistic
- * subset, that is a bit less strict that XML and lacks all the more
- * complex features, like entities, or namespaces. However, we do
- * support some HTML5-like simplifications */
-
-int xml_tokenize(const char **p, char **name, void **state, unsigned *line) {
- const char *c, *e, *b;
- char *ret;
- int t;
-
- assert(p);
- assert(*p);
- assert(name);
- assert(state);
-
- t = PTR_TO_INT(*state);
- c = *p;
-
- if (t == STATE_NULL) {
- if (line)
- *line = 1;
- t = STATE_TEXT;
- }
-
- for (;;) {
- if (*c == 0)
- return XML_END;
-
- switch (t) {
-
- case STATE_TEXT: {
- int x;
-
- e = strchrnul(c, '<');
- if (e > c) {
- /* More text... */
- ret = strndup(c, e - c);
- if (!ret)
- return -ENOMEM;
-
- inc_lines(line, c, e - c);
-
- *name = ret;
- *p = e;
- *state = INT_TO_PTR(STATE_TEXT);
-
- return XML_TEXT;
- }
-
- assert(*e == '<');
- b = c + 1;
-
- if (startswith(b, "!--")) {
- /* A comment */
- e = strstr(b + 3, "-->");
- if (!e)
- return -EINVAL;
-
- inc_lines(line, b, e + 3 - b);
-
- c = e + 3;
- continue;
- }
-
- if (*b == '?') {
- /* Processing instruction */
-
- e = strstr(b + 1, "?>");
- if (!e)
- return -EINVAL;
-
- inc_lines(line, b, e + 2 - b);
-
- c = e + 2;
- continue;
- }
-
- if (*b == '!') {
- /* DTD */
-
- e = strchr(b + 1, '>');
- if (!e)
- return -EINVAL;
-
- inc_lines(line, b, e + 1 - b);
-
- c = e + 1;
- continue;
- }
-
- if (*b == '/') {
- /* A closing tag */
- x = XML_TAG_CLOSE;
- b++;
- } else
- x = XML_TAG_OPEN;
-
- e = strpbrk(b, WHITESPACE "/>");
- if (!e)
- return -EINVAL;
-
- ret = strndup(b, e - b);
- if (!ret)
- return -ENOMEM;
-
- *name = ret;
- *p = e;
- *state = INT_TO_PTR(STATE_TAG);
-
- return x;
- }
-
- case STATE_TAG:
-
- b = c + strspn(c, WHITESPACE);
- if (*b == 0)
- return -EINVAL;
-
- inc_lines(line, c, b - c);
-
- e = b + strcspn(b, WHITESPACE "=/>");
- if (e > b) {
- /* An attribute */
-
- ret = strndup(b, e - b);
- if (!ret)
- return -ENOMEM;
-
- *name = ret;
- *p = e;
- *state = INT_TO_PTR(STATE_ATTRIBUTE);
-
- return XML_ATTRIBUTE_NAME;
- }
-
- if (startswith(b, "/>")) {
- /* An empty tag */
-
- *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */
- *p = b + 2;
- *state = INT_TO_PTR(STATE_TEXT);
-
- return XML_TAG_CLOSE_EMPTY;
- }
-
- if (*b != '>')
- return -EINVAL;
-
- c = b + 1;
- t = STATE_TEXT;
- continue;
-
- case STATE_ATTRIBUTE:
-
- if (*c == '=') {
- c++;
-
- if (*c == '\'' || *c == '\"') {
- /* Tag with a quoted value */
-
- e = strchr(c+1, *c);
- if (!e)
- return -EINVAL;
-
- inc_lines(line, c, e - c);
-
- ret = strndup(c+1, e - c - 1);
- if (!ret)
- return -ENOMEM;
-
- *name = ret;
- *p = e + 1;
- *state = INT_TO_PTR(STATE_TAG);
-
- return XML_ATTRIBUTE_VALUE;
-
- }
-
- /* Tag with a value without quotes */
-
- b = strpbrk(c, WHITESPACE ">");
- if (!b)
- b = c;
-
- ret = strndup(c, b - c);
- if (!ret)
- return -ENOMEM;
-
- *name = ret;
- *p = b;
- *state = INT_TO_PTR(STATE_TAG);
- return XML_ATTRIBUTE_VALUE;
- }
-
- t = STATE_TAG;
- continue;
- }
-
- }
-
- assert_not_reached("Bad state");
-}
diff --git a/src/shared/xml.h b/src/shared/xml.h
deleted file mode 100644
index b256b0b..0000000
--- a/src/shared/xml.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2013 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/>.
-***/
-
-enum {
- XML_END,
- XML_TEXT,
- XML_TAG_OPEN,
- XML_TAG_CLOSE,
- XML_TAG_CLOSE_EMPTY,
- XML_ATTRIBUTE_NAME,
- XML_ATTRIBUTE_VALUE,
-};
-
-int xml_tokenize(const char **p, char **name, void **state, unsigned *line);
commit 3de1c8ceabfc170a38798476ff378e2fbc06dbad
Author: Susant Sahani <susant at redhat.com>
Date: Thu Jun 11 14:07:15 2015 +0530
sd-network: fix CID 1306198
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index 207eda1..b63fdf8 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -338,10 +338,8 @@ _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category
good = true;
}
- if (!good) {
- close_nointr(fd);
+ if (!good)
return -EINVAL;
- }
*m = FD_TO_MONITOR(fd);
fd = -1;
commit f6a0ea8568c6d2676239da7f8c67ae6b496fffa4
Author: Susant Sahani <susant at redhat.com>
Date: Thu Jun 11 13:11:33 2015 +0530
networkd: improve logging
Replace strerror() usage with log_netdev_error_errno()
diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/networkd-netdev-ipvlan.c
index 5189000..f1c8e0c 100644
--- a/src/network/networkd-netdev-ipvlan.c
+++ b/src/network/networkd-netdev-ipvlan.c
@@ -42,13 +42,9 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl
assert(netdev->ifname);
if (m->mode != _NETDEV_IPVLAN_MODE_INVALID) {
- r = sd_rtnl_message_append_u16(req, IFLA_IPVLAN_MODE, m->mode);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_IPVLAN_MODE attribute: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_message_append_u16(req, IFLA_IPVLAN_MODE, m->mode);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_MODE attribute: %m");
}
return 0;
commit c8a09ef46302b4bafe36623857a60eafa6b76e2e
Author: Susant Sahani <susant at redhat.com>
Date: Thu Jun 11 13:08:19 2015 +0530
networkd: macvlan improve logging
Replace strerror() usage with log_netdev_error_errno()
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c
index 5f41f1c..a8bf6e6 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/networkd-netdev-macvlan.c
@@ -44,13 +44,9 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_rtn
assert(netdev->ifname);
if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) {
- r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode);
- if (r < 0) {
- log_netdev_error(netdev,
- "Could not append IFLA_MACVLAN_MODE attribute: %s",
- strerror(-r));
- return r;
- }
+ r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MODE attribute: %m");
}
return 0;
commit f73fb7b742f294b6d2126afa16001bd2ff6ab461
Author: Filipe Brandenburger <filbranden at google.com>
Date: Wed Jun 10 22:40:31 2015 -0700
build-sys: suppress warnings of unused results
Unfortunately, gcc keeps warning about those even when we use an
explicit (void) cast to indicate we are not interested in the result.
LLVM's clang does not have that issue and works fine with the casts.
GCC bug being tracked at:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
Until that GCC bug is fixed (and the version with the fix is in
many/most distributions) or we switch to LLVM as the default compiler,
it looks like we'll have to disable this warning by default...
Tested by building files known to present warnings about unused results
without the suppression, confirmed that the warnings were no longer
present with this patch applied.
This partially reverts commit 00c11bc53a1d37 ("build-sys: don't suppress irrelevant warnings").
diff --git a/configure.ac b/configure.ac
index 878b23b..344bfe1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -173,6 +173,7 @@ CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
-Wwrite-strings \
-Wno-unused-parameter \
-Wno-missing-field-initializers \
+ -Wno-unused-result \
-Wno-format-signedness \
-Werror=overflow \
-Wdate-time \
commit ec5ff4445cca6a1d786b8da36cf6fe0acc0b94c8
Author: Filipe Brandenburger <filbranden at google.com>
Date: Wed Jun 10 22:33:44 2015 -0700
journald: do not strip leading whitespace from messages
Keep leading whitespace for compatibility with older syslog
implementations. Also useful when piping formatted output to the
`logger` command. Keep removing trailing whitespace.
Tested with `pstree | logger` and checking that the output of
`journalctl | tail` included aligned and formatted output.
Confirmed that all test cases still pass as expected.
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 90b7530..ffba451 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -234,7 +234,8 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
if (t)
*identifier = t;
- e += strspn(p + e, WHITESPACE);
+ if (strchr(WHITESPACE, p[e]))
+ e++;
*buf = p + e;
return e;
}
commit 37c47e5e5b8e47dd886ed6e8355b1203082b4a3e
Merge: 9649050 1d84ae0
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 01:52:25 2015 +0200
Merge pull request #3 from threatgrid/more_cgtop_enhancements
cgtop enhancements for easier machine-readable output
commit 1d84ae050cb1eb6bfbcd17a10de4e5804fdd4722
Author: Charles Duffy <chaduffy at cisco.com>
Date: Tue Jun 2 17:38:49 2015 -0500
cgtop: IO readings are valid if any data is available, even if unchanged since last tick
Emit "0" rather than "-" if no change in IO values are seen for a process since
last tick, so long as accounting has registered content at all.
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 3a2842d..d630e35 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -281,11 +281,10 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap
yr = rd - g->io_input;
yw = wr - g->io_output;
- if (yr > 0 || yw > 0) {
+ if (g->io_input > 0 || g->io_output > 0) {
g->io_input_bps = (yr * 1000000000ULL) / x;
g->io_output_bps = (yw * 1000000000ULL) / x;
g->io_valid = true;
-
}
}
commit 96490500cffcd5771ee29c939151d87b0673507e
Merge: 6f45f8c 185a087
Author: Lennart Poettering <lennart at poettering.net>
Date: Thu Jun 11 00:50:04 2015 +0200
Merge pull request #6 from xnox/drop-name
shared: Drop 'name=' prefix from SYSTEMD_CGROUP_CONTROLLER define.
commit fb2d301085261f477e1f0a4e3c8e823bc8da7c33
Author: Alex Crawford <alex.crawford at coreos.com>
Date: Wed Jun 10 13:12:02 2015 -0700
rules: re-add cciss rules
The original commit (1aff206) doesn't explain why these were removed.
This adds them back since they are in fact needed.
diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules
index 2daeb6d..71b8e46 100644
--- a/rules/60-persistent-storage.rules
+++ b/rules/60-persistent-storage.rules
@@ -32,8 +32,9 @@ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin
# SCSI devices
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi"
-KERNEL=="sd*|sr*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
-KERNEL=="sd*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
+KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss"
+KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
# FireWire
KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}"
commit 6f45f8ca76651a9658494eef1d99bae98819a9a0
Merge: d112701 747ca69
Author: Lennart Poettering <lennart at poettering.net>
Date: Wed Jun 10 23:02:23 2015 +0200
Merge pull request #152 from zonque/buildsys
build-sys: make man/systemd.directives.xml depend on man/custom-entit…
commit d11270119023cf1e1807293b3b46a8b9ad1c0869
Merge: ad09153 870395a
Author: Lennart Poettering <lennart at poettering.net>
Date: Wed Jun 10 20:43:38 2015 +0200
Merge pull request #148 from teg/sd-network-race
sd-network: allow the state dir to be created after the monitor
commit ad091530b30bfbbc94159c17f3ea434a835ad3c9
Merge: 4061eed 37de250
Author: Lennart Poettering <lennart at poettering.net>
Date: Wed Jun 10 20:43:06 2015 +0200
Merge pull request #151 from mischief/dns-shared-3
move dns code from resolve to shared v3
commit 747ca69b5d984b130c4b983281f9b51f3b710340
Author: Daniel Mack <daniel at zonque.org>
Date: Wed Jun 10 20:42:56 2015 +0200
build-sys: make man/systemd.directives.xml depend on man/custom-entities.ent
Currently, the following command sequence fails:
make distclean
./autogen.sh c
make distcheck
That's because the command invoked to build man/systemd.directives.xml needs
man/custom-entities.ent to function, which itself isn't a dependency.
The $(filter-out $<,$^) logic used to filter out everything from the
prerequisites except for the first word, which doesn't work anymore
now. Use $(SOURCE_XML_FILES) instead.
diff --git a/Makefile.am b/Makefile.am
index e98e8b8..9b42f23 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -714,9 +714,9 @@ man/systemd.index.xml: $(top_srcdir)/tools/make-man-index.py $(NON_INDEX_XML_FIL
$(AM_V_at)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)$(PYTHON) $< $@ $(filter-out $<,$^)
-man/systemd.directives.xml: $(top_srcdir)/tools/make-directive-index.py $(SOURCE_XML_FILES)
+man/systemd.directives.xml: $(top_srcdir)/tools/make-directive-index.py man/custom-entities.ent $(SOURCE_XML_FILES)
$(AM_V_at)$(MKDIR_P) $(dir $@)
- $(AM_V_GEN)$(PYTHON) $< $@ $(filter-out $<,$^)
+ $(AM_V_GEN)$(PYTHON) $< $@ $(SOURCE_XML_FILES)
CLEANFILES += \
man/systemd.index.xml \
commit 37de250906222211d2bf1755c41191a06d2126dd
Author: Nick Owens <nick.owens at coreos.com>
Date: Tue Jun 2 16:30:42 2015 -0700
libsystemd-network: use domain validation instead of hostname validation for dhcp domain option
previously hostname_is_valid was used to validate domain names, which
would silently drop perfectly valid dns names that were longer than a
single dns label.
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 8a42206..d8bc76e 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -32,6 +32,7 @@
#include "dhcp-lease-internal.h"
#include "sd-dhcp-lease.h"
#include "network-internal.h"
+#include "dns-domain.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
@@ -504,9 +505,18 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
if (e)
*e = 0;
- if (!hostname_is_valid(domainname) || is_localhost(domainname))
+ if (is_localhost(domainname))
break;
+ r = dns_name_is_valid(domainname);
+ if (r <= 0) {
+ if (r < 0)
+ log_error_errno(r, "Failed to validate domain name: %s: %m", domainname);
+ if (r == 0)
+ log_warning("Domain name is not valid, ignoring: %s", domainname);
+ break;
+ }
+
free(lease->domainname);
lease->domainname = domainname;
domainname = NULL;
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 5947084..6f32e5f 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -30,6 +30,7 @@
#include "networkd-netdev.h"
#include "networkd-link.h"
#include "network-internal.h"
+#include "dns-domain.h"
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
@@ -466,11 +467,16 @@ int config_parse_domains(const char *unit,
STRV_FOREACH(domain, *domains) {
if (is_localhost(*domain))
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
- else if (!hostname_is_valid(*domain)) {
- if (!streq(*domain, "*"))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
- } else
- continue;
+ else {
+ r = dns_name_is_valid(*domain);
+ if (r <= 0 && !streq(*domain, "*")) {
+ if (r < 0)
+ log_error_errno(r, "Failed to validate domain name: %s: %m", *domain);
+ if (r == 0)
+ log_warning("Domain name is not valid, ignoring assignment: %s", *domain);
+ } else
+ continue;
+ }
strv_remove(*domains, *domain);
commit df6cfeeff7b66856dd20956efe97d87faf8ce1c3
Author: Nick Owens <nick.owens at coreos.com>
Date: Tue Jun 2 16:29:39 2015 -0700
shared: add convenience function for validating dns names
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 516d244..00caf5d 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -35,6 +35,15 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
int dns_name_normalize(const char *s, char **_ret);
+static inline int dns_name_is_valid(const char *s) {
+ int r;
+ r = dns_name_normalize(s, NULL);
+ if (r == -EINVAL)
+ return 0;
+ if (r < 0)
+ return r;
+ return 1;
+}
unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
int dns_name_compare_func(const void *a, const void *b);
commit 4ad7f2761da661853dcc29d542efb4727abb1101
Author: Nick Owens <nick.owens at coreos.com>
Date: Tue Jun 2 11:49:43 2015 -0700
resolve: move dns routines into shared
diff --git a/Makefile.am b/Makefile.am
index 5d30d7d..55bab72 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -762,6 +762,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/udev-util.h \
src/shared/device-nodes.c \
src/shared/device-nodes.h \
+ src/shared/dns-domain.c \
+ src/shared/dns-domain.h \
src/shared/util.c \
src/shared/util.h \
src/shared/virt.c \
@@ -978,6 +980,7 @@ libsystemd_shared_la_CFLAGS = \
libsystemd_shared_la_LIBADD = \
$(SELINUX_LIBS) \
$(CAP_LIBS) \
+ $(LIBIDN_LIBS) \
-lm
# -----------------------------------------------------------------------------
@@ -1446,7 +1449,8 @@ tests += \
test-copy \
test-cap-list \
test-sigbus \
- test-verbs
+ test-verbs \
+ test-dns-domain
EXTRA_DIST += \
test/a.service \
@@ -1577,6 +1581,15 @@ test_hostname_SOURCES = \
test_hostname_LDADD = \
libsystemd-core.la
+test_dns_domain_SOURCES = \
+ src/test/test-dns-domain.c
+
+test_dns_domain_LDADD = \
+ libsystemd-network.la \
+ libsystemd-internal.la \
+ libsystemd-shared.la \
+ $(LIBIDN_LIBS)
+
if ENABLE_EFI
manual_tests += \
test-boot-timestamp
@@ -3382,7 +3395,8 @@ test_dhcp_client_SOURCES = \
test_dhcp_client_LDADD = \
libsystemd-network.la \
libsystemd-internal.la \
- libsystemd-shared.la
+ libsystemd-shared.la \
+ $(LIBIDN_LIBS)
test_dhcp_server_SOURCES = \
src/libsystemd-network/test-dhcp-server.c
@@ -5405,8 +5419,6 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-link.h \
src/resolve/resolved-link.c \
src/resolve/resolved-def.h \
- src/resolve/resolved-dns-domain.h \
- src/resolve/resolved-dns-domain.c \
src/resolve/resolved-dns-rr.h \
src/resolve/resolved-dns-rr.c \
src/resolve/resolved-dns-question.h \
@@ -5476,20 +5488,6 @@ GENERAL_ALIASES += \
nodist_pkgsysconf_DATA += \
src/resolve/resolved.conf
-tests += \
- test-dns-domain
-
-test_dns_domain_SOURCES = \
- src/resolve/resolved-dns-domain.h \
- src/resolve/resolved-dns-domain.c \
- src/resolve/test-dns-domain.c
-
-test_dns_domain_LDADD = \
- libsystemd-network.la \
- libsystemd-internal.la \
- libsystemd-shared.la \
- $(LIBIDN_LIBS)
-
libnss_resolve_la_SOURCES = \
src/nss-resolve/nss-resolve.sym \
src/nss-resolve/nss-resolve.c
@@ -5520,8 +5518,6 @@ systemd_resolve_host_SOURCES = \
src/resolve/resolved-dns-answer.h \
src/resolve/resolved-dns-question.c \
src/resolve/resolved-dns-question.h \
- src/resolve/resolved-dns-domain.c \
- src/resolve/resolved-dns-domain.h \
src/resolve/dns-type.c \
src/resolve/dns-type.h
@@ -5551,7 +5547,8 @@ systemd_networkd_SOURCES = \
src/network/networkd.c
systemd_networkd_LDADD = \
- libsystemd-networkd-core.la
+ libsystemd-networkd-core.la \
+ $(LIBIDN_LIBS)
if HAVE_LIBIPTC
systemd_networkd_LDADD += \
@@ -5650,7 +5647,8 @@ test_network_SOURCES = \
src/network/test-network.c
test_network_LDADD = \
- libsystemd-networkd-core.la
+ libsystemd-networkd-core.la \
+ $(LIBIDN_LIBS)
if HAVE_LIBIPTC
test_network_LDADD += \
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 171141e..6db1251 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -22,7 +22,7 @@
#include "bus-common-errors.h"
#include "bus-util.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
#include "resolved-bus.h"
#include "resolved-def.h"
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index e08eb66..f77b98e 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -20,7 +20,7 @@
***/
#include "resolved-dns-answer.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
DnsAnswer *dns_answer_new(unsigned n) {
DnsAnswer *a;
diff --git a/src/resolve/resolved-dns-domain.c b/src/resolve/resolved-dns-domain.c
deleted file mode 100644
index e1eb3dd..0000000
--- a/src/resolve/resolved-dns-domain.c
+++ /dev/null
@@ -1,613 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
- ***/
-
-#ifdef HAVE_LIBIDN
-#include <idna.h>
-#include <stringprep.h>
-#endif
-
-#include "resolved-dns-domain.h"
-
-int dns_label_unescape(const char **name, char *dest, size_t sz) {
- const char *n;
- char *d;
- int r = 0;
-
- assert(name);
- assert(*name);
- assert(dest);
-
- n = *name;
- d = dest;
-
- for (;;) {
- if (*n == '.') {
- n++;
- break;
- }
-
- if (*n == 0)
- break;
-
- if (sz <= 0)
- return -ENOSPC;
-
- if (r >= DNS_LABEL_MAX)
- return -EINVAL;
-
- if (*n == '\\') {
- /* Escaped character */
-
- n++;
-
- if (*n == 0)
- /* Ending NUL */
- return -EINVAL;
-
- else if (*n == '\\' || *n == '.') {
- /* Escaped backslash or dot */
- *(d++) = *(n++);
- sz--;
- r++;
-
- } else if (n[0] >= '0' && n[0] <= '9') {
- unsigned k;
-
- /* Escaped literal ASCII character */
-
- if (!(n[1] >= '0' && n[1] <= '9') ||
- !(n[2] >= '0' && n[2] <= '9'))
- return -EINVAL;
-
- k = ((unsigned) (n[0] - '0') * 100) +
- ((unsigned) (n[1] - '0') * 10) +
- ((unsigned) (n[2] - '0'));
-
- /* Don't allow CC characters or anything that doesn't fit in 8bit */
- if (k < ' ' || k > 255 || k == 127)
- return -EINVAL;
-
- *(d++) = (char) k;
- sz--;
- r++;
-
- n += 3;
- } else
- return -EINVAL;
-
- } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
-
- /* Normal character */
- *(d++) = *(n++);
- sz--;
- r++;
- } else
- return -EINVAL;
- }
-
- /* Empty label that is not at the end? */
- if (r == 0 && *n)
- return -EINVAL;
-
- if (sz >= 1)
- *d = 0;
-
- *name = n;
- return r;
-}
-
-int dns_label_escape(const char *p, size_t l, char **ret) {
- _cleanup_free_ char *s = NULL;
- char *q;
- int r;
-
- assert(p);
- assert(ret);
-
- if (l > DNS_LABEL_MAX)
- return -EINVAL;
-
- s = malloc(l * 4 + 1);
- if (!s)
- return -ENOMEM;
-
- q = s;
- while (l > 0) {
-
- if (*p == '.' || *p == '\\') {
-
- /* Dot or backslash */
- *(q++) = '\\';
- *(q++) = *p;
-
- } else if (*p == '_' ||
- *p == '-' ||
- (*p >= '0' && *p <= '9') ||
- (*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z')) {
-
- /* Proper character */
- *(q++) = *p;
- } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
-
- /* Everything else */
- *(q++) = '\\';
- *(q++) = '0' + (char) ((uint8_t) *p / 100);
- *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
- *(q++) = '0' + (char) ((uint8_t) *p % 10);
-
- } else
- return -EINVAL;
-
- p++;
- l--;
- }
-
- *q = 0;
- *ret = s;
- r = q - s;
- s = NULL;
-
- return r;
-}
-
-int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
-#ifdef HAVE_LIBIDN
- _cleanup_free_ uint32_t *input = NULL;
- size_t input_size;
- const char *p;
- bool contains_8bit = false;
-
- assert(encoded);
- assert(decoded);
- assert(decoded_max >= DNS_LABEL_MAX);
-
- if (encoded_size <= 0)
- return 0;
-
- for (p = encoded; p < encoded + encoded_size; p++)
- if ((uint8_t) *p > 127)
- contains_8bit = true;
-
- if (!contains_8bit)
- return 0;
-
- input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
- if (!input)
- return -ENOMEM;
-
- if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0)
- return -EINVAL;
-
- return strlen(decoded);
-#else
- return 0;
-#endif
-}
-
-int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
-#ifdef HAVE_LIBIDN
- size_t input_size, output_size;
- _cleanup_free_ uint32_t *input = NULL;
- _cleanup_free_ char *result = NULL;
- uint32_t *output = NULL;
- size_t w;
-
- /* To be invoked after unescaping */
-
- assert(encoded);
- assert(decoded);
-
- if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
- return 0;
-
- if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
- return 0;
-
- input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
- if (!input)
- return -ENOMEM;
-
- output_size = input_size;
- output = newa(uint32_t, output_size);
-
- idna_to_unicode_44i(input, input_size, output, &output_size, 0);
-
- result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
- if (!result)
- return -ENOMEM;
- if (w <= 0)
- return 0;
- if (w+1 > decoded_max)
- return -EINVAL;
-
- memcpy(decoded, result, w+1);
- return w;
-#else
- return 0;
-#endif
-}
-
-int dns_name_normalize(const char *s, char **_ret) {
- _cleanup_free_ char *ret = NULL;
- size_t n = 0, allocated = 0;
- const char *p = s;
- bool first = true;
- int r;
-
- assert(s);
-
- for (;;) {
- _cleanup_free_ char *t = NULL;
- char label[DNS_LABEL_MAX];
- int k;
-
- r = dns_label_unescape(&p, label, sizeof(label));
- if (r < 0)
- return r;
- if (r == 0) {
- if (*p != 0)
- return -EINVAL;
- break;
- }
-
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
-
- r = dns_label_escape(label, r, &t);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
- return -ENOMEM;
-
- if (!first)
- ret[n++] = '.';
- else
- first = false;
-
- memcpy(ret + n, t, r);
- n += r;
- }
-
- if (n > DNS_NAME_MAX)
- return -EINVAL;
-
- if (!GREEDY_REALLOC(ret, allocated, n + 1))
- return -ENOMEM;
-
- ret[n] = 0;
-
- if (_ret) {
- *_ret = ret;
- ret = NULL;
- }
-
- return 0;
-}
-
-unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
- const char *p = s;
- unsigned long ul = hash_key[0];
- int r;
-
- assert(p);
-
- while (*p) {
- char label[DNS_LABEL_MAX+1];
- int k;
-
- r = dns_label_unescape(&p, label, sizeof(label));
- if (r < 0)
- break;
-
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
- break;
- if (k > 0)
- r = k;
-
- label[r] = 0;
- ascii_strlower(label);
-
- ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
- }
-
- return ul;
-}
-
-int dns_name_compare_func(const void *a, const void *b) {
- const char *x = a, *y = b;
- int r, q, k, w;
-
- assert(a);
- assert(b);
-
- for (;;) {
- char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
-
- if (*x == 0 && *y == 0)
- return 0;
-
- r = dns_label_unescape(&x, la, sizeof(la));
- q = dns_label_unescape(&y, lb, sizeof(lb));
- if (r < 0 || q < 0)
- return r - q;
-
- k = dns_label_undo_idna(la, r, la, sizeof(la));
- w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
- if (k < 0 || w < 0)
- return k - w;
- if (k > 0)
- r = k;
- if (w > 0)
- r = w;
-
- la[r] = lb[q] = 0;
- r = strcasecmp(la, lb);
- if (r != 0)
- return r;
- }
-}
-
-const struct hash_ops dns_name_hash_ops = {
- .hash = dns_name_hash_func,
- .compare = dns_name_compare_func
-};
-
-int dns_name_equal(const char *x, const char *y) {
- int r, q, k, w;
-
- assert(x);
- assert(y);
-
- for (;;) {
- char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
-
- if (*x == 0 && *y == 0)
- return true;
-
- r = dns_label_unescape(&x, la, sizeof(la));
- if (r < 0)
- return r;
-
- k = dns_label_undo_idna(la, r, la, sizeof(la));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
-
- q = dns_label_unescape(&y, lb, sizeof(lb));
- if (q < 0)
- return q;
- w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
-
- la[r] = lb[q] = 0;
- if (strcasecmp(la, lb))
- return false;
- }
-}
-
-int dns_name_endswith(const char *name, const char *suffix) {
- const char *n, *s, *saved_n = NULL;
- int r, q, k, w;
-
- assert(name);
- assert(suffix);
-
- n = name;
- s = suffix;
-
- for (;;) {
- char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
-
- r = dns_label_unescape(&n, ln, sizeof(ln));
- if (r < 0)
- return r;
- k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
-
- if (!saved_n)
- saved_n = n;
-
- q = dns_label_unescape(&s, ls, sizeof(ls));
- if (q < 0)
- return q;
- w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
-
- if (r == 0 && q == 0)
- return true;
- if (r == 0 && saved_n == n)
- return false;
-
- ln[r] = ls[q] = 0;
-
- if (r != q || strcasecmp(ln, ls)) {
-
- /* Not the same, let's jump back, and try with the next label again */
- s = suffix;
- n = saved_n;
- saved_n = NULL;
- }
- }
-}
-
-int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
- const uint8_t *p;
- int r;
-
- assert(a);
- assert(ret);
-
- p = (const uint8_t*) a;
-
- if (family == AF_INET)
- r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
- else if (family == AF_INET6)
- r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa",
- hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
- hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
- hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
- hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
- hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
- hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
- hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
- hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
- else
- return -EAFNOSUPPORT;
- if (r < 0)
- return -ENOMEM;
-
- return 0;
-}
-
-int dns_name_address(const char *p, int *family, union in_addr_union *address) {
- int r;
-
- assert(p);
- assert(family);
- assert(address);
-
- r = dns_name_endswith(p, "in-addr.arpa");
- if (r < 0)
- return r;
- if (r > 0) {
- uint8_t a[4];
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(a); i++) {
- char label[DNS_LABEL_MAX+1];
-
- r = dns_label_unescape(&p, label, sizeof(label));
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
- if (r > 3)
- return -EINVAL;
-
- r = safe_atou8(label, &a[i]);
- if (r < 0)
- return r;
- }
-
- r = dns_name_equal(p, "in-addr.arpa");
- if (r <= 0)
- return r;
-
- *family = AF_INET;
- address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
- ((uint32_t) a[2] << 16) |
- ((uint32_t) a[1] << 8) |
- (uint32_t) a[0]);
-
- return 1;
- }
-
- r = dns_name_endswith(p, "ip6.arpa");
- if (r < 0)
- return r;
- if (r > 0) {
- struct in6_addr a;
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
- char label[DNS_LABEL_MAX+1];
- int x, y;
-
- r = dns_label_unescape(&p, label, sizeof(label));
- if (r <= 0)
- return r;
- if (r != 1)
- return -EINVAL;
- x = unhexchar(label[0]);
- if (x < 0)
- return -EINVAL;
-
- r = dns_label_unescape(&p, label, sizeof(label));
- if (r <= 0)
- return r;
- if (r != 1)
- return -EINVAL;
- y = unhexchar(label[0]);
- if (y < 0)
- return -EINVAL;
-
- a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
- }
-
- r = dns_name_equal(p, "ip6.arpa");
- if (r <= 0)
- return r;
-
- *family = AF_INET6;
- address->in6 = a;
- return 1;
- }
-
- return 0;
-}
-
-int dns_name_root(const char *name) {
- char label[DNS_LABEL_MAX+1];
- int r;
-
- assert(name);
-
- r = dns_label_unescape(&name, label, sizeof(label));
- if (r < 0)
- return r;
-
- return r == 0 && *name == 0;
-}
-
-int dns_name_single_label(const char *name) {
- char label[DNS_LABEL_MAX+1];
- int r;
-
- assert(name);
-
- r = dns_label_unescape(&name, label, sizeof(label));
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
-
- r = dns_label_unescape(&name, label, sizeof(label));
- if (r < 0)
- return r;
-
- return r == 0 && *name == 0;
-}
diff --git a/src/resolve/resolved-dns-domain.h b/src/resolve/resolved-dns-domain.h
deleted file mode 100644
index 516d244..0000000
--- a/src/resolve/resolved-dns-domain.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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/>.
- ***/
-
-#pragma once
-
-
-#include "hashmap.h"
-#include "in-addr-util.h"
-
-#define DNS_LABEL_MAX 63
-#define DNS_NAME_MAX 255
-
-int dns_label_unescape(const char **name, char *dest, size_t sz);
-int dns_label_escape(const char *p, size_t l, char **ret);
-
-int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
-int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
-
-int dns_name_normalize(const char *s, char **_ret);
-
-unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
-int dns_name_compare_func(const void *a, const void *b);
-extern const struct hash_ops dns_name_hash_ops;
-
-int dns_name_equal(const char *x, const char *y);
-int dns_name_endswith(const char *name, const char *suffix);
-
-int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
-int dns_name_address(const char *p, int *family, union in_addr_union *a);
-
-int dns_name_root(const char *name);
-int dns_name_single_label(const char *name);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 21756f5..bb74b18 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -23,7 +23,7 @@
#include "util.h"
#include "strv.h"
#include "unaligned.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
#include "resolved-dns-packet.h"
int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 45bcbbf..4d71f5e 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -20,7 +20,7 @@
***/
#include "resolved-dns-question.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
DnsQuestion *dns_question_new(unsigned n) {
DnsQuestion *q;
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index 78d9e4a..c1818ee 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -23,7 +23,7 @@
#include "strv.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
#include "resolved-dns-rr.h"
#include "resolved-dns-packet.h"
#include "dns-type.h"
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 7369cbf..c25ac22 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -27,7 +27,7 @@
#include "af-list.h"
#include "random-util.h"
#include "hostname-util.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
#include "resolved-dns-scope.h"
#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index a4c9b7d..32d771a 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -22,7 +22,7 @@
#include "list.h"
#include "resolved-dns-zone.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
#include "resolved-dns-packet.h"
/* Never allow more than 1K entries */
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 7fc2803..f8d4db7 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -34,7 +34,7 @@
#include "random-util.h"
#include "hostname-util.h"
-#include "resolved-dns-domain.h"
+#include "dns-domain.h"
#include "resolved-conf.h"
#include "resolved-bus.h"
#include "resolved-manager.h"
diff --git a/src/resolve/test-dns-domain.c b/src/resolve/test-dns-domain.c
deleted file mode 100644
index c3208ab..0000000
--- a/src/resolve/test-dns-domain.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 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 "macro.h"
-#include "resolved-dns-domain.h"
-
-static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
- char buffer[buffer_sz];
- int r;
-
- r = dns_label_unescape(&what, buffer, buffer_sz);
- assert_se(r == ret);
-
- if (r < 0)
- return;
-
- assert_se(streq(buffer, expect));
-}
-
-static void test_dns_label_unescape(void) {
- test_dns_label_unescape_one("hallo", "hallo", 6, 5);
- test_dns_label_unescape_one("hallo", "hallo", 4, -ENOSPC);
- test_dns_label_unescape_one("", "", 10, 0);
- test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12);
- test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5);
- test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL);
- test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL);
- test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7);
- test_dns_label_unescape_one(".", "", 20, 0);
- test_dns_label_unescape_one("..", "", 20, -EINVAL);
- test_dns_label_unescape_one(".foobar", "", 20, -EINVAL);
- test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
-}
-
-static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- r = dns_label_escape(what, l, &t);
- assert_se(r == ret);
-
- if (r < 0)
- return;
-
- assert_se(streq_ptr(expect, t));
-}
-
-static void test_dns_label_escape(void) {
- test_dns_label_escape_one("", 0, "", 0);
- test_dns_label_escape_one("hallo", 5, "hallo", 5);
- test_dns_label_escape_one("hallo", 6, NULL, -EINVAL);
- test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31);
-}
-
-static void test_dns_name_normalize_one(const char *what, const char *expect, int ret) {
- _cleanup_free_ char *t = NULL;
- int r;
-
- r = dns_name_normalize(what, &t);
- assert_se(r == ret);
-
- if (r < 0)
- return;
-
- assert_se(streq_ptr(expect, t));
-}
-
-static void test_dns_name_normalize(void) {
- test_dns_name_normalize_one("", "", 0);
- test_dns_name_normalize_one("f", "f", 0);
- test_dns_name_normalize_one("f.waldi", "f.waldi", 0);
- test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0);
- test_dns_name_normalize_one("\\000", NULL, -EINVAL);
- test_dns_name_normalize_one("..", NULL, -EINVAL);
- test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
- test_dns_name_normalize_one("foobar.", "foobar", 0);
- test_dns_name_normalize_one(".", "", 0);
-}
-
-static void test_dns_name_equal_one(const char *a, const char *b, int ret) {
- int r;
-
- r = dns_name_equal(a, b);
- assert_se(r == ret);
-
- r = dns_name_equal(b, a);
- assert_se(r == ret);
-}
-
-static void test_dns_name_equal(void) {
- test_dns_name_equal_one("", "", true);
- test_dns_name_equal_one("x", "x", true);
- test_dns_name_equal_one("x", "x.", true);
- test_dns_name_equal_one("abc.def", "abc.def", true);
- test_dns_name_equal_one("abc.def", "ABC.def", true);
- test_dns_name_equal_one("abc.def", "CBA.def", false);
- test_dns_name_equal_one("", "xxx", false);
- test_dns_name_equal_one("ab", "a", false);
- test_dns_name_equal_one("\\000", "xxxx", -EINVAL);
- test_dns_name_equal_one(".", "", true);
- test_dns_name_equal_one(".", ".", true);
- test_dns_name_equal_one("..", "..", -EINVAL);
-}
-
-static void test_dns_name_endswith_one(const char *a, const char *b, int ret) {
- assert_se(dns_name_endswith(a, b) == ret);
-}
-
-static void test_dns_name_endswith(void) {
- test_dns_name_endswith_one("", "", true);
- test_dns_name_endswith_one("", "xxx", false);
- test_dns_name_endswith_one("xxx", "", true);
- test_dns_name_endswith_one("x", "x", true);
- test_dns_name_endswith_one("x", "y", false);
- test_dns_name_endswith_one("x.y", "y", true);
- test_dns_name_endswith_one("x.y", "Y", true);
- test_dns_name_endswith_one("x.y", "x", false);
- test_dns_name_endswith_one("x.y.z", "Z", true);
- test_dns_name_endswith_one("x.y.z", "y.Z", true);
- test_dns_name_endswith_one("x.y.z", "x.y.Z", true);
- test_dns_name_endswith_one("x.y.z", "waldo", false);
- test_dns_name_endswith_one("x.y.z.u.v.w", "y.z", false);
- test_dns_name_endswith_one("x.y.z.u.v.w", "u.v.w", true);
- test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
-}
-
-static void test_dns_name_root(void) {
- assert_se(dns_name_root("") == true);
- assert_se(dns_name_root(".") == true);
- assert_se(dns_name_root("xxx") == false);
- assert_se(dns_name_root("xxx.") == false);
- assert_se(dns_name_root("..") == -EINVAL);
-}
-
-static void test_dns_name_single_label(void) {
- assert_se(dns_name_single_label("") == false);
- assert_se(dns_name_single_label(".") == false);
- assert_se(dns_name_single_label("..") == -EINVAL);
- assert_se(dns_name_single_label("x") == true);
- assert_se(dns_name_single_label("x.") == true);
- assert_se(dns_name_single_label("xx.yy") == false);
-}
-
-static void test_dns_name_reverse_one(const char *address, const char *name) {
- _cleanup_free_ char *p = NULL;
- union in_addr_union a, b = {};
- int familya, familyb;
-
- assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0);
- assert_se(dns_name_reverse(familya, &a, &p) >= 0);
- assert_se(streq(p, name));
- assert_se(dns_name_address(p, &familyb, &b) > 0);
- assert_se(familya == familyb);
- assert_se(in_addr_equal(familya, &a, &b));
-}
-
-static void test_dns_name_reverse(void) {
- test_dns_name_reverse_one("47.11.8.15", "15.8.11.47.in-addr.arpa");
- test_dns_name_reverse_one("fe80::47", "7.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa");
-}
-
-int main(int argc, char *argv[]) {
-
- test_dns_label_unescape();
- test_dns_label_escape();
- test_dns_name_normalize();
- test_dns_name_equal();
- test_dns_name_endswith();
- test_dns_name_root();
- test_dns_name_single_label();
- test_dns_name_reverse();
-
- return 0;
-}
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
new file mode 100644
index 0000000..20a44ce
--- /dev/null
+++ b/src/shared/dns-domain.c
@@ -0,0 +1,613 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+ ***/
+
+#ifdef HAVE_LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+#endif
+
+#include "dns-domain.h"
+
+int dns_label_unescape(const char **name, char *dest, size_t sz) {
+ const char *n;
+ char *d;
+ int r = 0;
+
+ assert(name);
+ assert(*name);
+ assert(dest);
+
+ n = *name;
+ d = dest;
+
+ for (;;) {
+ if (*n == '.') {
+ n++;
+ break;
+ }
+
+ if (*n == 0)
+ break;
+
+ if (sz <= 0)
+ return -ENOSPC;
+
+ if (r >= DNS_LABEL_MAX)
+ return -EINVAL;
+
+ if (*n == '\\') {
+ /* Escaped character */
+
+ n++;
+
+ if (*n == 0)
+ /* Ending NUL */
+ return -EINVAL;
+
+ else if (*n == '\\' || *n == '.') {
+ /* Escaped backslash or dot */
+ *(d++) = *(n++);
+ sz--;
+ r++;
+
+ } else if (n[0] >= '0' && n[0] <= '9') {
+ unsigned k;
+
+ /* Escaped literal ASCII character */
+
+ if (!(n[1] >= '0' && n[1] <= '9') ||
+ !(n[2] >= '0' && n[2] <= '9'))
+ return -EINVAL;
+
+ k = ((unsigned) (n[0] - '0') * 100) +
+ ((unsigned) (n[1] - '0') * 10) +
+ ((unsigned) (n[2] - '0'));
+
+ /* Don't allow CC characters or anything that doesn't fit in 8bit */
+ if (k < ' ' || k > 255 || k == 127)
+ return -EINVAL;
+
+ *(d++) = (char) k;
+ sz--;
+ r++;
+
+ n += 3;
+ } else
+ return -EINVAL;
+
+ } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
+
+ /* Normal character */
+ *(d++) = *(n++);
+ sz--;
+ r++;
+ } else
+ return -EINVAL;
+ }
+
+ /* Empty label that is not at the end? */
+ if (r == 0 && *n)
+ return -EINVAL;
+
+ if (sz >= 1)
+ *d = 0;
+
+ *name = n;
+ return r;
+}
+
+int dns_label_escape(const char *p, size_t l, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ char *q;
+ int r;
+
+ assert(p);
+ assert(ret);
+
+ if (l > DNS_LABEL_MAX)
+ return -EINVAL;
+
+ s = malloc(l * 4 + 1);
+ if (!s)
+ return -ENOMEM;
+
+ q = s;
+ while (l > 0) {
+
+ if (*p == '.' || *p == '\\') {
+
+ /* Dot or backslash */
+ *(q++) = '\\';
+ *(q++) = *p;
+
+ } else if (*p == '_' ||
+ *p == '-' ||
+ (*p >= '0' && *p <= '9') ||
+ (*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z')) {
+
+ /* Proper character */
+ *(q++) = *p;
+ } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
+
+ /* Everything else */
+ *(q++) = '\\';
+ *(q++) = '0' + (char) ((uint8_t) *p / 100);
+ *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
+ *(q++) = '0' + (char) ((uint8_t) *p % 10);
+
+ } else
+ return -EINVAL;
+
+ p++;
+ l--;
+ }
+
+ *q = 0;
+ *ret = s;
+ r = q - s;
+ s = NULL;
+
+ return r;
+}
+
+int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
+#ifdef HAVE_LIBIDN
+ _cleanup_free_ uint32_t *input = NULL;
+ size_t input_size;
+ const char *p;
+ bool contains_8bit = false;
+
+ assert(encoded);
+ assert(decoded);
+ assert(decoded_max >= DNS_LABEL_MAX);
+
+ if (encoded_size <= 0)
+ return 0;
+
+ for (p = encoded; p < encoded + encoded_size; p++)
+ if ((uint8_t) *p > 127)
+ contains_8bit = true;
+
+ if (!contains_8bit)
+ return 0;
+
+ input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
+ if (!input)
+ return -ENOMEM;
+
+ if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0)
+ return -EINVAL;
+
+ return strlen(decoded);
+#else
+ return 0;
+#endif
+}
+
+int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
+#ifdef HAVE_LIBIDN
+ size_t input_size, output_size;
+ _cleanup_free_ uint32_t *input = NULL;
+ _cleanup_free_ char *result = NULL;
+ uint32_t *output = NULL;
+ size_t w;
+
+ /* To be invoked after unescaping */
+
+ assert(encoded);
+ assert(decoded);
+
+ if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
+ return 0;
+
+ if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
+ return 0;
+
+ input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
+ if (!input)
+ return -ENOMEM;
+
+ output_size = input_size;
+ output = newa(uint32_t, output_size);
+
+ idna_to_unicode_44i(input, input_size, output, &output_size, 0);
+
+ result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
+ if (!result)
+ return -ENOMEM;
+ if (w <= 0)
+ return 0;
+ if (w+1 > decoded_max)
+ return -EINVAL;
+
+ memcpy(decoded, result, w+1);
+ return w;
+#else
+ return 0;
+#endif
+}
+
+int dns_name_normalize(const char *s, char **_ret) {
+ _cleanup_free_ char *ret = NULL;
+ size_t n = 0, allocated = 0;
+ const char *p = s;
+ bool first = true;
+ int r;
+
+ assert(s);
+
+ for (;;) {
+ _cleanup_free_ char *t = NULL;
+ char label[DNS_LABEL_MAX];
+ int k;
+
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (*p != 0)
+ return -EINVAL;
+ break;
+ }
+
+ k = dns_label_undo_idna(label, r, label, sizeof(label));
+ if (k < 0)
+ return k;
+ if (k > 0)
+ r = k;
+
+ r = dns_label_escape(label, r, &t);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
+ return -ENOMEM;
+
+ if (!first)
+ ret[n++] = '.';
+ else
+ first = false;
+
+ memcpy(ret + n, t, r);
+ n += r;
+ }
+
+ if (n > DNS_NAME_MAX)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(ret, allocated, n + 1))
+ return -ENOMEM;
+
+ ret[n] = 0;
+
+ if (_ret) {
+ *_ret = ret;
+ ret = NULL;
+ }
+
+ return 0;
+}
+
+unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ const char *p = s;
+ unsigned long ul = hash_key[0];
+ int r;
+
+ assert(p);
+
+ while (*p) {
+ char label[DNS_LABEL_MAX+1];
+ int k;
+
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ break;
+
+ k = dns_label_undo_idna(label, r, label, sizeof(label));
+ if (k < 0)
+ break;
+ if (k > 0)
+ r = k;
+
+ label[r] = 0;
+ ascii_strlower(label);
+
+ ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
+ }
+
+ return ul;
+}
+
+int dns_name_compare_func(const void *a, const void *b) {
+ const char *x = a, *y = b;
+ int r, q, k, w;
+
+ assert(a);
+ assert(b);
+
+ for (;;) {
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
+
+ if (*x == 0 && *y == 0)
+ return 0;
+
+ r = dns_label_unescape(&x, la, sizeof(la));
+ q = dns_label_unescape(&y, lb, sizeof(lb));
+ if (r < 0 || q < 0)
+ return r - q;
+
+ k = dns_label_undo_idna(la, r, la, sizeof(la));
+ w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
+ if (k < 0 || w < 0)
+ return k - w;
+ if (k > 0)
+ r = k;
+ if (w > 0)
+ r = w;
+
+ la[r] = lb[q] = 0;
+ r = strcasecmp(la, lb);
+ if (r != 0)
+ return r;
+ }
+}
+
+const struct hash_ops dns_name_hash_ops = {
+ .hash = dns_name_hash_func,
+ .compare = dns_name_compare_func
+};
+
+int dns_name_equal(const char *x, const char *y) {
+ int r, q, k, w;
+
+ assert(x);
+ assert(y);
+
+ for (;;) {
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
+
+ if (*x == 0 && *y == 0)
+ return true;
+
+ r = dns_label_unescape(&x, la, sizeof(la));
+ if (r < 0)
+ return r;
+
+ k = dns_label_undo_idna(la, r, la, sizeof(la));
+ if (k < 0)
+ return k;
+ if (k > 0)
+ r = k;
+
+ q = dns_label_unescape(&y, lb, sizeof(lb));
+ if (q < 0)
+ return q;
+ w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
+ if (w < 0)
+ return w;
+ if (w > 0)
+ q = w;
+
+ la[r] = lb[q] = 0;
+ if (strcasecmp(la, lb))
+ return false;
+ }
+}
+
+int dns_name_endswith(const char *name, const char *suffix) {
+ const char *n, *s, *saved_n = NULL;
+ int r, q, k, w;
+
+ assert(name);
+ assert(suffix);
+
+ n = name;
+ s = suffix;
+
+ for (;;) {
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+
+ r = dns_label_unescape(&n, ln, sizeof(ln));
+ if (r < 0)
+ return r;
+ k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
+ if (k < 0)
+ return k;
+ if (k > 0)
+ r = k;
+
+ if (!saved_n)
+ saved_n = n;
+
+ q = dns_label_unescape(&s, ls, sizeof(ls));
+ if (q < 0)
+ return q;
+ w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
+ if (w < 0)
+ return w;
+ if (w > 0)
+ q = w;
+
+ if (r == 0 && q == 0)
+ return true;
+ if (r == 0 && saved_n == n)
+ return false;
+
+ ln[r] = ls[q] = 0;
+
+ if (r != q || strcasecmp(ln, ls)) {
+
+ /* Not the same, let's jump back, and try with the next label again */
+ s = suffix;
+ n = saved_n;
+ saved_n = NULL;
+ }
+ }
+}
+
+int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
+ const uint8_t *p;
+ int r;
+
+ assert(a);
+ assert(ret);
+
+ p = (const uint8_t*) a;
+
+ if (family == AF_INET)
+ r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
+ else if (family == AF_INET6)
+ r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa",
+ hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
+ hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
+ hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
+ hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
+ hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
+ hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
+ hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
+ hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
+ else
+ return -EAFNOSUPPORT;
+ if (r < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int dns_name_address(const char *p, int *family, union in_addr_union *address) {
+ int r;
+
+ assert(p);
+ assert(family);
+ assert(address);
+
+ r = dns_name_endswith(p, "in-addr.arpa");
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ uint8_t a[4];
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(a); i++) {
+ char label[DNS_LABEL_MAX+1];
+
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+ if (r > 3)
+ return -EINVAL;
+
+ r = safe_atou8(label, &a[i]);
+ if (r < 0)
+ return r;
+ }
+
+ r = dns_name_equal(p, "in-addr.arpa");
+ if (r <= 0)
+ return r;
+
+ *family = AF_INET;
+ address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
+ ((uint32_t) a[2] << 16) |
+ ((uint32_t) a[1] << 8) |
+ (uint32_t) a[0]);
+
+ return 1;
+ }
+
+ r = dns_name_endswith(p, "ip6.arpa");
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ struct in6_addr a;
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
+ char label[DNS_LABEL_MAX+1];
+ int x, y;
+
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r <= 0)
+ return r;
+ if (r != 1)
+ return -EINVAL;
+ x = unhexchar(label[0]);
+ if (x < 0)
+ return -EINVAL;
+
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r <= 0)
+ return r;
+ if (r != 1)
+ return -EINVAL;
+ y = unhexchar(label[0]);
+ if (y < 0)
+ return -EINVAL;
+
+ a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
+ }
+
+ r = dns_name_equal(p, "ip6.arpa");
+ if (r <= 0)
+ return r;
+
+ *family = AF_INET6;
+ address->in6 = a;
+ return 1;
+ }
+
+ return 0;
+}
+
+int dns_name_root(const char *name) {
+ char label[DNS_LABEL_MAX+1];
+ int r;
+
+ assert(name);
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return r;
+
+ return r == 0 && *name == 0;
+}
+
+int dns_name_single_label(const char *name) {
+ char label[DNS_LABEL_MAX+1];
+ int r;
+
+ assert(name);
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return r;
+
+ return r == 0 && *name == 0;
+}
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
new file mode 100644
index 0000000..516d244
--- /dev/null
+++ b/src/shared/dns-domain.h
@@ -0,0 +1,50 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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/>.
+ ***/
+
+#pragma once
+
+
+#include "hashmap.h"
+#include "in-addr-util.h"
+
+#define DNS_LABEL_MAX 63
+#define DNS_NAME_MAX 255
+
+int dns_label_unescape(const char **name, char *dest, size_t sz);
+int dns_label_escape(const char *p, size_t l, char **ret);
+
+int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
+int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
+
+int dns_name_normalize(const char *s, char **_ret);
+
+unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
+int dns_name_compare_func(const void *a, const void *b);
+extern const struct hash_ops dns_name_hash_ops;
+
+int dns_name_equal(const char *x, const char *y);
+int dns_name_endswith(const char *name, const char *suffix);
+
+int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
+int dns_name_address(const char *p, int *family, union in_addr_union *a);
+
+int dns_name_root(const char *name);
+int dns_name_single_label(const char *name);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
new file mode 100644
index 0000000..527cdd3
--- /dev/null
+++ b/src/test/test-dns-domain.c
@@ -0,0 +1,192 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 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 "macro.h"
+#include "dns-domain.h"
+
+static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
+ char buffer[buffer_sz];
+ int r;
+
+ r = dns_label_unescape(&what, buffer, buffer_sz);
+ assert_se(r == ret);
+
+ if (r < 0)
+ return;
+
+ assert_se(streq(buffer, expect));
+}
+
+static void test_dns_label_unescape(void) {
+ test_dns_label_unescape_one("hallo", "hallo", 6, 5);
+ test_dns_label_unescape_one("hallo", "hallo", 4, -ENOSPC);
+ test_dns_label_unescape_one("", "", 10, 0);
+ test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12);
+ test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5);
+ test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL);
+ test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL);
+ test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7);
+ test_dns_label_unescape_one(".", "", 20, 0);
+ test_dns_label_unescape_one("..", "", 20, -EINVAL);
+ test_dns_label_unescape_one(".foobar", "", 20, -EINVAL);
+ test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
+}
+
+static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ r = dns_label_escape(what, l, &t);
+ assert_se(r == ret);
+
+ if (r < 0)
+ return;
+
+ assert_se(streq_ptr(expect, t));
+}
+
+static void test_dns_label_escape(void) {
+ test_dns_label_escape_one("", 0, "", 0);
+ test_dns_label_escape_one("hallo", 5, "hallo", 5);
+ test_dns_label_escape_one("hallo", 6, NULL, -EINVAL);
+ test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31);
+}
+
+static void test_dns_name_normalize_one(const char *what, const char *expect, int ret) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ r = dns_name_normalize(what, &t);
+ assert_se(r == ret);
+
+ if (r < 0)
+ return;
+
+ assert_se(streq_ptr(expect, t));
+}
+
+static void test_dns_name_normalize(void) {
+ test_dns_name_normalize_one("", "", 0);
+ test_dns_name_normalize_one("f", "f", 0);
+ test_dns_name_normalize_one("f.waldi", "f.waldi", 0);
+ test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0);
+ test_dns_name_normalize_one("\\000", NULL, -EINVAL);
+ test_dns_name_normalize_one("..", NULL, -EINVAL);
+ test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
+ test_dns_name_normalize_one("foobar.", "foobar", 0);
+ test_dns_name_normalize_one(".", "", 0);
+}
+
+static void test_dns_name_equal_one(const char *a, const char *b, int ret) {
+ int r;
+
+ r = dns_name_equal(a, b);
+ assert_se(r == ret);
+
+ r = dns_name_equal(b, a);
+ assert_se(r == ret);
+}
+
+static void test_dns_name_equal(void) {
+ test_dns_name_equal_one("", "", true);
+ test_dns_name_equal_one("x", "x", true);
+ test_dns_name_equal_one("x", "x.", true);
+ test_dns_name_equal_one("abc.def", "abc.def", true);
+ test_dns_name_equal_one("abc.def", "ABC.def", true);
+ test_dns_name_equal_one("abc.def", "CBA.def", false);
+ test_dns_name_equal_one("", "xxx", false);
+ test_dns_name_equal_one("ab", "a", false);
+ test_dns_name_equal_one("\\000", "xxxx", -EINVAL);
+ test_dns_name_equal_one(".", "", true);
+ test_dns_name_equal_one(".", ".", true);
+ test_dns_name_equal_one("..", "..", -EINVAL);
+}
+
+static void test_dns_name_endswith_one(const char *a, const char *b, int ret) {
+ assert_se(dns_name_endswith(a, b) == ret);
+}
+
+static void test_dns_name_endswith(void) {
+ test_dns_name_endswith_one("", "", true);
+ test_dns_name_endswith_one("", "xxx", false);
+ test_dns_name_endswith_one("xxx", "", true);
+ test_dns_name_endswith_one("x", "x", true);
+ test_dns_name_endswith_one("x", "y", false);
+ test_dns_name_endswith_one("x.y", "y", true);
+ test_dns_name_endswith_one("x.y", "Y", true);
+ test_dns_name_endswith_one("x.y", "x", false);
+ test_dns_name_endswith_one("x.y.z", "Z", true);
+ test_dns_name_endswith_one("x.y.z", "y.Z", true);
+ test_dns_name_endswith_one("x.y.z", "x.y.Z", true);
+ test_dns_name_endswith_one("x.y.z", "waldo", false);
+ test_dns_name_endswith_one("x.y.z.u.v.w", "y.z", false);
+ test_dns_name_endswith_one("x.y.z.u.v.w", "u.v.w", true);
+ test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
+}
+
+static void test_dns_name_root(void) {
+ assert_se(dns_name_root("") == true);
+ assert_se(dns_name_root(".") == true);
+ assert_se(dns_name_root("xxx") == false);
+ assert_se(dns_name_root("xxx.") == false);
+ assert_se(dns_name_root("..") == -EINVAL);
+}
+
+static void test_dns_name_single_label(void) {
+ assert_se(dns_name_single_label("") == false);
+ assert_se(dns_name_single_label(".") == false);
+ assert_se(dns_name_single_label("..") == -EINVAL);
+ assert_se(dns_name_single_label("x") == true);
+ assert_se(dns_name_single_label("x.") == true);
+ assert_se(dns_name_single_label("xx.yy") == false);
+}
+
+static void test_dns_name_reverse_one(const char *address, const char *name) {
+ _cleanup_free_ char *p = NULL;
+ union in_addr_union a, b = {};
+ int familya, familyb;
+
+ assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0);
+ assert_se(dns_name_reverse(familya, &a, &p) >= 0);
+ assert_se(streq(p, name));
+ assert_se(dns_name_address(p, &familyb, &b) > 0);
+ assert_se(familya == familyb);
+ assert_se(in_addr_equal(familya, &a, &b));
+}
+
+static void test_dns_name_reverse(void) {
+ test_dns_name_reverse_one("47.11.8.15", "15.8.11.47.in-addr.arpa");
+ test_dns_name_reverse_one("fe80::47", "7.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa");
+}
+
+int main(int argc, char *argv[]) {
+
+ test_dns_label_unescape();
+ test_dns_label_escape();
+ test_dns_name_normalize();
+ test_dns_name_equal();
+ test_dns_name_endswith();
+ test_dns_name_root();
+ test_dns_name_single_label();
+ test_dns_name_reverse();
+
+ return 0;
+}
commit a315ac4e076c4ce7ce3e5c95792cf916d5e918c5
Author: Lennart Poettering <lennart at poettering.net>
Date: Wed Jun 10 19:24:58 2015 +0200
journald: don't employ inner loop for reading from incoming sockets
Otherwise, if the socket is constantly busy we will never return to the
event loop, but we really need to to dispatch other (possibly more
high-priority) events too. Hence, return after dispatching one message
to the event handler, and rely on the event loop calling us back
right-away.
Fixes #125
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 32da8d6..d0d670f 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -1104,6 +1104,42 @@ finish:
int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
Server *s = userdata;
+ struct ucred *ucred = NULL;
+ struct timeval *tv = NULL;
+ struct cmsghdr *cmsg;
+ char *label = NULL;
+ size_t label_len = 0, m;
+ struct iovec iovec;
+ ssize_t n;
+ int *fds = NULL, v = 0;
+ unsigned n_fds = 0;
+
+ union {
+ struct cmsghdr cmsghdr;
+
+ /* We use NAME_MAX space for the SELinux label
+ * here. The kernel currently enforces no
+ * limit, but according to suggestions from
+ * the SELinux people this will change and it
+ * will probably be identical to NAME_MAX. For
+ * now we use that, but this should be updated
+ * one day when the final limit is known. */
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(struct timeval)) +
+ CMSG_SPACE(sizeof(int)) + /* fd */
+ CMSG_SPACE(NAME_MAX)]; /* selinux label */
+ } control = {};
+
+ union sockaddr_union sa = {};
+
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ .msg_name = &sa,
+ .msg_namelen = sizeof(sa),
+ };
assert(s);
assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd);
@@ -1113,119 +1149,79 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
return -EIO;
}
- for (;;) {
- struct ucred *ucred = NULL;
- struct timeval *tv = NULL;
- struct cmsghdr *cmsg;
- char *label = NULL;
- size_t label_len = 0;
- struct iovec iovec;
-
- union {
- struct cmsghdr cmsghdr;
-
- /* We use NAME_MAX space for the SELinux label
- * here. The kernel currently enforces no
- * limit, but according to suggestions from
- * the SELinux people this will change and it
- * will probably be identical to NAME_MAX. For
- * now we use that, but this should be updated
- * one day when the final limit is known. */
- uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(sizeof(struct timeval)) +
- CMSG_SPACE(sizeof(int)) + /* fd */
- CMSG_SPACE(NAME_MAX)]; /* selinux label */
- } control = {};
- union sockaddr_union sa = {};
- struct msghdr msghdr = {
- .msg_iov = &iovec,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- .msg_name = &sa,
- .msg_namelen = sizeof(sa),
- };
-
- ssize_t n;
- int *fds = NULL;
- unsigned n_fds = 0;
- int v = 0;
- size_t m;
-
- /* Try to get the right size, if we can. (Not all
- * sockets support SIOCINQ, hence we just try, but
- * don't rely on it. */
- (void) ioctl(fd, SIOCINQ, &v);
-
- /* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful! */
- m = PAGE_ALIGN(MAX3((size_t) v + 1,
- (size_t) LINE_MAX,
- ALIGN(sizeof(struct nlmsghdr)) + ALIGN((size_t) MAX_AUDIT_MESSAGE_LENGTH)) + 1);
-
- if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m))
- return log_oom();
-
- iovec.iov_base = s->buffer;
- iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */
-
- n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (errno == EINTR || errno == EAGAIN)
- return 0;
-
- log_error_errno(errno, "recvmsg() failed: %m");
- return -errno;
- }
+ /* Try to get the right size, if we can. (Not all
+ * sockets support SIOCINQ, hence we just try, but
+ * don't rely on it. */
+ (void) ioctl(fd, SIOCINQ, &v);
- CMSG_FOREACH(cmsg, &msghdr) {
-
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
- ucred = (struct ucred*) CMSG_DATA(cmsg);
- else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_SECURITY) {
- label = (char*) CMSG_DATA(cmsg);
- label_len = cmsg->cmsg_len - CMSG_LEN(0);
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SO_TIMESTAMP &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
- tv = (struct timeval*) CMSG_DATA(cmsg);
- else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
- fds = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
- }
- }
+ /* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful! */
+ m = PAGE_ALIGN(MAX3((size_t) v + 1,
+ (size_t) LINE_MAX,
+ ALIGN(sizeof(struct nlmsghdr)) + ALIGN((size_t) MAX_AUDIT_MESSAGE_LENGTH)) + 1);
- /* And a trailing NUL, just in case */
- s->buffer[n] = 0;
+ if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m))
+ return log_oom();
- if (fd == s->syslog_fd) {
- if (n > 0 && n_fds == 0)
- server_process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len);
- else if (n_fds > 0)
- log_warning("Got file descriptors via syslog socket. Ignoring.");
+ iovec.iov_base = s->buffer;
+ iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */
- } else if (fd == s->native_fd) {
- if (n > 0 && n_fds == 0)
- server_process_native_message(s, s->buffer, n, ucred, tv, label, label_len);
- else if (n == 0 && n_fds == 1)
- server_process_native_file(s, fds[0], ucred, tv, label, label_len);
- else if (n_fds > 0)
- log_warning("Got too many file descriptors via native socket. Ignoring.");
+ n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
- } else {
- assert(fd == s->audit_fd);
+ return log_error_errno(errno, "recvmsg() failed: %m");
+ }
- if (n > 0 && n_fds == 0)
- server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen);
- else if (n_fds > 0)
- log_warning("Got file descriptors via audit socket. Ignoring.");
+ CMSG_FOREACH(cmsg, &msghdr) {
+
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_SECURITY) {
+ label = (char*) CMSG_DATA(cmsg);
+ label_len = cmsg->cmsg_len - CMSG_LEN(0);
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMP &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
+ tv = (struct timeval*) CMSG_DATA(cmsg);
+ else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ fds = (int*) CMSG_DATA(cmsg);
+ n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
}
+ }
+
+ /* And a trailing NUL, just in case */
+ s->buffer[n] = 0;
+
+ if (fd == s->syslog_fd) {
+ if (n > 0 && n_fds == 0)
+ server_process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len);
+ else if (n_fds > 0)
+ log_warning("Got file descriptors via syslog socket. Ignoring.");
+
+ } else if (fd == s->native_fd) {
+ if (n > 0 && n_fds == 0)
+ server_process_native_message(s, s->buffer, n, ucred, tv, label, label_len);
+ else if (n == 0 && n_fds == 1)
+ server_process_native_file(s, fds[0], ucred, tv, label, label_len);
+ else if (n_fds > 0)
+ log_warning("Got too many file descriptors via native socket. Ignoring.");
- close_many(fds, n_fds);
+ } else {
+ assert(fd == s->audit_fd);
+
+ if (n > 0 && n_fds == 0)
+ server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen);
+ else if (n_fds > 0)
+ log_warning("Got file descriptors via audit socket. Ignoring.");
}
+
+ close_many(fds, n_fds);
+ return 0;
}
static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
commit 870395a4d8e8fe55ab22fd648eb70e008844130c
Author: Tom Gundersen <teg at jklm.no>
Date: Wed Jun 10 19:00:39 2015 +0200
sd-network: allow the state dir to be created after the monitor
We now listen for new subdirs of /run/systemd, and /run/systemd/netif in case
/run/systemd/netif/links does not exist.
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index 5879414..207eda1 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -297,8 +297,31 @@ static inline sd_network_monitor* FD_TO_MONITOR(int fd) {
return (sd_network_monitor*) (unsigned long) (fd + 1);
}
+static int monitor_add_inotify_watch(int fd) {
+ int k;
+
+ k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE);
+ if (k >= 0)
+ return 0;
+ else if (errno != ENOENT)
+ return -errno;
+
+ k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR);
+ if (k >= 0)
+ return 0;
+ else if (errno != ENOENT)
+ return -errno;
+
+ k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR);
+ if (k < 0)
+ return -errno;
+
+ return 0;
+}
+
_public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) {
- int fd, k;
+ _cleanup_close_ int fd = -1;
+ int k;
bool good = false;
assert_return(m, -EINVAL);
@@ -308,11 +331,9 @@ _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category
return -errno;
if (!category || streq(category, "links")) {
- k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE);
- if (k < 0) {
- safe_close(fd);
- return -errno;
- }
+ k = monitor_add_inotify_watch(fd);
+ if (k < 0)
+ return k;
good = true;
}
@@ -323,6 +344,8 @@ _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category
}
*m = FD_TO_MONITOR(fd);
+ fd = -1;
+
return 0;
}
@@ -338,10 +361,36 @@ _public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
}
_public_ int sd_network_monitor_flush(sd_network_monitor *m) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
+ int fd, k;
assert_return(m, -EINVAL);
- return flush_fd(MONITOR_TO_FD(m));
+ fd = MONITOR_TO_FD(m);
+
+ l = read(fd, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return -errno;
+ }
+
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
+ if (e->mask & IN_ISDIR) {
+ k = monitor_add_inotify_watch(fd);
+ if (k < 0)
+ return k;
+
+ k = inotify_rm_watch(fd, e->wd);
+ if (k < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
}
_public_ int sd_network_monitor_get_fd(sd_network_monitor *m) {
commit 76f0a56790d6d926fcd293c2c60b9b779566ad2d
Author: Tom Gundersen <teg at jklm.no>
Date: Wed Jun 10 17:05:04 2015 +0200
networkd: bond - only set packets_per_slave on balance-rr mode
Otherwise the creation of the bond fails.
diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c
index 9919955..d7f9bb4 100644
--- a/src/network/networkd-netdev-bond.c
+++ b/src/network/networkd-netdev-bond.c
@@ -242,7 +242,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m
}
if (b->ad_select != _NETDEV_BOND_AD_SELECT_INVALID &&
- b->mode == BOND_MODE_8023AD) {
+ b->mode == NETDEV_BOND_MODE_802_3AD) {
r = sd_rtnl_message_append_u8(m, IFLA_BOND_AD_SELECT, b->ad_select);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_SELECT attribute: %m");
@@ -279,7 +279,8 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_RESEND_IGMP attribute: %m");
}
- if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX) {
+ if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX &&
+ b->mode == NETDEV_BOND_MODE_BALANCE_RR) {
r = sd_rtnl_message_append_u32(m, IFLA_BOND_PACKETS_PER_SLAVE, b->packets_per_slave);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_PACKETS_PER_SLAVE attribute: %m");
commit dcc7aacd67505b812214058dc9a8d321d8d98c7c
Author: Charles Duffy <chaduffy at cisco.com>
Date: Wed May 27 17:30:11 2015 -0500
cgtop: more sensible flushing behavior w/ non-TTY output
- Explicitly flush stdout before sleep between iterations
- Only clear user keystrokes when output is to TTY
- Add a newline between output batches when output is not to TTY
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index d04d6f2..3a2842d 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -749,6 +749,10 @@ int main(int argc, char *argv[]) {
if (arg_iterations && iteration >= arg_iterations)
break;
+ if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */
+ fputs("\n", stdout);
+ fflush(stdout);
+
if (arg_batch) {
usleep(last_refresh + arg_delay - t);
} else {
@@ -762,8 +766,10 @@ int main(int argc, char *argv[]) {
}
}
- fputs("\r \r", stdout);
- fflush(stdout);
+ if (on_tty()) { /* TTY: Clear any user keystroke */
+ fputs("\r \r", stdout);
+ fflush(stdout);
+ }
if (arg_batch)
continue;
commit 780fe62ecab08850cefd136b95f38c15cb31c0ec
Author: Charles Duffy <chaduffy at cisco.com>
Date: Fri May 22 22:18:15 2015 -0500
cgtop: allow user to force looping behavior even in non-TTY mode
diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml
index b3298b6..d145644 100644
--- a/man/systemd-cgtop.xml
+++ b/man/systemd-cgtop.xml
@@ -65,10 +65,12 @@
groups of the local Linux control group hierarchy, ordered by
their CPU, memory, or disk I/O load. The display is refreshed in
regular intervals (by default every 1s), similar in style to
- <citerefentry project='man-pages'><refentrytitle>top</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
- If <command>systemd-cgtop</command> is not connected to a tty,
- only one iteration is performed and no columns headers are
- printed. This mode is suitable for scripting.</para>
+ <citerefentry project='man-pages'><refentrytitle>top</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+ <para>If <command>systemd-cgtop</command> is not connected to a tty, no
+ column headers are printed and the default is to only run one iteration.
+ The <varname>--iterations</varname> argument, if given, is still honored.
+ This mode is suitable for scripting.</para>
<para>Resource usage is only accounted for control groups in the
relevant hierarchy, i.e. CPU usage is only accounted for control
@@ -155,8 +157,8 @@
<term><option>-n</option></term>
<term><option>--iterations=</option></term>
- <listitem><para>Perform only this many iterations.
- </para></listitem>
+ <listitem><para>Perform only this many iterations. A value of 0
+ indicates that the program should run indefinitely.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 8c58cd2..d04d6f2 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -60,7 +60,7 @@ typedef struct Group {
} Group;
static unsigned arg_depth = 3;
-static unsigned arg_iterations = 0;
+static unsigned arg_iterations = (unsigned)-1;
static bool arg_batch = false;
static bool arg_raw = false;
static usec_t arg_delay = 1*USEC_PER_SEC;
@@ -715,8 +715,8 @@ int main(int argc, char *argv[]) {
signal(SIGWINCH, columns_lines_cache_reset);
- if (!on_tty())
- arg_iterations = 1;
+ if (arg_iterations == (unsigned)-1)
+ arg_iterations = on_tty() ? 0 : 1;
while (!quit) {
Hashmap *c;
commit a2c9f63136775b128bdb9fb3e1b57f5ad977d5cb
Author: Charles Duffy <chaduffy at cisco.com>
Date: Fri May 22 16:20:49 2015 -0500
cgtop: raw output option (disable conversion to human-readable units)
diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml
index b0c8800..b3298b6 100644
--- a/man/systemd-cgtop.xml
+++ b/man/systemd-cgtop.xml
@@ -143,6 +143,15 @@
</varlistentry>
<varlistentry>
+ <term><option>-r</option></term>
+ <term><option>--raw</option></term>
+
+ <listitem><para>Format byte counts (as in memory usage and IO metrics)
+ with raw numeric values rather than human-readable
+ numbers.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-n</option></term>
<term><option>--iterations=</option></term>
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index a390cf3..8c58cd2 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -62,6 +62,7 @@ typedef struct Group {
static unsigned arg_depth = 3;
static unsigned arg_iterations = 0;
static bool arg_batch = false;
+static bool arg_raw = false;
static usec_t arg_delay = 1*USEC_PER_SEC;
static enum {
@@ -96,6 +97,16 @@ static void group_hashmap_free(Hashmap *h) {
hashmap_free(h);
}
+static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t t) {
+ if (!is_valid)
+ return "-";
+ if (arg_raw) {
+ snprintf(buf, l, "%jd", t);
+ return buf;
+ }
+ return format_bytes(buf, l, t);
+}
+
static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
Group *g;
int r;
@@ -532,18 +543,9 @@ static int display(Hashmap *a) {
} else
printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
- if (g->memory_valid)
- printf(" %8s", format_bytes(buffer, sizeof(buffer), g->memory));
- else
- fputs(" -", stdout);
-
- if (g->io_valid) {
- printf(" %8s",
- format_bytes(buffer, sizeof(buffer), g->io_input_bps));
- printf(" %8s",
- format_bytes(buffer, sizeof(buffer), g->io_output_bps));
- } else
- fputs(" - -", stdout);
+ printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory));
+ printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps));
+ printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_output_bps));
putchar('\n');
}
@@ -561,6 +563,7 @@ static void help(void) {
" -c Order by CPU load\n"
" -m Order by memory load\n"
" -i Order by IO load\n"
+ " -r --raw Provide raw (not human-readable) numbers\n"
" --cpu[=TYPE] Show CPU usage as time or percentage (default)\n"
" -d --delay=DELAY Delay between updates\n"
" -n --iterations=N Run for N iterations before exiting\n"
@@ -583,6 +586,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "delay", required_argument, NULL, 'd' },
{ "iterations", required_argument, NULL, 'n' },
{ "batch", no_argument, NULL, 'b' },
+ { "raw", no_argument, NULL, 'r' },
{ "depth", required_argument, NULL, ARG_DEPTH },
{ "cpu", optional_argument, NULL, ARG_CPU_TYPE},
{}
@@ -594,7 +598,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hptcmin:bd:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hptcmin:brd:", options, NULL)) >= 0)
switch (c) {
@@ -649,6 +653,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_batch = true;
break;
+ case 'r':
+ arg_raw = true;
+ break;
+
case 'p':
arg_order = ORDER_PATH;
break;
commit a0ab162a097c7d8ea4dbd4d868de5236610e8f14
Author: Daniel Hahler <git at thequod.de>
Date: Wed May 27 21:26:26 2015 +0200
zsh-completion: optimize _filter_units_by_property
Optimize _filter_units_by_property by calling `systemctl` only once with
a list of units, and not once per unit.
I could not reproduce the "Unknown unit" error mentioned in a FIXME,
which might have made this necessary previously.
diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in
index 17736de..4bf306a 100644
--- a/shell-completion/zsh/_systemctl.in
+++ b/shell-completion/zsh/_systemctl.in
@@ -127,16 +127,11 @@ _systemctl_really_all_units()
_filter_units_by_property() {
local property=$1 value=$2 ; shift ; shift
local -a units ; units=($*)
- local prop unit
- for ((i=1; $i <= ${#units[*]}; i++)); do
- # FIXME: "Failed to issue method call: Unknown unit" errors are ignored for
- # now (related to DBUS_ERROR_UNKNOWN_OBJECT). in the future, we need to
- # revert to calling 'systemctl show' once for all units, which is way
- # faster
- unit=${units[i]}
- prop=${(f)"$(_call_program units "$service show --no-pager --property="$property" ${unit} 2>/dev/null")"}
- if [[ "${prop}" = "$property=$value" ]]; then
- echo -E - " ${unit}"
+ local props
+ for props in ${(ps:\n\n:)"$(_call_program units "$service show --no-pager --property="Id,$property" -- ${units} 2>/dev/null")"}; do
+ props=(${(f)props})
+ if [[ "${props[2]}" = "$property=$value" ]]; then
+ echo -E - " ${props[1]#Id=}"
fi
done
}
commit 185a08745957cbd32e8293daf8c51ab9c995a71e
Author: Dimitri John Ledkov <dimitri.j.ledkov at intel.com>
Date: Mon Jun 1 12:46:52 2015 +0100
shared: Drop 'name=' prefix from SYSTEMD_CGROUP_CONTROLLER define.
In cgtop,mount-setup,nspawn the name= prefix is hard-coded in the
mount options, and the define is not used.
Everywhere else, we explicitly white-list allow 'name=' prefix to be
used with all controllers, and strip it out to 'normalise' the
controller name. That work is mostly inflicted on us due to 'name='
prefix in the define. Dropping this prefix makes everything more sane
overall.
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index 9988e5c..d83cdf7 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -441,9 +441,7 @@ static const char *normalize_controller(const char *controller) {
assert(controller);
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- return "systemd";
- else if (startswith(controller, "name="))
+ if (startswith(controller, "name="))
return controller + 5;
else
return controller;
@@ -483,7 +481,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
assert(fs);
- if (controller && !cg_controller_is_valid(controller, true))
+ if (controller && !cg_controller_is_valid(controller))
return -EINVAL;
if (_unlikely_(!good)) {
@@ -526,7 +524,7 @@ int cg_get_path_and_check(const char *controller, const char *path, const char *
assert(fs);
- if (!cg_controller_is_valid(controller, true))
+ if (!cg_controller_is_valid(controller))
return -EINVAL;
/* Normalize the controller syntax */
@@ -742,7 +740,7 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
assert(pid >= 0);
if (controller) {
- if (!cg_controller_is_valid(controller, true))
+ if (!cg_controller_is_valid(controller))
return -EINVAL;
controller = normalize_controller(controller);
@@ -971,7 +969,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
e = strchr(spec, ':');
if (!e) {
- if (!cg_controller_is_valid(spec, true))
+ if (!cg_controller_is_valid(spec))
return -EINVAL;
if (controller) {
@@ -994,7 +992,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
t = strdup(normalize_controller(v));
if (!t)
return -ENOMEM;
- if (!cg_controller_is_valid(t, true)) {
+ if (!cg_controller_is_valid(t)) {
free(t);
return -EINVAL;
}
@@ -1610,17 +1608,15 @@ char *cg_unescape(const char *p) {
DIGITS LETTERS \
"_"
-bool cg_controller_is_valid(const char *p, bool allow_named) {
+bool cg_controller_is_valid(const char *p) {
const char *t, *s;
if (!p)
return false;
- if (allow_named) {
- s = startswith(p, "name=");
- if (s)
- p = s;
- }
+ s = startswith(p, "name=");
+ if (s)
+ p = s;
if (*p == 0 || *p == '_')
return false;
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
index cbf7201..fd72e9e 100644
--- a/src/shared/cgroup-util.h
+++ b/src/shared/cgroup-util.h
@@ -122,7 +122,7 @@ int cg_path_decode_unit(const char *cgroup, char **unit);
char *cg_escape(const char *p);
char *cg_unescape(const char *p) _pure_;
-bool cg_controller_is_valid(const char *p, bool allow_named);
+bool cg_controller_is_valid(const char *p);
int cg_slice_to_path(const char *unit, char **ret);
diff --git a/src/shared/def.h b/src/shared/def.h
index a3d9fcf..011c7c6 100644
--- a/src/shared/def.h
+++ b/src/shared/def.h
@@ -35,7 +35,7 @@
* the watchdog pings will keep the loop busy. */
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
-#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"
+#define SYSTEMD_CGROUP_CONTROLLER "systemd"
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index 4a89f64..ecc9d70 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -244,16 +244,16 @@ static void test_escape(void) {
}
static void test_controller_is_valid(void) {
- assert_se(cg_controller_is_valid("foobar", false));
- assert_se(cg_controller_is_valid("foo_bar", false));
- assert_se(cg_controller_is_valid("name=foo", true));
- assert_se(!cg_controller_is_valid("", false));
- assert_se(!cg_controller_is_valid("name=", true));
- assert_se(!cg_controller_is_valid("=", false));
- assert_se(!cg_controller_is_valid("cpu,cpuacct", false));
- assert_se(!cg_controller_is_valid("_", false));
- assert_se(!cg_controller_is_valid("_foobar", false));
- assert_se(!cg_controller_is_valid("tatü", false));
+ assert_se(cg_controller_is_valid("foobar"));
+ assert_se(cg_controller_is_valid("foo_bar"));
+ assert_se(cg_controller_is_valid("name=foo"));
+ assert_se(!cg_controller_is_valid(""));
+ assert_se(!cg_controller_is_valid("name="));
+ assert_se(!cg_controller_is_valid("="));
+ assert_se(!cg_controller_is_valid("cpu,cpuacct"));
+ assert_se(!cg_controller_is_valid("_"));
+ assert_se(!cg_controller_is_valid("_foobar"));
+ assert_se(!cg_controller_is_valid("tatü"));
}
static void test_slice_to_path_one(const char *unit, const char *path, int error) {
More information about the systemd-commits
mailing list