summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--CODING_STYLE7
-rw-r--r--Makefile-man.am2
-rw-r--r--Makefile.am180
-rw-r--r--NEWS84
-rw-r--r--TODO11
-rwxr-xr-xautogen.sh3
-rw-r--r--configure.ac28
-rw-r--r--hwdb/20-OUI.hwdb333
-rw-r--r--hwdb/20-bluetooth-vendor-product.hwdb75
-rw-r--r--hwdb/20-pci-classes.hwdb6
-rw-r--r--hwdb/20-pci-vendor-model.hwdb277
-rw-r--r--hwdb/60-evdev.hwdb33
-rw-r--r--hwdb/60-keyboard.hwdb9
-rw-r--r--hwdb/70-mouse.hwdb27
-rw-r--r--hwdb/70-pointingstick.hwdb9
-rw-r--r--man/file-hierarchy.xml4
-rw-r--r--man/systemd-efi-boot-generator.xml85
-rw-r--r--man/systemd-fsck@.service.xml2
-rw-r--r--man/systemd-gpt-auto-generator.xml15
-rw-r--r--man/systemd-machine-id-commit.xml2
-rw-r--r--man/systemd-networkd-wait-online.service.xml3
-rw-r--r--man/systemd-nspawn.xml15
-rw-r--r--man/systemd.exec.xml3
-rw-r--r--man/systemd.generator.xml1
-rw-r--r--man/systemd.netdev.xml23
-rw-r--r--man/systemd.unit.xml2
-rw-r--r--src/analyze/analyze.c238
-rw-r--r--src/basic/bitmap.c16
-rw-r--r--src/basic/calendarspec.c8
-rw-r--r--src/basic/cgroup-util.c9
-rw-r--r--src/basic/copy.c6
-rw-r--r--src/basic/fileio.c10
-rw-r--r--src/basic/list.h26
-rw-r--r--src/basic/missing.h4
-rw-r--r--src/basic/smack-util.c117
-rw-r--r--src/basic/smack-util.h24
-rw-r--r--src/basic/socket-util.c8
-rw-r--r--src/basic/terminal-util.c36
-rw-r--r--src/basic/unit-name.c11
-rw-r--r--src/basic/util.c97
-rw-r--r--src/basic/util.h9
-rw-r--r--src/boot/bootctl.c8
-rw-r--r--src/boot/efi/boot.c57
-rw-r--r--src/boot/efi/disk.c51
-rw-r--r--src/boot/efi/disk.h21
-rw-r--r--src/boot/efi/stub.c6
-rw-r--r--src/bootchart/bootchart.c2
-rw-r--r--src/bus-proxyd/bus-xml-policy.c6
-rw-r--r--src/bus-proxyd/driver.c113
-rw-r--r--src/bus-proxyd/proxy.c19
-rw-r--r--src/bus-proxyd/proxy.h13
-rw-r--r--src/bus-proxyd/synthesize.c19
l---------src/console/Makefile1
-rw-r--r--src/console/consoled-display.c81
-rw-r--r--src/console/consoled-manager.c284
-rw-r--r--src/console/consoled-session.c279
-rw-r--r--src/console/consoled-terminal.c358
-rw-r--r--src/console/consoled-workspace.c167
-rw-r--r--src/console/consoled.c66
-rw-r--r--src/console/consoled.h164
-rw-r--r--src/core/dbus-cgroup.c4
-rw-r--r--src/core/dbus-manager.c7
-rw-r--r--src/core/execute.c12
-rw-r--r--src/core/load-fragment.c8
-rw-r--r--src/core/main.c6
-rw-r--r--src/core/manager.c13
-rw-r--r--src/core/mount.c6
-rw-r--r--src/core/selinux-access.c12
-rw-r--r--src/core/snapshot.c3
-rw-r--r--src/core/socket.c6
-rw-r--r--src/cryptsetup/cryptsetup.c3
l---------src/efi-boot-generator/Makefile1
-rw-r--r--src/efi-boot-generator/efi-boot-generator.c162
-rw-r--r--src/firstboot/firstboot.c33
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c339
-rw-r--r--src/import/pull-dkr.c6
-rw-r--r--src/journal-remote/journal-remote.c1
-rw-r--r--src/journal-remote/journal-upload.c23
-rw-r--r--src/journal/catalog.c15
-rw-r--r--src/journal/coredumpctl.c3
-rw-r--r--src/journal/journalctl.c3
-rw-r--r--src/journal/journald-server.h2
-rw-r--r--src/journal/journald-stream.c26
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h12
-rw-r--r--src/libsystemd-network/dhcp-protocol.h2
-rw-r--r--src/libsystemd-network/network-internal.c27
-rw-r--r--src/libsystemd-network/network-internal.h3
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c146
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c10
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c11
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c5
-rw-r--r--src/libsystemd-network/sd-lldp.c68
-rw-r--r--src/libsystemd-network/sd-pppoe.c4
-rw-r--r--src/libsystemd-terminal/.gitignore1
-rw-r--r--src/libsystemd-terminal/evcat.c488
-rw-r--r--src/libsystemd-terminal/grdev-drm.c3092
-rw-r--r--src/libsystemd-terminal/grdev-internal.h251
-rw-r--r--src/libsystemd-terminal/grdev.c1359
-rw-r--r--src/libsystemd-terminal/grdev.h199
-rw-r--r--src/libsystemd-terminal/idev-evdev.c859
-rw-r--r--src/libsystemd-terminal/idev-internal.h188
-rw-r--r--src/libsystemd-terminal/idev-keyboard.c1159
-rw-r--r--src/libsystemd-terminal/idev.c799
-rw-r--r--src/libsystemd-terminal/idev.h221
-rw-r--r--src/libsystemd-terminal/modeset.c482
-rw-r--r--src/libsystemd-terminal/subterm.c981
-rw-r--r--src/libsystemd-terminal/sysview-internal.h144
-rw-r--r--src/libsystemd-terminal/sysview.c1554
-rw-r--r--src/libsystemd-terminal/sysview.h162
-rw-r--r--src/libsystemd-terminal/term-charset.c488
-rw-r--r--src/libsystemd-terminal/term-internal.h650
-rw-r--r--src/libsystemd-terminal/term-page.c2091
-rw-r--r--src/libsystemd-terminal/term-parser.c1702
-rw-r--r--src/libsystemd-terminal/term-screen.c4333
-rw-r--r--src/libsystemd-terminal/term-wcwidth.c312
-rw-r--r--src/libsystemd-terminal/term.h183
-rw-r--r--src/libsystemd-terminal/test-term-page.c459
-rw-r--r--src/libsystemd-terminal/test-term-parser.c141
-rw-r--r--src/libsystemd-terminal/test-unifont.c125
-rw-r--r--src/libsystemd-terminal/unifont-def.h137
-rw-r--r--src/libsystemd-terminal/unifont.c238
-rw-r--r--src/libsystemd-terminal/unifont.h54
-rw-r--r--src/libsystemd/sd-bus/bus-control.c20
-rw-r--r--src/libsystemd/sd-bus/bus-dump.c7
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.c21
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.c10
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c4
-rw-r--r--src/libsystemd/sd-bus/bus-match.c8
-rw-r--r--src/libsystemd/sd-bus/bus-message.c59
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c82
-rw-r--r--src/libsystemd/sd-bus/busctl-introspect.c8
-rw-r--r--src/libsystemd/sd-bus/busctl.c36
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c33
-rw-r--r--src/libsystemd/sd-bus/test-bus-gvariant.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-marshal.c8
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c18
-rw-r--r--src/libsystemd/sd-bus/test-bus-proxy.c17
-rw-r--r--src/libsystemd/sd-device/device-private.c8
-rw-r--r--src/libsystemd/sd-event/sd-event.c9
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c18
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c41
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h2
-rw-r--r--src/libsystemd/sd-netlink/test-local-addresses.c3
-rw-r--r--src/locale/localectl.c6
-rw-r--r--src/locale/localed.c24
-rw-r--r--src/login/70-power-switch.rules2
-rw-r--r--src/login/loginctl.c8
-rw-r--r--src/login/logind-core.c38
-rw-r--r--src/login/logind-dbus.c63
-rw-r--r--src/login/logind-inhibit.c25
-rw-r--r--src/login/logind-seat.c25
-rw-r--r--src/login/logind-session.c65
-rw-r--r--src/login/logind-session.h1
-rw-r--r--src/login/logind-user.c25
-rw-r--r--src/login/logind.c18
-rw-r--r--src/login/logind.h5
-rw-r--r--src/machine/machine-dbus.c8
-rw-r--r--src/machine/machine.c32
-rw-r--r--src/machine/machinectl.c16
-rw-r--r--src/network/networkctl.c4
-rw-r--r--src/network/networkd-dhcp4.c8
-rw-r--r--src/network/networkd-link.c12
-rw-r--r--src/network/networkd-manager.c12
-rw-r--r--src/network/networkd-netdev-gperf.gperf4
-rw-r--r--src/network/networkd-netdev-macvlan.c26
-rw-r--r--src/network/networkd-netdev-macvlan.h1
-rw-r--r--src/network/networkd-netdev-tunnel.c12
-rw-r--r--src/network/networkd-netdev-tunnel.h5
-rw-r--r--src/network/networkd-netdev-vxlan.c8
-rw-r--r--src/network/networkd-netdev-vxlan.h1
-rw-r--r--src/network/networkd-netdev.c2
-rw-r--r--src/network/networkd-netdev.h2
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c1
-rw-r--r--src/network/networkd-wait-online-manager.c8
-rw-r--r--src/nspawn/nspawn.c11
-rw-r--r--src/nss-myhostname/nss-myhostname.c3
-rw-r--r--src/resolve/dns-type.c2
-rw-r--r--src/resolve/resolved-dns-cache.c4
-rw-r--r--src/resolve/resolved-dns-packet.c51
-rw-r--r--src/resolve/resolved-dns-question.c40
-rw-r--r--src/resolve/resolved-dns-question.h2
-rw-r--r--src/resolve/resolved-dns-scope.c84
-rw-r--r--src/resolve/resolved-dns-scope.h9
-rw-r--r--src/resolve/resolved-dns-server.c22
-rw-r--r--src/resolve/resolved-dns-server.h6
-rw-r--r--src/resolve/resolved-dns-transaction.c244
-rw-r--r--src/resolve/resolved-dns-transaction.h20
-rw-r--r--src/resolve/resolved-manager.c46
-rw-r--r--src/resolve/resolved-manager.h1
-rw-r--r--src/run/run.c8
-rw-r--r--src/shared/ask-password-api.c8
-rw-r--r--src/shared/bus-util.c4
-rw-r--r--src/shared/conf-parser.c3
-rw-r--r--src/shared/dns-domain.c95
-rw-r--r--src/shared/dns-domain.h2
-rw-r--r--src/shared/install.c5
-rw-r--r--src/sysctl/sysctl.c4
-rw-r--r--src/systemctl/systemctl.c3
-rw-r--r--src/systemd/sd-netlink.h1
-rw-r--r--src/sysusers/sysusers.c47
-rw-r--r--src/test/test-af-list.c48
-rw-r--r--src/test/test-arphrd-list.c48
-rw-r--r--src/test/test-bitmap.c20
-rw-r--r--src/test/test-dns-domain.c76
-rw-r--r--src/test/test-hostname-util.c12
-rw-r--r--src/test/test-list.c44
-rw-r--r--src/test/test-socket-util.c15
-rw-r--r--src/test/test-strv.c4
-rw-r--r--src/test/test-util.c89
-rw-r--r--src/timesync/timesyncd.c4
-rw-r--r--src/tmpfiles/tmpfiles.c4
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c3
-rw-r--r--src/udev/cdrom_id/cdrom_id.c4
-rw-r--r--src/udev/udev-builtin-path_id.c12
-rw-r--r--src/udev/udev-builtin-uaccess.c4
-rw-r--r--src/udev/udev-builtin.c20
-rw-r--r--src/udev/udev-node.c4
-rw-r--r--src/udev/udev-rules.c8
-rw-r--r--src/udev/udevd.c28
-rwxr-xr-xtools/compile-unifont.py119
-rw-r--r--units/systemd-bus-proxyd.service.m4.in1
-rw-r--r--units/systemd-machined.service.in2
-rw-r--r--units/user/.gitignore1
-rw-r--r--units/user/systemd-bus-proxyd.service.in1
-rw-r--r--units/user/systemd-consoled.service.in15
227 files changed, 3399 insertions, 26211 deletions
diff --git a/.gitignore b/.gitignore
index a66c7e1775..9260943f79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,7 +66,6 @@
/systemd-cgls
/systemd-cgroups-agent
/systemd-cgtop
-/systemd-consoled
/systemd-coredump
/systemd-cryptsetup
/systemd-cryptsetup-generator
@@ -74,9 +73,7 @@
/systemd-debug-generator
/systemd-delta
/systemd-detect-virt
-/systemd-efi-boot-generator
/systemd-escape
-/systemd-evcat
/systemd-export
/systemd-firstboot
/systemd-fsck
@@ -102,7 +99,6 @@
/systemd-machine-id-commit
/systemd-machine-id-setup
/systemd-machined
-/systemd-modeset
/systemd-modules-load
/systemd-networkd
/systemd-networkd-wait-online
@@ -124,7 +120,6 @@
/systemd-sleep
/systemd-socket-proxyd
/systemd-stdio-bridge
-/systemd-subterm
/systemd-sysctl
/systemd-system-update-generator
/systemd-sysusers
@@ -142,6 +137,8 @@
/tags
/test-architecture
/test-audit-type
+/test-af-list
+/test-arphrd-list
/test-async
/test-barrier
/test-bitmap
@@ -259,15 +256,12 @@
/test-strv
/test-strxcpyx
/test-tables
-/test-term-page
-/test-term-parser
/test-terminal-util
/test-time
/test-tmpfiles
/test-udev
/test-uid-range
/test-unaligned
-/test-unifont
/test-unit-file
/test-unit-name
/test-utf8
diff --git a/CODING_STYLE b/CODING_STYLE
index dbadfbdb54..a96ddd3598 100644
--- a/CODING_STYLE
+++ b/CODING_STYLE
@@ -314,3 +314,10 @@
are always defined after more global ones. Thus, our local
definitions will never "leak" into the global header files, possibly
altering their effect due to #ifdeffery.
+
+- To implement an endless loop, use "for (;;)" rather than "while
+ (1)". The latter is a bit ugly anyway, since you probably really
+ meant "while (true)"... To avoid the discussion what the right
+ always-true expression for an infinite while() loop is our
+ recommendation is to simply write it without any such expression by
+ using "for (;;)".
diff --git a/Makefile-man.am b/Makefile-man.am
index 218a299e91..c0cebaab63 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -91,7 +91,6 @@ MANPAGES += \
man/systemd-debug-generator.8 \
man/systemd-delta.1 \
man/systemd-detect-virt.1 \
- man/systemd-efi-boot-generator.8 \
man/systemd-escape.1 \
man/systemd-fsck@.service.8 \
man/systemd-fstab-generator.8 \
@@ -2304,7 +2303,6 @@ EXTRA_DIST += \
man/systemd-debug-generator.xml \
man/systemd-delta.xml \
man/systemd-detect-virt.xml \
- man/systemd-efi-boot-generator.xml \
man/systemd-escape.xml \
man/systemd-firstboot.xml \
man/systemd-fsck@.service.xml \
diff --git a/Makefile.am b/Makefile.am
index 4f97da10c2..a8dece23b1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,9 +42,9 @@ LIBUDEV_CURRENT=7
LIBUDEV_REVISION=4
LIBUDEV_AGE=6
-LIBSYSTEMD_CURRENT=9
-LIBSYSTEMD_REVISION=0
-LIBSYSTEMD_AGE=9
+LIBSYSTEMD_CURRENT=10
+LIBSYSTEMD_REVISION=1
+LIBSYSTEMD_AGE=10
# The following four libraries only exist for compatibility reasons,
# their version info should not be bumped anymore
@@ -237,7 +237,6 @@ AM_CPPFLAGS = \
-I $(top_srcdir)/src/libsystemd/sd-hwdb \
-I $(top_srcdir)/src/libsystemd/sd-device \
-I $(top_srcdir)/src/libsystemd-network \
- -I $(top_srcdir)/src/libsystemd-terminal \
$(OUR_CPPFLAGS)
AM_CFLAGS = $(OUR_CFLAGS)
@@ -1439,6 +1438,8 @@ tests += \
test-cap-list \
test-sigbus \
test-verbs \
+ test-af-list \
+ test-arphrd-list \
test-dns-domain
EXTRA_DIST += \
@@ -2055,6 +2056,18 @@ test_bus_policy_LDADD = \
libbus-proxy-core.la \
libshared.la
+test_af_list_SOURCES = \
+ src/test/test-af-list.c
+
+test_af_list_LDADD = \
+ libbasic.la
+
+test_arphrd_list_SOURCES = \
+ src/test/test-arphrd-list.c
+
+test_arphrd_list_LDADD = \
+ libbasic.la
+
# ------------------------------------------------------------------------------
## .PHONY so it always rebuilds it
.PHONY: coverage lcov-run lcov-report coverage-sync
@@ -2434,16 +2447,6 @@ EXTRA_DIST += \
# ------------------------------------------------------------------------------
if ENABLE_EFI
-systemgenerator_PROGRAMS += \
- systemd-efi-boot-generator
-
-systemd_efi_boot_generator_SOURCES = \
- src/efi-boot-generator/efi-boot-generator.c
-
-systemd_efi_boot_generator_LDADD = \
- libshared.la
-
-# ------------------------------------------------------------------------------
if HAVE_BLKID
bootctl_SOURCES = \
src/boot/bootctl.c
@@ -2537,13 +2540,15 @@ systemd_boot_headers = \
src/boot/efi/util.h \
src/boot/efi/console.h \
src/boot/efi/graphics.h \
- src/boot/efi/pefile.h
+ src/boot/efi/pefile.h \
+ src/boot/efi/disk.h
systemd_boot_sources = \
src/boot/efi/util.c \
src/boot/efi/console.c \
src/boot/efi/graphics.c \
src/boot/efi/pefile.c \
+ src/boot/efi/disk.c \
src/boot/efi/boot.c
EXTRA_DIST += $(systemd_boot_sources) $(systemd_boot_headers)
@@ -2576,6 +2581,7 @@ endif
stub_headers = \
src/boot/efi/util.h \
src/boot/efi/pefile.h \
+ src/boot/efi/disk.h \
src/boot/efi/graphics.h \
src/boot/efi/splash.h \
src/boot/efi/linux.h
@@ -2583,6 +2589,7 @@ stub_headers = \
stub_sources = \
src/boot/efi/util.c \
src/boot/efi/pefile.c \
+ src/boot/efi/disk.c \
src/boot/efi/graphics.c \
src/boot/efi/splash.c \
src/boot/efi/linux.c \
@@ -3315,145 +3322,6 @@ manual_tests += \
test-pppoe
# ------------------------------------------------------------------------------
-if ENABLE_TERMINAL
-noinst_LTLIBRARIES += \
- libsystemd-terminal.la
-
-rootlibexec_PROGRAMS += \
- systemd-consoled
-
-noinst_PROGRAMS += \
- systemd-evcat \
- systemd-modeset \
- systemd-subterm
-
-pkgdata_DATA = \
- src/libsystemd-terminal/unifont-glyph-array.bin
-
-nodist_userunit_DATA += \
- units/user/systemd-consoled.service
-
-USER_DEFAULT_TARGET_WANTS += \
- systemd-consoled.service
-
-tests += \
- test-term-page \
- test-term-parser \
- test-unifont
-endif
-
-EXTRA_DIST += \
- units/user/systemd-consoled.service.in
-
-libsystemd_terminal_la_CFLAGS = \
- $(AM_CFLAGS) \
- $(TERMINAL_CFLAGS)
-
-libsystemd_terminal_la_SOURCES = \
- src/libsystemd-terminal/grdev.h \
- src/libsystemd-terminal/grdev-internal.h \
- src/libsystemd-terminal/grdev.c \
- src/libsystemd-terminal/grdev-drm.c \
- src/libsystemd-terminal/idev.h \
- src/libsystemd-terminal/idev-internal.h \
- src/libsystemd-terminal/idev.c \
- src/libsystemd-terminal/idev-evdev.c \
- src/libsystemd-terminal/idev-keyboard.c \
- src/libsystemd-terminal/sysview.h \
- src/libsystemd-terminal/sysview-internal.h \
- src/libsystemd-terminal/sysview.c \
- src/libsystemd-terminal/term.h \
- src/libsystemd-terminal/term-internal.h \
- src/libsystemd-terminal/term-charset.c \
- src/libsystemd-terminal/term-page.c \
- src/libsystemd-terminal/term-parser.c \
- src/libsystemd-terminal/term-screen.c \
- src/libsystemd-terminal/term-wcwidth.c \
- src/libsystemd-terminal/unifont.h \
- src/libsystemd-terminal/unifont-def.h \
- src/libsystemd-terminal/unifont.c
-
-libsystemd_terminal_la_LIBADD = \
- libshared.la \
- $(TERMINAL_LIBS)
-
-systemd_consoled_CFLAGS = \
- $(AM_CFLAGS) \
- $(TERMINAL_CFLAGS)
-
-systemd_consoled_SOURCES = \
- src/console/consoled.h \
- src/console/consoled.c \
- src/console/consoled-display.c \
- src/console/consoled-manager.c \
- src/console/consoled-session.c \
- src/console/consoled-terminal.c \
- src/console/consoled-workspace.c
-
-systemd_consoled_LDADD = \
- libsystemd-terminal.la \
- libshared.la \
- $(TERMINAL_LIBS)
-
-systemd_evcat_CFLAGS = \
- $(AM_CFLAGS) \
- $(TERMINAL_CFLAGS)
-
-systemd_evcat_SOURCES = \
- src/libsystemd-terminal/evcat.c
-
-systemd_evcat_LDADD = \
- libsystemd-terminal.la \
- libshared.la \
- $(TERMINAL_LIBS)
-
-systemd_modeset_CFLAGS = \
- $(AM_CFLAGS) \
- $(TERMINAL_CFLAGS)
-
-systemd_modeset_SOURCES = \
- src/libsystemd-terminal/modeset.c
-
-systemd_modeset_LDADD = \
- libsystemd-terminal.la \
- libshared.la \
- $(TERMINAL_LIBS)
-
-systemd_subterm_SOURCES = \
- src/libsystemd-terminal/subterm.c
-
-systemd_subterm_LDADD = \
- libsystemd-terminal.la \
- libshared.la
-
-test_term_page_SOURCES = \
- src/libsystemd-terminal/test-term-page.c
-
-test_term_page_LDADD = \
- libsystemd-terminal.la \
- libshared.la
-
-test_term_parser_SOURCES = \
- src/libsystemd-terminal/test-term-parser.c
-
-test_term_parser_LDADD = \
- libsystemd-terminal.la \
- libshared.la
-
-test_unifont_SOURCES = \
- src/libsystemd-terminal/test-unifont.c
-
-test_unifont_LDADD = \
- libsystemd-terminal.la \
- libshared.la
-
-src/libsystemd-terminal/unifont-glyph-array.bin: tools/compile-unifont.py $(UNIFONT)
- $(AM_V_GEN)$(PYTHON) $< <$(UNIFONT) >$@
-
-EXTRA_DIST += \
- tools/compile-unifont.py
-
-# ------------------------------------------------------------------------------
include_HEADERS += \
src/libudev/libudev.h
@@ -6171,12 +6039,12 @@ git-tag:
.PHONY: git-tar
git-tar:
- git archive --format=tar --prefix=systemd-$(VERSION)/ HEAD | xz > systemd-$(VERSION).tar.xz
+ git archive --format=tar --prefix=systemd-$(VERSION)/ HEAD | gzip > systemd-$(VERSION).tar.gz
www_target = www.freedesktop.org:/srv/www.freedesktop.org/www/software/systemd
.PHONY: doc-sync
-doc-sync: all destdir-sphinx
+doc-sync: all
rsync -rlv --delete-excluded --include="*.html" --exclude="*" --omit-dir-times man/ $(www_target)/man/
.PHONY: gardel
diff --git a/NEWS b/NEWS
index 3f80af3783..97dd000d4e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,85 @@
systemd System and Service Manager
+CHANGES WITH 224:
+
+ * The systemd-efi-boot-generator functionality was merged into
+ systemd-gpt-auto-generator.
+
+ * systemd-networkd now supports Group Policy for vxlan devices. It can
+ be enabled via the new boolean configuration option called
+ 'GroupPolicyExtension='.
+
+ Contributions from: Andreas Kempf, Christian Hesse, Daniel Mack, David
+ Herrmann, Herman Fries, Johannes Nixdorf, Kay Sievers, Lennart
+ Poettering, Peter Hutterer, Susant Sahani, Tom Gundersen
+
+ -- Berlin, 2015-07-31
+
+CHANGES WITH 223:
+
+ * The python-systemd code has been removed from the systemd repository.
+ A new repository has been created which accommodates the code from
+ now on, and we kindly ask distributions to create a separate package
+ for this: https://github.com/systemd/python-systemd
+
+ * The systemd daemon will now reload its main configuration
+ (/etc/systemd/system.conf) on daemon-reload.
+
+ * sd-dhcp now exposes vendor specific extensions via
+ sd_dhcp_lease_get_vendor_specific().
+
+ * systemd-networkd gained a number of new configuration options.
+
+ - A new boolean configuration option for TAP devices called
+ 'VNetHeader='. If set, the IFF_VNET_HDR flag is set for the
+ device, thus allowing to send and receive GSO packets.
+
+ - A new tunnel configuration option called 'CopyDSCP='.
+ If enabled, the DSCP field of ip6 tunnels is copied into the
+ decapsulated packet.
+
+ - A set of boolean bridge configuration options were added.
+ 'UseBPDU=', 'HairPin=', 'FastLeave=', 'AllowPortToBeRoot=',
+ and 'UnicastFlood=' are now parsed by networkd and applied to the
+ respective bridge link device via the respective IFLA_BRPORT_*
+ netlink attribute.
+
+ - A new string configuration option to override the hostname sent
+ to a DHCP server, called 'Hostname='. If set and 'SendHostname='
+ is true, networkd will use the configured hostname instead of the
+ system hostname when sending DHCP requests.
+
+ - A new tunnel configuration option called 'IPv6FlowLabel='. If set,
+ networkd will configure the IPv6 flow-label of the tunnel device
+ according to RFC2460.
+
+ - The 'macvtap' virtual network devices are now supported, similar to
+ the already supported 'macvlan' devices.
+
+ * systemd-resolved now implements RFC5452 to improve resilience against
+ cache poisoning. Additionally, source port randomization is enabled
+ by default to further protect against DNS spoofing attacks.
+
+ * nss-mymachines now supports translating UIDs and GIDs of running
+ containers with user-namespaces enabled. If a container 'foo'
+ translates a host uid 'UID' to the container uid 'TUID', then
+ nss-mymachines will also map uid 'UID' to/from username 'vu-foo-TUID'
+ (with 'foo' and 'TUID' replaced accordingly). Similarly, groups are
+ mapped as 'vg-foo-TGID'.
+
+ Contributions from: Beniamino Galvani, cee1, Christian Hesse, Daniel
+ Buch, Daniel Mack, daurnimator, David Herrmann, Dimitri John Ledkov,
+ HATAYAMA Daisuke, Ivan Shapovalov, Jan Alexander Steffens (heftig),
+ Johan Ouwerkerk, Jose Carlos Venegas Munoz, Karel Zak, Kay Sievers,
+ Lennart Poettering, Lidong Zhong, Martin Pitt, Michael Biebl, Michael
+ Olbrich, Michal Schmidt, Michal Sekletar, Mike Gilbert, Namhyung Kim,
+ Nick Owens, Peter Hutterer, Richard Maw, Steven Allen, Sungbae Yoo,
+ Susant Sahani, Thomas Blume, Thomas Hindoe Paaboel Andersen, Tom
+ Gundersen, Torstein Husebø, Umut Tezduyar Lindskog, Vito Caputo,
+ Vivenzio Pagliari, Zbigniew Jędrzejewski-Szmek
+
+ -- Berlin, 2015-07-29
+
CHANGES WITH 222:
* udev does not longer support the WAIT_FOR_SYSFS= key in udev rules.
@@ -411,7 +491,7 @@ CHANGES WITH 219:
decompress bz2, xz, gzip compressed downloads if necessary,
and restore sparse files on disk. The daemon uses privilege
separation to ensure the actual download logic runs with
- fewer privileges than the deamon itself. machinectl has
+ fewer privileges than the daemon itself. machinectl has
gained new commands "pull-tar", "pull-raw" and "pull-dkr" to
make the functionality of importd available to the
user. With this in place the Fedora and Ubuntu "Cloud"
@@ -502,7 +582,7 @@ CHANGES WITH 219:
* systemd now provides a way to store file descriptors
per-service in PID 1.This is useful for daemons to ensure
that fds they require are not lost during a daemon
- restart. The fds are passed to the deamon on the next
+ restart. The fds are passed to the daemon on the next
invocation in the same way socket activation fds are
passed. This is now used by journald to ensure that the
various sockets connected to all the system's stdout/stderr
diff --git a/TODO b/TODO
index 30b444331d..ab7c27c86a 100644
--- a/TODO
+++ b/TODO
@@ -69,8 +69,6 @@ Features:
* log accumulated resource usage after each service invocation
-* networkd: dhcp server: try to assign stable IP addresses based on client's MAC address
-
* nspawn: a nice way to boot up without machine id set, so that it is set at boot automatically for supporting --ephemeral. Maybe hash the host machine id together with the machine name to generate the machine id for the container
* logind: rename session scope so that it includes the UID. THat way
@@ -96,8 +94,6 @@ Features:
* nspawn: as soon as networkd has a bus interface, hook up --network-interface=, --network-bridge= with networkd, to trigger netdev creation should an interface be missing
-* networkd: make DHCP server IP range configurable, including only with a single IP address
-
* rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring
to unicode chars, to make things more expressive.
@@ -316,6 +312,8 @@ Features:
(throughout the codebase, not only PID1)
* networkd:
+ - make DHCP server IP range configurable, including only with a single IP address
+ - dhcp server: try to assign stable IP addresses based on client's MAC address
- add LLDP client side support
- the DHCP lease data (such as NTP/DNS) is still made available when
a carrier is lost on a link. It should be removed instantly.
@@ -333,6 +331,7 @@ Features:
- allow Name= to be specified repeatedly in the [Match] section. Maybe also
support Name=foo*|bar*|baz ?
- duplicate address check for static IPs (like ARPCHECK in network-scripts)
+ - allow DUID/IAID to be customized, see issue #394.
* resolved:
- put networkd events and rtnl events at a higher priority, so that
@@ -691,10 +690,6 @@ Features:
* If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle().
-* fedup: add --unit to systemctl switch-root somehow
-* fedup: do not delete initrd on switch-root
-* fedup: generator
-
* clean up date formatting and parsing so that all absolute/relative timestamps we format can also be parsed
* on shutdown: move utmp, wall, audit logic all into PID 1 (or logind?), get rid of systemd-update-utmp-runlevel
diff --git a/autogen.sh b/autogen.sh
index 2d4acdfef1..607a9682dd 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -57,9 +57,6 @@ cd $oldpwd
if [ "x$1" = "xc" ]; then
$topdir/configure CFLAGS='-g -O0 -ftrapv' --enable-compat-libs --enable-kdbus $args
make clean
-elif [ "x$1" = "xt" ]; then
- $topdir/configure CFLAGS='-g -O0 -ftrapv' --enable-compat-libs --enable-kdbus --enable-terminal $args
- make clean
elif [ "x$1" = "xg" ]; then
$topdir/configure CFLAGS='-g -Og -ftrapv' --enable-compat-libs --enable-kdbus $args
make clean
diff --git a/configure.ac b/configure.ac
index 2fddf29f36..f1ce9ff300 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@
AC_PREREQ([2.64])
AC_INIT([systemd],
- [222],
+ [224],
[http://github.com/systemd/systemd/issues],
[systemd],
[http://www.freedesktop.org/wiki/Software/systemd])
@@ -1151,10 +1151,10 @@ AS_IF([test "x$enable_gnuefi" != "xno"], [
have_efi_lds=no
AC_ARG_WITH(efi-ldsdir,
AS_HELP_STRING([--with-efi-ldsdir=PATH], [Path to EFI lds directory]),
- [EFI_LDS_DIR="$withval" && AC_CHECK_FILE([${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds],
+ [EFI_LDS_DIR="$withval" && AS_IF([test -f "${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds"],
[have_efi_lds=yes])],
[AS_FOR([DIR], [EFI_LDS_DIR], ["${EFI_LIB_DIR}/gnuefi" "${EFI_LIB_DIR}"],
- [AC_CHECK_FILE([${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds],
+ [AS_IF([test -f "${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds"],
[have_efi_lds=yes && break])])])
AS_IF([test "x$have_efi_lds" = xyes],
[AC_SUBST([EFI_LDS_DIR])],
@@ -1171,27 +1171,6 @@ AS_IF([test "x$enable_gnuefi" != "xno"], [
AM_CONDITIONAL(HAVE_GNUEFI, [test "x$have_gnuefi" = xyes])
# ------------------------------------------------------------------------------
-AC_ARG_WITH(unifont,
- AS_HELP_STRING([--with-unifont=PATH],
- [Path to unifont.hex]),
- [UNIFONT="$withval"],
- [UNIFONT="/usr/share/unifont/unifont.hex"])
-AC_SUBST(UNIFONT)
-
-have_terminal=no
-have_unifont=no
-AC_ARG_ENABLE(terminal, AS_HELP_STRING([--enable-terminal], [enable terminal support]))
-if test "x$enable_terminal" = "xyes"; then
- PKG_CHECK_MODULES([TERMINAL], [ libevdev >= 1.2 xkbcommon >= 0.5 libdrm >= 2.4], [have_terminal=yes])
- AC_CHECK_FILE($UNIFONT, [have_unifont=yes])
- AS_IF([test "x$have_terminal" != xyes -o "x$have_unifont" != "xyes" -a "x$enable_terminal" = xyes],
- [AC_MSG_ERROR([*** terminal support requested but required dependencies not available])],
- [test "x$have_terminal" = xyes -a "x$have_unifont" = "xyes"],
- [AC_DEFINE(ENABLE_TERMINAL, 1, [Define if terminal support is to be enabled])])
-fi
-AM_CONDITIONAL(ENABLE_TERMINAL, [test "x$have_terminal" = "xyes" -a "x$have_unifont" = "xyes"])
-
-# ------------------------------------------------------------------------------
have_kdbus=no
AC_ARG_ENABLE(kdbus, AS_HELP_STRING([--disable-kdbus], [do not connect to kdbus by default]))
if test "x$enable_kdbus" != "xno"; then
@@ -1547,7 +1526,6 @@ AC_MSG_RESULT([
dbus: ${have_dbus}
nss-myhostname: ${have_myhostname}
hwdb: ${enable_hwdb}
- terminal: ${have_terminal}
kdbus: ${have_kdbus}
Python: ${have_python}
man pages: ${have_manpages}
diff --git a/hwdb/20-OUI.hwdb b/hwdb/20-OUI.hwdb
index cbf70da880..eb34e3efcc 100644
--- a/hwdb/20-OUI.hwdb
+++ b/hwdb/20-OUI.hwdb
@@ -23990,7 +23990,7 @@ OUI:000D96*
ID_OUI_FROM_DATABASE=Vtera Technology Inc.
OUI:000D97*
- ID_OUI_FROM_DATABASE=Tropos Networks, Inc.
+ ID_OUI_FROM_DATABASE=ABB Inc./Tropos
OUI:000D98*
ID_OUI_FROM_DATABASE=S.W.A.C. Schmitt-Walter Automation Consult GmbH
@@ -38138,7 +38138,7 @@ OUI:00200E*
ID_OUI_FROM_DATABASE=SATELLITE TECHNOLOGY MGMT, INC
OUI:00200F*
- ID_OUI_FROM_DATABASE=TANBAC CO., LTD.
+ ID_OUI_FROM_DATABASE=EBRAINS Inc
OUI:002010*
ID_OUI_FROM_DATABASE=JEOL SYSTEM TECHNOLOGY CO. LTD
@@ -40385,7 +40385,7 @@ OUI:0022FE*
ID_OUI_FROM_DATABASE=Advanced Illumination
OUI:0022FF*
- ID_OUI_FROM_DATABASE=iWDL Technologies
+ ID_OUI_FROM_DATABASE=NIVIS LLC
OUI:002300*
ID_OUI_FROM_DATABASE=Cayee Computer Ltd.
@@ -45856,6 +45856,9 @@ OUI:005218*
OUI:0054AF*
ID_OUI_FROM_DATABASE=Continental Automotive Systems Inc.
+OUI:0055DA*
+ ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
+
OUI:005907*
ID_OUI_FROM_DATABASE=LenovoEMC Products USA, LLC
@@ -46657,6 +46660,9 @@ OUI:006B9E*
OUI:006BA0*
ID_OUI_FROM_DATABASE=SHENZHEN UNIVERSAL INTELLISYS PTE LTD
+OUI:006D52*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:006DFB*
ID_OUI_FROM_DATABASE=Vutrix (UK) Ltd
@@ -51862,6 +51868,9 @@ OUI:045FA7*
OUI:046169*
ID_OUI_FROM_DATABASE=MEDIA GLOBAL LINKS CO., LTD.
+OUI:046273*
+ ID_OUI_FROM_DATABASE=Cisco Systems
+
OUI:0462D7*
ID_OUI_FROM_DATABASE=ALSTOM HYDRO FRANCE
@@ -51874,6 +51883,9 @@ OUI:046785*
OUI:0469F8*
ID_OUI_FROM_DATABASE=Apple
+OUI:046C9D*
+ ID_OUI_FROM_DATABASE=Cisco Systems
+
OUI:046D42*
ID_OUI_FROM_DATABASE=Bryston Ltd.
@@ -52534,6 +52546,9 @@ OUI:0808EA*
OUI:0809B6*
ID_OUI_FROM_DATABASE=Masimo Corp
+OUI:080A4E*
+ ID_OUI_FROM_DATABASE=Planet Bingo® — 3rd Rock Gaming®
+
OUI:080C0B*
ID_OUI_FROM_DATABASE=SysMik GmbH Dresden
@@ -52640,7 +52655,7 @@ OUI:084027*
ID_OUI_FROM_DATABASE=Gridstore Inc.
OUI:084656*
- ID_OUI_FROM_DATABASE=VODALYS Ingénierie
+ ID_OUI_FROM_DATABASE=VEO-LABS
OUI:08482C*
ID_OUI_FROM_DATABASE=Raycore Taiwan Co., LTD.
@@ -52735,6 +52750,9 @@ OUI:0881F4*
OUI:08863B*
ID_OUI_FROM_DATABASE=Belkin International, Inc.
+OUI:088C2C*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:088DC8*
ID_OUI_FROM_DATABASE=Ryowa Electronics Co.,Ltd
@@ -52927,6 +52945,9 @@ OUI:0C17F1*
OUI:0C191F*
ID_OUI_FROM_DATABASE=Inform Electronik
+OUI:0C1A10*
+ ID_OUI_FROM_DATABASE=Acoustic Stream
+
OUI:0C1DAF*
ID_OUI_FROM_DATABASE=Beijing Xiaomi communications co.,ltd
@@ -52996,6 +53017,9 @@ OUI:0C51F7*
OUI:0C54A5*
ID_OUI_FROM_DATABASE=PEGATRON CORPORATION
+OUI:0C54B9*
+ ID_OUI_FROM_DATABASE=Alcatel-Lucent
+
OUI:0C5521*
ID_OUI_FROM_DATABASE=Axiros GmbH
@@ -53065,6 +53089,9 @@ OUI:0C84DC*
OUI:0C8525*
ID_OUI_FROM_DATABASE=CISCO SYSTEMS, INC.
+OUI:0C8610*
+ ID_OUI_FROM_DATABASE=Juniper networks
+
OUI:0C8910*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,LTD
@@ -54556,6 +54583,9 @@ OUI:1C5C60*
OUI:1C5FFF*
ID_OUI_FROM_DATABASE=Beijing Ereneben Information Technology Co.,Ltd Shenzhen Branch
+OUI:1C60DE*
+ ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
+
OUI:1C62B8*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -55222,6 +55252,9 @@ OUI:242642*
OUI:242FFA*
ID_OUI_FROM_DATABASE=Toshiba Global Commerce Solutions
+OUI:243184*
+ ID_OUI_FROM_DATABASE=SHARP Corporation
+
OUI:24374C*
ID_OUI_FROM_DATABASE=Cisco SPVTG
@@ -55426,6 +55459,9 @@ OUI:24D2CC*
OUI:24D921*
ID_OUI_FROM_DATABASE=Avaya, Inc
+OUI:24DA9B*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:24DAB6*
ID_OUI_FROM_DATABASE=Sistemas de Gestión Energética S.A. de C.V
@@ -55501,6 +55537,9 @@ OUI:280CB8*
OUI:280DFC*
ID_OUI_FROM_DATABASE=Sony Computer Entertainment Inc.
+OUI:280E8B*
+ ID_OUI_FROM_DATABASE=Beijing Spirit Technology Development Co., Ltd.
+
OUI:28107B*
ID_OUI_FROM_DATABASE=D-Link International
@@ -55906,6 +55945,9 @@ OUI:2C1984*
OUI:2C1A31*
ID_OUI_FROM_DATABASE=Electronics Company Limited
+OUI:2C1BC8*
+ ID_OUI_FROM_DATABASE=Hunan Topview Network System CO.,LTD
+
OUI:2C1EEA*
ID_OUI_FROM_DATABASE=AERODEV
@@ -56020,6 +56062,9 @@ OUI:2C5A05*
OUI:2C5AA3*
ID_OUI_FROM_DATABASE=PROMATE ELECTRONIC CO.LTD
+OUI:2C5BB8*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD.
+
OUI:2C5BE1*
ID_OUI_FROM_DATABASE=Centripetal Networks, Inc
@@ -56134,6 +56179,9 @@ OUI:2CABA4*
OUI:2CAD13*
ID_OUI_FROM_DATABASE=SHENZHEN ZHILU TECHNOLOGY CO.,LTD
+OUI:2CAE2B*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:2CB05D*
ID_OUI_FROM_DATABASE=NETGEAR
@@ -56368,6 +56416,9 @@ OUI:30595B*
OUI:3059B7*
ID_OUI_FROM_DATABASE=Microsoft
+OUI:305A3A*
+ ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+
OUI:305D38*
ID_OUI_FROM_DATABASE=Beissbarth
@@ -56657,7 +56708,7 @@ OUI:3438AF*
ID_OUI_FROM_DATABASE=Inlab Software GmbH
OUI:343D98*
- ID_OUI_FROM_DATABASE=Fujian JinQianMao Electronic Technology Co.,Ltd.
+ ID_OUI_FROM_DATABASE=JinQianMao Technology Co.,Ltd.
OUI:3440B5*
ID_OUI_FROM_DATABASE=IBM
@@ -57052,6 +57103,12 @@ OUI:381C23*
OUI:381C4A*
ID_OUI_FROM_DATABASE=SIMCom Wireless Solutions Co.,Ltd.
+OUI:382056*
+ ID_OUI_FROM_DATABASE=Cisco Systems
+
+OUI:382187*
+ ID_OUI_FROM_DATABASE=Midea Group Co., Ltd.
+
OUI:38229D*
ID_OUI_FROM_DATABASE=Pirelli Tyre S.p.A.
@@ -57178,6 +57235,9 @@ OUI:388AB7*
OUI:388EE7*
ID_OUI_FROM_DATABASE=Fanhattan LLC
+OUI:3891D5*
+ ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+
OUI:3891FB*
ID_OUI_FROM_DATABASE=Xenox Holding BV
@@ -57310,6 +57370,9 @@ OUI:38F098*
OUI:38F33F*
ID_OUI_FROM_DATABASE=TATSUNO CORPORATION
+OUI:38F557*
+ ID_OUI_FROM_DATABASE=JOLATA, INC.
+
OUI:38F597*
ID_OUI_FROM_DATABASE=home2net GmbH
@@ -57328,6 +57391,9 @@ OUI:38FACA*
OUI:38FEC5*
ID_OUI_FROM_DATABASE=Ellips B.V.
+OUI:38FF36*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
OUI:3C02B1*
ID_OUI_FROM_DATABASE=Creation Technologies LP
@@ -57565,6 +57631,9 @@ OUI:3C8BFE*
OUI:3C8C40*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+OUI:3C8CF8*
+ ID_OUI_FROM_DATABASE=TRENDnet, Inc.
+
OUI:3C912B*
ID_OUI_FROM_DATABASE=Vexata Inc
@@ -57628,12 +57697,18 @@ OUI:3CB15B*
OUI:3CB17F*
ID_OUI_FROM_DATABASE=Wattwatchers Pty Ld
+OUI:3CB72B*
+ ID_OUI_FROM_DATABASE=PLUMgrid Inc
+
OUI:3CB792*
ID_OUI_FROM_DATABASE=Hitachi Maxell, Ltd., Optronics Division
OUI:3CB9A6*
ID_OUI_FROM_DATABASE=Belden Deutschland GmbH
+OUI:3CBBFD*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:3CBDD8*
ID_OUI_FROM_DATABASE=LG ELECTRONICS INC
@@ -57865,6 +57940,9 @@ OUI:40516C*
OUI:40520D*
ID_OUI_FROM_DATABASE=Pico Technology
+OUI:4054E4*
+ ID_OUI_FROM_DATABASE=Wearsafe Labs Inc
+
OUI:405539*
ID_OUI_FROM_DATABASE=CISCO SYSTEMS, INC.
@@ -57946,6 +58024,9 @@ OUI:4083DE*
OUI:408493*
ID_OUI_FROM_DATABASE=Clavister AB
+OUI:40862E*
+ ID_OUI_FROM_DATABASE=JDM MOBILE INTERNET SOLUTION CO., LTD.
+
OUI:4088E0*
ID_OUI_FROM_DATABASE=Beijing Ereneben Information Technology Limited Shenzhen Branch
@@ -58663,6 +58744,9 @@ OUI:489153*
OUI:4891F6*
ID_OUI_FROM_DATABASE=Shenzhen Reach software technology CO.,LTD
+OUI:489A42*
+ ID_OUI_FROM_DATABASE=Technomate Ltd
+
OUI:489BE2*
ID_OUI_FROM_DATABASE=SCI Innovations Ltd
@@ -58708,6 +58792,9 @@ OUI:48B9C2*
OUI:48BE2D*
ID_OUI_FROM_DATABASE=Symanitron
+OUI:48BF74*
+ ID_OUI_FROM_DATABASE=Baicells Technologies Co.,LTD
+
OUI:48C093*
ID_OUI_FROM_DATABASE=Xirrus, Inc.
@@ -58970,7 +59057,7 @@ OUI:4C73A5*
ID_OUI_FROM_DATABASE=KOVE
OUI:4C7403*
- ID_OUI_FROM_DATABASE=Mundo Reader (bq)
+ ID_OUI_FROM_DATABASE=BQ
OUI:4C7625*
ID_OUI_FROM_DATABASE=Dell Inc.
@@ -59452,6 +59539,9 @@ OUI:50C58D*
OUI:50C7BF*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:50C8E5*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:50C971*
ID_OUI_FROM_DATABASE=GN Netcom A/S
@@ -59461,6 +59551,9 @@ OUI:50C9A0*
OUI:50CCF8*
ID_OUI_FROM_DATABASE=Samsung Electro Mechanics
+OUI:50CD22*
+ ID_OUI_FROM_DATABASE=Avaya, Inc
+
OUI:50CD32*
ID_OUI_FROM_DATABASE=NanJing Chaoran Science & Technology Co.,Ltd.
@@ -59632,6 +59725,9 @@ OUI:544A05*
OUI:544A16*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:544B8C*
+ ID_OUI_FROM_DATABASE=juniper networks
+
OUI:544E90*
ID_OUI_FROM_DATABASE=Apple
@@ -59764,6 +59860,9 @@ OUI:54A619*
OUI:54A9D4*
ID_OUI_FROM_DATABASE=Minibar Systems
+OUI:54AB3A*
+ ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC.
+
OUI:54AE27*
ID_OUI_FROM_DATABASE=Apple
@@ -59932,6 +60031,9 @@ OUI:58238C*
OUI:582AF7*
ID_OUI_FROM_DATABASE=Huawei Technologies Co., Ltd
+OUI:582BDB*
+ ID_OUI_FROM_DATABASE=Pax AB
+
OUI:582EFE*
ID_OUI_FROM_DATABASE=Lighting Science Group
@@ -59953,6 +60055,9 @@ OUI:583F54*
OUI:5842E4*
ID_OUI_FROM_DATABASE=Sigma International General Medical Apparatus, LLC.
+OUI:584498*
+ ID_OUI_FROM_DATABASE=XIAOMI INC
+
OUI:58468F*
ID_OUI_FROM_DATABASE=Koncar Electronics and Informatics
@@ -60022,6 +60127,9 @@ OUI:58671A*
OUI:58677F*
ID_OUI_FROM_DATABASE=Clare Controls Inc.
+OUI:58685D*
+ ID_OUI_FROM_DATABASE=Tempo Australia Pty Ltd
+
OUI:58696C*
ID_OUI_FROM_DATABASE=Fujian Ruijie Networks co, ltd
@@ -60055,6 +60163,9 @@ OUI:587BE9*
OUI:587E61*
ID_OUI_FROM_DATABASE=Hisense Electric Co., Ltd
+OUI:587F57*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:587F66*
ID_OUI_FROM_DATABASE=Huawei Technologies Co., Ltd
@@ -60208,6 +60319,9 @@ OUI:58F387*
OUI:58F39C*
ID_OUI_FROM_DATABASE=Cisco
+OUI:58F496*
+ ID_OUI_FROM_DATABASE=Source Chain
+
OUI:58F67B*
ID_OUI_FROM_DATABASE=Xia Men UnionCore Technology LTD.
@@ -60217,6 +60331,9 @@ OUI:58F6BF*
OUI:58F98E*
ID_OUI_FROM_DATABASE=SECUDOS GmbH
+OUI:58FC73*
+ ID_OUI_FROM_DATABASE=Arria Live Media, Inc.
+
OUI:58FCDB*
ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
@@ -60331,6 +60448,9 @@ OUI:5C43D2*
OUI:5C4527*
ID_OUI_FROM_DATABASE=Juniper Networks
+OUI:5C4979*
+ ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH
+
OUI:5C4A26*
ID_OUI_FROM_DATABASE=Enguity Technology Corp
@@ -60454,6 +60574,9 @@ OUI:5CAAFD*
OUI:5CAC4C*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:5CADCF*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:5CB395*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -61049,7 +61172,7 @@ OUI:6416F0*
ID_OUI_FROM_DATABASE=Shehzhen Huawei Communication Technologies Co., Ltd.
OUI:641A22*
- ID_OUI_FROM_DATABASE=Heliospectra/Woodhill Investments
+ ID_OUI_FROM_DATABASE=Heliospectra AB
OUI:641C67*
ID_OUI_FROM_DATABASE=DIGIBRAS INDUSTRIA DO BRASILS/A
@@ -61684,6 +61807,9 @@ OUI:68A3C4*
OUI:68A40E*
ID_OUI_FROM_DATABASE=BSH Bosch and Siemens Home Appliances GmbH
+OUI:68A828*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:68A86D*
ID_OUI_FROM_DATABASE=Apple
@@ -62008,6 +62134,9 @@ OUI:6C90B1*
OUI:6C92BF*
ID_OUI_FROM_DATABASE=Inspur Electronic Information Industry Co.,Ltd.
+OUI:6C9354*
+ ID_OUI_FROM_DATABASE=Yaojin Technology (Shenzhen) Co., LTD.
+
OUI:6C94F8*
ID_OUI_FROM_DATABASE=Apple
@@ -62290,6 +62419,9 @@ OUI:7041B7*
OUI:704642*
ID_OUI_FROM_DATABASE=CHYNG HONG ELECTRONIC CO., LTD.
+OUI:70480F*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:704AAE*
ID_OUI_FROM_DATABASE=Xstream Flow (Pty) Ltd
@@ -62614,6 +62746,9 @@ OUI:741489*
OUI:7415E2*
ID_OUI_FROM_DATABASE=Tri-Sen Systems Corporation
+OUI:741865*
+ ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd
+
OUI:7419F8*
ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
@@ -62719,6 +62854,9 @@ OUI:7465D1*
OUI:746630*
ID_OUI_FROM_DATABASE=T:mi Ytti
+OUI:7467F7*
+ ID_OUI_FROM_DATABASE=Zebra Technologoes
+
OUI:746A3A*
ID_OUI_FROM_DATABASE=Aperi Corporation
@@ -62731,6 +62869,9 @@ OUI:746A8F*
OUI:746B82*
ID_OUI_FROM_DATABASE=MOVEK
+OUI:746F19*
+ ID_OUI_FROM_DATABASE=ICARVISIONS (SHENZHEN) TECHNOLOGY CO., LTD.
+
OUI:746F3D*
ID_OUI_FROM_DATABASE=Contec GmbH
@@ -62846,7 +62987,7 @@ OUI:74B00C*
ID_OUI_FROM_DATABASE=Network Video Technologies, Inc
OUI:74B9EB*
- ID_OUI_FROM_DATABASE=Fujian JinQianMao Electronic Technology Co.,Ltd
+ ID_OUI_FROM_DATABASE=JinQianMao Technology Co.,Ltd.
OUI:74BADB*
ID_OUI_FROM_DATABASE=Longconn Electornics(shenzhen)Co.,Ltd
@@ -63343,6 +63484,9 @@ OUI:78D5B5*
OUI:78D66F*
ID_OUI_FROM_DATABASE=Aristocrat Technologies Australia Pty. Ltd.
+OUI:78D6B2*
+ ID_OUI_FROM_DATABASE=Toshiba
+
OUI:78D6F0*
ID_OUI_FROM_DATABASE=Samsung Electro Mechanics
@@ -63433,6 +63577,9 @@ OUI:78FF57*
OUI:7C0187*
ID_OUI_FROM_DATABASE=Curtis Instruments, Inc.
+OUI:7C0191*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:7C02BC*
ID_OUI_FROM_DATABASE=Hansung Electronics Co. LTD
@@ -63616,6 +63763,9 @@ OUI:7C6FF8*
OUI:7C70BC*
ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
+OUI:7C7176*
+ ID_OUI_FROM_DATABASE=Wuxi iData Technology Company Ltd.
+
OUI:7C72E4*
ID_OUI_FROM_DATABASE=Unikey Technologies
@@ -64138,6 +64288,9 @@ OUI:80B32A*
OUI:80B686*
ID_OUI_FROM_DATABASE=Huawei Technologies Co., Ltd
+OUI:80B709*
+ ID_OUI_FROM_DATABASE=Viptela, Inc
+
OUI:80B95C*
ID_OUI_FROM_DATABASE=ELFTECH Co., Ltd.
@@ -64192,6 +64345,9 @@ OUI:80D21D*
OUI:80D433*
ID_OUI_FROM_DATABASE=LzLabs GmbH
+OUI:80D605*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:80D733*
ID_OUI_FROM_DATABASE=QSR Automations, Inc.
@@ -64261,6 +64417,9 @@ OUI:840B2D*
OUI:840F45*
ID_OUI_FROM_DATABASE=Shanghai GMT Digital Technologies Co., Ltd
+OUI:84100D*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:84119E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -64559,7 +64718,7 @@ OUI:84C7A9*
ID_OUI_FROM_DATABASE=C3PO S.A.
OUI:84C8B1*
- ID_OUI_FROM_DATABASE=Incognito Software Inc.
+ ID_OUI_FROM_DATABASE=Incognito Software Systems Inc.
OUI:84C9B2*
ID_OUI_FROM_DATABASE=D-Link International
@@ -64573,6 +64732,9 @@ OUI:84D32A*
OUI:84D4C8*
ID_OUI_FROM_DATABASE=Widex A/S
+OUI:84D6D0*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:84D9C8*
ID_OUI_FROM_DATABASE=Unipattern Co.,
@@ -64642,6 +64804,9 @@ OUI:880355*
OUI:880905*
ID_OUI_FROM_DATABASE=MTMCommunications
+OUI:8809AF*
+ ID_OUI_FROM_DATABASE=Masimo Corp.
+
OUI:880F10*
ID_OUI_FROM_DATABASE=Huami Information Technology Co.,Ltd.
@@ -65197,6 +65362,9 @@ OUI:8C9236*
OUI:8C94CF*
ID_OUI_FROM_DATABASE=Encell Technology, Inc.
+OUI:8C99E6*
+ ID_OUI_FROM_DATABASE=TCT Mobile Limited
+
OUI:8CA048*
ID_OUI_FROM_DATABASE=Beijing NeTopChip Technology Co.,LTD
@@ -65287,6 +65455,9 @@ OUI:8CDF9D*
OUI:8CE081*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:8CE2DA*
+ ID_OUI_FROM_DATABASE=Circle Media Inc
+
OUI:8CE78C*
ID_OUI_FROM_DATABASE=DK Networks
@@ -65575,6 +65746,9 @@ OUI:908D1D*
OUI:908D6C*
ID_OUI_FROM_DATABASE=Apple
+OUI:908D78*
+ ID_OUI_FROM_DATABASE=D-Link International
+
OUI:908FCF*
ID_OUI_FROM_DATABASE=UNO System Co., Ltd
@@ -65659,6 +65833,9 @@ OUI:90C682*
OUI:90C792*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:90C99B*
+ ID_OUI_FROM_DATABASE=Recore Systems
+
OUI:90CC24*
ID_OUI_FROM_DATABASE=Synaptics, Inc
@@ -65965,6 +66142,9 @@ OUI:94AE61*
OUI:94AEE3*
ID_OUI_FROM_DATABASE=Belden Hirschmann Industries (Suzhou) Ltd.
+OUI:94B10A*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:94B40F*
ID_OUI_FROM_DATABASE=Aruba Networks
@@ -66298,6 +66478,9 @@ OUI:988389*
OUI:9886B1*
ID_OUI_FROM_DATABASE=Flyaudio corporation (China)
+OUI:988744*
+ ID_OUI_FROM_DATABASE=Wuxi Hongda Science and Technology Co.,LTD
+
OUI:9889ED*
ID_OUI_FROM_DATABASE=Anadem Information Inc.
@@ -66391,6 +66574,9 @@ OUI:98E165*
OUI:98E79A*
ID_OUI_FROM_DATABASE=Foxconn(NanJing) Communication Co.,Ltd.
+OUI:98E848*
+ ID_OUI_FROM_DATABASE=Axiim
+
OUI:98EC65*
ID_OUI_FROM_DATABASE=Cosesy ApS
@@ -66901,6 +67087,9 @@ OUI:A03A75*
OUI:A03B1B*
ID_OUI_FROM_DATABASE=Inspire Tech
+OUI:A03E6B*
+ ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
+
OUI:A04025*
ID_OUI_FROM_DATABASE=Actioncable, Inc.
@@ -67219,6 +67408,9 @@ OUI:A0F849*
OUI:A0F895*
ID_OUI_FROM_DATABASE=Tinno Mobile Technology Corp
+OUI:A0F9E0*
+ ID_OUI_FROM_DATABASE=VIVATEL COMPANY LIMITED
+
OUI:A0FC6E*
ID_OUI_FROM_DATABASE=Telegrafia a.s.
@@ -67420,6 +67612,9 @@ OUI:A47E39*
OUI:A481EE*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:A48431*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:A4856B*
ID_OUI_FROM_DATABASE=Q Electronics Ltd
@@ -67507,6 +67702,9 @@ OUI:A4B2A7*
OUI:A4B36A*
ID_OUI_FROM_DATABASE=JSC SDO Chromatec
+OUI:A4B805*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:A4B818*
ID_OUI_FROM_DATABASE=PENTA Gesellschaft für elektronische Industriedatenverarbeitung mbH
@@ -67543,6 +67741,9 @@ OUI:A4C494*
OUI:A4C7DE*
ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd.
+OUI:A4CC32*
+ ID_OUI_FROM_DATABASE=Inficomm Co., Ltd
+
OUI:A4D094*
ID_OUI_FROM_DATABASE=Erwin Peters Systemtechnik GmbH
@@ -68479,6 +68680,9 @@ OUI:B05706*
OUI:B058C4*
ID_OUI_FROM_DATABASE=Broadcast Microwave Services, Inc
+OUI:B05ADA*
+ ID_OUI_FROM_DATABASE=Hewlett Packard
+
OUI:B05B1F*
ID_OUI_FROM_DATABASE=THERMO FISHER SCIENTIFIC S.P.A.
@@ -68941,6 +69145,9 @@ OUI:B4994C*
OUI:B499BA*
ID_OUI_FROM_DATABASE=Hewlett-Packard Company
+OUI:B49D0B*
+ ID_OUI_FROM_DATABASE=BQ
+
OUI:B49DB4*
ID_OUI_FROM_DATABASE=Axion Technologies Inc.
@@ -69094,6 +69301,9 @@ OUI:B8098A*
OUI:B80B9D*
ID_OUI_FROM_DATABASE=ROPEX Industrie-Elektronik GmbH
+OUI:B813E9*
+ ID_OUI_FROM_DATABASE=Trace Live Network
+
OUI:B81413*
ID_OUI_FROM_DATABASE=Keen High Holding(HK) Ltd.
@@ -69586,6 +69796,9 @@ OUI:BC305B*
OUI:BC307D*
ID_OUI_FROM_DATABASE=Wistron Neweb Corp.
+OUI:BC307E*
+ ID_OUI_FROM_DATABASE=Wistron Neweb Corp
+
OUI:BC3400*
ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
@@ -69685,6 +69898,9 @@ OUI:BC6A2F*
OUI:BC6B4D*
ID_OUI_FROM_DATABASE=Alcatel-Lucent
+OUI:BC6C21*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:BC6E64*
ID_OUI_FROM_DATABASE=Sony Mobile Communications AB
@@ -69757,6 +69973,9 @@ OUI:BC9889*
OUI:BC99BC*
ID_OUI_FROM_DATABASE=FonSee Technology Inc.
+OUI:BC9C31*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:BC9CC5*
ID_OUI_FROM_DATABASE=Beijing Huafei Technology Co., Ltd.
@@ -69826,6 +70045,9 @@ OUI:BCCD45*
OUI:BCCFCC*
ID_OUI_FROM_DATABASE=HTC Corporation
+OUI:BCD11F*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:BCD165*
ID_OUI_FROM_DATABASE=Cisco SPVTG
@@ -70528,6 +70750,9 @@ OUI:C49805*
OUI:C49A02*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communicaitons)
+OUI:C49E41*
+ ID_OUI_FROM_DATABASE=G24 Power Limited
+
OUI:C49FF3*
ID_OUI_FROM_DATABASE=Mciao Technologies, Inc.
@@ -70540,6 +70765,9 @@ OUI:C4AAA1*
OUI:C4AD21*
ID_OUI_FROM_DATABASE=MEDIAEDGE Corporation
+OUI:C4ADF1*
+ ID_OUI_FROM_DATABASE=GOPEACE Inc.
+
OUI:C4B512*
ID_OUI_FROM_DATABASE=General Electric Digital Energy
@@ -70549,6 +70777,9 @@ OUI:C4BA99*
OUI:C4BAA3*
ID_OUI_FROM_DATABASE=Beijing Winicssec Technologies Co., Ltd.
+OUI:C4BBEA*
+ ID_OUI_FROM_DATABASE=Pakedge Device and Software Inc
+
OUI:C4BD6A*
ID_OUI_FROM_DATABASE=SKF GmbH
@@ -70618,6 +70849,9 @@ OUI:C4EEAE*
OUI:C4EEF5*
ID_OUI_FROM_DATABASE=Oclaro, Inc.
+OUI:C4EF70*
+ ID_OUI_FROM_DATABASE=Home Skinovations
+
OUI:C4F464*
ID_OUI_FROM_DATABASE=Spica international
@@ -70759,6 +70993,9 @@ OUI:C86000*
OUI:C864C7*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:C869CD*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:C86C1E*
ID_OUI_FROM_DATABASE=Display Systems Ltd
@@ -70804,6 +71041,9 @@ OUI:C88A83*
OUI:C88B47*
ID_OUI_FROM_DATABASE=Nolangroup S.P.A con Socio Unico
+OUI:C88ED1*
+ ID_OUI_FROM_DATABASE=IEEE REGISTRATION AUTHORITY - Please see MAM public listing for more information.
+
OUI:C8903E*
ID_OUI_FROM_DATABASE=Pakton Technologies
@@ -71062,6 +71302,9 @@ OUI:CC1AFA*
OUI:CC1EFF*
ID_OUI_FROM_DATABASE=Metrological Group BV
+OUI:CC20E8*
+ ID_OUI_FROM_DATABASE=Apple
+
OUI:CC2218*
ID_OUI_FROM_DATABASE=InnoDigital Co., Ltd.
@@ -71419,6 +71662,9 @@ OUI:D00EA4*
OUI:D00ED9*
ID_OUI_FROM_DATABASE=TAICANG T AND W ELECTRONICS CO LTD
+OUI:D00F6D*
+ ID_OUI_FROM_DATABASE=T&W Electronics Company
+
OUI:D01242*
ID_OUI_FROM_DATABASE=BIOS Corporation
@@ -71482,6 +71728,9 @@ OUI:D03972*
OUI:D039B3*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:D03E5C*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:D0431E*
ID_OUI_FROM_DATABASE=Dell Inc.
@@ -71599,6 +71848,9 @@ OUI:D07650*
OUI:D07AB5*
ID_OUI_FROM_DATABASE=Huawei Technologies Co., Ltd
+OUI:D07C2D*
+ ID_OUI_FROM_DATABASE=Leie IOT technology Co., Ltd
+
OUI:D07DE5*
ID_OUI_FROM_DATABASE=Forward Pay Systems, Inc.
@@ -72004,6 +72256,9 @@ OUI:D46E5C*
OUI:D46F42*
ID_OUI_FROM_DATABASE=WAXESS USA Inc
+OUI:D47208*
+ ID_OUI_FROM_DATABASE=Bragi GmbH
+
OUI:D479C3*
ID_OUI_FROM_DATABASE=Cameronet GmbH & Co. KG
@@ -72427,6 +72682,9 @@ OUI:D888CE*
OUI:D88A3B*
ID_OUI_FROM_DATABASE=UNIT-EM
+OUI:D88B4C*
+ ID_OUI_FROM_DATABASE=KingTing Tech.
+
OUI:D88D5C*
ID_OUI_FROM_DATABASE=Elentec
@@ -72529,6 +72787,9 @@ OUI:D8C068*
OUI:D8C3FB*
ID_OUI_FROM_DATABASE=DETRACOM
+OUI:D8C4E9*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:D8C691*
ID_OUI_FROM_DATABASE=Hichan Technology Corp.
@@ -72787,12 +73048,18 @@ OUI:DC7FA4*
OUI:DC825B*
ID_OUI_FROM_DATABASE=JANUS, spol. s r.o.
+OUI:DC82F6*
+ ID_OUI_FROM_DATABASE=iPort
+
OUI:DC85DE*
ID_OUI_FROM_DATABASE=Azurewave Technologies., inc.
OUI:DC86D8*
ID_OUI_FROM_DATABASE=Apple, Inc
+OUI:DC9A8E*
+ ID_OUI_FROM_DATABASE=Nanjing Cocomm electronics co., LTD
+
OUI:DC9B1E*
ID_OUI_FROM_DATABASE=Intercom, Inc.
@@ -72868,6 +73135,9 @@ OUI:DCCE41*
OUI:DCCEBC*
ID_OUI_FROM_DATABASE=Shenzhen JSR Technology Co.,Ltd.
+OUI:DCCEC1*
+ ID_OUI_FROM_DATABASE=Cisco Systems
+
OUI:DCCF94*
ID_OUI_FROM_DATABASE=Beijing Rongcheng Hutong Technology Co., Ltd.
@@ -72892,6 +73162,9 @@ OUI:DCDA4F*
OUI:DCDB70*
ID_OUI_FROM_DATABASE=Tonfunk Systementwicklung und Service GmbH
+OUI:DCDC07*
+ ID_OUI_FROM_DATABASE=TRP Systems BV
+
OUI:DCDECA*
ID_OUI_FROM_DATABASE=Akyllor
@@ -72916,6 +73189,9 @@ OUI:DCEB94*
OUI:DCEC06*
ID_OUI_FROM_DATABASE=Heimi Network Technology Co., Ltd.
+OUI:DCEF09*
+ ID_OUI_FROM_DATABASE=Netgear
+
OUI:DCF05D*
ID_OUI_FROM_DATABASE=Letta Teknoloji
@@ -73018,6 +73294,9 @@ OUI:E031D0*
OUI:E03560*
ID_OUI_FROM_DATABASE=Challenger Supply Holdings, LLC
+OUI:E03676*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:E036E3*
ID_OUI_FROM_DATABASE=Stage One International Co., Ltd.
@@ -73396,6 +73675,9 @@ OUI:E432CB*
OUI:E43593*
ID_OUI_FROM_DATABASE=Hangzhou GoTo technology Co.Ltd
+OUI:E435C8*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:E435FB*
ID_OUI_FROM_DATABASE=Sabre Technology (Hull) Ltd
@@ -73441,6 +73723,9 @@ OUI:E45614*
OUI:E457A8*
ID_OUI_FROM_DATABASE=Stuart Manufacturing, Inc.
+OUI:E458B8*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:E458E7*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -73543,12 +73828,18 @@ OUI:E498D6*
OUI:E4A32F*
ID_OUI_FROM_DATABASE=Shanghai Artimen Technology Co., Ltd.
+OUI:E4A387*
+ ID_OUI_FROM_DATABASE=Control Solutions LLC
+
OUI:E4A5EF*
ID_OUI_FROM_DATABASE=TRON LINK ELECTRONICS CO., LTD.
OUI:E4A7FD*
ID_OUI_FROM_DATABASE=Cellco Partnership
+OUI:E4AA5D*
+ ID_OUI_FROM_DATABASE=Cisco Systems
+
OUI:E4AB46*
ID_OUI_FROM_DATABASE=UAB Selteka
@@ -73696,6 +73987,9 @@ OUI:E81132*
OUI:E81324*
ID_OUI_FROM_DATABASE=GuangZhou Bonsoninfo System CO.,LTD
+OUI:E81363*
+ ID_OUI_FROM_DATABASE=Comstock RD, Inc.
+
OUI:E8150E*
ID_OUI_FROM_DATABASE=Nokia Corporation
@@ -73981,6 +74275,9 @@ OUI:E8DAAA*
OUI:E8DE27*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:E8DED6*
+ ID_OUI_FROM_DATABASE=Intrising Networks, Inc.
+
OUI:E8DFF2*
ID_OUI_FROM_DATABASE=PRF Co., Ltd.
@@ -74119,6 +74416,9 @@ OUI:EC3091*
OUI:EC3586*
ID_OUI_FROM_DATABASE=Apple
+OUI:EC388F*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:EC3BF0*
ID_OUI_FROM_DATABASE=NovelSat
@@ -74188,6 +74488,9 @@ OUI:EC6264*
OUI:EC63E5*
ID_OUI_FROM_DATABASE=ePBoard Design LLC
+OUI:EC64E7*
+ ID_OUI_FROM_DATABASE=MOCACARE Corporation
+
OUI:EC66D1*
ID_OUI_FROM_DATABASE=B&W Group LTD
@@ -74287,6 +74590,9 @@ OUI:ECBBAE*
OUI:ECBD09*
ID_OUI_FROM_DATABASE=FUSION Electronics Ltd
+OUI:ECBD1D*
+ ID_OUI_FROM_DATABASE=Cisco Systems
+
OUI:ECC38A*
ID_OUI_FROM_DATABASE=Accuenergy (CANADA) Inc
@@ -74875,6 +75181,9 @@ OUI:F43E9D*
OUI:F44227*
ID_OUI_FROM_DATABASE=S & S Research Inc.
+OUI:F4428F*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:F44450*
ID_OUI_FROM_DATABASE=BND Co., Ltd.
@@ -74890,6 +75199,9 @@ OUI:F4472A*
OUI:F44848*
ID_OUI_FROM_DATABASE=Amscreen Group Ltd
+OUI:F44D30*
+ ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd.
+
OUI:F44E05*
ID_OUI_FROM_DATABASE=Cisco
@@ -75454,6 +75766,9 @@ OUI:F8C091*
OUI:F8C288*
ID_OUI_FROM_DATABASE=Cisco
+OUI:F8C372*
+ ID_OUI_FROM_DATABASE=TSUZUKI DENKI
+
OUI:F8C397*
ID_OUI_FROM_DATABASE=NZXT Corp. Ltd.
diff --git a/hwdb/20-bluetooth-vendor-product.hwdb b/hwdb/20-bluetooth-vendor-product.hwdb
index dccced77da..35ea5e9e52 100644
--- a/hwdb/20-bluetooth-vendor-product.hwdb
+++ b/hwdb/20-bluetooth-vendor-product.hwdb
@@ -1772,3 +1772,78 @@ bluetooth:v024B*
bluetooth:v024C*
ID_VENDOR_FROM_DATABASE=Blue Clover Devices
+
+bluetooth:v024D*
+ ID_VENDOR_FROM_DATABASE=M-Way Solutions GmbH
+
+bluetooth:v024E*
+ ID_VENDOR_FROM_DATABASE=Microtronics Engineering GmbH
+
+bluetooth:v024F*
+ ID_VENDOR_FROM_DATABASE=Schneider Schreibgerte GmbH
+
+bluetooth:v0250*
+ ID_VENDOR_FROM_DATABASE=Sapphire Circuits LLC
+
+bluetooth:v0251*
+ ID_VENDOR_FROM_DATABASE=Lumo Bodytech Inc.
+
+bluetooth:v0252*
+ ID_VENDOR_FROM_DATABASE=UKC Technosolution
+
+bluetooth:v0253*
+ ID_VENDOR_FROM_DATABASE=Xicato Inc.
+
+bluetooth:v0254*
+ ID_VENDOR_FROM_DATABASE=Playbrush
+
+bluetooth:v0255*
+ ID_VENDOR_FROM_DATABASE=Dai Nippon Printing Co., Ltd.
+
+bluetooth:v0256*
+ ID_VENDOR_FROM_DATABASE=G24 Power Limited
+
+bluetooth:v0257*
+ ID_VENDOR_FROM_DATABASE=AdBabble Local Commerce Inc.
+
+bluetooth:v0258*
+ ID_VENDOR_FROM_DATABASE=Devialet SA
+
+bluetooth:v0259*
+ ID_VENDOR_FROM_DATABASE=ALTYOR
+
+bluetooth:v025A*
+ ID_VENDOR_FROM_DATABASE=University of Applied Sciences Valais/Haute Ecole Valaisanne
+
+bluetooth:v025B*
+ ID_VENDOR_FROM_DATABASE=Five Interactive, LLC dba Zendo
+
+bluetooth:v025C*
+ ID_VENDOR_FROM_DATABASE=NetEase (Hangzhou) Network co.Ltd.
+
+bluetooth:v025D*
+ ID_VENDOR_FROM_DATABASE=Lexmark International Inc.
+
+bluetooth:v025E*
+ ID_VENDOR_FROM_DATABASE=Fluke Corporation
+
+bluetooth:v025F*
+ ID_VENDOR_FROM_DATABASE=Yardarm Technologies
+
+bluetooth:v0260*
+ ID_VENDOR_FROM_DATABASE=SensaRx
+
+bluetooth:v0261*
+ ID_VENDOR_FROM_DATABASE=SECVRE GmbH
+
+bluetooth:v0262*
+ ID_VENDOR_FROM_DATABASE=Glacial Ridge Technologies
+
+bluetooth:v0263*
+ ID_VENDOR_FROM_DATABASE=Identiv, Inc.
+
+bluetooth:v0264*
+ ID_VENDOR_FROM_DATABASE=DDS, Inc.
+
+bluetooth:v0265*
+ ID_VENDOR_FROM_DATABASE=SMK Corporation
diff --git a/hwdb/20-pci-classes.hwdb b/hwdb/20-pci-classes.hwdb
index 5702c40114..3c0c465e5f 100644
--- a/hwdb/20-pci-classes.hwdb
+++ b/hwdb/20-pci-classes.hwdb
@@ -95,6 +95,9 @@ pci:v*d*sv*sd*bc02sc06*
pci:v*d*sv*sd*bc02sc07*
ID_PCI_SUBCLASS_FROM_DATABASE=Infiniband controller
+pci:v*d*sv*sd*bc02sc08*
+ ID_PCI_SUBCLASS_FROM_DATABASE=Fabric controller
+
pci:v*d*sv*sd*bc02sc80*
ID_PCI_SUBCLASS_FROM_DATABASE=Network controller
@@ -554,5 +557,8 @@ pci:v*d*sv*sd*bc12sc00*
pci:v*d*sv*sd*bc13*
ID_PCI_CLASS_FROM_DATABASE=Non-Essential Instrumentation
+pci:v*d*sv*sd*bc40*
+ ID_PCI_CLASS_FROM_DATABASE=Coprocessor
+
pci:v*d*sv*sd*bcFF*
ID_PCI_CLASS_FROM_DATABASE=Unassigned class
diff --git a/hwdb/20-pci-vendor-model.hwdb b/hwdb/20-pci-vendor-model.hwdb
index 8316bc979e..61164acfe5 100644
--- a/hwdb/20-pci-vendor-model.hwdb
+++ b/hwdb/20-pci-vendor-model.hwdb
@@ -1847,6 +1847,9 @@ pci:v00001002d00004151*
pci:v00001002d00004151sv00001043sd0000C004*
ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600 Series] (A9600SE)
+pci:v00001002d00004151sv0000174Bsd00007C37*
+ ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600 Series] (Radeon 9600 SE)
+
pci:v00001002d00004152*
ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series]
@@ -1934,6 +1937,9 @@ pci:v00001002d00004171*
pci:v00001002d00004171sv00001043sd0000C005*
ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] (Secondary) (A9600SE (Secondary))
+pci:v00001002d00004171sv0000174Bsd00007C36*
+ ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] (Secondary) (Radeon 9600 SE (secondary))
+
pci:v00001002d00004172*
ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary)
@@ -3422,6 +3428,9 @@ pci:v00001002d00005159sv0000148Csd00002003*
pci:v00001002d00005159sv0000148Csd00002023*
ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV100 QY [Radeon 7000 Evil Master Multi-Display])
+pci:v00001002d00005159sv0000148Csd00002081*
+ ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV6DE)
+
pci:v00001002d00005159sv0000174Bsd00000280*
ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon RV100 QY [Radeon 7000/VE])
@@ -17060,6 +17069,9 @@ pci:v00001077d00002020*
pci:v00001077d00002031*
ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter
+pci:v00001077d00002031sv0000103Csd00008002*
+ ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter (3830C 16G Fibre Channel Host Bus Adapter)
+
pci:v00001077d00002100*
ID_MODEL_FROM_DATABASE=QLA2100 64-bit Fibre Channel Adapter
@@ -21335,6 +21347,12 @@ pci:v000010B5d00009656sv00001885sd00000700*
pci:v000010B5d00009656sv00001885sd00000701*
ID_MODEL_FROM_DATABASE=PCI9656 PCI <-> IOBus Bridge (Tsunami FPGA PMC with Altera Stratix S30)
+pci:v000010B5d00009733*
+ ID_MODEL_FROM_DATABASE=PEX 9733 33-lane, 9-port PCI Express Gen 3 (8.0 GT/s) Switch
+
+pci:v000010B5d00009749*
+ ID_MODEL_FROM_DATABASE=PEX 9749 49-lane, 13-port PCI Express Gen 3 (8.0 GT/s) Switch
+
pci:v000010B5d0000A100*
ID_MODEL_FROM_DATABASE=Blackmagic Design DeckLink
@@ -22497,13 +22515,13 @@ pci:v000010CDd00001200*
ID_MODEL_FROM_DATABASE=ASC1200 [(abp940) Fast SCSI-II]
pci:v000010CDd00001300*
- ID_MODEL_FROM_DATABASE=ABP940-U / ABP960-U
+ ID_MODEL_FROM_DATABASE=ASC1300 / ASC3030 [ABP940-U / ABP960-U / ABP3925]
pci:v000010CDd00001300sv000010CDsd00001310*
- ID_MODEL_FROM_DATABASE=ABP940-U / ABP960-U (ASC1300 SCSI Adapter)
+ ID_MODEL_FROM_DATABASE=ASC1300 / ASC3030 [ABP940-U / ABP960-U / ABP3925] (ASC1300/3030 SCSI adapter)
pci:v000010CDd00001300sv00001195sd00001320*
- ID_MODEL_FROM_DATABASE=ABP940-U / ABP960-U (Ultra-SCSI CardBus PC Card REX CB31)
+ ID_MODEL_FROM_DATABASE=ASC1300 / ASC3030 [ABP940-U / ABP960-U / ABP3925] (Ultra-SCSI CardBus PC Card REX CB31)
pci:v000010CDd00002300*
ID_MODEL_FROM_DATABASE=ABP940-UW
@@ -26223,7 +26241,7 @@ pci:v000010DEd0000056Dsv00001019sd0000297A*
ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73PVT-SM)
pci:v000010DEd0000056Dsv000010DEsd0000CB73*
- ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73 PCIe x1 port)
+ ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73 PCI Bridge)
pci:v000010DEd0000056E*
ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge
@@ -26240,6 +26258,9 @@ pci:v000010DEd0000056F*
pci:v000010DEd0000056Fsv00001019sd0000297A*
ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73PVT-SM)
+pci:v000010DEd0000056Fsv000010DEsd00000000*
+ ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73 PCIe x1 port)
+
pci:v000010DEd000005B1*
ID_MODEL_FROM_DATABASE=NF200 PCIe 2.0 switch
@@ -26531,6 +26552,9 @@ pci:v000010DEd00000643*
pci:v000010DEd00000644*
ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GS]
+pci:v000010DEd00000644sv0000174Bsd00009600*
+ ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GS] (Geforce 9500GS 512M DDR2 V/D/HDMI)
+
pci:v000010DEd00000645*
ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GS]
@@ -26708,6 +26732,9 @@ pci:v000010DEd000006DFsv000010DEsd0000087F*
pci:v000010DEd000006E0*
ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 GE]
+pci:v000010DEd000006E0sv0000107Dsd00005A96*
+ ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 GE] (Geforce 9300GE)
+
pci:v000010DEd000006E1*
ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 GS]
@@ -30404,6 +30431,15 @@ pci:v000010DEd000013F2*
pci:v000010DEd00001401*
ID_MODEL_FROM_DATABASE=GM206 [GeForce GTX 960]
+pci:v000010DEd00001617*
+ ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 980M]
+
+pci:v000010DEd00001618*
+ ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 970M]
+
+pci:v000010DEd00001619*
+ ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 965M]
+
pci:v000010DEd000017C2*
ID_MODEL_FROM_DATABASE=GM200 [GeForce GTX TITAN X]
@@ -34367,6 +34403,12 @@ pci:v0000111Dd00008088sv00001093sd00007602*
pci:v0000111Dd0000808F*
ID_MODEL_FROM_DATABASE=PES32NT8AG2
+pci:v0000111Dd000080CF*
+ ID_MODEL_FROM_DATABASE=F32P08xG3 [PCIe boot mode]
+
+pci:v0000111Dd000080D2*
+ ID_MODEL_FROM_DATABASE=F32P08xG3 NVMe controller
+
pci:v0000111E*
ID_VENDOR_FROM_DATABASE=Eldec
@@ -40107,7 +40149,13 @@ pci:v00001244d00000A00sv00001244sd00000A00*
ID_MODEL_FROM_DATABASE=A1 ISDN [Fritz] (FRITZ!Card ISDN Controller)
pci:v00001244d00000E00*
- ID_MODEL_FROM_DATABASE=Fritz!PCI v2.0 ISDN
+ ID_MODEL_FROM_DATABASE=Fritz!Card PCI v2.0 ISDN
+
+pci:v00001244d00000E80*
+ ID_MODEL_FROM_DATABASE=Fritz!Card PCI v2.1 ISDN
+
+pci:v00001244d00000E80sv00001244sd00000E00*
+ ID_MODEL_FROM_DATABASE=Fritz!Card PCI v2.1 ISDN (PSB 3100F (AVM KAFKA) [Fritz!Card PCI v2.1])
pci:v00001244d00001100*
ID_MODEL_FROM_DATABASE=C2 ISDN
@@ -44895,7 +44943,7 @@ pci:v00001409d00007168sv00001409sd00004027*
ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4027A (1x RS232 port))
pci:v00001409d00007168sv00001409sd00004037*
- ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4037A (2x RS232 port))
+ ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4037A(L) [SUNIX SUN1889] (2x RS232 port))
pci:v00001409d00007168sv00001409sd00004056*
ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4056A (4x RS232))
@@ -48146,6 +48194,12 @@ pci:v000014E4d000016A2sv0000103Csd00001916*
pci:v000014E4d000016A2sv0000103Csd00001917*
ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (HP FlexFabric 20Gb 2-port 630M Adapter)
+pci:v000014E4d000016A2sv0000103Csd00002231*
+ ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (3820C 10/20Gb Converged Network Adapter)
+
+pci:v000014E4d000016A2sv0000103Csd000022FA*
+ ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (FlexFabric 10Gb 2-port 536FLB Adapter)
+
pci:v000014E4d000016A3*
ID_MODEL_FROM_DATABASE=NetXtreme BCM57786 Gigabit Ethernet PCIe
@@ -48158,6 +48212,12 @@ pci:v000014E4d000016A4sv0000103Csd00001916*
pci:v000014E4d000016A4sv0000103Csd00001917*
ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (HP NPAR 20Gb 2-port 630M Adapter)
+pci:v000014E4d000016A4sv0000103Csd00002231*
+ ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (3820C 10/20Gb Converged Network Adapter (NPAR 1.5))
+
+pci:v000014E4d000016A4sv0000103Csd000022FA*
+ ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (FlexFabric 10Gb 2-port 536FLB Adapter (NPAR 1.5))
+
pci:v000014E4d000016A5*
ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function
@@ -48287,6 +48347,12 @@ pci:v000014E4d000016ADsv0000103Csd00001916*
pci:v000014E4d000016ADsv0000103Csd00001917*
ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (HP FlexFabric 20Gb 2-port 630M Adapter)
+pci:v000014E4d000016ADsv0000103Csd00002231*
+ ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (3820C 10/20Gb Converged Network Adapter (SR-IOV VF))
+
+pci:v000014E4d000016ADsv0000103Csd000022FA*
+ ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (FlexFabric 10Gb 2-port 536FLB Adapter (SR-IOV VF))
+
pci:v000014E4d000016AE*
ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function
@@ -49052,6 +49118,9 @@ pci:v000014E4d000043D9*
pci:v000014E4d000043DF*
ID_MODEL_FROM_DATABASE=BCM4354 802.11ac Wireless LAN SoC
+pci:v000014E4d000043E9*
+ ID_MODEL_FROM_DATABASE=BCM4358 802.11ac Wireless LAN SoC
+
pci:v000014E4d000043EC*
ID_MODEL_FROM_DATABASE=BCM4356 802.11ac Wireless Network Adapter
@@ -49232,6 +49301,9 @@ pci:v000014E4d00008602*
pci:v000014E4d0000A8D8*
ID_MODEL_FROM_DATABASE=BCM43224/5 Wireless Network Adapter
+pci:v000014E4d0000AA52*
+ ID_MODEL_FROM_DATABASE=BCM43602 802.11ac Wireless LAN SoC
+
pci:v000014E4d0000B302*
ID_MODEL_FROM_DATABASE=BCM56302 StrataXGS 24x1GE 2x10GE Switch Controller
@@ -52266,199 +52338,199 @@ pci:v0000168Cd00000012sv00001B47sd0000AA00*
ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (8460 802.11ab Wireless CardBus Adapter)
pci:v0000168Cd00000013*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter
pci:v0000168Cd00000013sv00000308sd00003402*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AG-100 802.11ag Wireless Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AG-100 802.11ag Wireless Cardbus Adapter)
pci:v0000168Cd00000013sv00000308sd00003405*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (G-102 v2 802.11g Wireless Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (G-102 v2 802.11g Wireless Cardbus Adapter)
pci:v0000168Cd00000013sv00000308sd00003408*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (G-170S 802.11g Wireless CardBus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (G-170S 802.11g Wireless CardBus Adapter)
pci:v0000168Cd00000013sv00000E11sd000000E5*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (NC6000/NC8000 laptop)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (NC6000/NC8000 laptop)
pci:v0000168Cd00000013sv000010B7sd00006002*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (3CRWE154A72 802.11abg Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (3CRWE154A72 802.11abg Cardbus Adapter)
pci:v0000168Cd00000013sv00001113sd0000D301*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Philips CPWNA100 Wireless CardBus adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Philips CPWNA100 Wireless CardBus adapter)
pci:v0000168Cd00000013sv00001113sd0000EE23*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (SMCWPCIT-G 108Mbps Wireless PCI adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (SMCWPCIT-G 108Mbps Wireless PCI adapter)
pci:v0000168Cd00000013sv00001154sd0000033B*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Buffalo WLI-CB-AMG54)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Buffalo WLI-CB-AMG54)
pci:v0000168Cd00000013sv00001154sd0000034E*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Buffalo WLI-CB-AG108HP 802.11abg Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Buffalo WLI-CB-AG108HP 802.11abg Cardbus Adapter)
pci:v0000168Cd00000013sv00001186sd00003202*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (DWL-G650 (Rev B3,B5) Wireless cardbus adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (DWL-G650 (Rev B3,B5) Wireless cardbus adapter)
pci:v0000168Cd00000013sv00001186sd00003203*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AirPlus DWL-G520 Wireless PCI Adapter (rev. A))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPlus DWL-G520 Wireless PCI Adapter (rev. A))
pci:v0000168Cd00000013sv00001186sd00003A07*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AirXpert DWL-AG650 Wireless Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirXpert DWL-AG650 Wireless Cardbus Adapter)
pci:v0000168Cd00000013sv00001186sd00003A08*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AirXpert DWL-AG520 Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirXpert DWL-AG520 Wireless PCI Adapter)
pci:v0000168Cd00000013sv00001186sd00003A12*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (D-Link AirPlus DWL-G650 Wireless Cardbus Adapter(rev.C))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPlus DWL-G650 Wireless Cardbus Adapter(rev.C))
pci:v0000168Cd00000013sv00001186sd00003A13*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AirPlus DWL-G520 Wireless PCI Adapter (rev. B))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPlus DWL-G520 Wireless PCI Adapter (rev. B))
pci:v0000168Cd00000013sv00001186sd00003A14*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AirPremier AG DWL-AG530 Wireless PCI Adapter (rev.A))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPremier AG DWL-AG530 Wireless PCI Adapter (rev.A))
pci:v0000168Cd00000013sv00001186sd00003A17*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (D-Link AirPremier DWL-G680 Wireless Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPremier DWL-G680 Wireless Cardbus Adapter)
pci:v0000168Cd00000013sv00001186sd00003A18*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (D-Link AirPremier DWL-G550 Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPremier DWL-G550 Wireless PCI Adapter)
pci:v0000168Cd00000013sv00001186sd00003A1A*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WNA-2330 802.11bg Wireless CardBus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WNA-2330 802.11bg Wireless CardBus Adapter)
pci:v0000168Cd00000013sv00001186sd00003A63*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (D-Link AirPremier DWL-AG660 Wireless Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPremier DWL-AG660 Wireless Cardbus Adapter)
pci:v0000168Cd00000013sv00001186sd00003A93*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Conceptronic C54I Wireless 801.11g PCI card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Conceptronic C54I Wireless 801.11g PCI card)
pci:v0000168Cd00000013sv00001186sd00003A94*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Conceptronic C54C 802.11g Wireless Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Conceptronic C54C 802.11g Wireless Cardbus Adapter)
pci:v0000168Cd00000013sv00001186sd00003AB0*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Allnet ALL0281 Wireless PCI Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Allnet ALL0281 Wireless PCI Card)
pci:v0000168Cd00000013sv00001385sd00004600*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WAG511 802.11a/b/g Dual Band Wireless PC Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WAG511 802.11a/b/g Dual Band Wireless PC Card)
pci:v0000168Cd00000013sv00001385sd00004610*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WAG511 802.11a/b/g Dual Band Wireless PC Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WAG511 802.11a/b/g Dual Band Wireless PC Card)
pci:v0000168Cd00000013sv00001385sd00004900*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WG311v1 802.11g Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG311v1 802.11g Wireless PCI Adapter)
pci:v0000168Cd00000013sv00001385sd00004A00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WAG311 802.11a/g Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WAG311 802.11a/g Wireless PCI Adapter)
pci:v0000168Cd00000013sv00001385sd00004B00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WG511T 108 Mbps Wireless PC Card (rev.A/B))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG511T 108 Mbps Wireless PC Card (rev.A/B))
pci:v0000168Cd00000013sv00001385sd00004D00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WG311T 108 Mbps Wireless PCI Adapter (rev.A2))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG311T 108 Mbps Wireless PCI Adapter (rev.A2))
pci:v0000168Cd00000013sv00001385sd00004F00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WG511U Double 108 Mbps Wireless PC Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG511U Double 108 Mbps Wireless PC Card)
pci:v0000168Cd00000013sv00001385sd00005A00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WG311T 108 Mbps Wireless PCI Adapter (rev.A3))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG311T (rev.A3 v1h3/v1h4) 108 Mbps Wireless PCI Adapter [AR2412])
pci:v0000168Cd00000013sv00001385sd00005B00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WG511T 108 Mbps Wireless PC Card (rev.C))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG511T 108 Mbps Wireless PC Card (rev.C))
pci:v0000168Cd00000013sv00001385sd00005D00*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WPN511 RangeMax Wireless PC Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WPN511 RangeMax Wireless PC Card)
pci:v0000168Cd00000013sv00001458sd0000E911*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (GN-WIAG02)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (GN-WIAG02)
pci:v0000168Cd00000013sv00001468sd00000403*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (U10H014 802.11g Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (U10H014 802.11g Cardbus Adapter)
pci:v0000168Cd00000013sv00001468sd00000408*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (ThinkPad 11b/g Wireless LAN Mini PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (ThinkPad 11b/g Wireless LAN Mini PCI Adapter)
pci:v0000168Cd00000013sv000014B7sd00000A10*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (8480-WD 802.11abg Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8480-WD 802.11abg Cardbus Adapter)
pci:v0000168Cd00000013sv000014B7sd00000A60*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (8482-WD ORiNOCO 11a/b/g Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8482-WD ORiNOCO 11a/b/g Wireless PCI Adapter)
pci:v0000168Cd00000013sv000014B7sd0000AA30*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (8800-FC 802.11bg Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8800-FC 802.11bg Cardbus Adapter)
pci:v0000168Cd00000013sv000014B7sd0000AA40*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (8470-WD 802.11bg Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8470-WD 802.11bg Cardbus Adapter)
pci:v0000168Cd00000013sv000014B9sd0000CB21*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (CB21 802.11a/b/g Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (CB21 802.11a/b/g Cardbus Adapter)
pci:v0000168Cd00000013sv00001668sd00001026*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (IBM HighRate 11 a/b/g Wireless CardBus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (IBM HighRate 11 a/b/g Wireless CardBus Adapter)
pci:v0000168Cd00000013sv0000168Csd00000013*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (AirPlus XtremeG DWL-G650 Wireless PCMCIA Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPlus XtremeG DWL-G650 Wireless PCMCIA Adapter)
pci:v0000168Cd00000013sv0000168Csd00001025*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (DWL-G650B2 Wireless CardBus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (DWL-G650B2 Wireless CardBus Adapter)
pci:v0000168Cd00000013sv0000168Csd00001027*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Engenius NL-3054CB ARIES b/g CardBus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Engenius NL-3054CB ARIES b/g CardBus Adapter)
pci:v0000168Cd00000013sv0000168Csd00001042*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Ubiquiti Networks SuperRange a/b/g Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Ubiquiti Networks SuperRange a/b/g Cardbus Adapter)
pci:v0000168Cd00000013sv0000168Csd00001051*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (EZ Connect g 802.11g 108Mbps Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (EZ Connect g 802.11g 108Mbps Wireless PCI Adapter)
pci:v0000168Cd00000013sv0000168Csd00002026*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Netgate 5354MP ARIES a(108Mb turbo)/b/g MiniPCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Netgate 5354MP ARIES a(108Mb turbo)/b/g MiniPCI Adapter)
pci:v0000168Cd00000013sv0000168Csd00002027*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (D-Link AirPlus DWL-G520 Wireless PCI Adapter (rev. A))
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPlus DWL-G520 Wireless PCI Adapter (rev. A))
pci:v0000168Cd00000013sv0000168Csd00002041*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Engenius 5354MP Plus ARIES2 b/g MiniPCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Engenius 5354MP Plus ARIES2 b/g MiniPCI Adapter)
pci:v0000168Cd00000013sv0000168Csd00002042*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Engenius 5354MP Plus ARIES2 a/b/g MiniPCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Engenius 5354MP Plus ARIES2 a/b/g MiniPCI Adapter)
pci:v0000168Cd00000013sv0000168Csd00002051*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (TRENDnet TEW-443PI Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (TRENDnet TEW-443PI Wireless PCI Adapter)
pci:v0000168Cd00000013sv000016A5sd0000160A*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (BWP712 802.11bg Wireless CardBus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (BWP712 802.11bg Wireless CardBus Adapter)
pci:v0000168Cd00000013sv000016ABsd00007302*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Trust Speedshare Turbo Pro Wireless PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Trust Speedshare Turbo Pro Wireless PCI Adapter)
pci:v0000168Cd00000013sv00001737sd00000017*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WPC55AG)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WPC55AG)
pci:v0000168Cd00000013sv00001737sd00000026*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WMP55AG v1.1)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WMP55AG v1.1)
pci:v0000168Cd00000013sv00001737sd00000035*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WPC55AG v1.2 802.11abg Cardbus Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WPC55AG v1.2 802.11abg Cardbus Adapter)
pci:v0000168Cd00000013sv00001737sd00000036*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (WMP55AG v1.2 802.11abg PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WMP55AG v1.2 802.11abg PCI Adapter)
pci:v0000168Cd00000013sv00001799sd00003000*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (F6D3000 Dual-Band Wireless A+G Desktop Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (F6D3000 Dual-Band Wireless A+G Desktop Card)
pci:v0000168Cd00000013sv00001799sd00003010*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (F6D3010 Dual-Band Wireless A+G Notebook Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (F6D3010 Dual-Band Wireless A+G Notebook Card)
pci:v0000168Cd00000013sv000017CFsd00000042*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Z-COMAX Highpower XG-622H (400mw) 802.11b/g mini-PCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Z-COMAX Highpower XG-622H (400mw) 802.11b/g mini-PCI Adapter)
pci:v0000168Cd00000013sv0000185Fsd00001012*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (CM9 Wireless a/b/g MiniPCI Adapter)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (CM9 Wireless a/b/g MiniPCI Adapter)
pci:v0000168Cd00000013sv0000185Fsd00002012*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (Wistron NeWeb WLAN a+b+g model CB9)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Wistron NeWeb WLAN a+b+g model CB9)
pci:v0000168Cd00000013sv0000A727sd00006801*
- ID_MODEL_FROM_DATABASE=AR5212/AR5213 Wireless Network Adapter (3CRXJK10075 OfficeConnect Wireless 108Mbps 11g XJACK PC Card)
+ ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (3CRXJK10075 OfficeConnect Wireless 108Mbps 11g XJACK PC Card)
pci:v0000168Cd0000001A*
ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg]
@@ -52733,6 +52805,9 @@ pci:v0000168Cd0000002Asv00000777sd00004F05*
pci:v0000168Cd0000002Asv0000103Csd00003041*
ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AR5BHB92-H 802.11abgn Wireless Half-size Mini PCIe Card [AR9280])
+pci:v0000168Cd0000002Asv0000103Csd00003042*
+ ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AzureWave AW-NE773 802.11abgn Wireless Half-size Mini PCIe Card [AR9280])
+
pci:v0000168Cd0000002Asv0000105Bsd0000E006*
ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (T77H053.00 802.11bgn Wireless Mini PCIe Card [AR9281])
@@ -53204,6 +53279,12 @@ pci:v000016D5d00007004*
pci:v000016D5d00007005*
ID_MODEL_FROM_DATABASE=XMC-7K410CC: User-Configurable Kintex-7 FPGA, 410k logic cells, conduction-cooled
+pci:v000016D5d00007006*
+ ID_MODEL_FROM_DATABASE=XMC-7A200: User-Configurable Artix-7 FPGA, 200k logic cells with Plug-In I/O
+
+pci:v000016D5d00007007*
+ ID_MODEL_FROM_DATABASE=XMC-7A200CC: User-Configurable Conduction-Cooled Artix-7 FPGA, with 200k logic cells
+
pci:v000016DA*
ID_VENDOR_FROM_DATABASE=Advantech Co., Ltd.
@@ -53471,6 +53552,12 @@ pci:v0000177Dd00000095*
pci:v0000177Dd00000096*
ID_MODEL_FROM_DATABASE=Octeon III CN70XX Network Processor
+pci:v0000177Dd00009700*
+ ID_MODEL_FROM_DATABASE=Octeon III CN73XX Network Processor
+
+pci:v0000177Dd00009800*
+ ID_MODEL_FROM_DATABASE=Octeon Fusion CNF75XX Processor
+
pci:v0000177Dd0000A001*
ID_MODEL_FROM_DATABASE=THUNDERX MRML Bridge
@@ -55295,6 +55382,9 @@ pci:v00001924d00000923sv00001924sd0000800B*
pci:v00001924d00000923sv00001924sd0000800E*
ID_MODEL_FROM_DATABASE=SFC9140 (SFN7x42Q-R2 Flareon Ultra 7000 Series 10/40G Adapter)
+pci:v00001924d00000923sv00001924sd0000800F*
+ ID_MODEL_FROM_DATABASE=SFC9140 (SFN7xx4F-R1 Flareon Ultra 7000 Series 10G Adapter)
+
pci:v00001924d00001803*
ID_MODEL_FROM_DATABASE=SFC9020 Virtual Function [Solarstorm]
@@ -56819,6 +56909,9 @@ pci:v00001B4Bd00009120*
pci:v00001B4Bd00009123*
ID_MODEL_FROM_DATABASE=88SE9123 PCIe SATA 6.0 Gb/s controller
+pci:v00001B4Bd00009123sv0000DC93sd0000600E*
+ ID_MODEL_FROM_DATABASE=88SE9123 PCIe SATA 6.0 Gb/s controller (DC-6xxe series SATA 6G controller)
+
pci:v00001B4Bd00009125*
ID_MODEL_FROM_DATABASE=88SE9125 PCIe SATA 6.0 Gb/s controller
@@ -56915,6 +57008,9 @@ pci:v00001B73d00001000sv00001D5Csd00001000*
pci:v00001B73d00001009*
ID_MODEL_FROM_DATABASE=FL1009 USB 3.0 Host Controller
+pci:v00001B73d00001100*
+ ID_MODEL_FROM_DATABASE=FL1100 USB 3.0 Host Controller
+
pci:v00001B74*
ID_VENDOR_FROM_DATABASE=OpenVox Communication Co. Ltd.
@@ -57191,6 +57287,9 @@ pci:v00001CE4d00000002*
pci:v00001CF7*
ID_VENDOR_FROM_DATABASE=Subspace Dynamics
+pci:v00001D00*
+ ID_VENDOR_FROM_DATABASE=Pure Storage
+
pci:v00001D21*
ID_VENDOR_FROM_DATABASE=Allo
@@ -57356,6 +57455,36 @@ pci:v00001FC9d00003314sv00000000sd00003012*
pci:v00001FC9d00003314sv00000000sd00003014*
ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port CX4 Low Profile SmartNIC)
+pci:v00001FC9d00004010*
+ ID_MODEL_FROM_DATABASE=TN4010 Clean SROM
+
+pci:v00001FC9d00004020*
+ ID_MODEL_FROM_DATABASE=TN9030 10GbE CX4 Ethernet Adapter
+
+pci:v00001FC9d00004022*
+ ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter
+
+pci:v00001FC9d00004022sv00001186sd00004D00*
+ ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter (DXE-810S 10GbE SFP+ Ethernet Adapter)
+
+pci:v00001FC9d00004022sv00001FC9sd00003015*
+ ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter (Ethernet Adapter)
+
+pci:v00001FC9d00004024*
+ ID_MODEL_FROM_DATABASE=TN9210 10GBase-T Ethernet Adapter
+
+pci:v00001FC9d00004025*
+ ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter
+
+pci:v00001FC9d00004025sv00001186sd00002900*
+ ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter (DXE-810T 10GBase-T Ethernet Adapter)
+
+pci:v00001FC9d00004025sv00001FC9sd00003015*
+ ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter (Ethernet Adapter)
+
+pci:v00001FC9d00004026*
+ ID_MODEL_FROM_DATABASE=TN9610 10GbE SFP+ Ethernet Adapter
+
pci:v00001FCC*
ID_VENDOR_FROM_DATABASE=StreamLabs
@@ -63962,6 +64091,12 @@ pci:v00008086d000015A2*
pci:v00008086d000015A3*
ID_MODEL_FROM_DATABASE=Ethernet Connection (3) I218-V
+pci:v00008086d000015A4*
+ ID_MODEL_FROM_DATABASE=Ethernet Switch FM10000 Host Interface
+
+pci:v00008086d000015A5*
+ ID_MODEL_FROM_DATABASE=Ethernet Switch FM10000 Host Virtual Interface
+
pci:v00008086d000015A8*
ID_MODEL_FROM_DATABASE=Ethernet Connection X552 Virtual Function
diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb
index 815ac24511..5c8351142a 100644
--- a/hwdb/60-evdev.hwdb
+++ b/hwdb/60-evdev.hwdb
@@ -20,10 +20,11 @@
# where /dev/input/eventXX is the device in question. If in
# doubt, simply use /dev/input/event* to reload all input rules.
#
-# If your changes are generally applicable, open a bug report on
-# http://bugs.freedesktop.org/enter_bug.cgi?product=systemd
-# and include your new rules, a description of the device, and the
-# output of
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+# https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
# udevadm info /dev/input/eventXX
# (or /dev/input/event*).
#
@@ -43,6 +44,11 @@
# Apple
#########################################
+# Macbook2,1 (late 2006), single-button touchpad
+evdev:input:b0003v05ACp021B*
+ EVDEV_ABS_00=256:1471:12
+ EVDEV_ABS_01=256:831:12
+
# Macbook5,1 (unibody), aka wellspring3
evdev:input:b0003v05ACp0236*
evdev:input:b0003v05ACp0237*
@@ -84,6 +90,16 @@ evdev:input:b0003v05ACp025b*
EVDEV_ABS_36=::92
#########################################
+# ASUS
+#########################################
+# Asus K52JT
+evdev:name:ETPS/2 Elantech Touchpad:dmi:bvn*:bvr*:bd*:svnASUSTeKComputerInc.:pnK52JT:*
+ EVDEV_ABS_00=::18
+ EVDEV_ABS_01=::16
+ EVDEV_ABS_35=::18
+ EVDEV_ABS_36=::16
+
+#########################################
# Google
#########################################
@@ -102,3 +118,12 @@ evdev:name:Atmel maXTouch Touch*:dmi:bvn*:bvr*:bd*:svnGOOGLE:pnSamus*
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*X230*
EVDEV_ABS_01=::100
EVDEV_ABS_36=::100
+
+#########################################
+# Dell
+#########################################
+
+# Dell Vostro 1510
+evdev:name:AlpsPS/2 ALPS GlidePoint*:dmi:bvn*:bvr*:bd*:svnDellInc.:pnVostro1510*
+ EVDEV_ABS_00=::14
+ EVDEV_ABS_01=::18
diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb
index d0fc914d17..65e939262a 100644
--- a/hwdb/60-keyboard.hwdb
+++ b/hwdb/60-keyboard.hwdb
@@ -51,10 +51,11 @@
# where /dev/input/eventXX is the keyboard in question. If in
# doubt, simply use /dev/input/event* to reload all input rules.
#
-# If your changes are generally applicable, open a bug report on
-# http://bugs.freedesktop.org/enter_bug.cgi?product=systemd
-# and include your new rules, a description of the device, and the
-# output of
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+# https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
# udevadm info /dev/input/eventXX
# (or /dev/input/event*).
diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb
index bc4b597bbf..7373be4778 100644
--- a/hwdb/70-mouse.hwdb
+++ b/hwdb/70-mouse.hwdb
@@ -36,10 +36,11 @@
# where /dev/input/eventXX is the mouse in question. If in
# doubt, simply use /dev/input/event* to reload all input rules.
#
-# If your changes are generally applicable, open a bug report on
-# http://bugs.freedesktop.org/enter_bug.cgi?product=systemd
-# and include your new rules, a description of the device, and the
-# output of
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+# https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
# udevadm info /dev/input/eventXX
# (or /dev/input/event*).
#
@@ -138,6 +139,11 @@ mouse:usb:v0461p4d16:name:USB Optical Mouse:
# HP
##########################################
+# HP USB 1000dpi Laser Mouse
+mouse:usb:v0458p0133:name:Mouse Laser Mouse:
+ MOUSE_DPI=1000@125
+ MOUSE_WHEEL_CLICK_ANGLE=15
+
# HP X1000
mouse:usb:v093ap2510:name:PixArt USB Optical Mouse:
MOUSE_DPI=1000@125
@@ -233,6 +239,10 @@ mouse:usb:v046dpc245:name:Logitech Gaming Mouse G400:
mouse:usb:v046dpc24c:name:Logitech G400s Optical Gaming Mouse:
MOUSE_DPI=400@1000 *800@1000 2000@1000 4000@1000
+# Logitech G402 Hyperion Fury
+mouse:usb:v046dpc07e:name:Logitech Gaming Mouse G402:
+ MOUSE_DPI=400@1000 *800@1000 1600@1000 3200@1000
+
# Logitech B605 Wireless Mouse (also M505)
mouse:usb:v046dp101d:name:Logitech B605:
mouse:usb:v046dp101d:name:Logitech M505:
@@ -272,11 +282,20 @@ mouse:usb:v046dp4027:name:Logitech T620:
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4027:
MOUSE_DPI=1200@250
+# Logitech LX8 Cordless Laser Mouse
+mouse:usb:v046dpc51b:name:Logitech USB Receiver:
+ MOUSE_DPI=1300@125
+ MOUSE_WHEEL_CLICK_ANGLE=15
+
# Logitech ZoneTouch Mouse T400
mouse:usb:v046dp4026:name:Logitech T400:
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4026:
MOUSE_DPI=1300@166
+# Logitech G500 Mouse
+mouse:usb:v046dpc068:name:Logitech G500:
+ MOUSE_DPI=*1600@500 2600@500 3600@500
+
# Logitech Ultrathin Touch Mouse
mouse:bluetooth:v046dpb00d:name:Ultrathin Touch Mouse:
MOUSE_DPI=1000@1000
diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb
index a8c21a2ee6..775ec7fea5 100644
--- a/hwdb/70-pointingstick.hwdb
+++ b/hwdb/70-pointingstick.hwdb
@@ -32,10 +32,11 @@
# where /dev/input/eventXX is the pointingstick in question. If in
# doubt, simply use /dev/input/event* to reload all input rules.
#
-# If your changes are generally applicable, open a bug report on
-# http://bugs.freedesktop.org/enter_bug.cgi?product=systemd
-# and include your new rules, a description of the device, and the
-# output of
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+# https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
# udevadm info /dev/input/eventXX
# (or /dev/input/event*).
#
diff --git a/man/file-hierarchy.xml b/man/file-hierarchy.xml
index 3a5627d196..058998b51f 100644
--- a/man/file-hierarchy.xml
+++ b/man/file-hierarchy.xml
@@ -86,7 +86,7 @@
<listitem><para>The boot partition used for bringing up the
system. On EFI systems this is possibly the EFI System
Partition, also see
- <citerefentry><refentrytitle>systemd-efi-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
This directory is usually strictly local to the host, and
should be considered read-only, except when a new kernel or
boot loader is installed. This directory only exists on
@@ -804,7 +804,7 @@
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>hier</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-efi-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
diff --git a/man/systemd-efi-boot-generator.xml b/man/systemd-efi-boot-generator.xml
deleted file mode 100644
index 23464bcf15..0000000000
--- a/man/systemd-efi-boot-generator.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0"?>
-<!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-<!--
- 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/>.
--->
-<refentry id="systemd-efi-boot-generator">
-
- <refentryinfo>
- <title>systemd-efi-boot-generator</title>
- <productname>systemd</productname>
-
- <authorgroup>
- <author>
- <contrib>Developer</contrib>
- <firstname>Lennart</firstname>
- <surname>Poettering</surname>
- <email>lennart@poettering.net</email>
- </author>
- </authorgroup>
- </refentryinfo>
-
- <refmeta>
- <refentrytitle>systemd-efi-boot-generator</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
-
- <refnamediv>
- <refname>systemd-efi-boot-generator</refname>
- <refpurpose>Generator for automatically mounting the
- EFI System Partition used by the current boot to
- <filename>/boot</filename></refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <para><filename>/usr/lib/systemd/system-generators/systemd-efi-boot-generator</filename></para>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para><filename>systemd-efi-boot-generator</filename> is a
- generator that automatically creates mount and automount units for
- the EFI System Partition (ESP), mounting it to
- <filename>/boot</filename>. Note that this generator will execute
- no operation on non-EFI systems, on systems where the boot loader
- does not communicate the used ESP to the OS, on systems where
- <filename>/boot</filename> is an explicitly configured mount (for
- example, listed in
- <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
- or where the <filename>/boot</filename> mount point is non-empty.
- Since this generator creates an automount unit, the mount will
- only be activated on-demand, when accessed.</para>
-
- <para><filename>systemd-efi-boot-generator</filename> implements
- <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd.automount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- </para>
- </refsect1>
-
-</refentry>
diff --git a/man/systemd-fsck@.service.xml b/man/systemd-fsck@.service.xml
index e4ffcba168..6d05e90e7b 100644
--- a/man/systemd-fsck@.service.xml
+++ b/man/systemd-fsck@.service.xml
@@ -61,7 +61,7 @@
responsible for file system checks. They are instantiated for each
device that is configured for file system checking.
<filename>systemd-fsck-root.service</filename> is responsible for
- file system checks on the root file system, but in only if the
+ file system checks on the root file system, but only if the
root filesystem wasn't checked in the initramfs.
<filename>systemd-fsck@.service</filename> is used for all other
file systems and for the root file system in the initramfs.</para>
diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml
index 710c2e065e..27ec72c986 100644
--- a/man/systemd-gpt-auto-generator.xml
+++ b/man/systemd-gpt-auto-generator.xml
@@ -150,10 +150,16 @@
<filename>/etc/crypttab</filename> with a different device mapper
device name.</para>
- <para>Also note that
- <citerefentry><refentrytitle>systemd-efi-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- will mount the EFI System Partition (ESP) to
- <filename>/boot</filename> if not otherwise mounted.</para>
+ <para>Mount and automount units for the EFI System Partition (ESP),
+ mounting it to <filename>/boot</filename> are generated on EFI
+ systems, where the boot loader communicates the used ESP to the operating
+ system. Since this generator creates an automount unit, the mount will
+ only be activated on-demand, when accessed. On systems where
+ <filename>/boot</filename> is an explicitly configured mount
+ (for example, listed in
+ <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
+ or where the <filename>/boot</filename> mount point is non-empty, no
+ mount units are generated.</para>
<para>When using this generator in conjunction with btrfs file
systems, make sure to set the correct default subvolumes on them,
@@ -170,7 +176,6 @@
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-efi-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
diff --git a/man/systemd-machine-id-commit.xml b/man/systemd-machine-id-commit.xml
index cfb1722063..d216aa0745 100644
--- a/man/systemd-machine-id-commit.xml
+++ b/man/systemd-machine-id-commit.xml
@@ -66,7 +66,7 @@
<para>This tool will execute no operation if
<filename>/etc/machine-id</filename> doesn't contain any valid
machine ID, isn't mounted as an independent temporary file system,
- of <filename>/etc</filename> is read-only. If those conditions are
+ or <filename>/etc</filename> is read-only. If those conditions are
met, it will then write current machine ID to disk and unmount the
transient <filename>/etc/machine-id</filename> file in a race-free
manner to ensure that this file is always valid for other
diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml
index f53b337daa..bcc5776a8d 100644
--- a/man/systemd-networkd-wait-online.service.xml
+++ b/man/systemd-networkd-wait-online.service.xml
@@ -80,7 +80,8 @@
several interfaces which will be configured, but a particular
one is necessary to access some network resources. This option
may be used more than once to wait for multiple network
- interfaces.</para></listitem>
+ interfaces. When used, all other interfaces are ignored.
+ </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--ignore=</option></term>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 06285edc0b..d1e050cb89 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -156,12 +156,15 @@
<para>If neither <option>--directory=</option>, nor
<option>--image=</option> is specified the directory is
- determined as <filename>/var/lib/machines/</filename> suffixed
- by the machine name as specified with
- <option>--machine=</option>. If neither
- <option>--directory=</option>, <option>--image=</option>, nor
- <option>--machine=</option> are specified, the current
- directory will be used. May not be specified together with
+ determined by searching for a directory named the same as the
+ machine name specified with <option>--machine=</option>. See
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ section "Files and Directories" for the precise search path.</para>
+
+ <para>If neither <option>--directory=</option>,
+ <option>--image=</option>, nor <option>--machine=</option>
+ are specified, the current directory will
+ be used. May not be specified together with
<option>--image=</option>.</para></listitem>
</varlistentry>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 45a4422dc3..8fd75d274e 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -263,7 +263,8 @@
<listitem><para>Similar to <varname>Environment=</varname> but
reads the environment variables from a text file. The text
file should contain new-line-separated variable assignments.
- Empty lines and lines starting with ; or # will be ignored,
+ Empty lines, lines without an <literal>=</literal> separator,
+ or lines starting with ; or # will be ignored,
which may be used for commenting. A line ending with a
backslash will be concatenated with the following one,
allowing multiline variable definitions. The parser strips
diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml
index 2285e91812..4514c1afdf 100644
--- a/man/systemd.generator.xml
+++ b/man/systemd.generator.xml
@@ -331,7 +331,6 @@ find $dir</programlisting>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-efi-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-getty-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 6287a00f9d..a78ceb1252 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -135,6 +135,9 @@
<row><entry><varname>macvlan</varname></entry>
<entry>A macvlan device is a stacked device which receives packets from its underlying device based on MAC address filtering.</entry></row>
+ <row><entry><varname>macvtap</varname></entry>
+ <entry>A macvtap device is a stacked device which receives packets from its underlying device based on MAC address filtering.</entry></row>
+
<row><entry><varname>sit</varname></entry>
<entry>An IPv6 over IPv4 tunnel.</entry></row>
@@ -316,6 +319,15 @@
</refsect1>
+ <refsect1>
+ <title>[MACVTAP] Section Options</title>
+
+ <para>The <literal>[MACVTAP]</literal> section applies for
+ netdevs of kind <literal>macvtap</literal> and accepts the
+ same key as <literal>[MACVLAN].</literal> </para>
+
+ </refsect1>
+
<refsect1>
<title>[IPVLAN] Section Options</title>
@@ -429,6 +441,15 @@
<para>A boolean. When true receiving zero checksums in VXLAN/IPv6 is turned on.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>GroupPolicyExtension=</varname></term>
+ <listitem>
+ <para>A boolean. When true it enables Group Policy VXLAN extension security label mechanism
+ across network peers based on VXLAN. For details about the Group Policy VXLAN see the
+ <ulink url="https://tools.ietf.org/html/draft-smith-vxlan-group-policy">
+ VXLAN Group Policy </ulink> document. Defaults to false.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
@@ -586,7 +607,7 @@
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>VnetHeader=</varname></term>
+ <term><varname>VNetHeader=</varname></term>
<listitem><para>Takes a boolean argument. Configures
IFF_VNET_HDR flag for a tap device. It allows sending
and receiving larger Generic Segmentation Offload (GSO)
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 0aa1eeac77..407f6d32eb 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -737,7 +737,7 @@
to 0 (job timeouts disabled), except for device units. NB:
this timeout is independent from any unit-specific timeout
(for example, the timeout set with
- <varname>StartTimeoutSec=</varname> in service units) as the
+ <varname>TimeoutStartSec=</varname> in service units) as the
job timeout has no effect on the unit itself, only on the job
that might be pending for it. Or in other words: unit-specific
timeouts are useful to abort unit state changes, and revert
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index c0863e4167..db1e7f3f37 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -88,6 +88,18 @@ struct boot_times {
usec_t generators_finish_time;
usec_t unitsload_start_time;
usec_t unitsload_finish_time;
+
+ /*
+ * If we're analyzing the user instance, all timestamps will be offset
+ * by its own start-up timestamp, which may be arbitrarily big.
+ * With "plot", this causes arbitrarily wide output SVG files which almost
+ * completely consist of empty space. Thus we cancel out this offset.
+ *
+ * This offset is subtracted from times above by acquire_boot_times(),
+ * but it still needs to be subtracted from unit-specific timestamps
+ * (so it is stored here for reference).
+ */
+ usec_t reverse_offset;
};
struct unit_times {
@@ -188,95 +200,13 @@ static void free_unit_times(struct unit_times *t, unsigned n) {
free(t);
}
-static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r, c = 0;
- struct unit_times *unit_times = NULL;
- size_t size = 0;
- UnitInfo u;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnits",
- &error, &reply,
- NULL);
- if (r < 0) {
- log_error("Failed to list units: %s", bus_error_message(&error, -r));
- goto fail;
- }
+static void subtract_timestamp(usec_t *a, usec_t b) {
+ assert(a);
- r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
- if (r < 0) {
- bus_log_parse_error(r);
- goto fail;
+ if (*a > 0) {
+ assert(*a >= b);
+ *a -= b;
}
-
- while ((r = bus_parse_unit_info(reply, &u)) > 0) {
- struct unit_times *t;
-
- if (!GREEDY_REALLOC(unit_times, size, c+1)) {
- r = log_oom();
- goto fail;
- }
-
- t = unit_times+c;
- t->name = NULL;
-
- assert_cc(sizeof(usec_t) == sizeof(uint64_t));
-
- if (bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "InactiveExitTimestampMonotonic",
- &t->activating) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "ActiveEnterTimestampMonotonic",
- &t->activated) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "ActiveExitTimestampMonotonic",
- &t->deactivating) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "InactiveEnterTimestampMonotonic",
- &t->deactivated) < 0) {
- r = -EIO;
- goto fail;
- }
-
- if (t->activated >= t->activating)
- t->time = t->activated - t->activating;
- else if (t->deactivated >= t->activating)
- t->time = t->deactivated - t->activating;
- else
- t->time = 0;
-
- if (t->activating == 0)
- continue;
-
- t->name = strdup(u.id);
- if (t->name == NULL) {
- r = log_oom();
- goto fail;
- }
- c++;
- }
- if (r < 0) {
- bus_log_parse_error(r);
- goto fail;
- }
-
- *out = unit_times;
- return c;
-
-fail:
- if (unit_times)
- free_unit_times(unit_times, (unsigned) c);
- return r;
}
static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
@@ -355,10 +285,30 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
return -EINPROGRESS;
}
- if (times.initrd_time)
- times.kernel_done_time = times.initrd_time;
- else
- times.kernel_done_time = times.userspace_time;
+ if (arg_user) {
+ /*
+ * User-instance-specific timestamps processing
+ * (see comment to reverse_offset in struct boot_times).
+ */
+ times.reverse_offset = times.userspace_time;
+
+ times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
+ subtract_timestamp(&times.finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.security_start_time, times.reverse_offset);
+ subtract_timestamp(&times.security_finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.generators_start_time, times.reverse_offset);
+ subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
+ subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
+ } else {
+ if (times.initrd_time)
+ times.kernel_done_time = times.initrd_time;
+ else
+ times.kernel_done_time = times.userspace_time;
+ }
cached = true;
@@ -378,6 +328,107 @@ static void free_host_info(struct host_info *hi) {
free(hi);
}
+static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r, c = 0;
+ struct boot_times *boot_times = NULL;
+ struct unit_times *unit_times = NULL;
+ size_t size = 0;
+ UnitInfo u;
+
+ r = acquire_boot_times(bus, &boot_times);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits",
+ &error, &reply,
+ NULL);
+ if (r < 0) {
+ log_error("Failed to list units: %s", bus_error_message(&error, -r));
+ goto fail;
+ }
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto fail;
+ }
+
+ while ((r = bus_parse_unit_info(reply, &u)) > 0) {
+ struct unit_times *t;
+
+ if (!GREEDY_REALLOC(unit_times, size, c+1)) {
+ r = log_oom();
+ goto fail;
+ }
+
+ t = unit_times+c;
+ t->name = NULL;
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+
+ if (bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "InactiveExitTimestampMonotonic",
+ &t->activating) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveEnterTimestampMonotonic",
+ &t->activated) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveExitTimestampMonotonic",
+ &t->deactivating) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "InactiveEnterTimestampMonotonic",
+ &t->deactivated) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ subtract_timestamp(&t->activating, boot_times->reverse_offset);
+ subtract_timestamp(&t->activated, boot_times->reverse_offset);
+ subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+ subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+
+ if (t->activated >= t->activating)
+ t->time = t->activated - t->activating;
+ else if (t->deactivated >= t->activating)
+ t->time = t->deactivated - t->activating;
+ else
+ t->time = 0;
+
+ if (t->activating == 0)
+ continue;
+
+ t->name = strdup(u.id);
+ if (t->name == NULL) {
+ r = log_oom();
+ goto fail;
+ }
+ c++;
+ }
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto fail;
+ }
+
+ *out = unit_times;
+ return c;
+
+fail:
+ if (unit_times)
+ free_unit_times(unit_times, (unsigned) c);
+ return r;
+}
+
static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
int r;
struct host_info *host;
@@ -450,10 +501,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
- if (t->kernel_time > 0)
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
- else
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
+ strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
ptr = strdup(buf);
if (!ptr)
diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c
index bf9d8d4d7c..7ea3357031 100644
--- a/src/basic/bitmap.c
+++ b/src/basic/bitmap.c
@@ -145,7 +145,10 @@ bool bitmap_isclear(Bitmap *b) {
void bitmap_clear(Bitmap *b) {
assert(b);
+ free(b->bitmaps);
+ b->bitmaps = NULL;
b->n_bitmaps = 0;
+ b->bitmaps_allocated = 0;
}
bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
@@ -184,6 +187,9 @@ bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
}
bool bitmap_equal(Bitmap *a, Bitmap *b) {
+ size_t common_n_bitmaps;
+ Bitmap *c;
+ unsigned i;
if (!a ^ !b)
return false;
@@ -191,8 +197,14 @@ bool bitmap_equal(Bitmap *a, Bitmap *b) {
if (!a)
return true;
- if (a->n_bitmaps != b->n_bitmaps)
+ common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps);
+ if (memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0)
return false;
- return memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * a->n_bitmaps) == 0;
+ c = a->n_bitmaps > b->n_bitmaps ? a : b;
+ for (i = common_n_bitmaps; i < c->n_bitmaps; i++)
+ if (c->bitmaps[i] != 0)
+ return false;
+
+ return true;
}
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index 2fde3e107e..2dcc9c5575 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -253,6 +253,7 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
char *buf = NULL;
size_t sz = 0;
FILE *f;
+ int r;
assert(c);
assert(p);
@@ -278,12 +279,11 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
fputc(':', f);
format_chain(f, 2, c->second);
- fflush(f);
-
- if (ferror(f)) {
+ r = fflush_and_check(f);
+ if (r < 0) {
free(buf);
fclose(f);
- return -ENOMEM;
+ return r;
}
fclose(f);
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 34a3060509..6b3162a35f 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -826,14 +826,12 @@ int cg_install_release_agent(const char *controller, const char *agent) {
} else if (!streq(sc, agent))
return -EEXIST;
- free(fs);
- fs = NULL;
+ fs = mfree(fs);
r = cg_get_path(controller, NULL, "notify_on_release", &fs);
if (r < 0)
return r;
- free(contents);
- contents = NULL;
+ contents = mfree(contents);
r = read_one_line_file(fs, &contents);
if (r < 0)
return r;
@@ -865,8 +863,7 @@ int cg_uninstall_release_agent(const char *controller) {
if (r < 0)
return r;
- free(fs);
- fs = NULL;
+ fs = mfree(fs);
r = cg_get_path(controller, NULL, "release_agent", &fs);
if (r < 0)
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e2d356d676..33427c6a73 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -467,8 +467,7 @@ int copy_xattr(int fdf, int fdt) {
sza *= 2;
- free(bufa);
- bufa = NULL;
+ bufa = mfree(bufa);
}
p = bufa;
@@ -491,8 +490,7 @@ int copy_xattr(int fdf, int fdt) {
if (m < 0) {
if (errno == ERANGE) {
szb *= 2;
- free(bufb);
- bufb = NULL;
+ bufb = mfree(bufb);
continue;
}
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 2216853777..4a9105f421 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -28,21 +28,15 @@
#include "fileio.h"
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
+
assert(f);
assert(line);
- errno = 0;
-
fputs(line, f);
if (enforce_newline && !endswith(line, "\n"))
fputc('\n', f);
- fflush(f);
-
- if (ferror(f))
- return errno ? -errno : -EIO;
-
- return 0;
+ return fflush_and_check(f);
}
static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
diff --git a/src/basic/list.h b/src/basic/list.h
index 2939216adb..760abcdab3 100644
--- a/src/basic/list.h
+++ b/src/basic/list.h
@@ -123,6 +123,32 @@
} \
} while(false)
+/* Insert an item before another one (a = where, b = what) */
+#define LIST_INSERT_BEFORE(name,head,a,b) \
+ do { \
+ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
+ assert(_b); \
+ if (!_a) { \
+ if (!*_head) { \
+ _b->name##_next = NULL; \
+ _b->name##_prev = NULL; \
+ *_head = _b; \
+ } else { \
+ typeof(*(head)) *_tail = (head); \
+ while (_tail->name##_next) \
+ _tail = _tail->name##_next; \
+ _b->name##_next = NULL; \
+ _b->name##_prev = _tail; \
+ _tail->name##_next = _b; \
+ } \
+ } else { \
+ if ((_b->name##_prev = _a->name##_prev)) \
+ _b->name##_prev->name##_next = _b; \
+ _b->name##_next = _a; \
+ _a->name##_prev = _b; \
+ } \
+ } while(false)
+
#define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next) \
diff --git a/src/basic/missing.h b/src/basic/missing.h
index ed6cd80c75..34ab0254dd 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -977,7 +977,11 @@ static inline int raw_clone(unsigned long flags, void *child_stack) {
}
static inline pid_t raw_getpid(void) {
+#if defined(__alpha__)
+ return (pid_t) syscall(__NR_getxpid);
+#else
return (pid_t) syscall(__NR_getpid);
+#endif
}
#if !HAVE_DECL_RENAMEAT2
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
index 047aa294f4..6d5c205117 100644
--- a/src/basic/smack-util.c
+++ b/src/basic/smack-util.c
@@ -32,109 +32,93 @@
#define SMACK_FLOOR_LABEL "_"
#define SMACK_STAR_LABEL "*"
-bool mac_smack_use(void) {
#ifdef HAVE_SMACK
+bool mac_smack_use(void) {
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;
+static const char* const smack_attr_table[_SMACK_ATTR_MAX] = {
+ [SMACK_ATTR_ACCESS] = "security.SMACK64",
+ [SMACK_ATTR_EXEC] = "security.SMACK64EXEC",
+ [SMACK_ATTR_MMAP] = "security.SMACK64MMAP",
+ [SMACK_ATTR_TRANSMUTE] = "security.SMACK64TRANSMUTE",
+ [SMACK_ATTR_IPIN] = "security.SMACK64IPIN",
+ [SMACK_ATTR_IPOUT] = "security.SMACK64IPOUT",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(smack_attr, SmackAttr);
+int mac_smack_read(const char *path, SmackAttr attr, char **label) {
assert(path);
+ assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
+ assert(label);
-#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;
+ return getxattr_malloc(path, smack_attr_to_string(attr), label, true);
}
-int mac_smack_apply_fd(int fd, const char *label) {
- int r = 0;
-
+int mac_smack_read_fd(int fd, SmackAttr attr, char **label) {
assert(fd >= 0);
+ assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
+ assert(label);
-#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;
+ return fgetxattr_malloc(fd, smack_attr_to_string(attr), label);
}
-int mac_smack_apply_ip_out_fd(int fd, const char *label) {
- int r = 0;
+int mac_smack_apply(const char *path, SmackAttr attr, const char *label) {
+ int r;
- assert(fd >= 0);
+ assert(path);
+ assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
-#ifdef HAVE_SMACK
if (!mac_smack_use())
return 0;
if (label)
- r = fsetxattr(fd, "security.SMACK64IPOUT", label, strlen(label), 0);
+ r = lsetxattr(path, smack_attr_to_string(attr), label, strlen(label), 0);
else
- r = fremovexattr(fd, "security.SMACK64IPOUT");
+ r = lremovexattr(path, smack_attr_to_string(attr));
if (r < 0)
return -errno;
-#endif
- return r;
+ return 0;
}
-int mac_smack_apply_ip_in_fd(int fd, const char *label) {
- int r = 0;
+int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
+ int r;
assert(fd >= 0);
+ assert(attr >= 0 && attr < _SMACK_ATTR_MAX);
-#ifdef HAVE_SMACK
if (!mac_smack_use())
return 0;
if (label)
- r = fsetxattr(fd, "security.SMACK64IPIN", label, strlen(label), 0);
+ r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0);
else
- r = fremovexattr(fd, "security.SMACK64IPIN");
+ r = fremovexattr(fd, smack_attr_to_string(attr));
if (r < 0)
return -errno;
-#endif
- return r;
+ return 0;
}
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;
@@ -142,21 +126,16 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
r = write_string_file(p, label, 0);
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;
@@ -202,7 +181,37 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path);
}
-#endif
return r;
}
+
+
+#else
+bool mac_smack_use(void) {
+ return false;
+}
+
+int mac_smack_read(const char *path, SmackAttr attr, char **label) {
+ return -EOPNOTSUPP;
+}
+
+int mac_smack_read_fd(int fd, SmackAttr attr, char **label) {
+ return -EOPNOTSUPP;
+}
+
+int mac_smack_apply(const char *path, SmackAttr attr, const char *label) {
+ return 0;
+}
+
+int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
+ return 0;
+}
+
+int mac_smack_apply_pid(pid_t pid, const char *label) {
+ return 0;
+}
+
+int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
+ return 0;
+}
+#endif
diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h
index 50f55b1f4b..1052cecf4c 100644
--- a/src/basic/smack-util.h
+++ b/src/basic/smack-util.h
@@ -25,12 +25,28 @@
#include <stdbool.h>
+#include "macro.h"
+
+typedef enum SmackAttr {
+ SMACK_ATTR_ACCESS = 0,
+ SMACK_ATTR_EXEC = 1,
+ SMACK_ATTR_MMAP = 2,
+ SMACK_ATTR_TRANSMUTE = 3,
+ SMACK_ATTR_IPIN = 4,
+ SMACK_ATTR_IPOUT = 5,
+ _SMACK_ATTR_MAX,
+ _SMACK_ATTR_INVALID = -1,
+} SmackAttr;
+
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);
+const char* smack_attr_to_string(SmackAttr i) _const_;
+SmackAttr smack_attr_from_string(const char *s) _pure_;
+int mac_smack_read(const char *path, SmackAttr attr, char **label);
+int mac_smack_read_fd(int fd, SmackAttr attr, char **label);
+int mac_smack_apply(const char *path, SmackAttr attr, const char *label);
+int mac_smack_apply_fd(int fd, SmackAttr attr, 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-util.c b/src/basic/socket-util.c
index e8bb10dc9b..8fd3149276 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -583,7 +583,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
} else {
p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
- if (!ret)
+ if (!p)
return -ENOMEM;
}
@@ -662,13 +662,13 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
if (r < 0)
- return log_error_errno(r, "sockadd_pretty() failed: %m");
+ return r;
log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
} else {
ret = strdup(host);
if (!ret)
- return log_oom();
+ return -ENOMEM;
}
*_ret = ret;
@@ -683,7 +683,7 @@ int getnameinfo_pretty(int fd, char **ret) {
assert(ret);
if (getsockname(fd, &sa.sa, &salen) < 0)
- return log_error_errno(errno, "getsockname(%d) failed: %m", fd);
+ return -errno;
return socknameinfo_pretty(&sa, salen, ret);
}
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 042b88f222..cf55263bbf 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -44,7 +44,7 @@ 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);
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return -errno;
@@ -230,14 +230,14 @@ int reset_terminal_fd(int fd, bool switch_to_text) {
* interfere with that. */
/* Disable exclusive mode, just in case */
- ioctl(fd, TIOCNXCL);
+ (void) ioctl(fd, TIOCNXCL);
/* Switch to text mode */
if (switch_to_text)
- ioctl(fd, KDSETMODE, KD_TEXT);
+ (void) ioctl(fd, KDSETMODE, KD_TEXT);
/* Enable console unicode mode */
- ioctl(fd, KDSKBMODE, K_UNICODE);
+ (void) ioctl(fd, KDSKBMODE, K_UNICODE);
if (tcgetattr(fd, &termios) < 0) {
r = -errno;
@@ -276,7 +276,7 @@ int reset_terminal_fd(int fd, bool switch_to_text) {
finish:
/* Just in case, flush all crap out */
- tcflush(fd, TCIOFLUSH);
+ (void) tcflush(fd, TCIOFLUSH);
return r;
}
@@ -284,7 +284,11 @@ finish:
int reset_terminal(const char *name) {
_cleanup_close_ int fd = -1;
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ /* We open the terminal with O_NONBLOCK here, to ensure we
+ * don't block on carrier if this is a terminal with carrier
+ * configured. */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return fd;
@@ -304,7 +308,8 @@ int open_terminal(const char *name, int mode) {
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
*/
- assert(!(mode & O_CREAT));
+ if (mode & O_CREAT)
+ return -EINVAL;
for (;;) {
fd = open(name, mode, 0);
@@ -413,9 +418,8 @@ int acquire_terminal(
if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
r = 0;
- if (r < 0 && (force || fail || r != -EPERM)) {
+ if (r < 0 && (force || fail || r != -EPERM))
goto fail;
- }
if (r >= 0)
break;
@@ -499,7 +503,7 @@ int release_terminal(void) {
struct sigaction sa_old;
int r = 0;
- fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
+ fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return -errno;
@@ -527,7 +531,7 @@ int terminal_vhangup_fd(int fd) {
int terminal_vhangup(const char *name) {
_cleanup_close_ int fd;
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return fd;
@@ -574,7 +578,7 @@ int vt_disallocate(const char *name) {
return -EINVAL;
/* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return fd;
@@ -612,16 +616,16 @@ void warn_melody(void) {
/* Yeah, this is synchronous. Kinda sucks. But well... */
- ioctl(fd, KIOCSOUND, (int)(1193180/440));
+ (void) ioctl(fd, KIOCSOUND, (int)(1193180/440));
usleep(125*USEC_PER_MSEC);
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
usleep(125*USEC_PER_MSEC);
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
usleep(125*USEC_PER_MSEC);
- ioctl(fd, KIOCSOUND, 0);
+ (void) ioctl(fd, KIOCSOUND, 0);
}
int make_console_stdio(void) {
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index bf52463d81..fa530da456 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -673,6 +673,7 @@ int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, c
int slice_build_parent_slice(const char *slice, char **ret) {
char *s, *dash;
+ int r;
assert(slice);
assert(ret);
@@ -693,11 +694,11 @@ int slice_build_parent_slice(const char *slice, char **ret) {
if (dash)
strcpy(dash, ".slice");
else {
- free(s);
-
- s = strdup("-.slice");
- if (!s)
- return -ENOMEM;
+ r = free_and_strdup(&s, "-.slice");
+ if (r < 0) {
+ free(s);
+ return r;
+ }
}
*ret = s;
diff --git a/src/basic/util.c b/src/basic/util.c
index f81bf4e31e..d4d3d3c83a 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -115,17 +115,23 @@ size_t page_size(void) {
return pgsz;
}
-bool streq_ptr(const char *a, const char *b) {
-
- /* Like streq(), but tries to make sense of NULL pointers */
+int strcmp_ptr(const char *a, const char *b) {
+ /* Like strcmp(), but tries to make sense of NULL pointers */
if (a && b)
- return streq(a, b);
+ return strcmp(a, b);
- if (!a && !b)
- return true;
+ if (!a && b)
+ return -1;
- return false;
+ if (a && !b)
+ return 1;
+
+ return 0;
+}
+
+bool streq_ptr(const char *a, const char *b) {
+ return strcmp_ptr(a, b) == 0;
}
char* endswith(const char *s, const char *postfix) {
@@ -954,7 +960,12 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
return 0;
}
-/* https://tools.ietf.org/html/rfc4648#section-6 */
+/* https://tools.ietf.org/html/rfc4648#section-6
+ * Notice that base32hex differs from base32 in the alphabet it uses.
+ * The distinction is that the base32hex representation preserves the
+ * order of the underlying data when compared as bytestrings, this is
+ * useful when representing NSEC3 hashes, as one can then verify the
+ * order of hashes directly from their representation. */
char base32hexchar(int x) {
static const char table[32] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUV";
@@ -6592,3 +6603,73 @@ int reset_uid_gid(void) {
return 0;
}
+
+int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) {
+ char *v;
+ size_t l;
+ ssize_t n;
+
+ assert(path);
+ assert(name);
+ assert(value);
+
+ for (l = 100; ; l = (size_t) n + 1) {
+ v = new0(char, l);
+ if (!v)
+ return -ENOMEM;
+
+ if (allow_symlink)
+ n = lgetxattr(path, name, v, l);
+ else
+ n = getxattr(path, name, v, l);
+
+ if (n >= 0 && (size_t) n < l) {
+ *value = v;
+ return n;
+ }
+
+ free(v);
+
+ if (n < 0 && errno != ERANGE)
+ return -errno;
+
+ if (allow_symlink)
+ n = lgetxattr(path, name, NULL, 0);
+ else
+ n = getxattr(path, name, NULL, 0);
+ if (n < 0)
+ return -errno;
+ }
+}
+
+int fgetxattr_malloc(int fd, const char *name, char **value) {
+ char *v;
+ size_t l;
+ ssize_t n;
+
+ assert(fd >= 0);
+ assert(name);
+ assert(value);
+
+ for (l = 100; ; l = (size_t) n + 1) {
+ v = new0(char, l);
+ if (!v)
+ return -ENOMEM;
+
+ n = fgetxattr(fd, name, v, l);
+
+ if (n >= 0 && (size_t) n < l) {
+ *value = v;
+ return n;
+ }
+
+ free(v);
+
+ if (n < 0 && errno != ERANGE)
+ return -errno;
+
+ n = fgetxattr(fd, name, NULL, 0);
+ if (n < 0)
+ return -errno;
+ }
+}
diff --git a/src/basic/util.h b/src/basic/util.h
index 5c0130d265..426b7f7d16 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -71,6 +71,7 @@ size_t page_size(void) _pure_;
#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
bool streq_ptr(const char *a, const char *b) _pure_;
+int strcmp_ptr(const char *a, const char *b) _pure_;
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
@@ -84,6 +85,11 @@ bool streq_ptr(const char *a, const char *b) _pure_;
#define malloc0(n) (calloc((n), 1))
+static inline void *mfree(void *memory) {
+ free(memory);
+ return NULL;
+}
+
static inline const char* yes_no(bool b) {
return b ? "yes" : "no";
}
@@ -918,3 +924,6 @@ int parse_mode(const char *s, mode_t *ret);
int mount_move_root(const char *path);
int reset_uid_gid(void);
+
+int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink);
+int fgetxattr_malloc(int fd, const char *name, char **value);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 091ea375d3..359fde9998 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -489,9 +489,9 @@ static int copy_file(const char *from, const char *to, bool force) {
}
} while (!feof(f));
- fflush(g);
- if (ferror(g)) {
- r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
+ r = fflush_and_check(g);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write \"%s\": %m", to);
goto error;
}
@@ -519,7 +519,7 @@ static int copy_file(const char *from, const char *to, bool force) {
return 0;
error:
- unlink(p);
+ (void) unlink(p);
return r;
}
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index e8cd8abd26..4ac193e22a 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -22,6 +22,7 @@
#include "console.h"
#include "graphics.h"
#include "pefile.h"
+#include "disk.h"
#include "linux.h"
#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
@@ -1139,13 +1140,11 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
config_add_entry(config, entry);
}
-static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
- EFI_FILE_HANDLE entries_dir;
- EFI_STATUS err;
+static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
CHAR8 *content = NULL;
UINTN sec;
UINTN len;
- UINTN i;
+ EFI_STATUS err;
len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content);
if (len > 0)
@@ -1158,6 +1157,11 @@ static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir,
config->timeout_sec = sec;
} else
config->timeout_sec_efivar = -1;
+}
+
+static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+ EFI_FILE_HANDLE entries_dir;
+ EFI_STATUS err;
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
if (!EFI_ERROR(err)) {
@@ -1194,8 +1198,11 @@ static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir,
}
uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
}
+}
+
+static VOID config_sort_entries(Config *config) {
+ UINTN i;
- /* sort entries after version number */
for (i = 1; i < config->entry_count; i++) {
BOOLEAN more;
UINTN k;
@@ -1696,11 +1703,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_LOADED_IMAGE *loaded_image;
EFI_FILE *root_dir;
CHAR16 *loaded_image_path;
- EFI_DEVICE_PATH *device_path;
EFI_STATUS err;
Config config;
UINT64 init_usec;
BOOLEAN menu = FALSE;
+ CHAR16 uuid[37];
InitializeLib(image, sys_table);
init_usec = time_usec();
@@ -1722,29 +1729,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
/* export the device path this image is started from */
- device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
- if (device_path) {
- EFI_DEVICE_PATH *path, *paths;
-
- paths = UnpackDevicePath(device_path);
- for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
- HARDDRIVE_DEVICE_PATH *drive;
- CHAR16 uuid[37];
-
- if (DevicePathType(path) != MEDIA_DEVICE_PATH)
- continue;
- if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
- continue;
- drive = (HARDDRIVE_DEVICE_PATH *)path;
- if (drive->SignatureType != SIGNATURE_TYPE_GUID)
- continue;
-
- GuidToString(uuid, (EFI_GUID *)&drive->Signature);
- efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
- break;
- }
- FreePool(paths);
- }
+ if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
+ efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
root_dir = LibOpenRoot(loaded_image->DeviceHandle);
if (!root_dir) {
@@ -1758,12 +1744,19 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
loaded_image_path = DevicePathToStr(loaded_image->FilePath);
efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
- /* scan "\loader\entries\*.conf" files */
ZeroMem(&config, sizeof(Config));
- config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+ config_load_defaults(&config, root_dir);
- /* if we find some well-known loaders, add them to the end of the list */
+ /* scan /EFI/Linux/ directory */
config_entry_add_linux(&config, loaded_image, root_dir);
+
+ /* scan /loader/entries/\*.conf files */
+ config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+
+ /* sort entries after version number */
+ config_sort_entries(&config);
+
+ /* if we find some well-known loaders, add them to the end of the list */
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
diff --git a/src/boot/efi/disk.c b/src/boot/efi/disk.c
new file mode 100644
index 0000000000..96063fbc28
--- /dev/null
+++ b/src/boot/efi/disk.c
@@ -0,0 +1,51 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+
+EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]) {
+ EFI_DEVICE_PATH *device_path;
+ EFI_STATUS r = EFI_NOT_FOUND;
+
+ /* export the device path this image is started from */
+ device_path = DevicePathFromHandle(handle);
+ if (device_path) {
+ EFI_DEVICE_PATH *path, *paths;
+
+ paths = UnpackDevicePath(device_path);
+ for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
+ HARDDRIVE_DEVICE_PATH *drive;
+
+ if (DevicePathType(path) != MEDIA_DEVICE_PATH)
+ continue;
+ if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
+ continue;
+ drive = (HARDDRIVE_DEVICE_PATH *)path;
+ if (drive->SignatureType != SIGNATURE_TYPE_GUID)
+ continue;
+
+ GuidToString(uuid, (EFI_GUID *)&drive->Signature);
+ r = EFI_SUCCESS;
+ break;
+ }
+ FreePool(paths);
+ }
+
+ return r;
+}
diff --git a/src/boot/efi/disk.h b/src/boot/efi/disk.h
new file mode 100644
index 0000000000..1b25343a00
--- /dev/null
+++ b/src/boot/efi/disk.h
@@ -0,0 +1,21 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#ifndef __SDBOOT_DISK_H
+#define __SDBOOT_DISK_H
+
+EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]);
+#endif
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 0b1bc491ed..0c5ee4e9ff 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -18,6 +18,7 @@
#include "util.h"
#include "pefile.h"
+#include "disk.h"
#include "graphics.h"
#include "splash.h"
#include "linux.h"
@@ -46,6 +47,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
UINTN szs[ELEMENTSOF(sections)-1] = {};
CHAR8 *cmdline = NULL;
UINTN cmdline_len;
+ CHAR16 uuid[37];
EFI_STATUS err;
InitializeLib(image, sys_table);
@@ -99,6 +101,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
cmdline = line;
}
+ /* export the device path this image is started from */
+ if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
+ efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
+
if (szs[3] > 0)
graphics_splash((UINT8 *)((UINTN)loaded_image->ImageBase + addrs[3]), szs[3], NULL);
diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c
index 1625d51fa8..322cec84a9 100644
--- a/src/bootchart/bootchart.c
+++ b/src/bootchart/bootchart.c
@@ -367,7 +367,7 @@ int main(int argc, char *argv[]) {
struct timespec n;
double uptime;
- clock_gettime(CLOCK_BOOTTIME, &n);
+ clock_gettime(clock_boottime_or_monotonic(), &n);
uptime = (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
log_start = gettime_ns();
diff --git a/src/bus-proxyd/bus-xml-policy.c b/src/bus-proxyd/bus-xml-policy.c
index dab5acbcb4..9a3b451c56 100644
--- a/src/bus-proxyd/bus-xml-policy.c
+++ b/src/bus-proxyd/bus-xml-policy.c
@@ -586,10 +586,8 @@ static int file_load(Policy *p, const char *path) {
case POLICY_ITEM_SEND:
case POLICY_ITEM_RECV:
- if (streq(name, "*")) {
- free(name);
- name = NULL;
- }
+ if (streq(name, "*"))
+ name = mfree(name);
break;
diff --git a/src/bus-proxyd/driver.c b/src/bus-proxyd/driver.c
index 1cb5ea5008..fa4aee691a 100644
--- a/src/bus-proxyd/driver.c
+++ b/src/bus-proxyd/driver.c
@@ -35,6 +35,7 @@
#include "driver.h"
#include "proxy.h"
#include "synthesize.h"
+#include "env-util.h"
static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
@@ -71,6 +72,27 @@ static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, s
return get_creds_by_name(bus, name, mask, _creds, error);
}
+static int driver_activation(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ ProxyActivation *activation = userdata;
+
+ /*
+ * The org.freedesktop.DBus.Peer.Ping() call returned. We don't care
+ * whether this succeeded, failed, was not implemented or timed out. We
+ * cannot assume that the target reacts to this properly. Hence, just
+ * send the reply to the activation request and be done.
+ */
+
+ m = activation->request; /* claim reference */
+
+ --activation->proxy->n_activations;
+ LIST_REMOVE(activations_by_proxy, activation->proxy->activations, activation);
+ sd_bus_slot_unref(activation->slot);
+ free(activation);
+
+ return synthetic_reply_method_return(m, "u", BUS_START_REPLY_SUCCESS);
+}
+
int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names) {
int r;
@@ -441,27 +463,29 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
name_list = (struct kdbus_info *) ((uint8_t *) a->kdbus_buffer + cmd.offset);
KDBUS_FOREACH(name, name_list, cmd.list_size) {
- const char *entry_name = NULL;
struct kdbus_item *item;
char *n;
- KDBUS_ITEM_FOREACH(item, name, items)
- if (item->type == KDBUS_ITEM_OWNED_NAME)
- entry_name = item->name.name;
-
- if (!streq_ptr(entry_name, arg0))
- continue;
-
- if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) {
- err = -ENOMEM;
- break;
+ KDBUS_ITEM_FOREACH(item, name, items) {
+ if (item->type == KDBUS_ITEM_OWNED_NAME) {
+ if (!streq_ptr(item->name.name, arg0))
+ continue;
+
+ if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) {
+ err = -ENOMEM;
+ break;
+ }
+
+ r = strv_consume(&owners, n);
+ if (r < 0) {
+ err = r;
+ break;
+ }
+ }
}
- r = strv_consume(&owners, n);
- if (r < 0) {
- err = r;
+ if (err < 0)
break;
- }
}
r = bus_kernel_cmd_free(a, cmd.offset);
@@ -585,7 +609,9 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "StartServiceByName")) {
_cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
+ ProxyActivation *activation;
const char *name;
+ uint64_t cookie;
uint32_t flags;
if (!sd_bus_message_has_signature(m, "su"))
@@ -604,21 +630,46 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
if (r != -ESRCH)
return synthetic_reply_method_errno(m, r, NULL);
- r = sd_bus_message_new_method_call(
- a,
- &msg,
- name,
- "/",
- "org.freedesktop.DBus.Peer",
- "Ping");
+ if (p->n_activations >= PROXY_ACTIVATIONS_MAX)
+ return synthetic_reply_method_errno(m, -EMFILE, NULL);
+
+ r = sd_bus_message_get_cookie(m, &cookie);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_new_method_call(a,
+ &msg,
+ name,
+ "/",
+ "org.freedesktop.DBus.Peer",
+ "Ping");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
- r = sd_bus_send(a, msg, NULL);
+ r = bus_message_seal(msg, cookie, BUS_DEFAULT_TIMEOUT);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
- return synthetic_reply_method_return(m, "u", BUS_START_REPLY_SUCCESS);
+ activation = new0(ProxyActivation, 1);
+ if (!activation)
+ return synthetic_reply_method_errno(m, -ENOMEM, NULL);
+
+ r = sd_bus_call_async(a,
+ &activation->slot,
+ msg,
+ driver_activation,
+ activation,
+ 0);
+ if (r < 0) {
+ free(activation);
+ return synthetic_reply_method_errno(m, r, NULL);
+ }
+
+ activation->proxy = p;
+ activation->request = sd_bus_message_ref(m);
+ LIST_PREPEND(activations_by_proxy, p->activations, activation);
+ ++p->n_activations;
+ return 1;
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
_cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
@@ -644,9 +695,13 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
if (!s)
return synthetic_reply_method_errno(m, -ENOMEM, NULL);
- r = strv_extend(&args, s);
- if (r < 0)
- return synthetic_reply_method_errno(m, r, NULL);
+ if (!env_assignment_is_valid(s)) {
+ log_warning("UpdateActivationEnvironment() called with invalid assignment, discarding: %s", s);
+ } else {
+ r = strv_extend(&args, s);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+ }
r = sd_bus_message_exit_container(m);
if (r < 0)
@@ -657,8 +712,8 @@ int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m,
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
- if (!args)
- return synthetic_reply_method_errno(m, -EINVAL, NULL);
+ if (strv_isempty(args)) /* nothing to do? */
+ return synthetic_reply_method_return(m, NULL);
r = sd_bus_message_new_method_call(
a,
diff --git a/src/bus-proxyd/proxy.c b/src/bus-proxyd/proxy.c
index 7163d6daef..df361ac400 100644
--- a/src/bus-proxyd/proxy.c
+++ b/src/bus-proxyd/proxy.c
@@ -261,9 +261,18 @@ int proxy_new(Proxy **out, int in_fd, int out_fd, const char *destination) {
}
Proxy *proxy_free(Proxy *p) {
+ ProxyActivation *activation;
+
if (!p)
return NULL;
+ while ((activation = p->activations)) {
+ LIST_REMOVE(activations_by_proxy, p->activations, activation);
+ sd_bus_message_unref(activation->request);
+ sd_bus_slot_unref(activation->slot);
+ free(activation);
+ }
+
sd_bus_flush_close_unref(p->local_bus);
sd_bus_flush_close_unref(p->destination_bus);
set_free_free(p->owned_names);
@@ -644,6 +653,10 @@ static int process_hello(Proxy *p, sd_bus_message *m) {
if (r < 0)
return log_error_errno(r, "Failed to append sender to NameAcquired message: %m");
+ r = sd_bus_message_set_destination(n, p->destination_bus->unique_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set destination for NameAcquired message: %m");
+
r = bus_seal_synthetic_message(p->local_bus, n);
if (r < 0)
return log_error_errno(r, "Failed to seal NameAcquired message: %m");
@@ -733,9 +746,9 @@ static int proxy_process_destination_to_local(Proxy *p) {
/* discard broadcasts that were not matched by any MATCH rule */
if (!matched && !sd_bus_message_get_destination(m)) {
if (!matched_synthetic)
- log_debug("Dropped unmatched broadcast: uid=" UID_FMT " gid=" GID_FMT" message=%s path=%s interface=%s member=%s",
- p->local_creds.uid, p->local_creds.gid, bus_message_type_to_string(m->header->type),
- strna(m->path), strna(m->interface), strna(m->member));
+ log_debug("Dropped unmatched broadcast: uid=" UID_FMT " gid=" GID_FMT " pid=" PID_FMT " message=%s path=%s interface=%s member=%s sender=%s destination=%s",
+ p->local_creds.uid, p->local_creds.gid, p->local_creds.pid, bus_message_type_to_string(m->header->type),
+ strna(m->path), strna(m->interface), strna(m->member), strna(m->sender), strna(m->destination));
return 1;
}
diff --git a/src/bus-proxyd/proxy.h b/src/bus-proxyd/proxy.h
index ccb951c109..6aac650ac9 100644
--- a/src/bus-proxyd/proxy.h
+++ b/src/bus-proxyd/proxy.h
@@ -25,6 +25,9 @@
#include "bus-xml-policy.h"
typedef struct Proxy Proxy;
+typedef struct ProxyActivation ProxyActivation;
+
+#define PROXY_ACTIVATIONS_MAX (16) /* max parallel activation requests */
struct Proxy {
sd_bus *local_bus;
@@ -37,12 +40,22 @@ struct Proxy {
Set *owned_names;
SharedPolicy *policy;
+ LIST_HEAD(ProxyActivation, activations);
+ size_t n_activations;
+
bool got_hello : 1;
bool queue_overflow : 1;
bool message_matched : 1;
bool synthetic_matched : 1;
};
+struct ProxyActivation {
+ LIST_FIELDS(ProxyActivation, activations_by_proxy);
+ Proxy *proxy;
+ sd_bus_message *request;
+ sd_bus_slot *slot;
+};
+
int proxy_new(Proxy **out, int in_fd, int out_fd, const char *dest);
Proxy *proxy_free(Proxy *p);
diff --git a/src/bus-proxyd/synthesize.c b/src/bus-proxyd/synthesize.c
index 3ecedfd575..15d99103f6 100644
--- a/src/bus-proxyd/synthesize.c
+++ b/src/bus-proxyd/synthesize.c
@@ -214,22 +214,13 @@ int synthesize_name_acquired(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m)
if (r < 0)
return r;
- r = bus_seal_synthetic_message(b, n);
+ r = sd_bus_message_set_destination(n, a->unique_name);
if (r < 0)
return r;
- /*
- * Make sure to only forward NameLost/NameAcquired messages if they
- * match an installed MATCH rule of the local client. We really must
- * not send messages the client doesn't expect.
- */
-
- r = bus_match_run(b, &b->match_callbacks, n);
- if (r >= 0 && p->message_matched)
- r = sd_bus_send(b, n, NULL);
-
- p->message_matched = false;
- p->synthetic_matched = false;
+ r = bus_seal_synthetic_message(b, n);
+ if (r < 0)
+ return r;
- return r;
+ return sd_bus_send(b, n, NULL);
}
diff --git a/src/console/Makefile b/src/console/Makefile
deleted file mode 120000
index d0b0e8e008..0000000000
--- a/src/console/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/console/consoled-display.c b/src/console/consoled-display.c
deleted file mode 100644
index 569c011dc0..0000000000
--- a/src/console/consoled-display.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@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 "consoled.h"
-#include "grdev.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-
-int display_new(Display **out, Session *s, grdev_display *display) {
- _cleanup_(display_freep) Display *d = NULL;
-
- assert(out);
- assert(s);
- assert(display);
-
- d = new0(Display, 1);
- if (!d)
- return -ENOMEM;
-
- d->session = s;
- d->grdev = display;
- d->width = grdev_display_get_width(display);
- d->height = grdev_display_get_height(display);
- LIST_PREPEND(displays_by_session, d->session->display_list, d);
-
- grdev_display_enable(display);
-
- *out = d;
- d = NULL;
- return 0;
-}
-
-Display *display_free(Display *d) {
- if (!d)
- return NULL;
-
- LIST_REMOVE(displays_by_session, d->session->display_list, d);
- free(d);
-
- return NULL;
-}
-
-void display_refresh(Display *d) {
- assert(d);
-
- d->width = grdev_display_get_width(d->grdev);
- d->height = grdev_display_get_height(d->grdev);
-}
-
-void display_render(Display *d, Workspace *w) {
- const grdev_display_target *target;
-
- assert(d);
- assert(w);
-
- GRDEV_DISPLAY_FOREACH_TARGET(d->grdev, target) {
- if (workspace_draw(w, target))
- grdev_display_flip_target(d->grdev, target);
- }
-}
diff --git a/src/console/consoled-manager.c b/src/console/consoled-manager.c
deleted file mode 100644
index 20424eb267..0000000000
--- a/src/console/consoled-manager.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@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 "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "log.h"
-#include "signal-util.h"
-#include "util.h"
-#include "consoled.h"
-#include "idev.h"
-#include "grdev.h"
-#include "sysview.h"
-#include "unifont.h"
-
-int manager_new(Manager **out) {
- _cleanup_(manager_freep) Manager *m = NULL;
- int r;
-
- assert(out);
-
- m = new0(Manager, 1);
- if (!m)
- return -ENOMEM;
-
- r = sd_event_default(&m->event);
- if (r < 0)
- return r;
-
- r = sd_event_set_watchdog(m->event, true);
- if (r < 0)
- return r;
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGQUIT, SIGINT, SIGWINCH, SIGCHLD, -1);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGQUIT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_open_system(&m->sysbus);
- if (r < 0)
- return r;
-
- r = sd_bus_attach_event(m->sysbus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return r;
-
- r = unifont_new(&m->uf);
- if (r < 0)
- return r;
-
- r = sysview_context_new(&m->sysview,
- SYSVIEW_CONTEXT_SCAN_LOGIND |
- SYSVIEW_CONTEXT_SCAN_EVDEV |
- SYSVIEW_CONTEXT_SCAN_DRM,
- m->event,
- m->sysbus,
- NULL);
- if (r < 0)
- return r;
-
- r = grdev_context_new(&m->grdev, m->event, m->sysbus);
- if (r < 0)
- return r;
-
- r = idev_context_new(&m->idev, m->event, m->sysbus);
- if (r < 0)
- return r;
-
- *out = m;
- m = NULL;
- return 0;
-}
-
-Manager *manager_free(Manager *m) {
- if (!m)
- return NULL;
-
- assert(!m->workspace_list);
-
- m->idev = idev_context_unref(m->idev);
- m->grdev = grdev_context_unref(m->grdev);
- m->sysview = sysview_context_free(m->sysview);
- m->uf = unifont_unref(m->uf);
- m->sysbus = sd_bus_unref(m->sysbus);
- m->event = sd_event_unref(m->event);
- free(m);
-
- return NULL;
-}
-
-static int manager_sysview_session_filter(Manager *m, sysview_event *event) {
- const char *sid = event->session_filter.id;
- _cleanup_free_ char *desktop = NULL;
- int r;
-
- assert(sid);
-
- r = sd_session_get_desktop(sid, &desktop);
- if (r < 0)
- return 0;
-
- return streq(desktop, "systemd-console");
-}
-
-static int manager_sysview_session_add(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_add.session;
- Session *s;
- int r;
-
- r = sysview_session_take_control(session);
- if (r < 0)
- return log_error_errno(r, "Cannot request session control on '%s': %m",
- sysview_session_get_name(session));
-
- r = session_new(&s, m, session);
- if (r < 0) {
- log_error_errno(r, "Cannot create session on '%s': %m",
- sysview_session_get_name(session));
- sysview_session_release_control(session);
- return r;
- }
-
- sysview_session_set_userdata(session, s);
-
- return 0;
-}
-
-static int manager_sysview_session_remove(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_remove.session;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_free(s);
-
- return 0;
-}
-
-static int manager_sysview_session_attach(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_attach.session;
- sysview_device *device = event->session_attach.device;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_add_device(s, device);
-
- return 0;
-}
-
-static int manager_sysview_session_detach(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_detach.session;
- sysview_device *device = event->session_detach.device;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_remove_device(s, device);
-
- return 0;
-}
-
-static int manager_sysview_session_refresh(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_refresh.session;
- sysview_device *device = event->session_refresh.device;
- struct udev_device *ud = event->session_refresh.ud;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_refresh_device(s, device, ud);
-
- return 0;
-}
-
-static int manager_sysview_session_control(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_control.session;
- int error = event->session_control.error;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- if (error < 0) {
- log_error_errno(error, "Cannot take session control on '%s': %m",
- sysview_session_get_name(session));
- session_free(s);
- sysview_session_set_userdata(session, NULL);
- return error;
- }
-
- return 0;
-}
-
-static int manager_sysview_fn(sysview_context *sysview, void *userdata, sysview_event *event) {
- Manager *m = userdata;
- int r;
-
- assert(m);
-
- switch (event->type) {
- case SYSVIEW_EVENT_SESSION_FILTER:
- r = manager_sysview_session_filter(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_ADD:
- r = manager_sysview_session_add(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_REMOVE:
- r = manager_sysview_session_remove(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_ATTACH:
- r = manager_sysview_session_attach(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_DETACH:
- r = manager_sysview_session_detach(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_REFRESH:
- r = manager_sysview_session_refresh(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_CONTROL:
- r = manager_sysview_session_control(m, event);
- break;
- default:
- r = 0;
- break;
- }
-
- return r;
-}
-
-int manager_run(Manager *m) {
- int r;
-
- assert(m);
-
- r = sysview_context_start(m->sysview, manager_sysview_fn, m);
- if (r < 0)
- return r;
-
- r = sd_event_loop(m->event);
-
- sysview_context_stop(m->sysview);
- return r;
-}
diff --git a/src/console/consoled-session.c b/src/console/consoled-session.c
deleted file mode 100644
index 264a4d009a..0000000000
--- a/src/console/consoled-session.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@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 "consoled.h"
-#include "grdev.h"
-#include "idev.h"
-#include "list.h"
-#include "macro.h"
-#include "sd-event.h"
-#include "sysview.h"
-#include "util.h"
-
-static bool session_feed_keyboard(Session *s, idev_data *data) {
- idev_data_keyboard *kdata = &data->keyboard;
-
- if (!data->resync && kdata->value == 1 && kdata->n_syms == 1) {
- uint32_t nr;
- sysview_seat *seat;
-
- /* handle VT-switch requests */
- nr = 0;
-
- switch (kdata->keysyms[0]) {
- case XKB_KEY_F1 ... XKB_KEY_F12:
- if (IDEV_KBDMATCH(kdata,
- IDEV_KBDMOD_CTRL | IDEV_KBDMOD_ALT,
- kdata->keysyms[0]))
- nr = kdata->keysyms[0] - XKB_KEY_F1 + 1;
- break;
- case XKB_KEY_XF86Switch_VT_1 ... XKB_KEY_XF86Switch_VT_12:
- nr = kdata->keysyms[0] - XKB_KEY_XF86Switch_VT_1 + 1;
- break;
- }
-
- if (nr != 0) {
- seat = sysview_session_get_seat(s->sysview);
- sysview_seat_switch_to(seat, nr);
- return true;
- }
- }
-
- return false;
-}
-
-static bool session_feed(Session *s, idev_data *data) {
- switch (data->type) {
- case IDEV_DATA_KEYBOARD:
- return session_feed_keyboard(s, data);
- default:
- return false;
- }
-}
-
-static int session_idev_fn(idev_session *idev, void *userdata, idev_event *event) {
- Session *s = userdata;
-
- switch (event->type) {
- case IDEV_EVENT_DEVICE_ADD:
- idev_device_enable(event->device_add.device);
- break;
- case IDEV_EVENT_DEVICE_REMOVE:
- idev_device_disable(event->device_remove.device);
- break;
- case IDEV_EVENT_DEVICE_DATA:
- if (!session_feed(s, &event->device_data.data))
- workspace_feed(s->active_ws, &event->device_data.data);
- break;
- }
-
- return 0;
-}
-
-static void session_grdev_fn(grdev_session *grdev, void *userdata, grdev_event *event) {
- grdev_display *display;
- Session *s = userdata;
- Display *d;
- int r;
-
- switch (event->type) {
- case GRDEV_EVENT_DISPLAY_ADD:
- display = event->display_add.display;
-
- r = display_new(&d, s, display);
- if (r < 0) {
- log_error_errno(r, "Cannot create display '%s' on '%s': %m",
- grdev_display_get_name(display), sysview_session_get_name(s->sysview));
- break;
- }
-
- grdev_display_set_userdata(display, d);
- workspace_refresh(s->active_ws);
- break;
- case GRDEV_EVENT_DISPLAY_REMOVE:
- display = event->display_remove.display;
- d = grdev_display_get_userdata(display);
- if (!d)
- break;
-
- display_free(d);
- workspace_refresh(s->active_ws);
- break;
- case GRDEV_EVENT_DISPLAY_CHANGE:
- display = event->display_remove.display;
- d = grdev_display_get_userdata(display);
- if (!d)
- break;
-
- display_refresh(d);
- workspace_refresh(s->active_ws);
- break;
- case GRDEV_EVENT_DISPLAY_FRAME:
- display = event->display_remove.display;
- d = grdev_display_get_userdata(display);
- if (!d)
- break;
-
- session_dirty(s);
- break;
- }
-}
-
-static int session_redraw_fn(sd_event_source *src, void *userdata) {
- Session *s = userdata;
- Display *d;
-
- LIST_FOREACH(displays_by_session, d, s->display_list)
- display_render(d, s->active_ws);
-
- grdev_session_commit(s->grdev);
-
- return 0;
-}
-
-int session_new(Session **out, Manager *m, sysview_session *session) {
- _cleanup_(session_freep) Session *s = NULL;
- int r;
-
- assert(out);
- assert(m);
- assert(session);
-
- s = new0(Session, 1);
- if (!s)
- return -ENOMEM;
-
- s->manager = m;
- s->sysview = session;
-
- r = grdev_session_new(&s->grdev,
- m->grdev,
- GRDEV_SESSION_MANAGED,
- sysview_session_get_name(session),
- session_grdev_fn,
- s);
- if (r < 0)
- return r;
-
- r = idev_session_new(&s->idev,
- m->idev,
- IDEV_SESSION_MANAGED,
- sysview_session_get_name(session),
- session_idev_fn,
- s);
- if (r < 0)
- return r;
-
- r = workspace_new(&s->my_ws, m);
- if (r < 0)
- return r;
-
- s->active_ws = workspace_attach(s->my_ws, s);
-
- r = sd_event_add_defer(m->event, &s->redraw_src, session_redraw_fn, s);
- if (r < 0)
- return r;
-
- grdev_session_enable(s->grdev);
- idev_session_enable(s->idev);
-
- *out = s;
- s = NULL;
- return 0;
-}
-
-Session *session_free(Session *s) {
- if (!s)
- return NULL;
-
- assert(!s->display_list);
-
- sd_event_source_unref(s->redraw_src);
-
- workspace_detach(s->active_ws, s);
- workspace_unref(s->my_ws);
-
- idev_session_free(s->idev);
- grdev_session_free(s->grdev);
- free(s);
-
- return NULL;
-}
-
-void session_dirty(Session *s) {
- int r;
-
- assert(s);
-
- r = sd_event_source_set_enabled(s->redraw_src, SD_EVENT_ONESHOT);
- if (r < 0)
- log_error_errno(r, "Cannot enable redraw-source: %m");
-}
-
-void session_add_device(Session *s, sysview_device *device) {
- unsigned int type;
-
- assert(s);
- assert(device);
-
- type = sysview_device_get_type(device);
- switch (type) {
- case SYSVIEW_DEVICE_DRM:
- grdev_session_add_drm(s->grdev, sysview_device_get_ud(device));
- break;
- case SYSVIEW_DEVICE_EVDEV:
- idev_session_add_evdev(s->idev, sysview_device_get_ud(device));
- break;
- }
-}
-
-void session_remove_device(Session *s, sysview_device *device) {
- unsigned int type;
-
- assert(s);
- assert(device);
-
- type = sysview_device_get_type(device);
- switch (type) {
- case SYSVIEW_DEVICE_DRM:
- grdev_session_remove_drm(s->grdev, sysview_device_get_ud(device));
- break;
- case SYSVIEW_DEVICE_EVDEV:
- idev_session_remove_evdev(s->idev, sysview_device_get_ud(device));
- break;
- }
-}
-
-void session_refresh_device(Session *s, sysview_device *device, struct udev_device *ud) {
- unsigned int type;
-
- assert(s);
- assert(device);
-
- type = sysview_device_get_type(device);
- switch (type) {
- case SYSVIEW_DEVICE_DRM:
- grdev_session_hotplug_drm(s->grdev, sysview_device_get_ud(device));
- break;
- }
-}
diff --git a/src/console/consoled-terminal.c b/src/console/consoled-terminal.c
deleted file mode 100644
index 03447d1b92..0000000000
--- a/src/console/consoled-terminal.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@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 "consoled.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-
-static int terminal_write_fn(term_screen *screen, void *userdata, const void *buf, size_t size) {
- Terminal *t = userdata;
- int r;
-
- if (t->pty) {
- r = pty_write(t->pty, buf, size);
- if (r < 0)
- return log_oom();
- }
-
- return 0;
-}
-
-static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size) {
- Terminal *t = userdata;
- int r;
-
- switch (event) {
- case PTY_CHILD:
- log_debug("PTY child exited");
- t->pty = pty_unref(t->pty);
- break;
- case PTY_DATA:
- r = term_screen_feed_text(t->screen, ptr, size);
- if (r < 0)
- log_error_errno(r, "Cannot update screen state: %m");
-
- workspace_dirty(t->workspace);
- break;
- }
-
- return 0;
-}
-
-int terminal_new(Terminal **out, Workspace *w) {
- _cleanup_(terminal_freep) Terminal *t = NULL;
- int r;
-
- assert(w);
-
- t = new0(Terminal, 1);
- if (!t)
- return -ENOMEM;
-
- t->workspace = w;
- LIST_PREPEND(terminals_by_workspace, w->terminal_list, t);
-
- r = term_parser_new(&t->parser, true);
- if (r < 0)
- return r;
-
- r = term_screen_new(&t->screen, terminal_write_fn, t, NULL, NULL);
- if (r < 0)
- return r;
-
- r = term_screen_set_answerback(t->screen, "systemd-console");
- if (r < 0)
- return r;
-
- if (out)
- *out = t;
- t = NULL;
- return 0;
-}
-
-Terminal *terminal_free(Terminal *t) {
- if (!t)
- return NULL;
-
- assert(t->workspace);
-
- if (t->pty) {
- (void) pty_signal(t->pty, SIGHUP);
- pty_close(t->pty);
- pty_unref(t->pty);
- }
- term_screen_unref(t->screen);
- term_parser_free(t->parser);
- LIST_REMOVE(terminals_by_workspace, t->workspace->terminal_list, t);
- free(t);
-
- return NULL;
-}
-
-void terminal_resize(Terminal *t) {
- uint32_t width, height, fw, fh;
- int r;
-
- assert(t);
-
- width = t->workspace->width;
- height = t->workspace->height;
- fw = unifont_get_width(t->workspace->manager->uf);
- fh = unifont_get_height(t->workspace->manager->uf);
-
- width = (fw > 0) ? width / fw : 0;
- height = (fh > 0) ? height / fh : 0;
-
- if (t->pty) {
- r = pty_resize(t->pty, width, height);
- if (r < 0)
- log_error_errno(r, "Cannot resize pty: %m");
- }
-
- r = term_screen_resize(t->screen, width, height);
- if (r < 0)
- log_error_errno(r, "Cannot resize screen: %m");
-}
-
-void terminal_run(Terminal *t) {
- pid_t pid;
-
- assert(t);
-
- if (t->pty)
- return;
-
- pid = pty_fork(&t->pty,
- t->workspace->manager->event,
- terminal_pty_fn,
- t,
- term_screen_get_width(t->screen),
- term_screen_get_height(t->screen));
- if (pid < 0) {
- log_error_errno(pid, "Cannot fork PTY: %m");
- return;
- } else if (pid == 0) {
- /* child */
-
- char **argv = (char*[]){
- (char*)getenv("SHELL") ? : (char*)_PATH_BSHELL,
- NULL
- };
-
- setenv("TERM", "xterm-256color", 1);
- setenv("COLORTERM", "systemd-console", 1);
-
- execve(argv[0], argv, environ);
- log_error_errno(errno, "Cannot exec %s (%d): %m", argv[0], -errno);
- _exit(1);
- }
-}
-
-static void terminal_feed_keyboard(Terminal *t, idev_data *data) {
- idev_data_keyboard *kdata = &data->keyboard;
- int r;
-
- if (!data->resync && (kdata->value == 1 || kdata->value == 2)) {
- assert_cc(TERM_KBDMOD_CNT == (int)IDEV_KBDMOD_CNT);
- assert_cc(TERM_KBDMOD_IDX_SHIFT == (int)IDEV_KBDMOD_IDX_SHIFT &&
- TERM_KBDMOD_IDX_CTRL == (int)IDEV_KBDMOD_IDX_CTRL &&
- TERM_KBDMOD_IDX_ALT == (int)IDEV_KBDMOD_IDX_ALT &&
- TERM_KBDMOD_IDX_LINUX == (int)IDEV_KBDMOD_IDX_LINUX &&
- TERM_KBDMOD_IDX_CAPS == (int)IDEV_KBDMOD_IDX_CAPS);
-
- r = term_screen_feed_keyboard(t->screen,
- kdata->keysyms,
- kdata->n_syms,
- kdata->ascii,
- kdata->codepoints,
- kdata->mods);
- if (r < 0)
- log_error_errno(r, "Cannot feed keyboard data to screen: %m");
- }
-}
-
-void terminal_feed(Terminal *t, idev_data *data) {
- switch (data->type) {
- case IDEV_DATA_KEYBOARD:
- terminal_feed_keyboard(t, data);
- break;
- }
-}
-
-static void terminal_fill(uint8_t *dst,
- uint32_t width,
- uint32_t height,
- uint32_t stride,
- uint32_t value) {
- uint32_t i, j, *px;
-
- for (j = 0; j < height; ++j) {
- px = (uint32_t*)dst;
-
- for (i = 0; i < width; ++i)
- *px++ = value;
-
- dst += stride;
- }
-}
-
-static void terminal_blend(uint8_t *dst,
- uint32_t width,
- uint32_t height,
- uint32_t dst_stride,
- const uint8_t *src,
- uint32_t src_stride,
- uint32_t fg,
- uint32_t bg) {
- uint32_t i, j, *px;
-
- for (j = 0; j < height; ++j) {
- px = (uint32_t*)dst;
-
- for (i = 0; i < width; ++i) {
- if (!src || src[i / 8] & (1 << (7 - i % 8)))
- *px = fg;
- else
- *px = bg;
-
- ++px;
- }
-
- src += src_stride;
- dst += dst_stride;
- }
-}
-
-typedef struct {
- const grdev_display_target *target;
- unifont *uf;
- uint32_t cell_width;
- uint32_t cell_height;
- bool dirty;
-} TerminalDrawContext;
-
-static int terminal_draw_cell(term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width) {
- TerminalDrawContext *ctx = userdata;
- const grdev_display_target *target = ctx->target;
- grdev_fb *fb = target->back;
- uint32_t xpos, ypos, width, height;
- uint32_t fg, bg;
- unifont_glyph g;
- uint8_t *dst;
- int r;
-
- if (n_ch > 0) {
- r = unifont_lookup(ctx->uf, &g, *ch);
- if (r < 0)
- r = unifont_lookup(ctx->uf, &g, 0xfffd);
- if (r < 0)
- unifont_fallback(&g);
- }
-
- xpos = x * ctx->cell_width;
- ypos = y * ctx->cell_height;
-
- if (xpos >= fb->width || ypos >= fb->height)
- return 0;
-
- width = MIN(fb->width - xpos, ctx->cell_width * ch_width);
- height = MIN(fb->height - ypos, ctx->cell_height);
-
- term_attr_to_argb32(attr, &fg, &bg, NULL);
-
- ctx->dirty = true;
-
- dst = fb->maps[0];
- dst += fb->strides[0] * ypos + sizeof(uint32_t) * xpos;
-
- if (n_ch < 1) {
- terminal_fill(dst,
- width,
- height,
- fb->strides[0],
- bg);
- } else {
- if (width > g.width)
- terminal_fill(dst + sizeof(uint32_t) * g.width,
- width - g.width,
- height,
- fb->strides[0],
- bg);
- if (height > g.height)
- terminal_fill(dst + fb->strides[0] * g.height,
- width,
- height - g.height,
- fb->strides[0],
- bg);
-
- terminal_blend(dst,
- width,
- height,
- fb->strides[0],
- g.data,
- g.stride,
- fg,
- bg);
- }
-
- return 0;
-}
-
-bool terminal_draw(Terminal *t, const grdev_display_target *target) {
- TerminalDrawContext ctx = { };
- uint64_t age;
-
- assert(t);
- assert(target);
-
- /* start up terminal on first frame */
- terminal_run(t);
-
- ctx.target = target;
- ctx.uf = t->workspace->manager->uf;
- ctx.cell_width = unifont_get_width(ctx.uf);
- ctx.cell_height = unifont_get_height(ctx.uf);
- ctx.dirty = false;
-
- if (target->front) {
- /* if the frontbuffer is new enough, no reason to redraw */
- age = term_screen_get_age(t->screen);
- if (age != 0 && age <= target->front->data.u64)
- return false;
- } else {
- /* force flip if no frontbuffer is set, yet */
- ctx.dirty = true;
- }
-
- term_screen_draw(t->screen, terminal_draw_cell, &ctx, &target->back->data.u64);
-
- return ctx.dirty;
-}
diff --git a/src/console/consoled-workspace.c b/src/console/consoled-workspace.c
deleted file mode 100644
index 5e9e5c7c49..0000000000
--- a/src/console/consoled-workspace.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@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 "consoled.h"
-#include "grdev.h"
-#include "idev.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-
-int workspace_new(Workspace **out, Manager *m) {
- _cleanup_(workspace_unrefp) Workspace *w = NULL;
- int r;
-
- assert(out);
-
- w = new0(Workspace, 1);
- if (!w)
- return -ENOMEM;
-
- w->ref = 1;
- w->manager = m;
- LIST_PREPEND(workspaces_by_manager, m->workspace_list, w);
-
- r = terminal_new(&w->current, w);
- if (r < 0)
- return r;
-
- *out = w;
- w = NULL;
- return 0;
-}
-
-static void workspace_cleanup(Workspace *w) {
- Terminal *t;
-
- assert(w);
- assert(w->ref == 0);
- assert(w->manager);
- assert(!w->session_list);
-
- w->current = NULL;
- while ((t = w->terminal_list))
- terminal_free(t);
-
- LIST_REMOVE(workspaces_by_manager, w->manager->workspace_list, w);
- free(w);
-}
-
-Workspace *workspace_ref(Workspace *w) {
- assert(w);
-
- ++w->ref;
- return w;
-}
-
-Workspace *workspace_unref(Workspace *w) {
- if (!w)
- return NULL;
-
- assert(w->ref > 0);
-
- if (--w->ref == 0)
- workspace_cleanup(w);
-
- return NULL;
-}
-
-Workspace *workspace_attach(Workspace *w, Session *s) {
- assert(w);
- assert(s);
-
- LIST_PREPEND(sessions_by_workspace, w->session_list, s);
- workspace_refresh(w);
- return workspace_ref(w);
-}
-
-Workspace *workspace_detach(Workspace *w, Session *s) {
- assert(w);
- assert(s);
- assert(s->active_ws == w);
-
- LIST_REMOVE(sessions_by_workspace, w->session_list, s);
- workspace_refresh(w);
- return workspace_unref(w);
-}
-
-void workspace_refresh(Workspace *w) {
- uint32_t width, height;
- Terminal *t;
- Session *s;
- Display *d;
-
- assert(w);
-
- width = 0;
- height = 0;
-
- /* find out minimum dimension of all attached displays */
- LIST_FOREACH(sessions_by_workspace, s, w->session_list) {
- LIST_FOREACH(displays_by_session, d, s->display_list) {
- assert(d->width > 0 && d->height > 0);
-
- if (width == 0 || d->width < width)
- width = d->width;
- if (height == 0 || d->height < height)
- height = d->height;
- }
- }
-
- /* either both are zero, or none is zero */
- assert(!(!width ^ !height));
-
- /* update terminal-sizes if dimensions changed */
- if (w->width != width || w->height != height) {
- w->width = width;
- w->height = height;
-
- LIST_FOREACH(terminals_by_workspace, t, w->terminal_list)
- terminal_resize(t);
-
- workspace_dirty(w);
- }
-}
-
-void workspace_dirty(Workspace *w) {
- Session *s;
-
- assert(w);
-
- LIST_FOREACH(sessions_by_workspace, s, w->session_list)
- session_dirty(s);
-}
-
-void workspace_feed(Workspace *w, idev_data *data) {
- assert(w);
- assert(data);
-
- terminal_feed(w->current, data);
-}
-
-bool workspace_draw(Workspace *w, const grdev_display_target *target) {
- assert(w);
- assert(target);
-
- return terminal_draw(w->current, target);
-}
diff --git a/src/console/consoled.c b/src/console/consoled.c
deleted file mode 100644
index 9f69e8983f..0000000000
--- a/src/console/consoled.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@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 "sd-daemon.h"
-#include "log.h"
-#include "signal-util.h"
-#include "consoled.h"
-
-int main(int argc, char *argv[]) {
- _cleanup_(manager_freep) Manager *m = NULL;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto out;
- }
-
- r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Could not create manager: %m");
- goto out;
- }
-
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- r = manager_run(m);
- if (r < 0) {
- log_error_errno(r, "Cannot run manager: %m");
- goto out;
- }
-
-out:
- sd_notify(false,
- "STATUS=Shutting down...");
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/console/consoled.h b/src/console/consoled.h
deleted file mode 100644
index f85c1a0791..0000000000
--- a/src/console/consoled.h
+++ /dev/null
@@ -1,164 +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@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 "grdev.h"
-#include "idev.h"
-#include "list.h"
-#include "macro.h"
-#include "pty.h"
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sysview.h"
-#include "term.h"
-#include "unifont.h"
-
-typedef struct Manager Manager;
-typedef struct Session Session;
-typedef struct Display Display;
-typedef struct Workspace Workspace;
-typedef struct Terminal Terminal;
-
-/*
- * Terminals
- */
-
-struct Terminal {
- Workspace *workspace;
- LIST_FIELDS(Terminal, terminals_by_workspace);
-
- term_utf8 utf8;
- term_parser *parser;
- term_screen *screen;
- Pty *pty;
-};
-
-int terminal_new(Terminal **out, Workspace *w);
-Terminal *terminal_free(Terminal *t);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Terminal*, terminal_free);
-
-void terminal_resize(Terminal *t);
-void terminal_run(Terminal *t);
-void terminal_feed(Terminal *t, idev_data *data);
-bool terminal_draw(Terminal *t, const grdev_display_target *target);
-
-/*
- * Workspaces
- */
-
-struct Workspace {
- unsigned long ref;
- Manager *manager;
- LIST_FIELDS(Workspace, workspaces_by_manager);
-
- LIST_HEAD(Terminal, terminal_list);
- Terminal *current;
-
- LIST_HEAD(Session, session_list);
- uint32_t width;
- uint32_t height;
-};
-
-int workspace_new(Workspace **out, Manager *m);
-Workspace *workspace_ref(Workspace *w);
-Workspace *workspace_unref(Workspace *w);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Workspace*, workspace_unref);
-
-Workspace *workspace_attach(Workspace *w, Session *s);
-Workspace *workspace_detach(Workspace *w, Session *s);
-void workspace_refresh(Workspace *w);
-
-void workspace_dirty(Workspace *w);
-void workspace_feed(Workspace *w, idev_data *data);
-bool workspace_draw(Workspace *w, const grdev_display_target *target);
-
-/*
- * Displays
- */
-
-struct Display {
- Session *session;
- LIST_FIELDS(Display, displays_by_session);
- grdev_display *grdev;
- uint32_t width;
- uint32_t height;
-};
-
-int display_new(Display **out, Session *s, grdev_display *grdev);
-Display *display_free(Display *d);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Display*, display_free);
-
-void display_refresh(Display *d);
-void display_render(Display *d, Workspace *w);
-
-/*
- * Sessions
- */
-
-struct Session {
- Manager *manager;
- sysview_session *sysview;
- grdev_session *grdev;
- idev_session *idev;
-
- LIST_FIELDS(Session, sessions_by_workspace);
- Workspace *my_ws;
- Workspace *active_ws;
-
- LIST_HEAD(Display, display_list);
- sd_event_source *redraw_src;
-};
-
-int session_new(Session **out, Manager *m, sysview_session *session);
-Session *session_free(Session *s);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Session*, session_free);
-
-void session_dirty(Session *s);
-
-void session_add_device(Session *s, sysview_device *device);
-void session_remove_device(Session *s, sysview_device *device);
-void session_refresh_device(Session *s, sysview_device *device, struct udev_device *ud);
-
-/*
- * Managers
- */
-
-struct Manager {
- sd_event *event;
- sd_bus *sysbus;
- unifont *uf;
- sysview_context *sysview;
- grdev_context *grdev;
- idev_context *idev;
- LIST_HEAD(Workspace, workspace_list);
-};
-
-int manager_new(Manager **out);
-Manager *manager_free(Manager *m);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
-
-int manager_run(Manager *m);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 4a9df06016..9dcc51f240 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -433,9 +433,9 @@ int bus_cgroup_set_property(
if (!f)
return -ENOMEM;
- if (read) {
+ if (read) {
fputs("BlockIOReadBandwidth=\n", f);
- LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
+ LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
if (a->read)
fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
} else {
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index d8b39bdf5f..5722e3c2bb 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1069,10 +1069,9 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er
manager_dump_units(m, f, NULL);
manager_dump_jobs(m, f, NULL);
- fflush(f);
-
- if (ferror(f))
- return -ENOMEM;
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, "s", dump);
}
diff --git a/src/core/execute.c b/src/core/execute.c
index 21721dc240..125cb0dbd4 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1719,7 +1719,15 @@ static int exec_child(
}
#ifdef SMACK_DEFAULT_PROCESS_LABEL
else {
- r = mac_smack_apply_pid(0, SMACK_DEFAULT_PROCESS_LABEL);
+ _cleanup_free_ char *exec_label = NULL;
+
+ r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
+ if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) {
+ *exit_status = EXIT_SMACK_PROCESS_LABEL;
+ return r;
+ }
+
+ r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL);
if (r < 0) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
return r;
@@ -2203,7 +2211,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
static bool tty_may_match_dev_console(const char *tty) {
_cleanup_free_ char *active = NULL;
- char *console;
+ char *console;
if (startswith(tty, "/dev/"))
tty += 5;
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index a48cb4029a..ba73cc410e 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -3508,9 +3508,7 @@ static int load_from_path(Unit *u, const char *path) {
r = open_follow(&filename, &f, symlink_names, &id);
if (r < 0) {
- free(filename);
- filename = NULL;
-
+ filename = mfree(filename);
if (r != -ENOENT)
return r;
}
@@ -3534,9 +3532,7 @@ static int load_from_path(Unit *u, const char *path) {
r = open_follow(&filename, &f, symlink_names, &id);
if (r < 0) {
- free(filename);
- filename = NULL;
-
+ filename = mfree(filename);
if (r != -ENOENT)
return r;
diff --git a/src/core/main.c b/src/core/main.c
index 6ae8b51544..a66fb18418 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1844,13 +1844,11 @@ finish:
arg_default_rlimit[j] = NULL;
}
- free(arg_default_unit);
- arg_default_unit = NULL;
+ arg_default_unit = mfree(arg_default_unit);
free_join_controllers();
- strv_free(arg_default_environment);
- arg_default_environment = NULL;
+ arg_default_environment = strv_free(arg_default_environment);
set_free(arg_syscall_archs);
arg_syscall_archs = NULL;
diff --git a/src/core/manager.c b/src/core/manager.c
index a1f37bbbb3..ba107d4615 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1701,6 +1701,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
ssize_t n;
struct signalfd_siginfo sfsi;
bool sigchld = false;
+ int r;
assert(m);
assert(m->signal_fd == fd);
@@ -1809,20 +1810,16 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
f = open_memstream(&dump, &size);
if (!f) {
- log_warning("Failed to allocate memory stream.");
+ log_warning_errno(errno, "Failed to allocate memory stream: %m");
break;
}
manager_dump_units(m, f, "\t");
manager_dump_jobs(m, f, "\t");
- if (ferror(f)) {
- log_warning("Failed to write status stream");
- break;
- }
-
- if (fflush(f)) {
- log_warning("Failed to flush status stream");
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to write status stream: %m");
break;
}
diff --git a/src/core/mount.c b/src/core/mount.c
index bf8e52bf0e..c0d1cdfbd4 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -834,8 +834,6 @@ static void mount_enter_unmounting(Mount *m) {
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r < 0)
goto fail;
@@ -886,8 +884,6 @@ static void mount_enter_mounting(Mount *m) {
r = exec_command_set(m->control_command, MOUNT_PATH,
m->parameters_fragment.what, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
if (r >= 0 && m->parameters_fragment.fstype)
@@ -934,8 +930,6 @@ static void mount_enter_remounting(Mount *m) {
r = exec_command_set(m->control_command, MOUNT_PATH,
m->parameters_fragment.what, m->where,
"-o", o, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
if (r >= 0 && m->parameters_fragment.fstype)
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index e9a9a020de..50a90b0bac 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -302,12 +302,12 @@ int mac_selinux_unit_access_check_strv(
int r;
STRV_FOREACH(i, units) {
- u = manager_get_unit(m, *i);
- if (u) {
- r = mac_selinux_unit_access_check(u, message, permission, error);
- if (r < 0)
- return r;
- }
+ r = manager_load_unit(m, *i, NULL, error, &u);
+ if (r < 0)
+ return r;
+ r = mac_selinux_unit_access_check(u, message, permission, error);
+ if (r < 0)
+ return r;
}
#endif
return 0;
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
index 1e634b9bc1..9518e21f36 100644
--- a/src/core/snapshot.c
+++ b/src/core/snapshot.c
@@ -217,8 +217,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e,
break;
}
- free(n);
- n = NULL;
+ n = mfree(n);
}
}
diff --git a/src/core/socket.c b/src/core/socket.c
index 87631f8753..a387057473 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -923,13 +923,13 @@ static void socket_apply_socket_options(Socket *s, int fd) {
log_unit_warning_errno(UNIT(s), errno, "TCP_CONGESTION failed: %m");
if (s->smack_ip_in) {
- r = mac_smack_apply_ip_in_fd(fd, s->smack_ip_in);
+ r = mac_smack_apply_fd(fd, SMACK_ATTR_IPIN, s->smack_ip_in);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_in_fd: %m");
}
if (s->smack_ip_out) {
- r = mac_smack_apply_ip_out_fd(fd, s->smack_ip_out);
+ r = mac_smack_apply_fd(fd, SMACK_ATTR_IPOUT, s->smack_ip_out);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_out_fd: %m");
}
@@ -946,7 +946,7 @@ static void socket_apply_fifo_options(Socket *s, int fd) {
log_unit_warning_errno(UNIT(s), errno, "F_SETPIPE_SZ: %m");
if (s->smack) {
- r = mac_smack_apply_fd(fd, s->smack);
+ r = mac_smack_apply_fd(fd, SMACK_ATTR_ACCESS, s->smack);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_fd: %m");
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 5c6c7c0ed8..74fa90a233 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -333,8 +333,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
/* If the description string is simply the
* volume name, then let's not show this
* twice */
- free(description);
- description = NULL;
+ description = mfree(description);
}
if (mount_point && description)
diff --git a/src/efi-boot-generator/Makefile b/src/efi-boot-generator/Makefile
deleted file mode 120000
index d0b0e8e008..0000000000
--- a/src/efi-boot-generator/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/efi-boot-generator/efi-boot-generator.c b/src/efi-boot-generator/efi-boot-generator.c
deleted file mode 100644
index e6b15c9bb0..0000000000
--- a/src/efi-boot-generator/efi-boot-generator.c
+++ /dev/null
@@ -1,162 +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 <unistd.h>
-#include <stdlib.h>
-
-#include "efivars.h"
-#include "path-util.h"
-#include "util.h"
-#include "mkdir.h"
-#include "virt.h"
-#include "generator.h"
-#include "special.h"
-
-static const char *arg_dest = "/tmp";
-
-int main(int argc, char *argv[]) {
- _cleanup_free_ char *what = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int r = EXIT_SUCCESS;
- sd_id128_t id;
- char *name;
-
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[3];
-
- log_set_target(LOG_TARGET_SAFE);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if (in_initrd()) {
- log_debug("In initrd, exiting.");
- return EXIT_SUCCESS;
- }
-
- if (detect_container(NULL) > 0) {
- log_debug("In a container, exiting.");
- return EXIT_SUCCESS;
- }
-
- if (!is_efi_boot()) {
- log_debug("Not an EFI boot, exiting.");
- return EXIT_SUCCESS;
- }
-
- r = path_is_mount_point("/boot", AT_SYMLINK_FOLLOW);
- if (r > 0) {
- log_debug("/boot is already a mount point, exiting.");
- return EXIT_SUCCESS;
- }
- if (r == -ENOENT)
- log_debug("/boot does not exist, continuing.");
- else if (dir_is_empty("/boot") <= 0) {
- log_debug("/boot already populated, exiting.");
- return EXIT_SUCCESS;
- }
-
- r = efi_loader_get_device_part_uuid(&id);
- if (r == -ENOENT) {
- log_debug("EFI loader partition unknown, exiting.");
- return EXIT_SUCCESS;
- } else if (r < 0) {
- log_error_errno(r, "Failed to read ESP partition UUID: %m");
- return EXIT_FAILURE;
- }
-
- name = strjoina(arg_dest, "/boot.mount");
- f = fopen(name, "wxe");
- if (!f) {
- log_error_errno(errno, "Failed to create mount unit file %s: %m", name);
- return EXIT_FAILURE;
- }
-
- r = asprintf(&what,
- "/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- SD_ID128_FORMAT_VAL(id));
- if (r < 0) {
- log_oom();
- return EXIT_FAILURE;
- }
-
- fprintf(f,
- "# Automatially generated by systemd-efi-boot-generator\n\n"
- "[Unit]\n"
- "Description=EFI System Partition\n"
- "Documentation=man:systemd-efi-boot-generator(8)\n");
-
- r = generator_write_fsck_deps(f, arg_dest, what, "/boot", "vfat");
- if (r < 0)
- return EXIT_FAILURE;
-
- fprintf(f,
- "\n"
- "[Mount]\n"
- "What=%s\n"
- "Where=/boot\n"
- "Type=vfat\n"
- "Options=umask=0077,noauto\n",
- what);
-
- r = fflush_and_check(f);
- if (r < 0) {
- log_error_errno(r, "Failed to write mount unit file: %m");
- return EXIT_FAILURE;
- }
-
- name = strjoina(arg_dest, "/boot.automount");
- fclose(f);
- f = fopen(name, "wxe");
- if (!f) {
- log_error_errno(errno, "Failed to create automount unit file %s: %m", name);
- return EXIT_FAILURE;
- }
-
- fputs("# Automatially generated by systemd-efi-boot-generator\n\n"
- "[Unit]\n"
- "Description=EFI System Partition Automount\n\n"
- "[Automount]\n"
- "Where=/boot\n"
- "TimeoutIdleSec=120\n", f);
-
- r = fflush_and_check(f);
- if (r < 0) {
- log_error_errno(r, "Failed to write automount unit file: %m");
- return EXIT_FAILURE;
- }
-
- name = strjoina(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/boot.automount");
- mkdir_parents(name, 0755);
-
- if (symlink("../boot.automount", name) < 0) {
- log_error_errno(errno, "Failed to create symlink %s: %m", name);
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 01a3d38746..e2a1c00a75 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -716,10 +716,8 @@ static int parse_argv(int argc, char *argv[]) {
path_kill_slashes(arg_root);
- if (path_equal(arg_root, "/")) {
- free(arg_root);
- arg_root = NULL;
- }
+ if (path_equal(arg_root, "/"))
+ arg_root = mfree(arg_root);
break;
@@ -729,9 +727,8 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- free(arg_locale);
- arg_locale = strdup(optarg);
- if (!arg_locale)
+ r = free_and_strdup(&arg_locale, optarg);
+ if (r < 0)
return log_oom();
break;
@@ -742,9 +739,8 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- free(arg_locale_messages);
- arg_locale_messages = strdup(optarg);
- if (!arg_locale_messages)
+ r = free_and_strdup(&arg_locale_messages, optarg);
+ if (r < 0)
return log_oom();
break;
@@ -755,24 +751,20 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- free(arg_timezone);
- arg_timezone = strdup(optarg);
- if (!arg_timezone)
+ r = free_and_strdup(&arg_timezone, optarg);
+ if (r < 0)
return log_oom();
break;
case ARG_ROOT_PASSWORD:
- free(arg_root_password);
- arg_root_password = strdup(optarg);
- if (!arg_root_password)
+ r = free_and_strdup(&arg_root_password, optarg);
+ if (r < 0)
return log_oom();
-
break;
case ARG_ROOT_PASSWORD_FILE:
- free(arg_root_password);
- arg_root_password = NULL;
+ arg_root_password = mfree(arg_root_password);
r = read_one_line_file(optarg, &arg_root_password);
if (r < 0)
@@ -787,7 +779,8 @@ static int parse_argv(int argc, char *argv[]) {
}
hostname_cleanup(optarg);
- if (free_and_strdup(&arg_hostname, optarg) < 0)
+ r = free_and_strdup(&arg_hostname, optarg);
+ if (r < 0)
return log_oom();
break;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index da5f3b647a..9d889c17d8 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -46,51 +46,6 @@ static bool arg_enabled = true;
static bool arg_root_enabled = true;
static bool arg_root_rw = false;
-static int add_swap(const char *path) {
- _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int r;
-
- assert(path);
-
- log_debug("Adding swap: %s", path);
-
- r = unit_name_from_path(path, ".swap", &name);
- if (r < 0)
- return log_error_errno(r, "Failed to generate unit name: %m");
-
- unit = strjoin(arg_dest, "/", name, NULL);
- if (!unit)
- return log_oom();
-
- f = fopen(unit, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
-
- fprintf(f,
- "# Automatically generated by systemd-gpt-auto-generator\n\n"
- "[Unit]\n"
- "Description=Swap Partition\n"
- "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
- "[Swap]\n"
- "What=%s\n",
- path);
-
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write unit file %s: %m", unit);
-
- lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
- if (!lnk)
- return log_oom();
-
- mkdir_parents_label(lnk, 0755);
- if (symlink(unit, lnk) < 0)
- return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
-
- return 0;
-}
-
static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
_cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -142,9 +97,9 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
id, what, rw ? "" : "read-only",
id);
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write file %s: %m", p);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file %s: %m", p);
from = strjoina("../", n);
@@ -202,6 +157,7 @@ static int add_mount(
const char *where,
const char *fstype,
bool rw,
+ const char *options,
const char *description,
const char *post) {
@@ -262,11 +218,14 @@ static int add_mount(
if (fstype)
fprintf(f, "Type=%s\n", fstype);
- fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
+ if (options)
+ fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro");
+ else
+ fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
- fflush(f);
- if (ferror(f))
- return log_error_errno(errno, "Failed to write unit file %s: %m", p);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", p);
if (post) {
lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
@@ -281,6 +240,104 @@ static int add_mount(
return 0;
}
+static int add_automount(
+ const char *id,
+ const char *what,
+ const char *where,
+ const char *fstype,
+ bool rw,
+ const char *options,
+ const char *description,
+ usec_t timeout) {
+
+ _cleanup_free_ char *unit = NULL, *lnk = NULL;
+ _cleanup_free_ char *opt, *p = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(id);
+ assert(where);
+ assert(description);
+
+ if (options)
+ opt = strjoin(options, ",noauto", NULL);
+ else
+ opt = strdup("noauto");
+ if (!opt)
+ return log_oom();
+
+ r = add_mount(id,
+ what,
+ where,
+ fstype,
+ rw,
+ opt,
+ description,
+ NULL);
+ if (r < 0)
+ return r;
+
+ r = unit_name_from_path(where, ".automount", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ p = strjoin(arg_dest, "/", unit, NULL);
+ if (!p)
+ return log_oom();
+
+ f = fopen(p, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+ fprintf(f,
+ "# Automatically generated by systemd-gpt-auto-generator\n\n"
+ "[Unit]\n"
+ "Description=%s\n"
+ "Documentation=man:systemd-gpt-auto-generator(8)\n"
+ "[Automount]\n"
+ "Where=%s\n"
+ "TimeoutIdleSec=%lld\n",
+ description,
+ where,
+ (unsigned long long)timeout / USEC_PER_SEC);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", p);
+
+ lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL);
+ if (!lnk)
+ return log_oom();
+ mkdir_parents_label(lnk, 0755);
+
+ if (symlink(p, lnk) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
+
+ return 0;
+}
+
+static bool path_is_busy(const char *where) {
+ int r;
+
+ /* already a mountpoint; generators run during reload */
+ r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ if (r > 0)
+ return false;
+
+ /* the directory might not exist on a stateless system */
+ if (r == -ENOENT)
+ return false;
+
+ if (r < 0)
+ return true;
+
+ /* not a mountpoint but it contains files */
+ if (dir_is_empty(where) <= 0)
+ return true;
+
+ return false;
+}
+
static int probe_and_add_mount(
const char *id,
const char *what,
@@ -298,8 +355,7 @@ static int probe_and_add_mount(
assert(where);
assert(description);
- if (path_is_mount_point(where, AT_SYMLINK_FOLLOW) <= 0 &&
- dir_is_empty(where) <= 0) {
+ if (path_is_busy(where)) {
log_debug("%s already populated, ignoring.", where);
return 0;
}
@@ -335,21 +391,163 @@ static int probe_and_add_mount(
where,
fstype,
rw,
+ NULL,
description,
post);
}
+static int add_swap(const char *path) {
+ _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(path);
+
+ log_debug("Adding swap: %s", path);
+
+ r = unit_name_from_path(path, ".swap", &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ unit = strjoin(arg_dest, "/", name, NULL);
+ if (!unit)
+ return log_oom();
+
+ f = fopen(unit, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+ fprintf(f,
+ "# Automatically generated by systemd-gpt-auto-generator\n\n"
+ "[Unit]\n"
+ "Description=Swap Partition\n"
+ "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
+ "[Swap]\n"
+ "What=%s\n",
+ path);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+
+ lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
+ if (!lnk)
+ return log_oom();
+
+ mkdir_parents_label(lnk, 0755);
+ if (symlink(unit, lnk) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
+
+ return 0;
+}
+
+static int add_boot(const char *what) {
+#ifdef ENABLE_EFI
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+ const char *fstype = NULL, *uuid = NULL;
+ sd_id128_t id, type_id;
+ int r;
+
+ assert(what);
+
+ if (!is_efi_boot()) {
+ log_debug("Not an EFI boot, ignoring /boot.");
+ return 0;
+ }
+
+ if (in_initrd()) {
+ log_debug("In initrd, ignoring /boot.");
+ return 0;
+ }
+
+ if (detect_container(NULL) > 0) {
+ log_debug("In a container, ignoring /boot.");
+ return 0;
+ }
+
+ if (path_is_busy("/boot")) {
+ log_debug("/boot already populated, ignoring.");
+ return 0;
+ }
+
+ r = efi_loader_get_device_part_uuid(&id);
+ if (r == -ENOENT) {
+ log_debug("EFI loader partition unknown.");
+ return 0;
+ }
+
+ if (r < 0) {
+ log_error_errno(r, "Failed to read ESP partition UUID: %m");
+ return r;
+ }
+
+ errno = 0;
+ b = blkid_new_probe_from_filename(what);
+ if (!b) {
+ if (errno == 0)
+ return log_oom();
+ log_error_errno(errno, "Failed to allocate prober: %m");
+ return -errno;
+ }
+
+ blkid_probe_enable_partitions(b, 1);
+ blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (r == -2 || r == 1) /* no result or uncertain */
+ return 0;
+ else if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
+
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ if (!streq(fstype, "vfat")) {
+ log_debug("Partition for /boot is not a FAT filesystem, ignoring.");
+ return 0;
+ }
+
+ r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL);
+ if (r != 0) {
+ log_debug_errno(r, "Partition for /boot does not have a UUID, ignoring. %m");
+ return 0;
+ }
+
+ if (sd_id128_from_string(uuid, &type_id) < 0) {
+ log_debug("Partition for /boot does not have a valid UUID, ignoring.");
+ return 0;
+ }
+
+ if (!sd_id128_equal(type_id, id)) {
+ log_debug("Partition for /boot does not appear to be the partition we are booted from.");
+ return 0;
+ }
+
+ r = add_automount("boot",
+ what,
+ "/boot",
+ "vfat",
+ "EFI System Partition Automount",
+ false,
+ "umask=0077",
+ 120 * USEC_PER_SEC);
+
+ return r;
+#else
+ return 0;
+#endif
+}
+
static int enumerate_partitions(dev_t devnum) {
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_free_ char *home = NULL, *srv = NULL;
+ _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL;
struct udev_list_entry *first, *item;
struct udev_device *parent = NULL;
const char *name, *node, *pttype, *devtype;
- int home_nr = -1, srv_nr = -1;
+ int boot_nr = -1, home_nr = -1, srv_nr = -1;
bool home_rw = true, srv_rw = true;
blkid_partlist pl;
int r, k;
@@ -521,6 +719,18 @@ static int enumerate_partitions(dev_t devnum) {
if (k < 0)
r = k;
+ } else if (sd_id128_equal(type_id, GPT_ESP)) {
+
+ /* We only care for the first /boot partition */
+ if (boot && nr >= boot_nr)
+ continue;
+
+ boot_nr = nr;
+
+ r = free_and_strdup(&boot, subnode);
+ if (r < 0)
+ return log_oom();
+
} else if (sd_id128_equal(type_id, GPT_HOME)) {
/* We only care for the first /home partition */
@@ -530,9 +740,8 @@ static int enumerate_partitions(dev_t devnum) {
home_nr = nr;
home_rw = !(flags & GPT_FLAG_READ_ONLY),
- free(home);
- home = strdup(subnode);
- if (!home)
+ r = free_and_strdup(&home, subnode);
+ if (r < 0)
return log_oom();
} else if (sd_id128_equal(type_id, GPT_SRV)) {
@@ -544,13 +753,18 @@ static int enumerate_partitions(dev_t devnum) {
srv_nr = nr;
srv_rw = !(flags & GPT_FLAG_READ_ONLY),
- free(srv);
- srv = strdup(subnode);
- if (!srv)
+ r = free_and_strdup(&srv, subnode);
+ if (r < 0)
return log_oom();
}
}
+ if (boot) {
+ k = add_boot(boot);
+ if (k < 0)
+ r = k;
+ }
+
if (home) {
k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
if (k < 0)
@@ -645,6 +859,7 @@ static int add_root_mount(void) {
in_initrd() ? "/sysroot" : "/",
NULL,
arg_root_rw,
+ NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
#else
diff --git a/src/import/pull-dkr.c b/src/import/pull-dkr.c
index 53d5174f33..5ff49baa22 100644
--- a/src/import/pull-dkr.c
+++ b/src/import/pull-dkr.c
@@ -592,8 +592,7 @@ static int dkr_pull_pull_layer_v2(DkrPull *i) {
i->current_ancestry++;
- free(path);
- path = NULL;
+ path = mfree(path);
}
log_info("Pulling layer %s...", layer);
@@ -652,8 +651,7 @@ static int dkr_pull_pull_layer(DkrPull *i) {
i->current_ancestry++;
- free(path);
- path = NULL;
+ path = mfree(path);
}
log_info("Pulling layer %s...", layer);
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 1baedf6367..e3bd76051b 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -1114,6 +1114,7 @@ static int accept_connection(const char* type, int fd,
r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
if (r < 0) {
+ log_error_errno(r, "Resolving hostname failed: %m");
close(fd2);
return r;
}
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 5d23639ee8..172fd80a12 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -126,26 +126,31 @@ static int update_cursor_state(Uploader *u) {
r = fopen_temporary(u->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fprintf(f,
"# This is private data. Do not parse.\n"
"LAST_CURSOR=%s\n",
u->last_cursor);
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, u->state_file) < 0) {
+ if (rename(temp_path, u->state_file) < 0) {
r = -errno;
- unlink(u->state_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save state %s: %m", u->state_file);
+ return 0;
- return r;
+fail:
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ (void) unlink(u->state_file);
+
+ return log_error_errno(r, "Failed to save state %s: %m", u->state_file);
}
static int load_cursor_state(Uploader *u) {
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
index 0801e13599..a3e51e2f52 100644
--- a/src/journal/catalog.c
+++ b/src/journal/catalog.c
@@ -263,8 +263,7 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
if (r < 0)
return r;
- free(lang);
- lang = NULL;
+ lang = mfree(lang);
}
if (with_language) {
@@ -371,25 +370,23 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
goto error;
}
- fflush(w);
-
- if (ferror(w)) {
- log_error("%s: failed to write database.", p);
+ r = fflush_and_check(w);
+ if (r < 0) {
+ log_error_errno(r, "%s: failed to write database: %m", p);
goto error;
}
fchmod(fileno(w), 0644);
if (rename(p, database) < 0) {
- log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
- r = -errno;
+ r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
goto error;
}
return ftell(w);
error:
- unlink(p);
+ (void) unlink(p);
return r;
}
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index fc49b2e174..098f62af50 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -587,8 +587,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
if (filename && access(filename, R_OK) < 0) {
log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
"File %s is not readable: %m", filename);
- free(filename);
- filename = NULL;
+ filename = mfree(filename);
}
if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index d2c0a03ccb..fe83edd2b3 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -1259,8 +1259,7 @@ static int add_units(sd_journal *j) {
}
}
- strv_free(patterns);
- patterns = NULL;
+ patterns = strv_free(patterns);
STRV_FOREACH(i, arg_user_units) {
_cleanup_free_ char *u = NULL;
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 559d100131..d954c5190d 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -144,7 +144,7 @@ typedef struct Server {
#define N_IOVEC_META_FIELDS 20
#define N_IOVEC_KERNEL_FIELDS 64
#define N_IOVEC_UDEV_FIELDS 32
-#define N_IOVEC_OBJECT_FIELDS 11
+#define N_IOVEC_OBJECT_FIELDS 12
void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid);
void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,4);
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index db2f581972..69e2d41863 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -142,7 +142,7 @@ static int stdout_stream_save(StdoutStream *s) {
r = fopen_temporary(s->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fprintf(f,
"# This is private data. Do not parse\n"
@@ -163,7 +163,7 @@ static int stdout_stream_save(StdoutStream *s) {
escaped = cescape(s->identifier);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "IDENTIFIER=%s\n", escaped);
@@ -175,7 +175,7 @@ static int stdout_stream_save(StdoutStream *s) {
escaped = cescape(s->unit_id);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "UNIT=%s\n", escaped);
@@ -183,16 +183,13 @@ static int stdout_stream_save(StdoutStream *s) {
r = fflush_and_check(f);
if (r < 0)
- goto finish;
+ goto fail;
if (rename(temp_path, s->state_file) < 0) {
r = -errno;
- goto finish;
+ goto fail;
}
- free(temp_path);
- temp_path = NULL;
-
/* Store the connection fd in PID 1, so that we get it passed
* in again on next start */
if (!s->fdstore) {
@@ -200,14 +197,15 @@ static int stdout_stream_save(StdoutStream *s) {
s->fdstore = true;
}
-finish:
- if (temp_path)
- unlink(temp_path);
+ return 0;
- if (r < 0)
- log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
+fail:
+ (void) unlink(s->state_file);
+
+ if (temp_path)
+ (void) unlink(temp_path);
- return r;
+ return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
}
static int stdout_stream_log(StdoutStream *s, const char *p) {
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index 6e00b1ad30..5a3fcddb1b 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -27,6 +27,7 @@
#include "refcnt.h"
#include "util.h"
+#include "list.h"
#include "dhcp-protocol.h"
@@ -38,6 +39,14 @@ struct sd_dhcp_route {
unsigned char dst_prefixlen;
};
+struct sd_dhcp_raw_option {
+ LIST_FIELDS(struct sd_dhcp_raw_option, options);
+
+ uint8_t tag;
+ uint8_t length;
+ void *data;
+};
+
struct sd_dhcp_lease {
RefCount n_ref;
@@ -74,11 +83,14 @@ struct sd_dhcp_lease {
size_t client_id_len;
uint8_t *vendor_specific;
size_t vendor_specific_len;
+ LIST_HEAD(struct sd_dhcp_raw_option, private_options);
};
int dhcp_lease_new(sd_dhcp_lease **ret);
int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
void *user_data);
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
+ const uint8_t *data, uint8_t len);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index aa37e9b0b5..4308723f91 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -138,5 +138,7 @@ enum {
DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
DHCP_OPTION_CLIENT_IDENTIFIER = 61,
DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
+ DHCP_OPTION_PRIVATE_BASE = 224,
+ DHCP_OPTION_PRIVATE_LAST = 254,
DHCP_OPTION_END = 255,
};
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index d579755cc8..3d78bf8b35 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -509,3 +509,30 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
return 0;
}
+
+int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size) {
+ _cleanup_free_ char *hex_buf = NULL;
+
+ assert(f);
+ assert(key);
+ assert(data);
+
+ hex_buf = hexmem(data, size);
+ if (hex_buf == NULL)
+ return -ENOMEM;
+
+ fprintf(f, "%s=%s\n", key, hex_buf);
+
+ return 0;
+}
+
+int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string) {
+ assert(data);
+ assert(data_len);
+ assert(string);
+
+ if (strlen(string) % 2)
+ return -EINVAL;
+
+ return unhexmem(string, strlen(string), (void **)data, data_len);
+}
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 06aba893ce..7aaecbb10d 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -74,3 +74,6 @@ struct sd_dhcp_route;
void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
+
+int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size);
+int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string);
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 43f7566f65..eae186c9d3 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -203,6 +203,14 @@ sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
if (lease && REFCNT_DEC(lease->n_ref) == 0) {
+ while (lease->private_options) {
+ struct sd_dhcp_raw_option *option = lease->private_options;
+
+ LIST_REMOVE(options, lease->private_options, option);
+
+ free(option->data);
+ free(option);
+ }
free(lease->hostname);
free(lease->domainname);
free(lease->dns);
@@ -606,11 +614,49 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
}
break;
+
+ default:
+ if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST)
+ break;
+
+ r = dhcp_lease_insert_private_option(lease, code, option, len);
+ if (r < 0)
+ return r;
}
return 0;
}
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
+ const uint8_t *data, uint8_t len) {
+ struct sd_dhcp_raw_option *cur, *option;
+
+ LIST_FOREACH(options, cur, lease->private_options) {
+ if (tag < cur->tag)
+ break;
+ else if (tag == cur->tag) {
+ log_error("Ignoring duplicate option, tagged %d.", tag);
+ return 0;
+ }
+ }
+
+ option = new(struct sd_dhcp_raw_option, 1);
+ if (!option)
+ return -ENOMEM;
+
+ option->tag = tag;
+ option->length = len;
+ option->data = memdup(data, len);
+ if (!option->data) {
+ free(option);
+ return -ENOMEM;
+ }
+
+ LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
+
+ return 0;
+}
+
int dhcp_lease_new(sd_dhcp_lease **ret) {
sd_dhcp_lease *lease;
@@ -620,6 +666,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
lease->router = INADDR_ANY;
lease->n_ref = REFCNT_INIT;
+ LIST_HEAD_INIT(lease->private_options);
*ret = lease;
return 0;
@@ -628,6 +675,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
+ struct sd_dhcp_raw_option *option;
struct in_addr address;
const struct in_addr *addresses;
const uint8_t *client_id, *data;
@@ -642,13 +690,13 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = fopen_temporary(lease_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fchmod(fileno(f), 0644);
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
- goto finish;
+ goto fail;
fprintf(f,
"# This is private data. Do not parse.\n"
@@ -656,7 +704,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = sd_dhcp_lease_get_netmask(lease, &address);
if (r < 0)
- goto finish;
+ goto fail;
fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
@@ -712,7 +760,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
client_id_hex = hexmem(client_id, client_id_len);
if (!client_id_hex) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "CLIENTID=%s\n", client_id_hex);
}
@@ -724,26 +772,35 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
option_hex = hexmem(data, data_len);
if (!option_hex) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
}
- r = 0;
+ LIST_FOREACH(options, option, lease->private_options) {
+ char key[strlen("OPTION_000")+1];
+ snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
+ r = serialize_dhcp_option(f, key, option->data, option->length);
+ if (r < 0)
+ goto fail;
+ }
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, lease_file) < 0) {
+ if (rename(temp_path, lease_file) < 0) {
r = -errno;
- unlink(lease_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
+ return 0;
+
+fail:
+ if (temp_path)
+ (void) unlink(temp_path);
- return r;
+ return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
}
int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
@@ -752,9 +809,11 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
*server_address = NULL, *next_server = NULL,
*dns = NULL, *ntp = NULL, *mtu = NULL,
*routes = NULL, *client_id_hex = NULL,
- *vendor_specific_hex = NULL;
+ *vendor_specific_hex = NULL,
+ *options[DHCP_OPTION_PRIVATE_LAST -
+ DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };
struct in_addr addr;
- int r;
+ int r, i;
assert(lease_file);
assert(ret);
@@ -778,6 +837,37 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"ROUTES", &routes,
"CLIENTID", &client_id_hex,
"VENDOR_SPECIFIC", &vendor_specific_hex,
+ "OPTION_224", &options[0],
+ "OPTION_225", &options[1],
+ "OPTION_226", &options[2],
+ "OPTION_227", &options[3],
+ "OPTION_228", &options[4],
+ "OPTION_229", &options[5],
+ "OPTION_230", &options[6],
+ "OPTION_231", &options[7],
+ "OPTION_232", &options[8],
+ "OPTION_233", &options[9],
+ "OPTION_234", &options[10],
+ "OPTION_235", &options[11],
+ "OPTION_236", &options[12],
+ "OPTION_237", &options[13],
+ "OPTION_238", &options[14],
+ "OPTION_239", &options[15],
+ "OPTION_240", &options[16],
+ "OPTION_241", &options[17],
+ "OPTION_242", &options[18],
+ "OPTION_243", &options[19],
+ "OPTION_244", &options[20],
+ "OPTION_245", &options[21],
+ "OPTION_246", &options[22],
+ "OPTION_247", &options[23],
+ "OPTION_248", &options[24],
+ "OPTION_249", &options[25],
+ "OPTION_250", &options[26],
+ "OPTION_251", &options[27],
+ "OPTION_252", &options[28],
+ "OPTION_253", &options[29],
+ "OPTION_254", &options[30],
NULL);
if (r < 0) {
if (r == -ENOENT)
@@ -852,19 +942,29 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
if (client_id_hex) {
- if (strlen(client_id_hex) % 2)
- return -EINVAL;
-
- r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len);
+ r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
if (r < 0)
return r;
}
if (vendor_specific_hex) {
- if (strlen(vendor_specific_hex) % 2)
- return -EINVAL;
+ r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
+ if (r < 0)
+ return r;
+ }
+
+ for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
+ uint8_t *data;
+ size_t len;
+
+ if (!options[i])
+ continue;
+
+ r = deserialize_dhcp_option(&data, &len, options[i]);
+ if (r < 0)
+ return r;
- r = unhexmem(vendor_specific_hex, strlen(vendor_specific_hex), (void**) &lease->vendor_specific, &lease->vendor_specific_len);
+ r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
if (r < 0)
return r;
}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index cc5e032344..faeab0fd30 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -67,7 +67,7 @@ int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
}
bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
- assert_return(server, -EINVAL);
+ assert_return(server, false);
return !!server->receive_message;
}
@@ -796,8 +796,12 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
r = sd_event_now(server->event,
clock_boottime_or_monotonic(),
&time_now);
- if (r < 0)
- time_now = now(clock_boottime_or_monotonic());
+ if (r < 0) {
+ if (!existing_lease)
+ dhcp_lease_free(lease);
+ return r;
+ }
+
lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
r = server_send_ack(server, req, address);
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 85162dc555..e2f5862851 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -975,14 +975,9 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
client->retransmit_time = 0;
client->retransmit_count = 0;
- if (client->state == DHCP6_STATE_STOPPED) {
- time_now = now(clock_boottime_or_monotonic());
- } else {
- r = sd_event_now(client->event, clock_boottime_or_monotonic(),
- &time_now);
- if (r < 0)
- return r;
- }
+ r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ return r;
switch (state) {
case DHCP6_STATE_STOPPED:
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 9e04db96bb..f080c5c0a7 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -187,8 +187,7 @@ static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
if (random_sec)
next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
- if (sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) < 0)
- time_now = now(clock_boottime_or_monotonic());
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
ll->next_wakeup = time_now + next_timeout;
ll->next_wakeup_valid = 1;
@@ -508,7 +507,7 @@ error:
}
bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
- assert_return(ll, -EINVAL);
+ assert_return(ll, false);
return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
}
diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
index 6a2c05185d..574e04b541 100644
--- a/src/libsystemd-network/sd-lldp.c
+++ b/src/libsystemd-network/sd-lldp.c
@@ -392,7 +392,7 @@ static void lldp_mib_delete_objects(sd_lldp *lldp) {
break;
if (t <= 0)
- t = now(CLOCK_BOOTTIME);
+ t = now(clock_boottime_or_monotonic());
if (p->until > t)
break;
@@ -440,7 +440,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
r = fopen_temporary(lldp_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fchmod(fileno(f), 0644);
@@ -457,8 +457,10 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
s = strdup(buf);
- if (!s)
- return -ENOMEM;
+ if (!s) {
+ r = -ENOMEM;
+ goto fail;
+ }
r = lldp_read_port_id(p->packet, &type, &length, &port_id);
if (r < 0)
@@ -466,8 +468,10 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
k = strndup((char *) port_id, length -1);
- if (!k)
- return -ENOMEM;
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
free(k);
@@ -478,13 +482,15 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
}
k = strappend(s, buf);
- if (!k)
- return -ENOMEM;
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
free(s);
s = k;
- time = now(CLOCK_BOOTTIME);
+ time = now(clock_boottime_or_monotonic());
/* Don't write expired packets */
if (time - p->until <= 0)
@@ -493,8 +499,10 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
k = strappend(s, buf);
- if (!k)
- return -ENOMEM;
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
free(s);
s = k;
@@ -504,15 +512,19 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
k = strappend(s, "'_NAME=N/A' ");
else {
t = strndup(k, length);
- if (!t)
- return -ENOMEM;
+ if (!t) {
+ r = -ENOMEM;
+ goto fail;
+ }
k = strjoin(s, "'_NAME=", t, "' ", NULL);
free(t);
}
- if (!k)
- return -ENOMEM;
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
free(s);
s = k;
@@ -522,8 +534,10 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
sprintf(buf, "'_CAP=%x'", data);
k = strappend(s, buf);
- if (!k)
- return -ENOMEM;
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
free(s);
s = k;
@@ -531,21 +545,23 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
fprintf(f, "%s\n", s);
}
}
- r = 0;
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, lldp_file) < 0) {
+ if (rename(temp_path, lldp_file) < 0) {
r = -errno;
- unlink(lldp_file);
- unlink(temp_path);
+ goto fail;
}
- finish:
- if (r < 0)
- log_error("Failed to save lldp data %s: %s", lldp_file, strerror(-r));
+ return 0;
+
+ fail:
+ if (temp_path)
+ (void) unlink(temp_path);
- return r;
+ return log_error_errno(r, "Failed to save lldp data %s: %m", lldp_file);
}
int sd_lldp_start(sd_lldp *lldp) {
diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c
index 1de8a5e8bf..ff064f563f 100644
--- a/src/libsystemd-network/sd-pppoe.c
+++ b/src/libsystemd-network/sd-pppoe.c
@@ -346,9 +346,7 @@ static int pppoe_arm_timeout(sd_pppoe *ppp) {
assert(ppp);
r = sd_event_now(ppp->event, clock_boottime_or_monotonic(), &next_timeout);
- if (r == -ENODATA)
- next_timeout = now(clock_boottime_or_monotonic());
- else if (r < 0)
+ if (r < 0)
return r;
next_timeout += 500 * USEC_PER_MSEC;
diff --git a/src/libsystemd-terminal/.gitignore b/src/libsystemd-terminal/.gitignore
deleted file mode 100644
index 7de83bd3e9..0000000000
--- a/src/libsystemd-terminal/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/unifont-glyph-array.bin
diff --git a/src/libsystemd-terminal/evcat.c b/src/libsystemd-terminal/evcat.c
deleted file mode 100644
index 2aeefc2e16..0000000000
--- a/src/libsystemd-terminal/evcat.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Event Catenation
- * The evcat tool catenates input events of all requested devices and prints
- * them to standard-output. It's only meant for debugging of input-related
- * problems.
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <libevdev/libevdev.h>
-#include <linux/kd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <termios.h>
-#include <unistd.h>
-#include <xkbcommon/xkbcommon.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "build.h"
-#include "event-util.h"
-#include "macro.h"
-#include "signal-util.h"
-#include "util.h"
-#include "idev.h"
-#include "sysview.h"
-#include "term-internal.h"
-
-typedef struct Evcat Evcat;
-
-struct Evcat {
- char *session;
- char *seat;
- sd_event *event;
- sd_bus *bus;
- sysview_context *sysview;
- idev_context *idev;
- idev_session *idev_session;
-
- bool managed : 1;
-};
-
-static Evcat *evcat_free(Evcat *e) {
- if (!e)
- return NULL;
-
- e->idev_session = idev_session_free(e->idev_session);
- e->idev = idev_context_unref(e->idev);
- e->sysview = sysview_context_free(e->sysview);
- e->bus = sd_bus_unref(e->bus);
- e->event = sd_event_unref(e->event);
- free(e->seat);
- free(e->session);
- free(e);
-
- tcflush(0, TCIOFLUSH);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Evcat*, evcat_free);
-
-static bool is_managed(const char *session) {
- unsigned int vtnr;
- struct stat st;
- long mode;
- int r;
-
- /* Using logind's Controller API is highly fragile if there is already
- * a session controller running. If it is registered as controller
- * itself, TakeControl will simply fail. But if its a legacy controller
- * that does not use logind's controller API, we must never register
- * our own controller. Otherwise, we really mess up the VT. Therefore,
- * only run in managed mode if there's no-one else. */
-
- if (geteuid() == 0)
- return false;
-
- if (!isatty(1))
- return false;
-
- if (!session)
- return false;
-
- r = sd_session_get_vt(session, &vtnr);
- if (r < 0 || vtnr < 1 || vtnr > 63)
- return false;
-
- mode = 0;
- r = ioctl(1, KDGETMODE, &mode);
- if (r < 0 || mode != KD_TEXT)
- return false;
-
- r = fstat(1, &st);
- if (r < 0 || minor(st.st_rdev) != vtnr)
- return false;
-
- return true;
-}
-
-static int evcat_new(Evcat **out) {
- _cleanup_(evcat_freep) Evcat *e = NULL;
- int r;
-
- assert(out);
-
- e = new0(Evcat, 1);
- if (!e)
- return log_oom();
-
- r = sd_pid_get_session(getpid(), &e->session);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve logind session: %m");
-
- r = sd_session_get_seat(e->session, &e->seat);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve seat of logind session: %m");
-
- e->managed = is_managed(e->session);
-
- r = sd_event_default(&e->event);
- if (r < 0)
- return r;
-
- r = sd_bus_open_system(&e->bus);
- if (r < 0)
- return r;
-
- r = sd_bus_attach_event(e->bus, e->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return r;
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(e->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(e->event, NULL, SIGINT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sysview_context_new(&e->sysview,
- SYSVIEW_CONTEXT_SCAN_LOGIND |
- SYSVIEW_CONTEXT_SCAN_EVDEV,
- e->event,
- e->bus,
- NULL);
- if (r < 0)
- return r;
-
- r = idev_context_new(&e->idev, e->event, e->bus);
- if (r < 0)
- return r;
-
- *out = e;
- e = NULL;
- return 0;
-}
-
-static void kdata_print(idev_data *data) {
- idev_data_keyboard *k = &data->keyboard;
- char buf[128];
- uint32_t i, c;
- int cwidth;
-
- /* Key-press state: UP/DOWN/REPEAT */
- printf(" %-6s", k->value == 0 ? "UP" :
- k->value == 1 ? "DOWN" :
- "REPEAT");
-
- /* Resync state */
- printf(" | %-6s", data->resync ? "RESYNC" : "");
-
- /* Keycode that triggered the event */
- printf(" | %5u", (unsigned)k->keycode);
-
- /* Well-known name of the keycode */
- printf(" | %-20s", libevdev_event_code_get_name(EV_KEY, k->keycode) ? : "<unknown>");
-
- /* Well-known modifiers */
- printf(" | %-5s", (k->mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
- printf(" %-4s", (k->mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
- printf(" %-3s", (k->mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
- printf(" %-5s", (k->mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
- printf(" %-4s", (k->mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
-
- /* Consumed modifiers */
- printf(" | %-5s", (k->consumed_mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
- printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
- printf(" %-3s", (k->consumed_mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
- printf(" %-5s", (k->consumed_mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
- printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
-
- /* Resolved symbols */
- printf(" |");
- for (i = 0; i < k->n_syms; ++i) {
- buf[0] = 0;
- xkb_keysym_get_name(k->keysyms[i], buf, sizeof(buf));
-
- if (is_locale_utf8()) {
- c = k->codepoints[i];
- if (c < 0x110000 && c > 0x20 && (c < 0x7f || c > 0x9f)) {
- /* "%4lc" doesn't work well, so hard-code it */
- cwidth = mk_wcwidth(c);
- while (cwidth++ < 2)
- printf(" ");
-
- printf(" '%lc':", (wchar_t)c);
- } else {
- printf(" ");
- }
- }
-
- printf(" XKB_KEY_%-30s", buf);
- }
-
- printf("\n");
-}
-
-static bool kdata_is_exit(idev_data *data) {
- idev_data_keyboard *k = &data->keyboard;
-
- if (k->value != 1)
- return false;
- if (k->n_syms != 1)
- return false;
-
- return k->codepoints[0] == 'q';
-}
-
-static int evcat_idev_fn(idev_session *session, void *userdata, idev_event *ev) {
- Evcat *e = userdata;
-
- switch (ev->type) {
- case IDEV_EVENT_DEVICE_ADD:
- idev_device_enable(ev->device_add.device);
- break;
- case IDEV_EVENT_DEVICE_REMOVE:
- idev_device_disable(ev->device_remove.device);
- break;
- case IDEV_EVENT_DEVICE_DATA:
- switch (ev->device_data.data.type) {
- case IDEV_DATA_KEYBOARD:
- if (kdata_is_exit(&ev->device_data.data))
- sd_event_exit(e->event, 0);
- else
- kdata_print(&ev->device_data.data);
-
- break;
- }
-
- break;
- }
-
- return 0;
-}
-
-static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
- unsigned int flags, type;
- Evcat *e = userdata;
- sysview_device *d;
- const char *name;
- int r;
-
- switch (ev->type) {
- case SYSVIEW_EVENT_SESSION_FILTER:
- if (streq_ptr(e->session, ev->session_filter.id))
- return 1;
-
- break;
- case SYSVIEW_EVENT_SESSION_ADD:
- assert(!e->idev_session);
-
- name = sysview_session_get_name(ev->session_add.session);
- flags = 0;
-
- if (e->managed)
- flags |= IDEV_SESSION_MANAGED;
-
- r = idev_session_new(&e->idev_session,
- e->idev,
- flags,
- name,
- evcat_idev_fn,
- e);
- if (r < 0)
- return log_error_errno(r, "Cannot create idev session: %m");
-
- if (e->managed) {
- r = sysview_session_take_control(ev->session_add.session);
- if (r < 0)
- return log_error_errno(r, "Cannot request session control: %m");
- }
-
- idev_session_enable(e->idev_session);
-
- break;
- case SYSVIEW_EVENT_SESSION_REMOVE:
- idev_session_disable(e->idev_session);
- e->idev_session = idev_session_free(e->idev_session);
- if (sd_event_get_exit_code(e->event, &r) == -ENODATA)
- sd_event_exit(e->event, 0);
- break;
- case SYSVIEW_EVENT_SESSION_ATTACH:
- d = ev->session_attach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_EVDEV) {
- r = idev_session_add_evdev(e->idev_session, sysview_device_get_ud(d));
- if (r < 0)
- return log_error_errno(r, "Cannot add evdev device to idev: %m");
- }
-
- break;
- case SYSVIEW_EVENT_SESSION_DETACH:
- d = ev->session_detach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_EVDEV) {
- r = idev_session_remove_evdev(e->idev_session, sysview_device_get_ud(d));
- if (r < 0)
- return log_error_errno(r, "Cannot remove evdev device from idev: %m");
- }
-
- break;
- case SYSVIEW_EVENT_SESSION_CONTROL:
- r = ev->session_control.error;
- if (r < 0)
- return log_error_errno(r, "Cannot acquire session control: %m");
-
- r = ioctl(1, KDSKBMODE, K_UNICODE);
- if (r < 0)
- return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m");
-
- r = ioctl(1, KDSETMODE, KD_TEXT);
- if (r < 0)
- return log_error_errno(errno, "Cannot set KD_TEXT on stdout: %m");
-
- printf("\n");
-
- break;
- }
-
- return 0;
-}
-
-static int evcat_run(Evcat *e) {
- struct termios in_attr, saved_attr;
- int r;
-
- assert(e);
-
- if (!e->managed && geteuid() > 0)
- log_warning("You run in unmanaged mode without being root. This is likely to produce no output..");
-
- printf("evcat - Read and catenate events from selected input devices\n"
- " Running on seat '%s' in user-session '%s'\n"
- " Exit by pressing ^C or 'q'\n\n",
- e->seat ? : "seat0", e->session ? : "<none>");
-
- r = sysview_context_start(e->sysview, evcat_sysview_fn, e);
- if (r < 0)
- goto out;
-
- r = tcgetattr(0, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- saved_attr = in_attr;
- in_attr.c_lflag &= ~ECHO;
-
- r = tcsetattr(0, TCSANOW, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- r = sd_event_loop(e->event);
- tcsetattr(0, TCSANOW, &saved_attr);
- printf("exiting..\n");
-
-out:
- sysview_context_stop(e->sysview);
- return r;
-}
-
-static int help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Read and catenate events from selected input devices.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- , program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- };
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- {},
- };
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- switch (c) {
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
-
- if (argc > optind) {
- log_error("Too many arguments");
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_(evcat_freep) Evcat *e = NULL;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- setlocale(LC_ALL, "");
- if (!is_locale_utf8())
- log_warning("Locale is not set to UTF-8. Codepoints will not be printed!");
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- r = evcat_new(&e);
- if (r < 0)
- goto finish;
-
- r = evcat_run(e);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
deleted file mode 100644
index 10c13e348a..0000000000
--- a/src/libsystemd-terminal/grdev-drm.c
+++ /dev/null
@@ -1,3092 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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 <fcntl.h>
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-/* Yuck! DRM headers need system headers included first.. but we have to
- * include it before util/missing.h to avoid redefining ioctl bits */
-#include <drm.h>
-#include <drm_fourcc.h>
-#include <drm_mode.h>
-
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "util.h"
-#include "bus-util.h"
-#include "grdev.h"
-#include "grdev-internal.h"
-
-#define GRDRM_MAX_TRIES (16)
-
-typedef struct grdrm_object grdrm_object;
-typedef struct grdrm_plane grdrm_plane;
-typedef struct grdrm_connector grdrm_connector;
-typedef struct grdrm_encoder grdrm_encoder;
-typedef struct grdrm_crtc grdrm_crtc;
-
-typedef struct grdrm_fb grdrm_fb;
-typedef struct grdrm_pipe grdrm_pipe;
-typedef struct grdrm_card grdrm_card;
-typedef struct unmanaged_card unmanaged_card;
-typedef struct managed_card managed_card;
-
-/*
- * Objects
- */
-
-enum {
- GRDRM_TYPE_CRTC,
- GRDRM_TYPE_ENCODER,
- GRDRM_TYPE_CONNECTOR,
- GRDRM_TYPE_PLANE,
- GRDRM_TYPE_CNT
-};
-
-struct grdrm_object {
- grdrm_card *card;
- uint32_t id;
- uint32_t index;
- unsigned int type;
- void (*free_fn) (grdrm_object *object);
-
- bool present : 1;
- bool assigned : 1;
-};
-
-struct grdrm_plane {
- grdrm_object object;
-
- struct {
- uint32_t used_crtc;
- uint32_t used_fb;
- uint32_t gamma_size;
-
- uint32_t n_crtcs;
- uint32_t max_crtcs;
- uint32_t *crtcs;
- uint32_t n_formats;
- uint32_t max_formats;
- uint32_t *formats;
- } kern;
-};
-
-struct grdrm_connector {
- grdrm_object object;
-
- struct {
- uint32_t type;
- uint32_t type_id;
- uint32_t used_encoder;
- uint32_t connection;
- uint32_t mm_width;
- uint32_t mm_height;
- uint32_t subpixel;
-
- uint32_t n_encoders;
- uint32_t max_encoders;
- uint32_t *encoders;
- uint32_t n_modes;
- uint32_t max_modes;
- struct drm_mode_modeinfo *modes;
- uint32_t n_props;
- uint32_t max_props;
- uint32_t *prop_ids;
- uint64_t *prop_values;
- } kern;
-};
-
-struct grdrm_encoder {
- grdrm_object object;
-
- struct {
- uint32_t type;
- uint32_t used_crtc;
-
- uint32_t n_crtcs;
- uint32_t max_crtcs;
- uint32_t *crtcs;
- uint32_t n_clones;
- uint32_t max_clones;
- uint32_t *clones;
- } kern;
-};
-
-struct grdrm_crtc {
- grdrm_object object;
-
- struct {
- uint32_t used_fb;
- uint32_t fb_offset_x;
- uint32_t fb_offset_y;
- uint32_t gamma_size;
-
- uint32_t n_used_connectors;
- uint32_t max_used_connectors;
- uint32_t *used_connectors;
-
- bool mode_set;
- struct drm_mode_modeinfo mode;
- } kern;
-
- struct {
- bool set;
- uint32_t fb;
- uint32_t fb_x;
- uint32_t fb_y;
- uint32_t gamma;
-
- uint32_t n_connectors;
- uint32_t *connectors;
-
- bool mode_set;
- struct drm_mode_modeinfo mode;
- } old;
-
- struct {
- struct drm_mode_modeinfo mode;
- uint32_t n_connectors;
- uint32_t max_connectors;
- uint32_t *connectors;
- } set;
-
- grdrm_pipe *pipe;
-
- bool applied : 1;
-};
-
-#define GRDRM_OBJECT_INIT(_card, _id, _index, _type, _free_fn) ((grdrm_object){ \
- .card = (_card), \
- .id = (_id), \
- .index = (_index), \
- .type = (_type), \
- .free_fn = (_free_fn), \
- })
-
-grdrm_object *grdrm_find_object(grdrm_card *card, uint32_t id);
-int grdrm_object_add(grdrm_object *object);
-grdrm_object *grdrm_object_free(grdrm_object *object);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdrm_object*, grdrm_object_free);
-
-int grdrm_plane_new(grdrm_plane **out, grdrm_card *card, uint32_t id, uint32_t index);
-int grdrm_connector_new(grdrm_connector **out, grdrm_card *card, uint32_t id, uint32_t index);
-int grdrm_encoder_new(grdrm_encoder **out, grdrm_card *card, uint32_t id, uint32_t index);
-int grdrm_crtc_new(grdrm_crtc **out, grdrm_card *card, uint32_t id, uint32_t index);
-
-#define plane_from_object(_obj) container_of((_obj), grdrm_plane, object)
-#define connector_from_object(_obj) container_of((_obj), grdrm_connector, object)
-#define encoder_from_object(_obj) container_of((_obj), grdrm_encoder, object)
-#define crtc_from_object(_obj) container_of((_obj), grdrm_crtc, object)
-
-/*
- * Framebuffers
- */
-
-struct grdrm_fb {
- grdev_fb base;
- grdrm_card *card;
- uint32_t id;
- uint32_t handles[4];
- uint32_t offsets[4];
- uint32_t sizes[4];
- uint32_t flipid;
-};
-
-static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_modeinfo *mode);
-grdrm_fb *grdrm_fb_free(grdrm_fb *fb);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdrm_fb*, grdrm_fb_free);
-
-#define fb_from_base(_fb) container_of((_fb), grdrm_fb, base)
-
-/*
- * Pipes
- */
-
-struct grdrm_pipe {
- grdev_pipe base;
- grdrm_crtc *crtc;
- uint32_t counter;
-};
-
-#define grdrm_pipe_from_base(_e) container_of((_e), grdrm_pipe, base)
-
-#define GRDRM_PIPE_NAME_MAX (GRDRM_CARD_NAME_MAX + 1 + DECIMAL_STR_MAX(uint32_t))
-
-static const grdev_pipe_vtable grdrm_pipe_vtable;
-
-static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_modeinfo *mode, size_t n_fbs);
-
-/*
- * Cards
- */
-
-struct grdrm_card {
- grdev_card base;
-
- int fd;
- sd_event_source *fd_src;
-
- uint32_t n_crtcs;
- uint32_t n_encoders;
- uint32_t n_connectors;
- uint32_t n_planes;
- uint32_t max_ids;
- Hashmap *object_map;
-
- bool async_hotplug : 1;
- bool hotplug : 1;
- bool running : 1;
- bool ready : 1;
- bool cap_dumb : 1;
- bool cap_monotonic : 1;
-};
-
-struct unmanaged_card {
- grdrm_card card;
- char *devnode;
-};
-
-struct managed_card {
- grdrm_card card;
- dev_t devnum;
-
- sd_bus_slot *slot_pause_device;
- sd_bus_slot *slot_resume_device;
- sd_bus_slot *slot_take_device;
-
- bool requested : 1; /* TakeDevice() was sent */
- bool acquired : 1; /* TakeDevice() was successful */
- bool master : 1; /* we are DRM-Master */
-};
-
-#define grdrm_card_from_base(_e) container_of((_e), grdrm_card, base)
-#define unmanaged_card_from_base(_e) \
- container_of(grdrm_card_from_base(_e), unmanaged_card, card)
-#define managed_card_from_base(_e) \
- container_of(grdrm_card_from_base(_e), managed_card, card)
-
-#define GRDRM_CARD_INIT(_vtable, _session) ((grdrm_card){ \
- .base = GRDEV_CARD_INIT((_vtable), (_session)), \
- .fd = -1, \
- .max_ids = 32, \
- })
-
-#define GRDRM_CARD_NAME_MAX (6 + DECIMAL_STR_MAX(unsigned) * 2)
-
-static const grdev_card_vtable unmanaged_card_vtable;
-static const grdev_card_vtable managed_card_vtable;
-
-static int grdrm_card_open(grdrm_card *card, int dev_fd);
-static void grdrm_card_close(grdrm_card *card);
-static bool grdrm_card_async(grdrm_card *card, int r);
-
-/*
- * The page-flip event of the kernel provides 64bit of arbitrary user-data. As
- * drivers tend to drop events on intermediate deep mode-sets or because we
- * might receive events during session activation, we try to avoid allocaing
- * dynamic data on those events. Instead, we safe the CRTC id plus a 32bit
- * counter in there. This way, we only get 32bit counters, not 64bit, but that
- * should be more than enough. On the bright side, we no longer care whether we
- * lose events. No memory leaks will occur.
- * Modern DRM drivers might be fixed to no longer leak events, but we want to
- * be safe. And associating dynamically allocated data with those events is
- * kinda ugly, anyway.
- */
-
-static uint64_t grdrm_encode_vblank_data(uint32_t id, uint32_t counter) {
- return id | ((uint64_t)counter << 32);
-}
-
-static void grdrm_decode_vblank_data(uint64_t data, uint32_t *out_id, uint32_t *out_counter) {
- if (out_id)
- *out_id = data & 0xffffffffU;
- if (out_counter)
- *out_counter = (data >> 32) & 0xffffffffU;
-}
-
-static bool grdrm_modes_compatible(const struct drm_mode_modeinfo *a, const struct drm_mode_modeinfo *b) {
- assert(a);
- assert(b);
-
- /* Test whether both modes are compatible according to our internal
- * assumptions on modes. This comparison is highly dependent on how
- * we treat modes in grdrm. If we export mode details, we need to
- * make this comparison much stricter. */
-
- if (a->hdisplay != b->hdisplay)
- return false;
- if (a->vdisplay != b->vdisplay)
- return false;
- if (a->vrefresh != b->vrefresh)
- return false;
-
- return true;
-}
-
-/*
- * Objects
- */
-
-grdrm_object *grdrm_find_object(grdrm_card *card, uint32_t id) {
- assert_return(card, NULL);
-
- return id > 0 ? hashmap_get(card->object_map, UINT32_TO_PTR(id)) : NULL;
-}
-
-int grdrm_object_add(grdrm_object *object) {
- int r;
-
- assert(object);
- assert(object->card);
- assert(object->id > 0);
- assert(IN_SET(object->type, GRDRM_TYPE_CRTC, GRDRM_TYPE_ENCODER, GRDRM_TYPE_CONNECTOR, GRDRM_TYPE_PLANE));
- assert(object->free_fn);
-
- if (object->index >= 32)
- log_debug("grdrm: %s: object index exceeds 32bit masks: type=%u, index=%" PRIu32,
- object->card->base.name, object->type, object->index);
-
- r = hashmap_put(object->card->object_map, UINT32_TO_PTR(object->id), object);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-grdrm_object *grdrm_object_free(grdrm_object *object) {
- if (!object)
- return NULL;
-
- assert(object->card);
- assert(object->id > 0);
- assert(IN_SET(object->type, GRDRM_TYPE_CRTC, GRDRM_TYPE_ENCODER, GRDRM_TYPE_CONNECTOR, GRDRM_TYPE_PLANE));
- assert(object->free_fn);
-
- hashmap_remove_value(object->card->object_map, UINT32_TO_PTR(object->id), object);
-
- object->free_fn(object);
- return NULL;
-}
-
-/*
- * Planes
- */
-
-static void plane_free(grdrm_object *object) {
- grdrm_plane *plane = plane_from_object(object);
-
- free(plane->kern.formats);
- free(plane->kern.crtcs);
- free(plane);
-}
-
-int grdrm_plane_new(grdrm_plane **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_plane *plane;
- int r;
-
- assert(card);
-
- plane = new0(grdrm_plane, 1);
- if (!plane)
- return -ENOMEM;
-
- object = &plane->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_PLANE, plane_free);
-
- plane->kern.max_crtcs = 32;
- plane->kern.crtcs = new0(uint32_t, plane->kern.max_crtcs);
- if (!plane->kern.crtcs)
- return -ENOMEM;
-
- plane->kern.max_formats = 32;
- plane->kern.formats = new0(uint32_t, plane->kern.max_formats);
- if (!plane->kern.formats)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = plane;
- object = NULL;
- return 0;
-}
-
-static int grdrm_plane_resync(grdrm_plane *plane) {
- grdrm_card *card = plane->object.card;
- size_t tries;
- int r;
-
- assert(plane);
-
- for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
- struct drm_mode_get_plane res;
- grdrm_object *object;
- bool resized = false;
- Iterator iter;
-
- zero(res);
- res.plane_id = plane->object.id;
- res.format_type_ptr = PTR_TO_UINT64(plane->kern.formats);
- res.count_format_types = plane->kern.max_formats;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANE, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: plane %u removed during resync",
- card->base.name, plane->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve plane %u: %m",
- card->base.name, plane->object.id);
- }
-
- return r;
- }
-
- plane->kern.n_crtcs = 0;
- memzero(plane->kern.crtcs, sizeof(uint32_t) * plane->kern.max_crtcs);
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CRTC || object->index >= 32)
- continue;
- if (!(res.possible_crtcs & (1 << object->index)))
- continue;
- if (plane->kern.n_crtcs >= 32) {
- log_debug("grdrm: %s: possible_crtcs of plane %" PRIu32 " exceeds 32bit mask",
- card->base.name, plane->object.id);
- continue;
- }
-
- plane->kern.crtcs[plane->kern.n_crtcs++] = object->id;
- }
-
- if (res.count_format_types > plane->kern.max_formats) {
- uint32_t max, *t;
-
- max = ALIGN_POWER2(res.count_format_types);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive plane resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- t = realloc(plane->kern.formats, sizeof(*t) * max);
- if (!t)
- return -ENOMEM;
-
- plane->kern.formats = t;
- plane->kern.max_formats = max;
- resized = true;
- }
-
- if (resized)
- continue;
-
- plane->kern.n_formats = res.count_format_types;
- plane->kern.used_crtc = res.crtc_id;
- plane->kern.used_fb = res.fb_id;
- plane->kern.gamma_size = res.gamma_size;
-
- break;
- }
-
- if (tries >= GRDRM_MAX_TRIES) {
- log_debug("grdrm: %s: plane %u not settled for retrieval", card->base.name, plane->object.id);
- return -EFAULT;
- }
-
- return 0;
-}
-
-/*
- * Connectors
- */
-
-static void connector_free(grdrm_object *object) {
- grdrm_connector *connector = connector_from_object(object);
-
- free(connector->kern.prop_values);
- free(connector->kern.prop_ids);
- free(connector->kern.modes);
- free(connector->kern.encoders);
- free(connector);
-}
-
-int grdrm_connector_new(grdrm_connector **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_connector *connector;
- int r;
-
- assert(card);
-
- connector = new0(grdrm_connector, 1);
- if (!connector)
- return -ENOMEM;
-
- object = &connector->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_CONNECTOR, connector_free);
-
- connector->kern.max_encoders = 32;
- connector->kern.encoders = new0(uint32_t, connector->kern.max_encoders);
- if (!connector->kern.encoders)
- return -ENOMEM;
-
- connector->kern.max_modes = 32;
- connector->kern.modes = new0(struct drm_mode_modeinfo, connector->kern.max_modes);
- if (!connector->kern.modes)
- return -ENOMEM;
-
- connector->kern.max_props = 32;
- connector->kern.prop_ids = new0(uint32_t, connector->kern.max_props);
- connector->kern.prop_values = new0(uint64_t, connector->kern.max_props);
- if (!connector->kern.prop_ids || !connector->kern.prop_values)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = connector;
- object = NULL;
- return 0;
-}
-
-static int grdrm_connector_resync(grdrm_connector *connector) {
- grdrm_card *card = connector->object.card;
- size_t tries;
- int r;
-
- assert(connector);
-
- for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
- struct drm_mode_get_connector res;
- bool resized = false;
- uint32_t max;
-
- zero(res);
- res.connector_id = connector->object.id;
- res.encoders_ptr = PTR_TO_UINT64(connector->kern.encoders);
- res.props_ptr = PTR_TO_UINT64(connector->kern.prop_ids);
- res.prop_values_ptr = PTR_TO_UINT64(connector->kern.prop_values);
- res.count_encoders = connector->kern.max_encoders;
- res.count_props = connector->kern.max_props;
-
- /* The kernel reads modes from the EDID information only if we
- * pass count_modes==0. This is a legacy hack for libdrm (which
- * called every ioctl twice). Now we have to adopt.. *sigh*.
- * If we never received an hotplug event, there's no reason to
- * sync modes. EDID reads are heavy, so skip that if not
- * required. */
- if (card->hotplug) {
- if (tries > 0) {
- res.modes_ptr = PTR_TO_UINT64(connector->kern.modes);
- res.count_modes = connector->kern.max_modes;
- } else {
- resized = true;
- }
- }
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETCONNECTOR, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: connector %u removed during resync",
- card->base.name, connector->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve connector %u: %m",
- card->base.name, connector->object.id);
- }
-
- return r;
- }
-
- if (res.count_encoders > connector->kern.max_encoders) {
- uint32_t *t;
-
- max = ALIGN_POWER2(res.count_encoders);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- t = realloc(connector->kern.encoders, sizeof(*t) * max);
- if (!t)
- return -ENOMEM;
-
- connector->kern.encoders = t;
- connector->kern.max_encoders = max;
- resized = true;
- }
-
- if (res.count_modes > connector->kern.max_modes) {
- struct drm_mode_modeinfo *t;
-
- max = ALIGN_POWER2(res.count_modes);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- t = realloc(connector->kern.modes, sizeof(*t) * max);
- if (!t)
- return -ENOMEM;
-
- connector->kern.modes = t;
- connector->kern.max_modes = max;
- resized = true;
- }
-
- if (res.count_props > connector->kern.max_props) {
- uint32_t *tids;
- uint64_t *tvals;
-
- max = ALIGN_POWER2(res.count_props);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- tids = realloc(connector->kern.prop_ids, sizeof(*tids) * max);
- if (!tids)
- return -ENOMEM;
- connector->kern.prop_ids = tids;
-
- tvals = realloc(connector->kern.prop_values, sizeof(*tvals) * max);
- if (!tvals)
- return -ENOMEM;
- connector->kern.prop_values = tvals;
-
- connector->kern.max_props = max;
- resized = true;
- }
-
- if (resized)
- continue;
-
- connector->kern.n_encoders = res.count_encoders;
- connector->kern.n_props = res.count_props;
- connector->kern.type = res.connector_type;
- connector->kern.type_id = res.connector_type_id;
- connector->kern.used_encoder = res.encoder_id;
- connector->kern.connection = res.connection;
- connector->kern.mm_width = res.mm_width;
- connector->kern.mm_height = res.mm_height;
- connector->kern.subpixel = res.subpixel;
- if (res.modes_ptr == PTR_TO_UINT64(connector->kern.modes))
- connector->kern.n_modes = res.count_modes;
-
- break;
- }
-
- if (tries >= GRDRM_MAX_TRIES) {
- log_debug("grdrm: %s: connector %u not settled for retrieval", card->base.name, connector->object.id);
- return -EFAULT;
- }
-
- return 0;
-}
-
-/*
- * Encoders
- */
-
-static void encoder_free(grdrm_object *object) {
- grdrm_encoder *encoder = encoder_from_object(object);
-
- free(encoder->kern.clones);
- free(encoder->kern.crtcs);
- free(encoder);
-}
-
-int grdrm_encoder_new(grdrm_encoder **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_encoder *encoder;
- int r;
-
- assert(card);
-
- encoder = new0(grdrm_encoder, 1);
- if (!encoder)
- return -ENOMEM;
-
- object = &encoder->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_ENCODER, encoder_free);
-
- encoder->kern.max_crtcs = 32;
- encoder->kern.crtcs = new0(uint32_t, encoder->kern.max_crtcs);
- if (!encoder->kern.crtcs)
- return -ENOMEM;
-
- encoder->kern.max_clones = 32;
- encoder->kern.clones = new0(uint32_t, encoder->kern.max_clones);
- if (!encoder->kern.clones)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = encoder;
- object = NULL;
- return 0;
-}
-
-static int grdrm_encoder_resync(grdrm_encoder *encoder) {
- grdrm_card *card = encoder->object.card;
- struct drm_mode_get_encoder res;
- grdrm_object *object;
- Iterator iter;
- int r;
-
- assert(encoder);
-
- zero(res);
- res.encoder_id = encoder->object.id;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETENCODER, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: encoder %u removed during resync",
- card->base.name, encoder->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve encoder %u: %m",
- card->base.name, encoder->object.id);
- }
-
- return r;
- }
-
- encoder->kern.type = res.encoder_type;
- encoder->kern.used_crtc = res.crtc_id;
-
- encoder->kern.n_crtcs = 0;
- memzero(encoder->kern.crtcs, sizeof(uint32_t) * encoder->kern.max_crtcs);
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CRTC || object->index >= 32)
- continue;
- if (!(res.possible_crtcs & (1 << object->index)))
- continue;
- if (encoder->kern.n_crtcs >= 32) {
- log_debug("grdrm: %s: possible_crtcs exceeds 32bit mask", card->base.name);
- continue;
- }
-
- encoder->kern.crtcs[encoder->kern.n_crtcs++] = object->id;
- }
-
- encoder->kern.n_clones = 0;
- memzero(encoder->kern.clones, sizeof(uint32_t) * encoder->kern.max_clones);
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_ENCODER || object->index >= 32)
- continue;
- if (!(res.possible_clones & (1 << object->index)))
- continue;
- if (encoder->kern.n_clones >= 32) {
- log_debug("grdrm: %s: possible_encoders exceeds 32bit mask", card->base.name);
- continue;
- }
-
- encoder->kern.clones[encoder->kern.n_clones++] = object->id;
- }
-
- return 0;
-}
-
-/*
- * Crtcs
- */
-
-static void crtc_free(grdrm_object *object) {
- grdrm_crtc *crtc = crtc_from_object(object);
-
- if (crtc->pipe)
- grdev_pipe_free(&crtc->pipe->base);
- free(crtc->set.connectors);
- free(crtc->old.connectors);
- free(crtc->kern.used_connectors);
- free(crtc);
-}
-
-int grdrm_crtc_new(grdrm_crtc **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_crtc *crtc;
- int r;
-
- assert(card);
-
- crtc = new0(grdrm_crtc, 1);
- if (!crtc)
- return -ENOMEM;
-
- object = &crtc->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_CRTC, crtc_free);
-
- crtc->kern.max_used_connectors = 32;
- crtc->kern.used_connectors = new0(uint32_t, crtc->kern.max_used_connectors);
- if (!crtc->kern.used_connectors)
- return -ENOMEM;
-
- crtc->old.connectors = new0(uint32_t, crtc->kern.max_used_connectors);
- if (!crtc->old.connectors)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = crtc;
- object = NULL;
- return 0;
-}
-
-static int grdrm_crtc_resync(grdrm_crtc *crtc) {
- grdrm_card *card = crtc->object.card;
- struct drm_mode_crtc res = { .crtc_id = crtc->object.id };
- int r;
-
- assert(crtc);
-
- /* make sure we can cache any combination later */
- if (card->n_connectors > crtc->kern.max_used_connectors) {
- uint32_t max, *t;
-
- max = ALIGN_POWER2(card->n_connectors);
- if (!max)
- return -ENOMEM;
-
- t = realloc_multiply(crtc->kern.used_connectors, sizeof(*t), max);
- if (!t)
- return -ENOMEM;
-
- crtc->kern.used_connectors = t;
- crtc->kern.max_used_connectors = max;
-
- if (!crtc->old.set) {
- crtc->old.connectors = calloc(sizeof(*t), max);
- if (!crtc->old.connectors)
- return -ENOMEM;
- }
- }
-
- /* GETCRTC doesn't return connectors. We have to read all
- * encoder-state and deduce the setup ourselves.. */
- crtc->kern.n_used_connectors = 0;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETCRTC, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: crtc %u removed during resync",
- card->base.name, crtc->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve crtc %u: %m",
- card->base.name, crtc->object.id);
- }
-
- return r;
- }
-
- crtc->kern.used_fb = res.fb_id;
- crtc->kern.fb_offset_x = res.x;
- crtc->kern.fb_offset_y = res.y;
- crtc->kern.gamma_size = res.gamma_size;
- crtc->kern.mode_set = res.mode_valid;
- crtc->kern.mode = res.mode;
-
- return 0;
-}
-
-static void grdrm_crtc_assign(grdrm_crtc *crtc, grdrm_connector *connector) {
- uint32_t n_connectors;
- int r;
-
- assert(crtc);
- assert(!crtc->object.assigned);
- assert(!connector || !connector->object.assigned);
-
- /* always mark both as assigned; even if assignments cannot be set */
- crtc->object.assigned = true;
- if (connector)
- connector->object.assigned = true;
-
- /* we will support hw clone mode in the future */
- n_connectors = connector ? 1 : 0;
-
- /* bail out if configuration is preserved */
- if (crtc->set.n_connectors == n_connectors &&
- (n_connectors == 0 || crtc->set.connectors[0] == connector->object.id))
- return;
-
- crtc->applied = false;
- crtc->set.n_connectors = 0;
-
- if (n_connectors > crtc->set.max_connectors) {
- uint32_t max, *t;
-
- max = ALIGN_POWER2(n_connectors);
- if (!max) {
- r = -ENOMEM;
- goto error;
- }
-
- t = realloc(crtc->set.connectors, sizeof(*t) * max);
- if (!t) {
- r = -ENOMEM;
- goto error;
- }
-
- crtc->set.connectors = t;
- crtc->set.max_connectors = max;
- }
-
- if (connector) {
- struct drm_mode_modeinfo *m, *pref = NULL;
- uint32_t i;
-
- for (i = 0; i < connector->kern.n_modes; ++i) {
- m = &connector->kern.modes[i];
-
- /* ignore 3D modes by default */
- if (m->flags & DRM_MODE_FLAG_3D_MASK)
- continue;
-
- if (!pref) {
- pref = m;
- continue;
- }
-
- /* use PREFERRED over non-PREFERRED */
- if ((pref->type & DRM_MODE_TYPE_PREFERRED) &&
- !(m->type & DRM_MODE_TYPE_PREFERRED))
- continue;
-
- /* use DRIVER over non-PREFERRED|DRIVER */
- if ((pref->type & DRM_MODE_TYPE_DRIVER) &&
- !(m->type & (DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED)))
- continue;
-
- /* always prefer higher resolution */
- if (pref->hdisplay > m->hdisplay ||
- (pref->hdisplay == m->hdisplay && pref->vdisplay > m->vdisplay))
- continue;
-
- pref = m;
- }
-
- if (pref) {
- crtc->set.mode = *pref;
- crtc->set.n_connectors = 1;
- crtc->set.connectors[0] = connector->object.id;
- log_debug("grdrm: %s: assigned connector %" PRIu32 " to crtc %" PRIu32 " with mode %s",
- crtc->object.card->base.name, connector->object.id, crtc->object.id, pref->name);
- } else {
- log_debug("grdrm: %s: connector %" PRIu32 " to be assigned but has no valid mode",
- crtc->object.card->base.name, connector->object.id);
- }
- }
-
- return;
-
-error:
- log_debug("grdrm: %s: cannot assign crtc %" PRIu32 ": %s",
- crtc->object.card->base.name, crtc->object.id, strerror(-r));
-}
-
-static void grdrm_crtc_expose(grdrm_crtc *crtc) {
- grdrm_pipe *pipe;
- grdrm_fb *fb;
- size_t i;
- int r;
-
- assert(crtc);
- assert(crtc->object.assigned);
-
- if (crtc->set.n_connectors < 1) {
- if (crtc->pipe)
- grdev_pipe_free(&crtc->pipe->base);
- crtc->pipe = NULL;
- return;
- }
-
- pipe = crtc->pipe;
- if (pipe) {
- if (pipe->base.width != crtc->set.mode.hdisplay ||
- pipe->base.height != crtc->set.mode.vdisplay ||
- pipe->base.vrefresh != crtc->set.mode.vrefresh) {
- grdev_pipe_free(&pipe->base);
- crtc->pipe = NULL;
- pipe = NULL;
- }
- }
-
- if (crtc->pipe) {
- pipe->base.front = NULL;
- pipe->base.back = NULL;
- for (i = 0; i < pipe->base.max_fbs; ++i) {
- fb = fb_from_base(pipe->base.fbs[i]);
- if (fb->id == crtc->kern.used_fb)
- pipe->base.front = &fb->base;
- else if (!fb->flipid)
- pipe->base.back = &fb->base;
- }
- } else {
- r = grdrm_pipe_new(&pipe, crtc, &crtc->set.mode, 2);
- if (r < 0) {
- log_debug("grdrm: %s: cannot create pipe for crtc %" PRIu32 ": %s",
- crtc->object.card->base.name, crtc->object.id, strerror(-r));
- return;
- }
-
- for (i = 0; i < pipe->base.max_fbs; ++i) {
- r = grdrm_fb_new(&fb, crtc->object.card, &crtc->set.mode);
- if (r < 0) {
- log_debug("grdrm: %s: cannot allocate framebuffer for crtc %" PRIu32 ": %s",
- crtc->object.card->base.name, crtc->object.id, strerror(-r));
- grdev_pipe_free(&pipe->base);
- return;
- }
-
- pipe->base.fbs[i] = &fb->base;
- }
-
- pipe->base.front = NULL;
- pipe->base.back = pipe->base.fbs[0];
- crtc->pipe = pipe;
- }
-
- grdev_pipe_ready(&crtc->pipe->base, true);
-}
-
-static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb *basefb) {
- struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- grdrm_pipe *pipe = crtc->pipe;
- grdrm_fb *fb;
- int r;
-
- assert(crtc);
- assert(basefb);
- assert(pipe);
-
- fb = fb_from_base(basefb);
-
- set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->set.connectors);
- set_crtc.count_connectors = crtc->set.n_connectors;
- set_crtc.fb_id = fb->id;
- set_crtc.x = 0;
- set_crtc.y = 0;
- set_crtc.mode_valid = 1;
- set_crtc.mode = crtc->set.mode;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot set crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- grdrm_card_async(card, r);
- return;
- }
-
- if (!crtc->applied) {
- log_debug("grdrm: %s: crtc %" PRIu32 " applied via deep modeset",
- card->base.name, crtc->object.id);
- crtc->applied = true;
- }
-
- pipe->base.back = NULL;
- pipe->base.front = &fb->base;
- fb->flipid = 0;
- ++pipe->counter;
- pipe->base.flipping = false;
- pipe->base.flip = false;
-
- /* We cannot schedule dummy page-flips on pipes, hence, the
- * application would have to schedule their own frame-timers.
- * To avoid duplicating that everywhere, we schedule our own
- * timer and raise a fake FRAME event when it fires. */
- grdev_pipe_schedule(&pipe->base, 1);
-}
-
-static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb *basefb) {
- struct drm_mode_crtc_page_flip page_flip = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- grdrm_pipe *pipe = crtc->pipe;
- grdrm_fb *fb;
- uint32_t cnt;
- int r;
-
- assert(crtc);
- assert(basefb);
- assert(pipe);
-
- if (!crtc->applied) {
- if (!grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode))
- return 0;
-
- /* TODO: Theoretically, we should be able to page-flip to our
- * framebuffer here. We didn't perform any deep modeset, but the
- * DRM driver is really supposed to reject our page-flip in case
- * the FB is not compatible. We then properly fall back to a
- * deep modeset.
- * As it turns out, drivers don't to this. Therefore, we need to
- * perform a full modeset on enter now. We might avoid this in
- * the future with fixed drivers.. */
-
- return 0;
- }
-
- fb = fb_from_base(basefb);
-
- cnt = ++pipe->counter ? : ++pipe->counter;
- page_flip.fb_id = fb->id;
- page_flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
- page_flip.user_data = grdrm_encode_vblank_data(crtc->object.id, cnt);
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_PAGE_FLIP, &page_flip);
- if (r < 0) {
- r = -errno;
- /* Avoid excessive logging on EINVAL; it is currently not
- * possible to see whether cards support page-flipping, so
- * avoid logging on each frame. */
- if (r != -EINVAL)
- log_debug_errno(errno, "grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- if (grdrm_card_async(card, r))
- return r;
-
- return 0;
- }
-
- if (!crtc->applied) {
- log_debug("grdrm: %s: crtc %" PRIu32 " applied via page flip",
- card->base.name, crtc->object.id);
- crtc->applied = true;
- }
-
- pipe->base.flipping = true;
- pipe->base.flip = false;
- pipe->counter = cnt;
- fb->flipid = cnt;
- pipe->base.back = NULL;
-
- /* Raise fake FRAME event if it takes longer than 2
- * frames to receive the pageflip event. We assume the
- * queue ran over or some other error happened. */
- grdev_pipe_schedule(&pipe->base, 2);
-
- return 1;
-}
-
-static void grdrm_crtc_commit(grdrm_crtc *crtc) {
- struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- grdrm_pipe *pipe;
- grdev_fb *fb;
- int r;
-
- assert(crtc);
- assert(crtc->object.assigned);
-
- pipe = crtc->pipe;
- if (!pipe) {
- /* If a crtc is not assigned any connector, we want any
- * previous setup to be cleared, so make sure the CRTC is
- * disabled. Otherwise, there might be content on the CRTC
- * while we run, which is not what we want.
- * If you want to avoid modesets on specific CRTCs, you should
- * still keep their assignment, but never enable the resulting
- * pipe. This way, we wouldn't touch it at all. */
- if (!crtc->applied) {
- crtc->applied = true;
- r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot shutdown crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- grdrm_card_async(card, r);
- return;
- }
-
- log_debug("grdrm: %s: crtc %" PRIu32 " applied via shutdown",
- card->base.name, crtc->object.id);
- }
-
- return;
- }
-
- /* we always fully ignore disabled pipes */
- if (!pipe->base.enabled)
- return;
-
- assert(crtc->set.n_connectors > 0);
-
- if (pipe->base.flip)
- fb = pipe->base.back;
- else if (!crtc->applied)
- fb = pipe->base.front;
- else
- return;
-
- if (!fb)
- return;
-
- r = grdrm_crtc_commit_flip(crtc, fb);
- if (r == 0) {
- /* in case we couldn't page-flip, perform deep modeset */
- grdrm_crtc_commit_deep(crtc, fb);
- }
-}
-
-static void grdrm_crtc_restore(grdrm_crtc *crtc) {
- struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- int r;
-
- if (!crtc->old.set)
- return;
-
- set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->old.connectors);
- set_crtc.count_connectors = crtc->old.n_connectors;
- set_crtc.fb_id = crtc->old.fb;
- set_crtc.x = crtc->old.fb_x;
- set_crtc.y = crtc->old.fb_y;
- set_crtc.gamma_size = crtc->old.gamma;
- set_crtc.mode_valid = crtc->old.mode_set;
- set_crtc.mode = crtc->old.mode;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot restore crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- grdrm_card_async(card, r);
- return;
- }
-
- if (crtc->pipe) {
- ++crtc->pipe->counter;
- crtc->pipe->base.front = NULL;
- crtc->pipe->base.flipping = false;
- }
-
- log_debug("grdrm: %s: crtc %" PRIu32 " restored", card->base.name, crtc->object.id);
-}
-
-static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct drm_event_vblank *event) {
- bool flipped = false;
- grdrm_pipe *pipe;
- size_t i;
-
- assert(crtc);
- assert(event);
-
- pipe = crtc->pipe;
- if (!pipe)
- return;
-
- /* We got a page-flip event. To be safe, we reset all FBs on the same
- * pipe that have smaller flipids than the flip we got as we know they
- * are executed in order. We need to do this to guarantee
- * queue-overflows or other missed events don't cause starvation.
- * Furthermore, if we find the exact FB this event is for, *and* this
- * is the most recent event, we mark it as front FB and raise a
- * frame event. */
-
- for (i = 0; i < pipe->base.max_fbs; ++i) {
- grdrm_fb *fb;
-
- if (!pipe->base.fbs[i])
- continue;
-
- fb = fb_from_base(pipe->base.fbs[i]);
- if (counter != 0 && counter == pipe->counter && fb->flipid == counter) {
- pipe->base.front = &fb->base;
- fb->flipid = 0;
- flipped = true;
- } else if (counter - fb->flipid < UINT16_MAX) {
- fb->flipid = 0;
- }
- }
-
- if (flipped) {
- crtc->pipe->base.flipping = false;
- grdev_pipe_frame(&pipe->base);
- }
-}
-
-/*
- * Framebuffers
- */
-
-static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_modeinfo *mode) {
- _cleanup_(grdrm_fb_freep) grdrm_fb *fb = NULL;
- struct drm_mode_create_dumb create_dumb = { };
- struct drm_mode_map_dumb map_dumb = { };
- struct drm_mode_fb_cmd2 add_fb = { };
- unsigned int i;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(card, -EINVAL);
-
- fb = new0(grdrm_fb, 1);
- if (!fb)
- return -ENOMEM;
-
- /* TODO: we should choose a compatible format of the previous CRTC
- * setting to allow page-flip to it. Only choose fallback if the
- * previous setting was crap (non xrgb32'ish). */
-
- fb->card = card;
- fb->base.format = DRM_FORMAT_XRGB8888;
- fb->base.width = mode->hdisplay;
- fb->base.height = mode->vdisplay;
-
- for (i = 0; i < ELEMENTSOF(fb->base.maps); ++i)
- fb->base.maps[i] = MAP_FAILED;
-
- create_dumb.width = fb->base.width;
- create_dumb.height = fb->base.height;
- create_dumb.bpp = 32;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
- if (r < 0) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot create dumb buffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- fb->handles[0] = create_dumb.handle;
- fb->base.strides[0] = create_dumb.pitch;
- fb->sizes[0] = create_dumb.size;
-
- map_dumb.handle = fb->handles[0];
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
- if (r < 0) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot map dumb buffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- fb->base.maps[0] = mmap(0, fb->sizes[0], PROT_WRITE, MAP_SHARED, card->fd, map_dumb.offset);
- if (fb->base.maps[0] == MAP_FAILED) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot memory-map dumb buffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- memzero(fb->base.maps[0], fb->sizes[0]);
-
- add_fb.width = fb->base.width;
- add_fb.height = fb->base.height;
- add_fb.pixel_format = fb->base.format;
- add_fb.flags = 0;
- memcpy(add_fb.handles, fb->handles, sizeof(fb->handles));
- memcpy(add_fb.pitches, fb->base.strides, sizeof(fb->base.strides));
- memcpy(add_fb.offsets, fb->offsets, sizeof(fb->offsets));
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_ADDFB2, &add_fb);
- if (r < 0) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot add framebuffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- fb->id = add_fb.fb_id;
-
- *out = fb;
- fb = NULL;
- return 0;
-}
-
-grdrm_fb *grdrm_fb_free(grdrm_fb *fb) {
- unsigned int i;
- int r;
-
- if (!fb)
- return NULL;
-
- assert(fb->card);
-
- if (fb->base.free_fn)
- fb->base.free_fn(fb->base.data.ptr);
-
- if (fb->id > 0 && fb->card->fd >= 0) {
- r = ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id);
- if (r < 0)
- log_debug_errno(errno, "grdrm: %s: cannot delete framebuffer %" PRIu32 ": %m",
- fb->card->base.name, fb->id);
- }
-
- for (i = 0; i < ELEMENTSOF(fb->handles); ++i) {
- struct drm_mode_destroy_dumb destroy_dumb = { };
-
- if (fb->base.maps[i] != MAP_FAILED)
- munmap(fb->base.maps[i], fb->sizes[i]);
-
- if (fb->handles[i] > 0 && fb->card->fd >= 0) {
- destroy_dumb.handle = fb->handles[i];
- r = ioctl(fb->card->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
- if (r < 0)
- log_debug_errno(errno, "grdrm: %s: cannot destroy dumb-buffer %" PRIu32 ": %m",
- fb->card->base.name, fb->handles[i]);
- }
- }
-
- free(fb);
-
- return NULL;
-}
-
-/*
- * Pipes
- */
-
-static void grdrm_pipe_name(char *out, grdrm_crtc *crtc) {
- /* @out must be at least of size GRDRM_PIPE_NAME_MAX */
- sprintf(out, "%s/%" PRIu32, crtc->object.card->base.name, crtc->object.id);
-}
-
-static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_modeinfo *mode, size_t n_fbs) {
- _cleanup_(grdev_pipe_freep) grdev_pipe *basepipe = NULL;
- grdrm_card *card = crtc->object.card;
- char name[GRDRM_PIPE_NAME_MAX];
- grdrm_pipe *pipe;
- int r;
-
- assert_return(crtc, -EINVAL);
- assert_return(grdev_is_drm_card(&card->base), -EINVAL);
-
- pipe = new0(grdrm_pipe, 1);
- if (!pipe)
- return -ENOMEM;
-
- basepipe = &pipe->base;
- pipe->base = GRDEV_PIPE_INIT(&grdrm_pipe_vtable, &card->base);
- pipe->crtc = crtc;
- pipe->base.width = mode->hdisplay;
- pipe->base.height = mode->vdisplay;
- pipe->base.vrefresh = mode->vrefresh ? : 25;
-
- grdrm_pipe_name(name, crtc);
- r = grdev_pipe_add(&pipe->base, name, n_fbs);
- if (r < 0)
- return r;
-
- if (out)
- *out = pipe;
- basepipe = NULL;
- return 0;
-}
-
-static void grdrm_pipe_free(grdev_pipe *basepipe) {
- grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
- size_t i;
-
- assert(pipe->crtc);
-
- for (i = 0; i < pipe->base.max_fbs; ++i)
- if (pipe->base.fbs[i])
- grdrm_fb_free(fb_from_base(pipe->base.fbs[i]));
-
- free(pipe);
-}
-
-static grdev_fb *grdrm_pipe_target(grdev_pipe *basepipe) {
- grdrm_fb *fb;
- size_t i;
-
- if (!basepipe->back) {
- for (i = 0; i < basepipe->max_fbs; ++i) {
- if (!basepipe->fbs[i])
- continue;
-
- fb = fb_from_base(basepipe->fbs[i]);
- if (&fb->base == basepipe->front)
- continue;
- if (basepipe->flipping && fb->flipid)
- continue;
-
- basepipe->back = &fb->base;
- break;
- }
- }
-
- return basepipe->back;
-}
-
-static void grdrm_pipe_enable(grdev_pipe *basepipe) {
- grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
-
- pipe->crtc->applied = false;
-}
-
-static void grdrm_pipe_disable(grdev_pipe *basepipe) {
- grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
-
- pipe->crtc->applied = false;
-}
-
-static const grdev_pipe_vtable grdrm_pipe_vtable = {
- .free = grdrm_pipe_free,
- .target = grdrm_pipe_target,
- .enable = grdrm_pipe_enable,
- .disable = grdrm_pipe_disable,
-};
-
-/*
- * Cards
- */
-
-static void grdrm_name(char *out, dev_t devnum) {
- /* @out must be at least of size GRDRM_CARD_NAME_MAX */
- sprintf(out, "drm/%u:%u", major(devnum), minor(devnum));
-}
-
-static void grdrm_card_print(grdrm_card *card) {
- grdrm_object *object;
- grdrm_crtc *crtc;
- grdrm_encoder *encoder;
- grdrm_connector *connector;
- grdrm_plane *plane;
- Iterator iter;
- uint32_t i;
- char *p, *buf;
-
- log_debug("grdrm: %s: state dump", card->base.name);
-
- log_debug(" crtcs:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
-
- if (crtc->kern.mode_set)
- log_debug(" mode: %dx%d", crtc->kern.mode.hdisplay, crtc->kern.mode.vdisplay);
- else
- log_debug(" mode: <none>");
- }
-
- log_debug(" encoders:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_ENCODER)
- continue;
-
- encoder = encoder_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
-
- if (encoder->kern.used_crtc)
- log_debug(" crtc: %u", encoder->kern.used_crtc);
- else
- log_debug(" crtc: <none>");
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * encoder->kern.n_crtcs + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < encoder->kern.n_crtcs; ++i)
- p += sprintf(p, " %" PRIu32, encoder->kern.crtcs[i]);
-
- log_debug(" possible crtcs:%s", buf);
- free(buf);
- }
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * encoder->kern.n_clones + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < encoder->kern.n_clones; ++i)
- p += sprintf(p, " %" PRIu32, encoder->kern.clones[i]);
-
- log_debug(" possible clones:%s", buf);
- free(buf);
- }
- }
-
- log_debug(" connectors:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- connector = connector_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
- log_debug(" type: %" PRIu32 "-%" PRIu32 " connection: %" PRIu32 " subpixel: %" PRIu32 " extents: %" PRIu32 "x%" PRIu32,
- connector->kern.type, connector->kern.type_id, connector->kern.connection, connector->kern.subpixel,
- connector->kern.mm_width, connector->kern.mm_height);
-
- if (connector->kern.used_encoder)
- log_debug(" encoder: %" PRIu32, connector->kern.used_encoder);
- else
- log_debug(" encoder: <none>");
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * connector->kern.n_encoders + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < connector->kern.n_encoders; ++i)
- p += sprintf(p, " %" PRIu32, connector->kern.encoders[i]);
-
- log_debug(" possible encoders:%s", buf);
- free(buf);
- }
-
- for (i = 0; i < connector->kern.n_modes; ++i) {
- struct drm_mode_modeinfo *mode = &connector->kern.modes[i];
- log_debug(" mode: %" PRIu32 "x%" PRIu32, mode->hdisplay, mode->vdisplay);
- }
- }
-
- log_debug(" planes:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_PLANE)
- continue;
-
- plane = plane_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
- log_debug(" gamma-size: %" PRIu32, plane->kern.gamma_size);
-
- if (plane->kern.used_crtc)
- log_debug(" crtc: %" PRIu32, plane->kern.used_crtc);
- else
- log_debug(" crtc: <none>");
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * plane->kern.n_crtcs + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < plane->kern.n_crtcs; ++i)
- p += sprintf(p, " %" PRIu32, plane->kern.crtcs[i]);
-
- log_debug(" possible crtcs:%s", buf);
- free(buf);
- }
-
- buf = malloc((DECIMAL_STR_MAX(unsigned int) + 3) * plane->kern.n_formats + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < plane->kern.n_formats; ++i)
- p += sprintf(p, " 0x%x", (unsigned int)plane->kern.formats[i]);
-
- log_debug(" possible formats:%s", buf);
- free(buf);
- }
- }
-}
-
-static int grdrm_card_resync(grdrm_card *card) {
- _cleanup_free_ uint32_t *crtc_ids = NULL, *encoder_ids = NULL, *connector_ids = NULL, *plane_ids = NULL;
- uint32_t allocated = 0;
- grdrm_object *object;
- Iterator iter;
- size_t tries;
- int r;
-
- assert(card);
-
- card->async_hotplug = false;
- allocated = 0;
-
- /* mark existing objects for possible removal */
- HASHMAP_FOREACH(object, card->object_map, iter)
- object->present = false;
-
- for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
- struct drm_mode_get_plane_res pres;
- struct drm_mode_card_res res;
- uint32_t i, max;
-
- if (allocated < card->max_ids) {
- free(crtc_ids);
- free(encoder_ids);
- free(connector_ids);
- free(plane_ids);
- crtc_ids = new0(uint32_t, card->max_ids);
- encoder_ids = new0(uint32_t, card->max_ids);
- connector_ids = new0(uint32_t, card->max_ids);
- plane_ids = new0(uint32_t, card->max_ids);
-
- if (!crtc_ids || !encoder_ids || !connector_ids || !plane_ids)
- return -ENOMEM;
-
- allocated = card->max_ids;
- }
-
- zero(res);
- res.crtc_id_ptr = PTR_TO_UINT64(crtc_ids);
- res.connector_id_ptr = PTR_TO_UINT64(connector_ids);
- res.encoder_id_ptr = PTR_TO_UINT64(encoder_ids);
- res.count_crtcs = allocated;
- res.count_encoders = allocated;
- res.count_connectors = allocated;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot retrieve drm resources: %m",
- card->base.name);
- return r;
- }
-
- zero(pres);
- pres.plane_id_ptr = PTR_TO_UINT64(plane_ids);
- pres.count_planes = allocated;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &pres);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot retrieve drm plane-resources: %m",
- card->base.name);
- return r;
- }
-
- max = MAX(MAX(res.count_crtcs, res.count_encoders),
- MAX(res.count_connectors, pres.count_planes));
- if (max > allocated) {
- uint32_t n;
-
- n = ALIGN_POWER2(max);
- if (!n || n > UINT16_MAX) {
- log_debug("grdrm: %s: excessive DRM resource limit: %" PRIu32,
- card->base.name, max);
- return -ERANGE;
- }
-
- /* retry with resized buffers */
- card->max_ids = n;
- continue;
- }
-
- /* mark available objects as present */
-
- for (i = 0; i < res.count_crtcs; ++i) {
- object = grdrm_find_object(card, crtc_ids[i]);
- if (object && object->type == GRDRM_TYPE_CRTC) {
- object->present = true;
- object->index = i;
- crtc_ids[i] = 0;
- }
- }
-
- for (i = 0; i < res.count_encoders; ++i) {
- object = grdrm_find_object(card, encoder_ids[i]);
- if (object && object->type == GRDRM_TYPE_ENCODER) {
- object->present = true;
- object->index = i;
- encoder_ids[i] = 0;
- }
- }
-
- for (i = 0; i < res.count_connectors; ++i) {
- object = grdrm_find_object(card, connector_ids[i]);
- if (object && object->type == GRDRM_TYPE_CONNECTOR) {
- object->present = true;
- object->index = i;
- connector_ids[i] = 0;
- }
- }
-
- for (i = 0; i < pres.count_planes; ++i) {
- object = grdrm_find_object(card, plane_ids[i]);
- if (object && object->type == GRDRM_TYPE_PLANE) {
- object->present = true;
- object->index = i;
- plane_ids[i] = 0;
- }
- }
-
- /* drop removed objects */
-
- HASHMAP_FOREACH(object, card->object_map, iter)
- if (!object->present)
- grdrm_object_free(object);
-
- /* add new objects */
-
- card->n_crtcs = res.count_crtcs;
- for (i = 0; i < res.count_crtcs; ++i) {
- if (crtc_ids[i] < 1)
- continue;
-
- r = grdrm_crtc_new(NULL, card, crtc_ids[i], i);
- if (r < 0)
- return r;
- }
-
- card->n_encoders = res.count_encoders;
- for (i = 0; i < res.count_encoders; ++i) {
- if (encoder_ids[i] < 1)
- continue;
-
- r = grdrm_encoder_new(NULL, card, encoder_ids[i], i);
- if (r < 0)
- return r;
- }
-
- card->n_connectors = res.count_connectors;
- for (i = 0; i < res.count_connectors; ++i) {
- if (connector_ids[i] < 1)
- continue;
-
- r = grdrm_connector_new(NULL, card, connector_ids[i], i);
- if (r < 0)
- return r;
- }
-
- card->n_planes = pres.count_planes;
- for (i = 0; i < pres.count_planes; ++i) {
- if (plane_ids[i] < 1)
- continue;
-
- r = grdrm_plane_new(NULL, card, plane_ids[i], i);
- if (r < 0)
- return r;
- }
-
- /* re-sync objects after object_map is synced */
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- switch (object->type) {
- case GRDRM_TYPE_CRTC:
- r = grdrm_crtc_resync(crtc_from_object(object));
- break;
- case GRDRM_TYPE_ENCODER:
- r = grdrm_encoder_resync(encoder_from_object(object));
- break;
- case GRDRM_TYPE_CONNECTOR:
- r = grdrm_connector_resync(connector_from_object(object));
- break;
- case GRDRM_TYPE_PLANE:
- r = grdrm_plane_resync(plane_from_object(object));
- break;
- default:
- assert_not_reached("grdrm: invalid object type");
- r = 0;
- }
-
- if (r < 0)
- return r;
-
- if (card->async_hotplug)
- break;
- }
-
- /* if modeset objects change during sync, start over */
- if (card->async_hotplug) {
- card->async_hotplug = false;
- continue;
- }
-
- /* cache crtc/connector relationship */
- HASHMAP_FOREACH(object, card->object_map, iter) {
- grdrm_connector *connector;
- grdrm_encoder *encoder;
- grdrm_crtc *crtc;
-
- if (object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- connector = connector_from_object(object);
- if (connector->kern.connection != 1 || connector->kern.used_encoder < 1)
- continue;
-
- object = grdrm_find_object(card, connector->kern.used_encoder);
- if (!object || object->type != GRDRM_TYPE_ENCODER)
- continue;
-
- encoder = encoder_from_object(object);
- if (encoder->kern.used_crtc < 1)
- continue;
-
- object = grdrm_find_object(card, encoder->kern.used_crtc);
- if (!object || object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
- assert(crtc->kern.n_used_connectors < crtc->kern.max_used_connectors);
- crtc->kern.used_connectors[crtc->kern.n_used_connectors++] = connector->object.id;
- }
-
- /* cache old crtc settings for later restore */
- HASHMAP_FOREACH(object, card->object_map, iter) {
- grdrm_crtc *crtc;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
-
- /* Save data if it is the first time we refresh the CRTC. This data can
- * be used optionally to restore any previous configuration. For
- * instance, it allows us to restore VT configurations after we close
- * our session again. */
- if (!crtc->old.set) {
- crtc->old.fb = crtc->kern.used_fb;
- crtc->old.fb_x = crtc->kern.fb_offset_x;
- crtc->old.fb_y = crtc->kern.fb_offset_y;
- crtc->old.gamma = crtc->kern.gamma_size;
- crtc->old.n_connectors = crtc->kern.n_used_connectors;
- if (crtc->old.n_connectors)
- memcpy(crtc->old.connectors, crtc->kern.used_connectors, sizeof(uint32_t) * crtc->old.n_connectors);
- crtc->old.mode_set = crtc->kern.mode_set;
- crtc->old.mode = crtc->kern.mode;
- crtc->old.set = true;
- }
- }
-
- /* everything synced */
- break;
- }
-
- if (tries >= GRDRM_MAX_TRIES) {
- /*
- * Ugh! We were unable to sync the DRM card state due to heavy
- * hotplugging. This should never happen, so print a debug
- * message and bail out. The next uevent will trigger
- * this again.
- */
-
- log_debug("grdrm: %s: hotplug-storm when syncing card", card->base.name);
- return -EFAULT;
- }
-
- return 0;
-}
-
-static bool card_configure_crtc(grdrm_crtc *crtc, grdrm_connector *connector) {
- grdrm_card *card = crtc->object.card;
- grdrm_encoder *encoder;
- grdrm_object *object;
- uint32_t i, j;
-
- if (crtc->object.assigned || connector->object.assigned)
- return false;
- if (connector->kern.connection != 1)
- return false;
-
- for (i = 0; i < connector->kern.n_encoders; ++i) {
- object = grdrm_find_object(card, connector->kern.encoders[i]);
- if (!object || object->type != GRDRM_TYPE_ENCODER)
- continue;
-
- encoder = encoder_from_object(object);
- for (j = 0; j < encoder->kern.n_crtcs; ++j) {
- if (encoder->kern.crtcs[j] == crtc->object.id) {
- grdrm_crtc_assign(crtc, connector);
- return true;
- }
- }
- }
-
- return false;
-}
-
-static void grdrm_card_configure(grdrm_card *card) {
- /*
- * Modeset Configuration
- * This is where we update our modeset configuration and assign
- * connectors to CRTCs. This means, each connector that we want to
- * enable needs a CRTC, disabled (or unavailable) connectors are left
- * alone in the dark. Once all CRTCs are assigned, the remaining CRTCs
- * are disabled.
- * Sounds trivial, but there're several caveats:
- *
- * * Multiple connectors can be driven by the same CRTC. This is
- * known as 'hardware clone mode'. Advantage over software clone
- * mode is that only a single CRTC is needed to drive multiple
- * displays. However, few hardware supports this and it's a huge
- * headache to configure on dynamic demands. Therefore, we only
- * support it if configured statically beforehand.
- *
- * * CRTCs are not created equal. Some might be much more powerful
- * than others, including more advanced plane support. So far, our
- * CRTC selection is random. You need to supply static
- * configuration if you want special setups. So far, there is no
- * proper way to do advanced CRTC selection on dynamic demands. It
- * is not really clear which demands require what CRTC, so, like
- * everyone else, we do random CRTC selection unless explicitly
- * states otherwise.
- *
- * * Each Connector has a list of possible encoders that can drive
- * it, and each encoder has a list of possible CRTCs. If this graph
- * is a tree, assignment is trivial. However, if not, we cannot
- * reliably decide on configurations beforehand. The encoder is
- * always selected by the kernel, so we have to actually set a mode
- * to know which encoder is used. There is no way to ask the kernel
- * whether a given configuration is possible. This will change with
- * atomic-modesetting, but until then, we keep our configurations
- * simple and assume they work all just fine. If one fails
- * unexpectedly, we print a warning and disable it.
- *
- * Configuring a card consists of several steps:
- *
- * 1) First of all, we apply any user-configuration. If a user wants
- * a fixed configuration, we apply it and preserve it.
- * So far, we don't support user configuration files, so this step
- * is skipped.
- *
- * 2) Secondly, we need to apply any quirks from hwdb. Some hardware
- * might only support limited configurations or require special
- * CRTC/Connector mappings. We read this from hwdb and apply it, if
- * present.
- * So far, we don't support this as there is no known quirk, so
- * this step is skipped.
- *
- * 3) As deep modesets are expensive, we try to avoid them if
- * possible. Therefore, we read the current configuration from the
- * kernel and try to preserve it, if compatible with our demands.
- * If not, we break it and reassign it in a following step.
- *
- * 4) The main step involves configuring all remaining objects. By
- * default, all available connectors are enabled, except for those
- * disabled by user-configuration. We lookup a suitable CRTC for
- * each connector and assign them. As there might be more
- * connectors than CRTCs, we apply some ordering so users can
- * select which connectors are more important right now.
- * So far, we only apply the default ordering, more might be added
- * in the future.
- */
-
- grdrm_object *object;
- grdrm_crtc *crtc;
- Iterator i, j;
-
- /* clear assignments */
- HASHMAP_FOREACH(object, card->object_map, i)
- object->assigned = false;
-
- /* preserve existing configurations */
- HASHMAP_FOREACH(object, card->object_map, i) {
- if (object->type != GRDRM_TYPE_CRTC || object->assigned)
- continue;
-
- crtc = crtc_from_object(object);
-
- if (crtc->applied) {
- /* If our mode is set, preserve it. If no connector is
- * set, modeset either failed or the pipe is unused. In
- * both cases, leave it alone. It might be tried again
- * below in case there're remaining connectors.
- * Otherwise, try restoring the assignments. If they
- * are no longer valid, leave the pipe untouched. */
-
- if (crtc->set.n_connectors < 1)
- continue;
-
- assert(crtc->set.n_connectors == 1);
-
- object = grdrm_find_object(card, crtc->set.connectors[0]);
- if (!object || object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- card_configure_crtc(crtc, connector_from_object(object));
- } else if (crtc->kern.mode_set && crtc->kern.n_used_connectors != 1) {
- /* If our mode is not set on the pipe, we know the kern
- * information is valid. Try keeping it. If it's not
- * possible, leave the pipe untouched for later
- * assignements. */
-
- object = grdrm_find_object(card, crtc->kern.used_connectors[0]);
- if (!object || object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- card_configure_crtc(crtc, connector_from_object(object));
- }
- }
-
- /* assign remaining objects */
- HASHMAP_FOREACH(object, card->object_map, i) {
- if (object->type != GRDRM_TYPE_CRTC || object->assigned)
- continue;
-
- crtc = crtc_from_object(object);
-
- HASHMAP_FOREACH(object, card->object_map, j) {
- if (object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- if (card_configure_crtc(crtc, connector_from_object(object)))
- break;
- }
-
- if (!crtc->object.assigned)
- grdrm_crtc_assign(crtc, NULL);
- }
-
- /* expose configuration */
- HASHMAP_FOREACH(object, card->object_map, i) {
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- grdrm_crtc_expose(crtc_from_object(object));
- }
-}
-
-static void grdrm_card_hotplug(grdrm_card *card) {
- int r;
-
- assert(card);
-
- if (!card->running)
- return;
-
- log_debug("grdrm: %s/%s: reconfigure card", card->base.session->name, card->base.name);
-
- card->ready = false;
- r = grdrm_card_resync(card);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot re-sync card: %m",
- card->base.session->name, card->base.name);
- return;
- }
-
- grdev_session_pin(card->base.session);
-
- /* debug statement to print card information */
- if (0)
- grdrm_card_print(card);
-
- grdrm_card_configure(card);
- card->ready = true;
- card->hotplug = false;
-
- grdev_session_unpin(card->base.session);
-}
-
-static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- grdrm_card *card = userdata;
- struct drm_event_vblank *vblank;
- struct drm_event *event;
- uint32_t id, counter;
- grdrm_object *object;
- char buf[4096];
- size_t len;
- ssize_t l;
-
- if (revents & (EPOLLHUP | EPOLLERR)) {
- /* Immediately close device on HUP; no need to flush pending
- * data.. there're no events we care about here. */
- log_debug("grdrm: %s/%s: HUP", card->base.session->name, card->base.name);
- grdrm_card_close(card);
- return 0;
- }
-
- if (revents & (EPOLLIN)) {
- l = read(card->fd, buf, sizeof(buf));
- if (l < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- log_debug_errno(errno, "grdrm: %s/%s: read error: %m",
- card->base.session->name, card->base.name);
- grdrm_card_close(card);
- return 0;
- }
-
- for (len = l; len > 0; len -= event->length) {
- event = (void*)buf;
-
- if (len < sizeof(*event) || len < event->length) {
- log_debug("grdrm: %s/%s: truncated event",
- card->base.session->name, card->base.name);
- break;
- }
-
- switch (event->type) {
- case DRM_EVENT_FLIP_COMPLETE:
- vblank = (void*)event;
- if (event->length < sizeof(*vblank)) {
- log_debug("grdrm: %s/%s: truncated vblank event",
- card->base.session->name, card->base.name);
- break;
- }
-
- grdrm_decode_vblank_data(vblank->user_data, &id, &counter);
- object = grdrm_find_object(card, id);
- if (!object || object->type != GRDRM_TYPE_CRTC)
- break;
-
- grdrm_crtc_flip_complete(crtc_from_object(object), counter, vblank);
- break;
- }
- }
- }
-
- return 0;
-}
-
-static int grdrm_card_add(grdrm_card *card, const char *name) {
- assert(card);
- assert(card->fd < 0);
-
- card->object_map = hashmap_new(&trivial_hash_ops);
- if (!card->object_map)
- return -ENOMEM;
-
- return grdev_card_add(&card->base, name);
-}
-
-static void grdrm_card_destroy(grdrm_card *card) {
- assert(card);
- assert(!card->running);
- assert(card->fd < 0);
- assert(hashmap_size(card->object_map) == 0);
-
- hashmap_free(card->object_map);
-}
-
-static void grdrm_card_commit(grdev_card *basecard) {
- grdrm_card *card = grdrm_card_from_base(basecard);
- grdrm_object *object;
- Iterator iter;
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (!card->ready)
- break;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- grdrm_crtc_commit(crtc_from_object(object));
- }
-}
-
-static void grdrm_card_restore(grdev_card *basecard) {
- grdrm_card *card = grdrm_card_from_base(basecard);
- grdrm_object *object;
- Iterator iter;
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (!card->ready)
- break;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- grdrm_crtc_restore(crtc_from_object(object));
- }
-}
-
-static void grdrm_card_enable(grdrm_card *card) {
- assert(card);
-
- if (card->fd < 0 || card->running)
- return;
-
- /* ignore cards without DUMB_BUFFER capability */
- if (!card->cap_dumb)
- return;
-
- assert(card->fd_src);
-
- log_debug("grdrm: %s/%s: enable", card->base.session->name, card->base.name);
-
- card->running = true;
- sd_event_source_set_enabled(card->fd_src, SD_EVENT_ON);
- grdrm_card_hotplug(card);
-}
-
-static void grdrm_card_disable(grdrm_card *card) {
- grdrm_object *object;
- Iterator iter;
-
- assert(card);
-
- if (card->fd < 0 || !card->running)
- return;
-
- assert(card->fd_src);
-
- log_debug("grdrm: %s/%s: disable", card->base.session->name, card->base.name);
-
- card->running = false;
- card->ready = false;
- sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
-
- /* stop all pipes */
- HASHMAP_FOREACH(object, card->object_map, iter) {
- grdrm_crtc *crtc;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
- crtc->applied = false;
- if (crtc->pipe)
- grdev_pipe_ready(&crtc->pipe->base, false);
- }
-}
-
-static int grdrm_card_open(grdrm_card *card, int dev_fd) {
- _cleanup_(grdev_session_unpinp) grdev_session *pin = NULL;
- _cleanup_close_ int fd = dev_fd;
- struct drm_get_cap cap;
- int r, flags;
-
- assert(card);
- assert(dev_fd >= 0);
- assert(card->fd != dev_fd);
-
- pin = grdev_session_pin(card->base.session);
- grdrm_card_close(card);
-
- log_debug("grdrm: %s/%s: open", card->base.session->name, card->base.name);
-
- r = fd_nonblock(fd, true);
- if (r < 0)
- return r;
-
- r = fd_cloexec(fd, true);
- if (r < 0)
- return r;
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0)
- return -errno;
- if ((flags & O_ACCMODE) != O_RDWR)
- return -EACCES;
-
- r = sd_event_add_io(card->base.session->context->event,
- &card->fd_src,
- fd,
- EPOLLHUP | EPOLLERR | EPOLLIN,
- grdrm_card_io_fn,
- card);
- if (r < 0)
- return r;
-
- sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
-
- card->hotplug = true;
- card->fd = fd;
- fd = -1;
-
- /* cache DUMB_BUFFER capability */
- cap.capability = DRM_CAP_DUMB_BUFFER;
- cap.value = 0;
- r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap);
- card->cap_dumb = r >= 0 && cap.value;
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot retrieve DUMB_BUFFER capability: %m",
- card->base.session->name, card->base.name);
- else if (!card->cap_dumb)
- log_debug("grdrm: %s/%s: DUMB_BUFFER capability not supported",
- card->base.session->name, card->base.name);
-
- /* cache TIMESTAMP_MONOTONIC capability */
- cap.capability = DRM_CAP_TIMESTAMP_MONOTONIC;
- cap.value = 0;
- r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap);
- card->cap_monotonic = r >= 0 && cap.value;
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot retrieve TIMESTAMP_MONOTONIC capability: %m",
- card->base.session->name, card->base.name);
- else if (!card->cap_monotonic)
- log_debug("grdrm: %s/%s: TIMESTAMP_MONOTONIC is disabled globally, fix this NOW!",
- card->base.session->name, card->base.name);
-
- return 0;
-}
-
-static void grdrm_card_close(grdrm_card *card) {
- grdrm_object *object;
-
- if (card->fd < 0)
- return;
-
- log_debug("grdrm: %s/%s: close", card->base.session->name, card->base.name);
-
- grdrm_card_disable(card);
-
- card->fd_src = sd_event_source_unref(card->fd_src);
- card->fd = safe_close(card->fd);
-
- grdev_session_pin(card->base.session);
- while ((object = hashmap_first(card->object_map)))
- grdrm_object_free(object);
- grdev_session_unpin(card->base.session);
-}
-
-static bool grdrm_card_async(grdrm_card *card, int r) {
- switch (r) {
- case -EACCES:
- /* If we get EACCES on runtime DRM calls, we lost DRM-Master
- * (or we did something terribly wrong). Immediately disable
- * the card, so we stop all pipes and wait to be activated
- * again. */
- grdrm_card_disable(card);
- break;
- case -ENOENT:
- /* DRM objects can be hotplugged at any time. If an object is
- * removed that we use, we remember that state so a following
- * call can test for this.
- * Note that we also get a uevent as followup, this will resync
- * the whole device. */
- card->async_hotplug = true;
- break;
- }
-
- return !card->ready;
-}
-
-/*
- * Unmanaged Cards
- * The unmanaged DRM card opens the device node for a given DRM device
- * directly (/dev/dri/cardX) and thus needs sufficient privileges. It opens
- * the device only if we really require it and releases it as soon as we're
- * disabled or closed.
- * The unmanaged element can be used in all situations where you have direct
- * access to DRM device nodes. Unlike managed DRM elements, it can be used
- * outside of user sessions and in emergency situations where logind is not
- * available.
- */
-
-static void unmanaged_card_enable(grdev_card *basecard) {
- unmanaged_card *cu = unmanaged_card_from_base(basecard);
- int r, fd;
-
- if (cu->card.fd < 0) {
- /* try open on activation if it failed during allocation */
- fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- /* not fatal; simply ignore the device */
- log_debug_errno(errno, "grdrm: %s/%s: cannot open node %s: %m",
- basecard->session->name, basecard->name, cu->devnode);
- return;
- }
-
- /* we might already be DRM-Master by open(); that's fine */
-
- r = grdrm_card_open(&cu->card, fd);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- basecard->session->name, basecard->name);
- return;
- }
- }
-
- r = ioctl(cu->card.fd, DRM_IOCTL_SET_MASTER, 0);
- if (r < 0) {
- log_debug_errno(errno, "grdrm: %s/%s: cannot acquire DRM-Master: %m",
- basecard->session->name, basecard->name);
- return;
- }
-
- grdrm_card_enable(&cu->card);
-}
-
-static void unmanaged_card_disable(grdev_card *basecard) {
- unmanaged_card *cu = unmanaged_card_from_base(basecard);
-
- grdrm_card_disable(&cu->card);
-}
-
-static int unmanaged_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
- _cleanup_(grdev_card_freep) grdev_card *basecard = NULL;
- char name[GRDRM_CARD_NAME_MAX];
- unmanaged_card *cu;
- const char *devnode;
- dev_t devnum;
- int r, fd;
-
- assert_return(session, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnode = udev_device_get_devnode(ud);
- devnum = udev_device_get_devnum(ud);
- if (!devnode || devnum == 0)
- return -ENODEV;
-
- grdrm_name(name, devnum);
-
- cu = new0(unmanaged_card, 1);
- if (!cu)
- return -ENOMEM;
-
- basecard = &cu->card.base;
- cu->card = GRDRM_CARD_INIT(&unmanaged_card_vtable, session);
-
- cu->devnode = strdup(devnode);
- if (!cu->devnode)
- return -ENOMEM;
-
- r = grdrm_card_add(&cu->card, name);
- if (r < 0)
- return r;
-
- /* try to open but ignore errors */
- fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- /* not fatal; allow uaccess based control on activation */
- log_debug_errno(errno, "grdrm: %s/%s: cannot open node %s: %m",
- basecard->session->name, basecard->name, cu->devnode);
- } else {
- /* We might get DRM-Master implicitly on open(); drop it immediately
- * so we acquire it only once we're actually enabled. We don't
- * really care whether this call fails or not, but let's log any
- * weird errors, anyway. */
- r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
- if (r < 0 && errno != EACCES && errno != EINVAL)
- log_debug_errno(errno, "grdrm: %s/%s: cannot drop DRM-Master: %m",
- basecard->session->name, basecard->name);
-
- r = grdrm_card_open(&cu->card, fd);
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- basecard->session->name, basecard->name);
- }
-
- if (out)
- *out = basecard;
- basecard = NULL;
- return 0;
-}
-
-static void unmanaged_card_free(grdev_card *basecard) {
- unmanaged_card *cu = unmanaged_card_from_base(basecard);
-
- assert(!basecard->enabled);
-
- grdrm_card_close(&cu->card);
- grdrm_card_destroy(&cu->card);
- free(cu->devnode);
- free(cu);
-}
-
-static const grdev_card_vtable unmanaged_card_vtable = {
- .free = unmanaged_card_free,
- .enable = unmanaged_card_enable,
- .disable = unmanaged_card_disable,
- .commit = grdrm_card_commit,
- .restore = grdrm_card_restore,
-};
-
-/*
- * Managed Cards
- * The managed DRM card uses systemd-logind to acquire DRM devices. This
- * means, we do not open the device node /dev/dri/cardX directly. Instead,
- * logind passes us a file-descriptor whenever our session is activated. Thus,
- * we don't need access to the device node directly.
- * Furthermore, whenever the session is put asleep, logind revokes the
- * file-descriptor so we loose access to the device.
- * Managed DRM cards should be preferred over unmanaged DRM cards whenever
- * you run inside a user session with exclusive device access.
- */
-
-static void managed_card_enable(grdev_card *card) {
- managed_card *cm = managed_card_from_base(card);
-
- /* If the device is manually re-enabled, we try to resume our card
- * management. Note that we have no control over DRM-Master and the fd,
- * so we have to take over the state from the last logind event. */
-
- if (cm->master)
- grdrm_card_enable(&cm->card);
-}
-
-static void managed_card_disable(grdev_card *card) {
- managed_card *cm = managed_card_from_base(card);
-
- /* If the device is manually disabled, we keep the FD but put our card
- * management asleep. This way, we can wake up at any time, but don't
- * touch the device while asleep. */
-
- grdrm_card_disable(&cm->card);
-}
-
-static int managed_card_pause_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_card *cm = userdata;
- grdev_session *session = cm->card.base.session;
- uint32_t major, minor;
- const char *mode;
- int r;
-
- /*
- * We get PauseDevice() signals from logind whenever a device we
- * requested was, or is about to be, paused. Arguments are major/minor
- * number of the device and the mode of the operation.
- * In case the event is not about our device, we ignore it. Otherwise,
- * we treat it as asynchronous DRM-DROP-MASTER. Note that we might have
- * already handled an EACCES error from a modeset ioctl, in which case
- * we already disabled the device.
- *
- * @mode can be one of the following:
- * "pause": The device is about to be paused. We must react
- * immediately and respond with PauseDeviceComplete(). Once
- * we replied, logind will pause the device. Note that
- * logind might apply any kind of timeout and force pause
- * the device if we don't respond in a timely manner. In
- * this case, we will receive a second PauseDevice event
- * with @mode set to "force" (or similar).
- * "force": The device was disabled forecfully by logind. DRM-Master
- * was already dropped. This is just an asynchronous
- * notification so we can put the device asleep (in case
- * we didn't already notice the dropped DRM-Master).
- * "gone": This is like "force" but is sent if the device was
- * paused due to a device-removal event.
- *
- * We always handle PauseDevice signals as "force" as we properly
- * support asynchronously dropping DRM-Master, anyway. But in case
- * logind sent mode "pause", we also call PauseDeviceComplete() to
- * immediately acknowledge the request.
- */
-
- r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
- if (r < 0) {
- log_debug("grdrm: %s/%s: erroneous PauseDevice signal",
- session->name, cm->card.base.name);
- return 0;
- }
-
- /* not our device? */
- if (makedev(major, minor) != cm->devnum)
- return 0;
-
- cm->master = false;
- grdrm_card_disable(&cm->card);
-
- if (streq(mode, "pause")) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- /*
- * Sending PauseDeviceComplete() is racy if logind triggers the
- * timeout. That is, if we take too long and logind pauses the
- * device by sending a forced PauseDevice, our
- * PauseDeviceComplete call will be stray. That's fine, though.
- * logind ignores such stray calls. Only if logind also sent a
- * further PauseDevice() signal, it might match our call
- * incorrectly to the newer PauseDevice(). That's fine, too, as
- * we handle that event asynchronously, anyway. Therefore,
- * whatever happens, we're fine. Yay!
- */
-
- r = sd_bus_message_new_method_call(session->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "PauseDeviceComplete");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major, minor);
- if (r >= 0)
- r = sd_bus_send(session->context->sysbus, m, NULL);
- }
-
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot send PauseDeviceComplete: %m",
- session->name, cm->card.base.name);
- }
-
- return 0;
-}
-
-static int managed_card_resume_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_card *cm = userdata;
- grdev_session *session = cm->card.base.session;
- uint32_t major, minor;
- int r, fd;
-
- /*
- * We get ResumeDevice signals whenever logind resumed a previously
- * paused device. The arguments contain the major/minor number of the
- * related device and a new file-descriptor for the freshly opened
- * device-node.
- * If the signal is not about our device, we simply ignore it.
- * Otherwise, we immediately resume the device. Note that we drop the
- * new file-descriptor as we already have one from TakeDevice(). logind
- * preserves the file-context across pause/resume for DRM but only
- * drops/acquires DRM-Master accordingly. This way, our context (like
- * DRM-FBs and BOs) is preserved.
- */
-
- r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
- if (r < 0) {
- log_debug("grdrm: %s/%s: erroneous ResumeDevice signal",
- session->name, cm->card.base.name);
- return 0;
- }
-
- /* not our device? */
- if (makedev(major, minor) != cm->devnum)
- return 0;
-
- if (cm->card.fd < 0) {
- /* This shouldn't happen. We should already own an FD from
- * TakeDevice(). However, let's be safe and use this FD in case
- * we really don't have one. There is no harm in doing this
- * and our code works fine this way. */
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "grdrm: %s/%s: cannot duplicate fd: %m",
- session->name, cm->card.base.name);
- return 0;
- }
-
- r = grdrm_card_open(&cm->card, fd);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- session->name, cm->card.base.name);
- return 0;
- }
- }
-
- cm->master = true;
- if (cm->card.base.enabled)
- grdrm_card_enable(&cm->card);
-
- return 0;
-}
-
-static int managed_card_setup_bus(managed_card *cm) {
- grdev_session *session = cm->card.base.session;
- _cleanup_free_ char *match = NULL;
- int r;
-
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='PauseDevice',"
- "path='", session->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(session->context->sysbus,
- &cm->slot_pause_device,
- match,
- managed_card_pause_device_fn,
- cm);
- if (r < 0)
- return r;
-
- free(match);
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='ResumeDevice',"
- "path='", session->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(session->context->sysbus,
- &cm->slot_resume_device,
- match,
- managed_card_resume_device_fn,
- cm);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int managed_card_take_device_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_card *cm = userdata;
- grdev_session *session = cm->card.base.session;
- int r, paused, fd;
-
- cm->slot_take_device = sd_bus_slot_unref(cm->slot_take_device);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("grdrm: %s/%s: TakeDevice failed: %s: %s",
- session->name, cm->card.base.name, error->name, error->message);
- return 0;
- }
-
- cm->acquired = true;
-
- r = sd_bus_message_read(reply, "hb", &fd, &paused);
- if (r < 0) {
- log_debug("grdrm: %s/%s: erroneous TakeDevice reply",
- session->name, cm->card.base.name);
- return 0;
- }
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "grdrm: %s/%s: cannot duplicate fd: %m",
- session->name, cm->card.base.name);
- return 0;
- }
-
- r = grdrm_card_open(&cm->card, fd);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- session->name, cm->card.base.name);
- return 0;
- }
-
- if (!paused && cm->card.base.enabled)
- grdrm_card_enable(&cm->card);
-
- return 0;
-}
-
-static void managed_card_take_device(managed_card *cm) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- grdev_session *session = cm->card.base.session;
- int r;
-
- r = sd_bus_message_new_method_call(session->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "TakeDevice");
- if (r < 0)
- goto error;
-
- r = sd_bus_message_append(m, "uu", major(cm->devnum), minor(cm->devnum));
- if (r < 0)
- goto error;
-
- r = sd_bus_call_async(session->context->sysbus,
- &cm->slot_take_device,
- m,
- managed_card_take_device_fn,
- cm,
- 0);
- if (r < 0)
- goto error;
-
- cm->requested = true;
- return;
-
-error:
- log_debug_errno(r, "grdrm: %s/%s: cannot send TakeDevice request: %m",
- session->name, cm->card.base.name);
-}
-
-static void managed_card_release_device(managed_card *cm) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- grdev_session *session = cm->card.base.session;
- int r;
-
- /*
- * If TakeDevice() is pending or was successful, make sure to
- * release the device again. We don't care for return-values,
- * so send it without waiting or callbacks.
- * If a failed TakeDevice() is pending, but someone else took
- * the device on the same bus-connection, we might incorrectly
- * release their device. This is an unlikely race, though.
- * Furthermore, you really shouldn't have two users of the
- * controller-API on the same session, on the same devices, *AND* on
- * the same bus-connection. So we don't care for that race..
- */
-
- grdrm_card_close(&cm->card);
- cm->requested = false;
-
- if (!cm->acquired && !cm->slot_take_device)
- return;
-
- cm->slot_take_device = sd_bus_slot_unref(cm->slot_take_device);
- cm->acquired = false;
-
- r = sd_bus_message_new_method_call(session->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "ReleaseDevice");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major(cm->devnum), minor(cm->devnum));
- if (r >= 0)
- r = sd_bus_send(session->context->sysbus, m, NULL);
- }
-
- if (r < 0 && r != -ENOTCONN)
- log_debug_errno(r, "grdrm: %s/%s: cannot send ReleaseDevice: %m",
- session->name, cm->card.base.name);
-}
-
-static int managed_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
- _cleanup_(grdev_card_freep) grdev_card *basecard = NULL;
- char name[GRDRM_CARD_NAME_MAX];
- managed_card *cm;
- dev_t devnum;
- int r;
-
- assert_return(session, -EINVAL);
- assert_return(session->managed, -EINVAL);
- assert_return(session->context->sysbus, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return -ENODEV;
-
- grdrm_name(name, devnum);
-
- cm = new0(managed_card, 1);
- if (!cm)
- return -ENOMEM;
-
- basecard = &cm->card.base;
- cm->card = GRDRM_CARD_INIT(&managed_card_vtable, session);
- cm->devnum = devnum;
-
- r = managed_card_setup_bus(cm);
- if (r < 0)
- return r;
-
- r = grdrm_card_add(&cm->card, name);
- if (r < 0)
- return r;
-
- managed_card_take_device(cm);
-
- if (out)
- *out = basecard;
- basecard = NULL;
- return 0;
-}
-
-static void managed_card_free(grdev_card *basecard) {
- managed_card *cm = managed_card_from_base(basecard);
-
- assert(!basecard->enabled);
-
- managed_card_release_device(cm);
- cm->slot_resume_device = sd_bus_slot_unref(cm->slot_resume_device);
- cm->slot_pause_device = sd_bus_slot_unref(cm->slot_pause_device);
- grdrm_card_destroy(&cm->card);
- free(cm);
-}
-
-static const grdev_card_vtable managed_card_vtable = {
- .free = managed_card_free,
- .enable = managed_card_enable,
- .disable = managed_card_disable,
- .commit = grdrm_card_commit,
- .restore = grdrm_card_restore,
-};
-
-/*
- * Generic Constructor
- * Instead of relying on the caller to choose between managed and unmanaged
- * DRM devices, the grdev_drm_new() constructor does that for you (by
- * looking at session->managed).
- */
-
-bool grdev_is_drm_card(grdev_card *basecard) {
- return basecard && (basecard->vtable == &unmanaged_card_vtable ||
- basecard->vtable == &managed_card_vtable);
-}
-
-grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum) {
- char name[GRDRM_CARD_NAME_MAX];
-
- assert_return(session, NULL);
- assert_return(devnum != 0, NULL);
-
- grdrm_name(name, devnum);
- return grdev_find_card(session, name);
-}
-
-int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
- assert_return(session, -EINVAL);
- assert_return(ud, -EINVAL);
-
- return session->managed ? managed_card_new(out, session, ud) : unmanaged_card_new(out, session, ud);
-}
-
-void grdev_drm_card_hotplug(grdev_card *basecard, struct udev_device *ud) {
- const char *p, *action;
- grdrm_card *card;
- dev_t devnum;
-
- assert(basecard);
- assert(grdev_is_drm_card(basecard));
- assert(ud);
-
- card = grdrm_card_from_base(basecard);
-
- action = udev_device_get_action(ud);
- if (!action || streq(action, "add") || streq(action, "remove")) {
- /* If we get add/remove events on DRM nodes without devnum, we
- * got hotplugged DRM objects so refresh the device. */
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0) {
- card->hotplug = true;
- grdrm_card_hotplug(card);
- }
- } else if (streq_ptr(action, "change")) {
- /* A change event with HOTPLUG=1 is sent whenever a connector
- * changed state. Refresh the device to update our state. */
- p = udev_device_get_property_value(ud, "HOTPLUG");
- if (streq_ptr(p, "1")) {
- card->hotplug = true;
- grdrm_card_hotplug(card);
- }
- }
-}
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
deleted file mode 100644
index 46d65f0248..0000000000
--- a/src/libsystemd-terminal/grdev-internal.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-#include "util.h"
-#include "grdev.h"
-
-typedef struct grdev_tile grdev_tile;
-typedef struct grdev_display_cache grdev_display_cache;
-
-typedef struct grdev_pipe_vtable grdev_pipe_vtable;
-typedef struct grdev_pipe grdev_pipe;
-typedef struct grdev_card_vtable grdev_card_vtable;
-typedef struct grdev_card grdev_card;
-
-/*
- * DRM cards
- */
-
-bool grdev_is_drm_card(grdev_card *card);
-grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum);
-int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud);
-void grdev_drm_card_hotplug(grdev_card *card, struct udev_device *ud);
-
-/*
- * Displays
- */
-
-enum {
- GRDEV_TILE_LEAF,
- GRDEV_TILE_NODE,
- GRDEV_TILE_CNT
-};
-
-struct grdev_tile {
- LIST_FIELDS(grdev_tile, children_by_node);
- grdev_tile *parent;
- grdev_display *display;
-
- uint32_t x;
- uint32_t y;
- unsigned int rotate;
- unsigned int flip;
- uint32_t cache_w;
- uint32_t cache_h;
-
- unsigned int type;
-
- union {
- struct {
- grdev_pipe *pipe;
- } leaf;
-
- struct {
- size_t n_children;
- LIST_HEAD(grdev_tile, child_list);
- } node;
- };
-};
-
-int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe);
-int grdev_tile_new_node(grdev_tile **out);
-grdev_tile *grdev_tile_free(grdev_tile *tile);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_tile*, grdev_tile_free);
-
-struct grdev_display {
- grdev_session *session;
- char *name;
- void *userdata;
-
- size_t n_leafs;
- grdev_tile *tile;
-
- size_t n_pipes;
- size_t max_pipes;
-
- uint32_t width;
- uint32_t height;
-
- struct grdev_display_cache {
- grdev_pipe *pipe;
- grdev_display_target target;
-
- bool incomplete : 1;
- } *pipes;
-
- bool enabled : 1;
- bool public : 1;
- bool modified : 1;
- bool framed : 1;
-};
-
-grdev_display *grdev_find_display(grdev_session *session, const char *name);
-
-int grdev_display_new(grdev_display **out, grdev_session *session, const char *name);
-grdev_display *grdev_display_free(grdev_display *display);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_display*, grdev_display_free);
-
-/*
- * Pipes
- */
-
-struct grdev_pipe_vtable {
- void (*free) (grdev_pipe *pipe);
- void (*enable) (grdev_pipe *pipe);
- void (*disable) (grdev_pipe *pipe);
- grdev_fb *(*target) (grdev_pipe *pipe);
-};
-
-struct grdev_pipe {
- const grdev_pipe_vtable *vtable;
- grdev_card *card;
- char *name;
-
- grdev_tile *tile;
- grdev_display_cache *cache;
- sd_event_source *vsync_src;
-
- uint32_t width;
- uint32_t height;
- uint32_t vrefresh;
-
- size_t max_fbs;
- grdev_fb *front;
- grdev_fb *back;
- grdev_fb **fbs;
-
- bool enabled : 1;
- bool running : 1;
- bool flip : 1;
- bool flipping : 1;
-};
-
-#define GRDEV_PIPE_INIT(_vtable, _card) ((grdev_pipe){ \
- .vtable = (_vtable), \
- .card = (_card), \
- })
-
-grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name);
-
-int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs);
-grdev_pipe *grdev_pipe_free(grdev_pipe *pipe);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_pipe*, grdev_pipe_free);
-
-void grdev_pipe_ready(grdev_pipe *pipe, bool running);
-void grdev_pipe_frame(grdev_pipe *pipe);
-void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames);
-
-/*
- * Cards
- */
-
-struct grdev_card_vtable {
- void (*free) (grdev_card *card);
- void (*enable) (grdev_card *card);
- void (*disable) (grdev_card *card);
- void (*commit) (grdev_card *card);
- void (*restore) (grdev_card *card);
-};
-
-struct grdev_card {
- const grdev_card_vtable *vtable;
- grdev_session *session;
- char *name;
-
- Hashmap *pipe_map;
-
- bool enabled : 1;
- bool modified : 1;
-};
-
-#define GRDEV_CARD_INIT(_vtable, _session) ((grdev_card){ \
- .vtable = (_vtable), \
- .session = (_session), \
- })
-
-grdev_card *grdev_find_card(grdev_session *session, const char *name);
-
-int grdev_card_add(grdev_card *card, const char *name);
-grdev_card *grdev_card_free(grdev_card *card);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_card*, grdev_card_free);
-
-/*
- * Sessions
- */
-
-struct grdev_session {
- grdev_context *context;
- char *name;
- char *path;
- grdev_event_fn event_fn;
- void *userdata;
-
- unsigned long n_pins;
-
- Hashmap *card_map;
- Hashmap *display_map;
-
- bool custom : 1;
- bool managed : 1;
- bool enabled : 1;
- bool modified : 1;
-};
-
-grdev_session *grdev_session_pin(grdev_session *session);
-grdev_session *grdev_session_unpin(grdev_session *session);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_session*, grdev_session_unpin);
-
-/*
- * Contexts
- */
-
-struct grdev_context {
- unsigned long ref;
- sd_event *event;
- sd_bus *sysbus;
-
- Hashmap *session_map;
-};
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
deleted file mode 100644
index 71f0bd31e7..0000000000
--- a/src/libsystemd-terminal/grdev.c
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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 <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "login-util.h"
-#include "macro.h"
-#include "util.h"
-#include "grdev.h"
-#include "grdev-internal.h"
-
-static void pipe_enable(grdev_pipe *pipe);
-static void pipe_disable(grdev_pipe *pipe);
-static void card_modified(grdev_card *card);
-static void session_frame(grdev_session *session, grdev_display *display);
-
-/*
- * Displays
- */
-
-static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
- if (!tile)
- return NULL;
-
- while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
- tile = tile->node.child_list;
-
- return tile;
-}
-
-#define TILE_FOREACH(_root, _i) \
- for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent)
-
-#define TILE_FOREACH_SAFE(_root, _i, _next) \
- for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next)
-
-static void tile_link(grdev_tile *tile, grdev_tile *parent) {
- grdev_display *display;
- grdev_tile *t;
-
- assert(tile);
- assert(!tile->parent);
- assert(!tile->display);
- assert(parent);
- assert(parent->type == GRDEV_TILE_NODE);
-
- display = parent->display;
-
- assert(!display || !display->enabled);
-
- ++parent->node.n_children;
- LIST_PREPEND(children_by_node, parent->node.child_list, tile);
- tile->parent = parent;
-
- if (display) {
- display->modified = true;
- TILE_FOREACH(tile, t) {
- t->display = display;
- if (t->type == GRDEV_TILE_LEAF) {
- ++display->n_leafs;
- if (display->enabled)
- pipe_enable(t->leaf.pipe);
- }
- }
- }
-}
-
-static void tile_unlink(grdev_tile *tile) {
- grdev_tile *parent, *t;
- grdev_display *display;
-
- assert(tile);
-
- display = tile->display;
- parent = tile->parent;
- if (!parent) {
- assert(!display);
- return;
- }
-
- assert(parent->type == GRDEV_TILE_NODE);
- assert(parent->display == display);
- assert(parent->node.n_children > 0);
-
- --parent->node.n_children;
- LIST_REMOVE(children_by_node, parent->node.child_list, tile);
- tile->parent = NULL;
-
- if (display) {
- display->modified = true;
- TILE_FOREACH(tile, t) {
- t->display = NULL;
- if (t->type == GRDEV_TILE_LEAF) {
- --display->n_leafs;
- t->leaf.pipe->cache = NULL;
- pipe_disable(t->leaf.pipe);
- }
- }
- }
-
- /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
- * we must take care to not leave them around. Therefore, whenever we
- * unlink any part of a tree, we also destroy the parent, in case it's
- * now stale.
- * Parents are stale if they have no children and either have no display
- * or if they are intermediate nodes (i.e, they have a parent).
- * This means, you can easily create trees, but you can never partially
- * move or destruct them so far. They're always reduced to minimal form
- * if you cut them. This might change later, but so far we didn't need
- * partial destruction or the ability to move whole trees. */
-
- if (parent->node.n_children < 1 && (parent->parent || !parent->display))
- grdev_tile_free(parent);
-}
-
-static int tile_new(grdev_tile **out) {
- _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
-
- assert(out);
-
- tile = new0(grdev_tile, 1);
- if (!tile)
- return -ENOMEM;
-
- tile->type = (unsigned)-1;
-
- *out = tile;
- tile = NULL;
- return 0;
-}
-
-int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
- _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(pipe, -EINVAL);
- assert_return(!pipe->tile, -EINVAL);
-
- r = tile_new(&tile);
- if (r < 0)
- return r;
-
- tile->type = GRDEV_TILE_LEAF;
- tile->leaf.pipe = pipe;
-
- if (out)
- *out = tile;
- tile = NULL;
- return 0;
-}
-
-int grdev_tile_new_node(grdev_tile **out) {
- _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
- int r;
-
- assert_return(out, -EINVAL);
-
- r = tile_new(&tile);
- if (r < 0)
- return r;
-
- tile->type = GRDEV_TILE_NODE;
-
- *out = tile;
- tile = NULL;
- return 0;
-}
-
-grdev_tile *grdev_tile_free(grdev_tile *tile) {
- if (!tile)
- return NULL;
-
- tile_unlink(tile);
-
- switch (tile->type) {
- case GRDEV_TILE_LEAF:
- assert(!tile->parent);
- assert(!tile->display);
- assert(tile->leaf.pipe);
-
- break;
- case GRDEV_TILE_NODE:
- assert(!tile->parent);
- assert(!tile->display);
- assert(tile->node.n_children == 0);
-
- break;
- }
-
- free(tile);
-
- return NULL;
-}
-
-grdev_display *grdev_find_display(grdev_session *session, const char *name) {
- assert_return(session, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(session->display_map, name);
-}
-
-int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
- _cleanup_(grdev_display_freep) grdev_display *display = NULL;
- int r;
-
- assert(session);
- assert(name);
-
- display = new0(grdev_display, 1);
- if (!display)
- return -ENOMEM;
-
- display->session = session;
-
- display->name = strdup(name);
- if (!display->name)
- return -ENOMEM;
-
- r = grdev_tile_new_node(&display->tile);
- if (r < 0)
- return r;
-
- display->tile->display = display;
-
- r = hashmap_put(session->display_map, display->name, display);
- if (r < 0)
- return r;
-
- if (out)
- *out = display;
- display = NULL;
- return 0;
-}
-
-grdev_display *grdev_display_free(grdev_display *display) {
- if (!display)
- return NULL;
-
- assert(!display->public);
- assert(!display->enabled);
- assert(!display->modified);
- assert(display->n_leafs == 0);
- assert(display->n_pipes == 0);
-
- if (display->name)
- hashmap_remove_value(display->session->display_map, display->name, display);
-
- if (display->tile) {
- display->tile->display = NULL;
- grdev_tile_free(display->tile);
- }
-
- free(display->pipes);
- free(display->name);
- free(display);
-
- return NULL;
-}
-
-void grdev_display_set_userdata(grdev_display *display, void *userdata) {
- assert(display);
-
- display->userdata = userdata;
-}
-
-void *grdev_display_get_userdata(grdev_display *display) {
- assert_return(display, NULL);
-
- return display->userdata;
-}
-
-const char *grdev_display_get_name(grdev_display *display) {
- assert_return(display, NULL);
-
- return display->name;
-}
-
-uint32_t grdev_display_get_width(grdev_display *display) {
- assert_return(display, 0);
-
- return display->width;
-}
-
-uint32_t grdev_display_get_height(grdev_display *display) {
- assert_return(display, 0);
-
- return display->height;
-}
-
-bool grdev_display_is_enabled(grdev_display *display) {
- return display && display->enabled;
-}
-
-void grdev_display_enable(grdev_display *display) {
- grdev_tile *t;
-
- assert(display);
-
- if (!display->enabled) {
- display->enabled = true;
- TILE_FOREACH(display->tile, t)
- if (t->type == GRDEV_TILE_LEAF)
- pipe_enable(t->leaf.pipe);
- }
-}
-
-void grdev_display_disable(grdev_display *display) {
- grdev_tile *t;
-
- assert(display);
-
- if (display->enabled) {
- display->enabled = false;
- TILE_FOREACH(display->tile, t)
- if (t->type == GRDEV_TILE_LEAF)
- pipe_disable(t->leaf.pipe);
- }
-}
-
-const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) {
- grdev_display_cache *cache;
- size_t idx;
-
- assert_return(display, NULL);
- assert_return(!display->modified, NULL);
- assert_return(display->enabled, NULL);
-
- if (prev) {
- cache = container_of(prev, grdev_display_cache, target);
-
- assert(cache->pipe);
- assert(cache->pipe->tile->display == display);
- assert(display->pipes >= cache);
-
- idx = cache - display->pipes + 1;
- } else {
- idx = 0;
- }
-
- for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
- grdev_display_target *target;
- grdev_pipe *pipe;
- grdev_fb *fb;
-
- pipe = cache->pipe;
- target = &cache->target;
-
- if (!pipe->running || !pipe->enabled)
- continue;
-
- /* find suitable back-buffer */
- if (!pipe->back) {
- if (!pipe->vtable->target)
- continue;
- if (!(fb = pipe->vtable->target(pipe)))
- continue;
-
- assert(fb == pipe->back);
- }
-
- target->front = pipe->front;
- target->back = pipe->back;
-
- return target;
- }
-
- return NULL;
-}
-
-void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target) {
- grdev_display_cache *cache;
-
- assert(display);
- assert(!display->modified);
- assert(display->enabled);
- assert(target);
-
- cache = container_of(target, grdev_display_cache, target);
-
- assert(cache->pipe);
- assert(cache->pipe->tile->display == display);
-
- cache->pipe->flip = true;
-}
-
-static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
- uint32_t x, y, width, height;
- grdev_display_target *t;
-
- assert(c);
- assert(l);
- assert(l->cache_w >= c->target.width + c->target.x);
- assert(l->cache_h >= c->target.height + c->target.y);
-
- t = &c->target;
-
- /* rotate child */
-
- t->rotate = (t->rotate + l->rotate) & 0x3;
-
- x = t->x;
- y = t->y;
- width = t->width;
- height = t->height;
-
- switch (l->rotate) {
- case GRDEV_ROTATE_0:
- break;
- case GRDEV_ROTATE_90:
- t->x = l->cache_h - (height + y);
- t->y = x;
- t->width = height;
- t->height = width;
- break;
- case GRDEV_ROTATE_180:
- t->x = l->cache_w - (width + x);
- t->y = l->cache_h - (height + y);
- break;
- case GRDEV_ROTATE_270:
- t->x = y;
- t->y = l->cache_w - (width + x);
- t->width = height;
- t->height = width;
- break;
- }
-
- /* flip child */
-
- t->flip ^= l->flip;
-
- if (l->flip & GRDEV_FLIP_HORIZONTAL)
- t->x = l->cache_w - (t->width + t->x);
- if (l->flip & GRDEV_FLIP_VERTICAL)
- t->y = l->cache_h - (t->height + t->y);
-
- /* move child */
-
- t->x += l->x;
- t->y += l->y;
-}
-
-static void display_cache_targets(grdev_display *display) {
- grdev_display_cache *c;
- grdev_tile *tile;
-
- assert(display);
-
- /* depth-first with children before parent */
- for (tile = tile_leftmost(display->tile);
- tile;
- tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
- if (tile->type == GRDEV_TILE_LEAF) {
- grdev_pipe *p;
-
- /* We're at a leaf and no parent has been cached, yet.
- * Copy the pipe information into the target cache and
- * update our global pipe-caches if required. */
-
- assert(tile->leaf.pipe);
- assert(display->n_pipes + 1 <= display->max_pipes);
-
- p = tile->leaf.pipe;
- c = &display->pipes[display->n_pipes++];
-
- zero(*c);
- c->pipe = p;
- c->pipe->cache = c;
- c->target.width = p->width;
- c->target.height = p->height;
- tile->cache_w = p->width;
- tile->cache_h = p->height;
-
- /* all new tiles are incomplete due to geometry changes */
- c->incomplete = true;
-
- display_cache_apply(c, tile);
- } else {
- grdev_tile *child, *l;
-
- /* We're now at a node with all its children already
- * computed (depth-first, child before parent). We
- * first need to know the size of our tile, then we
- * recurse into all leafs and update their cache. */
-
- tile->cache_w = 0;
- tile->cache_h = 0;
-
- LIST_FOREACH(children_by_node, child, tile->node.child_list) {
- if (child->x + child->cache_w > tile->cache_w)
- tile->cache_w = child->x + child->cache_w;
- if (child->y + child->cache_h > tile->cache_h)
- tile->cache_h = child->y + child->cache_h;
- }
-
- assert(tile->cache_w > 0);
- assert(tile->cache_h > 0);
-
- TILE_FOREACH(tile, l)
- if (l->type == GRDEV_TILE_LEAF)
- display_cache_apply(l->leaf.pipe->cache, tile);
- }
- }
-}
-
-static bool display_cache(grdev_display *display) {
- grdev_tile *tile;
- size_t n;
- void *t;
- int r;
-
- assert(display);
-
- if (!display->modified)
- return false;
-
- display->modified = false;
- display->framed = false;
- display->n_pipes = 0;
- display->width = 0;
- display->height = 0;
-
- if (display->n_leafs < 1)
- return false;
-
- TILE_FOREACH(display->tile, tile)
- if (tile->type == GRDEV_TILE_LEAF)
- tile->leaf.pipe->cache = NULL;
-
- if (display->n_leafs > display->max_pipes) {
- n = ALIGN_POWER2(display->n_leafs);
- if (!n) {
- r = -ENOMEM;
- goto out;
- }
-
- t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
- if (!t) {
- r = -ENOMEM;
- goto out;
- }
-
- display->pipes = t;
- display->max_pipes = n;
- }
-
- display_cache_targets(display);
- display->width = display->tile->cache_w;
- display->height = display->tile->cache_h;
-
- r = 0;
-
-out:
- if (r < 0)
- log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m",
- display->session->name, display->name);
- return true;
-}
-
-/*
- * Pipes
- */
-
-grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
- assert_return(card, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(card->pipe_map, name);
-}
-
-static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
- grdev_pipe *pipe = userdata;
-
- grdev_pipe_frame(pipe);
- return 0;
-}
-
-int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
- int r;
-
- assert_return(pipe, -EINVAL);
- assert_return(pipe->vtable, -EINVAL);
- assert_return(pipe->vtable->free, -EINVAL);
- assert_return(pipe->card, -EINVAL);
- assert_return(pipe->card->session, -EINVAL);
- assert_return(!pipe->cache, -EINVAL);
- assert_return(pipe->width > 0, -EINVAL);
- assert_return(pipe->height > 0, -EINVAL);
- assert_return(pipe->vrefresh > 0, -EINVAL);
- assert_return(!pipe->enabled, -EINVAL);
- assert_return(!pipe->running, -EINVAL);
- assert_return(name, -EINVAL);
-
- pipe->name = strdup(name);
- if (!pipe->name)
- return -ENOMEM;
-
- if (n_fbs > 0) {
- pipe->fbs = new0(grdev_fb*, n_fbs);
- if (!pipe->fbs)
- return -ENOMEM;
-
- pipe->max_fbs = n_fbs;
- }
-
- r = grdev_tile_new_leaf(&pipe->tile, pipe);
- if (r < 0)
- return r;
-
- r = sd_event_add_time(pipe->card->session->context->event,
- &pipe->vsync_src,
- CLOCK_MONOTONIC,
- 0,
- 10 * USEC_PER_MSEC,
- pipe_vsync_fn,
- pipe);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
- if (r < 0)
- return r;
-
- r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
- if (r < 0)
- return r;
-
- card_modified(pipe->card);
- return 0;
-}
-
-grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
- grdev_pipe tmp;
-
- if (!pipe)
- return NULL;
-
- assert(pipe->card);
- assert(pipe->vtable);
- assert(pipe->vtable->free);
-
- if (pipe->name)
- hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
- if (pipe->tile)
- tile_unlink(pipe->tile);
-
- assert(!pipe->cache);
-
- tmp = *pipe;
- pipe->vtable->free(pipe);
-
- sd_event_source_unref(tmp.vsync_src);
- grdev_tile_free(tmp.tile);
- card_modified(tmp.card);
- free(tmp.fbs);
- free(tmp.name);
-
- return NULL;
-}
-
-static void pipe_enable(grdev_pipe *pipe) {
- assert(pipe);
-
- if (!pipe->enabled) {
- pipe->enabled = true;
- if (pipe->vtable->enable)
- pipe->vtable->enable(pipe);
- }
-}
-
-static void pipe_disable(grdev_pipe *pipe) {
- assert(pipe);
-
- if (pipe->enabled) {
- pipe->enabled = false;
- if (pipe->vtable->disable)
- pipe->vtable->disable(pipe);
- }
-}
-
-void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
- assert(pipe);
-
- /* grdev_pipe_ready() is used by backends to notify about pipe state
- * changed. If a pipe is ready, it can be fully used by us (available,
- * enabled and accessible). Backends can disable pipes at any time
- * (like for async revocation), but can only enable them from parent
- * context. Otherwise, we might call user-callbacks recursively. */
-
- if (pipe->running == running)
- return;
-
- pipe->running = running;
-
- /* runtime events for unused pipes are not interesting */
- if (pipe->cache && pipe->enabled) {
- grdev_display *display = pipe->tile->display;
-
- assert(display);
-
- if (running)
- session_frame(display->session, display);
- else
- pipe->cache->incomplete = true;
- }
-}
-
-void grdev_pipe_frame(grdev_pipe *pipe) {
- grdev_display *display;
-
- assert(pipe);
-
- /* if pipe is unused, ignore any frame events */
- if (!pipe->cache || !pipe->enabled)
- return;
-
- display = pipe->tile->display;
- assert(display);
-
- grdev_pipe_schedule(pipe, 0);
- session_frame(display->session, display);
-}
-
-void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
- int r;
- uint64_t ts;
-
- if (!frames) {
- sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
- return;
- }
-
- r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
- if (r < 0)
- goto error;
-
- ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
-
- r = sd_event_source_set_time(pipe->vsync_src, ts);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
- if (r < 0)
- goto error;
-
- return;
-
-error:
- log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m",
- pipe->card->session->name, pipe->card->name, pipe->name);
-}
-
-/*
- * Cards
- */
-
-grdev_card *grdev_find_card(grdev_session *session, const char *name) {
- assert_return(session, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(session->card_map, name);
-}
-
-int grdev_card_add(grdev_card *card, const char *name) {
- int r;
-
- assert_return(card, -EINVAL);
- assert_return(card->vtable, -EINVAL);
- assert_return(card->vtable->free, -EINVAL);
- assert_return(card->session, -EINVAL);
- assert_return(name, -EINVAL);
-
- card->name = strdup(name);
- if (!card->name)
- return -ENOMEM;
-
- card->pipe_map = hashmap_new(&string_hash_ops);
- if (!card->pipe_map)
- return -ENOMEM;
-
- r = hashmap_put(card->session->card_map, card->name, card);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-grdev_card *grdev_card_free(grdev_card *card) {
- grdev_card tmp;
-
- if (!card)
- return NULL;
-
- assert(!card->enabled);
- assert(card->vtable);
- assert(card->vtable->free);
-
- if (card->name)
- hashmap_remove_value(card->session->card_map, card->name, card);
-
- tmp = *card;
- card->vtable->free(card);
-
- assert(hashmap_size(tmp.pipe_map) == 0);
-
- hashmap_free(tmp.pipe_map);
- free(tmp.name);
-
- return NULL;
-}
-
-static void card_modified(grdev_card *card) {
- assert(card);
- assert(card->session->n_pins > 0);
-
- card->modified = true;
-}
-
-static void grdev_card_enable(grdev_card *card) {
- assert(card);
-
- if (!card->enabled) {
- card->enabled = true;
- if (card->vtable->enable)
- card->vtable->enable(card);
- }
-}
-
-static void grdev_card_disable(grdev_card *card) {
- assert(card);
-
- if (card->enabled) {
- card->enabled = false;
- if (card->vtable->disable)
- card->vtable->disable(card);
- }
-}
-
-/*
- * Sessions
- */
-
-static void session_raise(grdev_session *session, grdev_event *event) {
- session->event_fn(session, session->userdata, event);
-}
-
-static void session_raise_display_add(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_ADD,
- .display_add = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_REMOVE,
- .display_remove = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_raise_display_change(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_CHANGE,
- .display_change = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_FRAME,
- .display_frame = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_add_card(grdev_session *session, grdev_card *card) {
- assert(session);
- assert(card);
-
- log_debug("grdev: %s: add card '%s'", session->name, card->name);
-
- /* Cards are not exposed to users, but managed internally. Cards are
- * enabled if the session is enabled, and will track that state. The
- * backend can probe the card at any time, but only if enabled. It
- * will then add pipes according to hardware state.
- * That is, the card may create pipes as soon as we enable it here. */
-
- if (session->enabled)
- grdev_card_enable(card);
-}
-
-static void session_remove_card(grdev_session *session, grdev_card *card) {
- assert(session);
- assert(card);
-
- log_debug("grdev: %s: remove card '%s'", session->name, card->name);
-
- /* As cards are not exposed, it can never be accessed by outside
- * users and we can simply remove it. Disabling the card does not
- * necessarily drop all pipes of the card. This is usually deferred
- * to card destruction (as pipes are cached as long as FDs remain
- * open). Therefore, the card destruction might cause pipes, and thus
- * visible displays, to be removed. */
-
- grdev_card_disable(card);
- grdev_card_free(card);
-}
-
-static void session_add_display(grdev_session *session, grdev_display *display) {
- assert(session);
- assert(display);
- assert(!display->enabled);
-
- log_debug("grdev: %s: add display '%s'", session->name, display->name);
-
- /* Displays are the main entity for public API users. We create them
- * independent of card backends and they wrap any underlying display
- * architecture. Displays are public at all times, thus, may be entered
- * by outside users at any time. */
-
- display->public = true;
- session_raise_display_add(session, display);
-}
-
-static void session_remove_display(grdev_session *session, grdev_display *display) {
- assert(session);
- assert(display);
-
- log_debug("grdev: %s: remove display '%s'", session->name, display->name);
-
- /* Displays are public, so we have to be careful when removing them.
- * We first tell users about their removal, disable them and then drop
- * them. We now, after the notification, no external access will
- * happen. Therefore, we can release the tiles afterwards safely. */
-
- if (display->public) {
- display->public = false;
- session_raise_display_remove(session, display);
- }
-
- grdev_display_disable(display);
- grdev_display_free(display);
-}
-
-static void session_change_display(grdev_session *session, grdev_display *display) {
- bool changed;
-
- assert(session);
- assert(display);
-
- changed = display_cache(display);
-
- if (display->n_leafs == 0) {
- session_remove_display(session, display);
- } else if (!display->public) {
- session_add_display(session, display);
- session_frame(session, display);
- } else if (changed) {
- session_raise_display_change(session, display);
- session_frame(session, display);
- } else if (display->framed) {
- session_frame(session, display);
- }
-}
-
-static void session_frame(grdev_session *session, grdev_display *display) {
- assert(session);
- assert(display);
-
- display->framed = false;
-
- if (!display->enabled || !session->enabled)
- return;
-
- if (session->n_pins > 0)
- display->framed = true;
- else
- session_raise_display_frame(session, display);
-}
-
-int grdev_session_new(grdev_session **out,
- grdev_context *context,
- unsigned int flags,
- const char *name,
- grdev_event_fn event_fn,
- void *userdata) {
- _cleanup_(grdev_session_freep) grdev_session *session = NULL;
- int r;
-
- assert(out);
- assert(context);
- assert(name);
- assert(event_fn);
- assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
- assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
- assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
-
- session = new0(grdev_session, 1);
- if (!session)
- return -ENOMEM;
-
- session->context = grdev_context_ref(context);
- session->custom = flags & GRDEV_SESSION_CUSTOM;
- session->managed = flags & GRDEV_SESSION_MANAGED;
- session->event_fn = event_fn;
- session->userdata = userdata;
-
- session->name = strdup(name);
- if (!session->name)
- return -ENOMEM;
-
- if (session->managed) {
- r = sd_bus_path_encode("/org/freedesktop/login1/session",
- session->name, &session->path);
- if (r < 0)
- return r;
- }
-
- session->card_map = hashmap_new(&string_hash_ops);
- if (!session->card_map)
- return -ENOMEM;
-
- session->display_map = hashmap_new(&string_hash_ops);
- if (!session->display_map)
- return -ENOMEM;
-
- r = hashmap_put(context->session_map, session->name, session);
- if (r < 0)
- return r;
-
- *out = session;
- session = NULL;
- return 0;
-}
-
-grdev_session *grdev_session_free(grdev_session *session) {
- grdev_card *card;
-
- if (!session)
- return NULL;
-
- grdev_session_disable(session);
-
- while ((card = hashmap_first(session->card_map)))
- session_remove_card(session, card);
-
- assert(hashmap_size(session->display_map) == 0);
-
- if (session->name)
- hashmap_remove_value(session->context->session_map, session->name, session);
-
- hashmap_free(session->display_map);
- hashmap_free(session->card_map);
- session->context = grdev_context_unref(session->context);
- free(session->path);
- free(session->name);
- free(session);
-
- return NULL;
-}
-
-bool grdev_session_is_enabled(grdev_session *session) {
- return session && session->enabled;
-}
-
-void grdev_session_enable(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (!session->enabled) {
- session->enabled = true;
- HASHMAP_FOREACH(card, session->card_map, iter)
- grdev_card_enable(card);
- }
-}
-
-void grdev_session_disable(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (session->enabled) {
- session->enabled = false;
- HASHMAP_FOREACH(card, session->card_map, iter)
- grdev_card_disable(card);
- }
-}
-
-void grdev_session_commit(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (!session->enabled)
- return;
-
- HASHMAP_FOREACH(card, session->card_map, iter)
- if (card->vtable->commit)
- card->vtable->commit(card);
-}
-
-void grdev_session_restore(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (!session->enabled)
- return;
-
- HASHMAP_FOREACH(card, session->card_map, iter)
- if (card->vtable->restore)
- card->vtable->restore(card);
-}
-
-void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
- grdev_card *card;
- dev_t devnum;
- int r;
-
- assert(session);
- assert(ud);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return grdev_session_hotplug_drm(session, ud);
-
- card = grdev_find_drm_card(session, devnum);
- if (card)
- return;
-
- r = grdev_drm_card_new(&card, session, ud);
- if (r < 0) {
- log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m",
- session->name, udev_device_get_syspath(ud));
- return;
- }
-
- session_add_card(session, card);
-}
-
-void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
- grdev_card *card;
- dev_t devnum;
-
- assert(session);
- assert(ud);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return grdev_session_hotplug_drm(session, ud);
-
- card = grdev_find_drm_card(session, devnum);
- if (!card)
- return;
-
- session_remove_card(session, card);
-}
-
-void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
- grdev_card *card = NULL;
- struct udev_device *p;
- dev_t devnum;
-
- assert(session);
- assert(ud);
-
- for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- continue;
-
- card = grdev_find_drm_card(session, devnum);
- if (card)
- break;
- }
-
- if (!card)
- return;
-
- grdev_drm_card_hotplug(card, ud);
-}
-
-static void session_configure(grdev_session *session) {
- grdev_display *display;
- grdev_tile *tile;
- grdev_card *card;
- grdev_pipe *pipe;
- Iterator i, j;
- int r;
-
- assert(session);
-
- /*
- * Whenever backends add or remove pipes, we set session->modified and
- * require them to pin the session while modifying it. On release, we
- * reconfigure the device and re-assign displays to all modified pipes.
- *
- * So far, we configure each pipe as a separate display. We do not
- * support user-configuration, nor have we gotten any reports from
- * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
- * we get reports, we keep the logic to a minimum.
- */
-
- /* create new displays for all unconfigured pipes */
- HASHMAP_FOREACH(card, session->card_map, i) {
- if (!card->modified)
- continue;
-
- card->modified = false;
-
- HASHMAP_FOREACH(pipe, card->pipe_map, j) {
- tile = pipe->tile;
- if (tile->display)
- continue;
-
- assert(!tile->parent);
-
- display = grdev_find_display(session, pipe->name);
- if (display && display->tile) {
- log_debug("grdev: %s/%s: occupied display for pipe %s",
- session->name, card->name, pipe->name);
- continue;
- } else if (!display) {
- r = grdev_display_new(&display, session, pipe->name);
- if (r < 0) {
- log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m",
- session->name, card->name, pipe->name);
- continue;
- }
- }
-
- tile_link(pipe->tile, display->tile);
- }
- }
-
- /* update displays */
- HASHMAP_FOREACH(display, session->display_map, i)
- session_change_display(session, display);
-}
-
-grdev_session *grdev_session_pin(grdev_session *session) {
- assert(session);
-
- ++session->n_pins;
- return session;
-}
-
-grdev_session *grdev_session_unpin(grdev_session *session) {
- if (!session)
- return NULL;
-
- assert(session->n_pins > 0);
-
- if (--session->n_pins == 0)
- session_configure(session);
-
- return NULL;
-}
-
-/*
- * Contexts
- */
-
-int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
- _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
-
- assert_return(out, -EINVAL);
- assert_return(event, -EINVAL);
-
- context = new0(grdev_context, 1);
- if (!context)
- return -ENOMEM;
-
- context->ref = 1;
- context->event = sd_event_ref(event);
-
- if (sysbus)
- context->sysbus = sd_bus_ref(sysbus);
-
- context->session_map = hashmap_new(&string_hash_ops);
- if (!context->session_map)
- return -ENOMEM;
-
- *out = context;
- context = NULL;
- return 0;
-}
-
-static void context_cleanup(grdev_context *context) {
- assert(hashmap_size(context->session_map) == 0);
-
- hashmap_free(context->session_map);
- context->sysbus = sd_bus_unref(context->sysbus);
- context->event = sd_event_unref(context->event);
- free(context);
-}
-
-grdev_context *grdev_context_ref(grdev_context *context) {
- assert_return(context, NULL);
- assert_return(context->ref > 0, NULL);
-
- ++context->ref;
- return context;
-}
-
-grdev_context *grdev_context_unref(grdev_context *context) {
- if (!context)
- return NULL;
-
- assert_return(context->ref > 0, NULL);
-
- if (--context->ref == 0)
- context_cleanup(context);
-
- return NULL;
-}
diff --git a/src/libsystemd-terminal/grdev.h b/src/libsystemd-terminal/grdev.h
deleted file mode 100644
index 110d24e6d5..0000000000
--- a/src/libsystemd-terminal/grdev.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Graphics Devices
- * The grdev layer provides generic access to graphics devices. The device
- * types are hidden in the implementation and exported in a generic way. The
- * grdev_session object forms the base layer. It loads, configures and prepares
- * any graphics devices associated with that session. Each session is totally
- * independent of other sessions and can be controlled separately.
- * The target devices on a session are called display. A display always
- * corresponds to a real display regardless how many pipes are needed to drive
- * that display. That is, an exported display might internally be created out
- * of arbitrary combinations of target pipes. However, this is meant as
- * implementation detail and API users must never assume details below the
- * display-level. That is, a display is the most low-level object exported.
- * Therefore, pipe-configuration and any low-level modesetting is hidden from
- * the public API. It is provided by the implementation, and it is the
- * implementation that decides how pipes are driven.
- *
- * The API users are free to ignore specific displays or combine them to create
- * larger screens. This often requires user-configuration so is dictated by
- * policy. The underlying pipe-configuration might be affected by these
- * high-level policies, but is never directly controlled by those. That means,
- * depending on the displays you use, it might affect how underlying resources
- * are assigned. However, users can never directly apply policies to the pipes,
- * but only to displays. In case specific hardware needs quirks on the pipe
- * level, we support that via hwdb, not via public user configuration.
- *
- * Right now, displays are limited to rgb32 memory-mapped framebuffers on the
- * primary plane. However, the grdev implementation can be easily extended to
- * allow more powerful access (including hardware-acceleration for 2D and 3D
- * compositing). So far, this wasn't needed so it is not exposed.
- */
-
-#pragma once
-
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "util.h"
-
-typedef struct grdev_fb grdev_fb;
-typedef struct grdev_display_target grdev_display_target;
-typedef struct grdev_display grdev_display;
-
-typedef struct grdev_event grdev_event;
-typedef struct grdev_session grdev_session;
-typedef struct grdev_context grdev_context;
-
-enum {
- /* clockwise rotation; we treat this is abelian group Z4 with ADD */
- GRDEV_ROTATE_0 = 0,
- GRDEV_ROTATE_90 = 1,
- GRDEV_ROTATE_180 = 2,
- GRDEV_ROTATE_270 = 3,
-};
-
-enum {
- /* flip states; we treat this as abelian group V4 with XOR */
- GRDEV_FLIP_NONE = 0x0,
- GRDEV_FLIP_HORIZONTAL = 0x1,
- GRDEV_FLIP_VERTICAL = 0x2,
-};
-
-/*
- * Displays
- */
-
-struct grdev_fb {
- uint32_t width;
- uint32_t height;
- uint32_t format;
- int32_t strides[4];
- void *maps[4];
-
- union {
- void *ptr;
- uint64_t u64;
- } data;
-
- void (*free_fn) (void *ptr);
-};
-
-struct grdev_display_target {
- uint32_t x;
- uint32_t y;
- uint32_t width;
- uint32_t height;
- unsigned int rotate;
- unsigned int flip;
- grdev_fb *front;
- grdev_fb *back;
-};
-
-void grdev_display_set_userdata(grdev_display *display, void *userdata);
-void *grdev_display_get_userdata(grdev_display *display);
-
-const char *grdev_display_get_name(grdev_display *display);
-uint32_t grdev_display_get_width(grdev_display *display);
-uint32_t grdev_display_get_height(grdev_display *display);
-
-bool grdev_display_is_enabled(grdev_display *display);
-void grdev_display_enable(grdev_display *display);
-void grdev_display_disable(grdev_display *display);
-
-const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev);
-void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target);
-
-#define GRDEV_DISPLAY_FOREACH_TARGET(_display, _t) \
- for ((_t) = grdev_display_next_target((_display), NULL); \
- (_t); \
- (_t) = grdev_display_next_target((_display), (_t)))
-
-/*
- * Events
- */
-
-enum {
- GRDEV_EVENT_DISPLAY_ADD,
- GRDEV_EVENT_DISPLAY_REMOVE,
- GRDEV_EVENT_DISPLAY_CHANGE,
- GRDEV_EVENT_DISPLAY_FRAME,
-};
-
-typedef void (*grdev_event_fn) (grdev_session *session, void *userdata, grdev_event *ev);
-
-struct grdev_event {
- unsigned int type;
- union {
- struct {
- grdev_display *display;
- } display_add, display_remove, display_change;
-
- struct {
- grdev_display *display;
- } display_frame;
- };
-};
-
-/*
- * Sessions
- */
-
-enum {
- GRDEV_SESSION_CUSTOM = (1 << 0),
- GRDEV_SESSION_MANAGED = (1 << 1),
-};
-
-int grdev_session_new(grdev_session **out,
- grdev_context *context,
- unsigned int flags,
- const char *name,
- grdev_event_fn event_fn,
- void *userdata);
-grdev_session *grdev_session_free(grdev_session *session);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_session*, grdev_session_free);
-
-bool grdev_session_is_enabled(grdev_session *session);
-void grdev_session_enable(grdev_session *session);
-void grdev_session_disable(grdev_session *session);
-
-void grdev_session_commit(grdev_session *session);
-void grdev_session_restore(grdev_session *session);
-
-void grdev_session_add_drm(grdev_session *session, struct udev_device *ud);
-void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud);
-void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud);
-
-/*
- * Contexts
- */
-
-int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus);
-grdev_context *grdev_context_ref(grdev_context *context);
-grdev_context *grdev_context_unref(grdev_context *context);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_context*, grdev_context_unref);
diff --git a/src/libsystemd-terminal/idev-evdev.c b/src/libsystemd-terminal/idev-evdev.c
deleted file mode 100644
index f1a18b91d3..0000000000
--- a/src/libsystemd-terminal/idev-evdev.c
+++ /dev/null
@@ -1,859 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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 <fcntl.h>
-#include <libevdev/libevdev.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "macro.h"
-#include "util.h"
-#include "bus-util.h"
-#include "idev.h"
-#include "idev-internal.h"
-
-typedef struct idev_evdev idev_evdev;
-typedef struct unmanaged_evdev unmanaged_evdev;
-typedef struct managed_evdev managed_evdev;
-
-struct idev_evdev {
- idev_element element;
- struct libevdev *evdev;
- int fd;
- sd_event_source *fd_src;
- sd_event_source *idle_src;
-
- bool unsync : 1; /* not in-sync with kernel */
- bool resync : 1; /* re-syncing with kernel */
- bool running : 1;
-};
-
-struct unmanaged_evdev {
- idev_evdev evdev;
- char *devnode;
-};
-
-struct managed_evdev {
- idev_evdev evdev;
- dev_t devnum;
- sd_bus_slot *slot_take_device;
-
- bool requested : 1; /* TakeDevice() was sent */
- bool acquired : 1; /* TakeDevice() was successful */
-};
-
-#define idev_evdev_from_element(_e) container_of((_e), idev_evdev, element)
-#define unmanaged_evdev_from_element(_e) \
- container_of(idev_evdev_from_element(_e), unmanaged_evdev, evdev)
-#define managed_evdev_from_element(_e) \
- container_of(idev_evdev_from_element(_e), managed_evdev, evdev)
-
-#define IDEV_EVDEV_INIT(_vtable, _session) ((idev_evdev){ \
- .element = IDEV_ELEMENT_INIT((_vtable), (_session)), \
- .fd = -1, \
- })
-
-#define IDEV_EVDEV_NAME_MAX (8 + DECIMAL_STR_MAX(unsigned) * 2)
-
-static const idev_element_vtable unmanaged_evdev_vtable;
-static const idev_element_vtable managed_evdev_vtable;
-
-static int idev_evdev_resume(idev_evdev *evdev, int dev_fd);
-static void idev_evdev_pause(idev_evdev *evdev, bool release);
-
-/*
- * Virtual Evdev Element
- * The virtual evdev element is the base class of all other evdev elements. It
- * uses libevdev to access the kernel evdev API. It supports asynchronous
- * access revocation, re-syncing if events got dropped and more.
- * This element cannot be used by itself. There must be a wrapper around it
- * which opens a file-descriptor and passes it to the virtual evdev element.
- */
-
-static void idev_evdev_name(char *out, dev_t devnum) {
- /* @out must be at least of size IDEV_EVDEV_NAME_MAX */
- sprintf(out, "evdev/%u:%u", major(devnum), minor(devnum));
-}
-
-static int idev_evdev_feed_resync(idev_evdev *evdev) {
- idev_data data = {
- .type = IDEV_DATA_RESYNC,
- .resync = evdev->resync,
- };
-
- return idev_element_feed(&evdev->element, &data);
-}
-
-static int idev_evdev_feed_evdev(idev_evdev *evdev, struct input_event *event) {
- idev_data data = {
- .type = IDEV_DATA_EVDEV,
- .resync = evdev->resync,
- .evdev = {
- .event = *event,
- },
- };
-
- return idev_element_feed(&evdev->element, &data);
-}
-
-static void idev_evdev_hup(idev_evdev *evdev) {
- /*
- * On HUP, we close the current fd via idev_evdev_pause(). This drops
- * the event-sources from the main-loop and effectively puts the
- * element asleep. If the HUP is part of a hotplug-event, a following
- * udev-notification will destroy the element. Otherwise, the HUP is
- * either result of access-revokation or a serious error.
- * For unmanaged devices, we should never receive HUP (except for
- * unplug-events). But if we do, something went seriously wrong and we
- * shouldn't try to be clever.
- * Instead, we simply stay asleep and wait for the device to be
- * disabled and then re-enabled (or closed and re-opened). This will
- * re-open the device node and restart the device.
- * For managed devices, a HUP usually means our device-access was
- * revoked. In that case, we simply put the device asleep and wait for
- * logind to notify us once the device is alive again. logind also
- * passes us a new fd. Hence, we don't have to re-enable the device.
- *
- * Long story short: The only thing we have to do here, is close() the
- * file-descriptor and remove it from the main-loop. Everything else is
- * handled via additional events we receive.
- */
-
- idev_evdev_pause(evdev, true);
-}
-
-static int idev_evdev_io(idev_evdev *evdev) {
- idev_element *e = &evdev->element;
- struct input_event ev;
- unsigned int flags;
- int r, error = 0;
-
- /*
- * Read input-events via libevdev until the input-queue is drained. In
- * case we're disabled, don't do anything. The input-queue might
- * overflow, but we don't care as we have to resync after wake-up,
- * anyway.
- * TODO: libevdev should give us a hint how many events to read. We
- * really want to avoid starvation, so we shouldn't read forever in
- * case we cannot keep up with the kernel.
- * TODO: Make sure libevdev always reports SYN_DROPPED to us, regardless
- * whether any event was synced afterwards.
- */
-
- flags = LIBEVDEV_READ_FLAG_NORMAL;
- while (e->enabled) {
- if (evdev->unsync) {
- /* immediately resync, even if in sync right now */
- evdev->unsync = false;
- evdev->resync = false;
- flags = LIBEVDEV_READ_FLAG_NORMAL;
- r = libevdev_next_event(evdev->evdev, flags | LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
- if (r < 0 && r != -EAGAIN) {
- r = 0;
- goto error;
- } else if (r != LIBEVDEV_READ_STATUS_SYNC) {
- log_debug("idev-evdev: %s/%s: cannot force resync: %d",
- e->session->name, e->name, r);
- }
- } else {
- r = libevdev_next_event(evdev->evdev, flags, &ev);
- }
-
- if (evdev->resync && r == -EAGAIN) {
- /* end of re-sync */
- evdev->resync = false;
- flags = LIBEVDEV_READ_FLAG_NORMAL;
- } else if (r == -EAGAIN) {
- /* no data available */
- break;
- } else if (r < 0) {
- /* read error */
- goto error;
- } else if (r == LIBEVDEV_READ_STATUS_SYNC) {
- if (evdev->resync) {
- /* sync-event */
- r = idev_evdev_feed_evdev(evdev, &ev);
- if (r != 0) {
- error = r;
- break;
- }
- } else {
- /* start of sync */
- evdev->resync = true;
- flags = LIBEVDEV_READ_FLAG_SYNC;
- r = idev_evdev_feed_resync(evdev);
- if (r != 0) {
- error = r;
- break;
- }
- }
- } else {
- /* normal event */
- r = idev_evdev_feed_evdev(evdev, &ev);
- if (r != 0) {
- error = r;
- break;
- }
- }
- }
-
- if (error < 0)
- log_debug_errno(error, "idev-evdev: %s/%s: error on data event: %m",
- e->session->name, e->name);
- return error;
-
-error:
- idev_evdev_hup(evdev);
- return 0; /* idev_evdev_hup() handles the error so discard it */
-}
-
-static int idev_evdev_event_fn(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- idev_evdev *evdev = userdata;
-
- /* fetch data as long as EPOLLIN is signalled */
- if (revents & EPOLLIN)
- return idev_evdev_io(evdev);
-
- if (revents & (EPOLLHUP | EPOLLERR))
- idev_evdev_hup(evdev);
-
- return 0;
-}
-
-static int idev_evdev_idle_fn(sd_event_source *s, void *userdata) {
- idev_evdev *evdev = userdata;
-
- /*
- * The idle-event is raised whenever we have to re-sync the libevdev
- * state from the kernel. We simply call into idev_evdev_io() which
- * flushes the state and re-syncs it if @unsync is set.
- * State has to be synced whenever our view of the kernel device is
- * out of date. This is the case when we open the device, if the
- * kernel's receive buffer overflows, or on other exceptional
- * situations. Events during re-syncs must be forwarded to the upper
- * layers so they can update their view of the device. However, such
- * events must only be handled passively, as they might be out-of-order
- * and/or re-ordered. Therefore, we mark them as 'sync' events.
- */
-
- if (!evdev->unsync)
- return 0;
-
- return idev_evdev_io(evdev);
-}
-
-static void idev_evdev_destroy(idev_evdev *evdev) {
- assert(evdev);
- assert(evdev->fd < 0);
-
- libevdev_free(evdev->evdev);
- evdev->evdev = NULL;
-}
-
-static void idev_evdev_enable(idev_evdev *evdev) {
- assert(evdev);
- assert(evdev->fd_src);
- assert(evdev->idle_src);
-
- if (evdev->running)
- return;
- if (evdev->fd < 0 || evdev->element.n_open < 1 || !evdev->element.enabled)
- return;
-
- evdev->running = true;
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_ON);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_ONESHOT);
-}
-
-static void idev_evdev_disable(idev_evdev *evdev) {
- assert(evdev);
- assert(evdev->fd_src);
- assert(evdev->idle_src);
-
- if (!evdev->running)
- return;
-
- evdev->running = false;
- idev_evdev_feed_resync(evdev);
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
-}
-
-static int idev_evdev_resume(idev_evdev *evdev, int dev_fd) {
- idev_element *e = &evdev->element;
- _cleanup_close_ int fd = dev_fd;
- int r, flags;
-
- if (fd < 0 || evdev->fd == fd) {
- fd = -1;
- idev_evdev_enable(evdev);
- return 0;
- }
-
- idev_evdev_pause(evdev, true);
- log_debug("idev-evdev: %s/%s: resume", e->session->name, e->name);
-
- r = fd_nonblock(fd, true);
- if (r < 0)
- return r;
-
- r = fd_cloexec(fd, true);
- if (r < 0)
- return r;
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0)
- return -errno;
-
- flags &= O_ACCMODE;
- if (flags == O_WRONLY)
- return -EACCES;
-
- evdev->element.readable = true;
- evdev->element.writable = !(flags & O_RDONLY);
-
- /*
- * TODO: We *MUST* re-sync the device so we get a delta of the changed
- * state while we didn't read events from the device. This works just
- * fine with libevdev_change_fd(), however, libevdev_new_from_fd() (or
- * libevdev_set_fd()) don't pass us events for the initial device
- * state. So even if we force a re-sync, we will not get the delta for
- * the initial device state.
- * We really need to fix libevdev to support that!
- */
- if (evdev->evdev)
- r = libevdev_change_fd(evdev->evdev, fd);
- else
- r = libevdev_new_from_fd(fd, &evdev->evdev);
-
- if (r < 0)
- return r;
-
- r = sd_event_add_io(e->session->context->event,
- &evdev->fd_src,
- fd,
- EPOLLHUP | EPOLLERR | EPOLLIN,
- idev_evdev_event_fn,
- evdev);
- if (r < 0)
- return r;
-
- r = sd_event_add_defer(e->session->context->event,
- &evdev->idle_src,
- idev_evdev_idle_fn,
- evdev);
- if (r < 0) {
- evdev->fd_src = sd_event_source_unref(evdev->fd_src);
- return r;
- }
-
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
-
- evdev->unsync = true;
- evdev->fd = fd;
- fd = -1;
-
- idev_evdev_enable(evdev);
- return 0;
-}
-
-static void idev_evdev_pause(idev_evdev *evdev, bool release) {
- idev_element *e = &evdev->element;
-
- if (evdev->fd < 0)
- return;
-
- log_debug("idev-evdev: %s/%s: pause", e->session->name, e->name);
-
- idev_evdev_disable(evdev);
- if (release) {
- evdev->idle_src = sd_event_source_unref(evdev->idle_src);
- evdev->fd_src = sd_event_source_unref(evdev->fd_src);
- evdev->fd = safe_close(evdev->fd);
- }
-}
-
-/*
- * Unmanaged Evdev Element
- * The unmanaged evdev element opens the evdev node for a given input device
- * directly (/dev/input/eventX) and thus needs sufficient privileges. It opens
- * the device only if we really require it and releases it as soon as we're
- * disabled or closed.
- * The unmanaged element can be used in all situations where you have direct
- * access to input device nodes. Unlike managed evdev elements, it can be used
- * outside of user sessions and in emergency situations where logind is not
- * available.
- */
-
-static void unmanaged_evdev_resume(idev_element *e) {
- unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
- int r, fd;
-
- /*
- * Unmanaged devices can be acquired on-demand. Therefore, don't
- * acquire it unless someone opened the device *and* we're enabled.
- */
- if (e->n_open < 1 || !e->enabled)
- return;
-
- fd = eu->evdev.fd;
- if (fd < 0) {
- fd = open(eu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- if (errno != EACCES && errno != EPERM) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m",
- e->session->name, e->name, eu->devnode);
- return;
- }
-
- fd = open(eu->devnode, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m",
- e->session->name, e->name, eu->devnode);
- return;
- }
-
- e->readable = true;
- e->writable = false;
- } else {
- e->readable = true;
- e->writable = true;
- }
- }
-
- r = idev_evdev_resume(&eu->evdev, fd);
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
- e->session->name, e->name);
-}
-
-static void unmanaged_evdev_pause(idev_element *e) {
- unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
-
- /*
- * Release the device if the device is disabled or there is no-one who
- * opened it. This guarantees we stay only available if we're opened
- * *and* enabled.
- */
-
- idev_evdev_pause(&eu->evdev, true);
-}
-
-static int unmanaged_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
- _cleanup_(idev_element_freep) idev_element *e = NULL;
- char name[IDEV_EVDEV_NAME_MAX];
- unmanaged_evdev *eu;
- const char *devnode;
- dev_t devnum;
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnode = udev_device_get_devnode(ud);
- devnum = udev_device_get_devnum(ud);
- if (!devnode || devnum == 0)
- return -ENODEV;
-
- idev_evdev_name(name, devnum);
-
- eu = new0(unmanaged_evdev, 1);
- if (!eu)
- return -ENOMEM;
-
- e = &eu->evdev.element;
- eu->evdev = IDEV_EVDEV_INIT(&unmanaged_evdev_vtable, s);
-
- eu->devnode = strdup(devnode);
- if (!eu->devnode)
- return -ENOMEM;
-
- r = idev_element_add(e, name);
- if (r < 0)
- return r;
-
- if (out)
- *out = e;
- e = NULL;
- return 0;
-}
-
-static void unmanaged_evdev_free(idev_element *e) {
- unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
-
- idev_evdev_destroy(&eu->evdev);
- free(eu->devnode);
- free(eu);
-}
-
-static const idev_element_vtable unmanaged_evdev_vtable = {
- .free = unmanaged_evdev_free,
- .enable = unmanaged_evdev_resume,
- .disable = unmanaged_evdev_pause,
- .open = unmanaged_evdev_resume,
- .close = unmanaged_evdev_pause,
-};
-
-/*
- * Managed Evdev Element
- * The managed evdev element uses systemd-logind to acquire evdev devices. This
- * means, we do not open the device node /dev/input/eventX directly. Instead,
- * logind passes us a file-descriptor whenever our session is activated. Thus,
- * we don't need access to the device node directly.
- * Furthermore, whenever the session is put asleep, logind revokes the
- * file-descriptor so we loose access to the device.
- * Managed evdev elements should be preferred over unmanaged elements whenever
- * you run inside a user session with exclusive device access.
- */
-
-static int managed_evdev_take_device_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_evdev *em = userdata;
- idev_element *e = &em->evdev.element;
- idev_session *s = e->session;
- int r, paused, fd;
-
- em->slot_take_device = sd_bus_slot_unref(em->slot_take_device);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("idev-evdev: %s/%s: TakeDevice failed: %s: %s",
- s->name, e->name, error->name, error->message);
- return 0;
- }
-
- em->acquired = true;
-
- r = sd_bus_message_read(reply, "hb", &fd, &paused);
- if (r < 0) {
- log_debug("idev-evdev: %s/%s: erroneous TakeDevice reply", s->name, e->name);
- return 0;
- }
-
- /* If the device is paused, ignore it; we will get the next fd via
- * ResumeDevice signals. */
- if (paused)
- return 0;
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name);
- return 0;
- }
-
- r = idev_evdev_resume(&em->evdev, fd);
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
- s->name, e->name);
-
- return 0;
-}
-
-static void managed_evdev_enable(idev_element *e) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- idev_context *c = s->context;
- int r;
-
- /*
- * Acquiring managed devices is heavy, so do it only once we're
- * enabled *and* opened by someone.
- */
- if (e->n_open < 1 || !e->enabled)
- return;
-
- /* bail out if already pending */
- if (em->requested)
- return;
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- s->path,
- "org.freedesktop.login1.Session",
- "TakeDevice");
- if (r < 0)
- goto error;
-
- r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
- if (r < 0)
- goto error;
-
- r = sd_bus_call_async(c->sysbus,
- &em->slot_take_device,
- m,
- managed_evdev_take_device_fn,
- em,
- 0);
- if (r < 0)
- goto error;
-
- em->requested = true;
- return;
-
-error:
- log_debug_errno(r, "idev-evdev: %s/%s: cannot send TakeDevice request: %m",
- s->name, e->name);
-}
-
-static void managed_evdev_disable(idev_element *e) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- idev_context *c = s->context;
- int r;
-
- /*
- * Releasing managed devices is heavy. Once acquired, we get
- * notifications for sleep/wake-up events, so there's no reason to
- * release it if disabled but opened. However, if a device is closed,
- * we release it immediately as we don't care for sleep/wake-up events
- * then (even if we're actually enabled).
- */
-
- idev_evdev_pause(&em->evdev, false);
-
- if (e->n_open > 0 || !em->requested)
- return;
-
- /*
- * If TakeDevice() is pending or was successful, make sure to
- * release the device again. We don't care for return-values,
- * so send it without waiting or callbacks.
- * If a failed TakeDevice() is pending, but someone else took
- * the device on the same bus-connection, we might incorrectly
- * release their device. This is an unlikely race, though.
- * Furthermore, you really shouldn't have two users of the
- * controller-API on the same session, on the same devices, *AND* on
- * the same bus-connection. So we don't care for that race..
- */
-
- idev_evdev_pause(&em->evdev, true);
- em->requested = false;
-
- if (!em->acquired && !em->slot_take_device)
- return;
-
- em->slot_take_device = sd_bus_slot_unref(em->slot_take_device);
- em->acquired = false;
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- s->path,
- "org.freedesktop.login1.Session",
- "ReleaseDevice");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
- if (r >= 0)
- r = sd_bus_send(c->sysbus, m, NULL);
- }
-
- if (r < 0 && r != -ENOTCONN)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot send ReleaseDevice: %m",
- s->name, e->name);
-}
-
-static void managed_evdev_resume(idev_element *e, int fd) {
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- int r;
-
- /*
- * We get ResumeDevice signals whenever logind resumed a previously
- * paused device. The arguments contain the major/minor number of the
- * related device and a new file-descriptor for the freshly opened
- * device-node. We take the file-descriptor and immediately resume the
- * device.
- */
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
- s->name, e->name);
- return;
- }
-
- r = idev_evdev_resume(&em->evdev, fd);
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
- s->name, e->name);
-
- return;
-}
-
-static void managed_evdev_pause(idev_element *e, const char *mode) {
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- idev_context *c = s->context;
- int r;
-
- /*
- * We get PauseDevice() signals from logind whenever a device we
- * requested was, or is about to be, paused. Arguments are major/minor
- * number of the device and the mode of the operation.
- * We treat it as asynchronous access-revocation (as if we got HUP on
- * the device fd). Note that we might have already treated the HUP
- * event via EPOLLHUP, whichever comes first.
- *
- * @mode can be one of the following:
- * "pause": The device is about to be paused. We must react
- * immediately and respond with PauseDeviceComplete(). Once
- * we replied, logind will pause the device. Note that
- * logind might apply any kind of timeout and force pause
- * the device if we don't respond in a timely manner. In
- * this case, we will receive a second PauseDevice event
- * with @mode set to "force" (or similar).
- * "force": The device was disabled forecfully by logind. Access is
- * already revoked. This is just an asynchronous
- * notification so we can put the device asleep (in case
- * we didn't already notice the access revocation).
- * "gone": This is like "force" but is sent if the device was
- * paused due to a device-removal event.
- *
- * We always handle PauseDevice signals as "force" as we properly
- * support asynchronous access revocation, anyway. But in case logind
- * sent mode "pause", we also call PauseDeviceComplete() to immediately
- * acknowledge the request.
- */
-
- idev_evdev_pause(&em->evdev, true);
-
- if (streq(mode, "pause")) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- /*
- * Sending PauseDeviceComplete() is racy if logind triggers the
- * timeout. That is, if we take too long and logind pauses the
- * device by sending a forced PauseDevice, our
- * PauseDeviceComplete call will be stray. That's fine, though.
- * logind ignores such stray calls. Only if logind also sent a
- * further PauseDevice() signal, it might match our call
- * incorrectly to the newer PauseDevice(). That's fine, too, as
- * we handle that event asynchronously, anyway. Therefore,
- * whatever happens, we're fine. Yay!
- */
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- s->path,
- "org.freedesktop.login1.Session",
- "PauseDeviceComplete");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
- if (r >= 0)
- r = sd_bus_send(c->sysbus, m, NULL);
- }
-
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot send PauseDeviceComplete: %m",
- s->name, e->name);
- }
-}
-
-static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
- _cleanup_(idev_element_freep) idev_element *e = NULL;
- char name[IDEV_EVDEV_NAME_MAX];
- managed_evdev *em;
- dev_t devnum;
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(s->managed, -EINVAL);
- assert_return(s->context->sysbus, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return -ENODEV;
-
- idev_evdev_name(name, devnum);
-
- em = new0(managed_evdev, 1);
- if (!em)
- return -ENOMEM;
-
- e = &em->evdev.element;
- em->evdev = IDEV_EVDEV_INIT(&managed_evdev_vtable, s);
- em->devnum = devnum;
-
- r = idev_element_add(e, name);
- if (r < 0)
- return r;
-
- if (out)
- *out = e;
- e = NULL;
- return 0;
-}
-
-static void managed_evdev_free(idev_element *e) {
- managed_evdev *em = managed_evdev_from_element(e);
-
- idev_evdev_destroy(&em->evdev);
- free(em);
-}
-
-static const idev_element_vtable managed_evdev_vtable = {
- .free = managed_evdev_free,
- .enable = managed_evdev_enable,
- .disable = managed_evdev_disable,
- .open = managed_evdev_enable,
- .close = managed_evdev_disable,
- .resume = managed_evdev_resume,
- .pause = managed_evdev_pause,
-};
-
-/*
- * Generic Constructor
- * Instead of relying on the caller to choose between managed and unmanaged
- * evdev devices, the idev_evdev_new() constructor does that for you (by
- * looking at s->managed).
- */
-
-bool idev_is_evdev(idev_element *e) {
- return e && (e->vtable == &unmanaged_evdev_vtable ||
- e->vtable == &managed_evdev_vtable);
-}
-
-idev_element *idev_find_evdev(idev_session *s, dev_t devnum) {
- char name[IDEV_EVDEV_NAME_MAX];
-
- assert_return(s, NULL);
- assert_return(devnum != 0, NULL);
-
- idev_evdev_name(name, devnum);
- return idev_find_element(s, name);
-}
-
-int idev_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
- assert_return(s, -EINVAL);
- assert_return(ud, -EINVAL);
-
- return s->managed ? managed_evdev_new(out, s, ud) : unmanaged_evdev_new(out, s, ud);
-}
diff --git a/src/libsystemd-terminal/idev-internal.h b/src/libsystemd-terminal/idev-internal.h
deleted file mode 100644
index a02a16c408..0000000000
--- a/src/libsystemd-terminal/idev-internal.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <xkbcommon/xkbcommon.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-#include "util.h"
-#include "idev.h"
-
-typedef struct idev_link idev_link;
-typedef struct idev_device_vtable idev_device_vtable;
-typedef struct idev_element idev_element;
-typedef struct idev_element_vtable idev_element_vtable;
-
-/*
- * Evdev Elements
- */
-
-bool idev_is_evdev(idev_element *e);
-idev_element *idev_find_evdev(idev_session *s, dev_t devnum);
-int idev_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud);
-
-/*
- * Keyboard Devices
- */
-
-bool idev_is_keyboard(idev_device *d);
-idev_device *idev_find_keyboard(idev_session *s, const char *name);
-int idev_keyboard_new(idev_device **out, idev_session *s, const char *name);
-
-/*
- * Element Links
- */
-
-struct idev_link {
- /* element-to-device connection */
- LIST_FIELDS(idev_link, links_by_element);
- idev_element *element;
-
- /* device-to-element connection */
- LIST_FIELDS(idev_link, links_by_device);
- idev_device *device;
-};
-
-/*
- * Devices
- */
-
-struct idev_device_vtable {
- void (*free) (idev_device *d);
- void (*attach) (idev_device *d, idev_link *l);
- void (*detach) (idev_device *d, idev_link *l);
- int (*feed) (idev_device *d, idev_data *data);
-};
-
-struct idev_device {
- const idev_device_vtable *vtable;
- idev_session *session;
- char *name;
-
- LIST_HEAD(idev_link, links);
-
- bool public : 1;
- bool enabled : 1;
-};
-
-#define IDEV_DEVICE_INIT(_vtable, _session) ((idev_device){ \
- .vtable = (_vtable), \
- .session = (_session), \
- })
-
-idev_device *idev_find_device(idev_session *s, const char *name);
-
-int idev_device_add(idev_device *d, const char *name);
-idev_device *idev_device_free(idev_device *d);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_device*, idev_device_free);
-
-int idev_device_feed(idev_device *d, idev_data *data);
-void idev_device_feedback(idev_device *d, idev_data *data);
-
-/*
- * Elements
- */
-
-struct idev_element_vtable {
- void (*free) (idev_element *e);
- void (*enable) (idev_element *e);
- void (*disable) (idev_element *e);
- void (*open) (idev_element *e);
- void (*close) (idev_element *e);
- void (*resume) (idev_element *e, int fd);
- void (*pause) (idev_element *e, const char *mode);
- void (*feedback) (idev_element *e, idev_data *data);
-};
-
-struct idev_element {
- const idev_element_vtable *vtable;
- idev_session *session;
- unsigned long n_open;
- char *name;
-
- LIST_HEAD(idev_link, links);
-
- bool enabled : 1;
- bool readable : 1;
- bool writable : 1;
-};
-
-#define IDEV_ELEMENT_INIT(_vtable, _session) ((idev_element){ \
- .vtable = (_vtable), \
- .session = (_session), \
- })
-
-idev_element *idev_find_element(idev_session *s, const char *name);
-
-int idev_element_add(idev_element *e, const char *name);
-idev_element *idev_element_free(idev_element *e);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_element*, idev_element_free);
-
-int idev_element_feed(idev_element *e, idev_data *data);
-void idev_element_feedback(idev_element *e, idev_data *data);
-
-/*
- * Sessions
- */
-
-struct idev_session {
- idev_context *context;
- char *name;
- char *path;
- sd_bus_slot *slot_resume_device;
- sd_bus_slot *slot_pause_device;
-
- Hashmap *element_map;
- Hashmap *device_map;
-
- idev_event_fn event_fn;
- void *userdata;
-
- bool custom : 1;
- bool managed : 1;
- bool enabled : 1;
-};
-
-idev_session *idev_find_session(idev_context *c, const char *name);
-int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data);
-
-/*
- * Contexts
- */
-
-struct idev_context {
- unsigned long ref;
- sd_event *event;
- sd_bus *sysbus;
-
- Hashmap *session_map;
- Hashmap *data_map;
-};
diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
deleted file mode 100644
index 93f49e9458..0000000000
--- a/src/libsystemd-terminal/idev-keyboard.c
+++ /dev/null
@@ -1,1159 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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 <stdbool.h>
-#include <stdlib.h>
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-compose.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "util.h"
-#include "bus-util.h"
-#include "idev.h"
-#include "idev-internal.h"
-#include "term-internal.h"
-
-typedef struct kbdtbl kbdtbl;
-typedef struct kbdmap kbdmap;
-typedef struct kbdctx kbdctx;
-typedef struct idev_keyboard idev_keyboard;
-
-struct kbdtbl {
- unsigned long ref;
- struct xkb_compose_table *xkb_compose_table;
-};
-
-struct kbdmap {
- unsigned long ref;
- struct xkb_keymap *xkb_keymap;
- xkb_mod_index_t modmap[IDEV_KBDMOD_CNT];
- xkb_led_index_t ledmap[IDEV_KBDLED_CNT];
-};
-
-struct kbdctx {
- unsigned long ref;
- idev_context *context;
- struct xkb_context *xkb_context;
- struct kbdmap *kbdmap;
- struct kbdtbl *kbdtbl;
-
- sd_bus_slot *slot_locale_props_changed;
- sd_bus_slot *slot_locale_get_all;
-
- char *locale_lang;
- char *locale_x11_model;
- char *locale_x11_layout;
- char *locale_x11_variant;
- char *locale_x11_options;
- char *last_x11_model;
- char *last_x11_layout;
- char *last_x11_variant;
- char *last_x11_options;
-};
-
-struct idev_keyboard {
- idev_device device;
- kbdctx *kbdctx;
- kbdmap *kbdmap;
- kbdtbl *kbdtbl;
-
- struct xkb_state *xkb_state;
- struct xkb_compose_state *xkb_compose;
-
- usec_t repeat_delay;
- usec_t repeat_rate;
- sd_event_source *repeat_timer;
-
- uint32_t n_syms;
- idev_data evdata;
- idev_data repdata;
- uint32_t *compose_res;
-
- bool repeating : 1;
-};
-
-#define keyboard_from_device(_d) container_of((_d), idev_keyboard, device)
-
-#define KBDCTX_KEY "keyboard.context" /* hashmap key for global kbdctx */
-#define KBDXKB_SHIFT (8) /* xkb shifts evdev key-codes by 8 */
-#define KBDKEY_UP (0) /* KEY UP event value */
-#define KBDKEY_DOWN (1) /* KEY DOWN event value */
-#define KBDKEY_REPEAT (2) /* KEY REPEAT event value */
-
-static const idev_device_vtable keyboard_vtable;
-
-static int keyboard_update_kbdmap(idev_keyboard *k);
-static int keyboard_update_kbdtbl(idev_keyboard *k);
-
-/*
- * Keyboard Compose Tables
- */
-
-static kbdtbl *kbdtbl_ref(kbdtbl *kt) {
- if (kt) {
- assert_return(kt->ref > 0, NULL);
- ++kt->ref;
- }
-
- return kt;
-}
-
-static kbdtbl *kbdtbl_unref(kbdtbl *kt) {
- if (!kt)
- return NULL;
-
- assert_return(kt->ref > 0, NULL);
-
- if (--kt->ref > 0)
- return NULL;
-
- xkb_compose_table_unref(kt->xkb_compose_table);
- free(kt);
-
- return 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdtbl*, kbdtbl_unref);
-
-static int kbdtbl_new_from_locale(kbdtbl **out, kbdctx *kc, const char *locale) {
- _cleanup_(kbdtbl_unrefp) kbdtbl *kt = NULL;
-
- assert_return(out, -EINVAL);
- assert_return(locale, -EINVAL);
-
- kt = new0(kbdtbl, 1);
- if (!kt)
- return -ENOMEM;
-
- kt->ref = 1;
-
- kt->xkb_compose_table = xkb_compose_table_new_from_locale(kc->xkb_context,
- locale,
- XKB_COMPOSE_COMPILE_NO_FLAGS);
- if (!kt->xkb_compose_table)
- return errno > 0 ? -errno : -EFAULT;
-
- *out = kt;
- kt = NULL;
- return 0;
-}
-
-/*
- * Keyboard Keymaps
- */
-
-static const char * const kbdmap_modmap[IDEV_KBDMOD_CNT] = {
- [IDEV_KBDMOD_IDX_SHIFT] = XKB_MOD_NAME_SHIFT,
- [IDEV_KBDMOD_IDX_CTRL] = XKB_MOD_NAME_CTRL,
- [IDEV_KBDMOD_IDX_ALT] = XKB_MOD_NAME_ALT,
- [IDEV_KBDMOD_IDX_LINUX] = XKB_MOD_NAME_LOGO,
- [IDEV_KBDMOD_IDX_CAPS] = XKB_MOD_NAME_CAPS,
-};
-
-static const char * const kbdmap_ledmap[IDEV_KBDLED_CNT] = {
- [IDEV_KBDLED_IDX_NUM] = XKB_LED_NAME_NUM,
- [IDEV_KBDLED_IDX_CAPS] = XKB_LED_NAME_CAPS,
- [IDEV_KBDLED_IDX_SCROLL] = XKB_LED_NAME_SCROLL,
-};
-
-static kbdmap *kbdmap_ref(kbdmap *km) {
- assert_return(km, NULL);
- assert_return(km->ref > 0, NULL);
-
- ++km->ref;
- return km;
-}
-
-static kbdmap *kbdmap_unref(kbdmap *km) {
- if (!km)
- return NULL;
-
- assert_return(km->ref > 0, NULL);
-
- if (--km->ref > 0)
- return NULL;
-
- xkb_keymap_unref(km->xkb_keymap);
- free(km);
-
- return 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdmap*, kbdmap_unref);
-
-static int kbdmap_new_from_names(kbdmap **out,
- kbdctx *kc,
- const char *model,
- const char *layout,
- const char *variant,
- const char *options) {
- _cleanup_(kbdmap_unrefp) kbdmap *km = NULL;
- struct xkb_rule_names rmlvo = { };
- unsigned int i;
-
- assert_return(out, -EINVAL);
-
- km = new0(kbdmap, 1);
- if (!km)
- return -ENOMEM;
-
- km->ref = 1;
-
- rmlvo.rules = "evdev";
- rmlvo.model = model;
- rmlvo.layout = layout;
- rmlvo.variant = variant;
- rmlvo.options = options;
-
- errno = 0;
- km->xkb_keymap = xkb_keymap_new_from_names(kc->xkb_context, &rmlvo, 0);
- if (!km->xkb_keymap)
- return errno > 0 ? -errno : -EFAULT;
-
- for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
- const char *t = kbdmap_modmap[i];
-
- if (t)
- km->modmap[i] = xkb_keymap_mod_get_index(km->xkb_keymap, t);
- else
- km->modmap[i] = XKB_MOD_INVALID;
- }
-
- for (i = 0; i < IDEV_KBDLED_CNT; ++i) {
- const char *t = kbdmap_ledmap[i];
-
- if (t)
- km->ledmap[i] = xkb_keymap_led_get_index(km->xkb_keymap, t);
- else
- km->ledmap[i] = XKB_LED_INVALID;
- }
-
- *out = km;
- km = NULL;
- return 0;
-}
-
-/*
- * Keyboard Context
- */
-
-static int kbdctx_refresh_compose_table(kbdctx *kc, const char *lang) {
- kbdtbl *kt;
- idev_session *s;
- idev_device *d;
- Iterator i, j;
- int r;
-
- if (!lang)
- lang = "C";
-
- if (streq_ptr(kc->locale_lang, lang))
- return 0;
-
- r = free_and_strdup(&kc->locale_lang, lang);
- if (r < 0)
- return r;
-
- log_debug("idev-keyboard: new default compose table: [ %s ]", lang);
-
- r = kbdtbl_new_from_locale(&kt, kc, lang);
- if (r < 0) {
- /* TODO: We need to catch the case where no compose-file is
- * available. xkb doesn't tell us so far.. so we must not treat
- * it as a hard-failure but just continue. Preferably, we want
- * xkb to tell us exactly whether compilation failed or whether
- * there is no compose file available for this locale. */
- log_debug_errno(r, "idev-keyboard: cannot load compose-table for '%s': %m",
- lang);
- r = 0;
- kt = NULL;
- }
-
- kbdtbl_unref(kc->kbdtbl);
- kc->kbdtbl = kt;
-
- HASHMAP_FOREACH(s, kc->context->session_map, i)
- HASHMAP_FOREACH(d, s->device_map, j)
- if (idev_is_keyboard(d))
- keyboard_update_kbdtbl(keyboard_from_device(d));
-
- return 0;
-}
-
-static void move_str(char **dest, char **src) {
- free(*dest);
- *dest = *src;
- *src = NULL;
-}
-
-static int kbdctx_refresh_keymap(kbdctx *kc) {
- idev_session *s;
- idev_device *d;
- Iterator i, j;
- kbdmap *km;
- int r;
-
- if (kc->kbdmap &&
- streq_ptr(kc->locale_x11_model, kc->last_x11_model) &&
- streq_ptr(kc->locale_x11_layout, kc->last_x11_layout) &&
- streq_ptr(kc->locale_x11_variant, kc->last_x11_variant) &&
- streq_ptr(kc->locale_x11_options, kc->last_x11_options))
- return 0 ;
-
- move_str(&kc->last_x11_model, &kc->locale_x11_model);
- move_str(&kc->last_x11_layout, &kc->locale_x11_layout);
- move_str(&kc->last_x11_variant, &kc->locale_x11_variant);
- move_str(&kc->last_x11_options, &kc->locale_x11_options);
-
- log_debug("idev-keyboard: new default keymap: [%s / %s / %s / %s]",
- kc->last_x11_model, kc->last_x11_layout, kc->last_x11_variant, kc->last_x11_options);
-
- /* TODO: add a fallback keymap that's compiled-in */
- r = kbdmap_new_from_names(&km, kc, kc->last_x11_model, kc->last_x11_layout,
- kc->last_x11_variant, kc->last_x11_options);
- if (r < 0)
- return log_debug_errno(r, "idev-keyboard: cannot create keymap from locale1: %m");
-
- kbdmap_unref(kc->kbdmap);
- kc->kbdmap = km;
-
- HASHMAP_FOREACH(s, kc->context->session_map, i)
- HASHMAP_FOREACH(d, s->device_map, j)
- if (idev_is_keyboard(d))
- keyboard_update_kbdmap(keyboard_from_device(d));
-
- return 0;
-}
-
-static int kbdctx_set_locale(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- kbdctx *kc = userdata;
- const char *s, *ctype = NULL, *lang = NULL;
- int r;
-
- r = sd_bus_message_enter_container(m, 'a', "s");
- if (r < 0)
- goto error;
-
- while ((r = sd_bus_message_read(m, "s", &s)) > 0) {
- if (!ctype)
- ctype = startswith(s, "LC_CTYPE=");
- if (!lang)
- lang = startswith(s, "LANG=");
- }
-
- if (r < 0)
- goto error;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- goto error;
-
- kbdctx_refresh_compose_table(kc, ctype ? : lang);
- r = 0;
-
-error:
- if (r < 0)
- log_debug_errno(r, "idev-keyboard: cannot parse locale property from locale1: %m");
-
- return r;
-}
-
-static const struct bus_properties_map kbdctx_locale_map[] = {
- { "Locale", "as", kbdctx_set_locale, 0 },
- { "X11Model", "s", NULL, offsetof(kbdctx, locale_x11_model) },
- { "X11Layout", "s", NULL, offsetof(kbdctx, locale_x11_layout) },
- { "X11Variant", "s", NULL, offsetof(kbdctx, locale_x11_variant) },
- { "X11Options", "s", NULL, offsetof(kbdctx, locale_x11_options) },
- { },
-};
-
-static int kbdctx_locale_get_all_fn(sd_bus_message *m,
- void *userdata,
- sd_bus_error *ret_err) {
- kbdctx *kc = userdata;
- int r;
-
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
- if (sd_bus_message_is_method_error(m, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(m);
-
- log_debug("idev-keyboard: GetAll() on locale1 failed: %s: %s",
- error->name, error->message);
- return 0;
- }
-
- r = bus_message_map_all_properties(m, kbdctx_locale_map, kc);
- if (r < 0) {
- log_debug("idev-keyboard: erroneous GetAll() reply from locale1");
- return 0;
- }
-
- kbdctx_refresh_keymap(kc);
- return 0;
-}
-
-static int kbdctx_query_locale(kbdctx *kc) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
- r = sd_bus_message_new_method_call(kc->context->sysbus,
- &m,
- "org.freedesktop.locale1",
- "/org/freedesktop/locale1",
- "org.freedesktop.DBus.Properties",
- "GetAll");
- if (r < 0)
- goto error;
-
- r = sd_bus_message_append(m, "s", "org.freedesktop.locale1");
- if (r < 0)
- goto error;
-
- r = sd_bus_call_async(kc->context->sysbus,
- &kc->slot_locale_get_all,
- m,
- kbdctx_locale_get_all_fn,
- kc,
- 0);
- if (r < 0)
- goto error;
-
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: cannot send GetAll to locale1: %m");
-}
-
-static int kbdctx_locale_props_changed_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_err) {
- kbdctx *kc = userdata;
- int r;
-
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
- /* skip interface name */
- r = sd_bus_message_skip(signal, "s");
- if (r < 0)
- goto error;
-
- r = bus_message_map_properties_changed(signal, kbdctx_locale_map, kc);
- if (r < 0)
- goto error;
-
- if (r > 0) {
- r = kbdctx_query_locale(kc);
- if (r < 0)
- return r;
- }
-
- kbdctx_refresh_keymap(kc);
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: cannot handle PropertiesChanged from locale1: %m");
-}
-
-static int kbdctx_setup_bus(kbdctx *kc) {
- int r;
-
- r = sd_bus_add_match(kc->context->sysbus,
- &kc->slot_locale_props_changed,
- "type='signal',"
- "sender='org.freedesktop.locale1',"
- "interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged',"
- "path='/org/freedesktop/locale1'",
- kbdctx_locale_props_changed_fn,
- kc);
- if (r < 0)
- return log_debug_errno(r, "idev-keyboard: cannot setup locale1 link: %m");
-
- return kbdctx_query_locale(kc);
-}
-
-static void kbdctx_log_fn(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
- char buf[LINE_MAX];
- int sd_lvl;
-
- if (lvl >= XKB_LOG_LEVEL_DEBUG)
- sd_lvl = LOG_DEBUG;
- else if (lvl >= XKB_LOG_LEVEL_INFO)
- sd_lvl = LOG_INFO;
- else if (lvl >= XKB_LOG_LEVEL_WARNING)
- sd_lvl = LOG_INFO; /* most XKB warnings really are informational */
- else
- /* XKB_LOG_LEVEL_ERROR and worse */
- sd_lvl = LOG_ERR;
-
- snprintf(buf, sizeof(buf), "idev-xkb: %s", format);
- log_internalv(sd_lvl, 0, __FILE__, __LINE__, __func__, buf, args);
-}
-
-static kbdctx *kbdctx_ref(kbdctx *kc) {
- assert_return(kc, NULL);
- assert_return(kc->ref > 0, NULL);
-
- ++kc->ref;
- return kc;
-}
-
-static kbdctx *kbdctx_unref(kbdctx *kc) {
- if (!kc)
- return NULL;
-
- assert_return(kc->ref > 0, NULL);
-
- if (--kc->ref > 0)
- return NULL;
-
- free(kc->last_x11_options);
- free(kc->last_x11_variant);
- free(kc->last_x11_layout);
- free(kc->last_x11_model);
- free(kc->locale_x11_options);
- free(kc->locale_x11_variant);
- free(kc->locale_x11_layout);
- free(kc->locale_x11_model);
- free(kc->locale_lang);
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
- kc->slot_locale_props_changed = sd_bus_slot_unref(kc->slot_locale_props_changed);
- kc->kbdtbl = kbdtbl_unref(kc->kbdtbl);
- kc->kbdmap = kbdmap_unref(kc->kbdmap);
- xkb_context_unref(kc->xkb_context);
- hashmap_remove_value(kc->context->data_map, KBDCTX_KEY, kc);
- free(kc);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdctx*, kbdctx_unref);
-
-static int kbdctx_new(kbdctx **out, idev_context *c) {
- _cleanup_(kbdctx_unrefp) kbdctx *kc = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(c, -EINVAL);
-
- kc = new0(kbdctx, 1);
- if (!kc)
- return -ENOMEM;
-
- kc->ref = 1;
- kc->context = c;
-
- errno = 0;
- kc->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- if (!kc->xkb_context)
- return errno > 0 ? -errno : -EFAULT;
-
- xkb_context_set_log_fn(kc->xkb_context, kbdctx_log_fn);
- xkb_context_set_log_level(kc->xkb_context, XKB_LOG_LEVEL_DEBUG);
-
- r = kbdctx_refresh_keymap(kc);
- if (r < 0)
- return r;
-
- r = kbdctx_refresh_compose_table(kc, NULL);
- if (r < 0)
- return r;
-
- if (c->sysbus) {
- r = kbdctx_setup_bus(kc);
- if (r < 0)
- return r;
- }
-
- r = hashmap_put(c->data_map, KBDCTX_KEY, kc);
- if (r < 0)
- return r;
-
- *out = kc;
- kc = NULL;
- return 0;
-}
-
-static int get_kbdctx(idev_context *c, kbdctx **out) {
- kbdctx *kc;
-
- assert_return(c, -EINVAL);
- assert_return(out, -EINVAL);
-
- kc = hashmap_get(c->data_map, KBDCTX_KEY);
- if (kc) {
- *out = kbdctx_ref(kc);
- return 0;
- }
-
- return kbdctx_new(out, c);
-}
-
-/*
- * Keyboard Devices
- */
-
-bool idev_is_keyboard(idev_device *d) {
- return d && d->vtable == &keyboard_vtable;
-}
-
-idev_device *idev_find_keyboard(idev_session *s, const char *name) {
- char *kname;
-
- assert_return(s, NULL);
- assert_return(name, NULL);
-
- kname = strjoina("keyboard/", name);
- return hashmap_get(s->device_map, kname);
-}
-
-static int keyboard_raise_data(idev_keyboard *k, idev_data *data) {
- idev_device *d = &k->device;
- int r;
-
- r = idev_session_raise_device_data(d->session, d, data);
- if (r < 0)
- log_debug_errno(r, "idev-keyboard: %s/%s: error while raising data event: %m",
- d->session->name, d->name);
-
- return r;
-}
-
-static int keyboard_resize_bufs(idev_keyboard *k, uint32_t n_syms) {
- uint32_t *t;
-
- if (n_syms <= k->n_syms)
- return 0;
-
- t = realloc(k->compose_res, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->compose_res = t;
-
- t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->evdata.keyboard.keysyms = t;
-
- t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->evdata.keyboard.codepoints = t;
-
- t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->repdata.keyboard.keysyms = t;
-
- t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->repdata.keyboard.codepoints = t;
-
- k->n_syms = n_syms;
- return 0;
-}
-
-static unsigned int keyboard_read_compose(idev_keyboard *k, const xkb_keysym_t **out) {
- _cleanup_free_ char *t = NULL;
- term_utf8 u8 = { };
- char buf[256], *p;
- size_t flen = 0;
- int i, r;
-
- r = xkb_compose_state_get_utf8(k->xkb_compose, buf, sizeof(buf));
- if (r >= (int)sizeof(buf)) {
- t = malloc(r + 1);
- if (!t)
- return 0;
-
- xkb_compose_state_get_utf8(k->xkb_compose, t, r + 1);
- p = t;
- } else {
- p = buf;
- }
-
- for (i = 0; i < r; ++i) {
- uint32_t *ucs;
- size_t len, j;
-
- len = term_utf8_decode(&u8, &ucs, p[i]);
- if (len > 0) {
- r = keyboard_resize_bufs(k, flen + len);
- if (r < 0)
- return 0;
-
- for (j = 0; j < len; ++j)
- k->compose_res[flen++] = ucs[j];
- }
- }
-
- *out = k->compose_res;
- return flen;
-}
-
-static void keyboard_arm(idev_keyboard *k, usec_t usecs) {
- int r;
-
- if (usecs != 0) {
- usecs += now(CLOCK_MONOTONIC);
- r = sd_event_source_set_time(k->repeat_timer, usecs);
- if (r >= 0)
- sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_ONESHOT);
- } else {
- sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
- }
-}
-
-static int keyboard_repeat_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
- idev_keyboard *k = userdata;
-
- /* never feed REPEAT keys into COMPOSE */
-
- keyboard_arm(k, k->repeat_rate);
- return keyboard_raise_data(k, &k->repdata);
-}
-
-int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
- _cleanup_(idev_device_freep) idev_device *d = NULL;
- idev_keyboard *k;
- char *kname;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(s, -EINVAL);
- assert_return(name, -EINVAL);
-
- k = new0(idev_keyboard, 1);
- if (!k)
- return -ENOMEM;
-
- d = &k->device;
- k->device = IDEV_DEVICE_INIT(&keyboard_vtable, s);
- k->repeat_delay = 250 * USEC_PER_MSEC;
- k->repeat_rate = 30 * USEC_PER_MSEC;
-
- /* TODO: add key-repeat configuration */
-
- r = get_kbdctx(s->context, &k->kbdctx);
- if (r < 0)
- return r;
-
- r = keyboard_update_kbdmap(k);
- if (r < 0)
- return r;
-
- r = keyboard_update_kbdtbl(k);
- if (r < 0)
- return r;
-
- r = keyboard_resize_bufs(k, 8);
- if (r < 0)
- return r;
-
- r = sd_event_add_time(s->context->event,
- &k->repeat_timer,
- CLOCK_MONOTONIC,
- 0,
- 10 * USEC_PER_MSEC,
- keyboard_repeat_timer_fn,
- k);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
- if (r < 0)
- return r;
-
- kname = strjoina("keyboard/", name);
- r = idev_device_add(d, kname);
- if (r < 0)
- return r;
-
- if (out)
- *out = d;
- d = NULL;
- return 0;
-}
-
-static void keyboard_free(idev_device *d) {
- idev_keyboard *k = keyboard_from_device(d);
-
- xkb_compose_state_unref(k->xkb_compose);
- xkb_state_unref(k->xkb_state);
- free(k->repdata.keyboard.codepoints);
- free(k->repdata.keyboard.keysyms);
- free(k->evdata.keyboard.codepoints);
- free(k->evdata.keyboard.keysyms);
- free(k->compose_res);
- k->repeat_timer = sd_event_source_unref(k->repeat_timer);
- k->kbdtbl = kbdtbl_unref(k->kbdtbl);
- k->kbdmap = kbdmap_unref(k->kbdmap);
- k->kbdctx = kbdctx_unref(k->kbdctx);
- free(k);
-}
-
-static int8_t guess_ascii(struct xkb_state *state, uint32_t code, uint32_t n_syms, const uint32_t *syms) {
- xkb_layout_index_t n_lo, lo;
- xkb_level_index_t lv;
- struct xkb_keymap *keymap;
- const xkb_keysym_t *s;
- int num;
-
- if (n_syms == 1 && syms[0] < 128 && syms[0] > 0)
- return syms[0];
-
- keymap = xkb_state_get_keymap(state);
- n_lo = xkb_keymap_num_layouts_for_key(keymap, code + KBDXKB_SHIFT);
-
- for (lo = 0; lo < n_lo; ++lo) {
- lv = xkb_state_key_get_level(state, code + KBDXKB_SHIFT, lo);
- num = xkb_keymap_key_get_syms_by_level(keymap, code + KBDXKB_SHIFT, lo, lv, &s);
- if (num == 1 && s[0] < 128 && s[0] > 0)
- return s[0];
- }
-
- return -1;
-}
-
-static int keyboard_fill(idev_keyboard *k,
- idev_data *dst,
- bool resync,
- uint16_t code,
- uint32_t value,
- uint32_t n_syms,
- const uint32_t *keysyms) {
- idev_data_keyboard *kev;
- uint32_t i;
- int r;
-
- assert(dst == &k->evdata || dst == &k->repdata);
-
- r = keyboard_resize_bufs(k, n_syms);
- if (r < 0)
- return r;
-
- dst->type = IDEV_DATA_KEYBOARD;
- dst->resync = resync;
- kev = &dst->keyboard;
- kev->ascii = guess_ascii(k->xkb_state, code, n_syms, keysyms);
- kev->value = value;
- kev->keycode = code;
- kev->mods = 0;
- kev->consumed_mods = 0;
- kev->n_syms = n_syms;
- memcpy(kev->keysyms, keysyms, sizeof(*keysyms) * n_syms);
-
- for (i = 0; i < n_syms; ++i) {
- kev->codepoints[i] = xkb_keysym_to_utf32(keysyms[i]);
- if (!kev->codepoints[i])
- kev->codepoints[i] = 0xffffffffUL;
- }
-
- for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
- if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
- continue;
-
- r = xkb_state_mod_index_is_active(k->xkb_state, k->kbdmap->modmap[i], XKB_STATE_MODS_EFFECTIVE);
- if (r > 0)
- kev->mods |= 1 << i;
-
- r = xkb_state_mod_index_is_consumed(k->xkb_state, code + KBDXKB_SHIFT, k->kbdmap->modmap[i]);
- if (r > 0)
- kev->consumed_mods |= 1 << i;
- }
-
- return 0;
-}
-
-static void keyboard_repeat(idev_keyboard *k) {
- idev_data *evdata = &k->evdata;
- idev_data *repdata = &k->repdata;
- idev_data_keyboard *evkbd = &evdata->keyboard;
- idev_data_keyboard *repkbd = &repdata->keyboard;
- const xkb_keysym_t *keysyms;
- idev_device *d = &k->device;
- bool repeats;
- int r, num;
-
- if (evdata->resync) {
- /*
- * We received a re-sync event. During re-sync, any number of
- * key-events may have been lost and sync-events may be
- * re-ordered. Always disable key-repeat for those events. Any
- * following event will trigger it again.
- */
-
- k->repeating = false;
- keyboard_arm(k, 0);
- return;
- }
-
- repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);
-
- if (k->repeating && repkbd->keycode == evkbd->keycode) {
- /*
- * We received an event for the key we currently repeat. If it
- * was released, stop key-repeat. Otherwise, ignore the event.
- */
-
- if (evkbd->value == KBDKEY_UP) {
- k->repeating = false;
- keyboard_arm(k, 0);
- }
- } else if (evkbd->value == KBDKEY_DOWN && repeats) {
- /*
- * We received a key-down event for a key that repeats. The
- * previous condition caught keys we already repeat, so we know
- * this is a different key or no key-repeat is running. Start
- * new key-repeat.
- */
-
- errno = 0;
- num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
- if (num < 0)
- r = errno > 0 ? errno : -EFAULT;
- else
- r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);
-
- if (r < 0) {
- log_debug_errno(r, "idev-keyboard: %s/%s: cannot set key-repeat: %m",
- d->session->name, d->name);
- k->repeating = false;
- keyboard_arm(k, 0);
- } else {
- k->repeating = true;
- keyboard_arm(k, k->repeat_delay);
- }
- } else if (k->repeating && !repeats) {
- /*
- * We received an event for a key that does not repeat, but we
- * currently repeat a previously received key. The new key is
- * usually a modifier, but might be any kind of key. In this
- * case, we continue repeating the old key, but update the
- * symbols according to the new state.
- */
-
- errno = 0;
- num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
- if (num < 0)
- r = errno > 0 ? errno : -EFAULT;
- else
- r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);
-
- if (r < 0) {
- log_debug_errno(r, "idev-keyboard: %s/%s: cannot update key-repeat: %m",
- d->session->name, d->name);
- k->repeating = false;
- keyboard_arm(k, 0);
- }
- }
-}
-
-static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
- struct input_event *ev = &data->evdev.event;
- enum xkb_state_component compch;
- enum xkb_compose_status cstatus;
- const xkb_keysym_t *keysyms;
- idev_device *d = &k->device;
- int num, r;
-
- if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
- return 0;
-
- /* TODO: We should audit xkb-actions, whether they need @resync as
- * flag. Most actions should just be executed, however, there might
- * be actions that depend on modifier-orders. Those should be
- * suppressed. */
-
- num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
- compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);
-
- if (compch & XKB_STATE_LEDS) {
- /* TODO: update LEDs */
- }
-
- if (num < 0) {
- r = num;
- goto error;
- }
-
- if (k->xkb_compose && ev->value == KBDKEY_DOWN) {
- if (num == 1 && !data->resync) {
- xkb_compose_state_feed(k->xkb_compose, keysyms[0]);
- cstatus = xkb_compose_state_get_status(k->xkb_compose);
- } else {
- cstatus = XKB_COMPOSE_CANCELLED;
- }
-
- switch (cstatus) {
- case XKB_COMPOSE_NOTHING:
- /* keep produced keysyms and forward unchanged */
- break;
- case XKB_COMPOSE_COMPOSING:
- /* consumed by compose-state, drop keysym */
- keysyms = NULL;
- num = 0;
- break;
- case XKB_COMPOSE_COMPOSED:
- /* compose-state produced sth, replace keysym */
- num = keyboard_read_compose(k, &keysyms);
- xkb_compose_state_reset(k->xkb_compose);
- break;
- case XKB_COMPOSE_CANCELLED:
- /* canceled compose, reset, forward cancellation sym */
- xkb_compose_state_reset(k->xkb_compose);
- break;
- }
- } else if (k->xkb_compose &&
- num == 1 &&
- keysyms[0] == XKB_KEY_Multi_key &&
- !data->resync &&
- ev->value == KBDKEY_UP) {
- /* Reset compose state on Multi-Key UP events. This effectively
- * requires you to hold the key during the whole sequence. I
- * think it's pretty handy to avoid accidental
- * Compose-sequences, but this may break Compose for disabled
- * people. We really need to make this opional! (TODO) */
- xkb_compose_state_reset(k->xkb_compose);
- }
-
- if (ev->value == KBDKEY_UP) {
- /* never produce keysyms for UP */
- keysyms = NULL;
- num = 0;
- }
-
- r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
- if (r < 0)
- goto error;
-
- keyboard_repeat(k);
- return keyboard_raise_data(k, &k->evdata);
-
-error:
- log_debug_errno(r, "idev-keyboard: %s/%s: cannot handle event: %m",
- d->session->name, d->name);
- k->repeating = false;
- keyboard_arm(k, 0);
- return 0;
-}
-
-static int keyboard_feed(idev_device *d, idev_data *data) {
- idev_keyboard *k = keyboard_from_device(d);
-
- switch (data->type) {
- case IDEV_DATA_RESYNC:
- /*
- * If the underlying device is re-synced, key-events might be
- * sent re-ordered. Thus, we don't know which key was pressed
- * last. Key-repeat might get confused, hence, disable it
- * during re-syncs. The first following event will enable it
- * again.
- */
-
- k->repeating = false;
- keyboard_arm(k, 0);
- return 0;
- case IDEV_DATA_EVDEV:
- return keyboard_feed_evdev(k, data);
- default:
- return 0;
- }
-}
-
-static int keyboard_update_kbdmap(idev_keyboard *k) {
- idev_device *d = &k->device;
- struct xkb_state *state;
- kbdmap *km;
- int r;
-
- assert(k);
-
- km = k->kbdctx->kbdmap;
- if (km == k->kbdmap)
- return 0;
-
- errno = 0;
- state = xkb_state_new(km->xkb_keymap);
- if (!state) {
- r = errno > 0 ? -errno : -EFAULT;
- goto error;
- }
-
- kbdmap_unref(k->kbdmap);
- k->kbdmap = kbdmap_ref(km);
- xkb_state_unref(k->xkb_state);
- k->xkb_state = state;
-
- /* TODO: On state-change, we should trigger a resync so the whole
- * event-state is flushed into the new xkb-state. libevdev currently
- * does not support that, though. */
-
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new keymap: %m",
- d->session->name, d->name);
-}
-
-static int keyboard_update_kbdtbl(idev_keyboard *k) {
- idev_device *d = &k->device;
- struct xkb_compose_state *compose = NULL;
- kbdtbl *kt;
- int r;
-
- assert(k);
-
- kt = k->kbdctx->kbdtbl;
- if (kt == k->kbdtbl)
- return 0;
-
- if (kt) {
- errno = 0;
- compose = xkb_compose_state_new(kt->xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
- if (!compose) {
- r = errno > 0 ? -errno : -EFAULT;
- goto error;
- }
- }
-
- kbdtbl_unref(k->kbdtbl);
- k->kbdtbl = kbdtbl_ref(kt);
- xkb_compose_state_unref(k->xkb_compose);
- k->xkb_compose = compose;
-
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new compose table: %m",
- d->session->name, d->name);
-}
-
-static const idev_device_vtable keyboard_vtable = {
- .free = keyboard_free,
- .feed = keyboard_feed,
-};
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
deleted file mode 100644
index b92a393b69..0000000000
--- a/src/libsystemd-terminal/idev.c
+++ /dev/null
@@ -1,799 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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 <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "login-util.h"
-#include "macro.h"
-#include "util.h"
-#include "idev.h"
-#include "idev-internal.h"
-
-static void element_open(idev_element *e);
-static void element_close(idev_element *e);
-
-/*
- * Devices
- */
-
-idev_device *idev_find_device(idev_session *s, const char *name) {
- assert_return(s, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(s->device_map, name);
-}
-
-int idev_device_add(idev_device *d, const char *name) {
- int r;
-
- assert_return(d, -EINVAL);
- assert_return(d->vtable, -EINVAL);
- assert_return(d->session, -EINVAL);
- assert_return(name, -EINVAL);
-
- d->name = strdup(name);
- if (!d->name)
- return -ENOMEM;
-
- r = hashmap_put(d->session->device_map, d->name, d);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-idev_device *idev_device_free(idev_device *d) {
- idev_device tmp;
-
- if (!d)
- return NULL;
-
- assert(!d->enabled);
- assert(!d->public);
- assert(!d->links);
- assert(d->vtable);
- assert(d->vtable->free);
-
- if (d->name)
- hashmap_remove_value(d->session->device_map, d->name, d);
-
- tmp = *d;
- d->vtable->free(d);
-
- free(tmp.name);
-
- return NULL;
-}
-
-int idev_device_feed(idev_device *d, idev_data *data) {
- assert(d);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- if (d->vtable->feed)
- return d->vtable->feed(d, data);
- else
- return 0;
-}
-
-void idev_device_feedback(idev_device *d, idev_data *data) {
- idev_link *l;
-
- assert(d);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- LIST_FOREACH(links_by_device, l, d->links)
- idev_element_feedback(l->element, data);
-}
-
-static void device_attach(idev_device *d, idev_link *l) {
- assert(d);
- assert(l);
-
- if (d->vtable->attach)
- d->vtable->attach(d, l);
-
- if (d->enabled)
- element_open(l->element);
-}
-
-static void device_detach(idev_device *d, idev_link *l) {
- assert(d);
- assert(l);
-
- if (d->enabled)
- element_close(l->element);
-
- if (d->vtable->detach)
- d->vtable->detach(d, l);
-}
-
-void idev_device_enable(idev_device *d) {
- idev_link *l;
-
- assert(d);
-
- if (!d->enabled) {
- d->enabled = true;
- LIST_FOREACH(links_by_device, l, d->links)
- element_open(l->element);
- }
-}
-
-void idev_device_disable(idev_device *d) {
- idev_link *l;
-
- assert(d);
-
- if (d->enabled) {
- d->enabled = false;
- LIST_FOREACH(links_by_device, l, d->links)
- element_close(l->element);
- }
-}
-
-/*
- * Elements
- */
-
-idev_element *idev_find_element(idev_session *s, const char *name) {
- assert_return(s, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(s->element_map, name);
-}
-
-int idev_element_add(idev_element *e, const char *name) {
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(e->vtable, -EINVAL);
- assert_return(e->session, -EINVAL);
- assert_return(name, -EINVAL);
-
- e->name = strdup(name);
- if (!e->name)
- return -ENOMEM;
-
- r = hashmap_put(e->session->element_map, e->name, e);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-idev_element *idev_element_free(idev_element *e) {
- idev_element tmp;
-
- if (!e)
- return NULL;
-
- assert(!e->enabled);
- assert(!e->links);
- assert(e->n_open == 0);
- assert(e->vtable);
- assert(e->vtable->free);
-
- if (e->name)
- hashmap_remove_value(e->session->element_map, e->name, e);
-
- tmp = *e;
- e->vtable->free(e);
-
- free(tmp.name);
-
- return NULL;
-}
-
-int idev_element_feed(idev_element *e, idev_data *data) {
- int r, error = 0;
- idev_link *l;
-
- assert(e);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- LIST_FOREACH(links_by_element, l, e->links) {
- r = idev_device_feed(l->device, data);
- if (r != 0)
- error = r;
- }
-
- return error;
-}
-
-void idev_element_feedback(idev_element *e, idev_data *data) {
- assert(e);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- if (e->vtable->feedback)
- e->vtable->feedback(e, data);
-}
-
-static void element_open(idev_element *e) {
- assert(e);
-
- if (e->n_open++ == 0 && e->vtable->open)
- e->vtable->open(e);
-}
-
-static void element_close(idev_element *e) {
- assert(e);
- assert(e->n_open > 0);
-
- if (--e->n_open == 0 && e->vtable->close)
- e->vtable->close(e);
-}
-
-static void element_enable(idev_element *e) {
- assert(e);
-
- if (!e->enabled) {
- e->enabled = true;
- if (e->vtable->enable)
- e->vtable->enable(e);
- }
-}
-
-static void element_disable(idev_element *e) {
- assert(e);
-
- if (e->enabled) {
- e->enabled = false;
- if (e->vtable->disable)
- e->vtable->disable(e);
- }
-}
-
-static void element_resume(idev_element *e, int fd) {
- assert(e);
- assert(fd >= 0);
-
- if (e->vtable->resume)
- e->vtable->resume(e, fd);
-}
-
-static void element_pause(idev_element *e, const char *mode) {
- assert(e);
- assert(mode);
-
- if (e->vtable->pause)
- e->vtable->pause(e, mode);
-}
-
-/*
- * Sessions
- */
-
-static int session_raise(idev_session *s, idev_event *ev) {
- return s->event_fn(s, s->userdata, ev);
-}
-
-static int session_raise_device_add(idev_session *s, idev_device *d) {
- idev_event event = {
- .type = IDEV_EVENT_DEVICE_ADD,
- .device_add = {
- .device = d,
- },
- };
-
- return session_raise(s, &event);
-}
-
-static int session_raise_device_remove(idev_session *s, idev_device *d) {
- idev_event event = {
- .type = IDEV_EVENT_DEVICE_REMOVE,
- .device_remove = {
- .device = d,
- },
- };
-
- return session_raise(s, &event);
-}
-
-int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data) {
- idev_event event = {
- .type = IDEV_EVENT_DEVICE_DATA,
- .device_data = {
- .device = d,
- .data = *data,
- },
- };
-
- return session_raise(s, &event);
-}
-
-static int session_add_device(idev_session *s, idev_device *d) {
- int r;
-
- assert(s);
- assert(d);
-
- log_debug("idev: %s: add device '%s'", s->name, d->name);
-
- d->public = true;
- r = session_raise_device_add(s, d);
- if (r != 0) {
- d->public = false;
- goto error;
- }
-
- return 0;
-
-error:
- if (r < 0)
- log_debug_errno(r, "idev: %s: error while adding device '%s': %m",
- s->name, d->name);
- return r;
-}
-
-static int session_remove_device(idev_session *s, idev_device *d) {
- int r, error = 0;
-
- assert(s);
- assert(d);
-
- log_debug("idev: %s: remove device '%s'", s->name, d->name);
-
- d->public = false;
- r = session_raise_device_remove(s, d);
- if (r != 0)
- error = r;
-
- idev_device_disable(d);
-
- if (error < 0)
- log_debug_errno(error, "idev: %s: error while removing device '%s': %m",
- s->name, d->name);
- idev_device_free(d);
- return error;
-}
-
-static int session_add_element(idev_session *s, idev_element *e) {
- assert(s);
- assert(e);
-
- log_debug("idev: %s: add element '%s'", s->name, e->name);
-
- if (s->enabled)
- element_enable(e);
-
- return 0;
-}
-
-static int session_remove_element(idev_session *s, idev_element *e) {
- int r, error = 0;
- idev_device *d;
- idev_link *l;
-
- assert(s);
- assert(e);
-
- log_debug("idev: %s: remove element '%s'", s->name, e->name);
-
- while ((l = e->links)) {
- d = l->device;
- LIST_REMOVE(links_by_device, d->links, l);
- LIST_REMOVE(links_by_element, e->links, l);
- device_detach(d, l);
-
- if (!d->links) {
- r = session_remove_device(s, d);
- if (r != 0)
- error = r;
- }
-
- l->device = NULL;
- l->element = NULL;
- free(l);
- }
-
- element_disable(e);
-
- if (error < 0)
- log_debug_errno(r, "idev: %s: error while removing element '%s': %m",
- s->name, e->name);
- idev_element_free(e);
- return error;
-}
-
-idev_session *idev_find_session(idev_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->session_map, name);
-}
-
-static int session_resume_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- idev_session *s = userdata;
- idev_element *e;
- uint32_t major, minor;
- int r, fd;
-
- r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
- if (r < 0) {
- log_debug("idev: %s: erroneous ResumeDevice signal", s->name);
- return 0;
- }
-
- e = idev_find_evdev(s, makedev(major, minor));
- if (!e)
- return 0;
-
- element_resume(e, fd);
- return 0;
-}
-
-static int session_pause_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- idev_session *s = userdata;
- idev_element *e;
- uint32_t major, minor;
- const char *mode;
- int r;
-
- r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
- if (r < 0) {
- log_debug("idev: %s: erroneous PauseDevice signal", s->name);
- return 0;
- }
-
- e = idev_find_evdev(s, makedev(major, minor));
- if (!e)
- return 0;
-
- element_pause(e, mode);
- return 0;
-}
-
-static int session_setup_bus(idev_session *s) {
- _cleanup_free_ char *match = NULL;
- int r;
-
- if (!s->managed)
- return 0;
-
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='ResumeDevice',"
- "path='", s->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(s->context->sysbus,
- &s->slot_resume_device,
- match,
- session_resume_device_fn,
- s);
- if (r < 0)
- return r;
-
- free(match);
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='PauseDevice',"
- "path='", s->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(s->context->sysbus,
- &s->slot_pause_device,
- match,
- session_pause_device_fn,
- s);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int idev_session_new(idev_session **out,
- idev_context *c,
- unsigned int flags,
- const char *name,
- idev_event_fn event_fn,
- void *userdata) {
- _cleanup_(idev_session_freep) idev_session *s = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(c, -EINVAL);
- assert_return(name, -EINVAL);
- assert_return(event_fn, -EINVAL);
- assert_return((flags & IDEV_SESSION_CUSTOM) == !session_id_valid(name), -EINVAL);
- assert_return(!(flags & IDEV_SESSION_CUSTOM) || !(flags & IDEV_SESSION_MANAGED), -EINVAL);
- assert_return(!(flags & IDEV_SESSION_MANAGED) || c->sysbus, -EINVAL);
-
- s = new0(idev_session, 1);
- if (!s)
- return -ENOMEM;
-
- s->context = idev_context_ref(c);
- s->custom = flags & IDEV_SESSION_CUSTOM;
- s->managed = flags & IDEV_SESSION_MANAGED;
- s->event_fn = event_fn;
- s->userdata = userdata;
-
- s->name = strdup(name);
- if (!s->name)
- return -ENOMEM;
-
- if (s->managed) {
- r = sd_bus_path_encode("/org/freedesktop/login1/session", s->name, &s->path);
- if (r < 0)
- return r;
- }
-
- s->element_map = hashmap_new(&string_hash_ops);
- if (!s->element_map)
- return -ENOMEM;
-
- s->device_map = hashmap_new(&string_hash_ops);
- if (!s->device_map)
- return -ENOMEM;
-
- r = session_setup_bus(s);
- if (r < 0)
- return r;
-
- r = hashmap_put(c->session_map, s->name, s);
- if (r < 0)
- return r;
-
- *out = s;
- s = NULL;
- return 0;
-}
-
-idev_session *idev_session_free(idev_session *s) {
- idev_element *e;
-
- if (!s)
- return NULL;
-
- while ((e = hashmap_first(s->element_map)))
- session_remove_element(s, e);
-
- assert(hashmap_size(s->device_map) == 0);
-
- if (s->name)
- hashmap_remove_value(s->context->session_map, s->name, s);
-
- s->slot_pause_device = sd_bus_slot_unref(s->slot_pause_device);
- s->slot_resume_device = sd_bus_slot_unref(s->slot_resume_device);
- s->context = idev_context_unref(s->context);
- hashmap_free(s->device_map);
- hashmap_free(s->element_map);
- free(s->path);
- free(s->name);
- free(s);
-
- return NULL;
-}
-
-bool idev_session_is_enabled(idev_session *s) {
- return s && s->enabled;
-}
-
-void idev_session_enable(idev_session *s) {
- idev_element *e;
- Iterator i;
-
- assert(s);
-
- if (!s->enabled) {
- s->enabled = true;
- HASHMAP_FOREACH(e, s->element_map, i)
- element_enable(e);
- }
-}
-
-void idev_session_disable(idev_session *s) {
- idev_element *e;
- Iterator i;
-
- assert(s);
-
- if (s->enabled) {
- s->enabled = false;
- HASHMAP_FOREACH(e, s->element_map, i)
- element_disable(e);
- }
-}
-
-static int add_link(idev_element *e, idev_device *d) {
- idev_link *l;
-
- assert(e);
- assert(d);
-
- l = new0(idev_link, 1);
- if (!l)
- return -ENOMEM;
-
- l->element = e;
- l->device = d;
- LIST_PREPEND(links_by_element, e->links, l);
- LIST_PREPEND(links_by_device, d->links, l);
- device_attach(d, l);
-
- return 0;
-}
-
-static int guess_type(struct udev_device *d) {
- const char *id_key;
-
- id_key = udev_device_get_property_value(d, "ID_INPUT_KEY");
- if (streq_ptr(id_key, "1"))
- return IDEV_DEVICE_KEYBOARD;
-
- return IDEV_DEVICE_CNT;
-}
-
-int idev_session_add_evdev(idev_session *s, struct udev_device *ud) {
- idev_element *e;
- idev_device *d;
- dev_t devnum;
- int r, type;
-
- assert_return(s, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return 0;
-
- e = idev_find_evdev(s, devnum);
- if (e)
- return 0;
-
- r = idev_evdev_new(&e, s, ud);
- if (r < 0)
- return r;
-
- r = session_add_element(s, e);
- if (r != 0)
- return r;
-
- type = guess_type(ud);
- if (type < 0)
- return type;
-
- switch (type) {
- case IDEV_DEVICE_KEYBOARD:
- d = idev_find_keyboard(s, e->name);
- if (d) {
- log_debug("idev: %s: keyboard for new evdev element '%s' already available",
- s->name, e->name);
- return 0;
- }
-
- r = idev_keyboard_new(&d, s, e->name);
- if (r < 0)
- return r;
-
- r = add_link(e, d);
- if (r < 0) {
- idev_device_free(d);
- return r;
- }
-
- return session_add_device(s, d);
- default:
- /* unknown elements are silently ignored */
- return 0;
- }
-}
-
-int idev_session_remove_evdev(idev_session *s, struct udev_device *ud) {
- idev_element *e;
- dev_t devnum;
-
- assert(s);
- assert(ud);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return 0;
-
- e = idev_find_evdev(s, devnum);
- if (!e)
- return 0;
-
- return session_remove_element(s, e);
-}
-
-/*
- * Contexts
- */
-
-int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) {
- _cleanup_(idev_context_unrefp) idev_context *c = NULL;
-
- assert_return(out, -EINVAL);
- assert_return(event, -EINVAL);
-
- c = new0(idev_context, 1);
- if (!c)
- return -ENOMEM;
-
- c->ref = 1;
- c->event = sd_event_ref(event);
-
- if (sysbus)
- c->sysbus = sd_bus_ref(sysbus);
-
- c->session_map = hashmap_new(&string_hash_ops);
- if (!c->session_map)
- return -ENOMEM;
-
- c->data_map = hashmap_new(&string_hash_ops);
- if (!c->data_map)
- return -ENOMEM;
-
- *out = c;
- c = NULL;
- return 0;
-}
-
-static void context_cleanup(idev_context *c) {
- assert(hashmap_size(c->data_map) == 0);
- assert(hashmap_size(c->session_map) == 0);
-
- hashmap_free(c->data_map);
- hashmap_free(c->session_map);
- c->sysbus = sd_bus_unref(c->sysbus);
- c->event = sd_event_unref(c->event);
- free(c);
-}
-
-idev_context *idev_context_ref(idev_context *c) {
- assert_return(c, NULL);
- assert_return(c->ref > 0, NULL);
-
- ++c->ref;
- return c;
-}
-
-idev_context *idev_context_unref(idev_context *c) {
- if (!c)
- return NULL;
-
- assert_return(c->ref > 0, NULL);
-
- if (--c->ref == 0)
- context_cleanup(c);
-
- return NULL;
-}
diff --git a/src/libsystemd-terminal/idev.h b/src/libsystemd-terminal/idev.h
deleted file mode 100644
index 241677cbbe..0000000000
--- a/src/libsystemd-terminal/idev.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * IDev
- */
-
-#pragma once
-
-#include <libudev.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <xkbcommon/xkbcommon.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-
-typedef struct idev_data idev_data;
-typedef struct idev_data_evdev idev_data_evdev;
-typedef struct idev_data_keyboard idev_data_keyboard;
-
-typedef struct idev_event idev_event;
-typedef struct idev_device idev_device;
-typedef struct idev_session idev_session;
-typedef struct idev_context idev_context;
-
-/*
- * Types
- */
-
-enum {
- IDEV_ELEMENT_EVDEV,
- IDEV_ELEMENT_CNT
-};
-
-enum {
- IDEV_DEVICE_KEYBOARD,
- IDEV_DEVICE_CNT
-};
-
-/*
- * Evdev Elements
- */
-
-struct idev_data_evdev {
- struct input_event event;
-};
-
-/*
- * Keyboard Devices
- */
-
-struct xkb_state;
-
-enum {
- IDEV_KBDMOD_IDX_SHIFT,
- IDEV_KBDMOD_IDX_CTRL,
- IDEV_KBDMOD_IDX_ALT,
- IDEV_KBDMOD_IDX_LINUX,
- IDEV_KBDMOD_IDX_CAPS,
- IDEV_KBDMOD_CNT,
-
- IDEV_KBDMOD_SHIFT = 1 << IDEV_KBDMOD_IDX_SHIFT,
- IDEV_KBDMOD_CTRL = 1 << IDEV_KBDMOD_IDX_CTRL,
- IDEV_KBDMOD_ALT = 1 << IDEV_KBDMOD_IDX_ALT,
- IDEV_KBDMOD_LINUX = 1 << IDEV_KBDMOD_IDX_LINUX,
- IDEV_KBDMOD_CAPS = 1 << IDEV_KBDMOD_IDX_CAPS,
-};
-
-enum {
- IDEV_KBDLED_IDX_NUM,
- IDEV_KBDLED_IDX_CAPS,
- IDEV_KBDLED_IDX_SCROLL,
- IDEV_KBDLED_CNT,
-
- IDEV_KBDLED_NUM = 1 << IDEV_KBDLED_IDX_NUM,
- IDEV_KBDLED_CAPS = 1 << IDEV_KBDLED_IDX_CAPS,
- IDEV_KBDLED_SCROLL = 1 << IDEV_KBDLED_IDX_SCROLL,
-};
-
-struct idev_data_keyboard {
- struct xkb_state *xkb_state;
- int8_t ascii;
- uint8_t value;
- uint16_t keycode;
- uint32_t mods;
- uint32_t consumed_mods;
- uint32_t n_syms;
- uint32_t *keysyms;
- uint32_t *codepoints;
-};
-
-static inline bool idev_kbdmatch(idev_data_keyboard *kdata,
- uint32_t mods, uint32_t n_syms,
- const uint32_t *syms) {
- const uint32_t significant = IDEV_KBDMOD_SHIFT |
- IDEV_KBDMOD_CTRL |
- IDEV_KBDMOD_ALT |
- IDEV_KBDMOD_LINUX;
- uint32_t real;
-
- if (n_syms != kdata->n_syms)
- return false;
-
- real = kdata->mods & ~kdata->consumed_mods & significant;
- if (real != mods)
- return false;
-
- return !memcmp(syms, kdata->keysyms, n_syms * sizeof(*syms));
-}
-
-#define IDEV_KBDMATCH(_kdata, _mods, _sym) \
- idev_kbdmatch((_kdata), (_mods), 1, (const uint32_t[]){ (_sym) })
-
-/*
- * Data Packets
- */
-
-enum {
- IDEV_DATA_RESYNC,
- IDEV_DATA_EVDEV,
- IDEV_DATA_KEYBOARD,
- IDEV_DATA_CNT
-};
-
-struct idev_data {
- unsigned int type;
- bool resync : 1;
-
- union {
- idev_data_evdev evdev;
- idev_data_keyboard keyboard;
- };
-};
-
-/*
- * Events
- */
-
-enum {
- IDEV_EVENT_DEVICE_ADD,
- IDEV_EVENT_DEVICE_REMOVE,
- IDEV_EVENT_DEVICE_DATA,
- IDEV_EVENT_CNT
-};
-
-struct idev_event {
- unsigned int type;
- union {
- struct {
- idev_device *device;
- } device_add, device_remove;
-
- struct {
- idev_device *device;
- idev_data data;
- } device_data;
- };
-};
-
-typedef int (*idev_event_fn) (idev_session *s, void *userdata, idev_event *ev);
-
-/*
- * Devices
- */
-
-void idev_device_enable(idev_device *d);
-void idev_device_disable(idev_device *d);
-
-/*
- * Sessions
- */
-
-enum {
- IDEV_SESSION_CUSTOM = (1 << 0),
- IDEV_SESSION_MANAGED = (1 << 1),
-};
-
-int idev_session_new(idev_session **out,
- idev_context *c,
- unsigned int flags,
- const char *name,
- idev_event_fn event_fn,
- void *userdata);
-idev_session *idev_session_free(idev_session *s);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_session*, idev_session_free);
-
-bool idev_session_is_enabled(idev_session *s);
-void idev_session_enable(idev_session *s);
-void idev_session_disable(idev_session *s);
-
-int idev_session_add_evdev(idev_session *s, struct udev_device *ud);
-int idev_session_remove_evdev(idev_session *s, struct udev_device *ud);
-
-/*
- * Contexts
- */
-
-int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus);
-idev_context *idev_context_ref(idev_context *c);
-idev_context *idev_context_unref(idev_context *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_context*, idev_context_unref);
diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
deleted file mode 100644
index 790a244772..0000000000
--- a/src/libsystemd-terminal/modeset.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Modeset Testing
- * The modeset tool attaches to the session of the caller and shows a
- * test-pattern on all displays of this session. It is meant as debugging tool
- * for the grdev infrastructure.
- */
-
-#include <drm_fourcc.h>
-#include <errno.h>
-#include <getopt.h>
-#include <linux/kd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <termios.h>
-#include <unistd.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "build.h"
-#include "macro.h"
-#include "random-util.h"
-#include "signal-util.h"
-#include "util.h"
-#include "grdev.h"
-#include "sysview.h"
-
-typedef struct Modeset Modeset;
-
-struct Modeset {
- char *session;
- char *seat;
- sd_event *event;
- sd_bus *bus;
- sd_event_source *exit_src;
- sysview_context *sysview;
- grdev_context *grdev;
- grdev_session *grdev_session;
-
- uint8_t r, g, b;
- bool r_up, g_up, b_up;
-
- bool my_tty : 1;
- bool managed : 1;
-};
-
-static int modeset_exit_fn(sd_event_source *source, void *userdata) {
- Modeset *m = userdata;
-
- if (m->grdev_session)
- grdev_session_restore(m->grdev_session);
-
- return 0;
-}
-
-static Modeset *modeset_free(Modeset *m) {
- if (!m)
- return NULL;
-
- m->grdev_session = grdev_session_free(m->grdev_session);
- m->grdev = grdev_context_unref(m->grdev);
- m->sysview = sysview_context_free(m->sysview);
- m->exit_src = sd_event_source_unref(m->exit_src);
- m->bus = sd_bus_unref(m->bus);
- m->event = sd_event_unref(m->event);
- free(m->seat);
- free(m->session);
- free(m);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Modeset*, modeset_free);
-
-static bool is_my_tty(const char *session) {
- unsigned int vtnr;
- struct stat st;
- long mode;
- int r;
-
- /* Using logind's Controller API is highly fragile if there is already
- * a session controller running. If it is registered as controller
- * itself, TakeControl will simply fail. But if its a legacy controller
- * that does not use logind's controller API, we must never register
- * our own controller. Otherwise, we really mess up the VT. Therefore,
- * only run in managed mode if there's no-one else. Furthermore, never
- * try to access graphics devices if there's someone else. Unlike input
- * devices, graphics devies cannot be shared easily. */
-
- if (!isatty(1))
- return false;
-
- if (!session)
- return false;
-
- r = sd_session_get_vt(session, &vtnr);
- if (r < 0 || vtnr < 1 || vtnr > 63)
- return false;
-
- mode = 0;
- r = ioctl(1, KDGETMODE, &mode);
- if (r < 0 || mode != KD_TEXT)
- return false;
-
- r = fstat(1, &st);
- if (r < 0 || minor(st.st_rdev) != vtnr)
- return false;
-
- return true;
-}
-
-static int modeset_new(Modeset **out) {
- _cleanup_(modeset_freep) Modeset *m = NULL;
- int r;
-
- assert(out);
-
- m = new0(Modeset, 1);
- if (!m)
- return log_oom();
-
- r = sd_pid_get_session(getpid(), &m->session);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve logind session: %m");
-
- r = sd_session_get_seat(m->session, &m->seat);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve seat of logind session: %m");
-
- m->my_tty = is_my_tty(m->session);
- m->managed = m->my_tty && geteuid() > 0;
-
- m->r = rand() % 0xff;
- m->g = rand() % 0xff;
- m->b = rand() % 0xff;
- m->r_up = m->g_up = m->b_up = true;
-
- r = sd_event_default(&m->event);
- if (r < 0)
- return r;
-
- r = sd_bus_open_system(&m->bus);
- if (r < 0)
- return r;
-
- r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return r;
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_exit(m->event, &m->exit_src, modeset_exit_fn, m);
- if (r < 0)
- return r;
-
- /* schedule before sd-bus close */
- r = sd_event_source_set_priority(m->exit_src, -10);
- if (r < 0)
- return r;
-
- r = sysview_context_new(&m->sysview,
- SYSVIEW_CONTEXT_SCAN_LOGIND |
- SYSVIEW_CONTEXT_SCAN_DRM,
- m->event,
- m->bus,
- NULL);
- if (r < 0)
- return r;
-
- r = grdev_context_new(&m->grdev, m->event, m->bus);
- if (r < 0)
- return r;
-
- *out = m;
- m = NULL;
- return 0;
-}
-
-static uint8_t next_color(bool *up, uint8_t cur, unsigned int mod) {
- uint8_t next;
-
- /* generate smoothly morphing colors */
-
- next = cur + (*up ? 1 : -1) * (rand() % mod);
- if ((*up && next < cur) || (!*up && next > cur)) {
- *up = !*up;
- next = cur;
- }
-
- return next;
-}
-
-static void modeset_draw(Modeset *m, const grdev_display_target *t) {
- uint32_t j, k, *b;
- uint8_t *l;
-
- assert(t->back->format == DRM_FORMAT_XRGB8888 || t->back->format == DRM_FORMAT_ARGB8888);
- assert(!t->rotate);
- assert(!t->flip);
-
- l = t->back->maps[0];
- for (j = 0; j < t->height; ++j) {
- for (k = 0; k < t->width; ++k) {
- b = (uint32_t*)l;
- b[k] = (0xff << 24) | (m->r << 16) | (m->g << 8) | m->b;
- }
-
- l += t->back->strides[0];
- }
-}
-
-static void modeset_render(Modeset *m, grdev_display *d) {
- const grdev_display_target *t;
-
- m->r = next_color(&m->r_up, m->r, 4);
- m->g = next_color(&m->g_up, m->g, 3);
- m->b = next_color(&m->b_up, m->b, 2);
-
- GRDEV_DISPLAY_FOREACH_TARGET(d, t) {
- modeset_draw(m, t);
- grdev_display_flip_target(d, t);
- }
-
- grdev_session_commit(m->grdev_session);
-}
-
-static void modeset_grdev_fn(grdev_session *session, void *userdata, grdev_event *ev) {
- Modeset *m = userdata;
-
- switch (ev->type) {
- case GRDEV_EVENT_DISPLAY_ADD:
- grdev_display_enable(ev->display_add.display);
- break;
- case GRDEV_EVENT_DISPLAY_REMOVE:
- break;
- case GRDEV_EVENT_DISPLAY_CHANGE:
- break;
- case GRDEV_EVENT_DISPLAY_FRAME:
- modeset_render(m, ev->display_frame.display);
- break;
- }
-}
-
-static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
- unsigned int flags, type;
- Modeset *m = userdata;
- sysview_device *d;
- const char *name;
- int r;
-
- switch (ev->type) {
- case SYSVIEW_EVENT_SESSION_FILTER:
- if (streq_ptr(m->session, ev->session_filter.id))
- return 1;
-
- break;
- case SYSVIEW_EVENT_SESSION_ADD:
- assert(!m->grdev_session);
-
- name = sysview_session_get_name(ev->session_add.session);
- flags = 0;
-
- if (m->managed)
- flags |= GRDEV_SESSION_MANAGED;
-
- r = grdev_session_new(&m->grdev_session,
- m->grdev,
- flags,
- name,
- modeset_grdev_fn,
- m);
- if (r < 0)
- return log_error_errno(r, "Cannot create grdev session: %m");
-
- if (m->managed) {
- r = sysview_session_take_control(ev->session_add.session);
- if (r < 0)
- return log_error_errno(r, "Cannot request session control: %m");
- }
-
- grdev_session_enable(m->grdev_session);
-
- break;
- case SYSVIEW_EVENT_SESSION_REMOVE:
- if (!m->grdev_session)
- return 0;
-
- grdev_session_restore(m->grdev_session);
- grdev_session_disable(m->grdev_session);
- m->grdev_session = grdev_session_free(m->grdev_session);
- if (sd_event_get_exit_code(m->event, &r) == -ENODATA)
- sd_event_exit(m->event, 0);
- break;
- case SYSVIEW_EVENT_SESSION_ATTACH:
- d = ev->session_attach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_DRM)
- grdev_session_add_drm(m->grdev_session, sysview_device_get_ud(d));
-
- break;
- case SYSVIEW_EVENT_SESSION_DETACH:
- d = ev->session_detach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_DRM)
- grdev_session_remove_drm(m->grdev_session, sysview_device_get_ud(d));
-
- break;
- case SYSVIEW_EVENT_SESSION_REFRESH:
- d = ev->session_refresh.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_DRM)
- grdev_session_hotplug_drm(m->grdev_session, ev->session_refresh.ud);
-
- break;
- case SYSVIEW_EVENT_SESSION_CONTROL:
- r = ev->session_control.error;
- if (r < 0)
- return log_error_errno(r, "Cannot acquire session control: %m");
-
- r = ioctl(1, KDSKBMODE, K_UNICODE);
- if (r < 0)
- return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m");
-
- break;
- }
-
- return 0;
-}
-
-static int modeset_run(Modeset *m) {
- struct termios in_attr, saved_attr;
- int r;
-
- assert(m);
-
- if (!m->my_tty) {
- log_warning("You need to run this program on a free VT");
- return -EACCES;
- }
-
- if (!m->managed && geteuid() > 0)
- log_warning("You run in unmanaged mode without being root. This is likely to fail..");
-
- printf("modeset - Show test pattern on selected graphics devices\n"
- " Running on seat '%s' in user-session '%s'\n"
- " Exit by pressing ^C\n\n",
- m->seat ? : "seat0", m->session ? : "<none>");
-
- r = sysview_context_start(m->sysview, modeset_sysview_fn, m);
- if (r < 0)
- goto out;
-
- r = tcgetattr(0, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- saved_attr = in_attr;
- in_attr.c_lflag &= ~ECHO;
-
- r = tcsetattr(0, TCSANOW, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- r = sd_event_loop(m->event);
- tcsetattr(0, TCSANOW, &saved_attr);
- printf("exiting..\n");
-
-out:
- sysview_context_stop(m->sysview);
- return r;
-}
-
-static int help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Show test pattern on all selected graphics devices.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- , program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- };
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- {},
- };
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- switch (c) {
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
-
- if (argc > optind) {
- log_error("Too many arguments");
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_(modeset_freep) Modeset *m = NULL;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- initialize_srand();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- r = modeset_new(&m);
- if (r < 0)
- goto finish;
-
- r = modeset_run(m);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/libsystemd-terminal/subterm.c b/src/libsystemd-terminal/subterm.c
deleted file mode 100644
index 5f12540111..0000000000
--- a/src/libsystemd-terminal/subterm.c
+++ /dev/null
@@ -1,981 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Stacked Terminal-Emulator
- * This is an interactive test of the term_screen implementation. It runs a
- * fully-fletched terminal-emulator inside of a parent TTY. That is, instead of
- * rendering the terminal as X11-window, it renders it as sub-window in the
- * parent TTY. Think of this like what "GNU-screen" does.
- */
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include "sd-event.h"
-#include "macro.h"
-#include "pty.h"
-#include "ring.h"
-#include "signal-util.h"
-#include "utf8.h"
-#include "util.h"
-#include "term-internal.h"
-
-typedef struct Output Output;
-typedef struct Terminal Terminal;
-
-struct Output {
- int fd;
- unsigned int width;
- unsigned int height;
- unsigned int in_width;
- unsigned int in_height;
- unsigned int cursor_x;
- unsigned int cursor_y;
-
- char obuf[4096];
- size_t n_obuf;
-
- bool resized : 1;
- bool in_menu : 1;
-};
-
-struct Terminal {
- sd_event *event;
- sd_event_source *frame_timer;
- Output *output;
- term_utf8 utf8;
- term_parser *parser;
- term_screen *screen;
-
- int in_fd;
- int out_fd;
- struct termios saved_in_attr;
- struct termios saved_out_attr;
-
- Pty *pty;
- Ring out_ring;
-
- bool is_scheduled : 1;
- bool is_dirty : 1;
- bool is_menu : 1;
-};
-
-/*
- * Output Handling
- */
-
-#define BORDER_HORIZ "\xe2\x94\x81"
-#define BORDER_VERT "\xe2\x94\x83"
-#define BORDER_VERT_RIGHT "\xe2\x94\xa3"
-#define BORDER_VERT_LEFT "\xe2\x94\xab"
-#define BORDER_DOWN_RIGHT "\xe2\x94\x8f"
-#define BORDER_DOWN_LEFT "\xe2\x94\x93"
-#define BORDER_UP_RIGHT "\xe2\x94\x97"
-#define BORDER_UP_LEFT "\xe2\x94\x9b"
-
-static int output_winch(Output *o) {
- struct winsize wsz = { };
- int r;
-
- assert_return(o, -EINVAL);
-
- r = ioctl(o->fd, TIOCGWINSZ, &wsz);
- if (r < 0)
- return log_error_errno(errno, "error: cannot read window-size: %m");
-
- if (wsz.ws_col != o->width || wsz.ws_row != o->height) {
- o->width = wsz.ws_col;
- o->height = wsz.ws_row;
- o->in_width = MAX(o->width, 2U) - 2;
- o->in_height = MAX(o->height, 6U) - 6;
- o->resized = true;
- }
-
- return 0;
-}
-
-static int output_flush(Output *o) {
- int r;
-
- if (o->n_obuf < 1)
- return 0;
-
- r = loop_write(o->fd, o->obuf, o->n_obuf, false);
- if (r < 0)
- return log_error_errno(r, "error: cannot write to TTY: %m");
-
- o->n_obuf = 0;
-
- return 0;
-}
-
-static int output_write(Output *o, const void *buf, size_t size) {
- ssize_t len;
- int r;
-
- assert_return(o, -EINVAL);
- assert_return(buf || size < 1, -EINVAL);
-
- if (size < 1)
- return 0;
-
- if (o->n_obuf + size > o->n_obuf && o->n_obuf + size <= sizeof(o->obuf)) {
- memcpy(o->obuf + o->n_obuf, buf, size);
- o->n_obuf += size;
- return 0;
- }
-
- r = output_flush(o);
- if (r < 0)
- return r;
-
- len = loop_write(o->fd, buf, size, false);
- if (len < 0)
- return log_error_errno(len, "error: cannot write to TTY (%zd): %m", len);
-
- return 0;
-}
-
-_printf_(3,0)
-static int output_vnprintf(Output *o, size_t max, const char *format, va_list args) {
- char buf[max];
- int r;
-
- assert_return(o, -EINVAL);
- assert_return(format, -EINVAL);
- assert_return(max <= 4096, -EINVAL);
-
- r = MIN(vsnprintf(buf, max, format, args), (int) max);
-
- return output_write(o, buf, r);
-}
-
-_printf_(3,4)
-static int output_nprintf(Output *o, size_t max, const char *format, ...) {
- va_list args;
- int r;
-
- va_start(args, format);
- r = output_vnprintf(o, max, format, args);
- va_end(args);
-
- return r;
-}
-
-_printf_(2,0)
-static int output_vprintf(Output *o, const char *format, va_list args) {
- char buf[4096];
- int r;
-
- assert_return(o, -EINVAL);
- assert_return(format, -EINVAL);
-
- r = vsnprintf(buf, sizeof(buf), format, args);
-
- assert_return(r < (ssize_t)sizeof(buf), -ENOBUFS);
-
- return output_write(o, buf, r);
-}
-
-_printf_(2,3)
-static int output_printf(Output *o, const char *format, ...) {
- va_list args;
- int r;
-
- va_start(args, format);
- r = output_vprintf(o, format, args);
- va_end(args);
-
- return r;
-}
-
-static int output_move_to(Output *o, unsigned int x, unsigned int y) {
- int r;
-
- assert_return(o, -EINVAL);
-
- /* force the \e[H code as o->cursor_x/y might be out-of-date */
-
- r = output_printf(o, "\e[%u;%uH", y + 1, x + 1);
- if (r < 0)
- return r;
-
- o->cursor_x = x;
- o->cursor_y = y;
- return 0;
-}
-
-static int output_print_line(Output *o, size_t len) {
- const char line[] =
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ;
- const size_t max = (sizeof(line) - 1) / (sizeof(BORDER_HORIZ) - 1);
- size_t i;
- int r = 0;
-
- assert_return(o, -EINVAL);
-
- for ( ; len > 0; len -= i) {
- i = (len > max) ? max : len;
- r = output_write(o, line, i * (sizeof(BORDER_HORIZ) - 1));
- if (r < 0)
- break;
- }
-
- return r;
-}
-
-_printf_(2,3)
-static int output_frame_printl(Output *o, const char *format, ...) {
- va_list args;
- int r;
-
- assert(o);
- assert(format);
-
- /* out of frame? */
- if (o->cursor_y < 3 || o->cursor_y >= o->height - 3)
- return 0;
-
- va_start(args, format);
- r = output_vnprintf(o, o->width - 2, format, args);
- va_end(args);
-
- if (r < 0)
- return r;
-
- return output_move_to(o, 1, o->cursor_y + 1);
-}
-
-static Output *output_free(Output *o) {
- if (!o)
- return NULL;
-
- /* re-enable cursor */
- output_printf(o, "\e[?25h");
- /* disable alternate screen buffer */
- output_printf(o, "\e[?1049l");
- output_flush(o);
-
- /* o->fd is owned by the caller */
- free(o);
-
- return NULL;
-}
-
-static int output_new(Output **out, int fd) {
- Output *o;
- int r;
-
- assert_return(out, -EINVAL);
-
- o = new0(Output, 1);
- if (!o)
- return log_oom();
-
- o->fd = fd;
-
- r = output_winch(o);
- if (r < 0)
- goto error;
-
- /* enable alternate screen buffer */
- r = output_printf(o, "\e[?1049h");
- if (r < 0)
- goto error;
-
- /* always hide cursor */
- r = output_printf(o, "\e[?25l");
- if (r < 0)
- goto error;
-
- r = output_flush(o);
- if (r < 0)
- goto error;
-
- *out = o;
- return 0;
-
-error:
- output_free(o);
- return r;
-}
-
-static void output_draw_frame(Output *o) {
- unsigned int i;
-
- assert(o);
-
- /* print header-frame */
-
- output_printf(o, BORDER_DOWN_RIGHT);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_DOWN_LEFT
- "\r\n"
- BORDER_VERT
- "\e[2;%uH" /* cursor-position: 2/x */
- BORDER_VERT
- "\r\n"
- BORDER_VERT_RIGHT,
- o->width);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_VERT_LEFT
- "\r\n");
-
- /* print body-frame */
-
- for (i = 0; i < o->in_height; ++i) {
- output_printf(o, BORDER_VERT
- "\e[%u;%uH" /* cursor-position: 2/x */
- BORDER_VERT
- "\r\n",
- i + 4, o->width);
- }
-
- /* print footer-frame */
-
- output_printf(o, BORDER_VERT_RIGHT);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_VERT_LEFT
- "\r\n"
- BORDER_VERT
- "\e[%u;%uH" /* cursor-position: 2/x */
- BORDER_VERT
- "\r\n"
- BORDER_UP_RIGHT,
- o->height - 1, o->width);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_UP_LEFT);
-
- /* print header/footer text */
-
- output_printf(o, "\e[2;3H");
- output_nprintf(o, o->width - 4, "systemd - embedded terminal emulator");
- output_printf(o, "\e[%u;3H", o->height - 1);
- output_nprintf(o, o->width - 4, "press ^C to enter menu");
-}
-
-static void output_draw_menu(Output *o) {
- assert(o);
-
- output_frame_printl(o, "%s", "");
- output_frame_printl(o, " Menu: (the following keys are recognized)");
- output_frame_printl(o, " q: quit");
- output_frame_printl(o, " ^C: send ^C to the PTY");
-}
-
-static int output_draw_cell_fn(term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width) {
- Output *o = userdata;
- size_t k, ulen;
- char utf8[4];
-
- if (x >= o->in_width || y >= o->in_height)
- return 0;
-
- if (x == 0 && y != 0)
- output_printf(o, "\e[m\r\n" BORDER_VERT);
-
- switch (attr->fg.ccode) {
- case TERM_CCODE_DEFAULT:
- output_printf(o, "\e[39m");
- break;
- case TERM_CCODE_256:
- output_printf(o, "\e[38;5;%um", attr->fg.c256);
- break;
- case TERM_CCODE_RGB:
- output_printf(o, "\e[38;2;%u;%u;%um", attr->fg.red, attr->fg.green, attr->fg.blue);
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
- output_printf(o, "\e[%um", attr->fg.ccode - TERM_CCODE_BLACK + 30);
- break;
- case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
- output_printf(o, "\e[%um", attr->fg.ccode - TERM_CCODE_LIGHT_BLACK + 90);
- break;
- }
-
- switch (attr->bg.ccode) {
- case TERM_CCODE_DEFAULT:
- output_printf(o, "\e[49m");
- break;
- case TERM_CCODE_256:
- output_printf(o, "\e[48;5;%um", attr->bg.c256);
- break;
- case TERM_CCODE_RGB:
- output_printf(o, "\e[48;2;%u;%u;%um", attr->bg.red, attr->bg.green, attr->bg.blue);
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
- output_printf(o, "\e[%um", attr->bg.ccode - TERM_CCODE_BLACK + 40);
- break;
- case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
- output_printf(o, "\e[%um", attr->bg.ccode - TERM_CCODE_LIGHT_BLACK + 100);
- break;
- }
-
- output_printf(o, "\e[%u;%u;%u;%u;%u;%um",
- attr->bold ? 1 : 22,
- attr->italic ? 3 : 23,
- attr->underline ? 4 : 24,
- attr->inverse ? 7 : 27,
- attr->blink ? 5 : 25,
- attr->hidden ? 8 : 28);
-
- if (n_ch < 1) {
- output_printf(o, " ");
- } else {
- for (k = 0; k < n_ch; ++k) {
- ulen = utf8_encode_unichar(utf8, ch[k]);
- output_write(o, utf8, ulen);
- }
- }
-
- return 0;
-}
-
-static void output_draw_screen(Output *o, term_screen *s) {
- assert(o);
- assert(s);
-
- term_screen_draw(s, output_draw_cell_fn, o, NULL);
-
- output_printf(o, "\e[m");
-}
-
-static void output_draw(Output *o, bool menu, term_screen *screen) {
- assert(o);
-
- /*
- * This renders the contenst of the terminal. The layout contains a
- * header, the main body and a footer. Around all areas we draw a
- * border. It looks something like this:
- *
- * +----------------------------------------------------+
- * | Header |
- * +----------------------------------------------------+
- * | |
- * | |
- * | |
- * | Body |
- * | |
- * | |
- * ~ ~
- * ~ ~
- * +----------------------------------------------------+
- * | Footer |
- * +----------------------------------------------------+
- *
- * The body is the part that grows vertically.
- *
- * We need at least 6 vertical lines to render the screen. This would
- * leave 0 lines for the body. Therefore, we require 7 lines so there's
- * at least one body line. Similarly, we need 2 horizontal cells for the
- * frame, so we require 3.
- * If the window is too small, we print an error message instead.
- */
-
- if (o->in_width < 1 || o->in_height < 1) {
- output_printf(o, "\e[2J" /* erase-in-display: whole screen */
- "\e[H"); /* cursor-position: home */
- output_printf(o, "error: screen too small, need at least 3x7 cells");
- output_flush(o);
- return;
- }
-
- /* hide cursor */
- output_printf(o, "\e[?25l");
-
- /* frame-content is contant; only resizes can change it */
- if (o->resized || o->in_menu != menu) {
- output_printf(o, "\e[2J" /* erase-in-display: whole screen */
- "\e[H"); /* cursor-position: home */
- output_draw_frame(o);
- o->resized = false;
- o->in_menu = menu;
- }
-
- /* move cursor to child's position */
- output_move_to(o, 1, 3);
-
- if (menu)
- output_draw_menu(o);
- else
- output_draw_screen(o, screen);
-
- /*
- * Hack: sd-term was not written to support TTY as output-objects, thus
- * expects callers to use term_screen_feed_keyboard(). However, we
- * forward TTY input directly. Hence, we're not notified about keypad
- * changes. Update the related modes djring redraw to keep them at least
- * in sync.
- */
- if (screen->flags & TERM_FLAG_CURSOR_KEYS)
- output_printf(o, "\e[?1h");
- else
- output_printf(o, "\e[?1l");
-
- if (screen->flags & TERM_FLAG_KEYPAD_MODE)
- output_printf(o, "\e=");
- else
- output_printf(o, "\e>");
-
- output_flush(o);
-}
-
-/*
- * Terminal Handling
- */
-
-static void terminal_dirty(Terminal *t) {
- usec_t usec;
- int r;
-
- assert(t);
-
- if (t->is_scheduled) {
- t->is_dirty = true;
- return;
- }
-
- /* 16ms timer */
- r = sd_event_now(t->event, CLOCK_MONOTONIC, &usec);
- assert(r >= 0);
-
- usec += 16 * USEC_PER_MSEC;
- r = sd_event_source_set_time(t->frame_timer, usec);
- if (r >= 0) {
- r = sd_event_source_set_enabled(t->frame_timer, SD_EVENT_ONESHOT);
- if (r >= 0)
- t->is_scheduled = true;
- }
-
- t->is_dirty = false;
- output_draw(t->output, t->is_menu, t->screen);
-}
-
-static int terminal_frame_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
- Terminal *t = userdata;
-
- t->is_scheduled = false;
- if (t->is_dirty)
- terminal_dirty(t);
-
- return 0;
-}
-
-static int terminal_winch_fn(sd_event_source *source, const struct signalfd_siginfo *ssi, void *userdata) {
- Terminal *t = userdata;
- int r;
-
- output_winch(t->output);
-
- if (t->pty) {
- r = pty_resize(t->pty, t->output->in_width, t->output->in_height);
- if (r < 0)
- log_error_errno(r, "error: pty_resize() (%d): %m", r);
- }
-
- r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height);
- if (r < 0)
- log_error_errno(r, "error: term_screen_resize() (%d): %m", r);
-
- terminal_dirty(t);
-
- return 0;
-}
-
-static int terminal_push_tmp(Terminal *t, uint32_t ucs4) {
- char buf[4];
- size_t len;
- int r;
-
- assert(t);
-
- len = utf8_encode_unichar(buf, ucs4);
- if (len < 1)
- return 0;
-
- r = ring_push(&t->out_ring, buf, len);
- if (r < 0)
- log_oom();
-
- return r;
-}
-
-static int terminal_write_tmp(Terminal *t) {
- struct iovec vec[2];
- size_t num, i;
- int r;
-
- assert(t);
-
- num = ring_peek(&t->out_ring, vec);
- if (num < 1)
- return 0;
-
- if (t->pty) {
- for (i = 0; i < num; ++i) {
- r = pty_write(t->pty, vec[i].iov_base, vec[i].iov_len);
- if (r < 0)
- return log_error_errno(r, "error: cannot write to PTY (%d): %m", r);
- }
- }
-
- ring_flush(&t->out_ring);
- return 0;
-}
-
-static void terminal_discard_tmp(Terminal *t) {
- assert(t);
-
- ring_flush(&t->out_ring);
-}
-
-static int terminal_menu(Terminal *t, const term_seq *seq) {
- switch (seq->type) {
- case TERM_SEQ_IGNORE:
- break;
- case TERM_SEQ_GRAPHIC:
- switch (seq->terminator) {
- case 'q':
- sd_event_exit(t->event, 0);
- return 0;
- }
-
- break;
- case TERM_SEQ_CONTROL:
- switch (seq->terminator) {
- case 0x03:
- terminal_push_tmp(t, 0x03);
- terminal_write_tmp(t);
- break;
- }
-
- break;
- }
-
- t->is_menu = false;
- terminal_dirty(t);
-
- return 0;
-}
-
-static int terminal_io_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- Terminal *t = userdata;
- char buf[4096];
- ssize_t len, i;
- int r, type;
-
- len = read(fd, buf, sizeof(buf));
- if (len < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- log_error_errno(errno, "error: cannot read from TTY (%d): %m", -errno);
- return -errno;
- }
-
- for (i = 0; i < len; ++i) {
- const term_seq *seq;
- uint32_t *str;
- size_t n_str, j;
-
- n_str = term_utf8_decode(&t->utf8, &str, buf[i]);
- for (j = 0; j < n_str; ++j) {
- type = term_parser_feed(t->parser, &seq, str[j]);
- if (type < 0)
- return log_error_errno(type, "error: term_parser_feed() (%d): %m", type);
-
- if (!t->is_menu) {
- r = terminal_push_tmp(t, str[j]);
- if (r < 0)
- return r;
- }
-
- if (type == TERM_SEQ_NONE) {
- /* We only intercept one-char sequences, so in
- * case term_parser_feed() couldn't parse a
- * sequence, it is waiting for more data. We
- * know it can never be a one-char sequence
- * then, so we can safely forward the data.
- * This avoids withholding ESC or other values
- * that may be one-shot depending on the
- * application. */
- r = terminal_write_tmp(t);
- if (r < 0)
- return r;
- } else if (t->is_menu) {
- r = terminal_menu(t, seq);
- if (r < 0)
- return r;
- } else if (seq->type == TERM_SEQ_CONTROL && seq->terminator == 0x03) { /* ^C opens the menu */
- terminal_discard_tmp(t);
- t->is_menu = true;
- terminal_dirty(t);
- } else {
- r = terminal_write_tmp(t);
- if (r < 0)
- return r;
- }
- }
- }
-
- return 0;
-}
-
-static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size) {
- Terminal *t = userdata;
- int r;
-
- switch (event) {
- case PTY_CHILD:
- sd_event_exit(t->event, 0);
- break;
- case PTY_DATA:
- r = term_screen_feed_text(t->screen, ptr, size);
- if (r < 0)
- return log_error_errno(r, "error: term_screen_feed_text() (%d): %m", r);
-
- terminal_dirty(t);
- break;
- }
-
- return 0;
-}
-
-static int terminal_write_fn(term_screen *screen, void *userdata, const void *buf, size_t size) {
- Terminal *t = userdata;
- int r;
-
- if (!t->pty)
- return 0;
-
- r = ring_push(&t->out_ring, buf, size);
- if (r < 0)
- log_oom();
-
- return r;
-}
-
-static int terminal_cmd_fn(term_screen *screen, void *userdata, unsigned int cmd, const term_seq *seq) {
- return 0;
-}
-
-static Terminal *terminal_free(Terminal *t) {
- if (!t)
- return NULL;
-
- ring_clear(&t->out_ring);
- term_screen_unref(t->screen);
- term_parser_free(t->parser);
- output_free(t->output);
- sd_event_source_unref(t->frame_timer);
- sd_event_unref(t->event);
- tcsetattr(t->in_fd, TCSANOW, &t->saved_in_attr);
- tcsetattr(t->out_fd, TCSANOW, &t->saved_out_attr);
- free(t);
-
- return NULL;
-}
-
-static int terminal_new(Terminal **out, int in_fd, int out_fd) {
- struct termios in_attr, out_attr;
- Terminal *t;
- int r;
-
- assert_return(out, -EINVAL);
-
- r = tcgetattr(in_fd, &in_attr);
- if (r < 0)
- return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno);
-
- r = tcgetattr(out_fd, &out_attr);
- if (r < 0)
- return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno);
-
- t = new0(Terminal, 1);
- if (!t)
- return log_oom();
-
- t->in_fd = in_fd;
- t->out_fd = out_fd;
- memcpy(&t->saved_in_attr, &in_attr, sizeof(in_attr));
- memcpy(&t->saved_out_attr, &out_attr, sizeof(out_attr));
-
- cfmakeraw(&in_attr);
- cfmakeraw(&out_attr);
-
- r = tcsetattr(t->in_fd, TCSANOW, &in_attr);
- if (r < 0) {
- log_error_errno(r, "error: tcsetattr() (%d): %m", r);
- goto error;
- }
-
- r = tcsetattr(t->out_fd, TCSANOW, &out_attr);
- if (r < 0) {
- log_error_errno(r, "error: tcsetattr() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_default(&t->event);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_default() (%d): %m", r);
- goto error;
- }
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGINT, SIGQUIT, SIGTERM, SIGWINCH, SIGCHLD, -1);
- if (r < 0) {
- log_error_errno(r, "error: sigprocmask_many() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGINT, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGQUIT, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGWINCH, terminal_winch_fn, t);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- /* force initial redraw on event-loop enter */
- t->is_dirty = true;
- r = sd_event_add_time(t->event, &t->frame_timer, CLOCK_MONOTONIC, 0, 0, terminal_frame_timer_fn, t);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_time() (%d): %m", r);
- goto error;
- }
-
- r = output_new(&t->output, out_fd);
- if (r < 0)
- goto error;
-
- r = term_parser_new(&t->parser, true);
- if (r < 0)
- goto error;
-
- r = term_screen_new(&t->screen, terminal_write_fn, t, terminal_cmd_fn, t);
- if (r < 0)
- goto error;
-
- r = term_screen_set_answerback(t->screen, "systemd-subterm");
- if (r < 0)
- goto error;
-
- r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height);
- if (r < 0) {
- log_error_errno(r, "error: term_screen_resize() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_io(t->event, NULL, in_fd, EPOLLIN, terminal_io_fn, t);
- if (r < 0)
- goto error;
-
- *out = t;
- return 0;
-
-error:
- terminal_free(t);
- return r;
-}
-
-static int terminal_run(Terminal *t) {
- pid_t pid;
-
- assert_return(t, -EINVAL);
-
- pid = pty_fork(&t->pty, t->event, terminal_pty_fn, t, t->output->in_width, t->output->in_height);
- if (pid < 0)
- return log_error_errno(pid, "error: cannot fork PTY (%d): %m", pid);
- else if (pid == 0) {
- /* child */
-
- char **argv = (char*[]){
- (char*)getenv("SHELL") ? : (char*)_PATH_BSHELL,
- NULL
- };
-
- setenv("TERM", "xterm-256color", 1);
- setenv("COLORTERM", "systemd-subterm", 1);
-
- execve(argv[0], argv, environ);
- log_error_errno(errno, "error: cannot exec %s (%d): %m", argv[0], -errno);
- _exit(1);
- }
-
- /* parent */
-
- return sd_event_loop(t->event);
-}
-
-/*
- * Context Handling
- */
-
-int main(int argc, char *argv[]) {
- Terminal *t = NULL;
- int r;
-
- r = terminal_new(&t, 0, 1);
- if (r < 0)
- goto out;
-
- r = terminal_run(t);
- if (r < 0)
- goto out;
-
-out:
- if (r < 0)
- log_error_errno(r, "error: terminal failed (%d): %m", r);
- terminal_free(t);
- return -r;
-}
diff --git a/src/libsystemd-terminal/sysview-internal.h b/src/libsystemd-terminal/sysview-internal.h
deleted file mode 100644
index 251c8d7300..0000000000
--- a/src/libsystemd-terminal/sysview-internal.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-#include "sysview.h"
-
-/*
- * Devices
- */
-
-struct sysview_device {
- sysview_seat *seat;
- char *name;
- unsigned int type;
-
- union {
- struct {
- struct udev_device *ud;
- } evdev, drm;
- };
-};
-
-sysview_device *sysview_find_device(sysview_context *c, const char *name);
-
-int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name);
-sysview_device *sysview_device_free(sysview_device *device);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_device*, sysview_device_free);
-
-/*
- * Sessions
- */
-
-struct sysview_session {
- sysview_seat *seat;
- char *name;
- char *path;
- void *userdata;
-
- sd_bus_slot *slot_take_control;
-
- bool custom : 1;
- bool public : 1;
- bool wants_control : 1;
- bool has_control : 1;
-};
-
-sysview_session *sysview_find_session(sysview_context *c, const char *name);
-
-int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name);
-sysview_session *sysview_session_free(sysview_session *session);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_session*, sysview_session_free);
-
-/*
- * Seats
- */
-
-struct sysview_seat {
- sysview_context *context;
- char *name;
- char *path;
-
- Hashmap *session_map;
- Hashmap *device_map;
-
- bool scanned : 1;
- bool public : 1;
-};
-
-sysview_seat *sysview_find_seat(sysview_context *c, const char *name);
-
-int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name);
-sysview_seat *sysview_seat_free(sysview_seat *seat);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_seat*, sysview_seat_free);
-
-/*
- * Contexts
- */
-
-struct sysview_context {
- sd_event *event;
- sd_bus *sysbus;
- struct udev *ud;
- uint64_t custom_sid;
- unsigned int n_probe;
-
- Hashmap *seat_map;
- Hashmap *session_map;
- Hashmap *device_map;
-
- sd_event_source *scan_src;
- sysview_event_fn event_fn;
- void *userdata;
-
- /* udev scanner */
- struct udev_monitor *ud_monitor;
- sd_event_source *ud_monitor_src;
-
- /* logind scanner */
- sd_bus_slot *ld_slot_manager_signal;
- sd_bus_slot *ld_slot_list_seats;
- sd_bus_slot *ld_slot_list_sessions;
-
- bool scan_logind : 1;
- bool scan_evdev : 1;
- bool scan_drm : 1;
- bool running : 1;
- bool scanned : 1;
- bool rescan : 1;
- bool settled : 1;
-};
-
-int sysview_context_rescan(sysview_context *c);
diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
deleted file mode 100644
index 2e9b15859a..0000000000
--- a/src/libsystemd-terminal/sysview.c
+++ /dev/null
@@ -1,1554 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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 <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "macro.h"
-#include "udev-util.h"
-#include "util.h"
-#include "bus-util.h"
-#include "sysview.h"
-#include "sysview-internal.h"
-
-static int context_raise_session_control(sysview_context *c, sysview_session *session, int error);
-
-/*
- * Devices
- */
-
-sysview_device *sysview_find_device(sysview_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->device_map, name);
-}
-
-int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name) {
- _cleanup_(sysview_device_freep) sysview_device *device = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
- assert_return(name, -EINVAL);
-
- device = new0(sysview_device, 1);
- if (!device)
- return -ENOMEM;
-
- device->seat = seat;
- device->type = (unsigned)-1;
-
- device->name = strdup(name);
- if (!device->name)
- return -ENOMEM;
-
- r = hashmap_put(seat->context->device_map, device->name, device);
- if (r < 0)
- return r;
-
- r = hashmap_put(seat->device_map, device->name, device);
- if (r < 0)
- return r;
-
- if (out)
- *out = device;
- device = NULL;
- return 0;
-}
-
-sysview_device *sysview_device_free(sysview_device *device) {
- if (!device)
- return NULL;
-
- if (device->name) {
- hashmap_remove_value(device->seat->device_map, device->name, device);
- hashmap_remove_value(device->seat->context->device_map, device->name, device);
- }
-
- switch (device->type) {
- case SYSVIEW_DEVICE_EVDEV:
- device->evdev.ud = udev_device_unref(device->evdev.ud);
- break;
- case SYSVIEW_DEVICE_DRM:
- device->drm.ud = udev_device_unref(device->drm.ud);
- break;
- }
-
- free(device->name);
- free(device);
-
- return NULL;
-}
-
-const char *sysview_device_get_name(sysview_device *device) {
- assert_return(device, NULL);
-
- return device->name;
-}
-
-unsigned int sysview_device_get_type(sysview_device *device) {
- assert_return(device, (unsigned)-1);
-
- return device->type;
-}
-
-struct udev_device *sysview_device_get_ud(sysview_device *device) {
- assert_return(device, NULL);
-
- switch (device->type) {
- case SYSVIEW_DEVICE_EVDEV:
- return device->evdev.ud;
- case SYSVIEW_DEVICE_DRM:
- return device->drm.ud;
- default:
- assert_return(0, NULL);
- }
-}
-
-static int device_new_ud(sysview_device **out, sysview_seat *seat, unsigned int type, struct udev_device *ud) {
- _cleanup_(sysview_device_freep) sysview_device *device = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
- assert_return(ud, -EINVAL);
-
- r = sysview_device_new(&device, seat, udev_device_get_syspath(ud));
- if (r < 0)
- return r;
-
- device->type = type;
-
- switch (type) {
- case SYSVIEW_DEVICE_EVDEV:
- device->evdev.ud = udev_device_ref(ud);
- break;
- case SYSVIEW_DEVICE_DRM:
- device->drm.ud = udev_device_ref(ud);
- break;
- default:
- assert_not_reached("sysview: invalid udev-device type");
- }
-
- if (out)
- *out = device;
- device = NULL;
- return 0;
-}
-
-/*
- * Sessions
- */
-
-sysview_session *sysview_find_session(sysview_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->session_map, name);
-}
-
-int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name) {
- _cleanup_(sysview_session_freep) sysview_session *session = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
-
- session = new0(sysview_session, 1);
- if (!session)
- return -ENOMEM;
-
- session->seat = seat;
-
- if (name) {
- /*
- * If a name is given, we require it to be a logind session
- * name. The session will be put in managed mode and we use
- * logind to request controller access.
- */
-
- session->name = strdup(name);
- if (!session->name)
- return -ENOMEM;
-
- r = sd_bus_path_encode("/org/freedesktop/login1/session",
- session->name, &session->path);
- if (r < 0)
- return r;
-
- session->custom = false;
- } else {
- /*
- * No session name was given. We assume this is an unmanaged
- * session controlled by the application. We don't use logind
- * at all and leave session management to the application. The
- * name of the session-object is set to a unique random string
- * that does not clash with the logind namespace.
- */
-
- r = asprintf(&session->name, "@custom%" PRIu64,
- ++seat->context->custom_sid);
- if (r < 0)
- return -ENOMEM;
-
- session->custom = true;
- }
-
- r = hashmap_put(seat->context->session_map, session->name, session);
- if (r < 0)
- return r;
-
- r = hashmap_put(seat->session_map, session->name, session);
- if (r < 0)
- return r;
-
- if (out)
- *out = session;
- session = NULL;
- return 0;
-}
-
-sysview_session *sysview_session_free(sysview_session *session) {
- if (!session)
- return NULL;
-
- assert(!session->public);
- assert(!session->wants_control);
-
- if (session->name) {
- hashmap_remove_value(session->seat->session_map, session->name, session);
- hashmap_remove_value(session->seat->context->session_map, session->name, session);
- }
-
- free(session->path);
- free(session->name);
- free(session);
-
- return NULL;
-}
-
-void sysview_session_set_userdata(sysview_session *session, void *userdata) {
- assert(session);
-
- session->userdata = userdata;
-}
-
-void *sysview_session_get_userdata(sysview_session *session) {
- assert_return(session, NULL);
-
- return session->userdata;
-}
-
-const char *sysview_session_get_name(sysview_session *session) {
- assert_return(session, NULL);
-
- return session->name;
-}
-
-sysview_seat *sysview_session_get_seat(sysview_session *session) {
- assert_return(session, NULL);
-
- return session->seat;
-}
-
-static int session_take_control_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_session *session = userdata;
- int r, error;
-
- session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *e = sd_bus_message_get_error(reply);
-
- log_debug("sysview: %s: TakeControl failed: %s: %s",
- session->name, e->name, e->message);
- error = -sd_bus_error_get_errno(e);
- } else {
- session->has_control = true;
- error = 0;
- }
-
- r = context_raise_session_control(session->seat->context, session, error);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while signalling session control '%d' on session '%s': %m",
- error, session->name);
-
- return 0;
-}
-
-int sysview_session_take_control(sysview_session *session) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(session, -EINVAL);
- assert_return(!session->custom, -EINVAL);
-
- if (session->wants_control)
- return 0;
-
- r = sd_bus_message_new_method_call(session->seat->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "TakeControl");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "b", 0);
- if (r < 0)
- return r;
-
- r = sd_bus_call_async(session->seat->context->sysbus,
- &session->slot_take_control,
- m,
- session_take_control_fn,
- session,
- 0);
- if (r < 0)
- return r;
-
- session->wants_control = true;
- return 0;
-}
-
-void sysview_session_release_control(sysview_session *session) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(session);
- assert(!session->custom);
-
- if (!session->wants_control)
- return;
-
- session->wants_control = false;
-
- if (!session->has_control && !session->slot_take_control)
- return;
-
- session->has_control = false;
- session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
-
- r = sd_bus_message_new_method_call(session->seat->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "ReleaseControl");
- if (r >= 0)
- r = sd_bus_send(session->seat->context->sysbus, m, NULL);
-
- if (r < 0 && r != -ENOTCONN)
- log_debug_errno(r, "sysview: %s: cannot send ReleaseControl: %m",
- session->name);
-}
-
-/*
- * Seats
- */
-
-sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->seat_map, name);
-}
-
-int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
- _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(name, -EINVAL);
-
- seat = new0(sysview_seat, 1);
- if (!seat)
- return -ENOMEM;
-
- seat->context = c;
-
- seat->name = strdup(name);
- if (!seat->name)
- return -ENOMEM;
-
- r = sd_bus_path_encode("/org/freedesktop/login1/seat", seat->name, &seat->path);
- if (r < 0)
- return r;
-
- seat->session_map = hashmap_new(&string_hash_ops);
- if (!seat->session_map)
- return -ENOMEM;
-
- seat->device_map = hashmap_new(&string_hash_ops);
- if (!seat->device_map)
- return -ENOMEM;
-
- r = hashmap_put(c->seat_map, seat->name, seat);
- if (r < 0)
- return r;
-
- if (out)
- *out = seat;
- seat = NULL;
- return 0;
-}
-
-sysview_seat *sysview_seat_free(sysview_seat *seat) {
- if (!seat)
- return NULL;
-
- assert(!seat->public);
- assert(hashmap_size(seat->device_map) == 0);
- assert(hashmap_size(seat->session_map) == 0);
-
- if (seat->name)
- hashmap_remove_value(seat->context->seat_map, seat->name, seat);
-
- hashmap_free(seat->device_map);
- hashmap_free(seat->session_map);
- free(seat->path);
- free(seat->name);
- free(seat);
-
- return NULL;
-}
-
-const char *sysview_seat_get_name(sysview_seat *seat) {
- assert_return(seat, NULL);
-
- return seat->name;
-}
-
-int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
- assert_return(seat->context->sysbus, -EINVAL);
-
- r = sd_bus_message_new_method_call(seat->context->sysbus,
- &m,
- "org.freedesktop.login1",
- seat->path,
- "org.freedesktop.login1.Seat",
- "SwitchTo");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "u", nr);
- if (r < 0)
- return r;
-
- return sd_bus_send(seat->context->sysbus, m, NULL);
-}
-
-/*
- * Contexts
- */
-
-static int context_raise(sysview_context *c, sysview_event *event, int def) {
- return c->running ? c->event_fn(c, c->userdata, event) : def;
-}
-
-static int context_raise_settle(sysview_context *c) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SETTLE,
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SEAT_ADD,
- .seat_add = {
- .seat = seat,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SEAT_REMOVE,
- .seat_remove = {
- .seat = seat,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_filter(sysview_context *c,
- const char *id,
- const char *seatid,
- const char *username,
- unsigned int uid) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_FILTER,
- .session_filter = {
- .id = id,
- .seatid = seatid,
- .username = username,
- .uid = uid,
- }
- };
-
- return context_raise(c, &event, 1);
-}
-
-static int context_raise_session_add(sysview_context *c, sysview_session *session) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_ADD,
- .session_add = {
- .session = session,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_REMOVE,
- .session_remove = {
- .session = session,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_CONTROL,
- .session_control = {
- .session = session,
- .error = error,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_ATTACH,
- .session_attach = {
- .session = session,
- .device = device,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_DETACH,
- .session_detach = {
- .session = session,
- .device = device,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_REFRESH,
- .session_refresh = {
- .session = session,
- .device = device,
- .ud = ud,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static void context_settle(sysview_context *c) {
- int r;
-
- if (c->n_probe <= 0 || --c->n_probe > 0)
- return;
-
- log_debug("sysview: settle");
-
- c->settled = true;
-
- r = context_raise_settle(c);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed on settle: %m");
-}
-
-static void context_add_device(sysview_context *c, sysview_device *device) {
- sysview_session *session;
- Iterator i;
- int r;
-
- assert(c);
- assert(device);
-
- log_debug("sysview: add device '%s' on seat '%s'",
- device->name, device->seat->name);
-
- HASHMAP_FOREACH(session, device->seat->session_map, i) {
- if (!session->public)
- continue;
-
- r = context_raise_session_attach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m",
- device->name, session->name);
- }
-}
-
-static void context_remove_device(sysview_context *c, sysview_device *device) {
- sysview_session *session;
- Iterator i;
- int r;
-
- assert(c);
- assert(device);
-
- log_debug("sysview: remove device '%s'", device->name);
-
- HASHMAP_FOREACH(session, device->seat->session_map, i) {
- if (!session->public)
- continue;
-
- r = context_raise_session_detach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m",
- device->name, session->name);
- }
-
- sysview_device_free(device);
-}
-
-static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
- sysview_session *session;
- Iterator i;
- int r;
-
- assert(c);
- assert(device);
-
- log_debug("sysview: change device '%s'", device->name);
-
- HASHMAP_FOREACH(session, device->seat->session_map, i) {
- if (!session->public)
- continue;
-
- r = context_raise_session_refresh(c, session, device, ud);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m",
- device->name, session->name);
- }
-}
-
-static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
- sysview_session *session;
- sysview_device *device;
- Iterator i;
- int r;
-
- assert(c);
- assert(seat);
- assert(id);
-
- session = sysview_find_session(c, id);
- if (session)
- return;
-
- log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
-
- r = sysview_session_new(&session, seat, id);
- if (r < 0)
- goto error;
-
- if (!seat->scanned) {
- r = sysview_context_rescan(c);
- if (r < 0)
- goto error;
- }
-
- if (seat->public) {
- session->public = true;
- r = context_raise_session_add(c, session);
- if (r < 0) {
- log_debug_errno(r, "sysview: callback failed while adding session '%s': %m",
- session->name);
- session->public = false;
- goto error;
- }
-
- HASHMAP_FOREACH(device, seat->device_map, i) {
- r = context_raise_session_attach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m",
- device->name, session->name);
- }
- }
-
- return;
-
-error:
- if (r < 0)
- log_debug_errno(r, "sysview: error while adding session '%s': %m",
- id);
-}
-
-static void context_remove_session(sysview_context *c, sysview_session *session) {
- sysview_device *device;
- Iterator i;
- int r;
-
- assert(c);
- assert(session);
-
- log_debug("sysview: remove session '%s'", session->name);
-
- if (session->public) {
- HASHMAP_FOREACH(device, session->seat->device_map, i) {
- r = context_raise_session_detach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m",
- device->name, session->name);
- }
-
- session->public = false;
- r = context_raise_session_remove(c, session);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while removing session '%s': %m",
- session->name);
- }
-
- if (!session->custom)
- sysview_session_release_control(session);
-
- sysview_session_free(session);
-}
-
-static void context_add_seat(sysview_context *c, const char *id) {
- sysview_seat *seat;
- int r;
-
- assert(c);
- assert(id);
-
- seat = sysview_find_seat(c, id);
- if (seat)
- return;
-
- log_debug("sysview: add seat '%s'", id);
-
- r = sysview_seat_new(&seat, c, id);
- if (r < 0)
- goto error;
-
- seat->public = true;
- r = context_raise_seat_add(c, seat);
- if (r < 0) {
- log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m",
- seat->name);
- seat->public = false;
- }
-
- return;
-
-error:
- if (r < 0)
- log_debug_errno(r, "sysview: error while adding seat '%s': %m",
- id);
-}
-
-static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
- sysview_session *session;
- sysview_device *device;
- int r;
-
- assert(c);
- assert(seat);
-
- log_debug("sysview: remove seat '%s'", seat->name);
-
- while ((device = hashmap_first(seat->device_map)))
- context_remove_device(c, device);
-
- while ((session = hashmap_first(seat->session_map)))
- context_remove_session(c, session);
-
- if (seat->public) {
- seat->public = false;
- r = context_raise_seat_remove(c, seat);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m",
- seat->name);
- }
-
- sysview_seat_free(seat);
-}
-
-int sysview_context_new(sysview_context **out,
- unsigned int flags,
- sd_event *event,
- sd_bus *sysbus,
- struct udev *ud) {
- _cleanup_(sysview_context_freep) sysview_context *c = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(event, -EINVAL);
-
- log_debug("sysview: new");
-
- c = new0(sysview_context, 1);
- if (!c)
- return -ENOMEM;
-
- c->event = sd_event_ref(event);
- if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
- c->scan_logind = true;
- if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
- c->scan_evdev = true;
- if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
- c->scan_drm = true;
-
- if (sysbus) {
- c->sysbus = sd_bus_ref(sysbus);
- } else if (c->scan_logind) {
- r = sd_bus_open_system(&c->sysbus);
- if (r < 0)
- return r;
- }
-
- if (ud) {
- c->ud = udev_ref(ud);
- } else if (c->scan_evdev || c->scan_drm) {
- errno = 0;
- c->ud = udev_new();
- if (!c->ud)
- return errno > 0 ? -errno : -EFAULT;
- }
-
- c->seat_map = hashmap_new(&string_hash_ops);
- if (!c->seat_map)
- return -ENOMEM;
-
- c->session_map = hashmap_new(&string_hash_ops);
- if (!c->session_map)
- return -ENOMEM;
-
- c->device_map = hashmap_new(&string_hash_ops);
- if (!c->device_map)
- return -ENOMEM;
-
- *out = c;
- c = NULL;
- return 0;
-}
-
-sysview_context *sysview_context_free(sysview_context *c) {
- if (!c)
- return NULL;
-
- log_debug("sysview: free");
-
- sysview_context_stop(c);
-
- assert(hashmap_size(c->device_map) == 0);
- assert(hashmap_size(c->session_map) == 0);
- assert(hashmap_size(c->seat_map) == 0);
-
- hashmap_free(c->device_map);
- hashmap_free(c->session_map);
- hashmap_free(c->seat_map);
- c->ud = udev_unref(c->ud);
- c->sysbus = sd_bus_unref(c->sysbus);
- c->event = sd_event_unref(c->event);
- free(c);
-
- return NULL;
-}
-
-static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
- int r;
-
- if (c->scan_evdev) {
- r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
- if (r < 0)
- return r;
- }
-
- if (c->scan_drm) {
- r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
- int r;
-
- if (c->scan_evdev) {
- r = udev_enumerate_add_match_subsystem(e, "input");
- if (r < 0)
- return r;
- }
-
- if (c->scan_drm) {
- r = udev_enumerate_add_match_subsystem(e, "drm");
- if (r < 0)
- return r;
- }
-
- r = udev_enumerate_add_match_is_initialized(e);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
- const char *syspath, *sysname, *subsystem, *action, *seatname;
- sysview_device *device;
- int r;
-
- syspath = udev_device_get_syspath(d);
- sysname = udev_device_get_sysname(d);
- subsystem = udev_device_get_subsystem(d);
- action = udev_device_get_action(d);
-
- /* not interested in custom devices without syspath/etc */
- if (!syspath || !sysname || !subsystem)
- return 0;
-
- device = sysview_find_device(c, syspath);
-
- if (streq_ptr(action, "remove")) {
- if (!device)
- return 0;
-
- context_remove_device(c, device);
- } else if (streq_ptr(action, "change")) {
- if (!device)
- return 0;
-
- context_change_device(c, device, d);
- } else if (!action || streq_ptr(action, "add")) {
- struct udev_device *p;
- unsigned int type, t;
- sysview_seat *seat;
-
- if (device)
- return 0;
-
- if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
- type = SYSVIEW_DEVICE_EVDEV;
- else if (streq(subsystem, "drm") && startswith(sysname, "card"))
- type = SYSVIEW_DEVICE_DRM;
- else
- type = (unsigned)-1;
-
- if (type >= SYSVIEW_DEVICE_CNT)
- return 0;
-
- p = d;
- seatname = NULL;
- do {
- seatname = udev_device_get_property_value(p, "ID_SEAT");
- if (seatname)
- break;
- } while ((p = udev_device_get_parent(p)));
-
- seat = sysview_find_seat(c, seatname ? : "seat0");
- if (!seat)
- return 0;
-
- r = device_new_ud(&device, seat, type, d);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m",
- syspath);
-
- context_add_device(c, device);
- }
-
- return 0;
-}
-
-static int context_ud_monitor_fn(sd_event_source *s,
- int fd,
- uint32_t revents,
- void *userdata) {
- sysview_context *c = userdata;
- struct udev_device *d;
- int r;
-
- if (revents & EPOLLIN) {
- while ((d = udev_monitor_receive_device(c->ud_monitor))) {
- r = context_ud_hotplug(c, d);
- udev_device_unref(d);
- if (r != 0)
- return r;
- }
-
- /* as long as EPOLLIN is signalled, read pending data */
- return 0;
- }
-
- if (revents & (EPOLLHUP | EPOLLERR)) {
- log_debug("sysview: HUP on udev-monitor");
- c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
- }
-
- return 0;
-}
-
-static int context_ud_start(sysview_context *c) {
- int r, fd;
-
- if (!c->ud)
- return 0;
-
- errno = 0;
- c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
- if (!c->ud_monitor)
- return errno > 0 ? -errno : -EFAULT;
-
- r = context_ud_prepare_monitor(c, c->ud_monitor);
- if (r < 0)
- return r;
-
- r = udev_monitor_enable_receiving(c->ud_monitor);
- if (r < 0)
- return r;
-
- fd = udev_monitor_get_fd(c->ud_monitor);
- r = sd_event_add_io(c->event,
- &c->ud_monitor_src,
- fd,
- EPOLLHUP | EPOLLERR | EPOLLIN,
- context_ud_monitor_fn,
- c);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static void context_ud_stop(sysview_context *c) {
- c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
- c->ud_monitor = udev_monitor_unref(c->ud_monitor);
-}
-
-static int context_ud_scan(sysview_context *c) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *entry;
- struct udev_device *d;
- int r;
-
- if (!c->ud_monitor)
- return 0;
-
- errno = 0;
- e = udev_enumerate_new(c->ud);
- if (!e)
- return errno > 0 ? -errno : -EFAULT;
-
- r = context_ud_prepare_scan(c, e);
- if (r < 0)
- return r;
-
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return r;
-
- udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
- const char *name;
-
- name = udev_list_entry_get_name(entry);
-
- errno = 0;
- d = udev_device_new_from_syspath(c->ud, name);
- if (!d) {
- r = errno > 0 ? -errno : -EFAULT;
- log_debug_errno(r, "sysview: cannot create udev-device for %s: %m",
- name);
- continue;
- }
-
- r = context_ud_hotplug(c, d);
- udev_device_unref(d);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
- const char *id, *path;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m");
-
- context_add_seat(c, id);
- return 0;
-}
-
-static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
- const char *id, *path;
- sysview_seat *seat;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m");
-
- seat = sysview_find_seat(c, id);
- if (!seat)
- return 0;
-
- context_remove_seat(c, seat);
- return 0;
-}
-
-static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
- _cleanup_free_ char *seatid = NULL, *username = NULL;
- const char *id, *path;
- sysview_seat *seat;
- uid_t uid;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m");
-
- /*
- * As the dbus message didn't contain enough information, we
- * read missing bits via sd-login. Note that this might race session
- * destruction, so we handle ENOENT properly.
- */
-
- /* ENOENT is also returned for sessions without seats */
- r = sd_session_get_seat(id, &seatid);
- if (r == -ENOENT)
- return 0;
- else if (r < 0)
- goto error;
-
- seat = sysview_find_seat(c, seatid);
- if (!seat)
- return 0;
-
- r = sd_session_get_uid(id, &uid);
- if (r == -ENOENT)
- return 0;
- else if (r < 0)
- goto error;
-
- username = lookup_uid(uid);
- if (!username) {
- r = -ENOMEM;
- goto error;
- }
-
- r = context_raise_session_filter(c, id, seatid, username, uid);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
- id);
- else if (r > 0)
- context_add_session(c, seat, id);
-
- return 0;
-
-error:
- return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m",
- id);
-}
-
-static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
- sysview_session *session;
- const char *id, *path;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m");
-
- session = sysview_find_session(c, id);
- if (!session)
- return 0;
-
- context_remove_session(c, session);
- return 0;
-}
-
-static int context_ld_manager_signal_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_context *c = userdata;
-
- if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
- return context_ld_seat_new(c, signal);
- else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
- return context_ld_seat_removed(c, signal);
- else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
- return context_ld_session_new(c, signal);
- else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
- return context_ld_session_removed(c, signal);
- else
- return 0;
-}
-
-static int context_ld_start(sysview_context *c) {
- int r;
-
- if (!c->scan_logind)
- return 0;
-
- r = sd_bus_add_match(c->sysbus,
- &c->ld_slot_manager_signal,
- "type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Manager',"
- "path='/org/freedesktop/login1'",
- context_ld_manager_signal_fn,
- c);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static void context_ld_stop(sysview_context *c) {
- c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
- c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
- c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
-}
-
-static int context_ld_list_seats_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_context *c = userdata;
- int r;
-
- c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("sysview: ListSeats on logind failed: %s: %s",
- error->name, error->message);
- r = -sd_bus_error_get_errno(error);
- goto settle;
- }
-
- r = sd_bus_message_enter_container(reply, 'a', "(so)");
- if (r < 0)
- goto error;
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
- const char *id, *path;
-
- r = sd_bus_message_read(reply, "so", &id, &path);
- if (r < 0)
- goto error;
-
- context_add_seat(c, id);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
- }
-
- if (r < 0)
- goto error;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
-
- r = 0;
- goto settle;
-
-error:
- log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
-settle:
- context_settle(c);
- return r;
-}
-
-static int context_ld_list_sessions_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_context *c = userdata;
- int r;
-
- c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("sysview: ListSessions on logind failed: %s: %s",
- error->name, error->message);
- r = -sd_bus_error_get_errno(error);
- goto settle;
- }
-
- r = sd_bus_message_enter_container(reply, 'a', "(susso)");
- if (r < 0)
- goto error;
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
- const char *id, *username, *seatid, *path;
- sysview_seat *seat;
- unsigned int uid;
-
- r = sd_bus_message_read(reply,
- "susso",
- &id,
- &uid,
- &username,
- &seatid,
- &path);
- if (r < 0)
- goto error;
-
- seat = sysview_find_seat(c, seatid);
- if (seat) {
- r = context_raise_session_filter(c, id, seatid, username, uid);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
- id);
- else if (r > 0)
- context_add_session(c, seat, id);
- }
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
- }
-
- if (r < 0)
- goto error;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
-
- r = 0;
- goto settle;
-
-error:
- log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
-settle:
- context_settle(c);
- return r;
-}
-
-static int context_ld_scan(sysview_context *c) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- if (!c->ld_slot_manager_signal)
- return 0;
-
- /* request seat list */
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "ListSeats");
- if (r < 0)
- return r;
-
- r = sd_bus_call_async(c->sysbus,
- &c->ld_slot_list_seats,
- m,
- context_ld_list_seats_fn,
- c,
- 0);
- if (r < 0)
- return r;
-
- if (!c->settled)
- ++c->n_probe;
-
- /* request session list */
-
- m = sd_bus_message_unref(m);
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "ListSessions");
- if (r < 0)
- return r;
-
- r = sd_bus_call_async(c->sysbus,
- &c->ld_slot_list_sessions,
- m,
- context_ld_list_sessions_fn,
- c,
- 0);
- if (r < 0)
- return r;
-
- if (!c->settled)
- ++c->n_probe;
-
- return 0;
-}
-
-bool sysview_context_is_running(sysview_context *c) {
- return c && c->running;
-}
-
-int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(event_fn, -EINVAL);
-
- if (c->running)
- return -EALREADY;
-
- log_debug("sysview: start");
-
- c->running = true;
- c->event_fn = event_fn;
- c->userdata = userdata;
-
- r = context_ld_start(c);
- if (r < 0)
- goto error;
-
- r = context_ud_start(c);
- if (r < 0)
- goto error;
-
- r = sysview_context_rescan(c);
- if (r < 0)
- goto error;
-
- return 0;
-
-error:
- sysview_context_stop(c);
- return r;
-}
-
-void sysview_context_stop(sysview_context *c) {
- sysview_session *session;
- sysview_device *device;
- sysview_seat *seat;
-
- assert(c);
-
- if (!c->running)
- return;
-
- log_debug("sysview: stop");
-
- while ((device = hashmap_first(c->device_map)))
- context_remove_device(c, device);
-
- while ((session = hashmap_first(c->session_map)))
- context_remove_session(c, session);
-
- while ((seat = hashmap_first(c->seat_map)))
- context_remove_seat(c, seat);
-
- c->running = false;
- c->scanned = false;
- c->settled = false;
- c->n_probe = 0;
- c->event_fn = NULL;
- c->userdata = NULL;
- c->scan_src = sd_event_source_unref(c->scan_src);
- context_ud_stop(c);
- context_ld_stop(c);
-}
-
-static int context_scan_fn(sd_event_source *s, void *userdata) {
- sysview_context *c = userdata;
- sysview_seat *seat;
- Iterator i;
- int r;
-
- c->rescan = false;
-
- if (!c->scanned) {
- r = context_ld_scan(c);
- if (r < 0)
- return log_debug_errno(r, "sysview: logind scan failed: %m");
- }
-
- /* skip device scans if no sessions are available */
- if (hashmap_size(c->session_map) > 0) {
- r = context_ud_scan(c);
- if (r < 0)
- return log_debug_errno(r, "sysview: udev scan failed: %m");
-
- HASHMAP_FOREACH(seat, c->seat_map, i)
- seat->scanned = true;
- }
-
- c->scanned = true;
- context_settle(c);
-
- return 0;
-}
-
-int sysview_context_rescan(sysview_context *c) {
- assert(c);
-
- if (!c->running)
- return 0;
-
- if (!c->rescan) {
- c->rescan = true;
- if (!c->settled)
- ++c->n_probe;
- }
-
- if (c->scan_src)
- return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
- else
- return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);
-}
diff --git a/src/libsystemd-terminal/sysview.h b/src/libsystemd-terminal/sysview.h
deleted file mode 100644
index a5e7a38df3..0000000000
--- a/src/libsystemd-terminal/sysview.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * System View
- * The sysview interface scans and monitors the system for seats, sessions and
- * devices. It basically mirrors the state of logind on the application side.
- * It's meant as base for session services that require managed device access.
- * The logind controller API is employed to allow unprivileged access to all
- * devices of a user.
- * Furthermore, the sysview interface can be used for system services that run
- * in situations where logind is not available, but session-like services are
- * needed. For instance, the initrd does not run logind but might require
- * graphics access. It cannot run session services, though. The sysview
- * interface pretends that a session is available and provides the same
- * interface as to normal session services.
- */
-
-#pragma once
-
-#include <stdbool.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-
-typedef struct sysview_event sysview_event;
-typedef struct sysview_device sysview_device;
-typedef struct sysview_session sysview_session;
-typedef struct sysview_seat sysview_seat;
-typedef struct sysview_context sysview_context;
-
-/*
- * Events
- */
-
-enum {
- SYSVIEW_EVENT_SETTLE,
-
- SYSVIEW_EVENT_SEAT_ADD,
- SYSVIEW_EVENT_SEAT_REMOVE,
-
- SYSVIEW_EVENT_SESSION_FILTER,
- SYSVIEW_EVENT_SESSION_ADD,
- SYSVIEW_EVENT_SESSION_REMOVE,
- SYSVIEW_EVENT_SESSION_ATTACH,
- SYSVIEW_EVENT_SESSION_DETACH,
- SYSVIEW_EVENT_SESSION_REFRESH,
- SYSVIEW_EVENT_SESSION_CONTROL,
-};
-
-struct sysview_event {
- unsigned int type;
-
- union {
- struct {
- sysview_seat *seat;
- } seat_add, seat_remove;
-
- struct {
- const char *id;
- const char *seatid;
- const char *username;
- unsigned int uid;
- } session_filter;
-
- struct {
- sysview_session *session;
- } session_add, session_remove;
-
- struct {
- sysview_session *session;
- sysview_device *device;
- } session_attach, session_detach;
-
- struct {
- sysview_session *session;
- sysview_device *device;
- struct udev_device *ud;
- } session_refresh;
-
- struct {
- sysview_session *session;
- int error;
- } session_control;
- };
-};
-
-typedef int (*sysview_event_fn) (sysview_context *c, void *userdata, sysview_event *e);
-
-/*
- * Devices
- */
-
-enum {
- SYSVIEW_DEVICE_EVDEV,
- SYSVIEW_DEVICE_DRM,
- SYSVIEW_DEVICE_CNT
-};
-
-const char *sysview_device_get_name(sysview_device *device);
-unsigned int sysview_device_get_type(sysview_device *device);
-struct udev_device *sysview_device_get_ud(sysview_device *device);
-
-/*
- * Sessions
- */
-
-void sysview_session_set_userdata(sysview_session *session, void *userdata);
-void *sysview_session_get_userdata(sysview_session *session);
-
-const char *sysview_session_get_name(sysview_session *session);
-sysview_seat *sysview_session_get_seat(sysview_session *session);
-
-int sysview_session_take_control(sysview_session *session);
-void sysview_session_release_control(sysview_session *session);
-
-/*
- * Seats
- */
-
-const char *sysview_seat_get_name(sysview_seat *seat);
-int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr);
-
-/*
- * Contexts
- */
-
-enum {
- SYSVIEW_CONTEXT_SCAN_LOGIND = (1 << 0),
- SYSVIEW_CONTEXT_SCAN_EVDEV = (1 << 1),
- SYSVIEW_CONTEXT_SCAN_DRM = (1 << 2),
-};
-
-int sysview_context_new(sysview_context **out,
- unsigned int flags,
- sd_event *event,
- sd_bus *sysbus,
- struct udev *ud);
-sysview_context *sysview_context_free(sysview_context *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_context*, sysview_context_free);
-
-bool sysview_context_is_running(sysview_context *c);
-int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata);
-void sysview_context_stop(sysview_context *c);
diff --git a/src/libsystemd-terminal/term-charset.c b/src/libsystemd-terminal/term-charset.c
deleted file mode 100644
index 9db178861c..0000000000
--- a/src/libsystemd-terminal/term-charset.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * VTE Character Sets
- * These are predefined charactersets that can be loaded into GL and GR. By
- * default we use unicode_lower and unicode_upper, that is, both sets have the
- * exact unicode mapping. unicode_lower is effectively ASCII and unicode_upper
- * as defined by the unicode standard (I guess, ISO 8859-1).
- * Several other character sets are defined here. However, all of them are
- * limited to the 96 character space of GL or GR. Everything beyond GR (which
- * was not supported by the classic VTs by DEC but is available in VT emulators
- * that support unicode/UTF8) is always mapped to unicode and cannot be changed
- * by these character sets. Even mapping GL and GR is only available for
- * backwards compatibility as new applications can use the Unicode functionality
- * of the VTE.
- *
- * Moreover, mapping GR is almost unnecessary to support. In fact, Unicode UTF-8
- * support in VTE works by reading every incoming data as UTF-8 stream. This
- * maps GL/ASCII to ASCII, as UTF-8 is backwards compatible to ASCII, however,
- * everything that has the 8th bit set is a >=2-byte haracter in UTF-8. That is,
- * this is in no way backwards compatible to >=VT220 8bit support. Therefore, if
- * someone maps a character set into GR and wants to use them with this VTE,
- * then they must already send UTF-8 characters to use GR (all GR characters are
- * 8-bits). Hence, they can easily also send the correct UTF-8 character for the
- * unicode mapping.
- * The only advantage is that most characters in many sets are 3-byte UTF-8
- * characters and by mapping the set into GR/GL you can use 2 or 1 byte UTF-8
- * characters which saves bandwidth.
- * Another reason is, if you have older applications that use the VT220 8-bit
- * support and you put a ASCII/8bit-extension to UTF-8 converter in between, you
- * need these mappings to have the application behave correctly if it uses GL/GR
- * mappings extensively.
- *
- * Anyway, we support GL/GR mappings so here are the most commonly used maps as
- * defined by Unicode-standard, DEC-private maps and other famous charmaps.
- *
- * Characters 1-32 are always the control characters (part of CL) and cannot be
- * mapped. Characters 34-127 (94 characters) are part of GL and can be mapped.
- * Characters 33 and 128 are not part of GL and always mapped by the VTE.
- * However, for GR they can be mapped differently (96 chars) so we have to
- * include them. The mapper has to take care not to use them in GL.
- */
-
-#include "term-internal.h"
-
-/*
- * Lower Unicode character set. This maps the characters to the basic ASCII
- * characters 33-126. These are all graphics characters defined in ASCII.
- */
-term_charset term_unicode_lower = {
- [0] = 32,
- [1] = 33,
- [2] = 34,
- [3] = 35,
- [4] = 36,
- [5] = 37,
- [6] = 38,
- [7] = 39,
- [8] = 40,
- [9] = 41,
- [10] = 42,
- [11] = 43,
- [12] = 44,
- [13] = 45,
- [14] = 46,
- [15] = 47,
- [16] = 48,
- [17] = 49,
- [18] = 50,
- [19] = 51,
- [20] = 52,
- [21] = 53,
- [22] = 54,
- [23] = 55,
- [24] = 56,
- [25] = 57,
- [26] = 58,
- [27] = 59,
- [28] = 60,
- [29] = 61,
- [30] = 62,
- [31] = 63,
- [32] = 64,
- [33] = 65,
- [34] = 66,
- [35] = 67,
- [36] = 68,
- [37] = 69,
- [38] = 70,
- [39] = 71,
- [40] = 72,
- [41] = 73,
- [42] = 74,
- [43] = 75,
- [44] = 76,
- [45] = 77,
- [46] = 78,
- [47] = 79,
- [48] = 80,
- [49] = 81,
- [50] = 82,
- [51] = 83,
- [52] = 84,
- [53] = 85,
- [54] = 86,
- [55] = 87,
- [56] = 88,
- [57] = 89,
- [58] = 90,
- [59] = 91,
- [60] = 92,
- [61] = 93,
- [62] = 94,
- [63] = 95,
- [64] = 96,
- [65] = 97,
- [66] = 98,
- [67] = 99,
- [68] = 100,
- [69] = 101,
- [70] = 102,
- [71] = 103,
- [72] = 104,
- [73] = 105,
- [74] = 106,
- [75] = 107,
- [76] = 108,
- [77] = 109,
- [78] = 110,
- [79] = 111,
- [80] = 112,
- [81] = 113,
- [82] = 114,
- [83] = 115,
- [84] = 116,
- [85] = 117,
- [86] = 118,
- [87] = 119,
- [88] = 120,
- [89] = 121,
- [90] = 122,
- [91] = 123,
- [92] = 124,
- [93] = 125,
- [94] = 126,
- [95] = 127,
-};
-
-/*
- * Upper Unicode Table
- * This maps all characters to the upper unicode characters 161-254. These are
- * not compatible to any older 8 bit character sets. See the Unicode standard
- * for the definitions of each symbol.
- */
-term_charset term_unicode_upper = {
- [0] = 160,
- [1] = 161,
- [2] = 162,
- [3] = 163,
- [4] = 164,
- [5] = 165,
- [6] = 166,
- [7] = 167,
- [8] = 168,
- [9] = 169,
- [10] = 170,
- [11] = 171,
- [12] = 172,
- [13] = 173,
- [14] = 174,
- [15] = 175,
- [16] = 176,
- [17] = 177,
- [18] = 178,
- [19] = 179,
- [20] = 180,
- [21] = 181,
- [22] = 182,
- [23] = 183,
- [24] = 184,
- [25] = 185,
- [26] = 186,
- [27] = 187,
- [28] = 188,
- [29] = 189,
- [30] = 190,
- [31] = 191,
- [32] = 192,
- [33] = 193,
- [34] = 194,
- [35] = 195,
- [36] = 196,
- [37] = 197,
- [38] = 198,
- [39] = 199,
- [40] = 200,
- [41] = 201,
- [42] = 202,
- [43] = 203,
- [44] = 204,
- [45] = 205,
- [46] = 206,
- [47] = 207,
- [48] = 208,
- [49] = 209,
- [50] = 210,
- [51] = 211,
- [52] = 212,
- [53] = 213,
- [54] = 214,
- [55] = 215,
- [56] = 216,
- [57] = 217,
- [58] = 218,
- [59] = 219,
- [60] = 220,
- [61] = 221,
- [62] = 222,
- [63] = 223,
- [64] = 224,
- [65] = 225,
- [66] = 226,
- [67] = 227,
- [68] = 228,
- [69] = 229,
- [70] = 230,
- [71] = 231,
- [72] = 232,
- [73] = 233,
- [74] = 234,
- [75] = 235,
- [76] = 236,
- [77] = 237,
- [78] = 238,
- [79] = 239,
- [80] = 240,
- [81] = 241,
- [82] = 242,
- [83] = 243,
- [84] = 244,
- [85] = 245,
- [86] = 246,
- [87] = 247,
- [88] = 248,
- [89] = 249,
- [90] = 250,
- [91] = 251,
- [92] = 252,
- [93] = 253,
- [94] = 254,
- [95] = 255,
-};
-
-/*
- * The DEC supplemental graphics set. For its definition see here:
- * http://vt100.net/docs/vt220-rm/table2-3b.html
- * Its basically a mixture of common European symbols that are not part of
- * ASCII. Most often, this is mapped into GR to extend the basci ASCII part.
- *
- * This is very similar to unicode_upper, however, few symbols differ so do not
- * mix them up!
- */
-term_charset term_dec_supplemental_graphics = {
- [0] = -1, /* undefined */
- [1] = 161,
- [2] = 162,
- [3] = 163,
- [4] = 0,
- [5] = 165,
- [6] = 0,
- [7] = 167,
- [8] = 164,
- [9] = 169,
- [10] = 170,
- [11] = 171,
- [12] = 0,
- [13] = 0,
- [14] = 0,
- [15] = 0,
- [16] = 176,
- [17] = 177,
- [18] = 178,
- [19] = 179,
- [20] = 0,
- [21] = 181,
- [22] = 182,
- [23] = 183,
- [24] = 0,
- [25] = 185,
- [26] = 186,
- [27] = 187,
- [28] = 188,
- [29] = 189,
- [30] = 0,
- [31] = 191,
- [32] = 192,
- [33] = 193,
- [34] = 194,
- [35] = 195,
- [36] = 196,
- [37] = 197,
- [38] = 198,
- [39] = 199,
- [40] = 200,
- [41] = 201,
- [42] = 202,
- [43] = 203,
- [44] = 204,
- [45] = 205,
- [46] = 206,
- [47] = 207,
- [48] = 0,
- [49] = 209,
- [50] = 210,
- [51] = 211,
- [52] = 212,
- [53] = 213,
- [54] = 214,
- [55] = 338,
- [56] = 216,
- [57] = 217,
- [58] = 218,
- [59] = 219,
- [60] = 220,
- [61] = 376,
- [62] = 0,
- [63] = 223,
- [64] = 224,
- [65] = 225,
- [66] = 226,
- [67] = 227,
- [68] = 228,
- [69] = 229,
- [70] = 230,
- [71] = 231,
- [72] = 232,
- [73] = 233,
- [74] = 234,
- [75] = 235,
- [76] = 236,
- [77] = 237,
- [78] = 238,
- [79] = 239,
- [80] = 0,
- [81] = 241,
- [82] = 242,
- [83] = 243,
- [84] = 244,
- [85] = 245,
- [86] = 246,
- [87] = 339,
- [88] = 248,
- [89] = 249,
- [90] = 250,
- [91] = 251,
- [92] = 252,
- [93] = 255,
- [94] = 0,
- [95] = -1, /* undefined */
-};
-
-/*
- * DEC special graphics character set. See here for its definition:
- * http://vt100.net/docs/vt220-rm/table2-4.html
- * This contains several characters to create ASCII drawings and similar. Its
- * commonly mapped into GR to extend the basic ASCII characters.
- *
- * Lower 62 characters map to ASCII 33-64, everything beyond is special and
- * commonly used for ASCII drawings. It depends on the Unicode Standard 3.2 for
- * the extended horizontal scan-line characters 3, 5, 7, and 9.
- */
-term_charset term_dec_special_graphics = {
- [0] = -1, /* undefined */
- [1] = 33,
- [2] = 34,
- [3] = 35,
- [4] = 36,
- [5] = 37,
- [6] = 38,
- [7] = 39,
- [8] = 40,
- [9] = 41,
- [10] = 42,
- [11] = 43,
- [12] = 44,
- [13] = 45,
- [14] = 46,
- [15] = 47,
- [16] = 48,
- [17] = 49,
- [18] = 50,
- [19] = 51,
- [20] = 52,
- [21] = 53,
- [22] = 54,
- [23] = 55,
- [24] = 56,
- [25] = 57,
- [26] = 58,
- [27] = 59,
- [28] = 60,
- [29] = 61,
- [30] = 62,
- [31] = 63,
- [32] = 64,
- [33] = 65,
- [34] = 66,
- [35] = 67,
- [36] = 68,
- [37] = 69,
- [38] = 70,
- [39] = 71,
- [40] = 72,
- [41] = 73,
- [42] = 74,
- [43] = 75,
- [44] = 76,
- [45] = 77,
- [46] = 78,
- [47] = 79,
- [48] = 80,
- [49] = 81,
- [50] = 82,
- [51] = 83,
- [52] = 84,
- [53] = 85,
- [54] = 86,
- [55] = 87,
- [56] = 88,
- [57] = 89,
- [58] = 90,
- [59] = 91,
- [60] = 92,
- [61] = 93,
- [62] = 94,
- [63] = 0,
- [64] = 9830,
- [65] = 9618,
- [66] = 9225,
- [67] = 9228,
- [68] = 9229,
- [69] = 9226,
- [70] = 176,
- [71] = 177,
- [72] = 9252,
- [73] = 9227,
- [74] = 9496,
- [75] = 9488,
- [76] = 9484,
- [77] = 9492,
- [78] = 9532,
- [79] = 9146,
- [80] = 9147,
- [81] = 9472,
- [82] = 9148,
- [83] = 9149,
- [84] = 9500,
- [85] = 9508,
- [86] = 9524,
- [87] = 9516,
- [88] = 9474,
- [89] = 8804,
- [90] = 8805,
- [91] = 960,
- [92] = 8800,
- [93] = 163,
- [94] = 8901,
- [95] = -1, /* undefined */
-};
diff --git a/src/libsystemd-terminal/term-internal.h b/src/libsystemd-terminal/term-internal.h
deleted file mode 100644
index 8c6a00188c..0000000000
--- a/src/libsystemd-terminal/term-internal.h
+++ /dev/null
@@ -1,650 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "term.h"
-#include "util.h"
-
-typedef struct term_char term_char_t;
-typedef struct term_charbuf term_charbuf_t;
-
-typedef struct term_cell term_cell;
-typedef struct term_line term_line;
-
-typedef struct term_page term_page;
-typedef struct term_history term_history;
-
-typedef uint32_t term_charset[96];
-typedef struct term_state term_state;
-
-/*
- * Miscellaneous
- * Sundry things and external helpers.
- */
-
-int mk_wcwidth(wchar_t ucs4);
-int mk_wcwidth_cjk(wchar_t ucs4);
-int mk_wcswidth(const wchar_t *str, size_t len);
-int mk_wcswidth_cjk(const wchar_t *str, size_t len);
-
-/*
- * Characters
- * Each cell in a terminal page contains only a single character. This is
- * usually a single UCS-4 value. However, Unicode allows combining-characters,
- * therefore, the number of UCS-4 characters per cell must be unlimited. The
- * term_char_t object wraps the internal combining char API so it can be
- * treated as a single object.
- */
-
-struct term_char {
- /* never access this value directly */
- uint64_t _value;
-};
-
-struct term_charbuf {
- /* 3 bytes + zero-terminator */
- uint32_t buf[4];
-};
-
-#define TERM_CHAR_INIT(_val) ((term_char_t){ ._value = (_val) })
-#define TERM_CHAR_NULL TERM_CHAR_INIT(0)
-
-term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4);
-term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4);
-term_char_t term_char_dup(term_char_t ch);
-term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4);
-
-const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b);
-unsigned int term_char_lookup_width(term_char_t ch);
-
-/* true if @ch is TERM_CHAR_NULL, otherwise false */
-static inline bool term_char_is_null(term_char_t ch) {
- return ch._value == 0;
-}
-
-/* true if @ch is dynamically allocated and needs to be freed */
-static inline bool term_char_is_allocated(term_char_t ch) {
- return !term_char_is_null(ch) && !(ch._value & 0x1);
-}
-
-/* true if (a == b), otherwise false; this is (a == b), NOT (*a == *b) */
-static inline bool term_char_same(term_char_t a, term_char_t b) {
- return a._value == b._value;
-}
-
-/* true if (*a == *b), otherwise false; this is implied by (a == b) */
-static inline bool term_char_equal(term_char_t a, term_char_t b) {
- const uint32_t *sa, *sb;
- term_charbuf_t ca, cb;
- size_t na, nb;
-
- sa = term_char_resolve(a, &na, &ca);
- sb = term_char_resolve(b, &nb, &cb);
- return na == nb && !memcmp(sa, sb, sizeof(*sa) * na);
-}
-
-/* free @ch in case it is dynamically allocated */
-static inline term_char_t term_char_free(term_char_t ch) {
- if (term_char_is_allocated(ch))
- term_char_set(ch, 0);
-
- return TERM_CHAR_NULL;
-}
-
-/* gcc _cleanup_ helpers */
-#define _term_char_free_ _cleanup_(term_char_freep)
-static inline void term_char_freep(term_char_t *p) {
- term_char_free(*p);
-}
-
-/*
- * Cells
- * The term_cell structure respresents a single cell in a terminal page. It
- * contains the stored character, the age of the cell and all its attributes.
- */
-
-struct term_cell {
- term_char_t ch; /* stored char or TERM_CHAR_NULL */
- term_age_t age; /* cell age or TERM_AGE_NULL */
- term_attr attr; /* cell attributes */
- unsigned int cwidth; /* cached term_char_lookup_width(cell->ch) */
-};
-
-/*
- * Lines
- * Instead of storing cells in a 2D array, we store them in an array of
- * dynamically allocated lines. This way, scrolling can be implemented very
- * fast without moving any cells at all. Similarly, the scrollback-buffer is
- * much simpler to implement.
- * We use term_line to store a single line. It contains an array of cells, a
- * fill-state which remembers the amount of blanks on the right side, a
- * separate age just for the line which can overwrite the age for all cells,
- * and some management data.
- */
-
-struct term_line {
- term_line *lines_next; /* linked-list for histories */
- term_line *lines_prev; /* linked-list for histories */
-
- unsigned int width; /* visible width of line */
- unsigned int n_cells; /* # of allocated cells */
- term_cell *cells; /* cell-array */
-
- term_age_t age; /* line age */
- unsigned int fill; /* # of valid cells; starting left */
-};
-
-int term_line_new(term_line **out);
-term_line *term_line_free(term_line *line);
-
-#define _term_line_free_ _cleanup_(term_line_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_line*, term_line_free);
-
-int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width);
-void term_line_set_width(term_line *line, unsigned int width);
-void term_line_write(term_line *line, unsigned int pos_x, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode);
-void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
-void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
-void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age);
-void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected);
-void term_line_reset(term_line *line, const term_attr *attr, term_age_t age);
-
-void term_line_link(term_line *line, term_line **first, term_line **last);
-void term_line_link_tail(term_line *line, term_line **first, term_line **last);
-void term_line_unlink(term_line *line, term_line **first, term_line **last);
-
-#define TERM_LINE_LINK(_line, _head) term_line_link((_line), &(_head)->lines_first, &(_head)->lines_last)
-#define TERM_LINE_LINK_TAIL(_line, _head) term_line_link_tail((_line), &(_head)->lines_first, &(_head)->lines_last)
-#define TERM_LINE_UNLINK(_line, _head) term_line_unlink((_line), &(_head)->lines_first, &(_head)->lines_last)
-
-/*
- * Pages
- * A page represents the 2D table containing all cells of a terminal. It stores
- * lines as an array of pointers so scrolling becomes a simple line-shuffle
- * operation.
- * Scrolling is always targeted only at the scroll-region defined via scroll_idx
- * and scroll_num. The fill-state keeps track of the number of touched lines in
- * the scroll-region. @width and @height describe the visible region of the page
- * and are guaranteed to be allocated at all times.
- */
-
-struct term_page {
- term_age_t age; /* page age */
-
- term_line **lines; /* array of line-pointers */
- term_line **line_cache; /* cache for temporary operations */
- unsigned int n_lines; /* # of allocated lines */
-
- unsigned int width; /* width of visible area */
- unsigned int height; /* height of visible area */
- unsigned int scroll_idx; /* scrolling-region start index */
- unsigned int scroll_num; /* scrolling-region length in lines */
- unsigned int scroll_fill; /* # of valid scroll-lines */
-};
-
-int term_page_new(term_page **out);
-term_page *term_page_free(term_page *page);
-
-#define _term_page_free_ _cleanup_(term_page_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_page*, term_page_free);
-
-term_cell *term_page_get_cell(term_page *page, unsigned int x, unsigned int y);
-
-int term_page_reserve(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age);
-void term_page_resize(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age, term_history *history);
-void term_page_write(term_page *page, unsigned int pos_x, unsigned int pos_y, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode);
-void term_page_insert_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age);
-void term_page_delete_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age);
-void term_page_append_combchar(term_page *page, unsigned int pos_x, unsigned int pos_y, uint32_t ucs4, term_age_t age);
-void term_page_erase(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int to_x, unsigned int to_y, const term_attr *attr, term_age_t age, bool keep_protected);
-void term_page_reset(term_page *page, const term_attr *attr, term_age_t age);
-
-void term_page_set_scroll_region(term_page *page, unsigned int idx, unsigned int num);
-void term_page_scroll_up(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
-void term_page_scroll_down(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
-void term_page_insert_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
-void term_page_delete_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
-
-/*
- * Histories
- * Scroll-back buffers use term_history objects to store scroll-back lines. A
- * page is independent of the history used. All page operations that modify a
- * history take it as separate argument. You're free to pass NULL at all times
- * if no history should be used.
- * Lines are stored in a linked list as no complex operations are ever done on
- * history lines, besides pushing/poping. Note that history lines do not have a
- * guaranteed minimum length. Any kind of line might be stored there. Missing
- * cells should be cleared to the background color.
- */
-
-struct term_history {
- term_line *lines_first;
- term_line *lines_last;
- unsigned int n_lines;
- unsigned int max_lines;
-};
-
-int term_history_new(term_history **out);
-term_history *term_history_free(term_history *history);
-
-#define _term_history_free_ _cleanup_(term_history_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_history*, term_history_free);
-
-void term_history_clear(term_history *history);
-void term_history_trim(term_history *history, unsigned int max);
-void term_history_push(term_history *history, term_line *line);
-term_line *term_history_pop(term_history *history, unsigned int reserve_width, const term_attr *attr, term_age_t age);
-unsigned int term_history_peek(term_history *history, unsigned int max, unsigned int reserve_width, const term_attr *attr, term_age_t age);
-
-/*
- * Parsers
- * The term_parser object parses control-sequences for both host and terminal
- * side. Based on this parser, there is a set of command-parsers that take a
- * term_seq sequence and returns the command it represents. This is different
- * for host and terminal side so a different set of parsers is provided.
- */
-
-enum {
- TERM_SEQ_NONE, /* placeholder, no sequence parsed */
-
- TERM_SEQ_IGNORE, /* no-op character */
- TERM_SEQ_GRAPHIC, /* graphic character */
- TERM_SEQ_CONTROL, /* control character */
- TERM_SEQ_ESCAPE, /* escape sequence */
- TERM_SEQ_CSI, /* control sequence function */
- TERM_SEQ_DCS, /* device control string */
- TERM_SEQ_OSC, /* operating system control */
-
- TERM_SEQ_CNT
-};
-
-enum {
- /* these must be kept compatible to (1U << (ch - 0x20)) */
-
- TERM_SEQ_FLAG_SPACE = (1U << 0), /* char: */
- TERM_SEQ_FLAG_BANG = (1U << 1), /* char: ! */
- TERM_SEQ_FLAG_DQUOTE = (1U << 2), /* char: " */
- TERM_SEQ_FLAG_HASH = (1U << 3), /* char: # */
- TERM_SEQ_FLAG_CASH = (1U << 4), /* char: $ */
- TERM_SEQ_FLAG_PERCENT = (1U << 5), /* char: % */
- TERM_SEQ_FLAG_AND = (1U << 6), /* char: & */
- TERM_SEQ_FLAG_SQUOTE = (1U << 7), /* char: ' */
- TERM_SEQ_FLAG_POPEN = (1U << 8), /* char: ( */
- TERM_SEQ_FLAG_PCLOSE = (1U << 9), /* char: ) */
- TERM_SEQ_FLAG_MULT = (1U << 10), /* char: * */
- TERM_SEQ_FLAG_PLUS = (1U << 11), /* char: + */
- TERM_SEQ_FLAG_COMMA = (1U << 12), /* char: , */
- TERM_SEQ_FLAG_MINUS = (1U << 13), /* char: - */
- TERM_SEQ_FLAG_DOT = (1U << 14), /* char: . */
- TERM_SEQ_FLAG_SLASH = (1U << 15), /* char: / */
-
- /* 16-35 is reserved for numbers; unused */
-
- /* COLON is reserved = (1U << 26), char: : */
- /* SEMICOLON is reserved = (1U << 27), char: ; */
- TERM_SEQ_FLAG_LT = (1U << 28), /* char: < */
- TERM_SEQ_FLAG_EQUAL = (1U << 29), /* char: = */
- TERM_SEQ_FLAG_GT = (1U << 30), /* char: > */
- TERM_SEQ_FLAG_WHAT = (1U << 31), /* char: ? */
-};
-
-enum {
- TERM_CMD_NONE, /* placeholder */
- TERM_CMD_GRAPHIC, /* graphics character */
-
- TERM_CMD_BEL, /* bell */
- TERM_CMD_BS, /* backspace */
- TERM_CMD_CBT, /* cursor-backward-tabulation */
- TERM_CMD_CHA, /* cursor-horizontal-absolute */
- TERM_CMD_CHT, /* cursor-horizontal-forward-tabulation */
- TERM_CMD_CNL, /* cursor-next-line */
- TERM_CMD_CPL, /* cursor-previous-line */
- TERM_CMD_CR, /* carriage-return */
- TERM_CMD_CUB, /* cursor-backward */
- TERM_CMD_CUD, /* cursor-down */
- TERM_CMD_CUF, /* cursor-forward */
- TERM_CMD_CUP, /* cursor-position */
- TERM_CMD_CUU, /* cursor-up */
- TERM_CMD_DA1, /* primary-device-attributes */
- TERM_CMD_DA2, /* secondary-device-attributes */
- TERM_CMD_DA3, /* tertiary-device-attributes */
- TERM_CMD_DC1, /* device-control-1 or XON */
- TERM_CMD_DC3, /* device-control-3 or XOFF */
- TERM_CMD_DCH, /* delete-character */
- TERM_CMD_DECALN, /* screen-alignment-pattern */
- TERM_CMD_DECANM, /* ansi-mode */
- TERM_CMD_DECBI, /* back-index */
- TERM_CMD_DECCARA, /* change-attributes-in-rectangular-area */
- TERM_CMD_DECCRA, /* copy-rectangular-area */
- TERM_CMD_DECDC, /* delete-column */
- TERM_CMD_DECDHL_BH, /* double-width-double-height-line: bottom half */
- TERM_CMD_DECDHL_TH, /* double-width-double-height-line: top half */
- TERM_CMD_DECDWL, /* double-width-single-height-line */
- TERM_CMD_DECEFR, /* enable-filter-rectangle */
- TERM_CMD_DECELF, /* enable-local-functions */
- TERM_CMD_DECELR, /* enable-locator-reporting */
- TERM_CMD_DECERA, /* erase-rectangular-area */
- TERM_CMD_DECFI, /* forward-index */
- TERM_CMD_DECFRA, /* fill-rectangular-area */
- TERM_CMD_DECIC, /* insert-column */
- TERM_CMD_DECID, /* return-terminal-id */
- TERM_CMD_DECINVM, /* invoke-macro */
- TERM_CMD_DECKBD, /* keyboard-language-selection */
- TERM_CMD_DECKPAM, /* keypad-application-mode */
- TERM_CMD_DECKPNM, /* keypad-numeric-mode */
- TERM_CMD_DECLFKC, /* local-function-key-control */
- TERM_CMD_DECLL, /* load-leds */
- TERM_CMD_DECLTOD, /* load-time-of-day */
- TERM_CMD_DECPCTERM, /* pcterm-mode */
- TERM_CMD_DECPKA, /* program-key-action */
- TERM_CMD_DECPKFMR, /* program-key-free-memory-report */
- TERM_CMD_DECRARA, /* reverse-attributes-in-rectangular-area */
- TERM_CMD_DECRC, /* restore-cursor */
- TERM_CMD_DECREQTPARM, /* request-terminal-parameters */
- TERM_CMD_DECRPKT, /* report-key-type */
- TERM_CMD_DECRQCRA, /* request-checksum-of-rectangular-area */
- TERM_CMD_DECRQDE, /* request-display-extent */
- TERM_CMD_DECRQKT, /* request-key-type */
- TERM_CMD_DECRQLP, /* request-locator-position */
- TERM_CMD_DECRQM_ANSI, /* request-mode-ansi */
- TERM_CMD_DECRQM_DEC, /* request-mode-dec */
- TERM_CMD_DECRQPKFM, /* request-program-key-free-memory */
- TERM_CMD_DECRQPSR, /* request-presentation-state-report */
- TERM_CMD_DECRQTSR, /* request-terminal-state-report */
- TERM_CMD_DECRQUPSS, /* request-user-preferred-supplemental-set */
- TERM_CMD_DECSACE, /* select-attribute-change-extent */
- TERM_CMD_DECSASD, /* select-active-status-display */
- TERM_CMD_DECSC, /* save-cursor */
- TERM_CMD_DECSCA, /* select-character-protection-attribute */
- TERM_CMD_DECSCL, /* select-conformance-level */
- TERM_CMD_DECSCP, /* select-communication-port */
- TERM_CMD_DECSCPP, /* select-columns-per-page */
- TERM_CMD_DECSCS, /* select-communication-speed */
- TERM_CMD_DECSCUSR, /* set-cursor-style */
- TERM_CMD_DECSDDT, /* select-disconnect-delay-time */
- TERM_CMD_DECSDPT, /* select-digital-printed-data-type */
- TERM_CMD_DECSED, /* selective-erase-in-display */
- TERM_CMD_DECSEL, /* selective-erase-in-line */
- TERM_CMD_DECSERA, /* selective-erase-rectangular-area */
- TERM_CMD_DECSFC, /* select-flow-control */
- TERM_CMD_DECSKCV, /* set-key-click-volume */
- TERM_CMD_DECSLCK, /* set-lock-key-style */
- TERM_CMD_DECSLE, /* select-locator-events */
- TERM_CMD_DECSLPP, /* set-lines-per-page */
- TERM_CMD_DECSLRM_OR_SC, /* set-left-and-right-margins or save-cursor */
- TERM_CMD_DECSMBV, /* set-margin-bell-volume */
- TERM_CMD_DECSMKR, /* select-modifier-key-reporting */
- TERM_CMD_DECSNLS, /* set-lines-per-screen */
- TERM_CMD_DECSPP, /* set-port-parameter */
- TERM_CMD_DECSPPCS, /* select-pro-printer-character-set */
- TERM_CMD_DECSPRTT, /* select-printer-type */
- TERM_CMD_DECSR, /* secure-reset */
- TERM_CMD_DECSRFR, /* select-refresh-rate */
- TERM_CMD_DECSSCLS, /* set-scroll-speed */
- TERM_CMD_DECSSDT, /* select-status-display-line-type */
- TERM_CMD_DECSSL, /* select-setup-language */
- TERM_CMD_DECST8C, /* set-tab-at-every-8-columns */
- TERM_CMD_DECSTBM, /* set-top-and-bottom-margins */
- TERM_CMD_DECSTR, /* soft-terminal-reset */
- TERM_CMD_DECSTRL, /* set-transmit-rate-limit */
- TERM_CMD_DECSWBV, /* set-warning-bell-volume */
- TERM_CMD_DECSWL, /* single-width-single-height-line */
- TERM_CMD_DECTID, /* select-terminal-id */
- TERM_CMD_DECTME, /* terminal-mode-emulation */
- TERM_CMD_DECTST, /* invoke-confidence-test */
- TERM_CMD_DL, /* delete-line */
- TERM_CMD_DSR_ANSI, /* device-status-report-ansi */
- TERM_CMD_DSR_DEC, /* device-status-report-dec */
- TERM_CMD_ECH, /* erase-character */
- TERM_CMD_ED, /* erase-in-display */
- TERM_CMD_EL, /* erase-in-line */
- TERM_CMD_ENQ, /* enquiry */
- TERM_CMD_EPA, /* end-of-guarded-area */
- TERM_CMD_FF, /* form-feed */
- TERM_CMD_HPA, /* horizontal-position-absolute */
- TERM_CMD_HPR, /* horizontal-position-relative */
- TERM_CMD_HT, /* horizontal-tab */
- TERM_CMD_HTS, /* horizontal-tab-set */
- TERM_CMD_HVP, /* horizontal-and-vertical-position */
- TERM_CMD_ICH, /* insert-character */
- TERM_CMD_IL, /* insert-line */
- TERM_CMD_IND, /* index */
- TERM_CMD_LF, /* line-feed */
- TERM_CMD_LS1R, /* locking-shift-1-right */
- TERM_CMD_LS2, /* locking-shift-2 */
- TERM_CMD_LS2R, /* locking-shift-2-right */
- TERM_CMD_LS3, /* locking-shift-3 */
- TERM_CMD_LS3R, /* locking-shift-3-right */
- TERM_CMD_MC_ANSI, /* media-copy-ansi */
- TERM_CMD_MC_DEC, /* media-copy-dec */
- TERM_CMD_NEL, /* next-line */
- TERM_CMD_NP, /* next-page */
- TERM_CMD_NULL, /* null */
- TERM_CMD_PP, /* preceding-page */
- TERM_CMD_PPA, /* page-position-absolute */
- TERM_CMD_PPB, /* page-position-backward */
- TERM_CMD_PPR, /* page-position-relative */
- TERM_CMD_RC, /* restore-cursor */
- TERM_CMD_REP, /* repeat */
- TERM_CMD_RI, /* reverse-index */
- TERM_CMD_RIS, /* reset-to-initial-state */
- TERM_CMD_RM_ANSI, /* reset-mode-ansi */
- TERM_CMD_RM_DEC, /* reset-mode-dec */
- TERM_CMD_S7C1T, /* set-7bit-c1-terminal */
- TERM_CMD_S8C1T, /* set-8bit-c1-terminal */
- TERM_CMD_SCS, /* select-character-set */
- TERM_CMD_SD, /* scroll-down */
- TERM_CMD_SGR, /* select-graphics-rendition */
- TERM_CMD_SI, /* shift-in */
- TERM_CMD_SM_ANSI, /* set-mode-ansi */
- TERM_CMD_SM_DEC, /* set-mode-dec */
- TERM_CMD_SO, /* shift-out */
- TERM_CMD_SPA, /* start-of-protected-area */
- TERM_CMD_SS2, /* single-shift-2 */
- TERM_CMD_SS3, /* single-shift-3 */
- TERM_CMD_ST, /* string-terminator */
- TERM_CMD_SU, /* scroll-up */
- TERM_CMD_SUB, /* substitute */
- TERM_CMD_TBC, /* tab-clear */
- TERM_CMD_VPA, /* vertical-line-position-absolute */
- TERM_CMD_VPR, /* vertical-line-position-relative */
- TERM_CMD_VT, /* vertical-tab */
- TERM_CMD_XTERM_CLLHP, /* xterm-cursor-lower-left-hp-bugfix */
- TERM_CMD_XTERM_IHMT, /* xterm-initiate-highlight-mouse-tracking */
- TERM_CMD_XTERM_MLHP, /* xterm-memory-lock-hp-bugfix */
- TERM_CMD_XTERM_MUHP, /* xterm-memory-unlock-hp-bugfix */
- TERM_CMD_XTERM_RPM, /* xterm-restore-private-mode */
- TERM_CMD_XTERM_RRV, /* xterm-reset-resource-value */
- TERM_CMD_XTERM_RTM, /* xterm-reset-title-mode */
- TERM_CMD_XTERM_SACL1, /* xterm-set-ansi-conformance-level-1 */
- TERM_CMD_XTERM_SACL2, /* xterm-set-ansi-conformance-level-2 */
- TERM_CMD_XTERM_SACL3, /* xterm-set-ansi-conformance-level-3 */
- TERM_CMD_XTERM_SDCS, /* xterm-set-default-character-set */
- TERM_CMD_XTERM_SGFX, /* xterm-sixel-graphics */
- TERM_CMD_XTERM_SPM, /* xterm-set-private-mode */
- TERM_CMD_XTERM_SRV, /* xterm-set-resource-value */
- TERM_CMD_XTERM_STM, /* xterm-set-title-mode */
- TERM_CMD_XTERM_SUCS, /* xterm-set-utf8-character-set */
- TERM_CMD_XTERM_WM, /* xterm-window-management */
-
- TERM_CMD_CNT
-};
-
-enum {
- /*
- * Charsets: DEC marks charsets according to "Digital Equ. Corp.".
- * NRCS marks charsets according to the "National Replacement
- * Character Sets". ISO marks charsets according to ISO-8859.
- * The USERDEF charset is special and can be modified by the host.
- */
-
- TERM_CHARSET_NONE,
-
- /* 96-compat charsets */
- TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
- TERM_CHARSET_BRITISH_NRCS = TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
- TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
- TERM_CHARSET_AMERICAN_NRCS = TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
- TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL,
- TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL,
- TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL,
- TERM_CHARSET_ISO_LATIN_CYRILLIC,
-
- TERM_CHARSET_96_CNT,
-
- /* 94-compat charsets */
- TERM_CHARSET_DEC_SPECIAL_GRAPHIC = TERM_CHARSET_96_CNT,
- TERM_CHARSET_DEC_SUPPLEMENTAL,
- TERM_CHARSET_DEC_TECHNICAL,
- TERM_CHARSET_CYRILLIC_DEC,
- TERM_CHARSET_DUTCH_NRCS,
- TERM_CHARSET_FINNISH_NRCS,
- TERM_CHARSET_FRENCH_NRCS,
- TERM_CHARSET_FRENCH_CANADIAN_NRCS,
- TERM_CHARSET_GERMAN_NRCS,
- TERM_CHARSET_GREEK_DEC,
- TERM_CHARSET_GREEK_NRCS,
- TERM_CHARSET_HEBREW_DEC,
- TERM_CHARSET_HEBREW_NRCS,
- TERM_CHARSET_ITALIAN_NRCS,
- TERM_CHARSET_NORWEGIAN_DANISH_NRCS,
- TERM_CHARSET_PORTUGUESE_NRCS,
- TERM_CHARSET_RUSSIAN_NRCS,
- TERM_CHARSET_SCS_NRCS,
- TERM_CHARSET_SPANISH_NRCS,
- TERM_CHARSET_SWEDISH_NRCS,
- TERM_CHARSET_SWISS_NRCS,
- TERM_CHARSET_TURKISH_DEC,
- TERM_CHARSET_TURKISH_NRCS,
-
- TERM_CHARSET_94_CNT,
-
- /* special charsets */
- TERM_CHARSET_USERPREF_SUPPLEMENTAL = TERM_CHARSET_94_CNT,
-
- TERM_CHARSET_CNT,
-};
-
-extern term_charset term_unicode_lower;
-extern term_charset term_unicode_upper;
-extern term_charset term_dec_supplemental_graphics;
-extern term_charset term_dec_special_graphics;
-
-#define TERM_PARSER_ARG_MAX (16)
-#define TERM_PARSER_ST_MAX (4096)
-
-struct term_seq {
- unsigned int type;
- unsigned int command;
- uint32_t terminator;
- unsigned int intermediates;
- unsigned int charset;
- unsigned int n_args;
- int args[TERM_PARSER_ARG_MAX];
- unsigned int n_st;
- char *st;
-};
-
-struct term_parser {
- term_seq seq;
- size_t st_alloc;
- unsigned int state;
-
- bool is_host : 1;
-};
-
-/*
- * Screens
- * A term_screen object represents the terminal-side of the communication. It
- * connects the term-parser and term-pages and handles all required commands.
- * All state is managed by it.
- */
-
-enum {
- TERM_FLAG_7BIT_MODE = (1U << 0), /* 7bit mode (default: on) */
- TERM_FLAG_HIDE_CURSOR = (1U << 1), /* hide cursor caret (default: off) */
- TERM_FLAG_INHIBIT_TPARM = (1U << 2), /* do not send TPARM unrequested (default: off) */
- TERM_FLAG_NEWLINE_MODE = (1U << 3), /* perform carriage-return on line-feeds (default: off) */
- TERM_FLAG_PENDING_WRAP = (1U << 4), /* wrap-around is pending */
- TERM_FLAG_KEYPAD_MODE = (1U << 5), /* application-keypad mode (default: off) */
- TERM_FLAG_CURSOR_KEYS = (1U << 6), /* enable application cursor-keys (default: off) */
-};
-
-enum {
- TERM_CONFORMANCE_LEVEL_VT52,
- TERM_CONFORMANCE_LEVEL_VT100,
- TERM_CONFORMANCE_LEVEL_VT400,
- TERM_CONFORMANCE_LEVEL_CNT,
-};
-
-struct term_state {
- unsigned int cursor_x;
- unsigned int cursor_y;
- term_attr attr;
- term_charset **gl;
- term_charset **gr;
- term_charset **glt;
- term_charset **grt;
-
- bool auto_wrap : 1;
- bool origin_mode : 1;
-};
-
-struct term_screen {
- unsigned long ref;
- term_age_t age;
-
- term_page *page;
- term_page *page_main;
- term_page *page_alt;
- term_history *history;
- term_history *history_main;
-
- unsigned int n_tabs;
- uint8_t *tabs;
-
- term_utf8 utf8;
- term_parser *parser;
-
- term_screen_write_fn write_fn;
- void *write_fn_data;
- term_screen_cmd_fn cmd_fn;
- void *cmd_fn_data;
-
- unsigned int flags;
- unsigned int conformance_level;
- term_attr default_attr;
-
- term_charset *g0;
- term_charset *g1;
- term_charset *g2;
- term_charset *g3;
-
- char *answerback;
-
- term_state state;
- term_state saved;
- term_state saved_alt;
-};
diff --git a/src/libsystemd-terminal/term-page.c b/src/libsystemd-terminal/term-page.c
deleted file mode 100644
index bac85200f1..0000000000
--- a/src/libsystemd-terminal/term-page.c
+++ /dev/null
@@ -1,2091 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Terminal Page/Line/Cell/Char Handling
- * This file implements page handling of a terminal. It is split into pages,
- * lines, cells and characters. Each object is independent of the next upper
- * object.
- *
- * The Terminal layer keeps each line of a terminal separate and dynamically
- * allocated. This allows us to move lines from main-screen to history-buffers
- * very fast. Same is true for scrolling, top/bottom borders and other buffer
- * operations.
- *
- * While lines are dynamically allocated, cells are not. This would be a waste
- * of memory and causes heavy fragmentation. Furthermore, cells are moved much
- * less frequently than lines so the performance-penalty is pretty small.
- * However, to support combining-characters, we have to initialize and cleanup
- * cells properly and cannot just release the underlying memory. Therefore,
- * cells are treated as proper objects despite being allocated in arrays.
- *
- * Each cell has a set of attributes and a stored character. This is usually a
- * single Unicode character stored as 32bit UCS-4 char. However, we need to
- * support Unicode combining-characters, therefore this gets more complicated.
- * Characters themselves are represented by a "term_char_t" object. It
- * should be treated as a normal integer and passed by value. The
- * surrounding struct is just to hide the internals. A term-char can contain a
- * base character together with up to 2 combining-chars in a single integer.
- * Only if you need more combining-chars (very unlikely!) a term-char is a
- * pointer to an allocated storage. This requires you to always free term-char
- * objects once no longer used (even though this is a no-op most of the time).
- * Furthermore, term-char objects are not ref-counted so you must duplicate them
- * in case you want to store it somewhere and retain a copy yourself. By
- * convention, all functions that take a term-char object will not duplicate
- * it but implicitly take ownership of the passed value. It's up to the caller
- * to duplicate it beforehand, in case it wants to retain a copy.
- *
- * If it turns out, that more than 2 comb-chars become common in specific
- * languages, we can try to optimize this. One idea is to ref-count allocated
- * characters and store them in a hash-table (like gnome's libvte3 does). This
- * way we will never have two allocated chars for the same content. Or we can
- * simply put two uint64_t into a "term_char_t". This will slow down operations
- * on systems that don't need that many comb-chars, but avoid the dynamic
- * allocations on others.
- * Anyhow, until we have proper benchmarks, we will keep the current code. It
- * seems to compete very well with other solutions so far.
- *
- * The page-layer is a one-dimensional array of lines. Considering that each
- * line is a one-dimensional array of cells, the page layer provides the
- * two-dimensional cell-page required for terminals. The page itself only
- * operates on lines. All cell-related operations are forwarded to the correct
- * line.
- * A page does not contain any cursor tracking. It only provides the raw
- * operations to shuffle lines and modify the page.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-
-/* maximum UCS-4 character */
-#define CHAR_UCS4_MAX (0x10ffff)
-/* mask for valid UCS-4 characters (21bit) */
-#define CHAR_UCS4_MASK (0x1fffff)
-/* UCS-4 replacement character */
-#define CHAR_UCS4_REPLACEMENT (0xfffd)
-
-/* real storage behind "term_char_t" in case it's not packed */
-typedef struct term_character {
- uint8_t n;
- uint32_t codepoints[];
-} term_character;
-
-/*
- * char_pack() takes 3 UCS-4 values and packs them into a term_char_t object.
- * Note that UCS-4 chars only take 21 bits, so we still have the LSB as marker.
- * We set it to 1 so others can distinguish it from pointers.
- */
-static inline term_char_t char_pack(uint32_t v1, uint32_t v2, uint32_t v3) {
- uint64_t packed, u1, u2, u3;
-
- u1 = v1;
- u2 = v2;
- u3 = v3;
-
- packed = 0x01;
- packed |= (u1 & (uint64_t)CHAR_UCS4_MASK) << 43;
- packed |= (u2 & (uint64_t)CHAR_UCS4_MASK) << 22;
- packed |= (u3 & (uint64_t)CHAR_UCS4_MASK) << 1;
-
- return TERM_CHAR_INIT(packed);
-}
-
-#define char_pack1(_v1) char_pack2((_v1), CHAR_UCS4_MAX + 1)
-#define char_pack2(_v1, _v2) char_pack3((_v1), (_v2), CHAR_UCS4_MAX + 1)
-#define char_pack3(_v1, _v2, _v3) char_pack((_v1), (_v2), (_v3))
-
-/*
- * char_unpack() is the inverse of char_pack(). It extracts the 3 stored UCS-4
- * characters and returns them. Note that this does not validate the passed
- * term_char_t. That's the responsibility of the caller.
- * This returns the number of characters actually packed. This obviously is a
- * number between 0 and 3 (inclusive).
- */
-static inline uint8_t char_unpack(term_char_t packed, uint32_t *out_v1, uint32_t *out_v2, uint32_t *out_v3) {
- uint32_t v1, v2, v3;
-
- v1 = (packed._value >> 43) & (uint64_t)CHAR_UCS4_MASK;
- v2 = (packed._value >> 22) & (uint64_t)CHAR_UCS4_MASK;
- v3 = (packed._value >> 1) & (uint64_t)CHAR_UCS4_MASK;
-
- if (out_v1)
- *out_v1 = v1;
- if (out_v2)
- *out_v2 = v2;
- if (out_v3)
- *out_v3 = v3;
-
- return (v1 > CHAR_UCS4_MAX) ? 0 :
- ((v2 > CHAR_UCS4_MAX) ? 1 :
- ((v3 > CHAR_UCS4_MAX) ? 2 :
- 3));
-}
-
-/* cast a term_char_t to a term_character* */
-static inline term_character *char_to_ptr(term_char_t ch) {
- return (term_character*)(unsigned long)ch._value;
-}
-
-/* cast a term_character* to a term_char_t */
-static inline term_char_t char_from_ptr(term_character *c) {
- return TERM_CHAR_INIT((unsigned long)c);
-}
-
-/*
- * char_alloc() allocates a properly aligned term_character object and returns
- * a pointer to it. NULL is returned on allocation errors. The object will have
- * enough room for @n following UCS-4 chars.
- * Note that we allocate (n+1) characters and set the last one to 0 in case
- * anyone prints this string for debugging.
- */
-static term_character *char_alloc(uint8_t n) {
- term_character *c;
- int r;
-
- r = posix_memalign((void**)&c,
- MAX(sizeof(void*), (size_t)2),
- sizeof(*c) + sizeof(*c->codepoints) * (n + 1));
- if (r)
- return NULL;
-
- c->n = n;
- c->codepoints[n] = 0;
-
- return c;
-}
-
-/*
- * char_free() frees the memory allocated via char_alloc(). It is safe to call
- * this on any term_char_t, only allocated characters are freed.
- */
-static inline void char_free(term_char_t ch) {
- if (term_char_is_allocated(ch))
- free(char_to_ptr(ch));
-}
-
-/*
- * This appends @append_ucs4 to the existing character @base and returns
- * it as a new character. In case that's not possible, @base is returned. The
- * caller can use term_char_same() to test whether the returned character was
- * freshly allocated or not.
- */
-static term_char_t char_build(term_char_t base, uint32_t append_ucs4) {
- /* soft-limit for combining-chars; hard-limit is currently 255 */
- const size_t climit = 64;
- term_character *c;
- uint32_t buf[3], *t;
- uint8_t n;
-
- /* ignore invalid UCS-4 */
- if (append_ucs4 > CHAR_UCS4_MAX)
- return base;
-
- if (term_char_is_null(base)) {
- return char_pack1(append_ucs4);
- } else if (!term_char_is_allocated(base)) {
- /* unpack and try extending the packed character */
- n = char_unpack(base, &buf[0], &buf[1], &buf[2]);
-
- switch (n) {
- case 0:
- return char_pack1(append_ucs4);
- case 1:
- if (climit < 2)
- return base;
-
- return char_pack2(buf[0], append_ucs4);
- case 2:
- if (climit < 3)
- return base;
-
- return char_pack3(buf[0], buf[1], append_ucs4);
- default:
- /* fallthrough */
- break;
- }
-
- /* already fully packed, we need to allocate a new one */
- t = buf;
- } else {
- /* already an allocated type, we need to allocate a new one */
- c = char_to_ptr(base);
- t = c->codepoints;
- n = c->n;
- }
-
- /* bail out if soft-limit is reached */
- if (n >= climit)
- return base;
-
- /* allocate new char */
- c = char_alloc(n + 1);
- if (!c)
- return base;
-
- memcpy(c->codepoints, t, sizeof(*t) * n);
- c->codepoints[n] = append_ucs4;
-
- return char_from_ptr(c);
-}
-
-/**
- * term_char_set() - Reset character to a single UCS-4 character
- * @previous: term-char to reset
- * @append_ucs4: UCS-4 char to set
- *
- * This frees all resources in @previous and re-initializes it to @append_ucs4.
- * The new char is returned.
- *
- * Usually, this is used like this:
- * obj->ch = term_char_set(obj->ch, ucs4);
- *
- * Returns: The previous character reset to @append_ucs4.
- */
-term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4) {
- char_free(previous);
- return char_build(TERM_CHAR_NULL, append_ucs4);
-}
-
-/**
- * term_char_merge() - Merge UCS-4 char at the end of an existing char
- * @base: existing term-char
- * @append_ucs4: UCS-4 character to append
- *
- * This appends @append_ucs4 to @base and returns the result. @base is
- * invalidated by this function and must no longer be used. The returned value
- * replaces the old one.
- *
- * Usually, this is used like this:
- * obj->ch = term_char_merge(obj->ch, ucs4);
- *
- * Returns: The new merged character.
- */
-term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4) {
- term_char_t ch;
-
- ch = char_build(base, append_ucs4);
- if (!term_char_same(ch, base))
- term_char_free(base);
-
- return ch;
-}
-
-/**
- * term_char_dup() - Duplicate character
- * @ch: character to duplicate
- *
- * This duplicates a term-character. In case the character is not allocated,
- * nothing is done. Otherwise, the underlying memory is copied and returned. You
- * need to call term_char_free() on the returned character to release it again.
- * On allocation errors, a replacement character is returned. Therefore, the
- * caller can safely assume that this function always succeeds.
- *
- * Returns: The duplicated term-character.
- */
-term_char_t term_char_dup(term_char_t ch) {
- term_character *c, *newc;
-
- if (!term_char_is_allocated(ch))
- return ch;
-
- c = char_to_ptr(ch);
- newc = char_alloc(c->n);
- if (!newc)
- return char_pack1(CHAR_UCS4_REPLACEMENT);
-
- memcpy(newc->codepoints, c->codepoints, sizeof(*c->codepoints) * c->n);
- return char_from_ptr(newc);
-}
-
-/**
- * term_char_dup_append() - Duplicate tsm-char with UCS-4 character appended
- * @base: existing term-char
- * @append_ucs4: UCS-4 character to append
- *
- * This is similar to term_char_merge(), but it returns a separately allocated
- * character. That is, @base will stay valid after this returns and is not
- * touched. In case the append-operation fails, @base is duplicated and
- * returned. That is, the returned char is always independent of @base.
- *
- * Returns: Newly allocated character with @append_ucs4 appended to @base.
- */
-term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4) {
- term_char_t ch;
-
- ch = char_build(base, append_ucs4);
- if (term_char_same(ch, base))
- ch = term_char_dup(base);
-
- return ch;
-}
-
-/**
- * term_char_resolve() - Retrieve the UCS-4 string for a term-char
- * @ch: character to resolve
- * @s: storage for size of string or NULL
- * @b: storage for string or NULL
- *
- * This takes a term-character and returns the UCS-4 string associated with it.
- * In case @ch is not allocated, the string is stored in @b (in case @b is NULL
- * static storage is used). Otherwise, a pointer to the allocated storage is
- * returned.
- *
- * The returned string is only valid as long as @ch and @b are valid. The string
- * is zero-terminated and can safely be printed via long-character printf().
- * The length of the string excluding the zero-character is returned in @s.
- *
- * This never returns NULL. Even if the size is 0, this points to a buffer of at
- * least a zero-terminator.
- *
- * Returns: The UCS-4 string-representation of @ch, and its size in @s.
- */
-const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b) {
- static term_charbuf_t static_b;
- term_character *c;
- uint32_t *cache;
- size_t len;
-
- if (b)
- cache = b->buf;
- else
- cache = static_b.buf;
-
- if (term_char_is_null(ch)) {
- len = 0;
- cache[0] = 0;
- } else if (term_char_is_allocated(ch)) {
- c = char_to_ptr(ch);
- len = c->n;
- cache = c->codepoints;
- } else {
- len = char_unpack(ch, &cache[0], &cache[1], &cache[2]);
- cache[len] = 0;
- }
-
- if (s)
- *s = len;
-
- return cache;
-}
-
-/**
- * term_char_lookup_width() - Lookup cell-width of a character
- * @ch: character to return cell-width for
- *
- * This is an equivalent of wcwidth() for term_char_t. It can deal directly
- * with UCS-4 and combining-characters and avoids the mess that is wchar_t and
- * locale handling.
- *
- * Returns: 0 for unprintable characters, >0 for everything else.
- */
-unsigned int term_char_lookup_width(term_char_t ch) {
- term_charbuf_t b;
- const uint32_t *str;
- unsigned int max;
- size_t i, len;
- int r;
-
- max = 0;
- str = term_char_resolve(ch, &len, &b);
-
- for (i = 0; i < len; ++i) {
- /*
- * Oh god, C99 locale handling strikes again: wcwidth() expects
- * wchar_t, but there is no way for us to know the
- * internal encoding of wchar_t. Moreover, it is nearly
- * impossible to convert UCS-4 into wchar_t (except for iconv,
- * which is way too much overhead).
- * Therefore, we use our own copy of wcwidth(). Lets just hope
- * that glibc will one day export it's internal UCS-4 and UTF-8
- * helpers for direct use.
- */
- assert_cc(sizeof(wchar_t) >= 4);
- r = mk_wcwidth((wchar_t)str[i]);
- if (r > 0 && (unsigned int)r > max)
- max = r;
- }
-
- return max;
-}
-
-/**
- * term_cell_init() - Initialize a new cell
- * @cell: cell to initialize
- * @ch: character to set on the cell or TERM_CHAR_NULL
- * @cwidth: character width of @ch
- * @attr: attributes to set on the cell or NULL
- * @age: age to set on the cell or TERM_AGE_NULL
- *
- * This initializes a new cell. The backing-memory of the cell must be allocated
- * by the caller beforehand. The caller is responsible to destroy the cell via
- * term_cell_destroy() before freeing the backing-memory.
- *
- * It is safe (and supported!) to use:
- * zero(*c);
- * instead of:
- * term_cell_init(c, TERM_CHAR_NULL, NULL, TERM_AGE_NULL);
- *
- * Note that this call takes ownership of @ch. If you want to use it yourself
- * after this call, you need to duplicate it before calling this.
- */
-static void term_cell_init(term_cell *cell, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age) {
- assert(cell);
-
- cell->ch = ch;
- cell->cwidth = cwidth;
- cell->age = age;
-
- if (attr)
- memcpy(&cell->attr, attr, sizeof(*attr));
- else
- zero(cell->attr);
-}
-
-/**
- * term_cell_destroy() - Destroy previously initialized cell
- * @cell: cell to destroy or NULL
- *
- * This releases all resources associated with a cell. The backing memory is
- * kept as-is. It's the responsibility of the caller to manage it.
- *
- * You must not call any other cell operations on this cell after this call
- * returns. You must re-initialize the cell via term_cell_init() before you can
- * use it again.
- *
- * If @cell is NULL, this is a no-op.
- */
-static void term_cell_destroy(term_cell *cell) {
- if (!cell)
- return;
-
- term_char_free(cell->ch);
-}
-
-/**
- * term_cell_set() - Change contents of a cell
- * @cell: cell to modify
- * @ch: character to set on the cell or cell->ch
- * @cwidth: character width of @ch or cell->cwidth
- * @attr: attributes to set on the cell or NULL
- * @age: age to set on the cell or cell->age
- *
- * This changes the contents of a cell. It can be used to change the character,
- * attributes and age. To keep the current character, pass cell->ch as @ch. To
- * reset the current attributes, pass NULL. To keep the current age, pass
- * cell->age.
- *
- * This call takes ownership of @ch. You need to duplicate it first, in case you
- * want to use it for your own purposes after this call.
- *
- * The cell must have been initialized properly before calling this. See
- * term_cell_init().
- */
-static void term_cell_set(term_cell *cell, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age) {
- assert(cell);
-
- if (!term_char_same(ch, cell->ch)) {
- term_char_free(cell->ch);
- cell->ch = ch;
- }
-
- cell->cwidth = cwidth;
- cell->age = age;
-
- if (attr)
- memcpy(&cell->attr, attr, sizeof(*attr));
- else
- zero(cell->attr);
-}
-
-/**
- * term_cell_append() - Append a combining-char to a cell
- * @cell: cell to modify
- * @ucs4: UCS-4 character to append to the cell
- * @age: new age to set on the cell or cell->age
- *
- * This appends a combining-character to a cell. No validation of the UCS-4
- * character is done, so this can be used to append any character. Additionally,
- * this can update the age of the cell.
- *
- * The cell must have been initialized properly before calling this. See
- * term_cell_init().
- */
-static void term_cell_append(term_cell *cell, uint32_t ucs4, term_age_t age) {
- assert(cell);
-
- cell->ch = term_char_merge(cell->ch, ucs4);
- cell->age = age;
-}
-
-/**
- * term_cell_init_n() - Initialize an array of cells
- * @cells: pointer to an array of cells to initialize
- * @n: number of cells
- * @attr: attributes to set on all cells or NULL
- * @age: age to set on all cells
- *
- * This is the same as term_cell_init() but initializes an array of cells.
- * Furthermore, this always sets the character to TERM_CHAR_NULL.
- * If you want to set a specific characters on all cells, you need to hard-code
- * this loop and duplicate the character for each cell.
- */
-static void term_cell_init_n(term_cell *cells, unsigned int n, const term_attr *attr, term_age_t age) {
- for ( ; n > 0; --n, ++cells)
- term_cell_init(cells, TERM_CHAR_NULL, 0, attr, age);
-}
-
-/**
- * term_cell_destroy_n() - Destroy an array of cells
- * @cells: pointer to an array of cells to destroy
- * @n: number of cells
- *
- * This is the same as term_cell_destroy() but destroys an array of cells.
- */
-static void term_cell_destroy_n(term_cell *cells, unsigned int n) {
- for ( ; n > 0; --n, ++cells)
- term_cell_destroy(cells);
-}
-
-/**
- * term_cell_clear_n() - Clear contents of an array of cells
- * @cells: pointer to an array of cells to modify
- * @n: number of cells
- * @attr: attributes to set on all cells or NULL
- * @age: age to set on all cells
- *
- * This is the same as term_cell_set() but operates on an array of cells. Note
- * that all characters are always set to TERM_CHAR_NULL, unlike term_cell_set()
- * which takes the character as argument.
- * If you want to set a specific characters on all cells, you need to hard-code
- * this loop and duplicate the character for each cell.
- */
-static void term_cell_clear_n(term_cell *cells, unsigned int n, const term_attr *attr, term_age_t age) {
- for ( ; n > 0; --n, ++cells)
- term_cell_set(cells, TERM_CHAR_NULL, 0, attr, age);
-}
-
-/**
- * term_line_new() - Allocate a new line
- * @out: place to store pointer to new line
- *
- * This allocates and initialized a new line. The line is unlinked and
- * independent of any page. It can be used for any purpose. The initial
- * cell-count is set to 0.
- *
- * The line has to be freed via term_line_free() once it's no longer needed.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_line_new(term_line **out) {
- _term_line_free_ term_line *line = NULL;
-
- assert_return(out, -EINVAL);
-
- line = new0(term_line, 1);
- if (!line)
- return -ENOMEM;
-
- *out = line;
- line = NULL;
- return 0;
-}
-
-/**
- * term_line_free() - Free a line
- * @line: line to free or NULL
- *
- * This frees a line that was previously allocated via term_line_free(). All its
- * cells are released, too.
- *
- * If @line is NULL, this is a no-op.
- */
-term_line *term_line_free(term_line *line) {
- if (!line)
- return NULL;
-
- term_cell_destroy_n(line->cells, line->n_cells);
- free(line->cells);
- free(line);
-
- return NULL;
-}
-
-/**
- * term_line_reserve() - Pre-allocate cells for a line
- * @line: line to pre-allocate cells for
- * @width: numbers of cells the line shall have pre-allocated
- * @attr: attribute for all allocated cells or NULL
- * @age: current age for all modifications
- * @protect_width: width to protect from erasure
- *
- * This pre-allocates cells for this line. Please note that @width is the number
- * of cells the line is guaranteed to have allocated after this call returns.
- * It's not the number of cells that are added, neither is it the new width of
- * the line.
- *
- * This function never frees memory. That is, reducing the line-width will
- * always succeed, same is true for increasing the width to a previously set
- * width.
- *
- * @attr and @age are used to initialize new cells. Additionally, any
- * existing cell outside of the protected area specified by @protect_width are
- * cleared and reset with @attr and @age.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width) {
- unsigned int min_width;
- term_cell *t;
-
- assert_return(line, -EINVAL);
-
- /* reset existing cells if required */
- min_width = MIN(line->n_cells, width);
- if (min_width > protect_width)
- term_cell_clear_n(line->cells + protect_width,
- min_width - protect_width,
- attr,
- age);
-
- /* allocate new cells if required */
-
- if (width > line->n_cells) {
- t = realloc_multiply(line->cells, sizeof(*t), width);
- if (!t)
- return -ENOMEM;
-
- if (!attr && !age)
- memzero(t + line->n_cells,
- sizeof(*t) * (width - line->n_cells));
- else
- term_cell_init_n(t + line->n_cells,
- width - line->n_cells,
- attr,
- age);
-
- line->cells = t;
- line->n_cells = width;
- }
-
- line->fill = MIN(line->fill, protect_width);
-
- return 0;
-}
-
-/**
- * term_line_set_width() - Change width of a line
- * @line: line to modify
- * @width: new width
- *
- * This changes the actual width of a line. It is the caller's responsibility
- * to use term_line_reserve() to make sure enough space is allocated. If @width
- * is greater than the allocated size, it is cropped.
- *
- * This does not modify any cells. Use term_line_reserve() or term_line_erase()
- * to clear any newly added cells.
- *
- * NOTE: The fill state is cropped at line->width. Therefore, if you increase
- * the line-width afterwards, but there is a multi-cell character at the
- * end of the line that got cropped, then the fill-state will _not_ be
- * adjusted.
- * This means, the fill-state always includes the cells up to the start
- * of the right-most character, but it might or might not cover it until
- * its end. This should be totally fine, though. You should never access
- * multi-cell tails directly, anyway.
- */
-void term_line_set_width(term_line *line, unsigned int width) {
- assert(line);
-
- if (width > line->n_cells)
- width = line->n_cells;
-
- line->width = width;
- line->fill = MIN(line->fill, width);
-}
-
-/**
- * line_insert() - Insert characters and move existing cells to the right
- * @from: position to insert cells at
- * @num: number of cells to insert
- * @head_char: character that is set on the first cell
- * @head_cwidth: character-length of @head_char
- * @attr: attribute for all inserted cells or NULL
- * @age: current age for all modifications
- *
- * The INSERT operation (or writes with INSERT_MODE) writes data at a specific
- * position on a line and shifts the existing cells to the right. Cells that are
- * moved beyond the right hand border are discarded.
- *
- * This helper contains the actual INSERT implementation which is independent of
- * the data written. It works on cells, not on characters. The first cell is set
- * to @head_char, all others are reset to TERM_CHAR_NULL. See each caller for a
- * more detailed description.
- */
-static inline void line_insert(term_line *line, unsigned int from, unsigned int num, term_char_t head_char, unsigned int head_cwidth, const term_attr *attr, term_age_t age) {
- unsigned int i, rem, move;
-
- if (from >= line->width)
- return;
- if (from + num < from || from + num > line->width)
- num = line->width - from;
- if (!num)
- return;
-
- move = line->width - from - num;
- rem = MIN(num, move);
-
- if (rem > 0) {
- /*
- * Make room for @num cells; shift cells to the right if
- * required. @rem is the number of remaining cells that we will
- * knock off on the right and overwrite during the right shift.
- *
- * For INSERT_MODE, @num/@rem are usually 1 or 2, @move is 50%
- * of the line on average. Therefore, the actual move is quite
- * heavy and we can safely invalidate cells manually instead of
- * the whole line.
- * However, for INSERT operations, any parameters are
- * possible. But we cannot place any assumption on its usage
- * across applications, so we just handle it the same as
- * INSERT_MODE and do per-cell invalidation.
- */
-
- /* destroy cells that are knocked off on the right */
- term_cell_destroy_n(line->cells + line->width - rem, rem);
-
- /* move remaining bulk of cells */
- memmove(line->cells + from + num,
- line->cells + from,
- sizeof(*line->cells) * move);
-
- /* invalidate cells */
- for (i = 0; i < move; ++i)
- line->cells[from + num + i].age = age;
-
- /* initialize fresh head-cell */
- term_cell_init(line->cells + from,
- head_char,
- head_cwidth,
- attr,
- age);
-
- /* initialize fresh tail-cells */
- term_cell_init_n(line->cells + from + 1,
- num - 1,
- attr,
- age);
-
- /* adjust fill-state */
- line->fill = MIN(line->width,
- MAX(line->fill + num,
- from + num));
- } else {
- /* modify head-cell */
- term_cell_set(line->cells + from,
- head_char,
- head_cwidth,
- attr,
- age);
-
- /* reset tail-cells */
- term_cell_clear_n(line->cells + from + 1,
- num - 1,
- attr,
- age);
-
- /* adjust fill-state */
- line->fill = line->width;
- }
-}
-
-/**
- * term_line_write() - Write to a single, specific cell
- * @line: line to write to
- * @pos_x: x-position of cell in @line to write to
- * @ch: character to write to the cell
- * @cwidth: character width of @ch
- * @attr: attributes to set on the cell or NULL
- * @age: current age for all modifications
- * @insert_mode: true if INSERT-MODE is enabled
- *
- * This writes to a specific cell in a line. The cell is addressed by its
- * X-position @pos_x. If that cell does not exist, this is a no-op.
- *
- * @ch and @attr are set on this cell.
- *
- * If @insert_mode is true, this inserts the character instead of overwriting
- * existing data (existing data is now moved to the right before writing).
- *
- * This function is the low-level handler of normal writes to a terminal.
- */
-void term_line_write(term_line *line, unsigned int pos_x, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode) {
- unsigned int len;
-
- assert(line);
-
- if (pos_x >= line->width)
- return;
-
- len = MAX(1U, cwidth);
- if (pos_x + len < pos_x || pos_x + len > line->width)
- len = line->width - pos_x;
- if (!len)
- return;
-
- if (insert_mode) {
- /* Use line_insert() to insert the character-head and fill
- * the remains with NULLs. */
- line_insert(line, pos_x, len, ch, cwidth, attr, age);
- } else {
- /* modify head-cell */
- term_cell_set(line->cells + pos_x, ch, cwidth, attr, age);
-
- /* reset tail-cells */
- term_cell_clear_n(line->cells + pos_x + 1,
- len - 1,
- attr,
- age);
-
- /* adjust fill-state */
- line->fill = MIN(line->width,
- MAX(line->fill,
- pos_x + len));
- }
-}
-
-/**
- * term_line_insert() - Insert empty cells
- * @line: line to insert empty cells into
- * @from: x-position where to insert cells
- * @num: number of cells to insert
- * @attr: attributes to set on the cells or NULL
- * @age: current age for all modifications
- *
- * This inserts @num empty cells at position @from in line @line. All existing
- * cells to the right are shifted to make room for the new cells. Cells that get
- * pushed beyond the right hand border are discarded.
- */
-void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age) {
- /* use line_insert() to insert @num empty cells */
- return line_insert(line, from, num, TERM_CHAR_NULL, 0, attr, age);
-}
-
-/**
- * term_line_delete() - Delete cells from line
- * @line: line to delete cells from
- * @from: position to delete cells at
- * @num: number of cells to delete
- * @attr: attributes to set on any new cells
- * @age: current age for all modifications
- *
- * Delete cells from a line. All cells to the right of the deleted cells are
- * shifted to the left to fill the empty space. New cells appearing on the right
- * hand border are cleared and initialized with @attr.
- */
-void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age) {
- unsigned int rem, move, i;
-
- assert(line);
-
- if (from >= line->width)
- return;
- if (from + num < from || from + num > line->width)
- num = line->width - from;
- if (!num)
- return;
-
- /* destroy and move as many upfront as possible */
- move = line->width - from - num;
- rem = MIN(num, move);
- if (rem > 0) {
- /* destroy to be removed cells */
- term_cell_destroy_n(line->cells + from, rem);
-
- /* move tail upfront */
- memmove(line->cells + from,
- line->cells + from + num,
- sizeof(*line->cells) * move);
-
- /* invalidate copied cells */
- for (i = 0; i < move; ++i)
- line->cells[from + i].age = age;
-
- /* initialize tail that was moved away */
- term_cell_init_n(line->cells + line->width - rem,
- rem,
- attr,
- age);
-
- /* reset remaining cells in case the move was too small */
- if (num > move)
- term_cell_clear_n(line->cells + from + move,
- num - move,
- attr,
- age);
- } else {
- /* reset cells */
- term_cell_clear_n(line->cells + from,
- num,
- attr,
- age);
- }
-
- /* adjust fill-state */
- if (from + num < line->fill)
- line->fill -= num;
- else if (from < line->fill)
- line->fill = from;
-}
-
-/**
- * term_line_append_combchar() - Append combining char to existing cell
- * @line: line to modify
- * @pos_x: position of cell to append combining char to
- * @ucs4: combining character to append
- * @age: current age for all modifications
- *
- * Unicode allows trailing combining characters, which belong to the
- * char in front of them. The caller is responsible of detecting
- * combining characters and calling term_line_append_combchar() instead of
- * term_line_write(). This simply appends the char to the correct cell then.
- * If the cell is not in the visible area, this call is skipped.
- *
- * Note that control-sequences are not 100% compatible with combining
- * characters as they require delayed parsing. However, we must handle
- * control-sequences immediately. Therefore, there might be trailing
- * combining chars that should be discarded by the parser.
- * However, to prevent programming errors, we're also being pedantic
- * here and discard weirdly placed combining chars. This prevents
- * situations were invalid content is parsed into the terminal and you
- * might end up with cells containing only combining chars.
- *
- * Long story short: To get combining-characters working with old-fashioned
- * terminal-emulation, we parse them exclusively for direct cell-writes. Other
- * combining-characters are usually simply discarded and ignored.
- */
-void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age) {
- assert(line);
-
- if (pos_x >= line->width)
- return;
-
- /* Unused cell? Skip appending any combining chars then. */
- if (term_char_is_null(line->cells[pos_x].ch))
- return;
-
- term_cell_append(line->cells + pos_x, ucs4, age);
-}
-
-/**
- * term_line_erase() - Erase parts of a line
- * @line: line to modify
- * @from: position to start the erase
- * @num: number of cells to erase
- * @attr: attributes to initialize erased cells with
- * @age: current age for all modifications
- * @keep_protected: true if protected cells should be kept
- *
- * This is the standard erase operation. It clears all cells in the targeted
- * area and re-initializes them. Cells to the right are not shifted left, you
- * must use DELETE to achieve that. Cells outside the visible area are skipped.
- *
- * If @keep_protected is true, protected cells will not be erased.
- */
-void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected) {
- term_cell *cell;
- unsigned int i, last_protected;
-
- assert(line);
-
- if (from >= line->width)
- return;
- if (from + num < from || from + num > line->width)
- num = line->width - from;
- if (!num)
- return;
-
- last_protected = 0;
- for (i = 0; i < num; ++i) {
- cell = line->cells + from + i;
- if (keep_protected && cell->attr.protect) {
- /* only count protected-cells inside the fill-region */
- if (from + i < line->fill)
- last_protected = from + i;
-
- continue;
- }
-
- term_cell_set(cell, TERM_CHAR_NULL, 0, attr, age);
- }
-
- /* Adjust fill-state. This is a bit tricks, we can only adjust it in
- * case the erase-region starts inside the fill-region and ends at the
- * tail or beyond the fill-region. Otherwise, the current fill-state
- * stays as it was.
- * Furthermore, we must account for protected cells. The loop above
- * ensures that protected-cells are only accounted for if they're
- * inside the fill-region. */
- if (from < line->fill && from + num >= line->fill)
- line->fill = MAX(from, last_protected);
-}
-
-/**
- * term_line_reset() - Reset a line
- * @line: line to reset
- * @attr: attributes to initialize all cells with
- * @age: current age for all modifications
- *
- * This resets all visible cells of a line and sets their attributes and ages
- * to @attr and @age. This is equivalent to erasing a whole line via
- * term_line_erase().
- */
-void term_line_reset(term_line *line, const term_attr *attr, term_age_t age) {
- assert(line);
-
- return term_line_erase(line, 0, line->width, attr, age, 0);
-}
-
-/**
- * term_line_link() - Link line in front of a list
- * @line: line to link
- * @first: member pointing to first entry
- * @last: member pointing to last entry
- *
- * This links a line into a list of lines. The line is inserted at the front and
- * must not be linked, yet. See the TERM_LINE_LINK() macro for an easier usage of
- * this.
- */
-void term_line_link(term_line *line, term_line **first, term_line **last) {
- assert(line);
- assert(first);
- assert(last);
- assert(!line->lines_prev);
- assert(!line->lines_next);
-
- line->lines_prev = NULL;
- line->lines_next = *first;
- if (*first)
- (*first)->lines_prev = line;
- else
- *last = line;
- *first = line;
-}
-
-/**
- * term_line_link_tail() - Link line at tail of a list
- * @line: line to link
- * @first: member pointing to first entry
- * @last: member pointing to last entry
- *
- * Same as term_line_link() but links the line at the tail.
- */
-void term_line_link_tail(term_line *line, term_line **first, term_line **last) {
- assert(line);
- assert(first);
- assert(last);
- assert(!line->lines_prev);
- assert(!line->lines_next);
-
- line->lines_next = NULL;
- line->lines_prev = *last;
- if (*last)
- (*last)->lines_next = line;
- else
- *first = line;
- *last = line;
-}
-
-/**
- * term_line_unlink() - Unlink line from a list
- * @line: line to unlink
- * @first: member pointing to first entry
- * @last: member pointing to last entry
- *
- * This unlinks a previously linked line. See TERM_LINE_UNLINK() for an easier to
- * use macro.
- */
-void term_line_unlink(term_line *line, term_line **first, term_line **last) {
- assert(line);
- assert(first);
- assert(last);
-
- if (line->lines_prev)
- line->lines_prev->lines_next = line->lines_next;
- else
- *first = line->lines_next;
- if (line->lines_next)
- line->lines_next->lines_prev = line->lines_prev;
- else
- *last = line->lines_prev;
-
- line->lines_prev = NULL;
- line->lines_next = NULL;
-}
-
-/**
- * term_page_new() - Allocate new page
- * @out: storage for pointer to new page
- *
- * Allocate a new page. The initial dimensions are 0/0.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_page_new(term_page **out) {
- _term_page_free_ term_page *page = NULL;
-
- assert_return(out, -EINVAL);
-
- page = new0(term_page, 1);
- if (!page)
- return -ENOMEM;
-
- *out = page;
- page = NULL;
- return 0;
-}
-
-/**
- * term_page_free() - Free page
- * @page: page to free or NULL
- *
- * Free a previously allocated page and all associated data. If @page is NULL,
- * this is a no-op.
- *
- * Returns: NULL
- */
-term_page *term_page_free(term_page *page) {
- unsigned int i;
-
- if (!page)
- return NULL;
-
- for (i = 0; i < page->n_lines; ++i)
- term_line_free(page->lines[i]);
-
- free(page->line_cache);
- free(page->lines);
- free(page);
-
- return NULL;
-}
-
-/**
- * term_page_get_cell() - Return pointer to requested cell
- * @page: page to operate on
- * @x: x-position of cell
- * @y: y-position of cell
- *
- * This returns a pointer to the cell at position @x/@y. You're free to modify
- * this cell as much as you like. However, once you call any other function on
- * the page, you must drop the pointer to the cell.
- *
- * Returns: Pointer to the cell or NULL if out of the visible area.
- */
-term_cell *term_page_get_cell(term_page *page, unsigned int x, unsigned int y) {
- assert_return(page, NULL);
-
- if (x >= page->width)
- return NULL;
- if (y >= page->height)
- return NULL;
-
- return &page->lines[y]->cells[x];
-}
-
-/**
- * page_scroll_up() - Scroll up
- * @page: page to operate on
- * @new_width: width to use for any new line moved into the visible area
- * @num: number of lines to scroll up
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for old lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are cleared and reset
- * with the given attributes. Old lines are moved into the history if non-NULL.
- * If a new line is allocated, moved from the history buffer or moved from
- * outside the visible region into the visible region, this call makes sure it
- * has at least @width cells allocated. If a possible memory-allocation fails,
- * the previous line is reused. This has the side effect, that it will not be
- * linked into the history buffer.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-static void page_scroll_up(term_page *page, unsigned int new_width, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- term_line *line, **cache;
- unsigned int i;
- int r;
-
- assert(page);
-
- if (num > page->scroll_num)
- num = page->scroll_num;
- if (num < 1)
- return;
-
- /* Better safe than sorry: avoid under-allocating lines, even when
- * resizing. */
- new_width = MAX(new_width, page->width);
-
- cache = page->line_cache;
-
- /* Try moving lines into history and allocate new lines for each moved
- * line. In case allocation fails, or if we have no history, reuse the
- * line.
- * We keep the lines in the line-cache so we can safely move the
- * remaining lines around. */
- for (i = 0; i < num; ++i) {
- line = page->lines[page->scroll_idx + i];
-
- r = -EAGAIN;
- if (history) {
- r = term_line_new(&cache[i]);
- if (r >= 0) {
- r = term_line_reserve(cache[i],
- new_width,
- attr,
- age,
- 0);
- if (r < 0)
- term_line_free(cache[i]);
- else
- term_line_set_width(cache[i], page->width);
- }
- }
-
- if (r >= 0) {
- term_history_push(history, line);
- } else {
- cache[i] = line;
- term_line_reset(line, attr, age);
- }
- }
-
- if (num < page->scroll_num) {
- memmove(page->lines + page->scroll_idx,
- page->lines + page->scroll_idx + num,
- sizeof(*page->lines) * (page->scroll_num - num));
-
- /* update age of moved lines */
- for (i = 0; i < page->scroll_num - num; ++i)
- page->lines[page->scroll_idx + i]->age = age;
- }
-
- /* copy remaining lines from cache; age is already updated */
- memcpy(page->lines + page->scroll_idx + page->scroll_num - num,
- cache,
- sizeof(*cache) * num);
-
- /* update fill */
- page->scroll_fill -= MIN(page->scroll_fill, num);
-}
-
-/**
- * page_scroll_down() - Scroll down
- * @page: page to operate on
- * @new_width: width to use for any new line moved into the visible area
- * @num: number of lines to scroll down
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for new lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are retrieved from
- * the history or cleared if the history is empty or NULL.
- *
- * Usually, scroll-down implies that new lines are cleared. Therefore, you're
- * highly encouraged to set @history to NULL. However, if you resize a terminal,
- * you might want to include history-lines in the new area. In that case, you
- * should set @history to non-NULL.
- *
- * If a new line is allocated, moved from the history buffer or moved from
- * outside the visible region into the visible region, this call makes sure it
- * has at least @width cells allocated. If a possible memory-allocation fails,
- * the previous line is reused. This will have the side-effect that lines from
- * the history will not get visible on-screen but kept in history.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-static void page_scroll_down(term_page *page, unsigned int new_width, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- term_line *line, **cache, *t;
- unsigned int i, last_idx;
-
- assert(page);
-
- if (num > page->scroll_num)
- num = page->scroll_num;
- if (num < 1)
- return;
-
- /* Better safe than sorry: avoid under-allocating lines, even when
- * resizing. */
- new_width = MAX(new_width, page->width);
-
- cache = page->line_cache;
- last_idx = page->scroll_idx + page->scroll_num - 1;
-
- /* Try pulling out lines from history; if history is empty or if no
- * history is given, we reuse the to-be-removed lines. Otherwise, those
- * lines are released. */
- for (i = 0; i < num; ++i) {
- line = page->lines[last_idx - i];
-
- t = NULL;
- if (history)
- t = term_history_pop(history, new_width, attr, age);
-
- if (t) {
- cache[num - 1 - i] = t;
- term_line_free(line);
- } else {
- cache[num - 1 - i] = line;
- term_line_reset(line, attr, age);
- }
- }
-
- if (num < page->scroll_num) {
- memmove(page->lines + page->scroll_idx + num,
- page->lines + page->scroll_idx,
- sizeof(*page->lines) * (page->scroll_num - num));
-
- /* update age of moved lines */
- for (i = 0; i < page->scroll_num - num; ++i)
- page->lines[page->scroll_idx + num + i]->age = age;
- }
-
- /* copy remaining lines from cache; age is already updated */
- memcpy(page->lines + page->scroll_idx,
- cache,
- sizeof(*cache) * num);
-
- /* update fill; but only if there's already content in it */
- if (page->scroll_fill > 0)
- page->scroll_fill = MIN(page->scroll_num,
- page->scroll_fill + num);
-}
-
-/**
- * page_reserve() - Reserve page area
- * @page: page to modify
- * @cols: required columns (width)
- * @rows: required rows (height)
- * @attr: attributes for newly allocated cells
- * @age: age to set on any modified cells
- *
- * This allocates the required amount of lines and cells to guarantee that the
- * page has at least the demanded dimensions of @cols x @rows. Note that this
- * never shrinks the page-memory. We keep cells allocated for performance
- * reasons.
- *
- * Additionally to allocating lines, this also clears any newly added cells so
- * you can safely change the size afterwards without clearing new cells.
- *
- * Note that you must be careful what operations you call on the page between
- * page_reserve() and updating page->width/height. Any newly allocated line (or
- * shifted line) might not meet your new width/height expectations.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_page_reserve(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age) {
- _term_line_free_ term_line *line = NULL;
- unsigned int i, min_lines;
- term_line **t;
- int r;
-
- assert_return(page, -EINVAL);
-
- /*
- * First make sure the first MIN(page->n_lines, rows) lines have at
- * least the required width of @cols. This does not modify any visible
- * cells in the existing @page->width x @page->height area, therefore,
- * we can safely bail out afterwards in case anything else fails.
- * Note that lines in between page->height and page->n_lines might be
- * shorter than page->width. Hence, we need to resize them all, but we
- * can skip some of them for better performance.
- */
- min_lines = MIN(page->n_lines, rows);
- for (i = 0; i < min_lines; ++i) {
- /* lines below page->height have at least page->width cells */
- if (cols < page->width && i < page->height)
- continue;
-
- r = term_line_reserve(page->lines[i],
- cols,
- attr,
- age,
- (i < page->height) ? page->width : 0);
- if (r < 0)
- return r;
- }
-
- /*
- * We now know the first @min_lines lines have at least width @cols and
- * are prepared for resizing. We now only have to allocate any
- * additional lines below @min_lines in case @rows is greater than
- * page->n_lines.
- */
- if (rows > page->n_lines) {
- t = realloc_multiply(page->lines, sizeof(*t), rows);
- if (!t)
- return -ENOMEM;
- page->lines = t;
-
- t = realloc_multiply(page->line_cache, sizeof(*t), rows);
- if (!t)
- return -ENOMEM;
- page->line_cache = t;
-
- while (page->n_lines < rows) {
- r = term_line_new(&line);
- if (r < 0)
- return r;
-
- r = term_line_reserve(line, cols, attr, age, 0);
- if (r < 0)
- return r;
-
- page->lines[page->n_lines++] = line;
- line = NULL;
- }
- }
-
- return 0;
-}
-
-/**
- * term_page_resize() - Resize page
- * @page: page to modify
- * @cols: number of columns (width)
- * @rows: number of rows (height)
- * @attr: attributes for newly allocated cells
- * @age: age to set on any modified cells
- * @history: history buffer to use for new/old lines or NULL
- *
- * This changes the visible dimensions of a page. You must have called
- * term_page_reserve() beforehand, otherwise, this will fail.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-void term_page_resize(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age, term_history *history) {
- unsigned int i, num, empty, max, old_height;
- term_line *line;
-
- assert(page);
- assert(page->n_lines >= rows);
-
- old_height = page->height;
-
- if (rows < old_height) {
- /*
- * If we decrease the terminal-height, we emulate a scroll-up.
- * This way, existing data from the scroll-area is moved into
- * the history, making space at the bottom to reduce the screen
- * height. In case the scroll-fill indicates empty lines, we
- * reduce the amount of scrolled lines.
- * Once scrolled, we have to move the lower margin from below
- * the scroll area up so it is preserved.
- */
-
- /* move lines to history if scroll region is filled */
- num = old_height - rows;
- empty = page->scroll_num - page->scroll_fill;
- if (num > empty)
- page_scroll_up(page,
- cols,
- num - empty,
- attr,
- age,
- history);
-
- /* move lower margin up; drop its lines if not enough space */
- num = LESS_BY(old_height, page->scroll_idx + page->scroll_num);
- max = LESS_BY(rows, page->scroll_idx);
- num = MIN(num, max);
- if (num > 0) {
- unsigned int top, bottom;
-
- top = rows - num;
- bottom = page->scroll_idx + page->scroll_num;
-
- /* might overlap; must run topdown, not bottomup */
- for (i = 0; i < num; ++i) {
- line = page->lines[top + i];
- page->lines[top + i] = page->lines[bottom + i];
- page->lines[bottom + i] = line;
- }
- }
-
- /* update vertical extents */
- page->height = rows;
- page->scroll_idx = MIN(page->scroll_idx, rows);
- page->scroll_num -= MIN(page->scroll_num, old_height - rows);
- /* fill is already up-to-date or 0 due to scroll-up */
- } else if (rows > old_height) {
- /*
- * If we increase the terminal-height, we emulate a scroll-down
- * and fetch new lines from the history.
- * New lines are always accounted to the scroll-region. Thus we
- * have to preserve the lower margin first, by moving it down.
- */
-
- /* move lower margin down */
- num = LESS_BY(old_height, page->scroll_idx + page->scroll_num);
- if (num > 0) {
- unsigned int top, bottom;
-
- top = page->scroll_idx + page->scroll_num;
- bottom = top + (rows - old_height);
-
- /* might overlap; must run bottomup, not topdown */
- for (i = num; i-- > 0; ) {
- line = page->lines[top + i];
- page->lines[top + i] = page->lines[bottom + i];
- page->lines[bottom + i] = line;
- }
- }
-
- /* update vertical extents */
- page->height = rows;
- page->scroll_num = MIN(LESS_BY(rows, page->scroll_idx),
- page->scroll_num + (rows - old_height));
-
- /* check how many lines can be received from history */
- if (history)
- num = term_history_peek(history,
- rows - old_height,
- cols,
- attr,
- age);
- else
- num = 0;
-
- /* retrieve new lines from history if available */
- if (num > 0)
- page_scroll_down(page,
- cols,
- num,
- attr,
- age,
- history);
- }
-
- /* set horizontal extents */
- page->width = cols;
- for (i = 0; i < page->height; ++i)
- term_line_set_width(page->lines[i], cols);
-}
-
-/**
- * term_page_write() - Write to a single cell
- * @page: page to operate on
- * @pos_x: x-position of cell to write to
- * @pos_y: y-position of cell to write to
- * @ch: character to write
- * @cwidth: character-width of @ch
- * @attr: attributes to set on the cell or NULL
- * @age: age to use for all modifications
- * @insert_mode: true if INSERT-MODE is enabled
- *
- * This writes a character to a specific cell. If the cell is beyond bounds,
- * this is a no-op. @attr and @age are used to update the cell. @flags can be
- * used to alter the behavior of this function.
- *
- * This is a wrapper around term_line_write().
- *
- * This call does not wrap around lines. That is, this only operates on a single
- * line.
- */
-void term_page_write(term_page *page, unsigned int pos_x, unsigned int pos_y, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode) {
- assert(page);
-
- if (pos_y >= page->height)
- return;
-
- term_line_write(page->lines[pos_y], pos_x, ch, cwidth, attr, age, insert_mode);
-}
-
-/**
- * term_page_insert_cells() - Insert cells into a line
- * @page: page to operate on
- * @from_x: x-position where to insert new cells
- * @from_y: y-position where to insert new cells
- * @num: number of cells to insert
- * @attr: attributes to set on new cells or NULL
- * @age: age to use for all modifications
- *
- * This inserts new cells into a given line. This is a wrapper around
- * term_line_insert().
- *
- * This call does not wrap around lines. That is, this only operates on a single
- * line.
- */
-void term_page_insert_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age) {
- assert(page);
-
- if (from_y >= page->height)
- return;
-
- term_line_insert(page->lines[from_y], from_x, num, attr, age);
-}
-
-/**
- * term_page_delete_cells() - Delete cells from a line
- * @page: page to operate on
- * @from_x: x-position where to delete cells
- * @from_y: y-position where to delete cells
- * @num: number of cells to delete
- * @attr: attributes to set on new cells or NULL
- * @age: age to use for all modifications
- *
- * This deletes cells from a given line. This is a wrapper around
- * term_line_delete().
- *
- * This call does not wrap around lines. That is, this only operates on a single
- * line.
- */
-void term_page_delete_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age) {
- assert(page);
-
- if (from_y >= page->height)
- return;
-
- term_line_delete(page->lines[from_y], from_x, num, attr, age);
-}
-
-/**
- * term_page_append_combchar() - Append combining-character to a cell
- * @page: page to operate on
- * @pos_x: x-position of target cell
- * @pos_y: y-position of target cell
- * @ucs4: combining character to append
- * @age: age to use for all modifications
- *
- * This appends a combining-character to a specific cell. This is a wrapper
- * around term_line_append_combchar().
- */
-void term_page_append_combchar(term_page *page, unsigned int pos_x, unsigned int pos_y, uint32_t ucs4, term_age_t age) {
- assert(page);
-
- if (pos_y >= page->height)
- return;
-
- term_line_append_combchar(page->lines[pos_y], pos_x, ucs4, age);
-}
-
-/**
- * term_page_erase() - Erase parts of a page
- * @page: page to operate on
- * @from_x: x-position where to start erasure (inclusive)
- * @from_y: y-position where to start erasure (inclusive)
- * @to_x: x-position where to stop erasure (inclusive)
- * @to_y: y-position where to stop erasure (inclusive)
- * @attr: attributes to set on cells
- * @age: age to use for all modifications
- * @keep_protected: true if protected cells should be kept
- *
- * This erases all cells starting at @from_x/@from_y up to @to_x/@to_y. Note
- * that this wraps around line-boundaries so lines between @from_y and @to_y
- * are cleared entirely.
- *
- * Lines outside the visible area are left untouched.
- */
-void term_page_erase(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int to_x, unsigned int to_y, const term_attr *attr, term_age_t age, bool keep_protected) {
- unsigned int i, from, to;
-
- assert(page);
-
- for (i = from_y; i <= to_y && i < page->height; ++i) {
- from = 0;
- to = page->width;
-
- if (i == from_y)
- from = from_x;
- if (i == to_y)
- to = to_x;
-
- term_line_erase(page->lines[i],
- from,
- LESS_BY(to, from),
- attr,
- age,
- keep_protected);
- }
-}
-
-/**
- * term_page_reset() - Reset page
- * @page: page to modify
- * @attr: attributes to set on cells
- * @age: age to use for all modifications
- *
- * This erases the whole visible page. See term_page_erase().
- */
-void term_page_reset(term_page *page, const term_attr *attr, term_age_t age) {
- assert(page);
-
- return term_page_erase(page,
- 0, 0,
- page->width - 1, page->height - 1,
- attr,
- age,
- 0);
-}
-
-/**
- * term_page_set_scroll_region() - Set scroll region
- * @page: page to operate on
- * @idx: start-index of scroll region
- * @num: number of lines in scroll region
- *
- * This sets the scroll region of a page. Whenever an operation needs to scroll
- * lines, it scrolls them inside of that region. Lines outside the region are
- * left untouched. In case a scroll-operation is targeted outside of this
- * region, it will implicitly get a scroll-region of only one line (i.e., no
- * scroll region at all).
- *
- * Note that the scroll-region is clipped to the current page-extents. Growing
- * or shrinking the page always accounts new/old lines to the scroll region and
- * moves top/bottom margins accordingly so they're preserved.
- */
-void term_page_set_scroll_region(term_page *page, unsigned int idx, unsigned int num) {
- assert(page);
-
- if (page->height < 1) {
- page->scroll_idx = 0;
- page->scroll_num = 0;
- } else {
- page->scroll_idx = MIN(idx, page->height - 1);
- page->scroll_num = MIN(num, page->height - page->scroll_idx);
- }
-}
-
-/**
- * term_page_scroll_up() - Scroll up
- * @page: page to operate on
- * @num: number of lines to scroll up
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for old lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are cleared and reset
- * with the given attributes. Old lines are moved into the history if non-NULL.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-void term_page_scroll_up(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- page_scroll_up(page, page->width, num, attr, age, history);
-}
-
-/**
- * term_page_scroll_down() - Scroll down
- * @page: page to operate on
- * @num: number of lines to scroll down
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for new lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are retrieved from
- * the history or cleared if the history is empty or NULL.
- *
- * Usually, scroll-down implies that new lines are cleared. Therefore, you're
- * highly encouraged to set @history to NULL. However, if you resize a terminal,
- * you might want to include history-lines in the new area. In that case, you
- * should set @history to non-NULL.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-void term_page_scroll_down(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- page_scroll_down(page, page->width, num, attr, age, history);
-}
-
-/**
- * term_page_insert_lines() - Insert new lines
- * @page: page to operate on
- * @pos_y: y-position where to insert new lines
- * @num: number of lines to insert
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- *
- * This inserts @num new lines at position @pos_y. If @pos_y is beyond
- * boundaries or @num is 0, this is a no-op.
- * All lines below @pos_y are moved down to make space for the new lines. Lines
- * on the bottom are dropped. Note that this only moves lines above or inside
- * the scroll-region. If @pos_y is below the scroll-region, a scroll-region of
- * one line is implied (which means the line is simply cleared).
- */
-void term_page_insert_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age) {
- unsigned int scroll_idx, scroll_num;
-
- assert(page);
-
- if (pos_y >= page->height)
- return;
- if (num >= page->height)
- num = page->height;
-
- /* remember scroll-region */
- scroll_idx = page->scroll_idx;
- scroll_num = page->scroll_num;
-
- /* set scroll-region temporarily so we can reuse scroll_down() */
- {
- page->scroll_idx = pos_y;
- if (pos_y >= scroll_idx + scroll_num)
- page->scroll_num = 1;
- else if (pos_y >= scroll_idx)
- page->scroll_num -= pos_y - scroll_idx;
- else
- page->scroll_num += scroll_idx - pos_y;
-
- term_page_scroll_down(page, num, attr, age, NULL);
- }
-
- /* reset scroll-region */
- page->scroll_idx = scroll_idx;
- page->scroll_num = scroll_num;
-}
-
-/**
- * term_page_delete_lines() - Delete lines
- * @page: page to operate on
- * @pos_y: y-position where to delete lines
- * @num: number of lines to delete
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- *
- * This deletes @num lines at position @pos_y. If @pos_y is beyond boundaries or
- * @num is 0, this is a no-op.
- * All lines below @pos_y are moved up into the newly made space. New lines
- * on the bottom are clear. Note that this only moves lines above or inside
- * the scroll-region. If @pos_y is below the scroll-region, a scroll-region of
- * one line is implied (which means the line is simply cleared).
- */
-void term_page_delete_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age) {
- unsigned int scroll_idx, scroll_num;
-
- assert(page);
-
- if (pos_y >= page->height)
- return;
- if (num >= page->height)
- num = page->height;
-
- /* remember scroll-region */
- scroll_idx = page->scroll_idx;
- scroll_num = page->scroll_num;
-
- /* set scroll-region temporarily so we can reuse scroll_up() */
- {
- page->scroll_idx = pos_y;
- if (pos_y >= scroll_idx + scroll_num)
- page->scroll_num = 1;
- else if (pos_y > scroll_idx)
- page->scroll_num -= pos_y - scroll_idx;
- else
- page->scroll_num += scroll_idx - pos_y;
-
- term_page_scroll_up(page, num, attr, age, NULL);
- }
-
- /* reset scroll-region */
- page->scroll_idx = scroll_idx;
- page->scroll_num = scroll_num;
-}
-
-/**
- * term_history_new() - Create new history object
- * @out: storage for pointer to new history
- *
- * Create a new history object. Histories are used to store scrollback-lines
- * from VTE pages. You're highly recommended to set a history-limit on
- * history->max_lines and trim it via term_history_trim(), otherwise history
- * allocations are unlimited.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_history_new(term_history **out) {
- _term_history_free_ term_history *history = NULL;
-
- assert_return(out, -EINVAL);
-
- history = new0(term_history, 1);
- if (!history)
- return -ENOMEM;
-
- history->max_lines = 4096;
-
- *out = history;
- history = NULL;
- return 0;
-}
-
-/**
- * term_history_free() - Free history
- * @history: history to free
- *
- * Clear and free history. You must not access the object afterwards.
- *
- * Returns: NULL
- */
-term_history *term_history_free(term_history *history) {
- if (!history)
- return NULL;
-
- term_history_clear(history);
- free(history);
- return NULL;
-}
-
-/**
- * term_history_clear() - Clear history
- * @history: history to clear
- *
- * Remove all linked lines from a history and reset it to its initial state.
- */
-void term_history_clear(term_history *history) {
- return term_history_trim(history, 0);
-}
-
-/**
- * term_history_trim() - Trim history
- * @history: history to trim
- * @max: maximum number of lines to be left in history
- *
- * This removes lines from the history until it is smaller than @max. Lines are
- * removed from the top.
- */
-void term_history_trim(term_history *history, unsigned int max) {
- term_line *line;
-
- if (!history)
- return;
-
- while (history->n_lines > max && (line = history->lines_first)) {
- TERM_LINE_UNLINK(line, history);
- term_line_free(line);
- --history->n_lines;
- }
-}
-
-/**
- * term_history_push() - Push line into history
- * @history: history to work on
- * @line: line to push into history
- *
- * This pushes a line into the given history. It is linked at the tail. In case
- * the history is limited, the top-most line might be freed.
- */
-void term_history_push(term_history *history, term_line *line) {
- assert(history);
- assert(line);
-
- TERM_LINE_LINK_TAIL(line, history);
- if (history->max_lines > 0 && history->n_lines >= history->max_lines) {
- line = history->lines_first;
- TERM_LINE_UNLINK(line, history);
- term_line_free(line);
- } else {
- ++history->n_lines;
- }
-}
-
-/**
- * term_history_pop() - Retrieve last line from history
- * @history: history to work on
- * @new_width: width to reserve and set on the line
- * @attr: attributes to use for cell reservation
- * @age: age to use for cell reservation
- *
- * This unlinks the last linked line of the history and returns it. This also
- * makes sure the line has the given width pre-allocated (see
- * term_line_reserve()). If the pre-allocation fails, this returns NULL, so it
- * is treated like there's no line in history left. This simplifies
- * history-handling on the caller's side in case of allocation errors. No need
- * to throw lines away just because the reservation failed. We can keep them in
- * history safely, and make them available as scrollback.
- *
- * Returns: Line from history or NULL
- */
-term_line *term_history_pop(term_history *history, unsigned int new_width, const term_attr *attr, term_age_t age) {
- term_line *line;
- int r;
-
- assert_return(history, NULL);
-
- line = history->lines_last;
- if (!line)
- return NULL;
-
- r = term_line_reserve(line, new_width, attr, age, line->width);
- if (r < 0)
- return NULL;
-
- term_line_set_width(line, new_width);
- TERM_LINE_UNLINK(line, history);
- --history->n_lines;
-
- return line;
-}
-
-/**
- * term_history_peek() - Return number of available history-lines
- * @history: history to work on
- * @max: maximum number of lines to look at
- * @reserve_width: width to reserve on the line
- * @attr: attributes to use for cell reservation
- * @age: age to use for cell reservation
- *
- * This returns the number of available lines in the history given as @history.
- * It returns at most @max. For each line that is looked at, the line is
- * verified to have at least @reserve_width cells. Valid cells are preserved,
- * new cells are initialized with @attr and @age. In case an allocation fails,
- * we bail out and return the number of lines that are valid so far.
- *
- * Usually, this function should be used before running a loop on
- * term_history_pop(). This function guarantees that term_history_pop() (with
- * the same arguments) will succeed at least the returned number of times.
- *
- * Returns: Number of valid lines that can be received via term_history_pop().
- */
-unsigned int term_history_peek(term_history *history, unsigned int max, unsigned int reserve_width, const term_attr *attr, term_age_t age) {
- unsigned int num;
- term_line *line;
- int r;
-
- assert(history);
-
- num = 0;
- line = history->lines_last;
-
- while (num < max && line) {
- r = term_line_reserve(line, reserve_width, attr, age, line->width);
- if (r < 0)
- break;
-
- ++num;
- line = line->lines_prev;
- }
-
- return num;
-}
diff --git a/src/libsystemd-terminal/term-parser.c b/src/libsystemd-terminal/term-parser.c
deleted file mode 100644
index 8dc1da2f9c..0000000000
--- a/src/libsystemd-terminal/term-parser.c
+++ /dev/null
@@ -1,1702 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Terminal Parser
- * This file contains a bunch of UTF-8 helpers and the main ctlseq-parser. The
- * parser is a simple state-machine that correctly parses all CSI, DCS, OSC, ST
- * control sequences and generic escape sequences.
- * The parser itself does not perform any actions but lets the caller react to
- * detected sequences.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-
-static const uint8_t default_palette[18][3] = {
- { 0, 0, 0 }, /* black */
- { 205, 0, 0 }, /* red */
- { 0, 205, 0 }, /* green */
- { 205, 205, 0 }, /* yellow */
- { 0, 0, 238 }, /* blue */
- { 205, 0, 205 }, /* magenta */
- { 0, 205, 205 }, /* cyan */
- { 229, 229, 229 }, /* light grey */
- { 127, 127, 127 }, /* dark grey */
- { 255, 0, 0 }, /* light red */
- { 0, 255, 0 }, /* light green */
- { 255, 255, 0 }, /* light yellow */
- { 92, 92, 255 }, /* light blue */
- { 255, 0, 255 }, /* light magenta */
- { 0, 255, 255 }, /* light cyan */
- { 255, 255, 255 }, /* white */
-
- { 229, 229, 229 }, /* light grey */
- { 0, 0, 0 }, /* black */
-};
-
-static uint32_t term_color_to_argb32(const term_color *color, const term_attr *attr, const uint8_t *palette) {
- static const uint8_t bval[] = {
- 0x00, 0x5f, 0x87,
- 0xaf, 0xd7, 0xff,
- };
- uint8_t r, g, b, t;
-
- assert(color);
-
- if (!palette)
- palette = (void*)default_palette;
-
- switch (color->ccode) {
- case TERM_CCODE_RGB:
- r = color->red;
- g = color->green;
- b = color->blue;
-
- break;
- case TERM_CCODE_256:
- t = color->c256;
- if (t < 16) {
- r = palette[t * 3 + 0];
- g = palette[t * 3 + 1];
- b = palette[t * 3 + 2];
- } else if (t < 232) {
- t -= 16;
- b = bval[t % 6];
- t /= 6;
- g = bval[t % 6];
- t /= 6;
- r = bval[t % 6];
- } else {
- t = (t - 232) * 10 + 8;
- r = t;
- g = t;
- b = t;
- }
-
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_LIGHT_WHITE:
- t = color->ccode - TERM_CCODE_BLACK;
-
- /* bold causes light colors (only for foreground colors) */
- if (t < 8 && attr->bold && color == &attr->fg)
- t += 8;
-
- r = palette[t * 3 + 0];
- g = palette[t * 3 + 1];
- b = palette[t * 3 + 2];
- break;
- case TERM_CCODE_DEFAULT:
- /* fallthrough */
- default:
- t = 16 + !(color == &attr->fg);
- r = palette[t * 3 + 0];
- g = palette[t * 3 + 1];
- b = palette[t * 3 + 2];
- break;
- }
-
- return (0xff << 24) | (r << 16) | (g << 8) | b;
-}
-
-/**
- * term_attr_to_argb32() - Encode terminal colors as native ARGB32 value
- * @color: Terminal attributes to work on
- * @fg: Storage for foreground color (or NULL)
- * @bg: Storage for background color (or NULL)
- * @palette: The color palette to use (or NULL for default)
- *
- * This encodes the colors attr->fg and attr->bg as native-endian ARGB32 values
- * and returns them. Any color conversions are automatically applied.
- */
-void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette) {
- uint32_t f, b, t;
-
- assert(attr);
-
- f = term_color_to_argb32(&attr->fg, attr, palette);
- b = term_color_to_argb32(&attr->bg, attr, palette);
-
- if (attr->inverse) {
- t = f;
- f = b;
- b = t;
- }
-
- if (fg)
- *fg = f;
- if (bg)
- *bg = b;
-}
-
-/**
- * term_utf8_decode() - Try decoding the next UCS-4 character
- * @p: decoder object to operate on or NULL
- * @out_len: output storage for pointer to decoded UCS-4 string or NULL
- * @c: next char to push into decoder
- *
- * This decodes a UTF-8 stream. It must be called for each input-byte of the
- * UTF-8 stream and returns a UCS-4 stream. A pointer to the parsed UCS-4
- * string is stored in @out_buf if non-NULL. The length of this string (number
- * of parsed UCS4 characters) is returned as result. The string is not
- * zero-terminated! Furthermore, the string is only valid until the next
- * invocation of this function. It is also bound to the parser state @p and
- * must not be freed nor written to by the caller.
- *
- * This function is highly optimized to work with terminal-emulators. Instead
- * of being strict about UTF-8 validity, this tries to perform a fallback to
- * ISO-8859-1 in case a wrong series was detected. Therefore, this function
- * might return multiple UCS-4 characters by parsing just a single UTF-8 byte.
- *
- * The parser state @p should be allocated and managed by the caller. There're
- * no helpers to do that for you. To initialize it, simply reset it to all
- * zero. You can reset or free the object at any point in time.
- *
- * Returns: Number of parsed UCS4 characters
- */
-size_t term_utf8_decode(term_utf8 *p, uint32_t **out_buf, char c) {
- static uint32_t ucs4_null = 0;
- uint32_t t, *res = NULL;
- uint8_t byte;
- size_t len = 0;
-
- if (!p)
- goto out;
-
- byte = c;
-
- if (!p->valid || p->i_bytes >= p->n_bytes) {
- /*
- * If the previous sequence was invalid or fully parsed, start
- * parsing a fresh new sequence.
- */
-
- if ((byte & 0xE0) == 0xC0) {
- /* start of two byte sequence */
- t = byte & 0x1F;
- p->n_bytes = 2;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF0) == 0xE0) {
- /* start of three byte sequence */
- t = byte & 0x0F;
- p->n_bytes = 3;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF8) == 0xF0) {
- /* start of four byte sequence */
- t = byte & 0x07;
- p->n_bytes = 4;
- p->i_bytes = 1;
- p->valid = 1;
- } else {
- /* Either of:
- * - single ASCII 7-bit char
- * - out-of-sync continuation byte
- * - overlong encoding
- * All of them are treated as single byte ISO-8859-1 */
- t = byte;
- p->n_bytes = 1;
- p->i_bytes = 1;
- p->valid = 0;
- }
-
- p->chars[0] = byte;
- p->ucs4 = t << (6 * (p->n_bytes - p->i_bytes));
- } else {
- /*
- * ..otherwise, try to continue the previous sequence..
- */
-
- if ((byte & 0xC0) == 0x80) {
- /*
- * Valid continuation byte. Append to sequence and
- * update the ucs4 cache accordingly.
- */
-
- t = byte & 0x3F;
- p->chars[p->i_bytes++] = byte;
- p->ucs4 |= t << (6 * (p->n_bytes - p->i_bytes));
- } else {
- /*
- * Invalid continuation? Treat cached sequence as
- * ISO-8859-1, but parse the new char as valid new
- * starting character. If it's a new single-byte UTF-8
- * sequence, we immediately return it in the same run,
- * otherwise, we might suffer from starvation.
- */
-
- if ((byte & 0xE0) == 0xC0 ||
- (byte & 0xF0) == 0xE0 ||
- (byte & 0xF8) == 0xF0) {
- /*
- * New multi-byte sequence. Move to-be-returned
- * data at the end and start new sequence. Only
- * return the old sequence.
- */
-
- memmove(p->chars + 1,
- p->chars,
- sizeof(*p->chars) * p->i_bytes);
- res = p->chars + 1;
- len = p->i_bytes;
-
- if ((byte & 0xE0) == 0xC0) {
- /* start of two byte sequence */
- t = byte & 0x1F;
- p->n_bytes = 2;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF0) == 0xE0) {
- /* start of three byte sequence */
- t = byte & 0x0F;
- p->n_bytes = 3;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF8) == 0xF0) {
- /* start of four byte sequence */
- t = byte & 0x07;
- p->n_bytes = 4;
- p->i_bytes = 1;
- p->valid = 1;
- } else
- assert_not_reached("Should not happen");
-
- p->chars[0] = byte;
- p->ucs4 = t << (6 * (p->n_bytes - p->i_bytes));
-
- goto out;
- } else {
- /*
- * New single byte sequence, append to output
- * and return combined sequence.
- */
-
- p->chars[p->i_bytes++] = byte;
- p->valid = 0;
- }
- }
- }
-
- /*
- * Check whether a full sequence (valid or invalid) has been parsed and
- * then return it. Otherwise, return nothing.
- */
- if (p->valid) {
- /* still parsing? then bail out */
- if (p->i_bytes < p->n_bytes)
- goto out;
-
- res = &p->ucs4;
- len = 1;
- } else {
- res = p->chars;
- len = p->i_bytes;
- }
-
- p->valid = 0;
- p->i_bytes = 0;
- p->n_bytes = 0;
-
-out:
- if (out_buf)
- *out_buf = res ? : &ucs4_null;
- return len;
-}
-
-/*
- * Command Parser
- * The ctl-seq parser "term_parser" only detects whole sequences, it does not
- * detect the specific command. Once a sequence is parsed, the command-parsers
- * are used to figure out their meaning. Note that this depends on whether we
- * run on the host or terminal side.
- */
-
-static unsigned int term_parse_host_control(const term_seq *seq) {
- assert_return(seq, TERM_CMD_NONE);
-
- switch (seq->terminator) {
- case 0x00: /* NUL */
- return TERM_CMD_NULL;
- case 0x05: /* ENQ */
- return TERM_CMD_ENQ;
- case 0x07: /* BEL */
- return TERM_CMD_BEL;
- case 0x08: /* BS */
- return TERM_CMD_BS;
- case 0x09: /* HT */
- return TERM_CMD_HT;
- case 0x0a: /* LF */
- return TERM_CMD_LF;
- case 0x0b: /* VT */
- return TERM_CMD_VT;
- case 0x0c: /* FF */
- return TERM_CMD_FF;
- case 0x0d: /* CR */
- return TERM_CMD_CR;
- case 0x0e: /* SO */
- return TERM_CMD_SO;
- case 0x0f: /* SI */
- return TERM_CMD_SI;
- case 0x11: /* DC1 */
- return TERM_CMD_DC1;
- case 0x13: /* DC3 */
- return TERM_CMD_DC3;
- case 0x18: /* CAN */
- /* this is already handled by the state-machine */
- break;
- case 0x1a: /* SUB */
- return TERM_CMD_SUB;
- case 0x1b: /* ESC */
- /* this is already handled by the state-machine */
- break;
- case 0x1f: /* DEL */
- /* this is already handled by the state-machine */
- break;
- case 0x84: /* IND */
- return TERM_CMD_IND;
- case 0x85: /* NEL */
- return TERM_CMD_NEL;
- case 0x88: /* HTS */
- return TERM_CMD_HTS;
- case 0x8d: /* RI */
- return TERM_CMD_RI;
- case 0x8e: /* SS2 */
- return TERM_CMD_SS2;
- case 0x8f: /* SS3 */
- return TERM_CMD_SS3;
- case 0x90: /* DCS */
- /* this is already handled by the state-machine */
- break;
- case 0x96: /* SPA */
- return TERM_CMD_SPA;
- case 0x97: /* EPA */
- return TERM_CMD_EPA;
- case 0x98: /* SOS */
- /* this is already handled by the state-machine */
- break;
- case 0x9a: /* DECID */
- return TERM_CMD_DECID;
- case 0x9b: /* CSI */
- /* this is already handled by the state-machine */
- break;
- case 0x9c: /* ST */
- return TERM_CMD_ST;
- case 0x9d: /* OSC */
- /* this is already handled by the state-machine */
- break;
- case 0x9e: /* PM */
- /* this is already handled by the state-machine */
- break;
- case 0x9f: /* APC */
- /* this is already handled by the state-machine */
- break;
- }
-
- return TERM_CMD_NONE;
-}
-
-static inline int charset_from_cmd(uint32_t raw, unsigned int flags, bool require_96) {
- static const struct {
- uint32_t raw;
- unsigned int flags;
- } charset_cmds[] = {
- /* 96-compat charsets */
- [TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL] = { .raw = 'A', .flags = 0 },
- [TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL] = { .raw = 'B', .flags = 0 },
- [TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL] = { .raw = 'M', .flags = 0 },
- [TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL] = { .raw = 'F', .flags = 0 },
- [TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL] = { .raw = 'H', .flags = 0 },
- [TERM_CHARSET_ISO_LATIN_CYRILLIC] = { .raw = 'L', .flags = 0 },
-
- /* 94-compat charsets */
- [TERM_CHARSET_DEC_SPECIAL_GRAPHIC] = { .raw = '0', .flags = 0 },
- [TERM_CHARSET_DEC_SUPPLEMENTAL] = { .raw = '5', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_DEC_TECHNICAL] = { .raw = '>', .flags = 0 },
- [TERM_CHARSET_CYRILLIC_DEC] = { .raw = '4', .flags = TERM_SEQ_FLAG_AND },
- [TERM_CHARSET_DUTCH_NRCS] = { .raw = '4', .flags = 0 },
- [TERM_CHARSET_FINNISH_NRCS] = { .raw = '5', .flags = 0 },
- [TERM_CHARSET_FRENCH_NRCS] = { .raw = 'R', .flags = 0 },
- [TERM_CHARSET_FRENCH_CANADIAN_NRCS] = { .raw = '9', .flags = 0 },
- [TERM_CHARSET_GERMAN_NRCS] = { .raw = 'K', .flags = 0 },
- [TERM_CHARSET_GREEK_DEC] = { .raw = '?', .flags = TERM_SEQ_FLAG_DQUOTE },
- [TERM_CHARSET_GREEK_NRCS] = { .raw = '>', .flags = TERM_SEQ_FLAG_DQUOTE },
- [TERM_CHARSET_HEBREW_DEC] = { .raw = '4', .flags = TERM_SEQ_FLAG_DQUOTE },
- [TERM_CHARSET_HEBREW_NRCS] = { .raw = '=', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_ITALIAN_NRCS] = { .raw = 'Y', .flags = 0 },
- [TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = '`', .flags = 0 },
- [TERM_CHARSET_PORTUGUESE_NRCS] = { .raw = '6', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_RUSSIAN_NRCS] = { .raw = '5', .flags = TERM_SEQ_FLAG_AND },
- [TERM_CHARSET_SCS_NRCS] = { .raw = '3', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_SPANISH_NRCS] = { .raw = 'Z', .flags = 0 },
- [TERM_CHARSET_SWEDISH_NRCS] = { .raw = '7', .flags = 0 },
- [TERM_CHARSET_SWISS_NRCS] = { .raw = '=', .flags = 0 },
- [TERM_CHARSET_TURKISH_DEC] = { .raw = '0', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_TURKISH_NRCS] = { .raw = '2', .flags = TERM_SEQ_FLAG_PERCENT },
-
- /* special charsets */
- [TERM_CHARSET_USERPREF_SUPPLEMENTAL] = { .raw = '<', .flags = 0 },
-
- /* secondary choices */
- [TERM_CHARSET_CNT + TERM_CHARSET_FINNISH_NRCS] = { .raw = 'C', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_FRENCH_NRCS] = { .raw = 'f', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_FRENCH_CANADIAN_NRCS] = { .raw = 'Q', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = 'E', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_SWEDISH_NRCS] = { .raw = 'H', .flags = 0 }, /* unused; conflicts with ISO_HEBREW */
-
- /* tertiary choices */
- [TERM_CHARSET_CNT + TERM_CHARSET_CNT + TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = '6', .flags = 0 },
- };
- size_t i, cs;
-
- /*
- * Secondary choice on SWEDISH_NRCS and primary choice on
- * ISO_HEBREW_SUPPLEMENTAL have a conflict: raw=="H", flags==0.
- * We always choose the ISO 96-compat set, which is what VT510 does.
- */
-
- for (i = 0; i < ELEMENTSOF(charset_cmds); ++i) {
- if (charset_cmds[i].raw == raw && charset_cmds[i].flags == flags) {
- cs = i;
- while (cs >= TERM_CHARSET_CNT)
- cs -= TERM_CHARSET_CNT;
-
- if (!require_96 || cs < TERM_CHARSET_96_CNT || cs >= TERM_CHARSET_94_CNT)
- return cs;
- }
- }
-
- return -ENOENT;
-}
-
-/* true if exactly one bit in @value is set */
-static inline bool exactly_one_bit_set(unsigned int value) {
- return __builtin_popcount(value) == 1;
-}
-
-static unsigned int term_parse_host_escape(const term_seq *seq, unsigned int *cs_out) {
- unsigned int t, flags;
- int cs;
-
- assert_return(seq, TERM_CMD_NONE);
-
- flags = seq->intermediates;
- t = TERM_SEQ_FLAG_POPEN | TERM_SEQ_FLAG_PCLOSE | TERM_SEQ_FLAG_MULT |
- TERM_SEQ_FLAG_PLUS | TERM_SEQ_FLAG_MINUS | TERM_SEQ_FLAG_DOT |
- TERM_SEQ_FLAG_SLASH;
-
- if (exactly_one_bit_set(flags & t)) {
- switch (flags & t) {
- case TERM_SEQ_FLAG_POPEN:
- case TERM_SEQ_FLAG_PCLOSE:
- case TERM_SEQ_FLAG_MULT:
- case TERM_SEQ_FLAG_PLUS:
- cs = charset_from_cmd(seq->terminator, flags & ~t, false);
- break;
- case TERM_SEQ_FLAG_MINUS:
- case TERM_SEQ_FLAG_DOT:
- case TERM_SEQ_FLAG_SLASH:
- cs = charset_from_cmd(seq->terminator, flags & ~t, true);
- break;
- default:
- cs = -ENOENT;
- break;
- }
-
- if (cs >= 0) {
- if (cs_out)
- *cs_out = cs;
- return TERM_CMD_SCS;
- }
-
- /* looked like a charset-cmd but wasn't; continue */
- }
-
- switch (seq->terminator) {
- case '3':
- if (flags == TERM_SEQ_FLAG_HASH) /* DECDHL top-half */
- return TERM_CMD_DECDHL_TH;
- break;
- case '4':
- if (flags == TERM_SEQ_FLAG_HASH) /* DECDHL bottom-half */
- return TERM_CMD_DECDHL_BH;
- break;
- case '5':
- if (flags == TERM_SEQ_FLAG_HASH) /* DECSWL */
- return TERM_CMD_DECSWL;
- break;
- case '6':
- if (flags == 0) /* DECBI */
- return TERM_CMD_DECBI;
- else if (flags == TERM_SEQ_FLAG_HASH) /* DECDWL */
- return TERM_CMD_DECDWL;
- break;
- case '7':
- if (flags == 0) /* DECSC */
- return TERM_CMD_DECSC;
- break;
- case '8':
- if (flags == 0) /* DECRC */
- return TERM_CMD_DECRC;
- else if (flags == TERM_SEQ_FLAG_HASH) /* DECALN */
- return TERM_CMD_DECALN;
- break;
- case '9':
- if (flags == 0) /* DECFI */
- return TERM_CMD_DECFI;
- break;
- case '<':
- if (flags == 0) /* DECANM */
- return TERM_CMD_DECANM;
- break;
- case '=':
- if (flags == 0) /* DECKPAM */
- return TERM_CMD_DECKPAM;
- break;
- case '>':
- if (flags == 0) /* DECKPNM */
- return TERM_CMD_DECKPNM;
- break;
- case '@':
- if (flags == TERM_SEQ_FLAG_PERCENT) {
- /* Select default character set */
- return TERM_CMD_XTERM_SDCS;
- }
- break;
- case 'D':
- if (flags == 0) /* IND */
- return TERM_CMD_IND;
- break;
- case 'E':
- if (flags == 0) /* NEL */
- return TERM_CMD_NEL;
- break;
- case 'F':
- if (flags == 0) /* Cursor to lower-left corner of screen */
- return TERM_CMD_XTERM_CLLHP;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* S7C1T */
- return TERM_CMD_S7C1T;
- break;
- case 'G':
- if (flags == TERM_SEQ_FLAG_SPACE) { /* S8C1T */
- return TERM_CMD_S8C1T;
- } else if (flags == TERM_SEQ_FLAG_PERCENT) {
- /* Select UTF-8 character set */
- return TERM_CMD_XTERM_SUCS;
- }
- break;
- case 'H':
- if (flags == 0) /* HTS */
- return TERM_CMD_HTS;
- break;
- case 'L':
- if (flags == TERM_SEQ_FLAG_SPACE) {
- /* Set ANSI conformance level 1 */
- return TERM_CMD_XTERM_SACL1;
- }
- break;
- case 'M':
- if (flags == 0) { /* RI */
- return TERM_CMD_RI;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* Set ANSI conformance level 2 */
- return TERM_CMD_XTERM_SACL2;
- }
- break;
- case 'N':
- if (flags == 0) { /* SS2 */
- return TERM_CMD_SS2;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* Set ANSI conformance level 3 */
- return TERM_CMD_XTERM_SACL3;
- }
- break;
- case 'O':
- if (flags == 0) /* SS3 */
- return TERM_CMD_SS3;
- break;
- case 'P':
- if (flags == 0) /* DCS: this is already handled by the state-machine */
- return 0;
- break;
- case 'V':
- if (flags == 0) /* SPA */
- return TERM_CMD_SPA;
- break;
- case 'W':
- if (flags == 0) /* EPA */
- return TERM_CMD_EPA;
- break;
- case 'X':
- if (flags == 0) { /* SOS */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case 'Z':
- if (flags == 0) /* DECID */
- return TERM_CMD_DECID;
- break;
- case '[':
- if (flags == 0) { /* CSI */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case '\\':
- if (flags == 0) /* ST */
- return TERM_CMD_ST;
- break;
- case ']':
- if (flags == 0) { /* OSC */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case '^':
- if (flags == 0) { /* PM */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case '_':
- if (flags == 0) { /* APC */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case 'c':
- if (flags == 0) /* RIS */
- return TERM_CMD_RIS;
- break;
- case 'l':
- if (flags == 0) /* Memory lock */
- return TERM_CMD_XTERM_MLHP;
- break;
- case 'm':
- if (flags == 0) /* Memory unlock */
- return TERM_CMD_XTERM_MUHP;
- break;
- case 'n':
- if (flags == 0) /* LS2 */
- return TERM_CMD_LS2;
- break;
- case 'o':
- if (flags == 0) /* LS3 */
- return TERM_CMD_LS3;
- break;
- case '|':
- if (flags == 0) /* LS3R */
- return TERM_CMD_LS3R;
- break;
- case '}':
- if (flags == 0) /* LS2R */
- return TERM_CMD_LS2R;
- break;
- case '~':
- if (flags == 0) /* LS1R */
- return TERM_CMD_LS1R;
- break;
- }
-
- return TERM_CMD_NONE;
-}
-
-static unsigned int term_parse_host_csi(const term_seq *seq) {
- unsigned int flags;
-
- assert_return(seq, TERM_CMD_NONE);
-
- flags = seq->intermediates;
-
- switch (seq->terminator) {
- case 'A':
- if (flags == 0) /* CUU */
- return TERM_CMD_CUU;
- break;
- case 'a':
- if (flags == 0) /* HPR */
- return TERM_CMD_HPR;
- break;
- case 'B':
- if (flags == 0) /* CUD */
- return TERM_CMD_CUD;
- break;
- case 'b':
- if (flags == 0) /* REP */
- return TERM_CMD_REP;
- break;
- case 'C':
- if (flags == 0) /* CUF */
- return TERM_CMD_CUF;
- break;
- case 'c':
- if (flags == 0) /* DA1 */
- return TERM_CMD_DA1;
- else if (flags == TERM_SEQ_FLAG_GT) /* DA2 */
- return TERM_CMD_DA2;
- else if (flags == TERM_SEQ_FLAG_EQUAL) /* DA3 */
- return TERM_CMD_DA3;
- break;
- case 'D':
- if (flags == 0) /* CUB */
- return TERM_CMD_CUB;
- break;
- case 'd':
- if (flags == 0) /* VPA */
- return TERM_CMD_VPA;
- break;
- case 'E':
- if (flags == 0) /* CNL */
- return TERM_CMD_CNL;
- break;
- case 'e':
- if (flags == 0) /* VPR */
- return TERM_CMD_VPR;
- break;
- case 'F':
- if (flags == 0) /* CPL */
- return TERM_CMD_CPL;
- break;
- case 'f':
- if (flags == 0) /* HVP */
- return TERM_CMD_HVP;
- break;
- case 'G':
- if (flags == 0) /* CHA */
- return TERM_CMD_CHA;
- break;
- case 'g':
- if (flags == 0) /* TBC */
- return TERM_CMD_TBC;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECLFKC */
- return TERM_CMD_DECLFKC;
- break;
- case 'H':
- if (flags == 0) /* CUP */
- return TERM_CMD_CUP;
- break;
- case 'h':
- if (flags == 0) /* SM ANSI */
- return TERM_CMD_SM_ANSI;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* SM DEC */
- return TERM_CMD_SM_DEC;
- break;
- case 'I':
- if (flags == 0) /* CHT */
- return TERM_CMD_CHT;
- break;
- case 'i':
- if (flags == 0) /* MC ANSI */
- return TERM_CMD_MC_ANSI;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* MC DEC */
- return TERM_CMD_MC_DEC;
- break;
- case 'J':
- if (flags == 0) /* ED */
- return TERM_CMD_ED;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* DECSED */
- return TERM_CMD_DECSED;
- break;
- case 'K':
- if (flags == 0) /* EL */
- return TERM_CMD_EL;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* DECSEL */
- return TERM_CMD_DECSEL;
- break;
- case 'L':
- if (flags == 0) /* IL */
- return TERM_CMD_IL;
- break;
- case 'l':
- if (flags == 0) /* RM ANSI */
- return TERM_CMD_RM_ANSI;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* RM DEC */
- return TERM_CMD_RM_DEC;
- break;
- case 'M':
- if (flags == 0) /* DL */
- return TERM_CMD_DL;
- break;
- case 'm':
- if (flags == 0) /* SGR */
- return TERM_CMD_SGR;
- else if (flags == TERM_SEQ_FLAG_GT) /* XTERM SMR */
- return TERM_CMD_XTERM_SRV;
- break;
- case 'n':
- if (flags == 0) /* DSR ANSI */
- return TERM_CMD_DSR_ANSI;
- else if (flags == TERM_SEQ_FLAG_GT) /* XTERM RMR */
- return TERM_CMD_XTERM_RRV;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* DSR DEC */
- return TERM_CMD_DSR_DEC;
- break;
- case 'P':
- if (flags == 0) /* DCH */
- return TERM_CMD_DCH;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* PPA */
- return TERM_CMD_PPA;
- break;
- case 'p':
- if (flags == 0) /* DECSSL */
- return TERM_CMD_DECSSL;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* DECSSCLS */
- return TERM_CMD_DECSSCLS;
- else if (flags == TERM_SEQ_FLAG_BANG) /* DECSTR */
- return TERM_CMD_DECSTR;
- else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECSCL */
- return TERM_CMD_DECSCL;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECRQM-ANSI */
- return TERM_CMD_DECRQM_ANSI;
- else if (flags == (TERM_SEQ_FLAG_CASH | TERM_SEQ_FLAG_WHAT)) /* DECRQM-DEC */
- return TERM_CMD_DECRQM_DEC;
- else if (flags == TERM_SEQ_FLAG_PCLOSE) /* DECSDPT */
- return TERM_CMD_DECSDPT;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSPPCS */
- return TERM_CMD_DECSPPCS;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECSR */
- return TERM_CMD_DECSR;
- else if (flags == TERM_SEQ_FLAG_COMMA) /* DECLTOD */
- return TERM_CMD_DECLTOD;
- else if (flags == TERM_SEQ_FLAG_GT) /* XTERM SPM */
- return TERM_CMD_XTERM_SPM;
- break;
- case 'Q':
- if (flags == TERM_SEQ_FLAG_SPACE) /* PPR */
- return TERM_CMD_PPR;
- break;
- case 'q':
- if (flags == 0) /* DECLL */
- return TERM_CMD_DECLL;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* DECSCUSR */
- return TERM_CMD_DECSCUSR;
- else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECSCA */
- return TERM_CMD_DECSCA;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECSDDT */
- return TERM_CMD_DECSDDT;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSRC */
- return TERM_CMD_DECSR;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECELF */
- return TERM_CMD_DECELF;
- else if (flags == TERM_SEQ_FLAG_COMMA) /* DECTID */
- return TERM_CMD_DECTID;
- break;
- case 'R':
- if (flags == TERM_SEQ_FLAG_SPACE) /* PPB */
- return TERM_CMD_PPB;
- break;
- case 'r':
- if (flags == 0) {
- /* DECSTBM */
- return TERM_CMD_DECSTBM;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* DECSKCV */
- return TERM_CMD_DECSKCV;
- } else if (flags == TERM_SEQ_FLAG_CASH) {
- /* DECCARA */
- return TERM_CMD_DECCARA;
- } else if (flags == TERM_SEQ_FLAG_MULT) {
- /* DECSCS */
- return TERM_CMD_DECSCS;
- } else if (flags == TERM_SEQ_FLAG_PLUS) {
- /* DECSMKR */
- return TERM_CMD_DECSMKR;
- } else if (flags == TERM_SEQ_FLAG_WHAT) {
- /*
- * There's a conflict between DECPCTERM and XTERM-RPM.
- * XTERM-RPM takes a single argument, DECPCTERM takes 2.
- * Split both up and forward the call to the closer
- * match.
- */
- if (seq->n_args <= 1) /* XTERM RPM */
- return TERM_CMD_XTERM_RPM;
- else if (seq->n_args >= 2) /* DECPCTERM */
- return TERM_CMD_DECPCTERM;
- }
- break;
- case 'S':
- if (flags == 0) /* SU */
- return TERM_CMD_SU;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* XTERM SGFX */
- return TERM_CMD_XTERM_SGFX;
- break;
- case 's':
- if (flags == 0) {
- /*
- * There's a conflict between DECSLRM and SC-ANSI which
- * cannot be resolved without knowing the state of
- * DECLRMM. We leave that decision up to the caller.
- */
- return TERM_CMD_DECSLRM_OR_SC;
- } else if (flags == TERM_SEQ_FLAG_CASH) {
- /* DECSPRTT */
- return TERM_CMD_DECSPRTT;
- } else if (flags == TERM_SEQ_FLAG_MULT) {
- /* DECSFC */
- return TERM_CMD_DECSFC;
- } else if (flags == TERM_SEQ_FLAG_WHAT) {
- /* XTERM SPM */
- return TERM_CMD_XTERM_SPM;
- }
- break;
- case 'T':
- if (flags == 0) {
- /*
- * Awesome: There's a conflict between SD and XTERM IHMT
- * that we have to resolve by checking the parameter
- * count.. XTERM_IHMT needs exactly 5 arguments, SD
- * takes 0 or 1. We're conservative here and give both
- * a wider range to allow unused arguments (compat...).
- */
- if (seq->n_args >= 5) {
- /* XTERM IHMT */
- return TERM_CMD_XTERM_IHMT;
- } else if (seq->n_args < 5) {
- /* SD */
- return TERM_CMD_SD;
- }
- } else if (flags == TERM_SEQ_FLAG_GT) {
- /* XTERM RTM */
- return TERM_CMD_XTERM_RTM;
- }
- break;
- case 't':
- if (flags == 0) {
- if (seq->n_args > 0 && seq->args[0] < 24) {
- /* XTERM WM */
- return TERM_CMD_XTERM_WM;
- } else {
- /* DECSLPP */
- return TERM_CMD_DECSLPP;
- }
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* DECSWBV */
- return TERM_CMD_DECSWBV;
- } else if (flags == TERM_SEQ_FLAG_DQUOTE) {
- /* DECSRFR */
- return TERM_CMD_DECSRFR;
- } else if (flags == TERM_SEQ_FLAG_CASH) {
- /* DECRARA */
- return TERM_CMD_DECRARA;
- } else if (flags == TERM_SEQ_FLAG_GT) {
- /* XTERM STM */
- return TERM_CMD_XTERM_STM;
- }
- break;
- case 'U':
- if (flags == 0) /* NP */
- return TERM_CMD_NP;
- break;
- case 'u':
- if (flags == 0) {
- /* RC */
- return TERM_CMD_RC;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* DECSMBV */
- return TERM_CMD_DECSMBV;
- } else if (flags == TERM_SEQ_FLAG_DQUOTE) {
- /* DECSTRL */
- return TERM_CMD_DECSTRL;
- } else if (flags == TERM_SEQ_FLAG_WHAT) {
- /* DECRQUPSS */
- return TERM_CMD_DECRQUPSS;
- } else if (seq->args[0] == 1 && flags == TERM_SEQ_FLAG_CASH) {
- /* DECRQTSR */
- return TERM_CMD_DECRQTSR;
- } else if (flags == TERM_SEQ_FLAG_MULT) {
- /* DECSCP */
- return TERM_CMD_DECSCP;
- } else if (flags == TERM_SEQ_FLAG_COMMA) {
- /* DECRQKT */
- return TERM_CMD_DECRQKT;
- }
- break;
- case 'V':
- if (flags == 0) /* PP */
- return TERM_CMD_PP;
- break;
- case 'v':
- if (flags == TERM_SEQ_FLAG_SPACE) /* DECSLCK */
- return TERM_CMD_DECSLCK;
- else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECRQDE */
- return TERM_CMD_DECRQDE;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECCRA */
- return TERM_CMD_DECCRA;
- else if (flags == TERM_SEQ_FLAG_COMMA) /* DECRPKT */
- return TERM_CMD_DECRPKT;
- break;
- case 'W':
- if (seq->args[0] == 5 && flags == TERM_SEQ_FLAG_WHAT) {
- /* DECST8C */
- return TERM_CMD_DECST8C;
- }
- break;
- case 'w':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECRQPSR */
- return TERM_CMD_DECRQPSR;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECEFR */
- return TERM_CMD_DECEFR;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECSPP */
- return TERM_CMD_DECSPP;
- break;
- case 'X':
- if (flags == 0) /* ECH */
- return TERM_CMD_ECH;
- break;
- case 'x':
- if (flags == 0) /* DECREQTPARM */
- return TERM_CMD_DECREQTPARM;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECFRA */
- return TERM_CMD_DECFRA;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSACE */
- return TERM_CMD_DECSACE;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECRQPKFM */
- return TERM_CMD_DECRQPKFM;
- break;
- case 'y':
- if (flags == 0) /* DECTST */
- return TERM_CMD_DECTST;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECRQCRA */
- return TERM_CMD_DECRQCRA;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECPKFMR */
- return TERM_CMD_DECPKFMR;
- break;
- case 'Z':
- if (flags == 0) /* CBT */
- return TERM_CMD_CBT;
- break;
- case 'z':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECERA */
- return TERM_CMD_DECERA;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECELR */
- return TERM_CMD_DECELR;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECINVM */
- return TERM_CMD_DECINVM;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECPKA */
- return TERM_CMD_DECPKA;
- break;
- case '@':
- if (flags == 0) /* ICH */
- return TERM_CMD_ICH;
- break;
- case '`':
- if (flags == 0) /* HPA */
- return TERM_CMD_HPA;
- break;
- case '{':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECSERA */
- return TERM_CMD_DECSERA;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECSLE */
- return TERM_CMD_DECSLE;
- break;
- case '|':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECSCPP */
- return TERM_CMD_DECSCPP;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECRQLP */
- return TERM_CMD_DECRQLP;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSNLS */
- return TERM_CMD_DECSNLS;
- break;
- case '}':
- if (flags == TERM_SEQ_FLAG_SPACE) /* DECKBD */
- return TERM_CMD_DECKBD;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECSASD */
- return TERM_CMD_DECSASD;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECIC */
- return TERM_CMD_DECIC;
- break;
- case '~':
- if (flags == TERM_SEQ_FLAG_SPACE) /* DECTME */
- return TERM_CMD_DECTME;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECSSDT */
- return TERM_CMD_DECSSDT;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECDC */
- return TERM_CMD_DECDC;
- break;
- }
-
- return TERM_CMD_NONE;
-}
-
-/*
- * State Machine
- * This parser controls the parser-state and returns any detected sequence to
- * the caller. The parser is based on this state-diagram from Paul Williams:
- * http://vt100.net/emu/
- * It was written from scratch and extended where needed.
- * This parser is fully compatible up to the vt500 series. We expect UCS-4 as
- * input. It's the callers responsibility to do any UTF-8 parsing.
- */
-
-enum parser_state {
- STATE_NONE, /* placeholder */
- STATE_GROUND, /* initial state and ground */
- STATE_ESC, /* ESC sequence was started */
- STATE_ESC_INT, /* intermediate escape characters */
- STATE_CSI_ENTRY, /* starting CSI sequence */
- STATE_CSI_PARAM, /* CSI parameters */
- STATE_CSI_INT, /* intermediate CSI characters */
- STATE_CSI_IGNORE, /* CSI error; ignore this CSI sequence */
- STATE_DCS_ENTRY, /* starting DCS sequence */
- STATE_DCS_PARAM, /* DCS parameters */
- STATE_DCS_INT, /* intermediate DCS characters */
- STATE_DCS_PASS, /* DCS data passthrough */
- STATE_DCS_IGNORE, /* DCS error; ignore this DCS sequence */
- STATE_OSC_STRING, /* parsing OSC sequence */
- STATE_ST_IGNORE, /* unimplemented seq; ignore until ST */
- STATE_NUM
-};
-
-enum parser_action {
- ACTION_NONE, /* placeholder */
- ACTION_CLEAR, /* clear parameters */
- ACTION_IGNORE, /* ignore the character entirely */
- ACTION_PRINT, /* print the character on the console */
- ACTION_EXECUTE, /* execute single control character (C0/C1) */
- ACTION_COLLECT, /* collect intermediate character */
- ACTION_PARAM, /* collect parameter character */
- ACTION_ESC_DISPATCH, /* dispatch escape sequence */
- ACTION_CSI_DISPATCH, /* dispatch csi sequence */
- ACTION_DCS_START, /* start of DCS data */
- ACTION_DCS_COLLECT, /* collect DCS data */
- ACTION_DCS_CONSUME, /* consume DCS terminator */
- ACTION_DCS_DISPATCH, /* dispatch dcs sequence */
- ACTION_OSC_START, /* start of OSC data */
- ACTION_OSC_COLLECT, /* collect OSC data */
- ACTION_OSC_CONSUME, /* consume OSC terminator */
- ACTION_OSC_DISPATCH, /* dispatch osc sequence */
- ACTION_NUM
-};
-
-int term_parser_new(term_parser **out, bool host) {
- _term_parser_free_ term_parser *parser = NULL;
-
- assert_return(out, -EINVAL);
-
- parser = new0(term_parser, 1);
- if (!parser)
- return -ENOMEM;
-
- parser->is_host = host;
- parser->st_alloc = 64;
- parser->seq.st = new0(char, parser->st_alloc + 1);
- if (!parser->seq.st)
- return -ENOMEM;
-
- *out = parser;
- parser = NULL;
- return 0;
-}
-
-term_parser *term_parser_free(term_parser *parser) {
- if (!parser)
- return NULL;
-
- free(parser->seq.st);
- free(parser);
- return NULL;
-}
-
-static inline void parser_clear(term_parser *parser) {
- unsigned int i;
-
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = 0;
- parser->seq.intermediates = 0;
- parser->seq.charset = TERM_CHARSET_NONE;
- parser->seq.n_args = 0;
- for (i = 0; i < TERM_PARSER_ARG_MAX; ++i)
- parser->seq.args[i] = -1;
-
- parser->seq.n_st = 0;
- parser->seq.st[0] = 0;
-}
-
-static int parser_ignore(term_parser *parser, uint32_t raw) {
- parser_clear(parser);
- parser->seq.type = TERM_SEQ_IGNORE;
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
-
- return parser->seq.type;
-}
-
-static int parser_print(term_parser *parser, uint32_t raw) {
- parser_clear(parser);
- parser->seq.type = TERM_SEQ_GRAPHIC;
- parser->seq.command = TERM_CMD_GRAPHIC;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
-
- return parser->seq.type;
-}
-
-static int parser_execute(term_parser *parser, uint32_t raw) {
- parser_clear(parser);
- parser->seq.type = TERM_SEQ_CONTROL;
- parser->seq.command = TERM_CMD_GRAPHIC;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
- if (!parser->is_host)
- parser->seq.command = term_parse_host_control(&parser->seq);
-
- return parser->seq.type;
-}
-
-static void parser_collect(term_parser *parser, uint32_t raw) {
- /*
- * Usually, characters from 0x30 to 0x3f are only allowed as leading
- * markers (or as part of the parameters), characters from 0x20 to 0x2f
- * are only allowed as trailing markers. However, our state-machine
- * already verifies those restrictions so we can handle them the same
- * way here. Note that we safely allow markers to be specified multiple
- * times.
- */
-
- if (raw >= 0x20 && raw <= 0x3f)
- parser->seq.intermediates |= 1 << (raw - 0x20);
-}
-
-static void parser_param(term_parser *parser, uint32_t raw) {
- int new;
-
- if (raw == ';') {
- if (parser->seq.n_args < TERM_PARSER_ARG_MAX)
- ++parser->seq.n_args;
-
- return;
- }
-
- if (parser->seq.n_args >= TERM_PARSER_ARG_MAX)
- return;
-
- if (raw >= '0' && raw <= '9') {
- new = parser->seq.args[parser->seq.n_args];
- if (new < 0)
- new = 0;
- new = new * 10 + raw - '0';
-
- /* VT510 tells us to clamp all values to [0, 9999], however, it
- * also allows commands with values up to 2^15-1. We simply use
- * 2^16 as maximum here to be compatible to all commands, but
- * avoid overflows in any calculations. */
- if (new > 0xffff)
- new = 0xffff;
-
- parser->seq.args[parser->seq.n_args] = new;
- }
-}
-
-static int parser_esc(term_parser *parser, uint32_t raw) {
- parser->seq.type = TERM_SEQ_ESCAPE;
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
- if (!parser->is_host)
- parser->seq.command = term_parse_host_escape(&parser->seq, &parser->seq.charset);
-
- return parser->seq.type;
-}
-
-static int parser_csi(term_parser *parser, uint32_t raw) {
- /* parser->seq is cleared during CSI-ENTER state, thus there's no need
- * to clear invalid fields here. */
-
- if (parser->seq.n_args < TERM_PARSER_ARG_MAX) {
- if (parser->seq.n_args > 0 ||
- parser->seq.args[parser->seq.n_args] >= 0)
- ++parser->seq.n_args;
- }
-
- parser->seq.type = TERM_SEQ_CSI;
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
- if (!parser->is_host)
- parser->seq.command = term_parse_host_csi(&parser->seq);
-
- return parser->seq.type;
-}
-
-/* perform state transition and dispatch related actions */
-static int parser_transition(term_parser *parser, uint32_t raw, unsigned int state, unsigned int action) {
- if (state != STATE_NONE)
- parser->state = state;
-
- switch (action) {
- case ACTION_NONE:
- return TERM_SEQ_NONE;
- case ACTION_CLEAR:
- parser_clear(parser);
- return TERM_SEQ_NONE;
- case ACTION_IGNORE:
- return parser_ignore(parser, raw);
- case ACTION_PRINT:
- return parser_print(parser, raw);
- case ACTION_EXECUTE:
- return parser_execute(parser, raw);
- case ACTION_COLLECT:
- parser_collect(parser, raw);
- return TERM_SEQ_NONE;
- case ACTION_PARAM:
- parser_param(parser, raw);
- return TERM_SEQ_NONE;
- case ACTION_ESC_DISPATCH:
- return parser_esc(parser, raw);
- case ACTION_CSI_DISPATCH:
- return parser_csi(parser, raw);
- case ACTION_DCS_START:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_DCS_COLLECT:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_DCS_CONSUME:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_DCS_DISPATCH:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_START:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_COLLECT:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_CONSUME:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_DISPATCH:
- /* not implemented */
- return TERM_SEQ_NONE;
- default:
- assert_not_reached("invalid vte-parser action");
- return TERM_SEQ_NONE;
- }
-}
-
-static int parser_feed_to_state(term_parser *parser, uint32_t raw) {
- switch (parser->state) {
- case STATE_NONE:
- /*
- * During initialization, parser->state is cleared. Treat this
- * as STATE_GROUND. We will then never get to STATE_NONE again.
- */
- case STATE_GROUND:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- case 0x80 ... 0x9b: /* C1 \ { ST } */
- case 0x9d ... 0x9f:
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_PRINT);
- case STATE_ESC:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_ESC_INT, ACTION_COLLECT);
- case 0x30 ... 0x4f: /* ['0' - '~'] \ { 'P', 'X', '[', ']', '^', '_' } */
- case 0x51 ... 0x57:
- case 0x59 ... 0x5a:
- case 0x5c:
- case 0x60 ... 0x7e:
- return parser_transition(parser, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
- case 0x50: /* 'P' */
- return parser_transition(parser, raw, STATE_DCS_ENTRY, ACTION_CLEAR);
- case 0x5b: /* '[' */
- return parser_transition(parser, raw, STATE_CSI_ENTRY, ACTION_CLEAR);
- case 0x5d: /* ']' */
- return parser_transition(parser, raw, STATE_OSC_STRING, ACTION_CLEAR);
- case 0x58: /* 'X' */
- case 0x5e: /* '^' */
- case 0x5f: /* '_' */
- return parser_transition(parser, raw, STATE_ST_IGNORE, ACTION_NONE);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_ESC_INT, ACTION_COLLECT);
- case STATE_ESC_INT:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case 0x30 ... 0x7e: /* ['0' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case STATE_CSI_ENTRY:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_CSI_INT, ACTION_COLLECT);
- case 0x3a: /* ':' */
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_CSI_PARAM, ACTION_PARAM);
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_CSI_PARAM, ACTION_COLLECT);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case STATE_CSI_PARAM:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_CSI_INT, ACTION_COLLECT);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_NONE, ACTION_PARAM);
- case 0x3a: /* ':' */
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case STATE_CSI_INT:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case 0x30 ... 0x3f: /* ['0' - '?'] */
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case STATE_CSI_IGNORE:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x3f: /* [' ' - '?'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_NONE);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- case STATE_DCS_ENTRY:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_DCS_INT, ACTION_COLLECT);
- case 0x3a: /* ':' */
- return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_DCS_PARAM, ACTION_PARAM);
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_DCS_PARAM, ACTION_COLLECT);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case STATE_DCS_PARAM:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_DCS_INT, ACTION_COLLECT);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_NONE, ACTION_PARAM);
- case 0x3a: /* ':' */
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case STATE_DCS_INT:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case 0x30 ... 0x3f: /* ['0' - '?'] */
- return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case STATE_DCS_PASS:
- switch (raw) {
- case 0x00 ... 0x7e: /* ASCII \ { DEL } */
- return parser_transition(parser, raw, STATE_NONE, ACTION_DCS_COLLECT);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_DCS_DISPATCH);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_DCS_COLLECT);
- case STATE_DCS_IGNORE:
- switch (raw) {
- case 0x00 ... 0x7f: /* ASCII */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_NONE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- case STATE_OSC_STRING:
- switch (raw) {
- case 0x00 ... 0x06: /* C0 \ { BEL } */
- case 0x08 ... 0x1f:
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x7f: /* [' ' - DEL] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_OSC_COLLECT);
- case 0x07: /* BEL */
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_OSC_DISPATCH);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_OSC_COLLECT);
- case STATE_ST_IGNORE:
- switch (raw) {
- case 0x00 ... 0x7f: /* ASCII */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- }
-
- assert_not_reached("bad vte-parser state");
- return -EINVAL;
-}
-
-int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw) {
- int r;
-
- assert_return(parser, -EINVAL);
- assert_return(seq_out, -EINVAL);
-
- /*
- * Notes:
- * * DEC treats GR codes as GL. We don't do that as we require UTF-8
- * as charset and, thus, it doesn't make sense to treat GR special.
- * * During control sequences, unexpected C1 codes cancel the sequence
- * and immediately start a new one. C0 codes, however, may or may not
- * be ignored/executed depending on the sequence.
- */
-
- switch (raw) {
- case 0x18: /* CAN */
- r = parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- break;
- case 0x1a: /* SUB */
- r = parser_transition(parser, raw, STATE_GROUND, ACTION_EXECUTE);
- break;
- case 0x80 ... 0x8f: /* C1 \ {DCS, SOS, CSI, ST, OSC, PM, APC} */
- case 0x91 ... 0x97:
- case 0x99 ... 0x9a:
- r = parser_transition(parser, raw, STATE_GROUND, ACTION_EXECUTE);
- break;
- case 0x1b: /* ESC */
- r = parser_transition(parser, raw, STATE_ESC, ACTION_CLEAR);
- break;
- case 0x98: /* SOS */
- case 0x9e: /* PM */
- case 0x9f: /* APC */
- r = parser_transition(parser, raw, STATE_ST_IGNORE, ACTION_NONE);
- break;
- case 0x90: /* DCS */
- r = parser_transition(parser, raw, STATE_DCS_ENTRY, ACTION_CLEAR);
- break;
- case 0x9d: /* OSC */
- r = parser_transition(parser, raw, STATE_OSC_STRING, ACTION_CLEAR);
- break;
- case 0x9b: /* CSI */
- r = parser_transition(parser, raw, STATE_CSI_ENTRY, ACTION_CLEAR);
- break;
- default:
- r = parser_feed_to_state(parser, raw);
- break;
- }
-
- if (r <= 0)
- *seq_out = NULL;
- else
- *seq_out = &parser->seq;
-
- return r;
-}
diff --git a/src/libsystemd-terminal/term-screen.c b/src/libsystemd-terminal/term-screen.c
deleted file mode 100644
index 0e38ff41c6..0000000000
--- a/src/libsystemd-terminal/term-screen.c
+++ /dev/null
@@ -1,4333 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Terminal Screens
- * The term_screen layer implements the terminal-side. It handles all commands
- * returned by the seq-parser and applies them to its own pages.
- *
- * While there are a lot of legacy control-sequences, we only support a small
- * subset. There is no reason to implement unused codes like horizontal
- * scrolling.
- * If you implement new commands, make sure to document them properly.
- *
- * Standards:
- * ECMA-48
- * ANSI X3.64
- * ISO/IEC 6429
- * References:
- * http://www.vt100.net/emu/ctrlseq_dec.html
- * http://www.vt100.net/docs/vt100-ug/chapter3.html
- * http://www.vt100.net/docs/vt510-rm/chapter4
- * http://www.vt100.net/docs/vt510-rm/contents
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- * ASCII
- * http://en.wikipedia.org/wiki/C0_and_C1_control_codes
- * https://en.wikipedia.org/wiki/ANSI_color
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <xkbcommon/xkbcommon-keysyms.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-#include "utf8.h"
-
-int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data) {
- _cleanup_(term_screen_unrefp) term_screen *screen = NULL;
- int r;
-
- assert_return(out, -EINVAL);
-
- screen = new0(term_screen, 1);
- if (!screen)
- return -ENOMEM;
-
- screen->ref = 1;
- screen->age = 1;
- screen->write_fn = write_fn;
- screen->write_fn_data = write_fn_data;
- screen->cmd_fn = cmd_fn;
- screen->cmd_fn_data = cmd_fn_data;
- screen->flags = TERM_FLAG_7BIT_MODE;
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
- screen->g0 = &term_unicode_lower;
- screen->g1 = &term_unicode_upper;
- screen->g2 = &term_unicode_lower;
- screen->g3 = &term_unicode_upper;
- screen->state.gl = &screen->g0;
- screen->state.gr = &screen->g1;
- screen->saved = screen->state;
- screen->saved_alt = screen->saved;
-
- r = term_page_new(&screen->page_main);
- if (r < 0)
- return r;
-
- r = term_page_new(&screen->page_alt);
- if (r < 0)
- return r;
-
- r = term_parser_new(&screen->parser, false);
- if (r < 0)
- return r;
-
- r = term_history_new(&screen->history_main);
- if (r < 0)
- return r;
-
- screen->page = screen->page_main;
- screen->history = screen->history_main;
-
- *out = screen;
- screen = NULL;
- return 0;
-}
-
-term_screen *term_screen_ref(term_screen *screen) {
- if (!screen)
- return NULL;
-
- assert_return(screen->ref > 0, NULL);
-
- ++screen->ref;
- return screen;
-}
-
-term_screen *term_screen_unref(term_screen *screen) {
- if (!screen)
- return NULL;
-
- assert_return(screen->ref > 0, NULL);
-
- if (--screen->ref)
- return NULL;
-
- free(screen->answerback);
- free(screen->tabs);
- term_history_free(screen->history_main);
- term_page_free(screen->page_alt);
- term_page_free(screen->page_main);
- term_parser_free(screen->parser);
- free(screen);
-
- return NULL;
-}
-
-/*
- * Write-Helpers
- * Unfortunately, 7bit/8bit compat mode requires us to send C1 controls encoded
- * as 7bit if asked by the application. This is really used in the wild, so we
- * cannot fall back to "always 7bit".
- * screen_write() is the underlying backend which forwards any writes to the
- * users's callback. It's the users responsibility to buffer these and write
- * them out once their call to term_screen_feed_*() returns.
- * The SEQ_WRITE() and SEQ_WRITE_KEY() macros allow constructing C0/C1 sequences
- * directly in the code-base without requiring any intermediate buffer during
- * runtime.
- */
-
-#define C0_CSI "\e["
-#define C1_CSI "\x9b"
-
-#define SEQ(_screen, _prefix_esc, _c0, _c1, _seq) \
- (((_screen)->flags & TERM_FLAG_7BIT_MODE) ? \
- ((_prefix_esc) ? ("\e" _c0 _seq) : (_c0 _seq)) : \
- ((_prefix_esc) ? ("\e" _c1 _seq) : (_c1 _seq)))
-
-#define SEQ_SIZE(_screen, _prefix_esc, _c0, _c1, _seq) \
- (((_screen)->flags & TERM_FLAG_7BIT_MODE) ? \
- ((_prefix_esc) ? sizeof("\e" _c0 _seq) : sizeof(_c0 _seq)) : \
- ((_prefix_esc) ? sizeof("\e" _c1 _seq) : sizeof(_c1 _seq)))
-
-#define SEQ_WRITE_KEY(_screen, _prefix_esc, _c0, _c1, _seq) \
- screen_write((_screen), \
- SEQ((_screen), (_prefix_esc), \
- _c0, _c1, _seq), \
- SEQ_SIZE((_screen), (_prefix_esc), \
- _c0, _c1, _seq) - 1)
-
-#define SEQ_WRITE(_screen, _c0, _c1, _seq) \
- SEQ_WRITE_KEY((_screen), false, _c0, _c1, _seq)
-
-static int screen_write(term_screen *screen, const void *buf, size_t len) {
- if (len < 1 || !screen->write_fn)
- return 0;
-
- return screen->write_fn(screen, screen->write_fn_data, buf, len);
-}
-
-/*
- * Command Forwarding
- * Some commands cannot be handled by the screen-layer directly. Those are
- * forwarded to the command-handler of the caller. This is rarely used and can
- * safely be set to NULL.
- */
-
-static int screen_forward(term_screen *screen, unsigned int cmd, const term_seq *seq) {
- if (!screen->cmd_fn)
- return 0;
-
- return screen->cmd_fn(screen, screen->cmd_fn_data, cmd, seq);
-}
-
-/*
- * Screen Helpers
- * These helpers implement common-operations like cursor-handler and more, which
- * are used by several command dispatchers.
- */
-
-static unsigned int screen_clamp_x(term_screen *screen, unsigned int x) {
- if (x >= screen->page->width)
- return (screen->page->width > 0) ? screen->page->width - 1 : 0;
-
- return x;
-}
-
-static unsigned int screen_clamp_y(term_screen *screen, unsigned int y) {
- if (y >= screen->page->height)
- return (screen->page->height > 0) ? screen->page->height - 1 : 0;
-
- return y;
-}
-
-static bool screen_tab_is_set(term_screen *screen, unsigned int pos) {
- if (pos >= screen->page->width)
- return false;
-
- return screen->tabs[pos / 8] & (1 << (pos % 8));
-}
-
-static inline void screen_age_cursor(term_screen *screen) {
- term_cell *cell;
-
- cell = term_page_get_cell(screen->page, screen->state.cursor_x, screen->state.cursor_y);
- if (cell)
- cell->age = screen->age;
-}
-
-static void screen_cursor_clear_wrap(term_screen *screen) {
- screen->flags &= ~TERM_FLAG_PENDING_WRAP;
-}
-
-static void screen_cursor_set(term_screen *screen, unsigned int x, unsigned int y) {
- x = screen_clamp_x(screen, x);
- y = screen_clamp_y(screen, y);
-
- if (x == screen->state.cursor_x && y == screen->state.cursor_y)
- return;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-
- screen->state.cursor_x = x;
- screen->state.cursor_y = y;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-}
-
-static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned int y) {
- if (screen->state.origin_mode) {
- x = screen_clamp_x(screen, x);
- y = screen_clamp_x(screen, y) + screen->page->scroll_idx;
-
- if (y >= screen->page->scroll_idx + screen->page->scroll_num) {
- y = screen->page->scroll_idx + screen->page->scroll_num;
- if (screen->page->scroll_num > 0)
- y -= 1;
- }
- }
-
- screen_cursor_set(screen, x, y);
-}
-
-static void screen_cursor_left(term_screen *screen, unsigned int num) {
- if (num > screen->state.cursor_x)
- num = screen->state.cursor_x;
-
- screen_cursor_set(screen, screen->state.cursor_x - num, screen->state.cursor_y);
-}
-
-static void screen_cursor_left_tab(term_screen *screen, unsigned int num) {
- unsigned int i;
-
- i = screen->state.cursor_x;
- while (i > 0 && num > 0) {
- if (screen_tab_is_set(screen, --i))
- --num;
- }
-
- screen_cursor_set(screen, i, screen->state.cursor_y);
-}
-
-static void screen_cursor_right(term_screen *screen, unsigned int num) {
- if (num > screen->page->width)
- num = screen->page->width;
-
- screen_cursor_set(screen, screen->state.cursor_x + num, screen->state.cursor_y);
-}
-
-static void screen_cursor_right_tab(term_screen *screen, unsigned int num) {
- unsigned int i;
-
- i = screen->state.cursor_x;
- while (i + 1 < screen->page->width && num > 0) {
- if (screen_tab_is_set(screen, ++i))
- --num;
- }
-
- screen_cursor_set(screen, i, screen->state.cursor_y);
-}
-
-static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) {
- unsigned int max;
-
- if (screen->state.cursor_y < screen->page->scroll_idx) {
- if (num > screen->state.cursor_y)
- num = screen->state.cursor_y;
-
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
- } else {
- max = screen->state.cursor_y - screen->page->scroll_idx;
- if (num > max) {
- if (num < 1)
- return;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-
- if (scroll)
- term_page_scroll_down(screen->page, num - max, &screen->state.attr, screen->age, NULL);
-
- screen->state.cursor_y = screen->page->scroll_idx;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
- } else {
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
- }
- }
-}
-
-static void screen_cursor_down(term_screen *screen, unsigned int num, bool scroll) {
- unsigned int max;
-
- if (screen->state.cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) {
- if (num > screen->page->height)
- num = screen->page->height;
-
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
- } else {
- max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->state.cursor_y;
- if (num > max) {
- if (num < 1)
- return;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-
- if (scroll)
- term_page_scroll_up(screen->page, num - max, &screen->state.attr, screen->age, screen->history);
-
- screen->state.cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
- } else {
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y + num);
- }
- }
-}
-
-static void screen_save_state(term_screen *screen, term_state *where) {
- *where = screen->state;
-}
-
-static void screen_restore_state(term_screen *screen, term_state *from) {
- screen_cursor_set(screen, from->cursor_x, from->cursor_y);
- screen->state = *from;
-}
-
-static void screen_reset_page(term_screen *screen, term_page *page) {
- term_page_set_scroll_region(page, 0, page->height);
- term_page_erase(page, 0, 0, page->width, page->height, &screen->state.attr, screen->age, false);
-}
-
-static void screen_change_alt(term_screen *screen, bool set) {
- if (set) {
- screen->page = screen->page_alt;
- screen->history = NULL;
- } else {
- screen->page = screen->page_main;
- screen->history = screen->history_main;
- }
-
- screen->page->age = screen->age;
-}
-
-static inline void set_reset(term_screen *screen, unsigned int flag, bool set) {
- if (set)
- screen->flags |= flag;
- else
- screen->flags &= ~flag;
-}
-
-static void screen_mode_change_ansi(term_screen *screen, unsigned mode, bool set) {
- switch (mode) {
- case 20:
- /*
- * LNM: line-feed/new-line mode
- * TODO
- */
- set_reset(screen, TERM_FLAG_NEWLINE_MODE, set);
-
- break;
- default:
- log_debug("terminal: failed to %s unknown ANSI mode %u", set ? "set" : "unset", mode);
- }
-}
-
-static void screen_mode_change_dec(term_screen *screen, unsigned int mode, bool set) {
- switch (mode) {
- case 1:
- /*
- * DECCKM: cursor-keys
- * TODO
- */
- set_reset(screen, TERM_FLAG_CURSOR_KEYS, set);
-
- break;
- case 6:
- /*
- * DECOM: origin-mode
- * TODO
- */
- screen->state.origin_mode = set;
-
- break;
- case 7:
- /*
- * DECAWN: auto-wrap mode
- * TODO
- */
- screen->state.auto_wrap = set;
-
- break;
- case 25:
- /*
- * DECTCEM: text-cursor-enable
- * TODO
- */
- set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set);
- screen_age_cursor(screen);
-
- break;
- case 47:
- /*
- * XTERM-ASB: alternate-screen-buffer
- * This enables/disables the alternate screen-buffer.
- * It effectively saves the current page content and
- * allows you to restore it when changing to the
- * original screen-buffer again.
- */
- screen_change_alt(screen, set);
-
- break;
- case 1047:
- /*
- * XTERM-ASBPE: alternate-screen-buffer-post-erase
- * This is the same as XTERM-ASB but erases the
- * alternate screen-buffer before switching back to the
- * original buffer. Use it to discard any data on the
- * alternate screen buffer when done.
- */
- if (!set)
- screen_reset_page(screen, screen->page_alt);
-
- screen_change_alt(screen, set);
-
- break;
- case 1048:
- /*
- * XTERM-ASBCS: alternate-screen-buffer-cursor-state
- * This has the same effect as DECSC/DECRC, but uses a
- * separate state buffer. It is usually used in
- * combination with alternate screen buffers to provide
- * stacked state storage.
- */
- if (set)
- screen_save_state(screen, &screen->saved_alt);
- else
- screen_restore_state(screen, &screen->saved_alt);
-
- break;
- case 1049:
- /*
- * XTERM-ASBX: alternate-screen-buffer-extended
- * This combines XTERM-ASBPE and XTERM-ASBCS somewhat.
- * When enabling, state is saved, alternate screen
- * buffer is activated and cleared.
- * When disabled, alternate screen buffer is cleared,
- * then normal screen buffer is enabled and state is
- * restored.
- */
- if (set)
- screen_save_state(screen, &screen->saved_alt);
-
- screen_reset_page(screen, screen->page_alt);
- screen_change_alt(screen, set);
-
- if (!set)
- screen_restore_state(screen, &screen->saved_alt);
-
- break;
- default:
- log_debug("terminal: failed to %s unknown DEC mode %u", set ? "set" : "unset", mode);
- }
-}
-
-/* map a character according to current GL and GR maps */
-static uint32_t screen_map(term_screen *screen, uint32_t val) {
- uint32_t nval = -1U;
-
- /* 32 and 127 always map to identity. 160 and 255 map to identity iff a
- * 96 character set is loaded into GR. Values above 255 always map to
- * identity. */
- switch (val) {
- case 33 ... 126:
- if (screen->state.glt) {
- nval = (**screen->state.glt)[val - 32];
- screen->state.glt = NULL;
- } else {
- nval = (**screen->state.gl)[val - 32];
- }
- break;
- case 160 ... 255:
- if (screen->state.grt) {
- nval = (**screen->state.grt)[val - 160];
- screen->state.grt = NULL;
- } else {
- nval = (**screen->state.gr)[val - 160];
- }
- break;
- }
-
- return (nval == -1U) ? val : nval;
-}
-
-/*
- * Command Handlers
- * This is the unofficial documentation of all the TERM_CMD_* definitions. Each
- * handled command has a separate function with an extensive comment on the
- * semantics of the command.
- * Note that many semantics are unknown and need to be verified. This is mostly
- * about error-handling, though. Applications rarely rely on those features.
- */
-
-static int screen_DA1(term_screen *screen, const term_seq *seq);
-static int screen_LF(term_screen *screen, const term_seq *seq);
-
-static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) {
- term_char_t ch = TERM_CHAR_NULL;
-
- if (screen->state.cursor_x + 1 == screen->page->width
- && screen->flags & TERM_FLAG_PENDING_WRAP
- && screen->state.auto_wrap) {
- screen_cursor_down(screen, 1, true);
- screen_cursor_set(screen, 0, screen->state.cursor_y);
- }
-
- screen_cursor_clear_wrap(screen);
-
- ch = term_char_merge(ch, screen_map(screen, seq->terminator));
- term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false);
-
- if (screen->state.cursor_x + 1 == screen->page->width)
- screen->flags |= TERM_FLAG_PENDING_WRAP;
- else
- screen_cursor_right(screen, 1);
-
- return 0;
-}
-
-static int screen_BEL(term_screen *screen, const term_seq *seq) {
- /*
- * BEL - sound bell tone
- * This command should trigger an acoustic bell. Usually, this is
- * forwarded directly to the pcspkr. However, bells have become quite
- * uncommon and annoying, so we're not implementing them here. Instead,
- * it's one of the commands we forward to the caller.
- */
-
- return screen_forward(screen, TERM_CMD_BEL, seq);
-}
-
-static int screen_BS(term_screen *screen, const term_seq *seq) {
- /*
- * BS - backspace
- * Move cursor one cell to the left. If already at the left margin,
- * nothing happens.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_left(screen, 1);
- return 0;
-}
-
-static int screen_CBT(term_screen *screen, const term_seq *seq) {
- /*
- * CBT - cursor-backward-tabulation
- * Move the cursor @args[0] tabs backwards (to the left). The
- * current cursor cell, in case it's a tab, is not counted.
- * Furthermore, the cursor cannot be moved beyond position 0 and
- * it will stop there.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_left_tab(screen, num);
-
- return 0;
-}
-
-static int screen_CHA(term_screen *screen, const term_seq *seq) {
- /*
- * CHA - cursor-horizontal-absolute
- * Move the cursor to position @args[0] in the current line. The
- * cursor cannot be moved beyond the rightmost cell and will stop
- * there.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int pos = 1;
-
- if (seq->args[0] > 0)
- pos = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, pos - 1, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_CHT(term_screen *screen, const term_seq *seq) {
- /*
- * CHT - cursor-horizontal-forward-tabulation
- * Move the cursor @args[0] tabs forward (to the right). The
- * current cursor cell, in case it's a tab, is not counted.
- * Furthermore, the cursor cannot be moved beyond the rightmost cell
- * and will stop there.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right_tab(screen, num);
-
- return 0;
-}
-
-static int screen_CNL(term_screen *screen, const term_seq *seq) {
- /*
- * CNL - cursor-next-line
- * Move the cursor @args[0] lines down.
- *
- * TODO: Does this stop at the bottom or cause a scroll-up?
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, num, false);
-
- return 0;
-}
-
-static int screen_CPL(term_screen *screen, const term_seq *seq) {
- /*
- * CPL - cursor-preceding-line
- * Move the cursor @args[0] lines up.
- *
- * TODO: Does this stop at the top or cause a scroll-up?
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_up(screen, num, false);
-
- return 0;
-}
-
-static int screen_CR(term_screen *screen, const term_seq *seq) {
- /*
- * CR - carriage-return
- * Move the cursor to the left margin on the current line.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, 0, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_CUB(term_screen *screen, const term_seq *seq) {
- /*
- * CUB - cursor-backward
- * Move the cursor @args[0] positions to the left. The cursor stops
- * at the left-most position.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_left(screen, num);
-
- return 0;
-}
-
-static int screen_CUD(term_screen *screen, const term_seq *seq) {
- /*
- * CUD - cursor-down
- * Move the cursor @args[0] positions down. The cursor stops at the
- * bottom margin. If it was already moved further, it stops at the
- * bottom line.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, num, false);
-
- return 0;
-}
-
-static int screen_CUF(term_screen *screen, const term_seq *seq) {
- /*
- * CUF -cursor-forward
- * Move the cursor @args[0] positions to the right. The cursor stops
- * at the right-most position.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right(screen, num);
-
- return 0;
-}
-
-static int screen_CUP(term_screen *screen, const term_seq *seq) {
- /*
- * CUP - cursor-position
- * Moves the cursor to position @args[1] x @args[0]. If either is 0, it
- * is treated as 1. The positions are subject to the origin-mode and
- * clamped to the addressable with/height.
- *
- * Defaults:
- * args[0]: 1
- * args[1]: 1
- */
-
- unsigned int x = 1, y = 1;
-
- if (seq->args[0] > 0)
- y = seq->args[0];
- if (seq->args[1] > 0)
- x = seq->args[1];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set_rel(screen, x - 1, y - 1);
-
- return 0;
-}
-
-static int screen_CUU(term_screen *screen, const term_seq *seq) {
- /*
- * CUU - cursor-up
- * Move the cursor @args[0] positions up. The cursor stops at the
- * top margin. If it was already moved further, it stops at the
- * top line.
- *
- * Defaults:
- * args[0]: 1
- *
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_up(screen, num, false);
-
- return 0;
-}
-
-static int screen_DA1(term_screen *screen, const term_seq *seq) {
- /*
- * DA1 - primary-device-attributes
- * The primary DA asks for basic terminal features. We simply return
- * a hard-coded list of features we implement.
- * Note that the primary DA asks for supported features, not currently
- * enabled features.
- *
- * The terminal's answer is:
- * ^[ ? 64 ; ARGS c
- * The first argument, 64, is fixed and denotes a VT420, the last
- * DEC-term that extended this number.
- * All following arguments denote supported features. Note
- * that at most 15 features can be sent (max CSI args). It is safe to
- * send more, but clients might not be able to parse them. This is a
- * client's problem and we shouldn't care. There is no other way to
- * send those feature lists, so we have to extend them beyond 15 in
- * those cases.
- *
- * Known modes:
- * 1: 132 column mode
- * The 132 column mode is supported by the terminal.
- * 2: printer port
- * A priner-port is supported and can be addressed via
- * control-codes.
- * 3: ReGIS graphics
- * Support for ReGIS graphics is available. The ReGIS routines
- * provide the "remote graphics instruction set" and allow basic
- * vector-rendering.
- * 4: sixel
- * Support of Sixel graphics is available. This provides access
- * to the sixel bitmap routines.
- * 6: selective erase
- * The terminal supports DECSCA and related selective-erase
- * functions. This allows to protect specific cells from being
- * erased, if specified.
- * 7: soft character set (DRCS)
- * TODO: ?
- * 8: user-defined keys (UDKs)
- * TODO: ?
- * 9: national-replacement character sets (NRCS)
- * National-replacement character-sets are available.
- * 12: Yugoslavian (SCS)
- * TODO: ?
- * 15: technical character set
- * The DEC technical-character-set is available.
- * 18: windowing capability
- * TODO: ?
- * 21: horizontal scrolling
- * TODO: ?
- * 22: ANSII color
- * TODO: ?
- * 23: Greek
- * TODO: ?
- * 24: Turkish
- * TODO: ?
- * 29: ANSI text locator
- * TODO: ?
- * 42: ISO Latin-2 character set
- * TODO: ?
- * 44: PCTerm
- * TODO: ?
- * 45: soft keymap
- * TODO: ?
- * 46: ASCII emulation
- * TODO: ?
- */
-
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, "?64;1;6;9;15c");
-}
-
-static int screen_DA2(term_screen *screen, const term_seq *seq) {
- /*
- * DA2 - secondary-device-attributes
- * The secondary DA asks for the terminal-ID, firmware versions and
- * other non-primary attributes. All these values are
- * informational-only and should not be used by the host to detect
- * terminal features.
- *
- * The terminal's response is:
- * ^[ > 61 ; FIRMWARE ; KEYBOARD c
- * whereas 65 is fixed for VT525 terminals, the last terminal-line that
- * increased this number. FIRMWARE is the firmware
- * version encoded as major/minor (20 == 2.0) and KEYBOARD is 0 for STD
- * keyboard and 1 for PC keyboards.
- *
- * We replace the firmware-version with the systemd-version so clients
- * can decode it again.
- */
-
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, ">65;" PACKAGE_VERSION ";1c");
-}
-
-static int screen_DA3(term_screen *screen, const term_seq *seq) {
- /*
- * DA3 - tertiary-device-attributes
- * The tertiary DA is used to query the terminal-ID.
- *
- * The terminal's response is:
- * ^P ! | XX AA BB CC ^\
- * whereas all four parameters are hexadecimal-encoded pairs. XX
- * denotes the manufacturing site, AA BB CC is the terminal's ID.
- */
-
- /* we do not support tertiary DAs */
- return 0;
-}
-
-static int screen_DC1(term_screen *screen, const term_seq *seq) {
- /*
- * DC1 - device-control-1 or XON
- * This clears any previous XOFF and resumes terminal-transmission.
- */
-
- /* we do not support XON */
- return 0;
-}
-
-static int screen_DC3(term_screen *screen, const term_seq *seq) {
- /*
- * DC3 - device-control-3 or XOFF
- * Stops terminal transmission. No further characters are sent until
- * an XON is received.
- */
-
- /* we do not support XOFF */
- return 0;
-}
-
-static int screen_DCH(term_screen *screen, const term_seq *seq) {
- /*
- * DCH - delete-character
- * This deletes @argv[0] characters at the current cursor position. As
- * characters are deleted, the remaining characters between the cursor
- * and right margin move to the left. Character attributes move with the
- * characters. The terminal adds blank spaces with no visual character
- * attributes at the right margin. DCH has no effect outside the
- * scrolling margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- term_page_delete_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_DECALN(term_screen *screen, const term_seq *seq) {
- /*
- * DECALN - screen-alignment-pattern
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECANM(term_screen *screen, const term_seq *seq) {
- /*
- * DECANM - ansi-mode
- * Set the terminal into VT52 compatibility mode. Control sequences
- * overlap with regular sequences so we have to detect them early before
- * dispatching them.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECBI(term_screen *screen, const term_seq *seq) {
- /*
- * DECBI - back-index
- * This control function moves the cursor backward one column. If the
- * cursor is at the left margin, then all screen data within the margin
- * moves one column to the right. The column that shifted past the right
- * margin is lost.
- * DECBI adds a new column at the left margin with no visual attributes.
- * DECBI does not affect the margins. If the cursor is beyond the
- * left-margin at the left border, then the terminal ignores DECBI.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECCARA(term_screen *screen, const term_seq *seq) {
- /*
- * DECCARA - change-attributes-in-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECCRA(term_screen *screen, const term_seq *seq) {
- /*
- * DECCRA - copy-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDC(term_screen *screen, const term_seq *seq) {
- /*
- * DECDC - delete-column
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDHL_BH(term_screen *screen, const term_seq *seq) {
- /*
- * DECDHL_BH - double-width-double-height-line: bottom half
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDHL_TH(term_screen *screen, const term_seq *seq) {
- /*
- * DECDHL_TH - double-width-double-height-line: top half
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDWL(term_screen *screen, const term_seq *seq) {
- /*
- * DECDWL - double-width-single-height-line
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECEFR(term_screen *screen, const term_seq *seq) {
- /*
- * DECEFR - enable-filter-rectangle
- * Defines the coordinates of a filter rectangle (top, left, bottom,
- * right as @args[0] to @args[3]) and activates it.
- * Anytime the locator is detected outside of the filter rectangle, an
- * outside rectangle event is generated and the rectangle is disabled.
- * Filter rectangles are always treated as "one-shot" events. Any
- * parameters that are omitted default to the current locator position.
- * If all parameters are omitted, any locator motion will be reported.
- * DECELR always cancels any prevous rectangle definition.
- *
- * The locator is usually associated with the mouse-cursor, but based
- * on cells instead of pixels. See DECELR how to initialize and enable
- * it. DECELR can also enable pixel-mode instead of cell-mode.
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECELF(term_screen *screen, const term_seq *seq) {
- /*
- * DECELF - enable-local-functions
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECELR(term_screen *screen, const term_seq *seq) {
- /*
- * DECELR - enable-locator-reporting
- * This changes the locator-reporting mode. @args[0] specifies the mode
- * to set, 0 disables locator-reporting, 1 enables it continuously, 2
- * enables it for a single report. @args[1] specifies the
- * precision-mode. 0 and 2 set the reporting to cell-precision, 1 sets
- * pixel-precision.
- *
- * Defaults:
- * args[0]: 0
- * args[1]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECERA(term_screen *screen, const term_seq *seq) {
- /*
- * DECERA - erase-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECFI(term_screen *screen, const term_seq *seq) {
- /*
- * DECFI - forward-index
- * This control function moves the cursor forward one column. If the
- * cursor is at the right margin, then all screen data within the
- * margins moves one column to the left. The column shifted past the
- * left margin is lost.
- * DECFI adds a new column at the right margin, with no visual
- * attributes. DECFI does not affect margins. If the cursor is beyond
- * the right margin at the border of the page when the terminal
- * receives DECFI, then the terminal ignores DECFI.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECFRA(term_screen *screen, const term_seq *seq) {
- /*
- * DECFRA - fill-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECIC(term_screen *screen, const term_seq *seq) {
- /*
- * DECIC - insert-column
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECID(term_screen *screen, const term_seq *seq) {
- /*
- * DECID - return-terminal-id
- * This is an obsolete form of TERM_CMD_DA1.
- */
-
- return screen_DA1(screen, seq);
-}
-
-static int screen_DECINVM(term_screen *screen, const term_seq *seq) {
- /*
- * DECINVM - invoke-macro
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECKBD(term_screen *screen, const term_seq *seq) {
- /*
- * DECKBD - keyboard-language-selection
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECKPAM(term_screen *screen, const term_seq *seq) {
- /*
- * DECKPAM - keypad-application-mode
- * Enables the keypad-application mode. If enabled, the keypad sends
- * special characters instead of the printed characters. This way,
- * applications can detect whether a numeric key was pressed on the
- * top-row or on the keypad.
- * Default is keypad-numeric-mode.
- */
-
- screen->flags |= TERM_FLAG_KEYPAD_MODE;
-
- return 0;
-}
-
-static int screen_DECKPNM(term_screen *screen, const term_seq *seq) {
- /*
- * DECKPNM - keypad-numeric-mode
- * This disables the keypad-application-mode (DECKPAM) and returns to
- * the keypad-numeric-mode. Keypresses on the keypad generate the same
- * sequences as corresponding keypresses on the main keyboard.
- * Default is keypad-numeric-mode.
- */
-
- screen->flags &= ~TERM_FLAG_KEYPAD_MODE;
-
- return 0;
-}
-
-static int screen_DECLFKC(term_screen *screen, const term_seq *seq) {
- /*
- * DECLFKC - local-function-key-control
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECLL(term_screen *screen, const term_seq *seq) {
- /*
- * DECLL - load-leds
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECLTOD(term_screen *screen, const term_seq *seq) {
- /*
- * DECLTOD - load-time-of-day
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECPCTERM(term_screen *screen, const term_seq *seq) {
- /*
- * DECPCTERM - pcterm-mode
- * This enters/exits the PCTerm mode. Default mode is VT-mode. It can
- * also select parameters for scancode/keycode mappings in SCO mode.
- *
- * Definitely not worth implementing. Lets kill PCTerm/SCO modes!
- */
-
- return 0;
-}
-
-static int screen_DECPKA(term_screen *screen, const term_seq *seq) {
- /*
- * DECPKA - program-key-action
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECPKFMR(term_screen *screen, const term_seq *seq) {
- /*
- * DECPKFMR - program-key-free-memory-report
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRARA(term_screen *screen, const term_seq *seq) {
- /*
- * DECRARA - reverse-attributes-in-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRC(term_screen *screen, const term_seq *seq) {
- /*
- * DECRC - restore-cursor
- * Restores the terminal to the state saved by the save cursor (DECSC)
- * function. This includes more than just the cursor-position.
- *
- * If nothing was saved by DECSC, then DECRC performs the following
- * actions:
- * * Moves the cursor to the home position (upper left of screen).
- * * Resets origin mode (DECOM).
- * * Turns all character attributes off (normal setting).
- * * Maps the ASCII character set into GL, and the DEC Supplemental
- * Graphic set into GR.
- *
- * The terminal maintains a separate DECSC buffer for the main display
- * and the status line. This feature lets you save a separate operating
- * state for the main display and the status line.
- */
-
- screen_restore_state(screen, &screen->saved);
-
- return 0;
-}
-
-static int screen_DECREQTPARM(term_screen *screen, const term_seq *seq) {
- /*
- * DECREQTPARM - request-terminal-parameters
- * The sequence DECREPTPARM is sent by the terminal controller to notify
- * the host of the status of selected terminal parameters. The status
- * sequence may be sent when requested by the host or at the terminal's
- * discretion. DECREPTPARM is sent upon receipt of a DECREQTPARM.
- *
- * If @args[0] is 0, this marks a request and the terminal is allowed
- * to send DECREPTPARM messages without request. If it is 1, the same
- * applies but the terminal should no longer send DECREPTPARM
- * unrequested.
- * 2 and 3 mark a report, but 3 is only used if the terminal answers as
- * an explicit request with @args[0] == 1.
- *
- * The other arguments are ignored in requests, but have the following
- * meaning in responses:
- * args[1]: 1=no-parity-set 4=parity-set-and-odd 5=parity-set-and-even
- * args[2]: 1=8bits-per-char 2=7bits-per-char
- * args[3]: transmission-speed
- * args[4]: receive-speed
- * args[5]: 1=bit-rate-multiplier-is-16
- * args[6]: This value communicates the four switch values in block 5
- * of SETUP B, which are only visible to the user when an STP
- * option is installed. These bits may be assigned for an STP
- * device. The four bits are a decimal-encoded binary number.
- * Value between 0-15.
- *
- * The transmission/receive speeds have mappings for number => bits/s
- * which are quite weird. Examples are: 96->3600, 112->9600, 120->19200
- *
- * Defaults:
- * args[0]: 0
- */
-
- if (seq->n_args < 1 || seq->args[0] == 0) {
- screen->flags &= ~TERM_FLAG_INHIBIT_TPARM;
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, "2;1;1;120;120;1;0x");
- } else if (seq->args[0] == 1) {
- screen->flags |= TERM_FLAG_INHIBIT_TPARM;
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, "3;1;1;120;120;1;0x");
- } else {
- return 0;
- }
-}
-
-static int screen_DECRPKT(term_screen *screen, const term_seq *seq) {
- /*
- * DECRPKT - report-key-type
- * Response to DECRQKT, we can safely ignore it as we're the one sending
- * it to the host.
- */
-
- return 0;
-}
-
-static int screen_DECRQCRA(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQCRA - request-checksum-of-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQDE(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQDE - request-display-extent
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQKT(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQKT - request-key-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQLP(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQLP - request-locator-position
- * See DECELR for locator-information.
- *
- * TODO: document and implement
- */
-
- return 0;
-}
-
-static int screen_DECRQM_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQM_ANSI - request-mode-ansi
- * The host sends this control function to find out if a particular mode
- * is set or reset. The terminal responds with a report mode function.
- * @args[0] contains the mode to query.
- *
- * Response is DECRPM with the first argument set to the mode that was
- * queried, second argument is 0 if mode is invalid, 1 if mode is set,
- * 2 if mode is not set (reset), 3 if mode is permanently set and 4 if
- * mode is permanently not set (reset):
- * ANSI: ^[ MODE ; VALUE $ y
- * DEC: ^[ ? MODE ; VALUE $ y
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECRQM_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQM_DEC - request-mode-dec
- * Same as DECRQM_ANSI but for DEC modes.
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECRQPKFM(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQPKFM - request-program-key-free-memory
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQPSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQPSR - request-presentation-state-report
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQTSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQTSR - request-terminal-state-report
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQUPSS(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQUPSS - request-user-preferred-supplemental-set
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSACE(term_screen *screen, const term_seq *seq) {
- /*
- * DECSACE - select-attribute-change-extent
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSASD(term_screen *screen, const term_seq *seq) {
- /*
- * DECSASD - select-active-status-display
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSC(term_screen *screen, const term_seq *seq) {
- /*
- * DECSC - save-cursor
- * Save cursor and terminal state so it can be restored later on.
- * Saves the following items in the terminal's memory:
- * * Cursor position
- * * Character attributes set by the SGR command
- * * Character sets (G0, G1, G2, or G3) currently in GL and GR
- * * Wrap flag (autowrap or no autowrap)
- * * State of origin mode (DECOM)
- * * Selective erase attribute
- * * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent
- */
-
- screen_save_state(screen, &screen->saved);
-
- return 0;
-}
-
-static int screen_DECSCA(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCA - select-character-protection-attribute
- * Defines the characters that come after it as erasable or not erasable
- * from the screen. The selective erase control functions (DECSED and
- * DECSEL) can only erase characters defined as erasable.
- *
- * @args[0] specifies the new mode. 0 and 2 mark any following character
- * as erasable, 1 marks it as not erasable.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- case 2:
- screen->state.attr.protect = 0;
- break;
- case 1:
- screen->state.attr.protect = 1;
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSCL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCL - select-conformance-level
- * Select the terminal's operating level. The factory default is
- * level 4 (VT Level 4 mode, 7-bit controls).
- * When you change the conformance level, the terminal performs a hard
- * reset (RIS).
- *
- * @args[0] defines the conformance-level, valid values are:
- * 61: Level 1 (VT100)
- * 62: Level 2 (VT200)
- * 63: Level 3 (VT300)
- * 64: Level 4 (VT400)
- * @args[1] defines the 8bit-mode, valid values are:
- * 0: 8-bit controls
- * 1: 7-bit controls
- * 2: 8-bit controls (same as 0)
- *
- * If @args[0] is 61, then @args[1] is ignored and 7bit controls are
- * enforced.
- *
- * Defaults:
- * args[0]: 64
- * args[1]: 0
- */
-
- unsigned int level = 64, bit = 0;
-
- if (seq->n_args > 0) {
- level = seq->args[0];
- if (seq->n_args > 1)
- bit = seq->args[1];
- }
-
- term_screen_hard_reset(screen);
-
- switch (level) {
- case 61:
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT100;
- screen->flags |= TERM_FLAG_7BIT_MODE;
- break;
- case 62 ... 69:
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
- if (bit == 1)
- screen->flags |= TERM_FLAG_7BIT_MODE;
- else
- screen->flags &= ~TERM_FLAG_7BIT_MODE;
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSCP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCP - select-communication-port
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSCPP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCPP - select-columns-per-page
- * Select columns per page. The number of rows is unaffected by this.
- * @args[0] selectes the number of columns (width), DEC only defines 80
- * and 132, but we allow any integer here. 0 is equivalent to 80.
- * Page content is *not* cleared and the cursor is left untouched.
- * However, if the page is reduced in width and the cursor would be
- * outside the visible region, it's set to the right border. Newly added
- * cells are cleared. No data is retained outside the visible region.
- *
- * Defaults:
- * args[0]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSCS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCS - select-communication-speed
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSCUSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCUSR - set-cursor-style
- * This changes the style of the cursor. @args[0] can be one of:
- * 0, 1: blinking block
- * 2: steady block
- * 3: blinking underline
- * 4: steady underline
- * Changing this setting does _not_ affect the cursor visibility itself.
- * Use DECTCEM for that.
- *
- * Defaults:
- * args[0]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSDDT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSDDT - select-disconnect-delay-time
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSDPT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSDPT - select-digital-printed-data-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSED(term_screen *screen, const term_seq *seq) {
- /*
- * DECSED - selective-erase-in-display
- * This control function erases some or all of the erasable characters
- * in the display. DECSED can only erase characters defined as erasable
- * by the DECSCA control function. DECSED works inside or outside the
- * scrolling margins.
- *
- * @args[0] defines which regions are erased. If it is 0, all cells from
- * the cursor (inclusive) till the end of the display are erase. If it
- * is 1, all cells from the start of the display till the cursor
- * (inclusive) are erased. If it is 2, all cells are erased.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, true);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, 0,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, 0,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, true);
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSEL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSEL - selective-erase-in-line
- * This control function erases some or all of the erasable characters
- * in a single line of text. DECSEL erases only those characters defined
- * as erasable by the DECSCA control function. DECSEL works inside or
- * outside the scrolling margins.
- *
- * @args[0] defines the region to be erased. If it is 0, all cells from
- * the cursor (inclusive) till the end of the line are erase. If it is
- * 1, all cells from the start of the line till the cursor (inclusive)
- * are erased. If it is 2, the whole line of the cursor is erased.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSERA(term_screen *screen, const term_seq *seq) {
- /*
- * DECSERA - selective-erase-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSFC(term_screen *screen, const term_seq *seq) {
- /*
- * DECSFC - select-flow-control
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSKCV(term_screen *screen, const term_seq *seq) {
- /*
- * DECSKCV - set-key-click-volume
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSLCK(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLCK - set-lock-key-style
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSLE(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLE - select-locator-events
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSLPP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLPP - set-lines-per-page
- * Set the number of lines used for the page. @args[0] specifies the
- * number of lines to be used. DEC only allows a limited number of
- * choices, however, we allow all integers. 0 is equivalent to 24.
- *
- * Defaults:
- * args[0]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSLRM_OR_SC(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLRM_OR_SC - set-left-and-right-margins or save-cursor
- *
- * TODO: Detect save-cursor and run it. DECSLRM is not worth
- * implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSMBV(term_screen *screen, const term_seq *seq) {
- /*
- * DECSMBV - set-margin-bell-volume
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSMKR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSMKR - select-modifier-key-reporting
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSNLS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSNLS - set-lines-per-screen
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSPP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSPP - set-port-parameter
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSPPCS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSPPCS - select-pro-printer-character-set
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSPRTT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSPRTT - select-printer-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSR - secure-reset
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSRFR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSRFR - select-refresh-rate
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSSCLS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSSCLS - set-scroll-speed
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSSDT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSSDT - select-status-display-line-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSSL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSSL - select-setup-language
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECST8C(term_screen *screen, const term_seq *seq) {
- /*
- * DECST8C - set-tab-at-every-8-columns
- * Clear the tab-ruler and reset it to a tab at every 8th column,
- * starting at 9 (though, setting a tab at 1 is fine as it has no
- * effect).
- */
-
- unsigned int i;
-
- for (i = 0; i < screen->page->width; i += 8)
- screen->tabs[i / 8] = 0x1;
-
- return 0;
-}
-
-static int screen_DECSTBM(term_screen *screen, const term_seq *seq) {
- /*
- * DECSTBM - set-top-and-bottom-margins
- * This control function sets the top and bottom margins for the current
- * page. You cannot perform scrolling outside the margins.
- *
- * @args[0] defines the top margin, @args[1] defines the bottom margin.
- * The bottom margin must be lower than the top-margin.
- *
- * This call resets the cursor position to 0/0 of the page.
- *
- * Defaults:
- * args[0]: 1
- * args[1]: last page-line
- */
-
- unsigned int top, bottom;
-
- top = 1;
- bottom = screen->page->height;
-
- if (seq->args[0] > 0)
- top = seq->args[0];
- if (seq->args[1] > 0)
- bottom = seq->args[1];
-
- if (top > screen->page->height)
- top = screen->page->height;
- if (bottom > screen->page->height)
- bottom = screen->page->height;
-
- if (top >= bottom || top > screen->page->height || bottom > screen->page->height) {
- top = 1;
- bottom = screen->page->height;
- }
-
- term_page_set_scroll_region(screen->page, top - 1, bottom - top + 1);
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, 0, 0);
-
- return 0;
-}
-
-static int screen_DECSTR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSTR - soft-terminal-reset
- * Perform a soft reset to the default values.
- */
-
- term_screen_soft_reset(screen);
-
- return 0;
-}
-
-static int screen_DECSTRL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSTRL - set-transmit-rate-limit
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSWBV(term_screen *screen, const term_seq *seq) {
- /*
- * DECSWBV - set-warning-bell-volume
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSWL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSWL - single-width-single-height-line
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECTID(term_screen *screen, const term_seq *seq) {
- /*
- * DECTID - select-terminal-id
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECTME(term_screen *screen, const term_seq *seq) {
- /*
- * DECTME - terminal-mode-emulation
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECTST(term_screen *screen, const term_seq *seq) {
- /*
- * DECTST - invoke-confidence-test
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DL(term_screen *screen, const term_seq *seq) {
- /*
- * DL - delete-line
- * This control function deletes one or more lines in the scrolling
- * region, starting with the line that has the cursor. @args[0] defines
- * the number of lines to delete. 0 is treated the same as 1.
- * As lines are deleted, lines below the cursor and in the scrolling
- * region move up. The terminal adds blank lines with no visual
- * character attributes at the bottom of the scrolling region. If it is
- * greater than the number of lines remaining on the page, DL deletes
- * only the remaining lines. DL has no effect outside the scrolling
- * margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_delete_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_DSR_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * DSR_ANSI - device-status-report-ansi
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DSR_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * DSR_DEC - device-status-report-dec
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_ECH(term_screen *screen, const term_seq *seq) {
- /*
- * ECH - erase-character
- * This control function erases one or more characters, from the cursor
- * position to the right. ECH clears character attributes from erased
- * character positions. ECH works inside or outside the scrolling
- * margins.
- * @args[0] defines the number of characters to erase. 0 is treated the
- * same as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->state.cursor_x + num, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
-
- return 0;
-}
-
-static int screen_ED(term_screen *screen, const term_seq *seq) {
- /*
- * ED - erase-in-display
- * This control function erases characters from part or all of the
- * display. When you erase complete lines, they become single-height,
- * single-width lines, with all visual character attributes cleared. ED
- * works inside or outside the scrolling margins.
- *
- * @args[0] defines the region to erase. 0 means from cursor (inclusive)
- * till the end of the screen. 1 means from the start of the screen till
- * the cursor (inclusive) and 2 means the whole screen.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, false);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, 0,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, 0,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, false);
- break;
- }
-
- return 0;
-}
-
-static int screen_EL(term_screen *screen, const term_seq *seq) {
- /*
- * EL - erase-in-line
- * This control function erases characters on the line that has the
- * cursor. EL clears all character attributes from erased character
- * positions. EL works inside or outside the scrolling margins.
- *
- * @args[0] defines the region to erase. 0 means from cursor (inclusive)
- * till the end of the line. 1 means from the start of the line till the
- * cursor (inclusive) and 2 means the whole line.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- }
-
- return 0;
-}
-
-static int screen_ENQ(term_screen *screen, const term_seq *seq) {
- /*
- * ENQ - enquiry
- * Transmit the answerback-string. If none is set, do nothing.
- */
-
- if (screen->answerback)
- return screen_write(screen, screen->answerback, strlen(screen->answerback));
-
- return 0;
-}
-
-static int screen_EPA(term_screen *screen, const term_seq *seq) {
- /*
- * EPA - end-of-guarded-area
- *
- * TODO: What is this?
- */
-
- return 0;
-}
-
-static int screen_FF(term_screen *screen, const term_seq *seq) {
- /*
- * FF - form-feed
- * This causes the cursor to jump to the next line. It is treated the
- * same as LF.
- */
-
- return screen_LF(screen, seq);
-}
-
-static int screen_HPA(term_screen *screen, const term_seq *seq) {
- /*
- * HPA - horizontal-position-absolute
- * HPA causes the active position to be moved to the n-th horizontal
- * position of the active line. If an attempt is made to move the active
- * position past the last position on the line, then the active position
- * stops at the last position on the line.
- *
- * @args[0] defines the horizontal position. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, num - 1, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_HPR(term_screen *screen, const term_seq *seq) {
- /*
- * HPR - horizontal-position-relative
- * HPR causes the active position to be moved to the n-th following
- * horizontal position of the active line. If an attempt is made to move
- * the active position past the last position on the line, then the
- * active position stops at the last position on the line.
- *
- * @args[0] defines the horizontal position. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right(screen, num);
-
- return 0;
-}
-
-static int screen_HT(term_screen *screen, const term_seq *seq) {
- /*
- * HT - horizontal-tab
- * Moves the cursor to the next tab stop. If there are no more tab
- * stops, the cursor moves to the right margin. HT does not cause text
- * to auto wrap.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right_tab(screen, 1);
-
- return 0;
-}
-
-static int screen_HTS(term_screen *screen, const term_seq *seq) {
- /*
- * HTS - horizontal-tab-set
- * HTS sets a horizontal tab stop at the column position indicated by
- * the value of the active column when the terminal receives an HTS.
- *
- * Executing an HTS does not effect the other horizontal tab stop
- * settings.
- */
-
- unsigned int pos;
-
- pos = screen->state.cursor_x;
- if (screen->page->width > 0)
- screen->tabs[pos / 8] |= 1U << (pos % 8);
-
- return 0;
-}
-
-static int screen_HVP(term_screen *screen, const term_seq *seq) {
- /*
- * HVP - horizontal-and-vertical-position
- * This control function works the same as the cursor position (CUP)
- * function. Origin mode (DECOM) selects line numbering and the ability
- * to move the cursor into margins.
- *
- * Defaults:
- * args[0]: 1
- * args[1]: 1
- */
-
- return screen_CUP(screen, seq);
-}
-
-static int screen_ICH(term_screen *screen, const term_seq *seq) {
- /*
- * ICH - insert-character
- * This control function inserts one or more space (SP) characters
- * starting at the cursor position. @args[0] is the number of characters
- * to insert. 0 is treated as 1.
- *
- * The ICH sequence inserts blank characters with the normal
- * character attribute. The cursor remains at the beginning of the blank
- * characters. Text between the cursor and right margin moves to the
- * right. Characters scrolled past the right margin are lost. ICH has no
- * effect outside the scrolling margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- term_page_insert_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_IL(term_screen *screen, const term_seq *seq) {
- /*
- * IL - insert-line
- * This control function inserts one or more blank lines, starting at
- * the cursor. @args[0] is the number of lines to insert. 0 is treated
- * as 1.
- *
- * As lines are inserted, lines below the cursor and in the scrolling
- * region move down. Lines scrolled off the page are lost. IL has no
- * effect outside the page margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- term_page_insert_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_IND(term_screen *screen, const term_seq *seq) {
- /*
- * IND - index
- * IND moves the cursor down one line in the same column. If the cursor
- * is at the bottom margin, then the screen performs a scroll-up.
- */
-
- screen_cursor_down(screen, 1, true);
-
- return 0;
-}
-
-static int screen_LF(term_screen *screen, const term_seq *seq) {
- /*
- * LF - line-feed
- * Causes a line feed or a new line operation, depending on the setting
- * of line feed/new line mode.
- */
-
- screen_cursor_down(screen, 1, true);
- if (screen->flags & TERM_FLAG_NEWLINE_MODE)
- screen_cursor_left(screen, screen->state.cursor_x);
-
- return 0;
-}
-
-static int screen_LS1R(term_screen *screen, const term_seq *seq) {
- /*
- * LS1R - locking-shift-1-right
- * Map G1 into GR.
- */
-
- screen->state.gr = &screen->g1;
-
- return 0;
-}
-
-static int screen_LS2(term_screen *screen, const term_seq *seq) {
- /*
- * LS2 - locking-shift-2
- * Map G2 into GL.
- */
-
- screen->state.gl = &screen->g2;
-
- return 0;
-}
-
-static int screen_LS2R(term_screen *screen, const term_seq *seq) {
- /*
- * LS2R - locking-shift-2-right
- * Map G2 into GR.
- */
-
- screen->state.gr = &screen->g2;
-
- return 0;
-}
-
-static int screen_LS3(term_screen *screen, const term_seq *seq) {
- /*
- * LS3 - locking-shift-3
- * Map G3 into GL.
- */
-
- screen->state.gl = &screen->g3;
-
- return 0;
-}
-
-static int screen_LS3R(term_screen *screen, const term_seq *seq) {
- /*
- * LS3R - locking-shift-3-right
- * Map G3 into GR.
- */
-
- screen->state.gr = &screen->g3;
-
- return 0;
-}
-
-static int screen_MC_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * MC_ANSI - media-copy-ansi
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_MC_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * MC_DEC - media-copy-dec
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_NEL(term_screen *screen, const term_seq *seq) {
- /*
- * NEL - next-line
- * Moves cursor to first position on next line. If cursor is at bottom
- * margin, then screen performs a scroll-up.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, 1, true);
- screen_cursor_set(screen, 0, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_NP(term_screen *screen, const term_seq *seq) {
- /*
- * NP - next-page
- * This control function moves the cursor forward to the home position
- * on one of the following pages in page memory. If there is only one
- * page, then the terminal ignores NP.
- * If NP tries to move the cursor past the last page in memory, then the
- * cursor stops at the last page.
- *
- * @args[0] defines the number of pages to forward. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_NULL(term_screen *screen, const term_seq *seq) {
- /*
- * NULL - null
- * The NULL operation does nothing. ASCII NULL is always ignored.
- */
-
- return 0;
-}
-
-static int screen_PP(term_screen *screen, const term_seq *seq) {
- /*
- * PP - preceding-page
- * This control function moves the cursor backward to the home position
- * on one of the preceding pages in page memory. If there is only one
- * page, then the terminal ignores PP.
- * If PP tries to move the cursor back farther than the first page in
- * memory, then the cursor stops at the first page.
- *
- * @args[0] defines the number of pages to go backwards. 0 is treated
- * as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_PPA(term_screen *screen, const term_seq *seq) {
- /*
- * PPA - page-position-absolute
- * This control function can move the cursor to the corresponding row
- * and column on any page in page memory. You select the page by its
- * number. If there is only one page, then the terminal ignores PPA.
- *
- * @args[0] is the number of the page to move the cursor to. If it is
- * greater than the number of the last page in memory, then the cursor
- * stops at the last page. If it is less than the number of the first
- * page, then the cursor stops at the first page.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_PPB(term_screen *screen, const term_seq *seq) {
- /*
- * PPB - page-position-backward
- * This control function moves the cursor backward to the corresponding
- * row and column on one of the preceding pages in page memory. If there
- * is only one page, then the terminal ignores PPB.
- *
- * @args[0] indicates the number of pages to move the cursor backward.
- * If it tries to move the cursor back farther than the first page in
- * memory, then the cursor stops at the first page. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_PPR(term_screen *screen, const term_seq *seq) {
- /*
- * PPR - page-position-relative
- * This control function moves the cursor forward to the corresponding
- * row and column on one of the following pages in page memory. If there
- * is only one page, then the terminal ignores PPR.
- *
- * @args[0] indicates how many pages to move the cursor forward. If it
- * tries to move the cursor beyond the last page in memory, then the
- * cursor stops at the last page. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_RC(term_screen *screen, const term_seq *seq) {
- /*
- * RC - restore-cursor
- */
-
- return screen_DECRC(screen, seq);
-}
-
-static int screen_REP(term_screen *screen, const term_seq *seq) {
- /*
- * REP - repeat
- * Repeat the preceding graphics-character the given number of times.
- * @args[0] specifies how often it shall be repeated. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_RI(term_screen *screen, const term_seq *seq) {
- /*
- * RI - reverse-index
- * Moves the cursor up one line in the same column. If the cursor is at
- * the top margin, the page scrolls down.
- */
-
- screen_cursor_up(screen, 1, true);
-
- return 0;
-}
-
-static int screen_RIS(term_screen *screen, const term_seq *seq) {
- /*
- * RIS - reset-to-initial-state
- * This control function causes a nonvolatile memory (NVR) recall to
- * occur. RIS replaces all set-up features with their saved settings.
- *
- * The terminal stores these saved settings in NVR memory. The saved
- * setting for a feature is the same as the factory-default setting,
- * unless you saved a new setting.
- */
-
- term_screen_hard_reset(screen);
-
- return 0;
-}
-
-static int screen_RM_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * RM_ANSI - reset-mode-ansi
- *
- * TODO: implement (see VT510rm manual)
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_ansi(screen, seq->args[i], false);
-
- return 0;
-}
-
-static int screen_RM_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * RM_DEC - reset-mode-dec
- * This is the same as RM_ANSI but for DEC modes.
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_dec(screen, seq->args[i], false);
-
- return 0;
-}
-
-static int screen_S7C1T(term_screen *screen, const term_seq *seq) {
- /*
- * S7C1T - set-7bit-c1-terminal
- * This causes the terminal to start sending C1 controls as 7bit
- * sequences instead of 8bit C1 controls.
- * This is ignored if the terminal is below level-2 emulation mode
- * (VT100 and below), the terminal already sends 7bit controls then.
- */
-
- if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
- screen->flags |= TERM_FLAG_7BIT_MODE;
-
- return 0;
-}
-
-static int screen_S8C1T(term_screen *screen, const term_seq *seq) {
- /*
- * S8C1T - set-8bit-c1-terminal
- * This causes the terminal to start sending C1 controls as 8bit C1
- * control instead of 7bit sequences.
- * This is ignored if the terminal is below level-2 emulation mode
- * (VT100 and below). The terminal always sends 7bit controls in those
- * modes.
- */
-
- if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
- screen->flags &= ~TERM_FLAG_7BIT_MODE;
-
- return 0;
-}
-
-static int screen_SCS(term_screen *screen, const term_seq *seq) {
- /*
- * SCS - select-character-set
- * Designate character sets to G-sets. The mapping from intermediates
- * and terminal characters in the escape sequence to G-sets and
- * character-sets is non-trivial and implemented separately. See there
- * for more information.
- * This call simply sets the selected G-set to the desired
- * character-set.
- */
-
- term_charset *cs = NULL;
-
- /* TODO: support more of them? */
- switch (seq->charset) {
- case TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_LATIN_CYRILLIC:
- break;
-
- case TERM_CHARSET_DEC_SPECIAL_GRAPHIC:
- cs = &term_dec_special_graphics;
- break;
- case TERM_CHARSET_DEC_SUPPLEMENTAL:
- cs = &term_dec_supplemental_graphics;
- break;
- case TERM_CHARSET_DEC_TECHNICAL:
- case TERM_CHARSET_CYRILLIC_DEC:
- case TERM_CHARSET_DUTCH_NRCS:
- case TERM_CHARSET_FINNISH_NRCS:
- case TERM_CHARSET_FRENCH_NRCS:
- case TERM_CHARSET_FRENCH_CANADIAN_NRCS:
- case TERM_CHARSET_GERMAN_NRCS:
- case TERM_CHARSET_GREEK_DEC:
- case TERM_CHARSET_GREEK_NRCS:
- case TERM_CHARSET_HEBREW_DEC:
- case TERM_CHARSET_HEBREW_NRCS:
- case TERM_CHARSET_ITALIAN_NRCS:
- case TERM_CHARSET_NORWEGIAN_DANISH_NRCS:
- case TERM_CHARSET_PORTUGUESE_NRCS:
- case TERM_CHARSET_RUSSIAN_NRCS:
- case TERM_CHARSET_SCS_NRCS:
- case TERM_CHARSET_SPANISH_NRCS:
- case TERM_CHARSET_SWEDISH_NRCS:
- case TERM_CHARSET_SWISS_NRCS:
- case TERM_CHARSET_TURKISH_DEC:
- case TERM_CHARSET_TURKISH_NRCS:
- break;
-
- case TERM_CHARSET_USERPREF_SUPPLEMENTAL:
- break;
- }
-
- if (seq->intermediates & TERM_SEQ_FLAG_POPEN)
- screen->g0 = cs ? : &term_unicode_lower;
- else if (seq->intermediates & TERM_SEQ_FLAG_PCLOSE)
- screen->g1 = cs ? : &term_unicode_upper;
- else if (seq->intermediates & TERM_SEQ_FLAG_MULT)
- screen->g2 = cs ? : &term_unicode_lower;
- else if (seq->intermediates & TERM_SEQ_FLAG_PLUS)
- screen->g3 = cs ? : &term_unicode_upper;
- else if (seq->intermediates & TERM_SEQ_FLAG_MINUS)
- screen->g1 = cs ? : &term_unicode_upper;
- else if (seq->intermediates & TERM_SEQ_FLAG_DOT)
- screen->g2 = cs ? : &term_unicode_lower;
- else if (seq->intermediates & TERM_SEQ_FLAG_SLASH)
- screen->g3 = cs ? : &term_unicode_upper;
-
- return 0;
-}
-
-static int screen_SD(term_screen *screen, const term_seq *seq) {
- /*
- * SD - scroll-down
- * This control function moves the user window down a specified number
- * of lines in page memory.
- * @args[0] is the number of lines to move the
- * user window up in page memory. New lines appear at the top of the
- * display. Old lines disappear at the bottom of the display. You
- * cannot pan past the top margin of the current page. 0 is treated
- * as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_scroll_down(screen->page, num, &screen->state.attr, screen->age, NULL);
-
- return 0;
-}
-
-static int screen_SGR(term_screen *screen, const term_seq *seq) {
- /*
- * SGR - select-graphics-rendition
- */
-
- term_color *dst;
- unsigned int i, code;
- int v;
-
- if (seq->n_args < 1) {
- zero(screen->state.attr);
- return 0;
- }
-
- for (i = 0; i < seq->n_args; ++i) {
- v = seq->args[i];
- switch (v) {
- case 1:
- screen->state.attr.bold = 1;
- break;
- case 3:
- screen->state.attr.italic = 1;
- break;
- case 4:
- screen->state.attr.underline = 1;
- break;
- case 5:
- screen->state.attr.blink = 1;
- break;
- case 7:
- screen->state.attr.inverse = 1;
- break;
- case 8:
- screen->state.attr.hidden = 1;
- break;
- case 22:
- screen->state.attr.bold = 0;
- break;
- case 23:
- screen->state.attr.italic = 0;
- break;
- case 24:
- screen->state.attr.underline = 0;
- break;
- case 25:
- screen->state.attr.blink = 0;
- break;
- case 27:
- screen->state.attr.inverse = 0;
- break;
- case 28:
- screen->state.attr.hidden = 0;
- break;
- case 30 ... 37:
- screen->state.attr.fg.ccode = v - 30 + TERM_CCODE_BLACK;
- break;
- case 39:
- screen->state.attr.fg.ccode = 0;
- break;
- case 40 ... 47:
- screen->state.attr.bg.ccode = v - 40 + TERM_CCODE_BLACK;
- break;
- case 49:
- screen->state.attr.bg.ccode = 0;
- break;
- case 90 ... 97:
- screen->state.attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK;
- break;
- case 100 ... 107:
- screen->state.attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK;
- break;
- case 38:
- /* fallthrough */
- case 48:
-
- if (v == 38)
- dst = &screen->state.attr.fg;
- else
- dst = &screen->state.attr.bg;
-
- ++i;
- if (i >= seq->n_args)
- break;
-
- switch (seq->args[i]) {
- case 2:
- /* 24bit-color support */
-
- i += 3;
- if (i >= seq->n_args)
- break;
-
- dst->ccode = TERM_CCODE_RGB;
- dst->red = (seq->args[i - 2] >= 0) ? seq->args[i - 2] : 0;
- dst->green = (seq->args[i - 1] >= 0) ? seq->args[i - 1] : 0;
- dst->blue = (seq->args[i] >= 0) ? seq->args[i] : 0;
-
- break;
- case 5:
- /* 256-color support */
-
- ++i;
- if (i >= seq->n_args || seq->args[i] < 0)
- break;
-
- dst->ccode = TERM_CCODE_256;
- code = seq->args[i];
- dst->c256 = code < 256 ? code : 0;
-
- break;
- }
-
- break;
- case -1:
- /* fallthrough */
- case 0:
- zero(screen->state.attr);
- break;
- }
- }
-
- return 0;
-}
-
-static int screen_SI(term_screen *screen, const term_seq *seq) {
- /*
- * SI - shift-in
- * Map G0 into GL.
- */
-
- screen->state.gl = &screen->g0;
-
- return 0;
-}
-
-static int screen_SM_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * SM_ANSI - set-mode-ansi
- *
- * TODO: implement
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_ansi(screen, seq->args[i], true);
-
- return 0;
-}
-
-static int screen_SM_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * SM_DEC - set-mode-dec
- * This is the same as SM_ANSI but for DEC modes.
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_dec(screen, seq->args[i], true);
-
- return 0;
-}
-
-static int screen_SO(term_screen *screen, const term_seq *seq) {
- /*
- * SO - shift-out
- * Map G1 into GL.
- */
-
- screen->state.gl = &screen->g1;
-
- return 0;
-}
-
-static int screen_SPA(term_screen *screen, const term_seq *seq) {
- /*
- * SPA - start-of-protected-area
- *
- * TODO: What is this?
- */
-
- return 0;
-}
-
-static int screen_SS2(term_screen *screen, const term_seq *seq) {
- /*
- * SS2 - single-shift-2
- * Temporarily map G2 into GL for the next graphics character.
- */
-
- screen->state.glt = &screen->g2;
-
- return 0;
-}
-
-static int screen_SS3(term_screen *screen, const term_seq *seq) {
- /*
- * SS3 - single-shift-3
- * Temporarily map G3 into GL for the next graphics character
- */
-
- screen->state.glt = &screen->g3;
-
- return 0;
-}
-
-static int screen_ST(term_screen *screen, const term_seq *seq) {
- /*
- * ST - string-terminator
- * The string-terminator is usually part of control-sequences and
- * handled by the parser. In all other situations it is silently
- * ignored.
- */
-
- return 0;
-}
-
-static int screen_SU(term_screen *screen, const term_seq *seq) {
- /*
- * SU - scroll-up
- * This control function moves the user window up a specified number of
- * lines in page memory.
- * @args[0] is the number of lines to move the
- * user window down in page memory. New lines appear at the bottom of
- * the display. Old lines disappear at the top of the display. You
- * cannot pan past the bottom margin of the current page. 0 is treated
- * as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_scroll_up(screen->page, num, &screen->state.attr, screen->age, screen->history);
-
- return 0;
-}
-
-static int screen_SUB(term_screen *screen, const term_seq *seq) {
- /*
- * SUB - substitute
- * Cancel the current control-sequence and print a replacement
- * character. Our parser already handles this so all we have to do is
- * print the replacement character.
- */
-
- static const term_seq rep = {
- .type = TERM_SEQ_GRAPHIC,
- .command = TERM_CMD_GRAPHIC,
- .terminator = 0xfffd,
- };
-
- return screen_GRAPHIC(screen, &rep);
-}
-
-static int screen_TBC(term_screen *screen, const term_seq *seq) {
- /*
- * TBC - tab-clear
- * This clears tab-stops. If @args[0] is 0, the tab-stop at the current
- * cursor position is cleared. If it is 3, all tab stops are cleared.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0, pos;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- pos = screen->state.cursor_x;
- if (screen->page->width > 0)
- screen->tabs[pos / 8] &= ~(1U << (pos % 8));
- break;
- case 3:
- if (screen->page->width > 0)
- memzero(screen->tabs, (screen->page->width + 7) / 8);
- break;
- }
-
- return 0;
-}
-
-static int screen_VPA(term_screen *screen, const term_seq *seq) {
- /*
- * VPA - vertical-line-position-absolute
- * VPA causes the active position to be moved to the corresponding
- * horizontal position. @args[0] specifies the line to jump to. If an
- * attempt is made to move the active position below the last line, then
- * the active position stops on the last line. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int pos = 1;
-
- if (seq->args[0] > 0)
- pos = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set_rel(screen, screen->state.cursor_x, pos - 1);
-
- return 0;
-}
-
-static int screen_VPR(term_screen *screen, const term_seq *seq) {
- /*
- * VPR - vertical-line-position-relative
- * VPR causes the active position to be moved to the corresponding
- * horizontal position. @args[0] specifies the number of lines to jump
- * down relative to the current cursor position. If an attempt is made
- * to move the active position below the last line, the active position
- * stops at the last line. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, num, false);
-
- return 0;
-}
-
-static int screen_VT(term_screen *screen, const term_seq *seq) {
- /*
- * VT - vertical-tab
- * This causes a vertical jump by one line. Terminals treat it exactly
- * the same as LF.
- */
-
- return screen_LF(screen, seq);
-}
-
-static int screen_XTERM_CLLHP(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_CLLHP - xterm-cursor-lower-left-hp-bugfix
- * Move the cursor to the lower-left corner of the page. This is an HP
- * bugfix by xterm.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_IHMT(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_IHMT - xterm-initiate-highlight-mouse-tracking
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_MLHP(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_MLHP - xterm-memory-lock-hp-bugfix
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_MUHP(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_MUHP - xterm-memory-unlock-hp-bugfix
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_RPM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_RPM - xterm-restore-private-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_RRV(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_RRV - xterm-reset-resource-value
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_RTM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_RTM - xterm-reset-title-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SACL1(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SACL1 - xterm-set-ansi-conformance-level-1
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SACL2(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SACL2 - xterm-set-ansi-conformance-level-2
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SACL3(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SACL3 - xterm-set-ansi-conformance-level-3
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SDCS(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SDCS - xterm-set-default-character-set
- * Select the default character set. We treat this the same as UTF-8 as
- * this is our default character set. As we always use UTF-8, this
- * becomes as no-op.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SGFX(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SGFX - xterm-sixel-graphics
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SPM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SPM - xterm-set-private-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SRV(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SRV - xterm-set-resource-value
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_STM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_STM - xterm-set-title-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SUCS(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SUCS - xterm-select-utf8-character-set
- * Select UTF-8 as character set. This is our default on only character
- * set. Hence, this is a no-op.
- */
-
- return 0;
-}
-
-static int screen_XTERM_WM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_WM - xterm-window-management
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-/*
- * Feeding data
- * The screen_feed_*() handlers take data from the user and feed it into the
- * screen. Once the parser has detected a sequence, we parse the command-type
- * and forward it to the command-dispatchers.
- */
-
-static int screen_feed_cmd(term_screen *screen, const term_seq *seq) {
- switch (seq->command) {
- case TERM_CMD_GRAPHIC:
- return screen_GRAPHIC(screen, seq);
- case TERM_CMD_BEL:
- return screen_BEL(screen, seq);
- case TERM_CMD_BS:
- return screen_BS(screen, seq);
- case TERM_CMD_CBT:
- return screen_CBT(screen, seq);
- case TERM_CMD_CHA:
- return screen_CHA(screen, seq);
- case TERM_CMD_CHT:
- return screen_CHT(screen, seq);
- case TERM_CMD_CNL:
- return screen_CNL(screen, seq);
- case TERM_CMD_CPL:
- return screen_CPL(screen, seq);
- case TERM_CMD_CR:
- return screen_CR(screen, seq);
- case TERM_CMD_CUB:
- return screen_CUB(screen, seq);
- case TERM_CMD_CUD:
- return screen_CUD(screen, seq);
- case TERM_CMD_CUF:
- return screen_CUF(screen, seq);
- case TERM_CMD_CUP:
- return screen_CUP(screen, seq);
- case TERM_CMD_CUU:
- return screen_CUU(screen, seq);
- case TERM_CMD_DA1:
- return screen_DA1(screen, seq);
- case TERM_CMD_DA2:
- return screen_DA2(screen, seq);
- case TERM_CMD_DA3:
- return screen_DA3(screen, seq);
- case TERM_CMD_DC1:
- return screen_DC1(screen, seq);
- case TERM_CMD_DC3:
- return screen_DC3(screen, seq);
- case TERM_CMD_DCH:
- return screen_DCH(screen, seq);
- case TERM_CMD_DECALN:
- return screen_DECALN(screen, seq);
- case TERM_CMD_DECANM:
- return screen_DECANM(screen, seq);
- case TERM_CMD_DECBI:
- return screen_DECBI(screen, seq);
- case TERM_CMD_DECCARA:
- return screen_DECCARA(screen, seq);
- case TERM_CMD_DECCRA:
- return screen_DECCRA(screen, seq);
- case TERM_CMD_DECDC:
- return screen_DECDC(screen, seq);
- case TERM_CMD_DECDHL_BH:
- return screen_DECDHL_BH(screen, seq);
- case TERM_CMD_DECDHL_TH:
- return screen_DECDHL_TH(screen, seq);
- case TERM_CMD_DECDWL:
- return screen_DECDWL(screen, seq);
- case TERM_CMD_DECEFR:
- return screen_DECEFR(screen, seq);
- case TERM_CMD_DECELF:
- return screen_DECELF(screen, seq);
- case TERM_CMD_DECELR:
- return screen_DECELR(screen, seq);
- case TERM_CMD_DECERA:
- return screen_DECERA(screen, seq);
- case TERM_CMD_DECFI:
- return screen_DECFI(screen, seq);
- case TERM_CMD_DECFRA:
- return screen_DECFRA(screen, seq);
- case TERM_CMD_DECIC:
- return screen_DECIC(screen, seq);
- case TERM_CMD_DECID:
- return screen_DECID(screen, seq);
- case TERM_CMD_DECINVM:
- return screen_DECINVM(screen, seq);
- case TERM_CMD_DECKBD:
- return screen_DECKBD(screen, seq);
- case TERM_CMD_DECKPAM:
- return screen_DECKPAM(screen, seq);
- case TERM_CMD_DECKPNM:
- return screen_DECKPNM(screen, seq);
- case TERM_CMD_DECLFKC:
- return screen_DECLFKC(screen, seq);
- case TERM_CMD_DECLL:
- return screen_DECLL(screen, seq);
- case TERM_CMD_DECLTOD:
- return screen_DECLTOD(screen, seq);
- case TERM_CMD_DECPCTERM:
- return screen_DECPCTERM(screen, seq);
- case TERM_CMD_DECPKA:
- return screen_DECPKA(screen, seq);
- case TERM_CMD_DECPKFMR:
- return screen_DECPKFMR(screen, seq);
- case TERM_CMD_DECRARA:
- return screen_DECRARA(screen, seq);
- case TERM_CMD_DECRC:
- return screen_DECRC(screen, seq);
- case TERM_CMD_DECREQTPARM:
- return screen_DECREQTPARM(screen, seq);
- case TERM_CMD_DECRPKT:
- return screen_DECRPKT(screen, seq);
- case TERM_CMD_DECRQCRA:
- return screen_DECRQCRA(screen, seq);
- case TERM_CMD_DECRQDE:
- return screen_DECRQDE(screen, seq);
- case TERM_CMD_DECRQKT:
- return screen_DECRQKT(screen, seq);
- case TERM_CMD_DECRQLP:
- return screen_DECRQLP(screen, seq);
- case TERM_CMD_DECRQM_ANSI:
- return screen_DECRQM_ANSI(screen, seq);
- case TERM_CMD_DECRQM_DEC:
- return screen_DECRQM_DEC(screen, seq);
- case TERM_CMD_DECRQPKFM:
- return screen_DECRQPKFM(screen, seq);
- case TERM_CMD_DECRQPSR:
- return screen_DECRQPSR(screen, seq);
- case TERM_CMD_DECRQTSR:
- return screen_DECRQTSR(screen, seq);
- case TERM_CMD_DECRQUPSS:
- return screen_DECRQUPSS(screen, seq);
- case TERM_CMD_DECSACE:
- return screen_DECSACE(screen, seq);
- case TERM_CMD_DECSASD:
- return screen_DECSASD(screen, seq);
- case TERM_CMD_DECSC:
- return screen_DECSC(screen, seq);
- case TERM_CMD_DECSCA:
- return screen_DECSCA(screen, seq);
- case TERM_CMD_DECSCL:
- return screen_DECSCL(screen, seq);
- case TERM_CMD_DECSCP:
- return screen_DECSCP(screen, seq);
- case TERM_CMD_DECSCPP:
- return screen_DECSCPP(screen, seq);
- case TERM_CMD_DECSCS:
- return screen_DECSCS(screen, seq);
- case TERM_CMD_DECSCUSR:
- return screen_DECSCUSR(screen, seq);
- case TERM_CMD_DECSDDT:
- return screen_DECSDDT(screen, seq);
- case TERM_CMD_DECSDPT:
- return screen_DECSDPT(screen, seq);
- case TERM_CMD_DECSED:
- return screen_DECSED(screen, seq);
- case TERM_CMD_DECSEL:
- return screen_DECSEL(screen, seq);
- case TERM_CMD_DECSERA:
- return screen_DECSERA(screen, seq);
- case TERM_CMD_DECSFC:
- return screen_DECSFC(screen, seq);
- case TERM_CMD_DECSKCV:
- return screen_DECSKCV(screen, seq);
- case TERM_CMD_DECSLCK:
- return screen_DECSLCK(screen, seq);
- case TERM_CMD_DECSLE:
- return screen_DECSLE(screen, seq);
- case TERM_CMD_DECSLPP:
- return screen_DECSLPP(screen, seq);
- case TERM_CMD_DECSLRM_OR_SC:
- return screen_DECSLRM_OR_SC(screen, seq);
- case TERM_CMD_DECSMBV:
- return screen_DECSMBV(screen, seq);
- case TERM_CMD_DECSMKR:
- return screen_DECSMKR(screen, seq);
- case TERM_CMD_DECSNLS:
- return screen_DECSNLS(screen, seq);
- case TERM_CMD_DECSPP:
- return screen_DECSPP(screen, seq);
- case TERM_CMD_DECSPPCS:
- return screen_DECSPPCS(screen, seq);
- case TERM_CMD_DECSPRTT:
- return screen_DECSPRTT(screen, seq);
- case TERM_CMD_DECSR:
- return screen_DECSR(screen, seq);
- case TERM_CMD_DECSRFR:
- return screen_DECSRFR(screen, seq);
- case TERM_CMD_DECSSCLS:
- return screen_DECSSCLS(screen, seq);
- case TERM_CMD_DECSSDT:
- return screen_DECSSDT(screen, seq);
- case TERM_CMD_DECSSL:
- return screen_DECSSL(screen, seq);
- case TERM_CMD_DECST8C:
- return screen_DECST8C(screen, seq);
- case TERM_CMD_DECSTBM:
- return screen_DECSTBM(screen, seq);
- case TERM_CMD_DECSTR:
- return screen_DECSTR(screen, seq);
- case TERM_CMD_DECSTRL:
- return screen_DECSTRL(screen, seq);
- case TERM_CMD_DECSWBV:
- return screen_DECSWBV(screen, seq);
- case TERM_CMD_DECSWL:
- return screen_DECSWL(screen, seq);
- case TERM_CMD_DECTID:
- return screen_DECTID(screen, seq);
- case TERM_CMD_DECTME:
- return screen_DECTME(screen, seq);
- case TERM_CMD_DECTST:
- return screen_DECTST(screen, seq);
- case TERM_CMD_DL:
- return screen_DL(screen, seq);
- case TERM_CMD_DSR_ANSI:
- return screen_DSR_ANSI(screen, seq);
- case TERM_CMD_DSR_DEC:
- return screen_DSR_DEC(screen, seq);
- case TERM_CMD_ECH:
- return screen_ECH(screen, seq);
- case TERM_CMD_ED:
- return screen_ED(screen, seq);
- case TERM_CMD_EL:
- return screen_EL(screen, seq);
- case TERM_CMD_ENQ:
- return screen_ENQ(screen, seq);
- case TERM_CMD_EPA:
- return screen_EPA(screen, seq);
- case TERM_CMD_FF:
- return screen_FF(screen, seq);
- case TERM_CMD_HPA:
- return screen_HPA(screen, seq);
- case TERM_CMD_HPR:
- return screen_HPR(screen, seq);
- case TERM_CMD_HT:
- return screen_HT(screen, seq);
- case TERM_CMD_HTS:
- return screen_HTS(screen, seq);
- case TERM_CMD_HVP:
- return screen_HVP(screen, seq);
- case TERM_CMD_ICH:
- return screen_ICH(screen, seq);
- case TERM_CMD_IL:
- return screen_IL(screen, seq);
- case TERM_CMD_IND:
- return screen_IND(screen, seq);
- case TERM_CMD_LF:
- return screen_LF(screen, seq);
- case TERM_CMD_LS1R:
- return screen_LS1R(screen, seq);
- case TERM_CMD_LS2:
- return screen_LS2(screen, seq);
- case TERM_CMD_LS2R:
- return screen_LS2R(screen, seq);
- case TERM_CMD_LS3:
- return screen_LS3(screen, seq);
- case TERM_CMD_LS3R:
- return screen_LS3R(screen, seq);
- case TERM_CMD_MC_ANSI:
- return screen_MC_ANSI(screen, seq);
- case TERM_CMD_MC_DEC:
- return screen_MC_DEC(screen, seq);
- case TERM_CMD_NEL:
- return screen_NEL(screen, seq);
- case TERM_CMD_NP:
- return screen_NP(screen, seq);
- case TERM_CMD_NULL:
- return screen_NULL(screen, seq);
- case TERM_CMD_PP:
- return screen_PP(screen, seq);
- case TERM_CMD_PPA:
- return screen_PPA(screen, seq);
- case TERM_CMD_PPB:
- return screen_PPB(screen, seq);
- case TERM_CMD_PPR:
- return screen_PPR(screen, seq);
- case TERM_CMD_RC:
- return screen_RC(screen, seq);
- case TERM_CMD_REP:
- return screen_REP(screen, seq);
- case TERM_CMD_RI:
- return screen_RI(screen, seq);
- case TERM_CMD_RIS:
- return screen_RIS(screen, seq);
- case TERM_CMD_RM_ANSI:
- return screen_RM_ANSI(screen, seq);
- case TERM_CMD_RM_DEC:
- return screen_RM_DEC(screen, seq);
- case TERM_CMD_S7C1T:
- return screen_S7C1T(screen, seq);
- case TERM_CMD_S8C1T:
- return screen_S8C1T(screen, seq);
- case TERM_CMD_SCS:
- return screen_SCS(screen, seq);
- case TERM_CMD_SD:
- return screen_SD(screen, seq);
- case TERM_CMD_SGR:
- return screen_SGR(screen, seq);
- case TERM_CMD_SI:
- return screen_SI(screen, seq);
- case TERM_CMD_SM_ANSI:
- return screen_SM_ANSI(screen, seq);
- case TERM_CMD_SM_DEC:
- return screen_SM_DEC(screen, seq);
- case TERM_CMD_SO:
- return screen_SO(screen, seq);
- case TERM_CMD_SPA:
- return screen_SPA(screen, seq);
- case TERM_CMD_SS2:
- return screen_SS2(screen, seq);
- case TERM_CMD_SS3:
- return screen_SS3(screen, seq);
- case TERM_CMD_ST:
- return screen_ST(screen, seq);
- case TERM_CMD_SU:
- return screen_SU(screen, seq);
- case TERM_CMD_SUB:
- return screen_SUB(screen, seq);
- case TERM_CMD_TBC:
- return screen_TBC(screen, seq);
- case TERM_CMD_VPA:
- return screen_VPA(screen, seq);
- case TERM_CMD_VPR:
- return screen_VPR(screen, seq);
- case TERM_CMD_VT:
- return screen_VT(screen, seq);
- case TERM_CMD_XTERM_CLLHP:
- return screen_XTERM_CLLHP(screen, seq);
- case TERM_CMD_XTERM_IHMT:
- return screen_XTERM_IHMT(screen, seq);
- case TERM_CMD_XTERM_MLHP:
- return screen_XTERM_MLHP(screen, seq);
- case TERM_CMD_XTERM_MUHP:
- return screen_XTERM_MUHP(screen, seq);
- case TERM_CMD_XTERM_RPM:
- return screen_XTERM_RPM(screen, seq);
- case TERM_CMD_XTERM_RRV:
- return screen_XTERM_RRV(screen, seq);
- case TERM_CMD_XTERM_RTM:
- return screen_XTERM_RTM(screen, seq);
- case TERM_CMD_XTERM_SACL1:
- return screen_XTERM_SACL1(screen, seq);
- case TERM_CMD_XTERM_SACL2:
- return screen_XTERM_SACL2(screen, seq);
- case TERM_CMD_XTERM_SACL3:
- return screen_XTERM_SACL3(screen, seq);
- case TERM_CMD_XTERM_SDCS:
- return screen_XTERM_SDCS(screen, seq);
- case TERM_CMD_XTERM_SGFX:
- return screen_XTERM_SGFX(screen, seq);
- case TERM_CMD_XTERM_SPM:
- return screen_XTERM_SPM(screen, seq);
- case TERM_CMD_XTERM_SRV:
- return screen_XTERM_SRV(screen, seq);
- case TERM_CMD_XTERM_STM:
- return screen_XTERM_STM(screen, seq);
- case TERM_CMD_XTERM_SUCS:
- return screen_XTERM_SUCS(screen, seq);
- case TERM_CMD_XTERM_WM:
- return screen_XTERM_WM(screen, seq);
- }
-
- return 0;
-}
-
-unsigned int term_screen_get_width(term_screen *screen) {
- assert_return(screen, -EINVAL);
-
- return screen->page->width;
-}
-
-unsigned int term_screen_get_height(term_screen *screen) {
- assert_return(screen, -EINVAL);
-
- return screen->page->height;
-}
-
-uint64_t term_screen_get_age(term_screen *screen) {
- assert_return(screen, 0);
-
- return screen->age;
-}
-
-int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) {
- uint32_t *ucs4_str;
- size_t i, j, ucs4_len;
- const term_seq *seq;
- int r;
-
- assert_return(screen, -EINVAL);
-
- ++screen->age;
-
- /* Feed bytes into utf8 decoder and handle parsed ucs4 chars. We always
- * treat data as UTF-8, but the parser makes sure to fall back to raw
- * 8bit mode if the stream is not valid UTF-8. This should be more than
- * enough to support old 7bit/8bit modes. */
- for (i = 0; i < size; ++i) {
- ucs4_len = term_utf8_decode(&screen->utf8, &ucs4_str, in[i]);
- for (j = 0; j < ucs4_len; ++j) {
- r = term_parser_feed(screen->parser, &seq, ucs4_str[j]);
- if (r < 0) {
- return r;
- } else if (r != TERM_SEQ_NONE) {
- r = screen_feed_cmd(screen, seq);
- if (r < 0)
- return r;
- }
- }
- }
-
- return 0;
-}
-
-static char *screen_map_key(term_screen *screen,
- char *p,
- const uint32_t *keysyms,
- size_t n_syms,
- uint32_t ascii,
- const uint32_t *ucs4,
- unsigned int mods) {
- char ch, ch2, ch_mods;
- uint32_t v;
- size_t i;
-
- /* TODO: All these key-mappings need to be verified. Public information
- * on those mappings is pretty scarce and every emulator seems to do it
- * slightly differently.
- * A lot of mappings are also missing. */
-
- if (n_syms < 1)
- return p;
-
- if (n_syms == 1)
- v = keysyms[0];
- else
- v = XKB_KEY_NoSymbol;
-
- /* In some mappings, the modifiers are encoded as CSI parameters. The
- * encoding is rather arbitrary, but seems to work. */
- ch_mods = 0;
- switch (mods & (TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT | TERM_KBDMOD_CTRL)) {
- case TERM_KBDMOD_SHIFT:
- ch_mods = '2';
- break;
- case TERM_KBDMOD_ALT:
- ch_mods = '3';
- break;
- case TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
- ch_mods = '4';
- break;
- case TERM_KBDMOD_CTRL:
- ch_mods = '5';
- break;
- case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT:
- ch_mods = '6';
- break;
- case TERM_KBDMOD_CTRL | TERM_KBDMOD_ALT:
- ch_mods = '7';
- break;
- case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
- ch_mods = '8';
- break;
- }
-
- /* A user might actually use multiple layouts for keyboard
- * input. @keysyms[0] contains the actual keysym that the user
- * used. But if this keysym is not in the ascii range, the
- * input handler does check all other layouts that the user
- * specified whether one of them maps the key to some ASCII
- * keysym and provides this via @ascii. We always use the real
- * keysym except when handling CTRL+<XY> shortcuts we use the
- * ascii keysym. This is for compatibility to xterm et. al. so
- * ctrl+c always works regardless of the currently active
- * keyboard layout. But if no ascii-sym is found, we still use
- * the real keysym. */
- if (ascii == XKB_KEY_NoSymbol)
- ascii = v;
-
- /* map CTRL+<ascii> */
- if (mods & TERM_KBDMOD_CTRL) {
- switch (ascii) {
- case 0x60 ... 0x7e:
- /* Right hand side is mapped to the left and then
- * treated equally. Fall through to left-hand side.. */
- ascii -= 0x20;
- case 0x20 ... 0x5f:
- /* Printable ASCII is mapped 1-1 in XKB and in
- * combination with CTRL bit 7 is flipped. This
- * is equivalent to the caret-notation. */
- *p++ = ascii ^ 0x40;
- return p;
- }
- }
-
- /* map cursor keys */
- ch = 0;
- switch (v) {
- case XKB_KEY_Up:
- ch = 'A';
- break;
- case XKB_KEY_Down:
- ch = 'B';
- break;
- case XKB_KEY_Right:
- ch = 'C';
- break;
- case XKB_KEY_Left:
- ch = 'D';
- break;
- case XKB_KEY_Home:
- ch = 'H';
- break;
- case XKB_KEY_End:
- ch = 'F';
- break;
- }
- if (ch) {
- *p++ = 0x1b;
- if (screen->flags & TERM_FLAG_CURSOR_KEYS)
- *p++ = 'O';
- else
- *p++ = '[';
- if (ch_mods) {
- *p++ = '1';
- *p++ = ';';
- *p++ = ch_mods;
- }
- *p++ = ch;
- return p;
- }
-
- /* map action keys */
- ch = 0;
- switch (v) {
- case XKB_KEY_Find:
- ch = '1';
- break;
- case XKB_KEY_Insert:
- ch = '2';
- break;
- case XKB_KEY_Delete:
- ch = '3';
- break;
- case XKB_KEY_Select:
- ch = '4';
- break;
- case XKB_KEY_Page_Up:
- ch = '5';
- break;
- case XKB_KEY_Page_Down:
- ch = '6';
- break;
- }
- if (ch) {
- *p++ = 0x1b;
- *p++ = '[';
- *p++ = ch;
- if (ch_mods) {
- *p++ = ';';
- *p++ = ch_mods;
- }
- *p++ = '~';
- return p;
- }
-
- /* map lower function keys */
- ch = 0;
- switch (v) {
- case XKB_KEY_F1:
- ch = 'P';
- break;
- case XKB_KEY_F2:
- ch = 'Q';
- break;
- case XKB_KEY_F3:
- ch = 'R';
- break;
- case XKB_KEY_F4:
- ch = 'S';
- break;
- }
- if (ch) {
- if (ch_mods) {
- *p++ = 0x1b;
- *p++ = '[';
- *p++ = '1';
- *p++ = ';';
- *p++ = ch_mods;
- *p++ = ch;
- } else {
- *p++ = 0x1b;
- *p++ = 'O';
- *p++ = ch;
- }
-
- return p;
- }
-
- /* map upper function keys */
- ch = 0;
- ch2 = 0;
- switch (v) {
- case XKB_KEY_F5:
- ch = '1';
- ch2 = '5';
- break;
- case XKB_KEY_F6:
- ch = '1';
- ch2 = '7';
- break;
- case XKB_KEY_F7:
- ch = '1';
- ch2 = '8';
- break;
- case XKB_KEY_F8:
- ch = '1';
- ch2 = '9';
- break;
- case XKB_KEY_F9:
- ch = '2';
- ch2 = '0';
- break;
- case XKB_KEY_F10:
- ch = '2';
- ch2 = '1';
- break;
- case XKB_KEY_F11:
- ch = '2';
- ch2 = '2';
- break;
- case XKB_KEY_F12:
- ch = '2';
- ch2 = '3';
- break;
- }
- if (ch) {
- *p++ = 0x1b;
- *p++ = '[';
- *p++ = ch;
- if (ch2)
- *p++ = ch2;
- if (ch_mods) {
- *p++ = ';';
- *p++ = ch_mods;
- }
- *p++ = '~';
- return p;
- }
-
- /* map special keys */
- switch (v) {
- case 0xff08: /* XKB_KEY_BackSpace */
- case 0xff09: /* XKB_KEY_Tab */
- case 0xff0a: /* XKB_KEY_Linefeed */
- case 0xff0b: /* XKB_KEY_Clear */
- case 0xff15: /* XKB_KEY_Sys_Req */
- case 0xff1b: /* XKB_KEY_Escape */
- case 0xffff: /* XKB_KEY_Delete */
- *p++ = v - 0xff00;
- return p;
- case 0xff13: /* XKB_KEY_Pause */
- /* TODO: What should we do with this key?
- * Sending XOFF is awful as there is no simple
- * way on modern keyboards to send XON again.
- * If someone wants this, we can re-eanble
- * optionally. */
- return p;
- case 0xff14: /* XKB_KEY_Scroll_Lock */
- /* TODO: What should we do on scroll-lock?
- * Sending 0x14 is what the specs say but it is
- * not used today the way most users would
- * expect so we disable it. If someone wants
- * this, we can re-enable it (optionally). */
- return p;
- case XKB_KEY_Return:
- *p++ = 0x0d;
- if (screen->flags & TERM_FLAG_NEWLINE_MODE)
- *p++ = 0x0a;
- return p;
- case XKB_KEY_ISO_Left_Tab:
- *p++ = 0x09;
- return p;
- }
-
- /* map unicode keys */
- for (i = 0; i < n_syms; ++i)
- p += utf8_encode_unichar(p, ucs4[i]);
-
- return p;
-}
-
-int term_screen_feed_keyboard(term_screen *screen,
- const uint32_t *keysyms,
- size_t n_syms,
- uint32_t ascii,
- const uint32_t *ucs4,
- unsigned int mods) {
- _cleanup_free_ char *dyn = NULL;
- static const size_t padding = 1;
- char buf[128], *start, *p;
-
- assert_return(screen, -EINVAL);
-
- /* allocate buffer if too small */
- start = buf;
- if (4 * n_syms + padding > sizeof(buf)) {
- dyn = malloc(4 * n_syms + padding);
- if (!dyn)
- return -ENOMEM;
-
- start = dyn;
- }
-
- /* reserve prefix space */
- start += padding;
- p = start;
-
- p = screen_map_key(screen, p, keysyms, n_syms, ascii, ucs4, mods);
- if (!p || p - start < 1)
- return 0;
-
- /* The ALT modifier causes ESC to be prepended to any key-stroke. We
- * already accounted for that buffer space above, so simply prepend it
- * here.
- * TODO: is altSendsEscape a suitable default? What are the semantics
- * exactly? Is it used in C0/C1 conversion? Is it prepended if there
- * already is an escape character? */
- if (mods & TERM_KBDMOD_ALT && *start != 0x1b)
- *--start = 0x1b;
-
- /* turn C0 into C1 */
- if (!(screen->flags & TERM_FLAG_7BIT_MODE) && p - start >= 2)
- if (start[0] == 0x1b && start[1] >= 0x40 && start[1] <= 0x5f)
- *++start ^= 0x40;
-
- return screen_write(screen, start, p - start);
-}
-
-int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) {
- unsigned int i;
- uint8_t *t;
- int r;
-
- assert_return(screen, -EINVAL);
-
- r = term_page_reserve(screen->page_main, x, y, &screen->state.attr, screen->age);
- if (r < 0)
- return r;
-
- r = term_page_reserve(screen->page_alt, x, y, &screen->state.attr, screen->age);
- if (r < 0)
- return r;
-
- if (x > screen->n_tabs) {
- t = realloc(screen->tabs, (x + 7) / 8);
- if (!t)
- return -ENOMEM;
-
- screen->tabs = t;
- screen->n_tabs = x;
- }
-
- for (i = (screen->page->width + 7) / 8 * 8; i < x; i += 8)
- screen->tabs[i / 8] = 0x1;
-
- term_page_resize(screen->page_main, x, y, &screen->state.attr, screen->age, screen->history);
- term_page_resize(screen->page_alt, x, y, &screen->state.attr, screen->age, NULL);
-
- screen->state.cursor_x = screen_clamp_x(screen, screen->state.cursor_x);
- screen->state.cursor_y = screen_clamp_x(screen, screen->state.cursor_y);
- screen_cursor_clear_wrap(screen);
-
- return 0;
-}
-
-void term_screen_soft_reset(term_screen *screen) {
- unsigned int i;
-
- assert(screen);
-
- screen->g0 = &term_unicode_lower;
- screen->g1 = &term_unicode_upper;
- screen->g2 = &term_unicode_lower;
- screen->g3 = &term_unicode_upper;
- screen->state.attr = screen->default_attr;
- screen->state.gl = &screen->g0;
- screen->state.gr = &screen->g1;
- screen->state.glt = NULL;
- screen->state.grt = NULL;
- screen->state.auto_wrap = 0;
- screen->state.origin_mode = 0;
-
- screen->saved = screen->state;
- screen->saved.cursor_x = 0;
- screen->saved.cursor_y = 0;
- screen->saved_alt = screen->saved;
-
- screen->page = screen->page_main;
- screen->history = screen->history_main;
- screen->flags = TERM_FLAG_7BIT_MODE;
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
-
- for (i = 0; i < screen->page->width; i += 8)
- screen->tabs[i / 8] = 0x1;
-
- term_page_set_scroll_region(screen->page_main, 0, screen->page->height);
- term_page_set_scroll_region(screen->page_alt, 0, screen->page->height);
-}
-
-void term_screen_hard_reset(term_screen *screen) {
- assert(screen);
-
- term_screen_soft_reset(screen);
- zero(screen->utf8);
- screen->state.cursor_x = 0;
- screen->state.cursor_y = 0;
- term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
- term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
-}
-
-int term_screen_set_answerback(term_screen *screen, const char *answerback) {
- char *t = NULL;
-
- assert_return(screen, -EINVAL);
-
- if (answerback) {
- t = strdup(answerback);
- if (!t)
- return -ENOMEM;
- }
-
- free(screen->answerback);
- screen->answerback = t;
-
- return 0;
-}
-
-int term_screen_draw(term_screen *screen,
- int (*draw_fn) (term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width),
- void *userdata,
- uint64_t *fb_age) {
- uint64_t cell_age, line_age, age = 0;
- term_charbuf_t ch_buf;
- const uint32_t *ch_str;
- unsigned int i, j, cw;
- term_page *page;
- term_line *line;
- term_cell *cell;
- size_t ch_n;
- int r;
-
- assert(screen);
- assert(draw_fn);
-
- if (fb_age)
- age = *fb_age;
-
- page = screen->page;
-
- for (j = 0; j < page->height; ++j) {
- line = page->lines[j];
- line_age = MAX(line->age, page->age);
-
- for (i = 0; i < page->width; ++i) {
- term_attr attr;
-
- cell = &line->cells[i];
- cell_age = MAX(cell->age, line_age);
-
- if (age != 0 && cell_age <= age)
- continue;
-
- ch_str = term_char_resolve(cell->ch, &ch_n, &ch_buf);
-
- /* Character-width of 0 is used for cleared cells.
- * Always treat this as single-cell character, so
- * renderers can assume ch_width is set properpy. */
- cw = MAX(cell->cwidth, 1U);
-
- attr = cell->attr;
- if (i == screen->state.cursor_x && j == screen->state.cursor_y &&
- !(screen->flags & TERM_FLAG_HIDE_CURSOR))
- attr.inverse ^= 1;
-
- r = draw_fn(screen,
- userdata,
- i,
- j,
- &attr,
- ch_str,
- ch_n,
- cw);
- if (r != 0)
- return r;
- }
- }
-
- if (fb_age)
- *fb_age = screen->age;
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/term-wcwidth.c b/src/libsystemd-terminal/term-wcwidth.c
deleted file mode 100644
index 833a099bd7..0000000000
--- a/src/libsystemd-terminal/term-wcwidth.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * (Minimal changes made by David Herrmann, to make clean for inclusion in
- * systemd. Original header follows.)
- *
- * This is an implementation of wcwidth() and wcswidth() (defined in
- * IEEE Std 1002.1-2001) for Unicode.
- *
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
- *
- * In fixed-width output devices, Latin characters all occupy a single
- * "cell" position of equal width, whereas ideographic CJK characters
- * occupy two such cells. Interoperability between terminal-line
- * applications and (teletype-style) character terminals using the
- * UTF-8 encoding requires agreement on which character should advance
- * the cursor by how many cell positions. No established formal
- * standards exist at present on which Unicode character shall occupy
- * how many cell positions on character terminals. These routines are
- * a first attempt of defining such behavior based on simple rules
- * applied to data provided by the Unicode Consortium.
- *
- * For some graphical characters, the Unicode standard explicitly
- * defines a character-cell width via the definition of the East Asian
- * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
- * In all these cases, there is no ambiguity about which width a
- * terminal shall use. For characters in the East Asian Ambiguous (A)
- * class, the width choice depends purely on a preference of backward
- * compatibility with either historic CJK or Western practice.
- * Choosing single-width for these characters is easy to justify as
- * the appropriate long-term solution, as the CJK practice of
- * displaying these characters as double-width comes from historic
- * implementation simplicity (8-bit encoded characters were displayed
- * single-width and 16-bit ones double-width, even for Greek,
- * Cyrillic, etc.) and not any typographic considerations.
- *
- * Much less clear is the choice of width for the Not East Asian
- * (Neutral) class. Existing practice does not dictate a width for any
- * of these characters. It would nevertheless make sense
- * typographically to allocate two character cells to characters such
- * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
- * represented adequately with a single-width glyph. The following
- * routines at present merely assign a single-cell width to all
- * neutral characters, in the interest of simplicity. This is not
- * entirely satisfactory and should be reconsidered before
- * establishing a formal standard in this area. At the moment, the
- * decision which Not East Asian (Neutral) characters should be
- * represented by double-width glyphs cannot yet be answered by
- * applying a simple rule from the Unicode database content. Setting
- * up a proper standard for the behavior of UTF-8 character terminals
- * will require a careful analysis not only of each Unicode character,
- * but also of each presentation form, something the author of these
- * routines has avoided to do so far.
- *
- * http://www.unicode.org/unicode/reports/tr11/
- *
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- *
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-
-#include "term-internal.h"
-
-struct interval {
- wchar_t first;
- wchar_t last;
-};
-
-/* auxiliary function for binary search in interval table */
-static int bisearch(wchar_t ucs, const struct interval *table, int max) {
- int min = 0;
- int mid;
-
- if (ucs < table[0].first || ucs > table[max].last)
- return 0;
- while (max >= min) {
- mid = (min + max) / 2;
- if (ucs > table[mid].last)
- min = mid + 1;
- else if (ucs < table[mid].first)
- max = mid - 1;
- else
- return 1;
- }
-
- return 0;
-}
-
-
-/* The following two functions define the column width of an ISO 10646
- * character as follows:
- *
- * - The null character (U+0000) has a column width of 0.
- *
- * - Other C0/C1 control characters and DEL will lead to a return
- * value of -1.
- *
- * - Non-spacing and enclosing combining characters (general
- * category code Mn or Me in the Unicode database) have a
- * column width of 0.
- *
- * - SOFT HYPHEN (U+00AD) has a column width of 1.
- *
- * - Other format characters (general category code Cf in the Unicode
- * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
- *
- * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
- * have a column width of 0.
- *
- * - Spacing characters in the East Asian Wide (W) or East Asian
- * Full-width (F) category as defined in Unicode Technical
- * Report #11 have a column width of 2.
- *
- * - All remaining characters (including all printable
- * ISO 8859-1 and WGL4 characters, Unicode control characters,
- * etc.) have a column width of 1.
- *
- * This implementation assumes that wchar_t characters are encoded
- * in ISO 10646.
- */
-
-int mk_wcwidth(wchar_t ucs)
-{
- /* sorted list of non-overlapping intervals of non-spacing characters */
- /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
- static const struct interval combining[] = {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
- { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
- { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
- { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
- { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
- { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
- { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
- { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
- { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
- { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
- { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
- { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
- { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
- { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
- { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
- { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
- { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
- { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
- { 0xE0100, 0xE01EF }
- };
-
- /* test for 8-bit control characters */
- if (ucs == 0)
- return 0;
- if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
- return -1;
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, combining,
- sizeof(combining) / sizeof(struct interval) - 1))
- return 0;
-
- /* if we arrive here, ucs is not a combining or C0/C1 control character */
-
- return 1 +
- (ucs >= 0x1100 &&
- (ucs <= 0x115f || /* Hangul Jamo init. consonants */
- ucs == 0x2329 || ucs == 0x232a ||
- (ucs >= 0x2e80 && ucs <= 0xa4cf &&
- ucs != 0x303f) || /* CJK ... Yi */
- (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
- (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
- (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
- (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
- (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
- (ucs >= 0xffe0 && ucs <= 0xffe6) ||
- (ucs >= 0x20000 && ucs <= 0x2fffd) ||
- (ucs >= 0x30000 && ucs <= 0x3fffd)));
-}
-
-
-int mk_wcswidth(const wchar_t *pwcs, size_t n)
-{
- int w, width = 0;
-
- for (;*pwcs && n-- > 0; pwcs++)
- if ((w = mk_wcwidth(*pwcs)) < 0)
- return -1;
- else
- width += w;
-
- return width;
-}
-
-
-/*
- * The following functions are the same as mk_wcwidth() and
- * mk_wcswidth(), except that spacing characters in the East Asian
- * Ambiguous (A) category as defined in Unicode Technical Report #11
- * have a column width of 2. This variant might be useful for users of
- * CJK legacy encodings who want to migrate to UCS without changing
- * the traditional terminal character-width behaviour. It is not
- * otherwise recommended for general use.
- */
-int mk_wcwidth_cjk(wchar_t ucs)
-{
- /* sorted list of non-overlapping intervals of East Asian Ambiguous
- * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
- static const struct interval ambiguous[] = {
- { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
- { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
- { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
- { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
- { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
- { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
- { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
- { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
- { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
- { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
- { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
- { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
- { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
- { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
- { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
- { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
- { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
- { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
- { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
- { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
- { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
- { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
- { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
- { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
- { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
- { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
- { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
- { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
- { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
- { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
- { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
- { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
- { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
- { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
- { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
- { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
- { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
- { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
- { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
- { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
- { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
- { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
- { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
- { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
- { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
- { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
- { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
- { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
- { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
- { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
- { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
- { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
- };
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, ambiguous,
- sizeof(ambiguous) / sizeof(struct interval) - 1))
- return 2;
-
- return mk_wcwidth(ucs);
-}
-
-
-int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
-{
- int w, width = 0;
-
- for (;*pwcs && n-- > 0; pwcs++)
- if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
- return -1;
- else
- width += w;
-
- return width;
-}
diff --git a/src/libsystemd-terminal/term.h b/src/libsystemd-terminal/term.h
deleted file mode 100644
index 1a78a81184..0000000000
--- a/src/libsystemd-terminal/term.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "util.h"
-
-typedef struct term_color term_color;
-typedef struct term_attr term_attr;
-
-typedef struct term_utf8 term_utf8;
-typedef struct term_seq term_seq;
-typedef struct term_parser term_parser;
-
-typedef struct term_screen term_screen;
-
-/*
- * Ageing
- */
-
-typedef uint64_t term_age_t;
-
-#define TERM_AGE_NULL 0
-
-/*
- * Attributes
- */
-
-enum {
- /* special color-codes */
- TERM_CCODE_DEFAULT, /* default foreground/background color */
- TERM_CCODE_256, /* 256color code */
- TERM_CCODE_RGB, /* color is specified as RGB */
-
- /* dark color-codes */
- TERM_CCODE_BLACK,
- TERM_CCODE_RED,
- TERM_CCODE_GREEN,
- TERM_CCODE_YELLOW,
- TERM_CCODE_BLUE,
- TERM_CCODE_MAGENTA,
- TERM_CCODE_CYAN,
- TERM_CCODE_WHITE, /* technically: light grey */
-
- /* light color-codes */
- TERM_CCODE_LIGHT_BLACK = TERM_CCODE_BLACK + 8, /* technically: dark grey */
- TERM_CCODE_LIGHT_RED = TERM_CCODE_RED + 8,
- TERM_CCODE_LIGHT_GREEN = TERM_CCODE_GREEN + 8,
- TERM_CCODE_LIGHT_YELLOW = TERM_CCODE_YELLOW + 8,
- TERM_CCODE_LIGHT_BLUE = TERM_CCODE_BLUE + 8,
- TERM_CCODE_LIGHT_MAGENTA = TERM_CCODE_MAGENTA + 8,
- TERM_CCODE_LIGHT_CYAN = TERM_CCODE_CYAN + 8,
- TERM_CCODE_LIGHT_WHITE = TERM_CCODE_WHITE + 8,
-
- TERM_CCODE_CNT,
-};
-
-struct term_color {
- uint8_t ccode;
- uint8_t c256;
- uint8_t red;
- uint8_t green;
- uint8_t blue;
-};
-
-struct term_attr {
- term_color fg; /* foreground color */
- term_color bg; /* background color */
-
- unsigned int bold : 1; /* bold font */
- unsigned int italic : 1; /* italic font */
- unsigned int underline : 1; /* underline text */
- unsigned int inverse : 1; /* inverse fg/bg */
- unsigned int protect : 1; /* protect from erase */
- unsigned int blink : 1; /* blink text */
- unsigned int hidden : 1; /* hidden */
-};
-
-void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette);
-
-/*
- * UTF-8
- */
-
-struct term_utf8 {
- uint32_t chars[5];
- uint32_t ucs4;
-
- unsigned int i_bytes : 3;
- unsigned int n_bytes : 3;
- unsigned int valid : 1;
-};
-
-size_t term_utf8_decode(term_utf8 *p, uint32_t **out_buf, char c);
-
-/*
- * Parsers
- */
-
-int term_parser_new(term_parser **out, bool host);
-term_parser *term_parser_free(term_parser *parser);
-int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw);
-
-#define _term_parser_free_ _cleanup_(term_parser_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_parser*, term_parser_free);
-
-/*
- * Screens
- */
-
-enum {
- TERM_KBDMOD_IDX_SHIFT,
- TERM_KBDMOD_IDX_CTRL,
- TERM_KBDMOD_IDX_ALT,
- TERM_KBDMOD_IDX_LINUX,
- TERM_KBDMOD_IDX_CAPS,
- TERM_KBDMOD_CNT,
-
- TERM_KBDMOD_SHIFT = 1 << TERM_KBDMOD_IDX_SHIFT,
- TERM_KBDMOD_CTRL = 1 << TERM_KBDMOD_IDX_CTRL,
- TERM_KBDMOD_ALT = 1 << TERM_KBDMOD_IDX_ALT,
- TERM_KBDMOD_LINUX = 1 << TERM_KBDMOD_IDX_LINUX,
- TERM_KBDMOD_CAPS = 1 << TERM_KBDMOD_IDX_CAPS,
-};
-
-typedef int (*term_screen_write_fn) (term_screen *screen, void *userdata, const void *buf, size_t size);
-typedef int (*term_screen_cmd_fn) (term_screen *screen, void *userdata, unsigned int cmd, const term_seq *seq);
-
-int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data);
-term_screen *term_screen_ref(term_screen *screen);
-term_screen *term_screen_unref(term_screen *screen);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_screen*, term_screen_unref);
-
-unsigned int term_screen_get_width(term_screen *screen);
-unsigned int term_screen_get_height(term_screen *screen);
-uint64_t term_screen_get_age(term_screen *screen);
-
-int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size);
-int term_screen_feed_keyboard(term_screen *screen,
- const uint32_t *keysyms,
- size_t n_syms,
- uint32_t ascii,
- const uint32_t *ucs4,
- unsigned int mods);
-int term_screen_resize(term_screen *screen, unsigned int width, unsigned int height);
-void term_screen_soft_reset(term_screen *screen);
-void term_screen_hard_reset(term_screen *screen);
-
-int term_screen_set_answerback(term_screen *screen, const char *answerback);
-
-int term_screen_draw(term_screen *screen,
- int (*draw_fn) (term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width),
- void *userdata,
- uint64_t *fb_age);
diff --git a/src/libsystemd-terminal/test-term-page.c b/src/libsystemd-terminal/test-term-page.c
deleted file mode 100644
index d59139b62d..0000000000
--- a/src/libsystemd-terminal/test-term-page.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Terminal Page/Line/Cell/Char Tests
- * This tests internals of terminal page, line, cell and char handling. It
- * relies on some implementation details, so it might need to be updated if
- * those internals are changed. They should be fairly obvious, though.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "macro.h"
-#include "term-internal.h"
-
-#define MY_ASSERT_VALS __FILE__, __LINE__, __PRETTY_FUNCTION__
-#define MY_ASSERT_FORW _FILE, _LINE, _FUNC
-#define MY_ASSERT_ARGS const char *_FILE, int _LINE, const char *_FUNC
-#define MY_ASSERT(expr) \
- do { \
- if (_unlikely_(!(expr))) \
- log_assert_failed(#expr, _FILE, _LINE, _FUNC); \
- } while (false) \
-
-/*
- * Character Tests
- *
- * These tests rely on some implementation details of term_char_t, including
- * the way we pack characters and the internal layout of "term_char_t". These
- * tests have to be updated once we change the implementation.
- */
-
-#define PACK(v1, v2, v3) \
- TERM_CHAR_INIT( \
- (((((uint64_t)v1) & 0x1fffffULL) << 43) | \
- ((((uint64_t)v2) & 0x1fffffULL) << 22) | \
- ((((uint64_t)v3) & 0x1fffffULL) << 1) | \
- 0x1) \
- )
-#define PACK1(v1) PACK2((v1), 0x110000)
-#define PACK2(v1, v2) PACK3((v1), (v2), 0x110000)
-#define PACK3(v1, v2, v3) PACK((v1), (v2), (v3))
-
-static void test_term_char_misc(void) {
- term_char_t c, t;
-
- /* test TERM_CHAR_NULL handling */
-
- c = TERM_CHAR_NULL; /* c is NULL */
- assert_se(term_char_same(c, TERM_CHAR_NULL));
- assert_se(term_char_equal(c, TERM_CHAR_NULL));
- assert_se(term_char_is_null(c));
- assert_se(term_char_is_null(TERM_CHAR_NULL));
- assert_se(!term_char_is_allocated(c));
-
- /* test single char handling */
-
- t = term_char_dup_append(c, 'A'); /* t is >A< now */
- assert_se(!term_char_same(c, t));
- assert_se(!term_char_equal(c, t));
- assert_se(!term_char_is_allocated(t));
- assert_se(!term_char_is_null(t));
-
- /* test basic combined char handling */
-
- t = term_char_dup_append(t, '~');
- t = term_char_dup_append(t, '^'); /* t is >A~^< now */
- assert_se(!term_char_same(c, t));
- assert_se(!term_char_is_allocated(t));
- assert_se(!term_char_is_null(t));
-
- c = term_char_dup_append(c, 'A');
- c = term_char_dup_append(c, '~');
- c = term_char_dup_append(c, '^'); /* c is >A~^< now */
- assert_se(term_char_same(c, t));
- assert_se(term_char_equal(c, t));
-
- /* test more than 2 comb-chars so the chars are allocated */
-
- t = term_char_dup_append(t, '`'); /* t is >A~^`< now */
- c = term_char_dup_append(c, '`'); /* c is >A~^`< now */
- assert_se(!term_char_same(c, t));
- assert_se(term_char_equal(c, t));
-
- /* test dup_append() on allocated chars */
-
- term_char_free(t);
- t = term_char_dup_append(c, '"'); /* t is >A~^`"< now */
- assert_se(!term_char_same(c, t));
- assert_se(!term_char_equal(c, t));
- c = term_char_merge(c, '"'); /* c is >A~^`"< now */
- assert_se(!term_char_same(c, t));
- assert_se(term_char_equal(c, t));
-
- term_char_free(t);
- term_char_free(c);
-}
-
-static void test_term_char_packing(void) {
- uint32_t seqs[][1024] = {
- { -1 },
- { 0, -1 },
- { 'A', '~', -1 },
- { 'A', '~', 0, -1 },
- { 'A', '~', 'a', -1 },
- };
- term_char_t res[] = {
- TERM_CHAR_NULL,
- PACK1(0),
- PACK2('A', '~'),
- PACK3('A', '~', 0),
- PACK3('A', '~', 'a'),
- };
- uint32_t next;
- unsigned int i, j;
- term_char_t c = TERM_CHAR_NULL;
-
- /*
- * This creates term_char_t objects based on the data in @seqs and
- * compares the result to @res. Only basic packed types are tested, no
- * allocations are done.
- */
-
- for (i = 0; i < ELEMENTSOF(seqs); ++i) {
- for (j = 0; j < ELEMENTSOF(seqs[i]); ++j) {
- next = seqs[i][j];
- if (next == (uint32_t)-1)
- break;
-
- c = term_char_merge(c, next);
- }
-
- assert_se(!memcmp(&c, &res[i], sizeof(c)));
- c = term_char_free(c);
- }
-}
-
-static void test_term_char_allocating(void) {
- uint32_t seqs[][1024] = {
- { 0, -1 },
- { 'A', '~', -1 },
- { 'A', '~', 0, -1 },
- { 'A', '~', 'a', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', 0, '^', -1 },
- /* exceeding implementation-defined soft-limit of 64 */
- { 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', -1 },
- };
- term_char_t res[] = {
- PACK1(0),
- PACK2('A', '~'),
- PACK3('A', '~', 0),
- PACK3('A', '~', 'a'),
- TERM_CHAR_NULL, /* allocated */
- TERM_CHAR_NULL, /* allocated */
- TERM_CHAR_NULL, /* allocated */
- };
- uint32_t str[][1024] = {
- { 0, -1 },
- { 'A', '~', -1 },
- { 'A', '~', 0, -1 },
- { 'A', '~', 'a', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', 0, '^', -1 },
- { 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', -1 },
- };
- size_t n;
- uint32_t next;
- unsigned int i, j;
- const uint32_t *t;
-
- /*
- * This builds term_char_t objects based on the data in @seqs. It
- * compares the result to @res for packed chars, otherwise it requires
- * them to be allocated.
- * After that, we resolve the UCS-4 string and compare it to the
- * expected strings in @str.
- */
-
- for (i = 0; i < ELEMENTSOF(seqs); ++i) {
- _term_char_free_ term_char_t c = TERM_CHAR_NULL;
-
- for (j = 0; j < ELEMENTSOF(seqs[i]); ++j) {
- next = seqs[i][j];
- if (next == (uint32_t)-1)
- break;
-
- c = term_char_merge(c, next);
- }
-
- /* we use TERM_CHAR_NULL as marker for allocated chars here */
- if (term_char_is_null(res[i]))
- assert_se(term_char_is_allocated(c));
- else
- assert_se(!memcmp(&c, &res[i], sizeof(c)));
-
- t = term_char_resolve(c, &n, NULL);
- for (j = 0; j < ELEMENTSOF(str[i]); ++j) {
- next = str[i][j];
- if (next == (uint32_t)-1)
- break;
-
- assert_se(t[j] == next);
- }
-
- assert_se(n == j);
- }
-}
-
-/*
- * Line Tests
- *
- * The following tests work on term_line objects and verify their behavior when
- * we modify them. To verify and set line layouts, we have two simple helpers
- * to avoid harcoding the cell-verification all the time:
- * line_set(): Set a line to a given layout
- * line_assert(): Verify that a line has a given layout
- *
- * These functions take the line-layout encoded as a string and verify it
- * against, or set it on, a term_line object. The format used to describe a
- * line looks like this:
- * example: "| | A | | | | | | 10 *AB* |"
- *
- * The string describes the contents of all cells of a line, separated by
- * pipe-symbols ('|'). Whitespace are ignored, the leading pipe-symbol is
- * optional.
- * The description of each cell can contain an arbitrary amount of characters
- * in the range 'A'-'Z', 'a'-'z'. All those are combined and used as term_char_t
- * on this cell. Any numbers in the description are combined and are used as
- * cell-age.
- * The occurrence of a '*'-symbol marks the cell as bold, '/' marks it as italic.
- * You can use those characters multiple times, but only the first one has an
- * effect.
- * For further symbols, see parse_attr().
- *
- * Therefore, the following descriptions are equivalent:
- * 1) "| | /A* | | | | | | 10 *AB* |"
- * 2) "| | /A** | | | | | | 10 *AB* |"
- * 3) "| | A* // | | | | | | 10 *AB* |"
- * 4) "| | A* // | | | | | | 1 *AB* 0 |"
- * 5) "| | A* // | | | | | | A1B0* |"
- *
- * The parser isn't very strict about placement of alpha/numerical characters,
- * but simply appends all found chars. Don't make use of that feature! It's
- * just a stupid parser to simplify these tests. Make them readable!
- */
-
-static void parse_attr(char c, term_char_t *ch, term_attr *attr, term_age_t *age) {
- switch (c) {
- case ' ':
- /* ignore */
- break;
- case '0' ... '9':
- /* increase age */
- *age = *age * 10;
- *age = *age + c - '0';
- break;
- case 'A' ... 'Z':
- case 'a' ... 'z':
- /* add to character */
- *ch = term_char_merge(*ch, c);
- break;
- case '*':
- attr->bold = true;
- break;
- case '/':
- attr->italic = true;
- break;
- default:
- assert_se(0);
- break;
- }
-}
-
-static void cell_assert(MY_ASSERT_ARGS, term_cell *c, term_char_t ch, const term_attr *attr, term_age_t age) {
- MY_ASSERT(term_char_equal(c->ch, ch));
- MY_ASSERT(!memcmp(&c->attr, attr, sizeof(*attr)));
- MY_ASSERT(c->age == age);
-}
-#define CELL_ASSERT(_cell, _ch, _attr, _age) cell_assert(MY_ASSERT_VALS, (_cell), (_ch), (_attr), (_age))
-
-static void line_assert(MY_ASSERT_ARGS, term_line *l, const char *str, unsigned int fill) {
- unsigned int cell_i;
- term_char_t ch = TERM_CHAR_NULL;
- term_attr attr = { };
- term_age_t age = TERM_AGE_NULL;
- char c;
-
- assert_se(l->fill == fill);
-
- /* skip leading whitespace */
- while (*str == ' ')
- ++str;
-
- /* skip leading '|' */
- if (*str == '|')
- ++str;
-
- cell_i = 0;
- while ((c = *str++)) {
- switch (c) {
- case '|':
- /* end of cell-description; compare it */
- assert_se(cell_i < l->n_cells);
- cell_assert(MY_ASSERT_FORW,
- &l->cells[cell_i],
- ch,
- &attr,
- age);
-
- ++cell_i;
- ch = term_char_free(ch);
- zero(attr);
- age = TERM_AGE_NULL;
- break;
- default:
- parse_attr(c, &ch, &attr, &age);
- break;
- }
- }
-
- assert_se(cell_i == l->n_cells);
-}
-#define LINE_ASSERT(_line, _str, _fill) line_assert(MY_ASSERT_VALS, (_line), (_str), (_fill))
-
-static void line_set(term_line *l, unsigned int pos, const char *str, bool insert_mode) {
- term_char_t ch = TERM_CHAR_NULL;
- term_attr attr = { };
- term_age_t age = TERM_AGE_NULL;
- char c;
-
- while ((c = *str++))
- parse_attr(c, &ch, &attr, &age);
-
- term_line_write(l, pos, ch, 1, &attr, age, insert_mode);
-}
-
-static void line_resize(term_line *l, unsigned int width, const term_attr *attr, term_age_t age) {
- assert_se(term_line_reserve(l, width, attr, age, width) >= 0);
- term_line_set_width(l, width);
-}
-
-static void test_term_line_misc(void) {
- term_line *l;
-
- assert_se(term_line_new(&l) >= 0);
- assert_se(!term_line_free(l));
-
- assert_se(term_line_new(NULL) < 0);
- assert_se(!term_line_free(NULL));
-
- assert_se(term_line_new(&l) >= 0);
- assert_se(l->n_cells == 0);
- assert_se(l->fill == 0);
- assert_se(term_line_reserve(l, 16, NULL, 0, 0) >= 0);
- assert_se(l->n_cells == 16);
- assert_se(l->fill == 0);
- assert_se(term_line_reserve(l, 512, NULL, 0, 0) >= 0);
- assert_se(l->n_cells == 512);
- assert_se(l->fill == 0);
- assert_se(term_line_reserve(l, 16, NULL, 0, 0) >= 0);
- assert_se(l->n_cells == 512);
- assert_se(l->fill == 0);
- assert_se(!term_line_free(l));
-}
-
-static void test_term_line_ops(void) {
- term_line *l;
- term_attr attr_regular = { };
- term_attr attr_bold = { .bold = true };
- term_attr attr_italic = { .italic = true };
-
- assert_se(term_line_new(&l) >= 0);
- line_resize(l, 8, NULL, 0);
- assert_se(l->n_cells == 8);
-
- LINE_ASSERT(l, "| | | | | | | | |", 0);
-
- term_line_write(l, 4, TERM_CHAR_NULL, 0, NULL, TERM_AGE_NULL, 0);
- LINE_ASSERT(l, "| | | | | | | | |", 5);
-
- term_line_write(l, 1, PACK1('A'), 1, NULL, TERM_AGE_NULL, 0);
- LINE_ASSERT(l, "| |A| | | | | | |", 5);
-
- term_line_write(l, 8, PACK2('A', 'B'), 1, NULL, TERM_AGE_NULL, 0);
- LINE_ASSERT(l, "| |A| | | | | | |", 5);
-
- term_line_write(l, 7, PACK2('A', 'B'), 1, &attr_regular, 10, 0);
- LINE_ASSERT(l, "| |A| | | | | | 10 AB |", 8);
-
- term_line_write(l, 7, PACK2('A', 'B'), 1, &attr_bold, 10, 0);
- LINE_ASSERT(l, "| |A| | | | | | 10 *AB* |", 8);
-
- term_line_reset(l, NULL, TERM_AGE_NULL);
-
- LINE_ASSERT(l, "| | | | | | | | |", 0);
- line_set(l, 2, "*wxyz* 8", 0);
- line_set(l, 3, "/wxyz/ 8", 0);
- LINE_ASSERT(l, "| | | *wxyz* 8 | /wxyz/ 8 | | | | |", 4);
- line_set(l, 2, "*abc* 9", true);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 9 | 9 |", 5);
- line_set(l, 7, "*abc* 10", true);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 9 | *abc* 10 |", 8);
-
- term_line_erase(l, 6, 1, NULL, 11, 0);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 11 | *abc* 10 |", 8);
- term_line_erase(l, 6, 2, &attr_italic, 12, 0);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 12 // | 12 // |", 6);
- term_line_erase(l, 7, 2, &attr_regular, 13, 0);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 12 // | 13 |", 6);
- term_line_delete(l, 1, 3, &attr_bold, 14);
- LINE_ASSERT(l, "| | /wxyz/ 14 | 14 | 14 // | 14 | 14 ** | 14 ** | 14 ** |", 3);
- term_line_insert(l, 2, 2, &attr_regular, 15);
- LINE_ASSERT(l, "| | /wxyz/ 14 | 15 | 15 | 15 | 15 // | 15 | 15 ** |", 5);
-
- assert_se(!term_line_free(l));
-}
-
-int main(int argc, char *argv[]) {
- test_term_char_misc();
- test_term_char_packing();
- test_term_char_allocating();
-
- test_term_line_misc();
- test_term_line_ops();
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/test-term-parser.c b/src/libsystemd-terminal/test-term-parser.c
deleted file mode 100644
index e40b267b1c..0000000000
--- a/src/libsystemd-terminal/test-term-parser.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Terminal Parser Tests
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "utf8.h"
-
-static void test_term_utf8_invalid(void) {
- term_utf8 p = { };
- uint32_t *res;
- size_t len;
-
- len = term_utf8_decode(NULL, NULL, 0);
- assert_se(!len);
-
- len = term_utf8_decode(&p, NULL, 0);
- assert_se(len == 1);
-
- res = NULL;
- len = term_utf8_decode(NULL, &res, 0);
- assert_se(!len);
- assert_se(res != NULL);
- assert_se(!*res);
-
- len = term_utf8_decode(&p, &res, 0);
- assert_se(len == 1);
- assert_se(res != NULL);
- assert_se(!*res);
-
- len = term_utf8_decode(&p, &res, 0xCf);
- assert_se(len == 0);
- assert_se(res != NULL);
- assert_se(!*res);
-
- len = term_utf8_decode(&p, &res, 0);
- assert_se(len == 2);
- assert_se(res != NULL);
- assert_se(res[0] == 0xCf && res[1] == 0);
-}
-
-static void test_term_utf8_range(void) {
- term_utf8 p = { };
- uint32_t *res;
- char u8[4];
- uint32_t i, j;
- size_t ulen, len;
-
- /* Convert all ucs-4 chars to utf-8 and back */
-
- for (i = 0; i < 0x10FFFF; ++i) {
- ulen = utf8_encode_unichar(u8, i);
- if (!ulen)
- continue;
-
- for (j = 0; j < ulen; ++j) {
- len = term_utf8_decode(&p, &res, u8[j]);
- if (len < 1) {
- assert_se(j + 1 != ulen);
- continue;
- }
-
- assert_se(j + 1 == ulen);
- assert_se(len == 1 && *res == i);
- assert_se(i <= 127 || ulen >= 2);
- }
- }
-}
-
-static void test_term_utf8_mix(void) {
- static const char source[] = {
- 0x00, /* normal 0 */
- 0xC0, 0x80, /* overlong 0 */
- 0xC0, 0x81, /* overlong 1 */
- 0xE0, 0x80, 0x81, /* overlong 1 */
- 0xF0, 0x80, 0x80, 0x81, /* overlong 1 */
- 0xC0, 0x00, /* invalid continuation */
- 0xC0, 0xC0, 0x81, /* invalid continuation with a following overlong 1 */
- 0xF8, 0x80, 0x80, 0x80, 0x81, /* overlong 1 with 5 bytes */
- 0xE0, 0x80, 0xC0, 0x81, /* invalid 3-byte followed by valid 2-byte */
- 0xF0, 0x80, 0x80, 0xC0, 0x81, /* invalid 4-byte followed by valid 2-byte */
- };
- static const uint32_t result[] = {
- 0x0000,
- 0x0000,
- 0x0001,
- 0x0001,
- 0x0001,
- 0x00C0, 0x0000,
- 0x00C0, 0x0001,
- 0x00F8, 0x0080, 0x0080, 0x0080, 0x0081,
- 0x00E0, 0x0080, 0x0001,
- 0x00F0, 0x0080, 0x0080, 0x0001,
- };
- term_utf8 p = { };
- uint32_t *res;
- unsigned int i, j;
- size_t len;
-
- for (i = 0, j = 0; i < sizeof(source); ++i) {
- len = term_utf8_decode(&p, &res, source[i]);
- if (len < 1)
- continue;
-
- assert_se(j + len <= ELEMENTSOF(result));
- assert_se(!memcmp(res, &result[j], sizeof(uint32_t) * len));
- j += len;
- }
-
- assert_se(j == ELEMENTSOF(result));
-}
-
-int main(int argc, char *argv[]) {
- test_term_utf8_invalid();
- test_term_utf8_range();
- test_term_utf8_mix();
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/test-unifont.c b/src/libsystemd-terminal/test-unifont.c
deleted file mode 100644
index 2366d38574..0000000000
--- a/src/libsystemd-terminal/test-unifont.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Test Unifont Helper
- * This tries opening the binary unifont glyph-array and renders some glyphs.
- * The glyphs are then compared to hard-coded glyphs.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "macro.h"
-#include "unifont-def.h"
-#include "unifont.h"
-
-static void render(char *w, const unifont_glyph *g) {
- unsigned int i, j;
- const uint8_t *d = g->data;
-
- for (j = 0; j < 16; ++j) {
- for (i = 0; i < 8 * g->cwidth; ++i) {
- if (d[i / 8] & (1 << (7 - i % 8)))
- *w++ = '#';
- else
- *w++ = ' ';
- }
- *w++ = '\n';
- d += g->stride;
- }
-
- *w++ = 0;
-}
-
-static void test_unifont(void) {
- char buf[4096];
- unifont_glyph g;
- unifont *u;
-
- assert_se(unifont_new(&u) >= 0);
-
- /* lookup invalid font */
- assert_se(unifont_lookup(u, &g, 0xffffffffU) < 0);
-
- /* lookup and render 'A' */
- assert_se(unifont_lookup(u, &g, 'A') >= 0);
- assert_se(g.width == 8);
- assert_se(g.height == 16);
- assert_se(g.stride >= 1);
- assert_se(g.cwidth == 1);
- assert_se(g.data != NULL);
- render(buf, &g);
- assert_se(!strcmp(buf,
- " \n"
- " \n"
- " \n"
- " \n"
- " ## \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " ###### \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " \n"
- " \n"
- ));
-
- /* lookup and render '什' */
- assert_se(unifont_lookup(u, &g, 0x4ec0) >= 0);
- assert_se(g.width == 16);
- assert_se(g.height == 16);
- assert_se(g.stride >= 2);
- assert_se(g.cwidth == 2);
- assert_se(g.data != NULL);
- render(buf, &g);
- assert_se(!strcmp(buf,
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " ## # \n"
- " ## ########## \n"
- " # # # \n"
- "# # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- ));
-
- unifont_unref(u);
-}
-
-int main(int argc, char **argv) {
- if (access(UNIFONT_PATH, F_OK))
- return 77;
-
- test_unifont();
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/unifont-def.h b/src/libsystemd-terminal/unifont-def.h
deleted file mode 100644
index 3847a2cf6c..0000000000
--- a/src/libsystemd-terminal/unifont-def.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "sparse-endian.h"
-#include "util.h"
-
-typedef struct unifont_header unifont_header;
-typedef struct unifont_glyph_header unifont_glyph_header;
-
-/*
- * Unifont: On-disk data
- * Conventional font-formats have the problem that you have to pre-render each
- * glyph before you can use it. If you just need one glyph, you have to parse
- * the font-file until you found that glyph.
- * GNU-Unifont is a bitmap font with very good Unicode coverage. All glyphs are
- * (n*8)x16 bitmaps. Our on-disk data stores all those glyphs pre-compiled with
- * fixed offsets. Therefore, the font-file can be mmap()ed and all glyphs can
- * be accessed in O(1) (because all glyphs have the same size and thus their
- * offsets can be easily computed). This guarantees, that the kernel only loads
- * the pages that are really accessed. Thus, we have a far lower overhead than
- * traditional font-formats like BDF. Furthermore, the backing file is read-only
- * and can be shared in memory between multiple users.
- *
- * The binary-format starts with a fixed header:
- *
- * | 2bytes | 2bytes | 2bytes | 2bytes |
- *
- * +-----------------------------------+
- * | SIGNATURE | 8 bytes
- * +-----------------+-----------------+
- * | COMPAT FLAGS | INCOMPAT FLAGS | 8 bytes
- * +-----------------+--------+--------+
- * | HEADER SIZE |GH-SIZE |G-STRIDE| 8 bytes
- * +-----------------+--------+--------+
- * | GLYPH BODY SIZE | 8 bytes
- * +-----------------------------------+
- *
- * * The 8 bytes signature must be set to the ASCII string "DVDHRMUF".
- * * The 4 bytes compatible-flags field contains flags for new features that
- * might be added in the future and which are compatible to older parsers.
- * * The 4 bytes incompatible-flags field contains flags for new features that
- * might be added in the future and which are incompatible to old parses.
- * Thus, if you encounter an unknown bit set, you must abort!
- * * The 4 bytes header-size field contains the size of the header in bytes. It
- * must be at least 32 (the size of this fixed header). If new features are
- * added, it might be increased. It can also be used to add padding to the
- * end of the header.
- * * The 2 bytes glyph-header-size field specifies the size of each glyph
- * header in bytes (see below).
- * * The 2 bytes glyph-stride field specifies the stride of each line of glyph
- * data in "bytes per line".
- * * The 8 byte glyph-body-size field defines the size of each glyph body in
- * bytes.
- *
- * After the header, the file can contain padding bytes, depending on the
- * header-size field. Everything beyond the header+padding is treated as a big
- * array of glyphs. Each glyph looks like this:
- *
- * | 1 byte |
- *
- * +-----------------------------------+
- * | WIDTH | 1 byte
- * +-----------------------------------+
- * ~ PADDING ~
- * +-----------------------------------+
- * ~ ~
- * ~ ~
- * ~ DATA ~
- * ~ ~
- * ~ ~
- * +-----------------------------------+
- *
- * * The first byte specifies the width of the glyph. If it is 0, the glyph
- * must be treated as non-existent.
- * All glyphs are "8*n" pixels wide and "16" pixels high. The width-field
- * specifies the width multiplier "n".
- * * After the width field padding might be added. This depends on the global
- * glyph-header-size field. It defines the total size of each glyph-header.
- * After the glyph-header+padding, the data-field starts.
- * * The data-field contains a byte-array of bitmap data. The array is always
- * as big as specified in the global glyph-body-size header field. This might
- * include padding.
- * The array contains all 16 lines of bitmap information for that glyph. The
- * stride is given in the global glyph-stride header field. This can be used
- * to add padding after each line.
- * Each line is encoded as 1 bit per pixel bitmap. That is, each byte encodes
- * data for 8 pixels (left most pixel is encoded in the LSB, right most pixel
- * in the MSB). The width field defines the number of bytes valid per line.
- * For width==1, you need 1 byte to encode the 8 pixels. The stride defines
- * where the encoding of the next line starts.
- * Any data beyond the 16th line is padding and must be ignored.
- */
-
-/* path to binary file */
-#define UNIFONT_PATH "/usr/share/systemd/unifont-glyph-array.bin"
-
-/* header-size of version 1 */
-#define UNIFONT_HEADER_SIZE_MIN 32
-
-struct unifont_header {
- /* fields available in version 1 */
- uint8_t signature[8];
- le32_t compatible_flags;
- le32_t incompatible_flags;
- le32_t header_size;
- le16_t glyph_header_size;
- le16_t glyph_stride;
- le64_t glyph_body_size;
-} _packed_;
-
-struct unifont_glyph_header {
- /* fields available in version 1 */
- uint8_t width;
-} _packed_;
diff --git a/src/libsystemd-terminal/unifont.c b/src/libsystemd-terminal/unifont.c
deleted file mode 100644
index 0da81e8ff2..0000000000
--- a/src/libsystemd-terminal/unifont.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-/*
- * Unifont
- * This implements the unifont glyph-array parser and provides it via a simple
- * API to the caller. No heavy transformations are performed so glyph-lookups
- * stay as fast as possible.
- */
-
-#include <endian.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include "macro.h"
-#include "unifont-def.h"
-#include "unifont.h"
-#include "util.h"
-
-struct unifont {
- unsigned long ref;
-
- int fd;
- const uint8_t *map;
- size_t size;
-
- unifont_header header;
- const void *glyphs; /* unaligned! */
- size_t n_glyphs;
- size_t glyphsize;
-};
-
-static int unifont_fetch_header(unifont *u) {
- unifont_header h = { };
- uint64_t glyphsize;
-
- if (u->size < UNIFONT_HEADER_SIZE_MIN)
- return -EBFONT;
-
- assert_cc(sizeof(h) >= UNIFONT_HEADER_SIZE_MIN);
- memcpy(&h, u->map, UNIFONT_HEADER_SIZE_MIN);
-
- h.compatible_flags = le32toh(h.compatible_flags);
- h.incompatible_flags = le32toh(h.incompatible_flags);
- h.header_size = le32toh(h.header_size);
- h.glyph_header_size = le16toh(h.glyph_header_size);
- h.glyph_stride = le16toh(h.glyph_stride);
- h.glyph_body_size = le64toh(h.glyph_body_size);
-
- if (memcmp(h.signature, "DVDHRMUF", 8))
- return -EBFONT;
- if (h.incompatible_flags != 0)
- return -EBFONT;
- if (h.header_size < UNIFONT_HEADER_SIZE_MIN || h.header_size > u->size)
- return -EBFONT;
- if (h.glyph_header_size + h.glyph_body_size < h.glyph_header_size)
- return -EBFONT;
- if (h.glyph_stride * 16ULL > h.glyph_body_size)
- return -EBFONT;
-
- glyphsize = h.glyph_header_size + h.glyph_body_size;
-
- if (glyphsize == 0 || glyphsize > u->size - h.header_size) {
- u->n_glyphs = 0;
- } else {
- u->glyphs = u->map + h.header_size;
- u->n_glyphs = (u->size - h.header_size) / glyphsize;
- u->glyphsize = glyphsize;
- }
-
- memcpy(&u->header, &h, sizeof(h));
- return 0;
-}
-
-static int unifont_fetch_glyph(unifont *u, unifont_glyph_header *out_header, const void **out_body, uint32_t ucs4) {
- unifont_glyph_header glyph_header = { };
- const void *glyph_body = NULL;
- const uint8_t *p;
-
- if (ucs4 >= u->n_glyphs)
- return -ENOENT;
-
- p = u->glyphs;
-
- /* copy glyph-header data */
- p += ucs4 * u->glyphsize;
- memcpy(&glyph_header, p, MIN(sizeof(glyph_header), u->header.glyph_header_size));
-
- /* copy glyph-body pointer */
- p += u->header.glyph_header_size;
- glyph_body = p;
-
- if (glyph_header.width < 1)
- return -ENOENT;
- if (glyph_header.width > u->header.glyph_stride)
- return -EBFONT;
-
- memcpy(out_header, &glyph_header, sizeof(glyph_header));
- *out_body = glyph_body;
- return 0;
-}
-
-int unifont_new(unifont **out) {
- _cleanup_(unifont_unrefp) unifont *u = NULL;
- struct stat st;
- int r;
-
- assert_return(out, -EINVAL);
-
- u = new0(unifont, 1);
- if (!u)
- return -ENOMEM;
-
- u->ref = 1;
- u->fd = -1;
- u->map = MAP_FAILED;
-
- u->fd = open(UNIFONT_PATH, O_RDONLY | O_CLOEXEC | O_NOCTTY);
- if (u->fd < 0)
- return -errno;
-
- r = fstat(u->fd, &st);
- if (r < 0)
- return -errno;
-
- u->size = st.st_size;
- u->map = mmap(NULL, u->size, PROT_READ, MAP_PRIVATE, u->fd, 0);
- if (u->map == MAP_FAILED)
- return -errno;
-
- r = unifont_fetch_header(u);
- if (r < 0)
- return r;
-
- *out = u;
- u = NULL;
- return 0;
-}
-
-unifont *unifont_ref(unifont *u) {
- if (!u || !u->ref)
- return NULL;
-
- ++u->ref;
-
- return u;
-}
-
-unifont *unifont_unref(unifont *u) {
- if (!u || !u->ref || --u->ref)
- return NULL;
-
- if (u->map != MAP_FAILED)
- munmap((void*)u->map, u->size);
- u->fd = safe_close(u->fd);
- free(u);
-
- return NULL;
-}
-
-unsigned int unifont_get_width(unifont *u) {
- assert(u);
-
- return 8U;
-}
-
-unsigned int unifont_get_height(unifont *u) {
- assert(u);
-
- return 16U;
-}
-
-unsigned int unifont_get_stride(unifont *u) {
- assert(u);
-
- return u->header.glyph_stride;
-}
-
-int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4) {
- unifont_glyph_header h = { };
- const void *b = NULL;
- unifont_glyph g = { };
- int r;
-
- assert_return(u, -EINVAL);
-
- r = unifont_fetch_glyph(u, &h, &b, ucs4);
- if (r < 0)
- return r;
-
- g.width = h.width * 8U;
- g.height = 16U;
- g.stride = u->header.glyph_stride;
- g.cwidth = h.width;
- g.data = b;
-
- if (out)
- memcpy(out, &g, sizeof(g));
- return 0;
-}
-
-void unifont_fallback(unifont_glyph *out) {
- static const uint8_t fallback_data[] = {
- /* unifont 0xfffd '�' (unicode replacement character) */
- 0x00, 0x00, 0x00, 0x7e,
- 0x66, 0x5a, 0x5a, 0x7a,
- 0x76, 0x76, 0x7e, 0x76,
- 0x76, 0x7e, 0x00, 0x00,
- };
-
- assert(out);
-
- out->width = 8;
- out->height = 16;
- out->stride = 1;
- out->cwidth = 1;
- out->data = fallback_data;
-}
diff --git a/src/libsystemd-terminal/unifont.h b/src/libsystemd-terminal/unifont.h
deleted file mode 100644
index 74ee5ecb3c..0000000000
--- a/src/libsystemd-terminal/unifont.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@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/>.
-***/
-
-#pragma once
-
-#include <stdint.h>
-
-typedef struct unifont unifont;
-typedef struct unifont_glyph unifont_glyph;
-
-/*
- * Unifont
- * The unifont API provides a glyph-lookup for bitmap fonts which can be used
- * as fallback if no system-font is available or if you don't want to deal with
- * full font renderers.
- */
-
-struct unifont_glyph {
- unsigned int width;
- unsigned int height;
- unsigned int stride;
- unsigned int cwidth;
- const void *data; /* unaligned! */
-};
-
-int unifont_new(unifont **out);
-unifont *unifont_ref(unifont *u);
-unifont *unifont_unref(unifont *u);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(unifont*, unifont_unref);
-
-unsigned int unifont_get_width(unifont *u);
-unsigned int unifont_get_height(unifont *u);
-unsigned int unifont_get_stride(unifont *u);
-int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4);
-void unifont_fallback(unifont_glyph *out);
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index c53666ddd0..773c264cf0 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -256,9 +256,7 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) {
name_list = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset);
KDBUS_FOREACH(name, name_list, cmd.list_size) {
-
struct kdbus_item *item;
- const char *entry_name = NULL;
if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id) {
char *n;
@@ -275,15 +273,15 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) {
previous_id = name->id;
}
- KDBUS_ITEM_FOREACH(item, name, items)
- if (item->type == KDBUS_ITEM_OWNED_NAME)
- entry_name = item->name.name;
-
- if (entry_name && service_name_is_valid(entry_name)) {
- r = strv_extend(x, entry_name);
- if (r < 0) {
- r = -ENOMEM;
- goto fail;
+ KDBUS_ITEM_FOREACH(item, name, items) {
+ if (item->type == KDBUS_ITEM_OWNED_NAME) {
+ if (service_name_is_valid(item->name.name)) {
+ r = strv_extend(x, item->name.name);
+ if (r < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
}
}
}
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index 9db86adb7f..a6b05eb88d 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -551,9 +551,8 @@ int bus_pcap_header(size_t snaplen, FILE *f) {
hdr.snaplen = (uint32_t) snaplen;
fwrite(&hdr, 1, sizeof(hdr), f);
- fflush(f);
- return 0;
+ return fflush_and_check(f);
}
int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
@@ -598,7 +597,5 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
snaplen -= w;
}
- fflush(f);
-
- return 0;
+ return fflush_and_check(f);
}
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
index 2d18a4e6c1..402d43d66d 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.c
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -75,14 +75,19 @@ int bus_gvariant_get_size(const char *signature) {
case SD_BUS_TYPE_STRUCT_BEGIN:
case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
- char t[n-1];
-
- memcpy(t, p + 1, n - 2);
- t[n - 2] = 0;
-
- r = bus_gvariant_get_size(t);
- if (r < 0)
- return r;
+ if (n == 2) {
+ /* unary type () has fixed size of 1 */
+ r = 1;
+ } else {
+ char t[n-1];
+
+ memcpy(t, p + 1, n - 2);
+ t[n - 2] = 0;
+
+ r = bus_gvariant_get_size(t);
+ if (r < 0)
+ return r;
+ }
sum += r;
break;
diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c
index e2f4550c7e..c2233d0cf3 100644
--- a/src/libsystemd/sd-bus/bus-introspect.c
+++ b/src/libsystemd/sd-bus/bus-introspect.c
@@ -179,10 +179,10 @@ int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_b
assert(reply);
fputs("</node>\n", i->f);
- fflush(i->f);
- if (ferror(i->f))
- return -ENOMEM;
+ r = fflush_and_check(i->f);
+ if (r < 0)
+ return r;
r = sd_bus_message_new_method_return(m, &q);
if (r < 0)
@@ -204,8 +204,6 @@ void introspect_free(struct introspect *i) {
if (i->f)
fclose(i->f);
- if (i->introspection)
- free(i->introspection);
-
+ free(i->introspection);
zero(*i);
}
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index 6ac5ebc3da..d72535b8bc 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -212,7 +212,9 @@ static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter
return r;
add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t);
- } if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) {
+ }
+
+ if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) {
/* As well as array of simple strings of any kinds */
r = sd_bus_message_enter_container(m, type, contents);
diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c
index 132b37526e..18c36ce243 100644
--- a/src/libsystemd/sd-bus/bus-match.c
+++ b/src/libsystemd/sd-bus/bus-match.c
@@ -861,8 +861,7 @@ int bus_match_parse(
if (r < 0)
goto fail;
- free(value);
- value = NULL;
+ value = mfree(value);
} else
u = 0;
@@ -914,6 +913,7 @@ char *bus_match_to_string(struct bus_match_component *components, unsigned n_com
char *buffer = NULL;
size_t size = 0;
unsigned i;
+ int r;
if (n_components <= 0)
return strdup("");
@@ -942,8 +942,8 @@ char *bus_match_to_string(struct bus_match_component *components, unsigned n_com
fputc('\'', f);
}
- fflush(f);
- if (ferror(f))
+ r = fflush_and_check(f);
+ if (r < 0)
return NULL;
return buffer;
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 18685be8ff..006e4a2b58 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -2209,7 +2209,14 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
assert(!c->need_offsets || i == c->n_offsets);
assert(c->need_offsets || n_variable == 0);
- if (n_variable <= 0) {
+ if (isempty(c->signature)) {
+ /* The unary type is encoded as fixed 1 byte padding */
+ a = message_extend_body(m, 1, 1, add_offset, false);
+ if (!a)
+ return -ENOMEM;
+
+ *a = 0;
+ } else if (n_variable <= 0) {
int alignment = 1;
/* Structures with fixed-size members only have to be
@@ -2899,18 +2906,20 @@ static int bus_message_close_header(sd_bus_message *m) {
signature = strempty(m->root_container.signature);
l = strlen(signature);
- sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l, 1);
- d = message_extend_body(m, 1, 1 + l + sz, false, true);
+ sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l + 2, 1);
+ d = message_extend_body(m, 1, 1 + l + 2 + sz, false, true);
if (!d)
return -ENOMEM;
*(uint8_t*) d = 0;
- memcpy((uint8_t*) d + 1, signature, l);
+ *((uint8_t*) d + 1) = SD_BUS_TYPE_STRUCT_BEGIN;
+ memcpy((uint8_t*) d + 2, signature, l);
+ *((uint8_t*) d + 1 + l + 1) = SD_BUS_TYPE_STRUCT_END;
- bus_gvariant_write_word_le((uint8_t*) d + 1 + l, sz, sizeof(struct bus_header) + m->fields_size);
+ bus_gvariant_write_word_le((uint8_t*) d + 1 + l + 2, sz, sizeof(struct bus_header) + m->fields_size);
m->footer = d;
- m->footer_accessible = 1 + l + sz;
+ m->footer_accessible = 1 + l + 2 + sz;
} else {
m->header->dbus1.fields_size = m->fields_size;
m->header->dbus1.body_size = m->body_size;
@@ -3814,6 +3823,14 @@ static int build_struct_offsets(
assert(n_offsets);
if (isempty(signature)) {
+ /* Unary type is encoded as *fixed* 1 byte padding */
+ r = message_peek_body(m, &m->rindex, 1, 1, &q);
+ if (r < 0)
+ return r;
+
+ if (*(uint8_t *) q != 0)
+ return -EBADMSG;
+
*item_size = 0;
*offsets = NULL;
*n_offsets = 0;
@@ -3954,12 +3971,6 @@ static int enter_struct_or_dict_entry(
if (r < 0)
return r;
- } else if (c->item_size <= 0) {
-
- /* gvariant empty struct */
- *item_size = 0;
- *offsets = NULL;
- *n_offsets = 0;
} else
/* gvariant with contents */
return build_struct_offsets(m, contents, c->item_size, item_size, offsets, n_offsets);
@@ -4146,7 +4157,14 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
w->before = before;
w->begin = m->rindex;
- w->end = m->rindex + c->item_size;
+
+ /* Unary type has fixed size of 1, but virtual size of 0 */
+ if (BUS_MESSAGE_IS_GVARIANT(m) &&
+ type == SD_BUS_TYPE_STRUCT &&
+ isempty(signature))
+ w->end = m->rindex + 0;
+ else
+ w->end = m->rindex + c->item_size;
w->array_size = array_size;
w->item_size = item_size;
@@ -4756,7 +4774,6 @@ _public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) {
r = sd_bus_message_skip(m, s);
if (r < 0)
return r;
- assert(r != 0);
r = sd_bus_message_exit_container(m);
if (r < 0)
@@ -5164,11 +5181,21 @@ int bus_message_parse_fields(sd_bus_message *m) {
return -EBADMSG;
if (*p == 0) {
+ size_t l;
char *c;
- /* We found the beginning of the signature string, yay! */
+ /* We found the beginning of the signature
+ * string, yay! We require the body to be a
+ * structure, so verify it and then strip the
+ * opening/closing brackets. */
+
+ l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz);
+ if (l < 2 ||
+ p[1] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END)
+ return -EBADMSG;
- c = strndup(p + 1, ((char*) m->footer + m->footer_accessible) - p - (1 + sz));
+ c = strndup(p + 1 + 1, l - 2);
if (!c)
return -ENOMEM;
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index a973bca84c..c25293e5e9 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -173,6 +173,7 @@ static int add_subtree_to_set(
sd_bus *bus,
const char *prefix,
struct node *n,
+ bool skip_subhierarchies,
Set *s,
sd_bus_error *error) {
@@ -204,11 +205,13 @@ static int add_subtree_to_set(
if (r < 0 && r != -EEXIST)
return r;
- r = add_subtree_to_set(bus, prefix, i, s, error);
- if (r < 0)
- return r;
- if (bus->nodes_modified)
- return 0;
+ if (!skip_subhierarchies || !i->object_managers) {
+ r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error);
+ if (r < 0)
+ return r;
+ if (bus->nodes_modified)
+ return 0;
+ }
}
return 0;
@@ -218,6 +221,7 @@ static int get_child_nodes(
sd_bus *bus,
const char *prefix,
struct node *n,
+ bool skip_subhierarchies,
Set **_s,
sd_bus_error *error) {
@@ -233,7 +237,7 @@ static int get_child_nodes(
if (!s)
return -ENOMEM;
- r = add_subtree_to_set(bus, prefix, n, s, error);
+ r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error);
if (r < 0) {
set_free_free(s);
return r;
@@ -900,7 +904,7 @@ static int process_introspect(
assert(n);
assert(found_object);
- r = get_child_nodes(bus, m->path, n, &s, &error);
+ r = get_child_nodes(bus, m->path, n, false, &s, &error);
if (r < 0)
return bus_maybe_reply_error(m, r, &error);
if (bus->nodes_modified)
@@ -1166,7 +1170,7 @@ static int process_get_managed_objects(
if (require_fallback || !n->object_managers)
return 0;
- r = get_child_nodes(bus, m->path, n, &s, &error);
+ r = get_child_nodes(bus, m->path, n, true, &s, &error);
if (r < 0)
return r;
if (bus->nodes_modified)
@@ -1475,6 +1479,32 @@ void bus_node_gc(sd_bus *b, struct node *n) {
free(n);
}
+static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
+ struct node *n;
+
+ assert(bus);
+ assert(path);
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n) {
+ char *prefix;
+
+ prefix = alloca(strlen(path) + 1);
+ OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
+ n = hashmap_get(bus->nodes, prefix);
+ if (n)
+ break;
+ }
+ }
+
+ while (n && !n->object_managers)
+ n = n->parent;
+
+ if (out)
+ *out = n;
+ return !!n;
+}
+
static int bus_add_object(
sd_bus *bus,
sd_bus_slot **slot,
@@ -2277,6 +2307,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
BUS_DONT_DESTROY(bus);
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ struct node *object_manager;
int r;
/*
@@ -2297,11 +2328,17 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ r = bus_find_parent_object_manager(bus, &object_manager, path);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ESRCH;
+
do {
bus->nodes_modified = false;
m = sd_bus_message_unref(m);
- r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
+ r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
if (r < 0)
return r;
@@ -2440,6 +2477,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
BUS_DONT_DESTROY(bus);
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ struct node *object_manager;
int r;
/*
@@ -2460,11 +2498,17 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
+ r = bus_find_parent_object_manager(bus, &object_manager, path);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ESRCH;
+
do {
bus->nodes_modified = false;
m = sd_bus_message_unref(m);
- r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
+ r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
if (r < 0)
return r;
@@ -2596,6 +2640,7 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch
BUS_DONT_DESTROY(bus);
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ struct node *object_manager;
char **i;
int r;
@@ -2609,11 +2654,17 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch
if (strv_isempty(interfaces))
return 0;
+ r = bus_find_parent_object_manager(bus, &object_manager, path);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ESRCH;
+
do {
bus->nodes_modified = false;
m = sd_bus_message_unref(m);
- r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
+ r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
if (r < 0)
return r;
@@ -2673,6 +2724,7 @@ _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const c
_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ struct node *object_manager;
int r;
assert_return(bus, -EINVAL);
@@ -2685,7 +2737,13 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path,
if (strv_isempty(interfaces))
return 0;
- r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
+ r = bus_find_parent_object_manager(bus, &object_manager, path);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ESRCH;
+
+ r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c
index 15c10da7e9..03e83d08a1 100644
--- a/src/libsystemd/sd-bus/busctl-introspect.c
+++ b/src/libsystemd/sd-bus/busctl-introspect.c
@@ -462,9 +462,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
}
}
- free(argument_type);
- free(argument_direction);
- argument_type = argument_direction = NULL;
+ argument_type = mfree(argument_type);
+ argument_direction = mfree(argument_direction);
}
state = STATE_METHOD;
@@ -604,8 +603,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (!strextend(&context->member_signature, argument_type, NULL))
return log_oom();
- free(argument_type);
- argument_type = NULL;
+ argument_type = mfree(argument_type);
}
state = STATE_SIGNAL;
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
index 6aaaf0e5ec..a1f0f30d6c 100644
--- a/src/libsystemd/sd-bus/busctl.c
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -656,28 +656,15 @@ static int member_compare_func(const void *a, const void *b) {
assert(x->type);
assert(y->type);
- if (!x->interface && y->interface)
- return -1;
- if (x->interface && !y->interface)
- return 1;
- if (x->interface && y->interface) {
- d = strcmp(x->interface, y->interface);
- if (d != 0)
- return d;
- }
+ d = strcmp_ptr(x->interface, y->interface);
+ if (d != 0)
+ return d;
d = strcmp(x->type, y->type);
if (d != 0)
return d;
- if (!x->name && y->name)
- return -1;
- if (x->name && !y->name)
- return 1;
- if (x->name && y->name)
- return strcmp(x->name, y->name);
-
- return 0;
+ return strcmp_ptr(x->name, y->name);
}
static int member_compare_funcp(const void *a, const void *b) {
@@ -1697,6 +1684,7 @@ static int help(void) {
" --acquired Only show acquired names\n"
" --activatable Only show activatable names\n"
" --match=MATCH Only show matching messages\n"
+ " --size=SIZE Maximum length of captured packet\n"
" --list Don't show tree, but simple object path list\n"
" --quiet Don't show method call reply\n"
" --verbose Show result values in long format\n"
@@ -1837,7 +1825,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_SIZE: {
off_t o;
- r = parse_size(optarg, 0, &o);
+ r = parse_size(optarg, 1024, &o);
if (r < 0) {
log_error("Failed to parse size: %s", optarg);
return r;
@@ -2018,15 +2006,15 @@ int main(int argc, char *argv[]) {
}
}
+ r = sd_bus_set_bus_client(bus, true);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set bus client: %m");
+ goto finish;
+ }
+
if (arg_address)
r = sd_bus_set_address(bus, arg_address);
else {
- r = sd_bus_set_bus_client(bus, true);
- if (r < 0) {
- log_error_errno(r, "Failed to set bus client: %m");
- goto finish;
- }
-
switch (arg_transport) {
case BUS_TRANSPORT_LOCAL:
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 0ca225c617..0f075907d5 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -73,13 +73,9 @@ static void bus_close_fds(sd_bus *b) {
detach_io_events(b);
- if (b->input_fd >= 0)
- safe_close(b->input_fd);
-
- if (b->output_fd >= 0 && b->output_fd != b->input_fd)
+ if (b->input_fd != b->output_fd)
safe_close(b->output_fd);
-
- b->input_fd = b->output_fd = -1;
+ b->output_fd = b->input_fd = safe_close(b->input_fd);
}
static void bus_reset_queues(sd_bus *b) {
@@ -88,15 +84,13 @@ static void bus_reset_queues(sd_bus *b) {
while (b->rqueue_size > 0)
sd_bus_message_unref(b->rqueue[--b->rqueue_size]);
- free(b->rqueue);
- b->rqueue = NULL;
+ b->rqueue = mfree(b->rqueue);
b->rqueue_allocated = 0;
while (b->wqueue_size > 0)
sd_bus_message_unref(b->wqueue[--b->wqueue_size]);
- free(b->wqueue);
- b->wqueue = NULL;
+ b->wqueue = mfree(b->wqueue);
b->wqueue_allocated = 0;
}
@@ -896,10 +890,9 @@ static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid
} else
b->nspid = 0;
- free(b->kernel);
- b->kernel = strdup("/sys/fs/kdbus/0-system/bus");
- if (!b->kernel)
- return -ENOMEM;
+ r = free_and_strdup(&b->kernel, "/sys/fs/kdbus/0-system/bus");
+ if (r < 0)
+ return r;
return 0;
}
@@ -909,15 +902,11 @@ static void bus_reset_parsed_address(sd_bus *b) {
zero(b->sockaddr);
b->sockaddr_size = 0;
- strv_free(b->exec_argv);
- free(b->exec_path);
- b->exec_path = NULL;
- b->exec_argv = NULL;
+ b->exec_argv = strv_free(b->exec_argv);
+ b->exec_path = mfree(b->exec_path);
b->server_id = SD_ID128_NULL;
- free(b->kernel);
- b->kernel = NULL;
- free(b->machine);
- b->machine = NULL;
+ b->kernel = mfree(b->kernel);
+ b->machine = mfree(b->machine);
b->nspid = 0;
}
diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c
index 9b7dd2e499..b078bdc5f6 100644
--- a/src/libsystemd/sd-bus/test-bus-gvariant.c
+++ b/src/libsystemd/sd-bus/test-bus-gvariant.c
@@ -59,7 +59,7 @@ static void test_bus_gvariant_is_fixed_size(void) {
static void test_bus_gvariant_get_size(void) {
assert_se(bus_gvariant_get_size("") == 0);
- assert_se(bus_gvariant_get_size("()") == 0);
+ assert_se(bus_gvariant_get_size("()") == 1);
assert_se(bus_gvariant_get_size("y") == 1);
assert_se(bus_gvariant_get_size("u") == 4);
assert_se(bus_gvariant_get_size("b") == 1);
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index 59deaea89f..b203707f27 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -134,6 +134,9 @@ int main(int argc, char *argv[]) {
r = sd_bus_message_append(m, "y(ty)y(yt)y", 8, 777ULL, 7, 9, 77, 7777ULL, 10);
assert_se(r >= 0);
+ r = sd_bus_message_append(m, "()");
+ assert_se(r >= 0);
+
r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3");
assert_se(r >= 0);
@@ -271,6 +274,9 @@ int main(int argc, char *argv[]) {
assert_se(r > 0);
assert_se(v == 10);
+ r = sd_bus_message_read(m, "()");
+ assert_se(r > 0);
+
r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d);
assert_se(r > 0);
assert_se(boolean);
@@ -350,7 +356,7 @@ int main(int argc, char *argv[]) {
assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0);
- r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y");
+ r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y()");
assert_se(r >= 0);
assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0);
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index 1db67ecfac..359984c7f3 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -153,7 +153,7 @@ static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error)
static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0);
+ assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -164,7 +164,7 @@ static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error
static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.test", NULL) >= 0);
+ assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -175,7 +175,7 @@ static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_err
static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), m->path) >= 0);
+ assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -186,7 +186,7 @@ static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *er
static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
- assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), m->path) >= 0);
+ assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 0);
r = sd_bus_reply_method_return(m, NULL);
assert_se(r >= 0);
@@ -228,6 +228,14 @@ static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, ch
return 1;
}
+static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+
+ if (object_path_startswith("/value/a", path))
+ assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", NULL));
+
+ return 1;
+}
+
static void *server(void *p) {
struct context *c = p;
sd_bus *bus = NULL;
@@ -246,7 +254,9 @@ static void *server(void *p) {
assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0);
+ assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0);
assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0);
+ assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 0);
assert_se(sd_bus_start(bus) >= 0);
diff --git a/src/libsystemd/sd-bus/test-bus-proxy.c b/src/libsystemd/sd-bus/test-bus-proxy.c
index 369c2f331c..aef768dc18 100644
--- a/src/libsystemd/sd-bus/test-bus-proxy.c
+++ b/src/libsystemd/sd-bus/test-bus-proxy.c
@@ -53,7 +53,9 @@ static int test_proxy_acquired(sd_bus_message *m, void *userdata, sd_bus_error *
static void test_proxy_matched(void) {
_cleanup_bus_flush_close_unref_ sd_bus *a = NULL;
+ _cleanup_free_ char *matchstr = NULL;
TestProxyMatch match = {};
+ const char *me;
int r;
/* open bus 'a' */
@@ -70,10 +72,17 @@ static void test_proxy_matched(void) {
r = sd_bus_start(a);
assert_se(r >= 0);
- r = sd_bus_add_match(a, NULL,
- "type='signal',"
- "member='NameAcquired'",
- test_proxy_acquired, &match);
+ r = sd_bus_get_unique_name(a, &me);
+ assert_se(r >= 0);
+
+ matchstr = strjoin("type='signal',"
+ "member='NameAcquired',"
+ "destination='",
+ me,
+ "'",
+ NULL);
+ assert_se(matchstr);
+ r = sd_bus_add_match(a, NULL, matchstr, test_proxy_acquired, &match);
assert_se(r >= 0);
r = sd_bus_get_unique_name(a, &match.sender);
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index 2e60433246..0ec9667744 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -1082,12 +1082,10 @@ int device_update_db(sd_device *device) {
return 0;
fail:
- log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty",
- path, device->devpath);
- unlink(path);
- unlink(path_tmp);
+ (void) unlink(path);
+ (void) unlink(path_tmp);
- return r;
+ return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
}
int device_delete_db(sd_device *device) {
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 76964aa0cc..754fb7614e 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -2572,9 +2572,12 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
assert_return(usec, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
- /* If we haven't run yet, just get the actual time */
- if (!dual_timestamp_is_set(&e->timestamp))
- return -ENODATA;
+ if (!dual_timestamp_is_set(&e->timestamp)) {
+ /* Implicitly fall back to now() if we never ran
+ * before and thus have no cached time. */
+ *usec = now(clock);
+ return 1;
+ }
switch (clock) {
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index b0ed2f2882..3ba62a6be9 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -262,6 +262,24 @@ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type,
return 0;
}
+int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) {
+ size_t size;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG);
+ if (r < 0)
+ return r;
+
+ r = add_rtattr(m, type, NULL, 0);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) {
int r;
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 9b68935245..2128329191 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -117,20 +117,30 @@ static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = {
};
static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = {
- [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32},
- [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32},
- [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32},
+ [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32},
+ [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG },
+ [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
};
static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = {
@@ -248,6 +258,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_
[NL_UNION_LINK_INFO_DATA_VETH] = "veth",
[NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy",
[NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan",
+ [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap",
[NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan",
[NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan",
[NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip",
@@ -274,6 +285,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_D
.types = rtnl_link_info_data_veth_types },
[NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
.types = rtnl_link_info_data_macvlan_types },
+ [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
+ .types = rtnl_link_info_data_macvlan_types },
[NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
.types = rtnl_link_info_data_ipvlan_types },
[NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types),
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index a210163241..bf7c641541 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -28,6 +28,7 @@ enum {
NETLINK_TYPE_U32, /* NLA_U32 */
NETLINK_TYPE_U64, /* NLA_U64 */
NETLINK_TYPE_STRING, /* NLA_STRING */
+ NETLINK_TYPE_FLAG, /* NLA_FLAG */
NETLINK_TYPE_IN_ADDR,
NETLINK_TYPE_ETHER_ADDR,
NETLINK_TYPE_CACHE_INFO,
@@ -73,6 +74,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_VETH,
NL_UNION_LINK_INFO_DATA_DUMMY,
NL_UNION_LINK_INFO_DATA_MACVLAN,
+ NL_UNION_LINK_INFO_DATA_MACVTAP,
NL_UNION_LINK_INFO_DATA_IPVLAN,
NL_UNION_LINK_INFO_DATA_VXLAN,
NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL,
diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c
index 38cbcfbccb..9867eec065 100644
--- a/src/libsystemd/sd-netlink/test-local-addresses.c
+++ b/src/libsystemd/sd-netlink/test-local-addresses.c
@@ -44,9 +44,8 @@ int main(int argc, char *argv[]) {
printf("Local Addresses:\n");
print_local_addresses(a, (unsigned) n);
- free(a);
+ a = mfree(a);
- a = NULL;
n = local_gateways(NULL, 0, AF_UNSPEC, &a);
assert_se(n >= 0);
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index 3616f4af1f..1e06f2251c 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -467,9 +467,9 @@ static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) {
} else
*w = 0;
- r = strv_extend(&list, l);
- if (r < 0)
- return log_oom();
+ r = strv_extend(&list, l);
+ if (r < 0)
+ return log_oom();
}
if (strv_isempty(list)) {
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 3b511bfaf4..cb4052fdd5 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -471,15 +471,25 @@ static int x11_write_data(Context *c) {
fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
fputs("EndSection\n", f);
- fflush(f);
- if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
+
+ if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
r = -errno;
- unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
- unlink(temp_path);
- return r;
- } else
- return 0;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ (void) unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
+
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return r;
}
static int vconsole_reload(sd_bus *bus) {
diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules
index 695d246370..71f9fe6c72 100644
--- a/src/login/70-power-switch.rules
+++ b/src/login/70-power-switch.rules
@@ -11,5 +11,7 @@ SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch"
+SUBSYSTEM=="input", KERNEL=="event*", \
+ SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="116", TAG+="power-switch"
LABEL="power_switch_end"
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 9709eca9bd..5fa98e069f 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -372,11 +372,9 @@ static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_mess
if (r < 0)
return r;
- free(*p);
- *p = strdup(s);
-
- if (!*p)
- return -ENOMEM;
+ r = free_and_strdup(p, s);
+ if (r < 0)
+ return r;
} else {
r = sd_bus_message_read_basic(m, contents[0], userdata);
if (r < 0)
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 96a20e27b9..6c05c11dbd 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -183,44 +183,6 @@ int manager_add_button(Manager *m, const char *name, Button **_button) {
return 0;
}
-int manager_watch_busname(Manager *m, const char *name) {
- char *n;
- int r;
-
- assert(m);
- assert(name);
-
- if (set_get(m->busnames, (char*) name))
- return 0;
-
- n = strdup(name);
- if (!n)
- return -ENOMEM;
-
- r = set_put(m->busnames, n);
- if (r < 0) {
- free(n);
- return r;
- }
-
- return 0;
-}
-
-void manager_drop_busname(Manager *m, const char *name) {
- Session *session;
- Iterator i;
-
- assert(m);
- assert(name);
-
- /* keep it if the name still owns a controller */
- HASHMAP_FOREACH(session, m->sessions, i)
- if (session_is_controller(session, name))
- return;
-
- free(set_remove(m->busnames, (char*) name));
-}
-
int manager_process_seat_device(Manager *m, struct udev_device *d) {
Device *device;
int r;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index e6371ff04d..1647bb293a 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -725,15 +725,13 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
log_warning("Existing logind session ID %s used by new audit session, ignoring", id);
audit_id = 0;
- free(id);
- id = NULL;
+ id = mfree(id);
}
}
if (!id) {
do {
- free(id);
- id = NULL;
+ id = mfree(id);
if (asprintf(&id, "c%lu", ++m->session_counter) < 0)
return -ENOMEM;
@@ -1816,17 +1814,22 @@ static int update_schedule_file(Manager *m) {
if (!isempty(m->wall_message))
fprintf(f, "WALL_MESSAGE=%s\n", t);
- (void) fflush_and_check(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
- log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
+ if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
r = -errno;
-
- (void) unlink(temp_path);
- (void) unlink("/run/systemd/shutdown/scheduled");
+ goto fail;
}
- return r;
+ return 0;
+
+fail:
+ (void) unlink(temp_path);
+ (void) unlink("/run/systemd/shutdown/scheduled");
+
+ return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m");
}
static int manager_scheduled_shutdown_handler(
@@ -2350,8 +2353,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
return r;
do {
- free(id);
- id = NULL;
+ id = mfree(id);
if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0)
return -ENOMEM;
@@ -2650,41 +2652,6 @@ int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error
return 0;
}
-int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *name, *old, *new;
- Manager *m = userdata;
- Session *session;
- Iterator i;
- int r;
- char *key;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "sss", &name, &old, &new);
- if (r < 0) {
- bus_log_parse_error(r);
- return r;
- }
-
- if (isempty(old) || !isempty(new))
- return 0;
-
- key = set_remove(m->busnames, (char*) old);
- if (!key)
- return 0;
-
- /* Drop all controllers owned by this name */
-
- free(key);
-
- HASHMAP_FOREACH(session, m->sessions, i)
- if (session_is_controller(session, old))
- session_drop_controller(session);
-
- return 0;
-}
-
int manager_send_changed(Manager *manager, const char *property, ...) {
char **l;
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index 855c85402c..a261e6a719 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -86,11 +86,11 @@ int inhibitor_save(Inhibitor *i) {
r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
if (r < 0)
- goto finish;
+ goto fail;
r = fopen_temporary(i->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fchmod(fileno(f), 0644);
@@ -128,19 +128,24 @@ int inhibitor_save(Inhibitor *i) {
if (i->fifo_path)
fprintf(f, "FIFO=%s\n", i->fifo_path);
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, i->state_file) < 0) {
+ if (rename(temp_path, i->state_file) < 0) {
r = -errno;
- unlink(i->state_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
+ return 0;
- return r;
+fail:
+ (void) unlink(i->state_file);
+
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
}
int inhibitor_start(Inhibitor *i) {
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index 495ec50be0..8d13a63688 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -93,11 +93,11 @@ int seat_save(Seat *s) {
r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
if (r < 0)
- goto finish;
+ goto fail;
r = fopen_temporary(s->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fchmod(fileno(f), 0644);
@@ -141,19 +141,24 @@ int seat_save(Seat *s) {
i->sessions_by_seat_next ? ' ' : '\n');
}
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, s->state_file) < 0) {
+ if (rename(temp_path, s->state_file) < 0) {
r = -errno;
- unlink(s->state_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
+ return 0;
- return r;
+fail:
+ (void) unlink(s->state_file);
+
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
}
int seat_load(Seat *s) {
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 45f4c09d3d..fc92f7f73b 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -165,11 +165,11 @@ int session_save(Session *s) {
r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
if (r < 0)
- goto finish;
+ goto fail;
r = fopen_temporary(s->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
assert(s->user);
@@ -217,7 +217,7 @@ int session_save(Session *s) {
escaped = cescape(s->remote_host);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "REMOTE_HOST=%s\n", escaped);
@@ -229,7 +229,7 @@ int session_save(Session *s) {
escaped = cescape(s->remote_user);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "REMOTE_USER=%s\n", escaped);
@@ -241,7 +241,7 @@ int session_save(Session *s) {
escaped = cescape(s->service);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "SERVICE=%s\n", escaped);
@@ -254,7 +254,7 @@ int session_save(Session *s) {
escaped = cescape(s->desktop);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "DESKTOP=%s\n", escaped);
@@ -282,21 +282,27 @@ int session_save(Session *s) {
if (s->controller)
fprintf(f, "CONTROLLER=%s\n", s->controller);
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, s->state_file) < 0) {
+ if (rename(temp_path, s->state_file) < 0) {
r = -errno;
- unlink(s->state_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save session data %s: %m", s->state_file);
+ return 0;
- return r;
+fail:
+ (void) unlink(s->state_file);
+
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save session data %s: %m", s->state_file);
}
+
int session_load(Session *s) {
_cleanup_free_ char *remote = NULL,
*seat = NULL,
@@ -630,6 +636,9 @@ int session_stop(Session *s, bool force) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ if (s->seat)
+ seat_evict_position(s->seat, s);
+
/* We are going down, don't care about FIFOs anymore */
session_remove_fifo(s);
@@ -666,6 +675,9 @@ int session_finalize(Session *s) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ if (s->seat)
+ seat_evict_position(s->seat, s);
+
/* Kill session devices */
while ((sd = hashmap_first(s->devices)))
session_device_free(sd);
@@ -1108,7 +1120,18 @@ static void session_release_controller(Session *s, bool notify) {
session_device_free(sd);
s->controller = NULL;
- manager_drop_busname(s->manager, name);
+ s->track = sd_bus_track_unref(s->track);
+}
+
+static int on_bus_track(sd_bus_track *track, void *userdata) {
+ Session *s = userdata;
+
+ assert(track);
+ assert(s);
+
+ session_drop_controller(s);
+
+ return 0;
}
int session_set_controller(Session *s, const char *sender, bool force) {
@@ -1127,8 +1150,13 @@ int session_set_controller(Session *s, const char *sender, bool force) {
if (!name)
return -ENOMEM;
- r = manager_watch_busname(s->manager, name);
- if (r)
+ s->track = sd_bus_track_unref(s->track);
+ r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_add_name(s->track, name);
+ if (r < 0)
return r;
/* When setting a session controller, we forcibly mute the VT and set
@@ -1141,7 +1169,7 @@ int session_set_controller(Session *s, const char *sender, bool force) {
* or reset the VT in case it crashed/exited, too. */
r = session_prepare_vt(s);
if (r < 0) {
- manager_drop_busname(s->manager, name);
+ s->track = sd_bus_track_unref(s->track);
return r;
}
@@ -1159,6 +1187,7 @@ void session_drop_controller(Session *s) {
if (!s->controller)
return;
+ s->track = sd_bus_track_unref(s->track);
session_release_controller(s, false);
session_save(s);
session_restore_vt(s);
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index b8565ebf51..d054c33cec 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -117,6 +117,7 @@ struct Session {
char *controller;
Hashmap *devices;
+ sd_bus_track *track;
LIST_FIELDS(Session, sessions_by_user);
LIST_FIELDS(Session, sessions_by_seat);
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 21d7268120..5d8a7571cd 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -116,11 +116,11 @@ static int user_save_internal(User *u) {
r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
if (r < 0)
- goto finish;
+ goto fail;
r = fopen_temporary(u->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
fchmod(fileno(f), 0644);
@@ -241,19 +241,24 @@ static int user_save_internal(User *u) {
fputc('\n', f);
}
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
- if (ferror(f) || rename(temp_path, u->state_file) < 0) {
+ if (rename(temp_path, u->state_file) < 0) {
r = -errno;
- unlink(u->state_file);
- unlink(temp_path);
+ goto fail;
}
-finish:
- if (r < 0)
- log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
+ return 0;
- return r;
+fail:
+ (void) unlink(u->state_file);
+
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
}
int user_save(User *u) {
diff --git a/src/login/logind.c b/src/login/logind.c
index e2fb496289..49a2811842 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -76,10 +76,7 @@ static Manager *manager_new(void) {
m->user_units = hashmap_new(&string_hash_ops);
m->session_units = hashmap_new(&string_hash_ops);
- m->busnames = set_new(&string_hash_ops);
-
- if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->busnames ||
- !m->user_units || !m->session_units)
+ if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
goto fail;
m->kill_exclude_users = strv_new("root", NULL);
@@ -141,8 +138,6 @@ static void manager_free(Manager *m) {
hashmap_free(m->user_units);
hashmap_free(m->session_units);
- set_free_free(m->busnames);
-
sd_event_source_unref(m->idle_action_event_source);
sd_event_source_unref(m->inhibit_timeout_source);
sd_event_source_unref(m->scheduled_shutdown_timeout_source);
@@ -629,17 +624,6 @@ static int manager_connect_bus(Manager *m) {
r = sd_bus_add_match(m->bus,
NULL,
"type='signal',"
- "sender='org.freedesktop.DBus',"
- "interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged',"
- "path='/org/freedesktop/DBus'",
- match_name_owner_changed, m);
- if (r < 0)
- return log_error_errno(r, "Failed to add match for NameOwnerChanged: %m");
-
- r = sd_bus_add_match(m->bus,
- NULL,
- "type='signal',"
"sender='org.freedesktop.systemd1',"
"interface='org.freedesktop.systemd1.Manager',"
"member='JobRemoved',"
diff --git a/src/login/logind.h b/src/login/logind.h
index ad437b72cb..ce99d75bc1 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -48,8 +48,6 @@ struct Manager {
Hashmap *inhibitors;
Hashmap *buttons;
- Set *busnames;
-
LIST_HEAD(Seat, seat_gc_queue);
LIST_HEAD(Session, session_gc_queue);
LIST_HEAD(User, user_gc_queue);
@@ -181,9 +179,6 @@ int manager_job_is_active(Manager *manager, const char *path);
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
-int manager_watch_busname(Manager *manager, const char *name);
-void manager_drop_busname(Manager *manager, const char *name);
-
int manager_set_lid_switch_ignore(Manager *m, usec_t until);
int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index dc42ffdc52..7658d7146d 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -825,13 +825,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
if (r < 0)
return r;
- if (!path_is_absolute(src) || !path_is_safe(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
+ if (!path_is_absolute(src))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
if (isempty(dest))
dest = src;
- else if (!path_is_absolute(dest) || !path_is_safe(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
+ else if (!path_is_absolute(dest))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
r = bus_verify_polkit_async(
message,
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 05fc4f849f..ab26803683 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -112,13 +112,13 @@ int machine_save(Machine *m) {
r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
if (r < 0)
- goto finish;
+ goto fail;
r = fopen_temporary(m->state_file, &f, &temp_path);
if (r < 0)
- goto finish;
+ goto fail;
- fchmod(fileno(f), 0644);
+ (void) fchmod(fileno(f), 0644);
fprintf(f,
"# This is private data. Do not parse.\n"
@@ -131,7 +131,7 @@ int machine_save(Machine *m) {
escaped = cescape(m->unit);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
@@ -146,7 +146,7 @@ int machine_save(Machine *m) {
escaped = cescape(m->service);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "SERVICE=%s\n", escaped);
}
@@ -157,7 +157,7 @@ int machine_save(Machine *m) {
escaped = cescape(m->root_directory);
if (!escaped) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
fprintf(f, "ROOT=%s\n", escaped);
}
@@ -195,16 +195,13 @@ int machine_save(Machine *m) {
r = fflush_and_check(f);
if (r < 0)
- goto finish;
+ goto fail;
if (rename(temp_path, m->state_file) < 0) {
r = -errno;
- goto finish;
+ goto fail;
}
- free(temp_path);
- temp_path = NULL;
-
if (m->unit) {
char *sl;
@@ -215,14 +212,15 @@ int machine_save(Machine *m) {
(void) symlink(m->name, sl);
}
-finish:
- if (temp_path)
- unlink(temp_path);
+ return 0;
- if (r < 0)
- log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
+fail:
+ (void) unlink(m->state_file);
- return r;
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
}
static void machine_unlink(Machine *m) {
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 7cb6ce77ac..66ed41087c 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1073,6 +1073,8 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
static int copy_files(int argc, char *argv[], void *userdata) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *abs_host_path = NULL;
+ char *dest, *host_path, *container_path;
sd_bus *bus = userdata;
bool copy_from;
int r;
@@ -1082,6 +1084,16 @@ static int copy_files(int argc, char *argv[], void *userdata) {
polkit_agent_open_if_enabled();
copy_from = streq(argv[0], "copy-from");
+ dest = argv[3] ?: argv[2];
+ host_path = copy_from ? dest : argv[2];
+ container_path = copy_from ? argv[2] : dest;
+
+ if (!path_is_absolute(host_path)) {
+ abs_host_path = path_make_absolute_cwd(host_path);
+ if (!abs_host_path)
+ return log_oom();
+ host_path = abs_host_path;
+ }
r = sd_bus_call_method(
bus,
@@ -1093,8 +1105,8 @@ static int copy_files(int argc, char *argv[], void *userdata) {
NULL,
"sss",
argv[1],
- argv[2],
- argv[3]);
+ copy_from ? container_path : host_path,
+ copy_from ? host_path : container_path);
if (r < 0) {
log_error("Failed to copy: %s", bus_error_message(&error, -r));
return r;
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 9d6c453dbc..c9b53dc3f6 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -227,7 +227,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
_cleanup_device_unref_ sd_device *d = NULL;
const char *on_color_operational, *off_color_operational,
*on_color_setup, *off_color_setup;
- char devid[2 + DECIMAL_STR_MAX(int)];
+ char devid[2 + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *t = NULL;
sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
@@ -976,7 +976,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
return log_warning_errno(r < 0 ? r : ERANGE,
"Failed to parse TTL \"%s\": %m", b);
- time = now(CLOCK_BOOTTIME);
+ time = now(clock_boottime_or_monotonic());
if (x < time)
continue;
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 4aa301b112..6288644a1a 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -248,7 +248,7 @@ static int dhcp_lease_lost(Link *link) {
address->in_addr.in = addr;
address->prefixlen = prefixlen;
- address_drop(address, link, &link_address_drop_handler);
+ address_drop(address, link, &link_address_drop_handler);
}
}
@@ -468,7 +468,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
if (link->network->dhcp_hostname) {
- const char *hostname;
+ const char *hostname = NULL;
if (!link->network->hostname)
r = sd_dhcp_lease_get_hostname(lease, &hostname);
@@ -620,8 +620,8 @@ int dhcp4_configure(Link *link) {
return r;
r = sd_dhcp_client_set_request_option(link->dhcp_client,
DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
- if (r < 0)
- return r;
+ if (r < 0)
+ return r;
}
if (link->network->dhcp_sendhost) {
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index f20f68b482..78e96c4e5b 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2059,10 +2059,9 @@ int link_update(Link *link, sd_netlink_message *m) {
link_free_carrier_maps(link);
- free(link->ifname);
- link->ifname = strdup(ifname);
- if (!link->ifname)
- return -ENOMEM;
+ r = free_and_strdup(&link->ifname, ifname);
+ if (r < 0)
+ return r;
r = link_new_carrier_maps(link);
if (r < 0)
@@ -2388,14 +2387,13 @@ int link_save(Link *link) {
}
return 0;
+
fail:
- log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
(void) unlink(link->state_file);
-
if (temp_path)
(void) unlink(temp_path);
- return r;
+ return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file);
}
static const char* const link_state_table[_LINK_STATE_MAX] = {
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index a5c2351cf9..09a929e0da 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -145,7 +145,9 @@ int manager_connect_bus(Manager *m) {
return log_error_errno(r, "Failed to install bus reconnect time event: %m");
return 0;
- } if (r < 0)
+ }
+
+ if (r < 0)
return r;
r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
@@ -818,10 +820,10 @@ int manager_save(Manager *m) {
return 0;
fail:
- log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
- unlink(m->state_file);
- unlink(temp_path);
- return r;
+ (void) unlink(m->state_file);
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file);
}
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index be76022bc7..9469160eba 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -29,6 +29,7 @@ NetDev.MTUBytes, config_parse_iec_size, 0,
NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id)
MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
@@ -53,6 +54,7 @@ VXLAN.UDPCheckSum, config_parse_bool, 0,
VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
+VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
@@ -61,7 +63,7 @@ Tun.Group, config_parse_string, 0,
Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tap.VnetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
+Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c
index c2c564935c..e17de793ce 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/networkd-netdev-macvlan.c
@@ -35,14 +35,20 @@ DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
- MacVlan *m = MACVLAN(netdev);
+ MacVlan *m;
int r;
assert(netdev);
- assert(m);
assert(link);
assert(netdev->ifname);
+ if (netdev->kind == NETDEV_KIND_MACVLAN)
+ m = MACVLAN(netdev);
+ else
+ m = MACVTAP(netdev);
+
+ assert(m);
+
if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) {
r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode);
if (r < 0)
@@ -53,14 +59,28 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net
}
static void macvlan_init(NetDev *n) {
- MacVlan *m = MACVLAN(n);
+ MacVlan *m;
assert(n);
+
+ if (n->kind == NETDEV_KIND_MACVLAN)
+ m = MACVLAN(n);
+ else
+ m = MACVTAP(n);
+
assert(m);
m->mode = _NETDEV_MACVLAN_MODE_INVALID;
}
+const NetDevVTable macvtap_vtable = {
+ .object_size = sizeof(MacVlan),
+ .init = macvlan_init,
+ .sections = "Match\0NetDev\0MACVTAP\0",
+ .fill_message_create = netdev_macvlan_fill_message_create,
+ .create_type = NETDEV_CREATE_STACKED,
+};
+
const NetDevVTable macvlan_vtable = {
.object_size = sizeof(MacVlan),
.init = macvlan_init,
diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/networkd-netdev-macvlan.h
index d61efc16d4..c491bfa312 100644
--- a/src/network/networkd-netdev-macvlan.h
+++ b/src/network/networkd-netdev-macvlan.h
@@ -41,6 +41,7 @@ struct MacVlan {
};
extern const NetDevVTable macvlan_vtable;
+extern const NetDevVTable macvtap_vtable;
const char *macvlan_mode_to_string(MacVlanMode d) _const_;
MacVlanMode macvlan_mode_from_string(const char *d) _pure_;
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index 7fd9ef584b..265e67b7e3 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -404,12 +404,6 @@ int config_parse_tunnel_address(const char *unit,
return 0;
}
-static const char* const ipv6_flowlabel_table[_NETDEV_IPV6_FLOWLABEL_MAX] = {
- [NETDEV_IPV6_FLOWLABEL_INHERIT] = "inherit",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ipv6_flowlabel, IPv6FlowLabel);
-
int config_parse_ipv6_flowlabel(const char* unit,
const char *filename,
unsigned line,
@@ -422,7 +416,6 @@ int config_parse_ipv6_flowlabel(const char* unit,
void *userdata) {
IPv6FlowLabel *ipv6_flowlabel = data;
Tunnel *t = userdata;
- IPv6FlowLabel s;
int k = 0;
int r;
@@ -431,12 +424,11 @@ int config_parse_ipv6_flowlabel(const char* unit,
assert(rvalue);
assert(ipv6_flowlabel);
- s = ipv6_flowlabel_from_string(rvalue);
- if (s != _NETDEV_IPV6_FLOWLABEL_INVALID) {
+ if (streq(rvalue, "inherit")) {
*ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL;
t->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
} else {
- r = config_parse_unsigned(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata);
+ r = config_parse_int(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata);
if (r >= 0) {
if (k > 0xFFFFF)
log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h
index 1fd2b94ae1..e4fa74aef4 100644
--- a/src/network/networkd-netdev-tunnel.h
+++ b/src/network/networkd-netdev-tunnel.h
@@ -45,6 +45,7 @@ struct Tunnel {
uint8_t encap_limit;
int family;
+ int ipv6_flowlabel;
unsigned ttl;
unsigned tos;
@@ -54,7 +55,6 @@ struct Tunnel {
union in_addr_union remote;
Ip6TnlMode ip6tnl_mode;
- IPv6FlowLabel ipv6_flowlabel;
bool pmtudisc;
bool copy_dscp;
@@ -90,9 +90,6 @@ int config_parse_tunnel_address(const char *unit,
void *data,
void *userdata);
-const char *ipv6_flowlabel_to_string(IPv6FlowLabel d) _const_;
-IPv6FlowLabel ipv6_flowlabel_from_string(const char *d) _pure_;
-
int config_parse_ipv6_flowlabel(const char *unit, const char *filename,
unsigned line, const char *section,
unsigned section_line, const char *lvalue,
diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c
index 2a5c5f0baa..2518e2732b 100644
--- a/src/network/networkd-netdev-vxlan.c
+++ b/src/network/networkd-netdev-vxlan.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright 2014 Susant Sahani <susant@redhat.com>
+ Copyright 2014 Susant Sahani
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -101,6 +101,12 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %m");
+ if (v->group_policy) {
+ r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GBP attribute: %m");
+ }
+
return r;
}
diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h
index e7d1306f13..4ec33946cc 100644
--- a/src/network/networkd-netdev-vxlan.h
+++ b/src/network/networkd-netdev-vxlan.h
@@ -50,6 +50,7 @@ struct VxLan {
bool udpcsum;
bool udp6zerocsumtx;
bool udp6zerocsumrx;
+ bool group_policy;
};
extern const NetDevVTable vxlan_vtable;
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index 6949b403c8..cd31387b41 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -34,6 +34,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BOND] = &bond_vtable,
[NETDEV_KIND_VLAN] = &vlan_vtable,
[NETDEV_KIND_MACVLAN] = &macvlan_vtable,
+ [NETDEV_KIND_MACVTAP] = &macvtap_vtable,
[NETDEV_KIND_IPVLAN] = &ipvlan_vtable,
[NETDEV_KIND_VXLAN] = &vxlan_vtable,
[NETDEV_KIND_IPIP] = &ipip_vtable,
@@ -56,6 +57,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BOND] = "bond",
[NETDEV_KIND_VLAN] = "vlan",
[NETDEV_KIND_MACVLAN] = "macvlan",
+ [NETDEV_KIND_MACVTAP] = "macvtap",
[NETDEV_KIND_IPVLAN] = "ipvlan",
[NETDEV_KIND_VXLAN] = "vxlan",
[NETDEV_KIND_IPIP] = "ipip",
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index a004f2fe5f..19fb5bb185 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -40,6 +40,7 @@ typedef enum NetDevKind {
NETDEV_KIND_BOND,
NETDEV_KIND_VLAN,
NETDEV_KIND_MACVLAN,
+ NETDEV_KIND_MACVTAP,
NETDEV_KIND_IPVLAN,
NETDEV_KIND_VXLAN,
NETDEV_KIND_IPIP,
@@ -161,6 +162,7 @@ DEFINE_CAST(BRIDGE, Bridge);
DEFINE_CAST(BOND, Bond);
DEFINE_CAST(VLAN, VLan);
DEFINE_CAST(MACVLAN, MacVlan);
+DEFINE_CAST(MACVTAP, MacVlan);
DEFINE_CAST(IPVLAN, IPVlan);
DEFINE_CAST(VXLAN, VxLan);
DEFINE_CAST(IPIP, Tunnel);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 83224d7109..8735b39581 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -31,6 +31,7 @@ Network.Bridge, config_parse_netdev, 0
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
Network.VLAN, config_parse_netdev, 0, 0
Network.MACVLAN, config_parse_netdev, 0, 0
+Network.MACVTAP, 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
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 6678b2c77a..3f7e77da3e 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -428,6 +428,7 @@ int config_parse_netdev(const char *unit,
break;
case NETDEV_KIND_VLAN:
case NETDEV_KIND_MACVLAN:
+ case NETDEV_KIND_MACVTAP:
case NETDEV_KIND_IPVLAN:
case NETDEV_KIND_VXLAN:
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c
index 1fc724f5a4..112d92a568 100644
--- a/src/network/networkd-wait-online-manager.c
+++ b/src/network/networkd-wait-online-manager.c
@@ -38,9 +38,15 @@ bool manager_ignore_link(Manager *m, Link *link) {
assert(m);
assert(link);
+ /* always ignore the loopback interface */
if (link->flags & IFF_LOOPBACK)
return true;
+ /* if interfaces are given on the command line, ignore all others */
+ if (m->interfaces && !strv_contains(m->interfaces, link->ifname))
+ return true;
+
+ /* ignore interfaces we explicitly are asked to ignore */
STRV_FOREACH(ignore, m->ignore)
if (fnmatch(*ignore, link->ifname, 0) == 0)
return true;
@@ -77,7 +83,7 @@ bool manager_all_configured(Manager *m) {
return false;
}
- if (streq(l->state, "configuring")) {
+ if (STR_IN_SET(l->state, "configuring", "pending")) {
log_debug("link %s is being processed by networkd",
l->ifname);
return false;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index cf77cfd220..0e74f20f1a 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -309,8 +309,7 @@ static void custom_mount_free_all(void) {
strv_free(m->lower);
}
- free(arg_custom_mounts);
- arg_custom_mounts = NULL;
+ arg_custom_mounts = mfree(arg_custom_mounts);
arg_n_custom_mounts = 0;
}
@@ -503,9 +502,8 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'u':
- free(arg_user);
- arg_user = strdup(optarg);
- if (!arg_user)
+ r = free_and_strdup(&arg_user, optarg);
+ if (r < 0)
return log_oom();
break;
@@ -562,8 +560,7 @@ static int parse_argv(int argc, char *argv[]) {
case 'M':
if (isempty(optarg)) {
- free(arg_machine);
- arg_machine = NULL;
+ arg_machine = mfree(arg_machine);
} else {
if (!machine_name_is_valid(optarg)) {
log_error("Invalid machine name: %s", optarg);
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 31db1aaf68..ab96cb231e 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -464,8 +464,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
}
}
- free(addresses);
- addresses = NULL;
+ addresses = mfree(addresses);
n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
if (n_addresses > 0) {
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index e1087b3219..63b4b36e88 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -44,7 +44,7 @@ int dns_type_from_string(const char *s) {
return sc->id;
}
-/* XXX: find an authorotative list of all pseudo types? */
+/* XXX: find an authoritative list of all pseudo types? */
bool dns_type_is_pseudo(int n) {
return IN_SET(n, DNS_TYPE_ANY, DNS_TYPE_AXFR, DNS_TYPE_IXFR, DNS_TYPE_OPT);
}
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index be52891681..9ffaf4b19f 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -152,7 +152,7 @@ void dns_cache_prune(DnsCache *c) {
break;
if (t <= 0)
- t = now(CLOCK_BOOTTIME);
+ t = now(clock_boottime_or_monotonic());
if (i->until > t)
break;
@@ -406,7 +406,7 @@ int dns_cache_put(
dns_cache_make_space(c, answer->n_rrs + q->n_keys);
if (timestamp <= 0)
- timestamp = now(CLOCK_BOOTTIME);
+ timestamp = now(clock_boottime_or_monotonic());
/* Second, add in positive entries for all contained RRs */
for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 649e8b74e1..39951a362c 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -508,23 +508,21 @@ static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t l
assert(p);
assert(types);
+ assert(length > 0);
saved_size = p->size;
- if (length != 0) {
-
- r = dns_packet_append_uint8(p, window, NULL);
- if (r < 0)
- goto fail;
+ r = dns_packet_append_uint8(p, window, NULL);
+ if (r < 0)
+ goto fail;
- r = dns_packet_append_uint8(p, length, NULL);
- if (r < 0)
- goto fail;
+ r = dns_packet_append_uint8(p, length, NULL);
+ if (r < 0)
+ goto fail;
- r = dns_packet_append_blob(p, types, length, NULL);
- if (r < 0)
- goto fail;
- }
+ r = dns_packet_append_blob(p, types, length, NULL);
+ if (r < 0)
+ goto fail;
if (start)
*start = saved_size;
@@ -538,7 +536,7 @@ fail:
static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
Iterator i;
uint8_t window = 0;
- uint8_t len = 0;
+ uint8_t entry = 0;
uint8_t bitmaps[32] = {};
unsigned n;
size_t saved_size;
@@ -550,30 +548,24 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
saved_size = p->size;
BITMAP_FOREACH(n, types, i) {
- uint8_t entry;
-
assert(n <= 0xffff);
- if ((n << 8) != window) {
- r = dns_packet_append_type_window(p, window, len, bitmaps, NULL);
+ if ((n >> 8) != window && bitmaps[entry / 8] != 0) {
+ r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
if (r < 0)
goto fail;
- if (len > 0) {
- len = 0;
- zero(bitmaps);
- }
+ zero(bitmaps);
}
- window = n << 8;
- len ++;
+ window = n >> 8;
entry = n & 255;
bitmaps[entry / 8] |= 1 << (7 - (entry % 8));
}
- r = dns_packet_append_type_window(p, window, len, bitmaps, NULL);
+ r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
if (r < 0)
goto fail;
@@ -1164,6 +1156,7 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
uint8_t window;
uint8_t length;
const uint8_t *bitmap;
+ uint8_t bit = 0;
unsigned i;
bool found = false;
size_t saved_rindex;
@@ -1195,10 +1188,10 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
for (i = 0; i < length; i++) {
uint8_t bitmask = 1 << 7;
- uint8_t bit = 0;
if (!bitmap[i]) {
found = false;
+ bit += 8;
continue;
}
@@ -1673,8 +1666,12 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
if (r < 0)
goto fail;
- /* NSEC RRs with empty bitmpas makes no sense, but the RFC does not explicitly forbid them
- so we allow it */
+ /* The types bitmap must contain at least the NSEC record itself, so an empty bitmap means
+ something went wrong */
+ if (bitmap_isclear(rr->nsec.types)) {
+ r = -EBADMSG;
+ goto fail;
+ }
break;
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 4d71f5e3d4..0efe740d1a 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -188,6 +188,46 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
return 1;
}
+int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
+ unsigned j;
+ int r;
+
+ assert(a);
+ assert(k);
+
+ for (j = 0; j < a->n_keys; j++) {
+ r = dns_resource_key_equal(a->keys[j], k);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
+ unsigned j;
+ int r;
+
+ assert(a);
+ assert(b);
+
+ /* Checks if all keys in a are also contained b, and vice versa */
+
+ for (j = 0; j < a->n_keys; j++) {
+ r = dns_question_contains(b, a->keys[j]);
+ if (r <= 0)
+ return r;
+ }
+
+ for (j = 0; j < b->n_keys; j++) {
+ r = dns_question_contains(a, b->keys[j]);
+ if (r <= 0)
+ return r;
+ }
+
+ return 1;
+}
+
int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) {
_cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
bool same = true;
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 4ba2fe9f0e..fc98677798 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -43,6 +43,8 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr);
int dns_question_is_valid(DnsQuestion *q);
int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other);
+int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
+int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret);
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 0aab1e35d3..b8414da87e 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -34,6 +34,10 @@
#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
#define MULTICAST_RATELIMIT_BURST 1000
+/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */
+#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC)
+#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC)
+
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
DnsScope *s;
@@ -48,6 +52,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
s->link = l;
s->protocol = protocol;
s->family = family;
+ s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
LIST_PREPEND(scopes, m->dns_scopes, s);
@@ -125,18 +130,34 @@ void dns_scope_next_dns_server(DnsScope *s) {
manager_next_dns_server(s->manager);
}
-int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server) {
- DnsServer *srv = NULL;
+void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
+ assert(s);
+
+ if (rtt > s->max_rtt) {
+ s->max_rtt = rtt;
+ s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2),
+ MULTICAST_RESEND_TIMEOUT_MAX_USEC);
+ }
+}
+
+void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
+ assert(s);
+
+ if (s->resend_timeout <= usec)
+ s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
+}
+
+int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
union in_addr_union addr;
int ifindex = 0, r;
int family;
uint16_t port;
uint32_t mtu;
- int fd;
assert(s);
assert(p);
assert(p->protocol == s->protocol);
+ assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
if (s->link) {
mtu = s->link->mtu;
@@ -148,28 +169,15 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
- srv = dns_scope_get_dns_server(s);
- if (!srv)
- return -ESRCH;
-
- family = srv->family;
- addr = srv->address;
- port = 53;
-
if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
return -EMSGSIZE;
if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
return -EMSGSIZE;
- if (family == AF_INET)
- fd = transaction_dns_ipv4_fd(t);
- else if (family == AF_INET6)
- fd = transaction_dns_ipv6_fd(t);
- else
- return -EAFNOSUPPORT;
- if (fd < 0)
- return fd;
+ r = manager_write(s->manager, fd, p);
+ if (r < 0)
+ return r;
} else if (s->protocol == DNS_PROTOCOL_LLMNR) {
@@ -192,20 +200,17 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser
return -EAFNOSUPPORT;
if (fd < 0)
return fd;
+
+ r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
+ if (r < 0)
+ return r;
} else
return -EAFNOSUPPORT;
- r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
- if (r < 0)
- return r;
-
- if (server)
- *server = srv;
-
return 1;
}
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
+static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
DnsServer *srv = NULL;
_cleanup_close_ int fd = -1;
union sockaddr_union sa = {};
@@ -249,13 +254,15 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
return -EAFNOSUPPORT;
}
- fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
- r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
- if (r < 0)
- return -errno;
+ if (type == SOCK_STREAM) {
+ r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+ if (r < 0)
+ return -errno;
+ }
if (s->link) {
uint32_t ifindex = htobe32(s->link->ifindex);
@@ -298,6 +305,14 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
return ret;
}
+int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
+ return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
+}
+
+int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
+ return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
+}
+
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
char **i;
@@ -320,6 +335,11 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
if (is_localhost(domain))
return DNS_SCOPE_NO;
+ /* Never resolve any loopback IP address via DNS, LLMNR or mDNS */
+ if (dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
+ dns_name_equal(domain, "1.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.0.0.0.0.ip6.arpa") > 0)
+ return DNS_SCOPE_NO;
+
if (s->protocol == DNS_PROTOCOL_DNS) {
if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
@@ -687,7 +707,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
return 0;
}
- r = dns_scope_emit(scope, NULL, p, NULL);
+ r = dns_scope_emit(scope, -1, p);
if (r < 0)
log_debug_errno(r, "Failed to send conflict packet: %m");
}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 21a160ea39..b2dac86b44 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -57,6 +57,9 @@ struct DnsScope {
RateLimit ratelimit;
+ usec_t resend_timeout;
+ usec_t max_rtt;
+
LIST_HEAD(DnsTransaction, transactions);
LIST_FIELDS(DnsScope, scopes);
@@ -65,8 +68,12 @@ struct DnsScope {
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
DnsScope* dns_scope_free(DnsScope *s);
-int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server);
+void dns_scope_packet_received(DnsScope *s, usec_t rtt);
+void dns_scope_packet_lost(DnsScope *s, usec_t usec);
+
+int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p);
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
+int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 92e48ae442..2ff5b192df 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -23,6 +23,10 @@
#include "resolved-dns-server.h"
+/* After how much time to repeat classic DNS requests */
+#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
+#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
+
int dns_server_new(
Manager *m,
DnsServer **ret,
@@ -45,6 +49,7 @@ int dns_server_new(
s->type = type;
s->family = family;
s->address = *in_addr;
+ s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
if (type == DNS_SERVER_LINK) {
LIST_FIND_TAIL(servers, l->dns_servers, tail);
@@ -115,6 +120,23 @@ DnsServer* dns_server_unref(DnsServer *s) {
return NULL;
}
+void dns_server_packet_received(DnsServer *s, usec_t rtt) {
+ assert(s);
+
+ if (rtt > s->max_rtt) {
+ s->max_rtt = rtt;
+ s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2),
+ DNS_TIMEOUT_MAX_USEC);
+ }
+}
+
+void dns_server_packet_lost(DnsServer *s, usec_t usec) {
+ assert(s);
+
+ if (s->resend_timeout <= usec)
+ s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
+}
+
static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
const DnsServer *s = p;
uint64_t u;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 06059e8829..10111fd6bd 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -46,6 +46,9 @@ struct DnsServer {
int family;
union in_addr_union address;
+ usec_t resend_timeout;
+ usec_t max_rtt;
+
bool marked:1;
LIST_FIELDS(DnsServer, servers);
@@ -62,6 +65,9 @@ int dns_server_new(
DnsServer* dns_server_ref(DnsServer *s);
DnsServer* dns_server_unref(DnsServer *s);
+void dns_server_packet_received(DnsServer *s, usec_t rtt);
+void dns_server_packet_lost(DnsServer *s, usec_t usec);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 3d46c99df8..53779f3372 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -39,10 +39,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
dns_packet_unref(t->received);
dns_answer_unref(t->cached);
- sd_event_source_unref(t->dns_ipv4_event_source);
- sd_event_source_unref(t->dns_ipv6_event_source);
- safe_close(t->dns_ipv4_fd);
- safe_close(t->dns_ipv6_fd);
+ sd_event_source_unref(t->dns_event_source);
+ safe_close(t->dns_fd);
dns_server_unref(t->server);
dns_stream_free(t->stream);
@@ -94,7 +92,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
if (!t)
return -ENOMEM;
- t->dns_ipv4_fd = t->dns_ipv6_fd = -1;
+ t->dns_fd = -1;
t->question = dns_question_ref(q);
@@ -245,7 +243,7 @@ static int on_stream_complete(DnsStream *s, int error) {
}
static int dns_transaction_open_tcp(DnsTransaction *t) {
- _cleanup_(dns_server_unrefp) DnsServer *server = NULL;
+ DnsServer *server = NULL;
_cleanup_close_ int fd = -1;
int r;
@@ -310,12 +308,25 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
return 0;
}
+static void dns_transaction_next_dns_server(DnsTransaction *t) {
+ assert(t);
+
+ t->server = dns_server_unref(t->server);
+ t->dns_event_source = sd_event_source_unref(t->dns_event_source);
+ t->dns_fd = safe_close(t->dns_fd);
+
+ dns_scope_next_dns_server(t->scope);
+}
+
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
+ usec_t ts;
int r;
assert(t);
assert(p);
assert(t->state == DNS_TRANSACTION_PENDING);
+ assert(t->scope);
+ assert(t->scope->manager);
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
@@ -342,24 +353,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
}
- if (t->scope->protocol == DNS_PROTOCOL_DNS) {
-
- /* For DNS we are fine with accepting packets on any
- * interface, but the source IP address must be the
- * one of the DNS server we queried */
-
- assert(t->server);
-
- if (t->server->family != p->family)
- return;
-
- if (!in_addr_equal(p->family, &p->sender, &t->server->address))
- return;
-
- if (p->sender_port != 53)
- return;
- }
-
if (t->received != p) {
dns_packet_unref(t->received);
t->received = dns_packet_ref(p);
@@ -379,6 +372,24 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
}
+ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ switch (t->scope->protocol) {
+ case DNS_PROTOCOL_DNS:
+ assert(t->server);
+
+ dns_server_packet_received(t->server, ts - t->start_usec);
+
+ break;
+ case DNS_PROTOCOL_LLMNR:
+ case DNS_PROTOCOL_MDNS:
+ dns_scope_packet_received(t->scope, ts - t->start_usec);
+
+ break;
+ default:
+ assert_not_reached("Invalid DNS protocol.");
+ }
+
if (DNS_PACKET_TC(p)) {
/* Response was truncated, let's try again with good old TCP */
r = dns_transaction_open_tcp(t);
@@ -396,7 +407,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
/* On DNS, couldn't send? Try immediately again, with a new server */
- dns_scope_next_dns_server(t->scope);
+ dns_transaction_next_dns_server(t);
r = dns_transaction_go(t);
if (r < 0) {
@@ -416,8 +427,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
/* Only consider responses with equivalent query section to the request */
- if (!dns_question_is_superset(p->question, t->question) ||
- !dns_question_is_superset(t->question, p->question)) {
+ r = dns_question_is_equal(p->question, t->question);
+ if (r <= 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
@@ -431,6 +442,56 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
}
+static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ DnsTransaction *t = userdata;
+ int r;
+
+ assert(t);
+ assert(t->scope);
+
+ r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
+ if (r <= 0)
+ return r;
+
+ if (dns_packet_validate_reply(p) > 0 &&
+ DNS_PACKET_ID(p) == t->id)
+ dns_transaction_process_reply(t, p);
+ else
+ log_debug("Invalid DNS packet.");
+
+ return 0;
+}
+
+static int dns_transaction_emit(DnsTransaction *t) {
+ int r;
+
+ assert(t);
+
+ if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
+ DnsServer *server = NULL;
+ _cleanup_close_ int fd = -1;
+
+ fd = dns_scope_udp_dns_socket(t->scope, &server);
+ if (fd < 0)
+ return fd;
+
+ r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t);
+ if (r < 0)
+ return r;
+
+ t->dns_fd = fd;
+ fd = -1;
+ t->server = dns_server_ref(server);
+ }
+
+ r = dns_scope_emit(t->scope, t->dns_fd, t->sent);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
DnsTransaction *t = userdata;
int r;
@@ -439,7 +500,13 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
assert(t);
/* Timeout reached? Try again, with a new server */
- dns_scope_next_dns_server(t->scope);
+ dns_transaction_next_dns_server(t);
+
+ /* ... and possibly increased timeout */
+ if (t->server)
+ dns_server_packet_lost(t->server, usec - t->start_usec);
+ else
+ dns_scope_packet_lost(t->scope, usec - t->start_usec);
r = dns_transaction_go(t);
if (r < 0)
@@ -488,8 +555,26 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
return 0;
}
+static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
+ assert(t);
+ assert(t->scope);
+
+ switch (t->scope->protocol) {
+ case DNS_PROTOCOL_DNS:
+ assert(t->server);
+
+ return t->server->resend_timeout;
+ case DNS_PROTOCOL_LLMNR:
+ case DNS_PROTOCOL_MDNS:
+ return t->scope->resend_timeout;
+ default:
+ assert_not_reached("Invalid DNS protocol.");
+ }
+}
+
int dns_transaction_go(DnsTransaction *t) {
bool had_stream;
+ usec_t ts;
int r;
assert(t);
@@ -515,8 +600,10 @@ int dns_transaction_go(DnsTransaction *t) {
return 0;
}
+ assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
t->n_attempts++;
- t->server = dns_server_unref(t->server);
+ t->start_usec = ts;
t->received = dns_packet_unref(t->received);
t->cached = dns_answer_unref(t->cached);
t->cached_rcode = 0;
@@ -561,7 +648,7 @@ int dns_transaction_go(DnsTransaction *t) {
t->scope->manager->event,
&t->timeout_event_source,
clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()) + jitter,
+ ts + jitter,
LLMNR_JITTER_INTERVAL_USEC,
on_transaction_timeout, t);
if (r < 0)
@@ -596,13 +683,9 @@ int dns_transaction_go(DnsTransaction *t) {
* always be made via TCP on LLMNR */
r = dns_transaction_open_tcp(t);
} else {
- DnsServer *server;
-
/* Try via UDP, and if that fails due to large size try via TCP */
- r = dns_scope_emit(t->scope, t, t->sent, &server);
- if (r >= 0)
- t->server = dns_server_ref(server);
- else if (r == -EMSGSIZE)
+ r = dns_transaction_emit(t);
+ if (r == -EMSGSIZE)
r = dns_transaction_open_tcp(t);
}
if (r == -ESRCH) {
@@ -616,7 +699,7 @@ int dns_transaction_go(DnsTransaction *t) {
}
/* Couldn't send? Try immediately again, with a new server */
- dns_scope_next_dns_server(t->scope);
+ dns_transaction_next_dns_server(t);
return dns_transaction_go(t);
}
@@ -625,7 +708,7 @@ int dns_transaction_go(DnsTransaction *t) {
t->scope->manager->event,
&t->timeout_event_source,
clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()) + TRANSACTION_TIMEOUT_USEC(t->scope->protocol), 0,
+ ts + transaction_get_resend_timeout(t), 0,
on_transaction_timeout, t);
if (r < 0)
return r;
@@ -634,91 +717,6 @@ int dns_transaction_go(DnsTransaction *t) {
return 1;
}
-static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- DnsTransaction *t = userdata;
- int r;
-
- assert(t);
- assert(t->scope);
-
- r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
- if (r <= 0)
- return r;
-
- if (dns_packet_validate_reply(p) > 0 &&
- DNS_PACKET_ID(p) == t->id) {
- dns_transaction_process_reply(t, p);
- } else
- log_debug("Invalid DNS packet.");
-
- return 0;
-}
-
-int transaction_dns_ipv4_fd(DnsTransaction *t) {
- const int one = 1;
- int r;
-
- assert(t);
- assert(t->scope);
- assert(t->scope->manager);
-
- if (t->dns_ipv4_fd >= 0)
- return t->dns_ipv4_fd;
-
- t->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (t->dns_ipv4_fd < 0)
- return -errno;
-
- r = setsockopt(t->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv4_event_source, t->dns_ipv4_fd, EPOLLIN, on_dns_packet, t);
- if (r < 0)
- goto fail;
-
- return t->dns_ipv4_fd;
-
-fail:
- t->dns_ipv4_fd = safe_close(t->dns_ipv4_fd);
- return r;
-}
-
-int transaction_dns_ipv6_fd(DnsTransaction *t) {
- const int one = 1;
- int r;
-
- assert(t);
- assert(t->scope);
- assert(t->scope->manager);
-
- if (t->dns_ipv6_fd >= 0)
- return t->dns_ipv6_fd;
-
- t->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (t->dns_ipv6_fd < 0)
- return -errno;
-
- r = setsockopt(t->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv6_event_source, t->dns_ipv6_fd, EPOLLIN, on_dns_packet, t);
- if (r < 0)
- goto fail;
-
- return t->dns_ipv6_fd;
-
-fail:
- t->dns_ipv6_fd = safe_close(t->dns_ipv6_fd);
- return r;
-}
-
static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
[DNS_TRANSACTION_NULL] = "null",
[DNS_TRANSACTION_PENDING] = "pending",
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 87f342ca11..d8a5647609 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -58,14 +58,12 @@ struct DnsTransaction {
DnsAnswer *cached;
int cached_rcode;
+ usec_t start_usec;
sd_event_source *timeout_event_source;
unsigned n_attempts;
- int dns_ipv4_fd;
- int dns_ipv6_fd;
-
- sd_event_source *dns_ipv4_event_source;
- sd_event_source *dns_ipv6_event_source;
+ int dns_fd;
+ sd_event_source *dns_event_source;
/* the active server */
DnsServer *server;
@@ -95,26 +93,16 @@ int dns_transaction_go(DnsTransaction *t);
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
-int transaction_dns_ipv4_fd(DnsTransaction *t);
-int transaction_dns_ipv6_fd(DnsTransaction *t);
-
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
-/* After how much time to repeat classic DNS requests */
-#define DNS_TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
-
-/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */
-#define LLMNR_TRANSACTION_TIMEOUT_USEC (1 * USEC_PER_SEC)
-
/* LLMNR Jitter interval, see RFC 4795 Section 7 */
#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
/* Maximum attempts to send DNS requests, across all DNS servers */
-#define DNS_TRANSACTION_ATTEMPTS_MAX 8
+#define DNS_TRANSACTION_ATTEMPTS_MAX 16
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
-#define TRANSACTION_TIMEOUT_USEC(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_TIMEOUT_USEC : DNS_TRANSACTION_TIMEOUT_USEC)
#define TRANSACTION_ATTEMPTS_MAX(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 17de14bae1..5be01d3cb8 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -912,10 +912,12 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
if (p->ifindex == LOOPBACK_IFINDEX)
p->ifindex = 0;
- /* If we don't know the interface index still, we look for the
- * first local interface with a matching address. Yuck! */
- if (p->ifindex <= 0)
- p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
+ if (protocol != DNS_PROTOCOL_DNS) {
+ /* If we don't know the interface index still, we look for the
+ * first local interface with a matching address. Yuck! */
+ if (p->ifindex <= 0)
+ p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
+ }
*ret = p;
p = NULL;
@@ -947,6 +949,42 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
}
}
+static int write_loop(int fd, void *message, size_t length) {
+ int r;
+
+ assert(fd >= 0);
+ assert(message);
+
+ for (;;) {
+ if (write(fd, message, length) >= 0)
+ return 0;
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno != EAGAIN)
+ return -errno;
+
+ r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIMEDOUT;
+ }
+}
+
+int manager_write(Manager *m, int fd, DnsPacket *p) {
+ int r;
+
+ log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
+
+ r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 005f844df2..53b5acb33c 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -119,6 +119,7 @@ void manager_next_dns_server(Manager *m);
uint32_t manager_find_mtu(Manager *m);
+int manager_write(Manager *m, int fd, DnsPacket *p);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p);
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
diff --git a/src/run/run.c b/src/run/run.c
index 148854a9b5..3dd97022de 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -1129,13 +1129,9 @@ int main(int argc, char* argv[]) {
}
if (arg_unit && isempty(description)) {
- free(description);
- description = strdup(arg_unit);
-
- if (!description) {
- r = log_oom();
+ r = free_and_strdup(&description, arg_unit);
+ if (r < 0)
goto finish;
- }
}
arg_description = description;
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 3941605cec..ca4c24ebde 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -382,11 +382,9 @@ int ask_password_agent(
if (id)
fprintf(f, "Id=%s\n", id);
- fflush(f);
-
- if (ferror(f)) {
- log_error_errno(errno, "Failed to write query file: %m");
- r = -errno;
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write query file: %m");
goto finish;
}
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 11350dad71..aa87cd0910 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -972,8 +972,8 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
}
case SD_BUS_TYPE_ARRAY: {
- _cleanup_strv_free_ char **l = NULL;
- char ***p = userdata;
+ _cleanup_strv_free_ char **l = NULL;
+ char ***p = userdata;
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 7370c786f9..d99aa1d6e9 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -333,8 +333,7 @@ int config_parse(const char *unit,
return -ENOMEM;
}
- free(continuation);
- continuation = NULL;
+ continuation = mfree(continuation);
p = c;
} else
p = l;
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 20a44ce4e1..8a0dec1540 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -114,6 +114,68 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
return r;
}
+/* @label_terminal: terminal character of a label, updated to point to the terminal character of
+ * the previous label (always skipping one dot) or to NULL if there are no more
+ * labels. */
+int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
+ const char *terminal;
+ int r;
+
+ assert(name);
+ assert(label_terminal);
+ assert(dest);
+
+ /* no more labels */
+ if (!*label_terminal) {
+ if (sz >= 1)
+ *dest = 0;
+
+ return 0;
+ }
+
+ assert(**label_terminal == '.' || **label_terminal == 0);
+
+ /* skip current terminal character */
+ terminal = *label_terminal - 1;
+
+ /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
+ for (;;) {
+ if (terminal < name) {
+ /* reached the first label, so indicate that there are no more */
+ terminal = NULL;
+ break;
+ }
+
+ /* find the start of the last label */
+ if (*terminal == '.') {
+ const char *y;
+ unsigned slashes = 0;
+
+ for (y = terminal - 1; y >= name && *y == '\\'; y--)
+ slashes ++;
+
+ if (slashes % 2 == 0) {
+ /* the '.' was not escaped */
+ name = terminal + 1;
+ break;
+ } else {
+ terminal = y;
+ continue;
+ }
+ }
+
+ terminal --;
+ }
+
+ r = dns_label_unescape(&name, dest, sz);
+ if (r < 0)
+ return r;
+
+ *label_terminal = terminal;
+
+ return r;
+}
+
int dns_label_escape(const char *p, size_t l, char **ret) {
_cleanup_free_ char *s = NULL;
char *q;
@@ -338,20 +400,23 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
}
int dns_name_compare_func(const void *a, const void *b) {
- const char *x = a, *y = b;
+ const char *x, *y;
int r, q, k, w;
assert(a);
assert(b);
+ x = (const char *) a + strlen(a);
+ y = (const char *) b + strlen(b);
+
for (;;) {
char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
- if (*x == 0 && *y == 0)
+ if (x == NULL && y == NULL)
return 0;
- r = dns_label_unescape(&x, la, sizeof(la));
- q = dns_label_unescape(&y, lb, sizeof(lb));
+ r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
+ q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
if (r < 0 || q < 0)
return r - q;
@@ -464,6 +529,28 @@ int dns_name_endswith(const char *name, const char *suffix) {
}
}
+int dns_name_between(const char *a, const char *b, const char *c) {
+ int n;
+
+ /* Determine if b is strictly greater than a and strictly smaller than c.
+ We consider the order of names to be circular, so that if a is
+ strictly greater than c, we consider b to be between them if it is
+ either greater than a or smaller than c. This is how the canonical
+ DNS name order used in NSEC records work. */
+
+ n = dns_name_compare_func(a, c);
+ if (n == 0)
+ return -EINVAL;
+ else if (n < 0)
+ /* a<---b--->c */
+ return dns_name_compare_func(a, b) < 0 &&
+ dns_name_compare_func(b, c) < 0;
+ else
+ /* <--b--c a--b--> */
+ return dns_name_compare_func(b, c) < 0 ||
+ dns_name_compare_func(a, b) < 0;
+}
+
int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
const uint8_t *p;
int r;
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 00caf5d700..bd50ad3e6d 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -29,6 +29,7 @@
#define DNS_NAME_MAX 255
int dns_label_unescape(const char **name, char *dest, size_t sz);
+int dns_label_unescape_suffix(const char *name, const char **label_end, 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);
@@ -49,6 +50,7 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
int dns_name_compare_func(const void *a, const void *b);
extern const struct hash_ops dns_name_hash_ops;
+int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
int dns_name_endswith(const char *name, const char *suffix);
diff --git a/src/shared/install.c b/src/shared/install.c
index c37cf1948a..3d2b5ae77f 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -2190,6 +2190,7 @@ int unit_file_get_list(
_cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
struct dirent *de;
_cleanup_free_ char *path = NULL;
+ bool also = false;
errno = 0;
de = readdir(d);
@@ -2243,7 +2244,7 @@ int unit_file_get_list(
if (!path)
return -ENOMEM;
- r = unit_file_can_install(&paths, root_dir, path, true, NULL);
+ r = unit_file_can_install(&paths, root_dir, path, true, &also);
if (r == -EINVAL || /* Invalid setting? */
r == -EBADMSG || /* Invalid format? */
r == -ENOENT /* Included file not found? */)
@@ -2253,7 +2254,7 @@ int unit_file_get_list(
else if (r > 0)
f->state = UNIT_FILE_DISABLED;
else
- f->state = UNIT_FILE_STATIC;
+ f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC;
found:
r = hashmap_put(h, basename(f->path), f);
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index fe277a2015..618844382f 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -51,8 +51,8 @@ static int apply_all(Hashmap *sysctl_options) {
k = sysctl_write(property, value);
if (k < 0) {
- log_full_errno(k == -ENOENT ? LOG_DEBUG : LOG_WARNING, k,
- "Failed to write '%s' to '%s': %m", value, property);
+ log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k,
+ "Couldn't write '%s' to '%s', ignoring: %m", value, property);
if (r == 0 && k != -ENOENT)
r = k;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 6db4d6587a..4e850ea1cf 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3327,8 +3327,7 @@ static void print_status_info(
if (! dir || last) {
printf(dir ? " " : " Drop-In: ");
- free(dir);
- dir = NULL;
+ dir = mfree(dir);
if (path_get_parent(*dropin, &dir) < 0) {
log_oom();
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index 24a9ed8e77..cb462bf48f 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -69,6 +69,7 @@ int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int priority);
int sd_netlink_detach_event(sd_netlink *nl);
int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data);
+int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type);
int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data);
int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index d7ba482834..3a92d120d2 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -704,8 +704,7 @@ static int write_files(void) {
goto finish;
}
- free(group_tmp);
- group_tmp = NULL;
+ group_tmp = mfree(group_tmp);
}
if (gshadow) {
if (rename(gshadow_tmp, gshadow_path) < 0) {
@@ -713,8 +712,7 @@ static int write_files(void) {
goto finish;
}
- free(gshadow_tmp);
- gshadow_tmp = NULL;
+ gshadow_tmp = mfree(gshadow_tmp);
}
}
@@ -724,8 +722,7 @@ static int write_files(void) {
goto finish;
}
- free(passwd_tmp);
- passwd_tmp = NULL;
+ passwd_tmp = mfree(passwd_tmp);
}
if (shadow) {
if (rename(shadow_tmp, shadow_path) < 0) {
@@ -733,8 +730,7 @@ static int write_files(void) {
goto finish;
}
- free(shadow_tmp);
- shadow_tmp = NULL;
+ shadow_tmp = mfree(shadow_tmp);
}
r = 0;
@@ -891,8 +887,10 @@ static int add_user(Item *i) {
i->uid = p->pw_uid;
i->uid_set = true;
- free(i->description);
- i->description = strdup(p->pw_gecos);
+ r = free_and_strdup(&i->description, p->pw_gecos);
+ if (r < 0)
+ return log_oom();
+
return 0;
}
if (!IN_SET(errno, 0, ENOENT))
@@ -1149,9 +1147,8 @@ static int process_item(Item *i) {
}
if (i->gid_path) {
- free(j->gid_path);
- j->gid_path = strdup(i->gid_path);
- if (!j->gid_path)
+ r = free_and_strdup(&j->gid_path, i->gid_path);
+ if (r < 0)
return log_oom();
}
@@ -1409,10 +1406,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
/* Verify name */
- if (isempty(name) || streq(name, "-")) {
- free(name);
- name = NULL;
- }
+ if (isempty(name) || streq(name, "-"))
+ name = mfree(name);
if (name) {
r = specifier_printf(name, specifier_table, NULL, &resolved_name);
@@ -1428,10 +1423,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
/* Verify id */
- if (isempty(id) || streq(id, "-")) {
- free(id);
- id = NULL;
- }
+ if (isempty(id) || streq(id, "-"))
+ id = mfree(id);
if (id) {
r = specifier_printf(id, specifier_table, NULL, &resolved_id);
@@ -1442,10 +1435,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
/* Verify description */
- if (isempty(description) || streq(description, "-")) {
- free(description);
- description = NULL;
- }
+ if (isempty(description) || streq(description, "-"))
+ description = mfree(description);
if (description) {
if (!valid_gecos(description)) {
@@ -1455,10 +1446,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
/* Verify home */
- if (isempty(home) || streq(home, "-")) {
- free(home);
- home = NULL;
- }
+ if (isempty(home) || streq(home, "-"))
+ home = mfree(home);
if (home) {
if (!valid_home(home)) {
diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c
new file mode 100644
index 0000000000..d69104f540
--- /dev/null
+++ b/src/test/test-af-list.c
@@ -0,0 +1,48 @@
+/***
+ This file is part of systemd
+
+ Copyright 2015 Daniel Mack
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ 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 "macro.h"
+#include "util.h"
+
+static const struct af_name* lookup_af(register const char *str, register unsigned int len);
+
+#include "af-list.h"
+#include "af-to-name.h"
+#include "af-from-name.h"
+
+int main(int argc, const char *argv[]) {
+
+ unsigned int i;
+
+ for (i = 0; i < ELEMENTSOF(af_names); i++) {
+ if (af_names[i]) {
+ assert_se(streq(af_to_name(i), af_names[i]));
+ assert_se(af_from_name(af_names[i]) == (int) i);
+ }
+ }
+
+ assert_se(af_to_name(af_max()) == NULL);
+ assert_se(af_to_name(-1) == NULL);
+ assert_se(af_from_name("huddlduddl") == AF_UNSPEC);
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-list.c
new file mode 100644
index 0000000000..d7c8eaa4a9
--- /dev/null
+++ b/src/test/test-arphrd-list.c
@@ -0,0 +1,48 @@
+/***
+ This file is part of systemd
+
+ Copyright 2015 Daniel Mack
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ 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 "macro.h"
+#include "util.h"
+
+static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
+
+#include "arphrd-list.h"
+#include "arphrd-to-name.h"
+#include "arphrd-from-name.h"
+
+int main(int argc, const char *argv[]) {
+
+ unsigned int i;
+
+ for (i = 1; i < ELEMENTSOF(arphrd_names); i++) {
+ if (arphrd_names[i]) {
+ assert_se(streq(arphrd_to_name(i), arphrd_names[i]));
+ assert_se(arphrd_from_name(arphrd_names[i]) == (int) i);
+ }
+ }
+
+ assert_se(arphrd_to_name(arphrd_max()) == NULL);
+ assert_se(arphrd_to_name(0) == NULL);
+ assert_se(arphrd_from_name("huddlduddl") == 0);
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/test/test-bitmap.c b/src/test/test-bitmap.c
index 96deeded7e..ff22117745 100644
--- a/src/test/test-bitmap.c
+++ b/src/test/test-bitmap.c
@@ -20,7 +20,7 @@
#include "bitmap.h"
int main(int argc, const char *argv[]) {
- _cleanup_bitmap_free_ Bitmap *b = NULL;
+ _cleanup_bitmap_free_ Bitmap *b = NULL, *b2 = NULL;
Iterator it;
unsigned n = (unsigned) -1, i = 0;
@@ -101,5 +101,23 @@ int main(int argc, const char *argv[]) {
assert_se(bitmap_set(b, (unsigned) -1) == -ERANGE);
+ bitmap_free(b);
+ b = NULL;
+ assert_se(bitmap_ensure_allocated(&b) == 0);
+ assert_se(bitmap_ensure_allocated(&b2) == 0);
+
+ assert_se(bitmap_equal(b, b2));
+ assert_se(bitmap_set(b, 0) == 0);
+ bitmap_unset(b, 0);
+ assert_se(bitmap_equal(b, b2));
+
+ assert_se(bitmap_set(b, 1) == 0);
+ bitmap_clear(b);
+ assert_se(bitmap_equal(b, b2));
+
+ assert_se(bitmap_set(b, 0) == 0);
+ assert_se(bitmap_set(b2, 0) == 0);
+ assert_se(bitmap_equal(b, b2));
+
return 0;
}
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 527cdd3b54..0042722c99 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -50,6 +50,46 @@ static void test_dns_label_unescape(void) {
test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
}
+static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
+ char buffer[buffer_sz];
+ const char *label;
+ int r;
+
+ label = what + strlen(what);
+
+ r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
+ assert_se(r == ret1);
+ if (r >= 0)
+ assert_se(streq(buffer, expect1));
+
+ r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
+ assert_se(r == ret2);
+ if (r >= 0)
+ assert_se(streq(buffer, expect2));
+}
+
+static void test_dns_label_unescape_suffix(void) {
+ test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0);
+ test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC);
+ test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0);
+ test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0);
+ test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5);
+ test_dns_label_unescape_suffix_one("hallo.foobar\n", "foobar", "foobar", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo ", "", 20, 7, 0);
+ test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0);
+ test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0);
+ test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL);
+ test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6);
+ test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0);
+ test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3);
+ test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL);
+ test_dns_label_unescape_suffix_one("foo...bar", "bar", "", 20, 3, -EINVAL);
+ test_dns_label_unescape_suffix_one("foo\\.bar", "foo.bar", "", 20, 7, 0);
+ test_dns_label_unescape_suffix_one("foo\\\\.bar", "bar", "foo\\", 20, 3, 4);
+ test_dns_label_unescape_suffix_one("foo\\\\\\.bar", "foo\\.bar", "", 20, 8, 0);
+}
+
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;
@@ -120,6 +160,38 @@ static void test_dns_name_equal(void) {
test_dns_name_equal_one("..", "..", -EINVAL);
}
+static void test_dns_name_between_one(const char *a, const char *b, const char *c, int ret) {
+ int r;
+
+ r = dns_name_between(a, b, c);
+ assert_se(r == ret);
+
+ r = dns_name_between(c, b, a);
+ if (ret >= 0)
+ assert_se(r == 0);
+ else
+ assert_se(r == ret);
+}
+
+static void test_dns_name_between(void) {
+ /* see https://tools.ietf.org/html/rfc4034#section-6.1
+ Note that we use "\033.z.example" in stead of "\001.z.example" as we
+ consider the latter invalid */
+ test_dns_name_between_one("example", "a.example", "yljkjljk.a.example", true);
+ test_dns_name_between_one("a.example", "yljkjljk.a.example", "Z.a.example", true);
+ test_dns_name_between_one("yljkjljk.a.example", "Z.a.example", "zABC.a.EXAMPLE", true);
+ test_dns_name_between_one("Z.a.example", "zABC.a.EXAMPLE", "z.example", true);
+ test_dns_name_between_one("zABC.a.EXAMPLE", "z.example", "\\033.z.example", true);
+ test_dns_name_between_one("z.example", "\\033.z.example", "*.z.example", true);
+ test_dns_name_between_one("\\033.z.example", "*.z.example", "\\200.z.example", true);
+ test_dns_name_between_one("*.z.example", "\\200.z.example", "example", true);
+ test_dns_name_between_one("\\200.z.example", "example", "a.example", true);
+
+ test_dns_name_between_one("example", "a.example", "example", -EINVAL);
+ test_dns_name_between_one("example", "example", "yljkjljk.a.example", false);
+ test_dns_name_between_one("example", "yljkjljk.a.example", "yljkjljk.a.example", false);
+}
+
static void test_dns_name_endswith_one(const char *a, const char *b, int ret) {
assert_se(dns_name_endswith(a, b) == ret);
}
@@ -175,15 +247,19 @@ static void test_dns_name_reverse_one(const char *address, const char *name) {
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");
+ test_dns_name_reverse_one("127.0.0.1", "1.0.0.127.in-addr.arpa");
+ test_dns_name_reverse_one("::1", "1.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.0.0.0.0.ip6.arpa");
}
int main(int argc, char *argv[]) {
test_dns_label_unescape();
+ test_dns_label_unescape_suffix();
test_dns_label_escape();
test_dns_name_normalize();
test_dns_name_equal();
test_dns_name_endswith();
+ test_dns_name_between();
test_dns_name_root();
test_dns_name_single_label();
test_dns_name_reverse();
diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c
index 08be3f7bf2..6f5ef2615e 100644
--- a/src/test/test-hostname-util.c
+++ b/src/test/test-hostname-util.c
@@ -111,32 +111,28 @@ static void test_read_hostname_config(void) {
write_string_file(path, "foo", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(streq(hostname, "foo"));
- free(hostname);
- hostname = NULL;
+ hostname = mfree(hostname);
/* with comment */
write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(hostname);
assert_se(streq(hostname, "foo"));
- free(hostname);
- hostname = NULL;
+ hostname = mfree(hostname);
/* with comment and extra whitespace */
write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(hostname);
assert_se(streq(hostname, "foo"));
- free(hostname);
- hostname = NULL;
+ hostname = mfree(hostname);
/* cleans up name */
write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE);
assert_se(read_hostname_config(path, &hostname) == 0);
assert_se(hostname);
assert_se(streq(hostname, "foobar.com"));
- free(hostname);
- hostname = NULL;
+ hostname = mfree(hostname);
/* no value set */
hostname = (char*) 0x1234;
diff --git a/src/test/test-list.c b/src/test/test-list.c
index f6da1a7053..160064d06a 100644
--- a/src/test/test-list.c
+++ b/src/test/test-list.c
@@ -99,6 +99,50 @@ int main(int argc, const char *argv[]) {
assert_se(items[1].item_prev == &items[3]);
assert_se(items[3].item_prev == NULL);
+ LIST_REMOVE(item, head, &items[1]);
+ assert_se(LIST_JUST_US(item, &items[1]));
+
+ assert_se(items[0].item_next == NULL);
+ assert_se(items[2].item_next == &items[0]);
+ assert_se(items[3].item_next == &items[2]);
+
+ assert_se(items[0].item_prev == &items[2]);
+ assert_se(items[2].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
+ LIST_INSERT_BEFORE(item, head, &items[2], &items[1]);
+ assert_se(items[0].item_next == NULL);
+ assert_se(items[2].item_next == &items[0]);
+ assert_se(items[1].item_next == &items[2]);
+ assert_se(items[3].item_next == &items[1]);
+
+ assert_se(items[0].item_prev == &items[2]);
+ assert_se(items[2].item_prev == &items[1]);
+ assert_se(items[1].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
+ LIST_REMOVE(item, head, &items[0]);
+ assert_se(LIST_JUST_US(item, &items[0]));
+
+ assert_se(items[2].item_next == NULL);
+ assert_se(items[1].item_next == &items[2]);
+ assert_se(items[3].item_next == &items[1]);
+
+ assert_se(items[2].item_prev == &items[1]);
+ assert_se(items[1].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
+ LIST_INSERT_BEFORE(item, head, NULL, &items[0]);
+ assert_se(items[0].item_next == NULL);
+ assert_se(items[2].item_next == &items[0]);
+ assert_se(items[1].item_next == &items[2]);
+ assert_se(items[3].item_next == &items[1]);
+
+ assert_se(items[0].item_prev == &items[2]);
+ assert_se(items[2].item_prev == &items[1]);
+ assert_se(items[1].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
LIST_REMOVE(item, head, &items[0]);
assert_se(LIST_JUST_US(item, &items[0]));
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index f257af445a..2c18090ae5 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -158,6 +158,20 @@ static void test_socket_address_is_netlink(void) {
assert_se(!socket_address_is_netlink(&a, "route 1"));
}
+static void test_in_addr_is_null(void) {
+
+ union in_addr_union i = {};
+
+ assert_se(in_addr_is_null(AF_INET, &i) == true);
+ assert_se(in_addr_is_null(AF_INET6, &i) == true);
+
+ i.in.s_addr = 0x1000000;
+ assert_se(in_addr_is_null(AF_INET, &i) == false);
+ assert_se(in_addr_is_null(AF_INET6, &i) == false);
+
+ assert_se(in_addr_is_null(-1, &i) == -EAFNOSUPPORT);
+}
+
static void test_in_addr_prefix_intersect_one(unsigned f, const char *a, unsigned apl, const char *b, unsigned bpl, int result) {
union in_addr_union ua, ub;
@@ -340,6 +354,7 @@ int main(int argc, char *argv[]) {
test_socket_address_is();
test_socket_address_is_netlink();
+ test_in_addr_is_null();
test_in_addr_prefix_intersect();
test_in_addr_prefix_next();
test_in_addr_to_string();
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index d5ea2b3fab..6e3c81395c 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -307,7 +307,7 @@ static void test_strv_sort(void) {
}
static void test_strv_extend_strv_concat(void) {
- _cleanup_strv_free_ char **a = NULL, **b = NULL;
+ _cleanup_strv_free_ char **a = NULL, **b = NULL;
a = strv_new("without", "suffix", NULL);
b = strv_new("with", "suffix", NULL);
@@ -323,7 +323,7 @@ static void test_strv_extend_strv_concat(void) {
}
static void test_strv_extend_strv(void) {
- _cleanup_strv_free_ char **a = NULL, **b = NULL;
+ _cleanup_strv_free_ char **a = NULL, **b = NULL;
a = strv_new("abc", "def", "ghi", NULL);
b = strv_new("jkl", "mno", "pqr", NULL);
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 9ad912d20f..03e18df080 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -279,6 +279,39 @@ static void test_parse_uid(void) {
r = parse_uid("100", &uid);
assert_se(r == 0);
assert_se(uid == 100);
+
+ r = parse_uid("65535", &uid);
+ assert_se(r == -ENXIO);
+}
+
+static void test_safe_atou16(void) {
+ int r;
+ uint16_t l;
+
+ r = safe_atou16("12345", &l);
+ assert_se(r == 0);
+ assert_se(l == 12345);
+
+ r = safe_atou16("123456", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atou16("junk", &l);
+ assert_se(r == -EINVAL);
+}
+
+static void test_safe_atoi16(void) {
+ int r;
+ int16_t l;
+
+ r = safe_atoi16("-12345", &l);
+ assert_se(r == 0);
+ assert_se(l == -12345);
+
+ r = safe_atoi16("36536", &l);
+ assert_se(r == -ERANGE);
+
+ r = safe_atoi16("junk", &l);
+ assert_se(r == -EINVAL);
}
static void test_safe_atolli(void) {
@@ -582,6 +615,15 @@ static void test_unbase32hexmem(void) {
assert_se(unbase32hexmem("AAAAB===", strlen("AAAAB==="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAAAB=", strlen("AAAAAAB="), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("XPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CXNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CPXMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CPNXUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CPNMXOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CPNMUXJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CPNMUOX1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+ assert_se(unbase32hexmem("CPNMUOJX", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
+
assert_se(unbase32hexmem("", strlen(""), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), ""));
free(mem);
@@ -713,45 +755,38 @@ static void test_cunescape(void) {
assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0);
assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
/* incomplete sequences */
assert_se(cunescape("\\x0", 0, &unescaped) < 0);
assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\x0"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
assert_se(cunescape("\\x", 0, &unescaped) < 0);
assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\x"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
assert_se(cunescape("\\", 0, &unescaped) < 0);
assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
assert_se(cunescape("\\11", 0, &unescaped) < 0);
assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\11"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
assert_se(cunescape("\\1", 0, &unescaped) < 0);
assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\1"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
assert_se(cunescape("\\u0000", 0, &unescaped) < 0);
assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "ßßΠA"));
- free(unescaped);
- unescaped = NULL;
+ unescaped = mfree(unescaped);
assert_se(cunescape("\\073", 0, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, ";"));
@@ -1193,6 +1228,16 @@ static void test_endswith(void) {
assert_se(!endswith("foobar", "foobarfoofoo"));
}
+static void test_endswith_no_case(void) {
+ assert_se(endswith_no_case("fooBAR", "bar"));
+ assert_se(endswith_no_case("foobar", ""));
+ assert_se(endswith_no_case("foobar", "FOOBAR"));
+ assert_se(endswith_no_case("", ""));
+
+ assert_se(!endswith_no_case("foobar", "FOO"));
+ assert_se(!endswith_no_case("foobar", "FOOBARFOOFOO"));
+}
+
static void test_close_nointr(void) {
char name[] = "/tmp/test-test-close_nointr.XXXXXX";
int fd;
@@ -2016,6 +2061,18 @@ static void test_tempfn(void) {
free(ret);
}
+static void test_strcmp_ptr(void) {
+ assert_se(strcmp_ptr(NULL, NULL) == 0);
+ assert_se(strcmp_ptr("", NULL) > 0);
+ assert_se(strcmp_ptr("foo", NULL) > 0);
+ assert_se(strcmp_ptr(NULL, "") < 0);
+ assert_se(strcmp_ptr(NULL, "bar") < 0);
+ assert_se(strcmp_ptr("foo", "bar") > 0);
+ assert_se(strcmp_ptr("bar", "baz") < 0);
+ assert_se(strcmp_ptr("foo", "foo") == 0);
+ assert_se(strcmp_ptr("", "") == 0);
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -2031,6 +2088,8 @@ int main(int argc, char *argv[]) {
test_parse_boolean();
test_parse_pid();
test_parse_uid();
+ test_safe_atou16();
+ test_safe_atoi16();
test_safe_atolli();
test_safe_atod();
test_strappend();
@@ -2078,6 +2137,7 @@ int main(int argc, char *argv[]) {
test_is_valid_documentation_url();
test_file_in_same_dir();
test_endswith();
+ test_endswith_no_case();
test_close_nointr();
test_unlink_noerrno();
test_readlink_and_make_absolute();
@@ -2100,6 +2160,7 @@ int main(int argc, char *argv[]) {
test_shell_maybe_quote();
test_parse_mode();
test_tempfn();
+ test_strcmp_ptr();
return 0;
}
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index b030206948..7b4178c993 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -113,10 +113,6 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- /* We need one process for ourselves, plus one thread for the asynchronous resolver */
- if (setrlimit(RLIMIT_NPROC, &RLIMIT_MAKE_CONST(2)) < 0)
- log_warning_errno(errno, "Failed to lower RLIMIT_NPROC to 2: %m");
-
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 271984b5a8..d6f7801561 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -616,7 +616,7 @@ static int path_set_perms(Item *i, const char *path) {
if (!(st.st_mode & 0111))
m &= ~0111;
if (!(st.st_mode & 0222))
- m &= ~0222;
+ m &= ~0222;
if (!(st.st_mode & 0444))
m &= ~0444;
if (!S_ISDIR(st.st_mode))
@@ -641,7 +641,7 @@ static int path_set_perms(Item *i, const char *path) {
if (chown(fn,
i->uid_set ? i->uid : UID_INVALID,
i->gid_set ? i->gid : GID_INVALID) < 0)
- return log_error_errno(errno, "chown(%s) failed: %m", path);
+ return log_error_errno(errno, "chown(%s) failed: %m", path);
}
}
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 73b19d8e89..82cbf95f1e 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -162,8 +162,7 @@ static int ask_password_plymouth(
/* Hmm, first try with cached
* passwords failed, so let's retry
* with a normal password request */
- free(packet);
- packet = NULL;
+ packet = mfree(packet);
if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
return -ENOMEM;
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 3d74ae50f1..775da0355d 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -106,11 +106,11 @@ static bool is_mounted(const char *device)
bool mounted = false;
if (stat(device, &statbuf) < 0)
- return -ENODEV;
+ return false;
fp = fopen("/proc/self/mountinfo", "re");
if (fp == NULL)
- return -ENOSYS;
+ return false;
while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) {
if (makedev(maj, min) == statbuf.st_rdev) {
mounted = true;
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 4ca0a69d7d..aa5cda6fe7 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -674,20 +674,16 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
* might produce conflicting IDs if the parent does not provide a
* unique and predictable name.
*/
- if (!supported_parent) {
- free(path);
- path = NULL;
- }
+ if (!supported_parent)
+ path = mfree(path);
/*
* Do not return block devices without a well-known transport. Some
* devices do not expose their buses and do not provide a unique
* and predictable name that way.
*/
- if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) {
- free(path);
- path = NULL;
- }
+ if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport)
+ path = mfree(path);
out:
if (path != NULL) {
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index 99bb91ae57..43bab8af63 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -56,7 +56,7 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool
r = devnode_acl(path, true, false, 0, true, uid);
if (r < 0) {
- log_error_errno(r, "Failed to apply ACL on %s: %m", path);
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path);
goto finish;
}
@@ -70,7 +70,7 @@ finish:
/* Better be safe than sorry and reset ACL */
k = devnode_acl(path, true, false, 0, false, 0);
if (k < 0) {
- log_error_errno(k, "Failed to apply ACL on %s: %m", path);
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL on %s: %m", path);
if (r >= 0)
r = k;
}
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index fabc653800..4f625251d6 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -52,7 +52,7 @@ void udev_builtin_init(struct udev *udev) {
return;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i]->init)
+ if (builtins[i] && builtins[i]->init)
builtins[i]->init(udev);
initialized = true;
@@ -65,7 +65,7 @@ void udev_builtin_exit(struct udev *udev) {
return;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i]->exit)
+ if (builtins[i] && builtins[i]->exit)
builtins[i]->exit(udev);
initialized = false;
@@ -75,7 +75,7 @@ bool udev_builtin_validate(struct udev *udev) {
unsigned int i;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i]->validate && builtins[i]->validate(udev))
+ if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev))
return true;
return false;
}
@@ -84,14 +84,21 @@ void udev_builtin_list(struct udev *udev) {
unsigned int i;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
+ if (builtins[i])
+ fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
}
const char *udev_builtin_name(enum udev_builtin_cmd cmd) {
+ if (!builtins[cmd])
+ return NULL;
+
return builtins[cmd]->name;
}
bool udev_builtin_run_once(enum udev_builtin_cmd cmd) {
+ if (!builtins[cmd])
+ return false;
+
return builtins[cmd]->run_once;
}
@@ -105,7 +112,7 @@ enum udev_builtin_cmd udev_builtin_lookup(const char *command) {
if (pos)
pos[0] = '\0';
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (streq(builtins[i]->name, name))
+ if (builtins[i] && streq(builtins[i]->name, name))
return i;
return UDEV_BUILTIN_MAX;
}
@@ -115,6 +122,9 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c
int argc;
char *argv[128];
+ if (!builtins[cmd])
+ return -EOPNOTSUPP;
+
/* we need '0' here to reset the internal state */
optind = 0;
strscpy(arg, sizeof(arg), command);
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index d824172b89..e730fb45f1 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -309,7 +309,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
} else if (streq(name, "smack")) {
smack = true;
- r = mac_smack_apply(devnode, label);
+ r = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
if (r < 0)
log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label);
else
@@ -323,7 +323,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply,
if (!selinux)
mac_selinux_fix(devnode, true, false);
if (!smack)
- mac_smack_apply(devnode, NULL);
+ mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL);
}
/* always update timestamp when we re-use the node, like on media change events */
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index d00f90afa6..43255fb9d4 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -2125,7 +2125,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
rule->rule.filename_line);
/* return the result from earlier run */
if (event->builtin_ret & (1 << cur->key.builtin_cmd))
- if (cur->key.op != OP_NOMATCH)
+ if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
}
@@ -2429,8 +2429,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
break;
}
- free(event->name);
- event->name = strdup(name_str);
+ free_and_strdup(&event->name, name_str);
log_debug("NAME '%s' %s:%u",
event->name,
rules_str(rules, rule->rule.filename_off),
@@ -2590,8 +2589,7 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
uid = 0;
gid = 0;
mode = 0;
- strv_free(tags);
- tags = NULL;
+ tags = strv_free(tags);
break;
case TK_A_OWNER_ID:
uid = cur->key.uid;
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index d0b8bad48e..28ac44fb8e 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -261,7 +261,6 @@ static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *use
static void worker_attach_event(struct worker *worker, struct event *event) {
sd_event *e;
uint64_t usec;
- int r;
assert(worker);
assert(worker->manager);
@@ -276,9 +275,7 @@ static void worker_attach_event(struct worker *worker, struct event *event) {
e = worker->manager->event;
- r = sd_event_now(e, clock_boottime_or_monotonic(), &usec);
- if (r < 0)
- return;
+ assert_se(sd_event_now(e, clock_boottime_or_monotonic(), &usec) >= 0);
(void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(),
usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
@@ -749,9 +746,7 @@ static void manager_exit(Manager *manager) {
event_queue_cleanup(manager, EVENT_QUEUED);
manager_kill_workers(manager);
- r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
- if (r < 0)
- return;
+ assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(),
usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
@@ -780,7 +775,6 @@ static void manager_reload(Manager *manager) {
static void event_queue_start(Manager *manager) {
struct udev_list_node *loop;
usec_t usec;
- int r;
assert(manager);
@@ -788,17 +782,15 @@ static void event_queue_start(Manager *manager) {
manager->exit || manager->stop_exec_queue)
return;
- r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
- if (r >= 0) {
- /* check for changed config, every 3 seconds at most */
- if (manager->last_usec == 0 ||
- (usec - manager->last_usec) > 3 * USEC_PER_SEC) {
- if (udev_rules_check_timestamp(manager->rules) ||
- udev_builtin_validate(manager->udev))
- manager_reload(manager);
+ assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
+ /* check for changed config, every 3 seconds at most */
+ if (manager->last_usec == 0 ||
+ (usec - manager->last_usec) > 3 * USEC_PER_SEC) {
+ if (udev_rules_check_timestamp(manager->rules) ||
+ udev_builtin_validate(manager->udev))
+ manager_reload(manager);
- manager->last_usec = usec;
- }
+ manager->last_usec = usec;
}
udev_builtin_init(manager->udev);
diff --git a/tools/compile-unifont.py b/tools/compile-unifont.py
deleted file mode 100755
index 5464c53e7f..0000000000
--- a/tools/compile-unifont.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-#
-# This file is part of systemd.
-#
-# Copyright 2013-2014 David Herrmann <dh.herrmann@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/>.
-
-#
-# Parse a unifont.hex file and produce a compressed binary-format.
-#
-
-from __future__ import print_function
-import re
-import sys
-import fileinput
-import struct
-
-#
-# Write "bits" array as binary output.
-#
-
-
-write = getattr(sys.stdout, 'buffer', sys.stdout).write
-
-def write_bin_entry(entry):
- l = len(entry)
- if l != 32 and l != 64:
- entry = "0" * 64
- l = 0
- elif l < 64:
- entry += "0" * (64 - l)
-
- write(struct.pack('B', int(l / 32))) # width
- write(struct.pack('B', 0)) # padding
- write(struct.pack('H', 0)) # padding
- write(struct.pack('I', 0)) # padding
-
- i = 0
- for j in range(0, 16):
- for k in range(0, 2):
- if l <= k * 16 * 2:
- c = 0
- else:
- c = int(entry[i:i+2], 16)
- i += 2
-
- write(struct.pack('B', c))
-
-def write_bin(bits):
- write(struct.pack('B', 0x44)) # ASCII: 'D'
- write(struct.pack('B', 0x56)) # ASCII: 'V'
- write(struct.pack('B', 0x44)) # ASCII: 'D'
- write(struct.pack('B', 0x48)) # ASCII: 'H'
- write(struct.pack('B', 0x52)) # ASCII: 'R'
- write(struct.pack('B', 0x4d)) # ASCII: 'M'
- write(struct.pack('B', 0x55)) # ASCII: 'U'
- write(struct.pack('B', 0x46)) # ASCII: 'F'
- write(struct.pack('<I', 0)) # compatible-flags
- write(struct.pack('<I', 0)) # incompatible-flags
- write(struct.pack('<I', 32)) # header-size
- write(struct.pack('<H', 8)) # glyph-header-size
- write(struct.pack('<H', 2)) # glyph-stride
- write(struct.pack('<Q', 32)) # glyph-body-size
-
- # write glyphs
- for idx in range(len(bits)):
- write_bin_entry(bits[idx])
-
-#
-# Parse hex file into "bits" array
-#
-
-def parse_hex_line(bits, line):
- m = re.match(r"^([0-9A-Fa-f]+):([0-9A-Fa-f]+)$", line)
- if m == None:
- return
-
- idx = int(m.group(1), 16)
- val = m.group(2)
-
- # insert skipped lines
- for i in range(len(bits), idx):
- bits.append("")
-
- bits.insert(idx, val)
-
-def parse_hex():
- bits = []
-
- for line in sys.stdin:
- if not line:
- continue
- if line.startswith("#"):
- continue
-
- parse_hex_line(bits, line)
-
- return bits
-
-#
-# In normal mode we simply read line by line from standard-input and write the
-# binary-file to standard-output.
-#
-
-if __name__ == "__main__":
- bits = parse_hex()
- write_bin(bits)
diff --git a/units/systemd-bus-proxyd.service.m4.in b/units/systemd-bus-proxyd.service.m4.in
index ffaf0bdc87..64f5ac7d17 100644
--- a/units/systemd-bus-proxyd.service.m4.in
+++ b/units/systemd-bus-proxyd.service.m4.in
@@ -10,6 +10,7 @@ Description=Legacy D-Bus Protocol Compatibility Daemon
[Service]
ExecStart=@rootlibexecdir@/systemd-bus-proxyd --address=kernel:path=/sys/fs/kdbus/0-system/bus
+ExecReload=@bindir@/busctl --address=unix:path=/run/dbus/system_bus_socket call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus ReloadConfig
NotifyAccess=main
CapabilityBoundingSet=CAP_IPC_OWNER CAP_SETUID CAP_SETGID CAP_SETPCAP m4_ifdef(`HAVE_SMACK', CAP_MAC_ADMIN )
PrivateTmp=yes
diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in
index 19c33959d6..fb1f383cdc 100644
--- a/units/systemd-machined.service.in
+++ b/units/systemd-machined.service.in
@@ -15,7 +15,7 @@ After=machine.slice
[Service]
ExecStart=@rootlibexecdir@/systemd-machined
BusName=org.freedesktop.machine1
-CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
+CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID
WatchdogSec=1min
# Note that machined cannot be placed in a mount namespace, since it
diff --git a/units/user/.gitignore b/units/user/.gitignore
index 6111b10ccf..ce9df9e7e1 100644
--- a/units/user/.gitignore
+++ b/units/user/.gitignore
@@ -1,3 +1,2 @@
/systemd-exit.service
/systemd-bus-proxyd.service
-/systemd-consoled.service
diff --git a/units/user/systemd-bus-proxyd.service.in b/units/user/systemd-bus-proxyd.service.in
index e1e399dc32..6f79707b46 100644
--- a/units/user/systemd-bus-proxyd.service.in
+++ b/units/user/systemd-bus-proxyd.service.in
@@ -10,4 +10,5 @@ Description=Legacy D-Bus Protocol Compatibility Daemon
[Service]
ExecStart=@rootlibexecdir@/systemd-bus-proxyd --address=kernel:path=/sys/fs/kdbus/%U-user/bus
+ExecReload=@bindir@/busctl --address=unix:path=/run/user/%U/bus call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus ReloadConfig
NotifyAccess=main
diff --git a/units/user/systemd-consoled.service.in b/units/user/systemd-consoled.service.in
deleted file mode 100644
index fd7938aa8b..0000000000
--- a/units/user/systemd-consoled.service.in
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-
-[Unit]
-Description=Console Manager and Terminal Emulator
-
-[Service]
-Type=notify
-Restart=always
-RestartSec=0
-ExecStart=@rootlibexecdir@/systemd-consoled