]> git.proxmox.com Git - systemd.git/commitdiff
Imported Upstream version 223
authorMartin Pitt <martin.pitt@ubuntu.com>
Wed, 29 Jul 2015 13:16:01 +0000 (15:16 +0200)
committerMartin Pitt <martin.pitt@ubuntu.com>
Wed, 29 Jul 2015 13:16:01 +0000 (15:16 +0200)
250 files changed:
.gitignore
DISTRO_PORTING
Makefile-man.am
Makefile.am
NEWS
README
TODO
autogen.sh
configure.ac
hwdb/70-mouse.hwdb
man/coredump.conf.xml
man/custom-html.xsl
man/machinectl.xml
man/nss-mymachines.xml
man/sd-bus-errors.xml [new file with mode: 0644]
man/sd_bus_creds_get_pid.xml
man/sd_bus_creds_new_from_pid.xml
man/sd_bus_default.xml
man/sd_bus_error.xml
man/sd_bus_error_add_map.xml [new file with mode: 0644]
man/sd_bus_message_append.xml
man/sd_bus_message_append_array.xml
man/sd_bus_message_append_basic.xml
man/sd_bus_negotiate_fds.xml
man/sd_bus_new.xml
man/sd_bus_request_name.xml
man/sd_pid_get_session.xml
man/systemd-coredump.xml
man/systemd-journald.service.xml
man/systemd.journal-fields.xml
man/systemd.link.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.preset.xml
man/systemd.socket.xml
src/analyze/analyze.c
src/backlight/backlight.c
src/basic/bitmap.c [new file with mode: 0644]
src/basic/bitmap.h [new file with mode: 0644]
src/basic/capability.c
src/basic/cgroup-util.c
src/basic/copy.c
src/basic/exit-status.c
src/basic/fileio-label.c
src/basic/fileio.c
src/basic/fileio.h
src/basic/macro.h
src/basic/missing.h
src/basic/path-util.c
src/basic/process-util.c
src/basic/smack-util.c
src/basic/util.c
src/basic/util.h
src/basic/virt.c
src/binfmt/binfmt.c
src/boot/bootctl.c
src/boot/efi/boot.c
src/bootchart/bootchart.c
src/bus-proxyd/driver.c
src/bus-proxyd/driver.h
src/bus-proxyd/proxy.c
src/bus-proxyd/proxy.h
src/bus-proxyd/synthesize.c
src/bus-proxyd/synthesize.h
src/cgls/cgls.c
src/cgtop/cgtop.c
src/console/Makefile [deleted symlink]
src/console/consoled-display.c [deleted file]
src/console/consoled-manager.c [deleted file]
src/console/consoled-session.c [deleted file]
src/console/consoled-terminal.c [deleted file]
src/console/consoled-workspace.c [deleted file]
src/console/consoled.c [deleted file]
src/console/consoled.h [deleted file]
src/core/automount.c
src/core/busname.c
src/core/execute.c
src/core/job.c
src/core/machine-id-setup.c
src/core/main.c
src/core/mount.c
src/core/path.c
src/core/selinux-access.c
src/core/service.c
src/core/slice.c
src/core/smack-setup.c
src/core/socket.c
src/core/swap.c
src/core/target.c
src/core/unit.c
src/core/unit.h
src/firstboot/firstboot.c
src/gpt-auto-generator/gpt-auto-generator.c
src/hibernate-resume/hibernate-resume.c
src/import/pull-dkr.c
src/journal-remote/journal-gatewayd.c
src/journal/journal-file.c
src/journal/journal-file.h
src/journal/journal-vacuum.c
src/journal/journal-verify.c
src/journal/journalctl.c
src/journal/journald-server.c
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/dhcp-protocol.h
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-terminal/.gitignore [deleted file]
src/libsystemd-terminal/evcat.c [deleted file]
src/libsystemd-terminal/grdev-drm.c [deleted file]
src/libsystemd-terminal/grdev-internal.h [deleted file]
src/libsystemd-terminal/grdev.c [deleted file]
src/libsystemd-terminal/grdev.h [deleted file]
src/libsystemd-terminal/idev-evdev.c [deleted file]
src/libsystemd-terminal/idev-internal.h [deleted file]
src/libsystemd-terminal/idev-keyboard.c [deleted file]
src/libsystemd-terminal/idev.c [deleted file]
src/libsystemd-terminal/idev.h [deleted file]
src/libsystemd-terminal/modeset.c [deleted file]
src/libsystemd-terminal/subterm.c [deleted file]
src/libsystemd-terminal/sysview-internal.h [deleted file]
src/libsystemd-terminal/sysview.c [deleted file]
src/libsystemd-terminal/sysview.h [deleted file]
src/libsystemd-terminal/term-charset.c [deleted file]
src/libsystemd-terminal/term-internal.h [deleted file]
src/libsystemd-terminal/term-page.c [deleted file]
src/libsystemd-terminal/term-parser.c [deleted file]
src/libsystemd-terminal/term-screen.c [deleted file]
src/libsystemd-terminal/term-wcwidth.c [deleted file]
src/libsystemd-terminal/term.h [deleted file]
src/libsystemd-terminal/test-term-page.c [deleted file]
src/libsystemd-terminal/test-term-parser.c [deleted file]
src/libsystemd-terminal/test-unifont.c [deleted file]
src/libsystemd-terminal/unifont-def.h [deleted file]
src/libsystemd-terminal/unifont.c [deleted file]
src/libsystemd-terminal/unifont.h [deleted file]
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-control.c
src/libsystemd/sd-bus/bus-kernel.c
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-objects.c
src/libsystemd/sd-bus/bus-slot.c
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/test-bus-marshal.c
src/libsystemd/sd-bus/test-bus-objects.c
src/libsystemd/sd-bus/test-bus-proxy.c [new file with mode: 0644]
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-netlink/netlink-internal.h
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-types.h
src/login/logind-dbus.c
src/login/logind-seat.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user-dbus.c
src/login/org.freedesktop.login1.conf
src/machine/machine-dbus.c
src/machine/machined-dbus.c
src/machine/org.freedesktop.machine1.conf
src/network/networkd-dhcp4.c
src/network/networkd-link.c
src/network/networkd-netdev-gperf.gperf
src/network/networkd-netdev-macvlan.c
src/network/networkd-netdev-macvlan.h
src/network/networkd-netdev-tunnel.c
src/network/networkd-netdev-tunnel.h
src/network/networkd-netdev-tuntap.c
src/network/networkd-netdev-tuntap.h
src/network/networkd-netdev-vxlan.h
src/network/networkd-netdev.c
src/network/networkd-netdev.h
src/network/networkd-network-bus.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-wait-online.c
src/network/networkd.h
src/nspawn/nspawn.c
src/nss-mymachines/nss-mymachines.c
src/nss-mymachines/nss-mymachines.sym
src/python-systemd/.gitignore [deleted file]
src/python-systemd/Makefile [deleted symlink]
src/python-systemd/__init__.py [deleted file]
src/python-systemd/_daemon.c [deleted file]
src/python-systemd/_journal.c [deleted file]
src/python-systemd/_reader.c [deleted file]
src/python-systemd/daemon.py [deleted file]
src/python-systemd/docs/.gitignore [deleted file]
src/python-systemd/docs/conf.py [deleted file]
src/python-systemd/docs/daemon.rst [deleted file]
src/python-systemd/docs/default.css [deleted file]
src/python-systemd/docs/id128.rst [deleted file]
src/python-systemd/docs/index.rst [deleted file]
src/python-systemd/docs/journal.rst [deleted file]
src/python-systemd/docs/layout.html [deleted file]
src/python-systemd/docs/login.rst [deleted file]
src/python-systemd/id128.c [deleted file]
src/python-systemd/journal.py [deleted file]
src/python-systemd/login.c [deleted file]
src/python-systemd/pyutil.c [deleted file]
src/python-systemd/pyutil.h [deleted file]
src/resolve-host/resolve-host.c
src/resolve/dns-type.c
src/resolve/dns-type.h
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-question.c
src/resolve/resolved-dns-question.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-link.c
src/resolve/resolved-llmnr.c [new file with mode: 0644]
src/resolve/resolved-llmnr.h [new file with mode: 0644]
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/rfkill/rfkill.c
src/shared/dns-domain.c
src/shared/dns-domain.h
src/shared/efivars.c
src/shared/install.c
src/shared/nss-util.h
src/shared/sysctl-util.c
src/sleep/sleep.c
src/systemd/sd-bus-vtable.h
src/systemd/sd-bus.h
src/systemd/sd-dhcp-lease.h
src/test/test-bitmap.c [new file with mode: 0644]
src/test/test-btrfs.c
src/test/test-copy.c
src/test/test-dns-domain.c
src/test/test-fileio.c
src/test/test-util.c
src/tmpfiles/tmpfiles.c
src/udev/ata_id/ata_id.c
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin.c
src/udev/udevd.c
src/user-sessions/user-sessions.c
src/vconsole/vconsole-setup.c
tmpfiles.d/systemd.conf.m4
tools/compile-unifont.py [deleted file]
units/emergency.service.in
units/systemd-machined.service.in
units/systemd-networkd.service.m4.in
units/user/.gitignore
units/user/systemd-consoled.service.in [deleted file]

index 99f361d555b5e137c11f835d1470d5105ffd4937..10622d4dfaa8b48b51ec2c934a4511bcf30fe02c 100644 (file)
@@ -66,7 +66,6 @@
 /systemd-cgls
 /systemd-cgroups-agent
 /systemd-cgtop
-/systemd-consoled
 /systemd-coredump
 /systemd-cryptsetup
 /systemd-cryptsetup-generator
@@ -76,7 +75,6 @@
 /systemd-detect-virt
 /systemd-efi-boot-generator
 /systemd-escape
-/systemd-evcat
 /systemd-export
 /systemd-firstboot
 /systemd-fsck
 /systemd-machine-id-commit
 /systemd-machine-id-setup
 /systemd-machined
-/systemd-modeset
 /systemd-modules-load
 /systemd-networkd
 /systemd-networkd-wait-online
 /systemd-sleep
 /systemd-socket-proxyd
 /systemd-stdio-bridge
-/systemd-subterm
 /systemd-sysctl
 /systemd-system-update-generator
 /systemd-sysusers
 /test-audit-type
 /test-async
 /test-barrier
+/test-bitmap
 /test-boot-timestamp
 /test-btrfs
 /test-bus-benchmark
 /test-bus-match
 /test-bus-objects
 /test-bus-policy
+/test-bus-proxy
 /test-bus-server
 /test-bus-signature
 /test-bus-zero-copy
 /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
index d8e9ded9437b00c86e76def7abc6dabf3ae15bde..07aea865be8367519c99f7b749fa788abd22be0e 100644 (file)
@@ -14,6 +14,7 @@ HOWTO:
             --with-kbd-loadkeys=
             --with-kbd-setfont=
             --with-tty-gid=
+            --with-ntp-servers=
 
         2) Try it out. Play around (as an ordinary user) with
         '/usr/lib/systemd/systemd --test --system' for a test run
@@ -21,6 +22,24 @@ HOWTO:
         print the initial transaction it would execute during boot-up.
         This will also inform you about ordering loops and suchlike
 
+NTP POOL:
+
+        By default, timesyncd uses the Google NTP servers
+        time[1-4].google.com. They serve time that is not standards
+        compliant, and can be up to .5s off. Google does not
+        officially support these servers for the broader
+        audience. Distributions and vendors really should not ship
+        OSes or devices with these NTP servers configured. Instead,
+        please register your own vendor pool at ntp.org and make it
+        the built-in default by passing --with-ntp-servers= to
+        configure. Registering vendor pools is free:
+
+        http://www.pool.ntp.org/en/vendors.html
+
+        Again, if you ship your software or device with the default
+        NTP servers, then you will get served wrong time, and will
+        rely on services that might not be supported for long.
+
 CONTRIBUTING UPSTREAM:
 
         We generally do no longer accept distribution-specific
index 74a1f4cb432cc3d6052405863a109f2062e26777..218a299e91df478a43f84b7cdc818080be8cbffb 100644 (file)
@@ -24,6 +24,7 @@ MANPAGES += \
        man/machine-id.5 \
        man/machine-info.5 \
        man/os-release.5 \
+       man/sd-bus-errors.3 \
        man/sd-daemon.3 \
        man/sd-id128.3 \
        man/sd-journal.3 \
@@ -32,6 +33,7 @@ MANPAGES += \
        man/sd_bus_creds_new_from_pid.3 \
        man/sd_bus_default.3 \
        man/sd_bus_error.3 \
+       man/sd_bus_error_add_map.3 \
        man/sd_bus_message_append.3 \
        man/sd_bus_message_append_array.3 \
        man/sd_bus_message_append_basic.3 \
@@ -161,6 +163,40 @@ MANPAGES += \
        man/udevadm.8
 MANPAGES_ALIAS += \
        man/SD_ALERT.3 \
+       man/SD_BUS_ERROR_ACCESS_DENIED.3 \
+       man/SD_BUS_ERROR_ADDRESS_IN_USE.3 \
+       man/SD_BUS_ERROR_AUTH_FAILED.3 \
+       man/SD_BUS_ERROR_BAD_ADDRESS.3 \
+       man/SD_BUS_ERROR_DISCONNECTED.3 \
+       man/SD_BUS_ERROR_END.3 \
+       man/SD_BUS_ERROR_FAILED.3 \
+       man/SD_BUS_ERROR_FILE_EXISTS.3 \
+       man/SD_BUS_ERROR_FILE_NOT_FOUND.3 \
+       man/SD_BUS_ERROR_INCONSISTENT_MESSAGE.3 \
+       man/SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED.3 \
+       man/SD_BUS_ERROR_INVALID_ARGS.3 \
+       man/SD_BUS_ERROR_INVALID_SIGNATURE.3 \
+       man/SD_BUS_ERROR_IO_ERROR.3 \
+       man/SD_BUS_ERROR_LIMITS_EXCEEDED.3 \
+       man/SD_BUS_ERROR_MAKE_CONST.3 \
+       man/SD_BUS_ERROR_MAP.3 \
+       man/SD_BUS_ERROR_MATCH_RULE_INVALID.3 \
+       man/SD_BUS_ERROR_MATCH_RULE_NOT_FOUND.3 \
+       man/SD_BUS_ERROR_NAME_HAS_NO_OWNER.3 \
+       man/SD_BUS_ERROR_NOT_SUPPORTED.3 \
+       man/SD_BUS_ERROR_NO_MEMORY.3 \
+       man/SD_BUS_ERROR_NO_NETWORK.3 \
+       man/SD_BUS_ERROR_NO_REPLY.3 \
+       man/SD_BUS_ERROR_NO_SERVER.3 \
+       man/SD_BUS_ERROR_NULL.3 \
+       man/SD_BUS_ERROR_PROPERTY_READ_ONLY.3 \
+       man/SD_BUS_ERROR_SERVICE_UNKNOWN.3 \
+       man/SD_BUS_ERROR_TIMEOUT.3 \
+       man/SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN.3 \
+       man/SD_BUS_ERROR_UNKNOWN_INTERFACE.3 \
+       man/SD_BUS_ERROR_UNKNOWN_METHOD.3 \
+       man/SD_BUS_ERROR_UNKNOWN_OBJECT.3 \
+       man/SD_BUS_ERROR_UNKNOWN_PROPERTY.3 \
        man/SD_CRIT.3 \
        man/SD_DEBUG.3 \
        man/SD_EMERG.3 \
@@ -191,6 +227,7 @@ MANPAGES_ALIAS += \
        man/reboot.8 \
        man/sd_bus_creds_get_audit_login_uid.3 \
        man/sd_bus_creds_get_audit_session_id.3 \
+       man/sd_bus_creds_get_augmented_mask.3 \
        man/sd_bus_creds_get_cgroup.3 \
        man/sd_bus_creds_get_cmdline.3 \
        man/sd_bus_creds_get_comm.3 \
@@ -216,6 +253,7 @@ MANPAGES_ALIAS += \
        man/sd_bus_creds_get_uid.3 \
        man/sd_bus_creds_get_unique_name.3 \
        man/sd_bus_creds_get_unit.3 \
+       man/sd_bus_creds_get_user_slice.3 \
        man/sd_bus_creds_get_user_unit.3 \
        man/sd_bus_creds_get_well_known_names.3 \
        man/sd_bus_creds_has_bounding_cap.3 \
@@ -231,10 +269,13 @@ MANPAGES_ALIAS += \
        man/sd_bus_error_get_errno.3 \
        man/sd_bus_error_has_name.3 \
        man/sd_bus_error_is_set.3 \
+       man/sd_bus_error_map.3 \
        man/sd_bus_error_set.3 \
        man/sd_bus_error_set_const.3 \
        man/sd_bus_error_set_errno.3 \
        man/sd_bus_error_set_errnof.3 \
+       man/sd_bus_error_set_errnofv.3 \
+       man/sd_bus_error_setf.3 \
        man/sd_bus_message_append_array_iovec.3 \
        man/sd_bus_message_append_array_memfd.3 \
        man/sd_bus_message_append_array_space.3 \
@@ -244,7 +285,7 @@ MANPAGES_ALIAS += \
        man/sd_bus_message_get_reply_cookie.3 \
        man/sd_bus_message_get_seqnum.3 \
        man/sd_bus_negotiate_creds.3 \
-       man/sd_bus_negotiate_timestamps.3 \
+       man/sd_bus_negotiate_timestamp.3 \
        man/sd_bus_open.3 \
        man/sd_bus_open_system.3 \
        man/sd_bus_open_system_machine.3 \
@@ -408,6 +449,40 @@ MANPAGES_ALIAS += \
        man/udev_unref.3 \
        man/user.conf.d.5
 man/SD_ALERT.3: man/sd-daemon.3
+man/SD_BUS_ERROR_ACCESS_DENIED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_ADDRESS_IN_USE.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_AUTH_FAILED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_BAD_ADDRESS.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_DISCONNECTED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_END.3: man/sd_bus_error_add_map.3
+man/SD_BUS_ERROR_FAILED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_FILE_EXISTS.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_FILE_NOT_FOUND.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_INCONSISTENT_MESSAGE.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_INVALID_ARGS.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_INVALID_SIGNATURE.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_IO_ERROR.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_LIMITS_EXCEEDED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_MAKE_CONST.3: man/sd_bus_error.3
+man/SD_BUS_ERROR_MAP.3: man/sd_bus_error_add_map.3
+man/SD_BUS_ERROR_MATCH_RULE_INVALID.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_MATCH_RULE_NOT_FOUND.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NAME_HAS_NO_OWNER.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NOT_SUPPORTED.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NO_MEMORY.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NO_NETWORK.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NO_REPLY.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NO_SERVER.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_NULL.3: man/sd_bus_error.3
+man/SD_BUS_ERROR_PROPERTY_READ_ONLY.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_SERVICE_UNKNOWN.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_TIMEOUT.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_UNKNOWN_INTERFACE.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_UNKNOWN_METHOD.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_UNKNOWN_OBJECT.3: man/sd-bus-errors.3
+man/SD_BUS_ERROR_UNKNOWN_PROPERTY.3: man/sd-bus-errors.3
 man/SD_CRIT.3: man/sd-daemon.3
 man/SD_DEBUG.3: man/sd-daemon.3
 man/SD_EMERG.3: man/sd-daemon.3
@@ -438,6 +513,7 @@ man/poweroff.8: man/halt.8
 man/reboot.8: man/halt.8
 man/sd_bus_creds_get_audit_login_uid.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_audit_session_id.3: man/sd_bus_creds_get_pid.3
+man/sd_bus_creds_get_augmented_mask.3: man/sd_bus_creds_new_from_pid.3
 man/sd_bus_creds_get_cgroup.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_cmdline.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_comm.3: man/sd_bus_creds_get_pid.3
@@ -463,6 +539,7 @@ man/sd_bus_creds_get_tty.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_uid.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_unique_name.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_unit.3: man/sd_bus_creds_get_pid.3
+man/sd_bus_creds_get_user_slice.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_user_unit.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_get_well_known_names.3: man/sd_bus_creds_get_pid.3
 man/sd_bus_creds_has_bounding_cap.3: man/sd_bus_creds_get_pid.3
@@ -478,10 +555,13 @@ man/sd_bus_error_free.3: man/sd_bus_error.3
 man/sd_bus_error_get_errno.3: man/sd_bus_error.3
 man/sd_bus_error_has_name.3: man/sd_bus_error.3
 man/sd_bus_error_is_set.3: man/sd_bus_error.3
+man/sd_bus_error_map.3: man/sd_bus_error_add_map.3
 man/sd_bus_error_set.3: man/sd_bus_error.3
 man/sd_bus_error_set_const.3: man/sd_bus_error.3
 man/sd_bus_error_set_errno.3: man/sd_bus_error.3
 man/sd_bus_error_set_errnof.3: man/sd_bus_error.3
+man/sd_bus_error_set_errnofv.3: man/sd_bus_error.3
+man/sd_bus_error_setf.3: man/sd_bus_error.3
 man/sd_bus_message_append_array_iovec.3: man/sd_bus_message_append_array.3
 man/sd_bus_message_append_array_memfd.3: man/sd_bus_message_append_array.3
 man/sd_bus_message_append_array_space.3: man/sd_bus_message_append_array.3
@@ -491,7 +571,7 @@ man/sd_bus_message_get_realtime_usec.3: man/sd_bus_message_get_monotonic_usec.3
 man/sd_bus_message_get_reply_cookie.3: man/sd_bus_message_get_cookie.3
 man/sd_bus_message_get_seqnum.3: man/sd_bus_message_get_monotonic_usec.3
 man/sd_bus_negotiate_creds.3: man/sd_bus_negotiate_fds.3
-man/sd_bus_negotiate_timestamps.3: man/sd_bus_negotiate_fds.3
+man/sd_bus_negotiate_timestamp.3: man/sd_bus_negotiate_fds.3
 man/sd_bus_open.3: man/sd_bus_default.3
 man/sd_bus_open_system.3: man/sd_bus_default.3
 man/sd_bus_open_system_machine.3: man/sd_bus_default.3
@@ -657,6 +737,108 @@ man/user.conf.d.5: man/systemd-system.conf.5
 man/SD_ALERT.html: man/sd-daemon.html
        $(html-alias)
 
+man/SD_BUS_ERROR_ACCESS_DENIED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_ADDRESS_IN_USE.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_AUTH_FAILED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_BAD_ADDRESS.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_DISCONNECTED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_END.html: man/sd_bus_error_add_map.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_FAILED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_FILE_EXISTS.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_FILE_NOT_FOUND.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_INCONSISTENT_MESSAGE.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_INVALID_ARGS.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_INVALID_SIGNATURE.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_IO_ERROR.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_LIMITS_EXCEEDED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_MAKE_CONST.html: man/sd_bus_error.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_MAP.html: man/sd_bus_error_add_map.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_MATCH_RULE_INVALID.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_MATCH_RULE_NOT_FOUND.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NAME_HAS_NO_OWNER.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NOT_SUPPORTED.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NO_MEMORY.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NO_NETWORK.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NO_REPLY.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NO_SERVER.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_NULL.html: man/sd_bus_error.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_PROPERTY_READ_ONLY.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_SERVICE_UNKNOWN.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_TIMEOUT.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_UNKNOWN_INTERFACE.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_UNKNOWN_METHOD.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_UNKNOWN_OBJECT.html: man/sd-bus-errors.html
+       $(html-alias)
+
+man/SD_BUS_ERROR_UNKNOWN_PROPERTY.html: man/sd-bus-errors.html
+       $(html-alias)
+
 man/SD_CRIT.html: man/sd-daemon.html
        $(html-alias)
 
@@ -747,6 +929,9 @@ man/sd_bus_creds_get_audit_login_uid.html: man/sd_bus_creds_get_pid.html
 man/sd_bus_creds_get_audit_session_id.html: man/sd_bus_creds_get_pid.html
        $(html-alias)
 
+man/sd_bus_creds_get_augmented_mask.html: man/sd_bus_creds_new_from_pid.html
+       $(html-alias)
+
 man/sd_bus_creds_get_cgroup.html: man/sd_bus_creds_get_pid.html
        $(html-alias)
 
@@ -822,6 +1007,9 @@ man/sd_bus_creds_get_unique_name.html: man/sd_bus_creds_get_pid.html
 man/sd_bus_creds_get_unit.html: man/sd_bus_creds_get_pid.html
        $(html-alias)
 
+man/sd_bus_creds_get_user_slice.html: man/sd_bus_creds_get_pid.html
+       $(html-alias)
+
 man/sd_bus_creds_get_user_unit.html: man/sd_bus_creds_get_pid.html
        $(html-alias)
 
@@ -867,6 +1055,9 @@ man/sd_bus_error_has_name.html: man/sd_bus_error.html
 man/sd_bus_error_is_set.html: man/sd_bus_error.html
        $(html-alias)
 
+man/sd_bus_error_map.html: man/sd_bus_error_add_map.html
+       $(html-alias)
+
 man/sd_bus_error_set.html: man/sd_bus_error.html
        $(html-alias)
 
@@ -879,6 +1070,12 @@ man/sd_bus_error_set_errno.html: man/sd_bus_error.html
 man/sd_bus_error_set_errnof.html: man/sd_bus_error.html
        $(html-alias)
 
+man/sd_bus_error_set_errnofv.html: man/sd_bus_error.html
+       $(html-alias)
+
+man/sd_bus_error_setf.html: man/sd_bus_error.html
+       $(html-alias)
+
 man/sd_bus_message_append_array_iovec.html: man/sd_bus_message_append_array.html
        $(html-alias)
 
@@ -906,7 +1103,7 @@ man/sd_bus_message_get_seqnum.html: man/sd_bus_message_get_monotonic_usec.html
 man/sd_bus_negotiate_creds.html: man/sd_bus_negotiate_fds.html
        $(html-alias)
 
-man/sd_bus_negotiate_timestamps.html: man/sd_bus_negotiate_fds.html
+man/sd_bus_negotiate_timestamp.html: man/sd_bus_negotiate_fds.html
        $(html-alias)
 
 man/sd_bus_open.html: man/sd_bus_default.html
@@ -1740,11 +1937,13 @@ MANPAGES_ALIAS += \
        man/sd_peer_get_session.3 \
        man/sd_peer_get_slice.3 \
        man/sd_peer_get_unit.3 \
+       man/sd_peer_get_user_slice.3 \
        man/sd_peer_get_user_unit.3 \
        man/sd_pid_get_machine_name.3 \
        man/sd_pid_get_owner_uid.3 \
        man/sd_pid_get_slice.3 \
        man/sd_pid_get_unit.3 \
+       man/sd_pid_get_user_slice.3 \
        man/sd_pid_get_user_unit.3 \
        man/sd_seat_can_graphical.3 \
        man/sd_seat_can_multi_session.3 \
@@ -1782,11 +1981,13 @@ man/sd_peer_get_owner_uid.3: man/sd_pid_get_session.3
 man/sd_peer_get_session.3: man/sd_pid_get_session.3
 man/sd_peer_get_slice.3: man/sd_pid_get_session.3
 man/sd_peer_get_unit.3: man/sd_pid_get_session.3
+man/sd_peer_get_user_slice.3: man/sd_pid_get_session.3
 man/sd_peer_get_user_unit.3: man/sd_pid_get_session.3
 man/sd_pid_get_machine_name.3: man/sd_pid_get_session.3
 man/sd_pid_get_owner_uid.3: man/sd_pid_get_session.3
 man/sd_pid_get_slice.3: man/sd_pid_get_session.3
 man/sd_pid_get_unit.3: man/sd_pid_get_session.3
+man/sd_pid_get_user_slice.3: man/sd_pid_get_session.3
 man/sd_pid_get_user_unit.3: man/sd_pid_get_session.3
 man/sd_seat_can_graphical.3: man/sd_seat_get_active.3
 man/sd_seat_can_multi_session.3: man/sd_seat_get_active.3
@@ -1852,6 +2053,9 @@ man/sd_peer_get_slice.html: man/sd_pid_get_session.html
 man/sd_peer_get_unit.html: man/sd_pid_get_session.html
        $(html-alias)
 
+man/sd_peer_get_user_slice.html: man/sd_pid_get_session.html
+       $(html-alias)
+
 man/sd_peer_get_user_unit.html: man/sd_pid_get_session.html
        $(html-alias)
 
@@ -1867,6 +2071,9 @@ man/sd_pid_get_slice.html: man/sd_pid_get_session.html
 man/sd_pid_get_unit.html: man/sd_pid_get_session.html
        $(html-alias)
 
+man/sd_pid_get_user_slice.html: man/sd_pid_get_session.html
+       $(html-alias)
+
 man/sd_pid_get_user_unit.html: man/sd_pid_get_session.html
        $(html-alias)
 
@@ -2015,6 +2222,7 @@ EXTRA_DIST += \
        man/pam_systemd.xml \
        man/resolved.conf.xml \
        man/runlevel.xml \
+       man/sd-bus-errors.xml \
        man/sd-daemon.xml \
        man/sd-id128.xml \
        man/sd-journal.xml \
@@ -2024,6 +2232,7 @@ EXTRA_DIST += \
        man/sd_bus_creds_new_from_pid.xml \
        man/sd_bus_default.xml \
        man/sd_bus_error.xml \
+       man/sd_bus_error_add_map.xml \
        man/sd_bus_message_append.xml \
        man/sd_bus_message_append_array.xml \
        man/sd_bus_message_append_basic.xml \
index 19a3706b18781bfb2a2b9067af8a2d4dbcbcbce8..ce2e19b3f9fb4a586898d0ff23118ec3f571f9f2 100644 (file)
@@ -42,9 +42,9 @@ LIBUDEV_CURRENT=7
 LIBUDEV_REVISION=4
 LIBUDEV_AGE=6
 
-LIBSYSTEMD_CURRENT=9
+LIBSYSTEMD_CURRENT=10
 LIBSYSTEMD_REVISION=0
-LIBSYSTEMD_AGE=9
+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)
@@ -639,7 +638,6 @@ UNINSTALL_DATA_HOOKS += units-uninstall-hook
 
 dist_doc_DATA = \
        README \
-       README.md \
        NEWS \
        CODING_STYLE \
        LICENSE.LGPL2.1 \
@@ -647,7 +645,10 @@ dist_doc_DATA = \
        DISTRO_PORTING \
        src/libsystemd/sd-bus/PORTING-DBUS1 \
        src/libsystemd/sd-bus/DIFFERENCES \
-       src/libsystemd/sd-bus/GVARIANT-SERIALIZATION \
+       src/libsystemd/sd-bus/GVARIANT-SERIALIZATION
+
+EXTRA_DIST += \
+       README.md \
        autogen.sh \
        .dir-locals.el \
        .vimrc \
@@ -789,6 +790,8 @@ libbasic_la_SOURCES = \
        src/basic/siphash24.h \
        src/basic/set.h \
        src/basic/ordered-set.h \
+       src/basic/bitmap.c \
+       src/basic/bitmap.h \
        src/basic/fdset.c \
        src/basic/fdset.h \
        src/basic/prioq.c \
@@ -1410,6 +1413,7 @@ tests += \
        test-time \
        test-hashmap \
        test-set \
+       test-bitmap \
        test-list \
        test-unaligned \
        test-tables \
@@ -1766,6 +1770,12 @@ test_set_SOURCES = \
 test_set_LDADD = \
        libshared.la
 
+test_bitmap_SOURCES = \
+       src/test/test-bitmap.c
+
+test_bitmap_LDADD = \
+       libshared.la
+
 test_xml_SOURCES = \
        src/test/test-xml.c
 
@@ -2985,6 +2995,7 @@ tests += \
        test-bus-cleanup \
        test-bus-server \
        test-bus-match \
+       test-bus-proxy \
        test-bus-kernel \
        test-bus-kernel-bloom \
        test-bus-zero-copy \
@@ -3077,6 +3088,12 @@ test_bus_match_SOURCES = \
 test_bus_match_LDADD = \
        libshared.la
 
+test_bus_proxy_SOURCES = \
+       src/libsystemd/sd-bus/test-bus-proxy.c
+
+test_bus_proxy_LDADD = \
+       libshared.la
+
 test_bus_kernel_SOURCES = \
        src/libsystemd/sd-bus/test-bus-kernel.c
 
@@ -3289,145 +3306,6 @@ tests += \
 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
@@ -5100,6 +4978,8 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-bus.h \
        src/resolve/resolved-link.h \
        src/resolve/resolved-link.c \
+       src/resolve/resolved-llmnr.h \
+       src/resolve/resolved-llmnr.c \
        src/resolve/resolved-def.h \
        src/resolve/resolved-dns-rr.h \
        src/resolve/resolved-dns-rr.c \
@@ -5620,164 +5500,6 @@ EXTRA_DIST += \
        test/loopy.service.d \
        test/loopy.service.d/compat.conf
 
-# ------------------------------------------------------------------------------
-if HAVE_PYTHON_DEVEL
-pkgpyexec_LTLIBRARIES = \
-       _journal.la \
-       id128.la \
-       _daemon.la \
-       _reader.la \
-       login.la
-
-_journal_la_SOURCES = \
-       src/python-systemd/_journal.c
-
-_journal_la_CFLAGS = \
-       $(AM_CFLAGS) \
-       -fvisibility=default \
-       $(PYTHON_DEVEL_CFLAGS)
-
-_journal_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       -shared \
-       -module \
-       -avoid-version
-
-_journal_la_LIBADD = \
-       $(PYTHON_DEVEL_LIBS) \
-       libsystemd.la
-
-id128_la_SOURCES = \
-       src/python-systemd/id128.c \
-       src/python-systemd/pyutil.c \
-       src/python-systemd/pyutil.h
-
-nodist_id128_la_SOURCES = \
-       src/python-systemd/id128-constants.h
-
-id128_la_CFLAGS = \
-       $(AM_CFLAGS) \
-       -fvisibility=default \
-       $(PYTHON_DEVEL_CFLAGS) \
-       -I$(top_builddir)/src/python-systemd
-
-id128_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       -shared \
-       -module \
-       -avoid-version
-
-id128_la_LIBADD = \
-       $(PYTHON_DEVEL_LIBS) \
-       libshared.la \
-       libsystemd.la
-
-_daemon_la_SOURCES = \
-       src/python-systemd/_daemon.c \
-       src/python-systemd/pyutil.c \
-       src/python-systemd/pyutil.h
-
-_daemon_la_CFLAGS = \
-       $(AM_CFLAGS) \
-       -fvisibility=default \
-       $(PYTHON_DEVEL_CFLAGS) \
-       -I$(top_builddir)/src/python-systemd
-
-_daemon_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       -shared \
-       -module \
-       -avoid-version
-
-_daemon_la_LIBADD = \
-       $(PYTHON_DEVEL_LIBS) \
-       libshared.la \
-       libsystemd.la
-
-_reader_la_SOURCES = \
-       src/python-systemd/_reader.c \
-       src/python-systemd/pyutil.c \
-       src/python-systemd/pyutil.h
-
-_reader_la_CFLAGS = \
-       $(AM_CFLAGS) \
-       -fvisibility=default \
-       $(PYTHON_DEVEL_CFLAGS)
-
-_reader_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       -shared \
-       -module \
-       -avoid-version
-
-_reader_la_LIBADD = \
-       $(PYTHON_DEVEL_LIBS) \
-       libshared.la \
-       libsystemd.la
-
-login_la_SOURCES = \
-       src/python-systemd/login.c \
-       src/python-systemd/pyutil.c \
-       src/python-systemd/pyutil.h
-
-login_la_CFLAGS = \
-       $(AM_CFLAGS) \
-       -fvisibility=default \
-       $(PYTHON_DEVEL_CFLAGS)
-
-login_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       -shared \
-       -module \
-       -avoid-version
-
-login_la_LIBADD = \
-       $(PYTHON_DEVEL_LIBS) \
-       libshared.la \
-       libsystemd.la
-
-dist_pkgpyexec_PYTHON = \
-       src/python-systemd/journal.py \
-       src/python-systemd/daemon.py \
-       src/python-systemd/__init__.py
-
-src/python-systemd/id128-constants.h: src/systemd/sd-messages.h
-       $(AM_V_at)$(MKDIR_P) $(dir $@)
-       $(AM_V_GEN)$(SED) -n -r 's/,//g; s/#define (SD_MESSAGE_[A-Z0-9_]+)\s.*/add_id(m, "\1", \1) JOINER/p' <$< >$@
-
-BUILT_SOURCES += \
-       $(nodist_id128_la_SOURCES)
-
-SPHINXOPTS = -D version=$(VERSION) -D release=$(VERSION)
-sphinx-%:
-       $(AM_V_at)test -n "$(SPHINX_BUILD)" || { echo " *** sphinx-build is not available"; exit 1; }
-       $(AM_V_GEN)PYTHONPATH=$(DESTDIR)$(pyexecdir) LD_LIBRARY_PATH=$(DESTDIR)$(libdir) $(SPHINX_BUILD) -b $* $(SPHINXOPTS) $(top_srcdir)/src/python-systemd/docs $(top_builddir)/docs/html/python-systemd/
-       $(AM_V_at)echo Output has been generated in $(abs_top_builddir)/docs/html/python-systemd/
-
-python-shell:
-       $(AM_V_at)echo "Starting python with $(DESTDIR)$(pyexecdir)"
-       $(AM_V_at)PYTHONPATH=$(DESTDIR)$(pyexecdir) LD_LIBRARY_PATH=$(DESTDIR)$(libdir) $(PYTHON)
-
-destdir-sphinx: all
-       dir="$$(mktemp -d /tmp/systemd-install.XXXXXX)" && \
-               $(MAKE) DESTDIR="$$dir" install && \
-               $(MAKE) DESTDIR="$$dir" sphinx-html && \
-               rm -rf "$$dir"
-
-endif
-
-CLEAN_LOCAL_HOOKS += clean-sphinx
-
-.PHONY: python-shell destdir-sphinx clean-sphinx clean-python
-
-clean-sphinx:
-       -rm -rf docs/html/python-systemd/
-
-# Remove Python stuff, e.g. to force rebuilding for a different Python version.
-clean-python:
-       -rm -rf src/python-systemd/.libs src/python-systemd/*.l[ao]
-       -rm -f _daemon.la id128.la _journal.la login.la _reader.la
-
 # ------------------------------------------------------------------------------
 if ENABLE_COMPAT_LIBS
 libsystemd-%.c: src/compat-libs/libsystemd-%.sym
@@ -5950,7 +5672,6 @@ substitutions = \
        '|RC_LOCAL_SCRIPT_PATH_START=$(RC_LOCAL_SCRIPT_PATH_START)|' \
        '|RC_LOCAL_SCRIPT_PATH_STOP=$(RC_LOCAL_SCRIPT_PATH_STOP)|' \
        '|PYTHON=$(PYTHON)|' \
-       '|PYTHON_BINARY=$(PYTHON_BINARY)|' \
        '|NTP_SERVERS=$(NTP_SERVERS)|' \
        '|DNS_SERVERS=$(DNS_SERVERS)|' \
        '|systemuidmax=$(SYSTEM_UID_MAX)|' \
@@ -6251,11 +5972,6 @@ DISTCHECK_CONFIGURE_FLAGS += \
        --with-sysvrcnd-path=
 endif
 
-if HAVE_PYTHON
-DISTCHECK_CONFIGURE_FLAGS += \
-       --with-python
-endif
-
 if ENABLE_SPLIT_USR
 DISTCHECK_CONFIGURE_FLAGS += \
        --enable-split-usr
@@ -6299,6 +6015,9 @@ hwdb-update:
                http://standards.ieee.org/develop/regauth/iab/iab.txt && \
        ./ids-update.pl )
 
+.PHONY: built-sources
+built-sources: $(BUILT_SOURCES)
+
 .PHONY: git-tag
 git-tag:
        git tag -s "v$(VERSION)" -m "systemd $(VERSION)"
@@ -6312,7 +6031,6 @@ www_target = www.freedesktop.org:/srv/www.freedesktop.org/www/software/systemd
 .PHONY: doc-sync
 doc-sync: all destdir-sphinx
        rsync -rlv --delete-excluded --include="*.html" --exclude="*" --omit-dir-times man/ $(www_target)/man/
-       rsync -rlv --delete --omit-dir-times docs/html/python-systemd/ $(www_target)/python-systemd/
 
 .PHONY: gardel
 gardel: upload
diff --git a/NEWS b/NEWS
index 3f80af378331748e1373d4c189a8e20cddb735c9..9fc6cc6e0003403cd886b5f53cd26280646be828 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,69 @@
 systemd System and Service Manager
 
+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, Jan
+        Alexander Steffens (heftig), Johan Ouwerkerk, Jose Carlos Venegas Munoz,
+        Kay Sievers, Lennart Poettering, Lidong Zhong, Martin Pitt, Michael
+        Biebl, Michael Olbrich, Michal Schmidt, Mike Gilbert, Namhyung Kim, Nick
+        Owens, Peter Hutterer, Richard Maw, Steven Allen, Sungbae Yoo, Susant
+        Sahani, Thomas Blume, Thomas Hindoe Paaboel Andersen, Tom Gundersen,
+        Umut Tezduyar Lindskog, Vito Caputo, Vivenzio Pagliari, Zbigniew
+        JÄ™drzejewski-Szmek
+
+        -- Berlin, 2015-XX-XX
+
 CHANGES WITH 222:
 
         * udev does not longer support the WAIT_FOR_SYSFS= key in udev rules.
@@ -411,7 +475,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 +566,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/README b/README
index 53220ff3f21743fd2ed5576eda4519eb5a9842d2..75959497650b63ae59bff341121abbb9c31c3a55 100644 (file)
--- a/README
+++ b/README
@@ -82,11 +82,11 @@ REQUIREMENTS:
           CONFIG_SECCOMP
           CONFIG_CHECKPOINT_RESTORE (for the kcmp() syscall)
 
-        Required for CPUShares in resource control unit settings
+        Required for CPUShares= in resource control unit settings
           CONFIG_CGROUP_SCHED
           CONFIG_FAIR_GROUP_SCHED
 
-        Required for CPUQuota in resource control unit settings
+        Required for CPUQuota= in resource control unit settings
           CONFIG_CFS_BANDWIDTH
 
         For systemd-bootchart, several proc debug interfaces are required:
@@ -97,6 +97,15 @@ REQUIREMENTS:
           CONFIG_EFIVAR_FS
           CONFIG_EFI_PARTITION
 
+        We recommend to turn off Real-Time group scheduling in the
+        kernel when using systemd. RT group scheduling effectively
+        makes RT scheduling unavailable for most userspace, since it
+        requires explicit assignment of RT budgets to each unit whose
+        processes making use of RT. As there's no sensible way to
+        assign these budgets automatically this cannot really be
+        fixed, and it's best to disable group scheduling hence.
+           CONFIG_RT_GROUP_SCHED=n
+
         Note that kernel auditing is broken when used with systemd's
         container code. When using systemd in conjunction with
         containers, please make sure to either turn off auditing at
@@ -261,6 +270,11 @@ WARNINGS:
         false positives will be triggered by code which violates
         some rules but is actually safe.
 
+        Currently, systemd-timesyncd defaults to use the Google NTP
+        servers if not specified otherwise at configure time. You
+        really should not ship an OS or device with this default
+        setting. See DISTRO_PORTING for details.
+
 ENGINEERING AND CONSULTING SERVICES:
         ENDOCODE <https://endocode.com/> offers professional
         engineering and consulting services for systemd. Please
diff --git a/TODO b/TODO
index 2904e2b44542534c5f4030455434b8ef5d008828..9514af5a90862d40a6fd329b0a2c35b3a86e84d4 100644 (file)
--- a/TODO
+++ b/TODO
@@ -51,8 +51,6 @@ Features:
 
 * install: include generator dirs in unit file search paths
 
-* introduce an NSS module that uses machined info to give container UIDs pretty names when user namespacing is used.
-
 * stop using off_t, it's a crazy type. Use uint64_t instead.
 
 * logind: follow PropertiesChanged state more closely, to deal with quick logouts and relogins
@@ -71,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
@@ -98,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.
 
@@ -318,11 +312,11 @@ 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.
-  - .network setting that allows overriding of the hostname to send to the dhcp server
-    http://lists.freedesktop.org/archives/systemd-devel/2014-July/021550.html
   - expose in the API the following bits:
         - option 15, domain name and/or option 119, search list
         - option 12, host name and/or option 81, fqdn
@@ -337,14 +331,13 @@ 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
     we always process them before we process client requests
   - DNSSEC
-        - use base64 for key presentation?
         - add display of private key types (http://tools.ietf.org/html/rfc4034#appendix-A.1.1)?
-        - add nice formatting of DNS timestamps
   - DNS
         - search paths
   - mDNS/DNS-SD
@@ -353,8 +346,6 @@ Features:
   - edns0
   - dname
   - cname on PTR (?)
-  - maybe randomize DNS UDP source ports
-  - maybe compare query section of DNS replies
 
 * Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely
 
index 2d4acdfef167ca6a69424c823ea5d5d67e2829c1..607a9682dd887260faabe1aaee0a33f0403c2374 100755 (executable)
@@ -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
index 5c6273520c2e9d857fb8a6b4744ca0208c20f7d6..a16ca6396b2c3b2fed103062df19e78a2ff54fbc 100644 (file)
@@ -20,7 +20,7 @@
 AC_PREREQ([2.64])
 
 AC_INIT([systemd],
-        [222],
+        [223],
         [http://github.com/systemd/systemd/issues],
         [systemd],
         [http://www.freedesktop.org/wiki/Software/systemd])
@@ -38,6 +38,11 @@ AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-di
 AM_SILENT_RULES([yes])
 AC_CANONICAL_HOST
 AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
+
+AC_CHECK_TOOLS([AR], [gcc-ar ar], [:])
+AC_CHECK_TOOLS([NM], [gcc-nm nm], [:])
+AC_CHECK_TOOLS([RANLIB], [gcc-ranlib ranlib], [:])
+
 LT_PREREQ(2.2)
 LT_INIT([disable-static])
 
@@ -196,7 +201,7 @@ AS_CASE([$CC], [*clang*],
 
 AS_CASE([$CFLAGS], [*-O[[12345sz\ ]]*],
         [CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
-               -flto -ffat-lto-objects])],
+               -flto])],
         [AC_MSG_RESULT([skipping -flto, optimization not enabled])])
 AC_SUBST([OUR_CFLAGS], "$with_cflags $sanitizer_cflags")
 
@@ -238,7 +243,7 @@ AC_CHECK_SIZEOF(rlim_t,,[
 ])
 
 # ------------------------------------------------------------------------------
-# we use python to build the man page index, and for systemd-python
+# we use python to build the man page index
 have_python=no
 AC_ARG_WITH([python],
         [AS_HELP_STRING([--without-python], [Disable building the man page index and systemd-python (default: test)])])
@@ -260,30 +265,7 @@ AS_IF([test "$have_python" != "yes"], [
       AS_IF([test "$with_python" != "no"],
             [AC_MSG_WARN([*** python support not found, some documentation cannot be built])])
 ])
-
 AM_CONDITIONAL([HAVE_PYTHON], [test "x$have_python" = "xyes"])
-AS_IF([test "x$PYTHON_BINARY" = "x"],
-      [AS_IF([test "x$have_python" = "xyes"],
-             [PYTHON_BINARY="$(which "$PYTHON")"],
-             [PYTHON_BINARY=/usr/bin/python])])
-AC_ARG_VAR(PYTHON_BINARY, [Python binary used to launch installed scripts])
-
-AS_IF([test "x$have_python" != "xyes" -a "x$enable_python_devel" = "xyes"],
-      [AC_MSG_ERROR([*** python-devel support requires --with-python])])
-
-have_python_devel=no
-AC_ARG_ENABLE(python_devel, AS_HELP_STRING([--disable-python-devel], [Do not build python modules]))
-AS_IF([test "x$have_python" = "xyes" -a "x$enable_python_devel" != "xno"], [
-      PKG_CHECK_MODULES([PYTHON_DEVEL], [python-${PYTHON_VERSION}],
-            [have_python_devel=yes],
-            [PKG_CHECK_MODULES([PYTHON_DEVEL], [python],
-                  [have_python_devel=yes],
-                  [have_python_devel=no])])
-      AS_IF([test "x$have_python_devel" = xno -a "x$enable_python_devel" = xyes],
-            [AC_MSG_ERROR([*** python-devel support requested but libraries not found])])
-      AC_PATH_PROGS(SPHINX_BUILD, sphinx-build-${PYTHON_VERSION} sphinx-build)
-])
-AM_CONDITIONAL([HAVE_PYTHON_DEVEL], [test "$have_python_devel" = "yes"])
 
 # ------------------------------------------------------------------------------
 
@@ -322,9 +304,10 @@ AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE,
                 IFLA_BOND_AD_INFO,
                 IFLA_VLAN_PROTOCOL,
                 IFLA_VXLAN_REMCSUM_NOPARTIAL,
-                IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+                IFLA_IPTUN_ENCAP_DPORT,
+                IFLA_GRE_ENCAP_DPORT,
                 IFLA_BRIDGE_VLAN_INFO,
-                IFLA_BRPORT_UNICAST_FLOOD,
+                IFLA_BRPORT_LEARNING_SYNC,
                 NDA_IFINDEX,
                 IFA_FLAGS],
 [], [], [[
@@ -1032,7 +1015,8 @@ AC_ARG_WITH(ntp-servers,
         AS_HELP_STRING([--with-ntp-servers=NTPSERVERS],
                 [Space-separated list of default NTP servers]),
         [NTP_SERVERS="$withval"],
-        [NTP_SERVERS="time1.google.com time2.google.com time3.google.com time4.google.com"])
+        [NTP_SERVERS="time1.google.com time2.google.com time3.google.com time4.google.com"
+        AC_MSG_WARN([*** Using Google NTP servers. Please do not ship OSes or devices with these default settings. See DISTRO_PORTING for details!])])
 
 AC_DEFINE_UNQUOTED(NTP_SERVERS, ["$NTP_SERVERS"], [Default NTP Servers])
 AC_SUBST(NTP_SERVERS)
@@ -1186,27 +1170,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]))
@@ -1563,10 +1526,8 @@ AC_MSG_RESULT([
         dbus:                    ${have_dbus}
         nss-myhostname:          ${have_myhostname}
         hwdb:                    ${enable_hwdb}
-        terminal:                ${have_terminal}
         kdbus:                   ${have_kdbus}
         Python:                  ${have_python}
-        Python Headers:          ${have_python_devel}
         man pages:               ${have_manpages}
         test coverage:           ${have_coverage}
         Split /usr:              ${enable_split_usr}
@@ -1587,7 +1548,6 @@ AC_MSG_RESULT([
         SysV init scripts:       ${SYSTEM_SYSVINIT_PATH}
         SysV rc?.d directories:  ${SYSTEM_SYSVRCND_PATH}
         Build Python:            ${PYTHON}
-        Installation Python:     ${PYTHON_BINARY}
         sphinx binary:           ${SPHINX_BUILD}
         PAM modules dir:         ${with_pamlibdir}
         PAM configuration dir:   ${with_pamconfdir}
@@ -1608,6 +1568,4 @@ AC_MSG_RESULT([
         CFLAGS:                  ${OUR_CFLAGS} ${CFLAGS}
         CPPFLAGS:                ${OUR_CPPFLAGS} ${CPPFLAGS}
         LDFLAGS:                 ${OUR_LDFLAGS} ${LDFLAGS}
-        PYTHON_CFLAGS:           ${PYTHON_DEVEL_CFLAGS}
-        PYTHON_LIBS:             ${PYTHON_DEVEL_LIBS}
 ])
index 16333215026ece05b6a83e1dfe9ca73496194fd3..76dcfd1b87bdefe2a139e38141238b4f991c3ff0 100644 (file)
@@ -138,6 +138,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
@@ -247,6 +252,10 @@ mouse:usb:v046dpc05a:name:Logitech USB Optical Mouse:
 mouse:usb:v046dpc065:name:Logitech USB Laser Mouse:
  MOUSE_DPI=1000@125
 
+# Logitech MX Master
+mouse:usb:v046dp4041:name:Logitech MX Master:
+ MOUSE_DPI=1000@166
+
 # Logitech MK260 Wireless Combo Receiver aka M-R0011
 mouse:usb:v046dpc52e:name:Logitech USB Receiver:
  MOUSE_DPI=1000@200
index fd54c59e6bc97106c3a0fdbf6b13a24e8869da29..8e71f7d4ec93f84faee777bf7437523a07a48bad 100644 (file)
@@ -58,7 +58,7 @@
   <refsect1>
     <title>Description</title>
 
-    <para>These files configure the behaviour of
+    <para>These files configure the behavior of
     <citerefentry><refentrytitle>systemd-coredump</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
     a handler for core dumps invoked by the kernel.</para>
   </refsect1>
index b298c216b15efca48585ee0df4c800768e68ce46..3e266e4a7f9c9be695981209037b093a251dd0af 100644 (file)
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
 <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
+<!--
+  - The docbook stylesheet injects empty anchor tags into generated HTML, identified by an auto-generated ID.
+  - Ask the docbook stylesheet to generate reproducible output when generating (these) ID values.
+  - This makes the output of this stylesheet reproducible across identical invocations on the same input,
+  - which is an easy and significant win for achieving reproducible builds.
+  -
+  - It may be even better to strip the empty anchors from the document output in addition to turning on consistent IDs,
+  - for this stylesheet contains its own custom ID logic (for generating permalinks) already.
+ -->
+<xsl:param name="generate.consistent.ids" select="1"/>
 
 <!-- translate man page references to links to html pages -->
 <xsl:template match="citerefentry[not(@project)]">
   </a>
 </xsl:template>
 
-<xsl:template match="refsect1/title|refsect1/info/title">
-  <!-- the ID is output in the block.object call for refsect1 -->
-  <h2>
+<!--
+  - helper template to do conflict resolution between various headings with the same inferred ID attribute/tag from the headerlink template
+  - this conflict resolution is necessary to prevent malformed HTML output (multiple id attributes with the same value)
+  - and it fixes xsltproc warnings during compilation of HTML man pages
+  -
+  - A simple top-to-bottom numbering scheme is implemented for nodes with the same ID value to derive unique ID values for HTML output.
+  - It uses two parameters:
+      templateID  the proposed ID string to use which must be checked for conflicts
+      keyNode     the context node which 'produced' the given templateID.
+  -
+  - Conflicts are detected solely based on keyNode, templateID is not taken into account for that purpose.
+ -->
+<xsl:template name="generateID">
+  <!-- node which generatedID needs to assume as the 'source' of the ID -->
+  <xsl:param name="keyNode"/>
+  <!-- suggested value for generatedID output, a contextually meaningful ID string -->
+  <xsl:param name="templateID"/>
+  <xsl:variable name="conflictSource" select="preceding::refsect1/title|preceding::refsect1/info/title|
+                                             preceding::refsect2/title|preceding::refsect2/info/title|
+                                             preceding::varlistentry/term[1]"/>
+  <xsl:variable name="conflictCount" select="count($conflictSource[. = $keyNode])"/>
+  <xsl:choose>
+    <!-- special case conflictCount = 0 to preserve compatibility with URLs generated by previous versions of this XSL stylesheet where possible -->
+    <xsl:when test="$conflictCount = 0">
+      <xsl:value-of select="$templateID"/>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="concat($templateID, $conflictCount)"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<!--
+  - a helper template to abstract over the structure of generated subheading + permalink HTML output
+  - It helps reduce tedious repetition and groups all actual markup output (as opposed to URL/ID logic) in a single location.
+ -->
+<xsl:template name="permalink">
+  <xsl:param name="nodeType"/> <!-- local name of the element node to generate, e.g. 'h2' for <h2></h2> -->
+  <xsl:param name="nodeContent"/> <!-- nodeset to apply further templates to obtain the content of the subheading/term -->
+  <xsl:param name="linkTitle"/> <!-- value for title attribute of generated permalink, e.g. 'this is a permalink' -->
+
+  <!-- parameters passed to generateID template, otherwise unused. -->
+  <xsl:param name="keyNode"/>
+  <xsl:param name="templateID"/>
+
+  <!--
+    - If stable URLs with fragment markers (references to the ID) turn out not to be important:
+    - generatedID could simply take the value of generate-id(), and various other helper templates may be dropped entirely.
+    - Alternatively if xsltproc is patched to generate reproducible generate-id() output the same simplifications can be
+    - applied at the cost of breaking compatibility with URLs generated from output of previous versions of this stylesheet.
+   -->
+  <xsl:variable name="generatedID">
+    <xsl:call-template name="generateID">
+      <xsl:with-param name="keyNode" select="$keyNode"/>
+      <xsl:with-param name="templateID" select="$templateID"/>
+    </xsl:call-template>
+  </xsl:variable>
+
+  <xsl:element name="{$nodeType}">
     <xsl:attribute name="id">
-      <xsl:call-template name="inline.charseq"/>
+      <xsl:value-of select="$generatedID"/>
     </xsl:attribute>
-    <xsl:apply-templates/>
-    <a>
-      <xsl:attribute name="class">
-        <xsl:text>headerlink</xsl:text>
-      </xsl:attribute>
-      <xsl:attribute name="title">
-        <xsl:text>Permalink to this headline</xsl:text>
-      </xsl:attribute>
-      <xsl:attribute name="href">
-        <xsl:text>#</xsl:text>
-        <xsl:call-template name="inline.charseq"/>
-      </xsl:attribute>
-      <xsl:text>¶</xsl:text>
-    </a>
-  </h2>
+    <xsl:apply-templates select="$nodeContent"/>
+    <a class="headerlink" title="{$linkTitle}" href="#{$generatedID}">¶</a>
+  </xsl:element>
 </xsl:template>
 
-<xsl:template match="refsect2/title|refsect2/info/title">
-  <h3>
-    <xsl:attribute name="id">
+<!-- simple wrapper around permalink to avoid repeating common info for each level of subheading covered by the permalink logic (h2, h3) -->
+<xsl:template name="headerlink">
+  <xsl:param name="nodeType"/>
+  <xsl:call-template name="permalink">
+    <xsl:with-param name="nodeType" select="$nodeType"/>
+    <xsl:with-param name="linkTitle" select="'Permalink to this headline'"/>
+    <xsl:with-param name="nodeContent" select="node()"/>
+    <xsl:with-param name="keyNode" select="."/>
+    <!--
+      - To retain compatibility with IDs generated by previous versions of the template, inline.charseq must be called.
+      - The purpose of that template is to generate markup (according to docbook documentation its purpose is to mark/format something as plain text).
+      - The only reason to call this template is to get the auto-generated text such as brackets ([]) before flattening it.
+     -->
+    <xsl:with-param name="templateID">
       <xsl:call-template name="inline.charseq"/>
-    </xsl:attribute>
-    <xsl:apply-templates/>
-    <a>
-      <xsl:attribute name="class">
-        <xsl:text>headerlink</xsl:text>
-      </xsl:attribute>
-      <xsl:attribute name="title">
-        <xsl:text>Permalink to this headline</xsl:text>
-      </xsl:attribute>
-      <xsl:attribute name="href">
-        <xsl:text>#</xsl:text>
-        <xsl:call-template name="inline.charseq"/>
-      </xsl:attribute>
-      <xsl:text>¶</xsl:text>
-    </a>
-  </h3>
+    </xsl:with-param>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="refsect1/title|refsect1/info/title">
+  <!-- the ID is output in the block.object call for refsect1 -->
+  <xsl:call-template name="headerlink">
+    <xsl:with-param name="nodeType" select="'h2'"/>
+  </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="refsect2/title|refsect2/info/title">
+  <xsl:call-template name="headerlink">
+    <xsl:with-param name="nodeType" select="'h3'"/>
+  </xsl:call-template>
 </xsl:template>
 
 <xsl:template match="varlistentry">
-  <dt>
-    <xsl:attribute name="id">
+  <xsl:call-template name="permalink">
+    <xsl:with-param name="nodeType" select="'dt'"/>
+    <xsl:with-param name="linkTitle" select="'Permalink to this term'"/>
+    <xsl:with-param name="nodeContent" select="term"/>
+    <xsl:with-param name="keyNode" select="term[1]"/>
+    <!--
+      - To retain compatibility with IDs generated by previous versions of the template, inline.charseq must be called.
+      - The purpose of that template is to generate markup (according to docbook documentation its purpose is to mark/format something as plain text).
+      - The only reason to call this template is to get the auto-generated text such as brackets ([]) before flattening it.
+     -->
+    <xsl:with-param name="templateID">
       <xsl:call-template name="inline.charseq">
-        <xsl:with-param name="content">
-          <xsl:copy-of select="term[position()=1]" />
-        </xsl:with-param>
+       <xsl:with-param name="content" select="term[1]"/>
       </xsl:call-template>
-    </xsl:attribute>
-    <xsl:apply-templates select="term"/>
-    <a>
-      <xsl:attribute name="class">
-        <xsl:text>headerlink</xsl:text>
-      </xsl:attribute>
-      <xsl:attribute name="title">
-        <xsl:text>Permalink to this term</xsl:text>
-      </xsl:attribute>
-      <xsl:attribute name="href">
-        <!--        <xsl:call-template name="href.target.uri" /> -->
-        <xsl:text>#</xsl:text>
-        <xsl:call-template name="inline.charseq">
-          <xsl:with-param name="content">
-            <xsl:copy-of select="term[position()=1]" />
-          </xsl:with-param>
-        </xsl:call-template>
-      </xsl:attribute>
-      <xsl:text>¶</xsl:text>
-    </a>
-  </dt>
+    </xsl:with-param>
+  </xsl:call-template>
   <dd>
     <xsl:apply-templates select="listitem"/>
   </dd>
       <xsl:text>systemd.directives.html</xsl:text>
     </xsl:attribute>
     <xsl:text>Directives </xsl:text>
-  </a>·
-  <a>
-    <xsl:attribute name="href">
-      <xsl:text>../python-systemd/index.html</xsl:text>
-    </xsl:attribute>
-    <xsl:text>Python </xsl:text>
   </a>
 
   <span style="float:right">
index 4b8787085363ed7f0af846aab090a53491f910af..a5eb3f08e469efae9ed7a1a1f6c02b56e06d483e 100644 (file)
         <filename>/var/lib/machines/</filename>, that is named after
         the specified URL and its HTTP etag. A writable snapshot is
         then taken from this subvolume, and named after the specified
-        local name. This behaviour ensures that creating multiple
+        local name. This behavior ensures that creating multiple
         container instances of the same URL is efficient, as multiple
         downloads are not necessary. In order to create only the
         read-only image, and avoid creating its writable snapshot,
         machine name. To omit creation of the local, writable copy
         pass <literal>-</literal> as local machine name.</para>
 
-        <para>Similar to the behaviour of <command>pull-tar</command>,
+        <para>Similar to the behavior of <command>pull-tar</command>,
         the read-only image is prefixed with
         <filename>.raw-</filename>, and thus not shown by
         <command>list-images</command>, unless <option>--all</option>
index eb1ed2592b835f3b80df8e537f7bcb8c0cc93c94..41ec458e4b87835ac7b1b273f6c11e761f864d68 100644 (file)
     <para><command>nss-mymachines</command> is a plugin for the GNU
     Name Service Switch (NSS) functionality of the GNU C Library
     (<command>glibc</command>) providing hostname resolution for
-    containers running locally, that are registered with
+    container names of containers running locally, that are registered
+    with
     <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-    The container names are resolved to IP addresses of the specific
-    container, ordered by their scope.</para>
+    The container names are resolved to the IP addresses of the
+    specific container, ordered by their scope.</para>
+
+    <para>The module also resolves user IDs used by containers to user
+    names indicating the container name, and back.</para>
 
     <para>To activate the NSS modules, <literal>mymachines</literal>
-    has to be added to the line starting with
-    <literal>hosts:</literal> in
+    has to be added to the lines starting with
+    <literal>hosts:</literal>, <literal>passwd:</literal> and
+    <literal>group:</literal> in
     <filename>/etc/nsswitch.conf</filename>.</para>
 
     <para>It is recommended to place <literal>mymachines</literal>
-    near the end of the <filename>nsswitch.conf</filename> line to
-    make sure that this mapping is only used as fallback, and any DNS
-    or <filename>/etc/hosts</filename> based mapping takes
-    precedence.</para>
+    near the end of the <filename>nsswitch.conf</filename> lines to
+    make sure that its mappings are only used as fallback, and any
+    other mappings, such as DNS or <filename>/etc/hosts</filename>
+    based mappings take precedence.</para>
   </refsect1>
 
   <refsect1>
     <para>Here's an example <filename>/etc/nsswitch.conf</filename>
     file, that enables <command>mymachines</command> correctly:</para>
 
-<programlisting>passwd:   compat
-group:    compat
-shadow:   compat
+    <programlisting>passwd:         compat <command>mymachines</command>
+group:          compat <command>mymachines</command>
+shadow:         compat
 
-hosts:    files dns <command>mymachines</command> myhostname
+hosts:          files dns <command>mymachines</command> myhostname
 networks:       files
 
 protocols:      db files
 services:       db files
-ethers:   db files
-rpc:      db files
+ethers:         db files
+rpc:            db files
 
 netgroup:       nis</programlisting>
 
diff --git a/man/sd-bus-errors.xml b/man/sd-bus-errors.xml
new file mode 100644 (file)
index 0000000..a1e8462
--- /dev/null
@@ -0,0 +1,309 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!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 2015 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="sd-bus-errors">
+
+  <refentryinfo>
+    <title>sd-bus-errors</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>sd-bus-errors</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd-bus-errors</refname>
+    <refname>SD_BUS_ERROR_FAILED</refname>
+    <refname>SD_BUS_ERROR_NO_MEMORY</refname>
+    <refname>SD_BUS_ERROR_SERVICE_UNKNOWN</refname>
+    <refname>SD_BUS_ERROR_NAME_HAS_NO_OWNER</refname>
+    <refname>SD_BUS_ERROR_NO_REPLY</refname>
+    <refname>SD_BUS_ERROR_IO_ERROR</refname>
+    <refname>SD_BUS_ERROR_BAD_ADDRESS</refname>
+    <refname>SD_BUS_ERROR_NOT_SUPPORTED</refname>
+    <refname>SD_BUS_ERROR_LIMITS_EXCEEDED</refname>
+    <refname>SD_BUS_ERROR_ACCESS_DENIED</refname>
+    <refname>SD_BUS_ERROR_AUTH_FAILED</refname>
+    <refname>SD_BUS_ERROR_NO_SERVER</refname>
+    <refname>SD_BUS_ERROR_TIMEOUT</refname>
+    <refname>SD_BUS_ERROR_NO_NETWORK</refname>
+    <refname>SD_BUS_ERROR_ADDRESS_IN_USE</refname>
+    <refname>SD_BUS_ERROR_DISCONNECTED</refname>
+    <refname>SD_BUS_ERROR_INVALID_ARGS</refname>
+    <refname>SD_BUS_ERROR_FILE_NOT_FOUND</refname>
+    <refname>SD_BUS_ERROR_FILE_EXISTS</refname>
+    <refname>SD_BUS_ERROR_UNKNOWN_METHOD</refname>
+    <refname>SD_BUS_ERROR_UNKNOWN_OBJECT</refname>
+    <refname>SD_BUS_ERROR_UNKNOWN_INTERFACE</refname>
+    <refname>SD_BUS_ERROR_UNKNOWN_PROPERTY</refname>
+    <refname>SD_BUS_ERROR_PROPERTY_READ_ONLY</refname>
+    <refname>SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN</refname>
+    <refname>SD_BUS_ERROR_INVALID_SIGNATURE</refname>
+    <refname>SD_BUS_ERROR_INCONSISTENT_MESSAGE</refname>
+    <refname>SD_BUS_ERROR_MATCH_RULE_NOT_FOUND</refname>
+    <refname>SD_BUS_ERROR_MATCH_RULE_INVALID</refname>
+    <refname>SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED</refname>
+
+    <refpurpose>Standard D-Bus error names</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+<funcsynopsisinfo>#define SD_BUS_ERROR_FAILED                     "org.freedesktop.DBus.Error.Failed"
+#define SD_BUS_ERROR_NO_MEMORY                  "org.freedesktop.DBus.Error.NoMemory"
+#define SD_BUS_ERROR_SERVICE_UNKNOWN            "org.freedesktop.DBus.Error.ServiceUnknown"
+#define SD_BUS_ERROR_NAME_HAS_NO_OWNER          "org.freedesktop.DBus.Error.NameHasNoOwner"
+#define SD_BUS_ERROR_NO_REPLY                   "org.freedesktop.DBus.Error.NoReply"
+#define SD_BUS_ERROR_IO_ERROR                   "org.freedesktop.DBus.Error.IOError"
+#define SD_BUS_ERROR_BAD_ADDRESS                "org.freedesktop.DBus.Error.BadAddress"
+#define SD_BUS_ERROR_NOT_SUPPORTED              "org.freedesktop.DBus.Error.NotSupported"
+#define SD_BUS_ERROR_LIMITS_EXCEEDED            "org.freedesktop.DBus.Error.LimitsExceeded"
+#define SD_BUS_ERROR_ACCESS_DENIED              "org.freedesktop.DBus.Error.AccessDenied"
+#define SD_BUS_ERROR_AUTH_FAILED                "org.freedesktop.DBus.Error.AuthFailed"
+#define SD_BUS_ERROR_NO_SERVER                  "org.freedesktop.DBus.Error.NoServer"
+#define SD_BUS_ERROR_TIMEOUT                    "org.freedesktop.DBus.Error.Timeout"
+#define SD_BUS_ERROR_NO_NETWORK                 "org.freedesktop.DBus.Error.NoNetwork"
+#define SD_BUS_ERROR_ADDRESS_IN_USE             "org.freedesktop.DBus.Error.AddressInUse"
+#define SD_BUS_ERROR_DISCONNECTED               "org.freedesktop.DBus.Error.Disconnected"
+#define SD_BUS_ERROR_INVALID_ARGS               "org.freedesktop.DBus.Error.InvalidArgs"
+#define SD_BUS_ERROR_FILE_NOT_FOUND             "org.freedesktop.DBus.Error.FileNotFound"
+#define SD_BUS_ERROR_FILE_EXISTS                "org.freedesktop.DBus.Error.FileExists"
+#define SD_BUS_ERROR_UNKNOWN_METHOD             "org.freedesktop.DBus.Error.UnknownMethod"
+#define SD_BUS_ERROR_UNKNOWN_OBJECT             "org.freedesktop.DBus.Error.UnknownObject"
+#define SD_BUS_ERROR_UNKNOWN_INTERFACE          "org.freedesktop.DBus.Error.UnknownInterface"
+#define SD_BUS_ERROR_UNKNOWN_PROPERTY           "org.freedesktop.DBus.Error.UnknownProperty"
+#define SD_BUS_ERROR_PROPERTY_READ_ONLY         "org.freedesktop.DBus.Error.PropertyReadOnly"
+#define SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN    "org.freedesktop.DBus.Error.UnixProcessIdUnknown"
+#define SD_BUS_ERROR_INVALID_SIGNATURE          "org.freedesktop.DBus.Error.InvalidSignature"
+#define SD_BUS_ERROR_INCONSISTENT_MESSAGE       "org.freedesktop.DBus.Error.InconsistentMessage"
+#define SD_BUS_ERROR_MATCH_RULE_NOT_FOUND       "org.freedesktop.DBus.Error.MatchRuleNotFound"
+#define SD_BUS_ERROR_MATCH_RULE_INVALID         "org.freedesktop.DBus.Error.MatchRuleInvalid"
+#define SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED \
+                                                "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired"</funcsynopsisinfo>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>In addition to the error names user programs define, D-Bus
+    knows a number of generic, standardized error names, that are
+    listed below.</para>
+
+    <para>In addition to this list, in sd-bus the special error
+    namespace <literal>System.Error.</literal> is used to map
+    arbitrary Linux system errors (as defined by <citerefentry
+    project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
+    to D-Bus errors and back. For example, the error
+    <constant>EUCLEAN</constant> is mapped to
+    <literal>System.Error.EUCLEAN</literal> and back.</para>
+
+    <variablelist>
+
+      <varlistentry>
+         <term><varname>SD_BUS_ERROR_FAILED</varname></term>
+         <listitem><para>A generic error indication. See the error
+         message for further details. This error name should be
+         avoided, in favor of a more expressive error
+         name.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_NO_MEMORY</varname></term>
+        <listitem><para>A memory allocation failed, and the requested
+        operation could not be completed.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_SERVICE_UNKNOWN</varname></term>
+        <listitem><para>The contacted bus service is unknown and
+        cannot be activated.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_NAME_HAS_NO_OWNER</varname></term>
+        <listitem><para>The specified bus service name currently has
+        no owner.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_NO_REPLY</varname></term>
+        <listitem><para>A message did not receive a reply. This error
+        is usually generated after a timeout.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_IO_ERROR</varname></term>
+        <listitem><para>Generic input/output error, for example when
+        accessing a socket or other IO context.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_BAD_ADDRESS</varname></term>
+        <listitem><para>The specified D-Bus bus address string is
+        malformed.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_NOT_SUPPORTED</varname></term>
+        <listitem><para>The requested operation is not supported on
+        the local system.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_LIMITS_EXCEEDED</varname></term>
+        <listitem><para>Some limited resource has been
+        exhausted.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_ACCESS_DENIED</varname></term>
+        <listitem><para>Access to a resource has been denied, due to security restrictions.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_AUTH_FAILED</varname></term>
+        <listitem><para>Authentication did not complete successfully.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_NO_SERVER</varname></term>
+        <listitem><para>Unable to connect to the specified server.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_TIMEOUT</varname></term>
+        <listitem><para>An operation timed out. Note that method calls
+        which timeout generate a
+        <varname>SD_BUS_ERROR_NO_REPLY</varname>.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_NO_NETWORK</varname></term>
+        <listitem><para>No network available to execute requested network operation on.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_ADDRESS_IN_USE</varname></term>
+        <listitem><para>The specified network address is already being listened on.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_DISCONNECTED</varname></term>
+        <listitem><para>The connection has been terminated.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_INVALID_ARGS</varname></term>
+        <listitem><para>One or more invalid arguments have been passed.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_FILE_NOT_FOUND</varname></term>
+        <listitem><para>The requested file could not be found.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_FILE_EXISTS</varname></term>
+        <listitem><para>The requested file exists already.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_UNKNOWN_METHOD</varname></term>
+        <listitem><para>The requested method does not exist in the selected interface.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_UNKNOWN_OBJECT</varname></term>
+        <listitem><para>The requested object does not exist in the selected service.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_UNKNOWN_INTERFACE</varname></term>
+        <listitem><para>The requested interface does not exist on the selected object.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_UNKNOWN_PROPERTY</varname></term>
+        <listitem><para>The requested property does not exist in the selected interface.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_PROPERTY_READ_ONLY</varname></term>
+        <listitem><para>A write operation was requested on a read-only property.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN</varname></term>
+        <listitem><para>The requested PID is not known.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_INVALID_SIGNATURE</varname></term>
+        <listitem><para>The specified message signature is not
+        valid.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_INCONSISTENT_MESSAGE</varname></term>
+        <listitem><para>The passed message does not validate
+        correctly.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_MATCH_RULE_NOT_FOUND</varname></term>
+        <listitem><para>The specified match rule does not exist.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_MATCH_RULE_INVALID</varname></term>
+        <listitem><para>The specified match rule is invalid.</para></listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED</varname></term>
+        <listitem><para>Access to the requested operation is not
+        permitted, however, it might be available after interactive
+        authentication. This is usually returned by method calls
+        supporting a framework for additional interactive
+        authorization, when interactive authorization was not enabled
+        with the
+        <citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        for the method call message.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Notes</title>
+
+    <para>The various error definitions described here are available
+    as a shared library, which can be compiled and linked to with the
+    <constant>libsystemd</constant> <citerefentry
+    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    file.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='die-net'><refentrytitle>strerror</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 13f885cd5db2b21e920cda16156dfacaae840930..4162fab0650b74b77c360df97b14366bab245a10 100644 (file)
@@ -61,8 +61,9 @@
     <refname>sd_bus_creds_get_cmdline</refname>
     <refname>sd_bus_creds_get_cgroup</refname>
     <refname>sd_bus_creds_get_unit</refname>
-    <refname>sd_bus_creds_get_user_unit</refname>
     <refname>sd_bus_creds_get_slice</refname>
+    <refname>sd_bus_creds_get_user_unit</refname>
+    <refname>sd_bus_creds_get_user_slice</refname>
     <refname>sd_bus_creds_get_session</refname>
     <refname>sd_bus_creds_get_owner_uid</refname>
     <refname>sd_bus_creds_has_effective_cap</refname>
         <paramdef>const char **<parameter>unit</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_creds_get_slice</function></funcdef>
+        <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
+        <paramdef>const char **<parameter>slice</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_creds_get_user_unit</function></funcdef>
         <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
-        <funcdef>int <function>sd_bus_creds_get_slice</function></funcdef>
+        <funcdef>int <function>sd_bus_creds_get_user_slice</function></funcdef>
         <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
         <paramdef>const char **<parameter>slice</parameter></paramdef>
       </funcprototype>
   <refsect1>
     <title>Description</title>
 
-    <para>These functions return information from an
-    <parameter>sd_bus_creds</parameter> credential object. Credential
-    objects may be created with
+    <para>These functions return credential information from an
+    <parameter>sd_bus_creds</parameter> object. Credential objects may
+    be created with
     <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     in which case they describe the credentials of the process
     identified by the specified PID, with
     in which case they describe the credentials of the creator of a
     bus, or with
     <citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-    in which case they describe the credentials of the sender of the message.</para>
+    in which case they describe the credentials of the sender of the
+    message.</para>
+
+    <para>Not all credential fields are part of every
+    <literal>sd_bus_creds</literal> object. Use
+    <citerefentry><refentrytitle>sd_bus_creds_get_mask</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    to determine the mask of fields available.</para>
 
     <para><function>sd_bus_creds_get_pid()</function> will retrieve
     the PID (process identifier). Similar,
     <para><function>sd_bus_creds_get_slice()</function> will retrieve
     the systemd slice (a unit in the system instance of systemd) that
     the process is part of. See
-    <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Similar,
+    <function>sd_bus_creds_get_user_slice()</function> retrieves the
+    systemd slice of the process, in the user instance of systemd.
     </para>
 
     <para><function>sd_bus_creds_get_session()</function> will
-    retrieve the logind session that the process is part of. See
+    retrieve the identifier of the login session that the process is
+    part of. See
     <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. For
     processes that are not part of a session returns -ENXIO.
     </para>
 
     <para><function>sd_bus_creds_get_owner_uid()</function> will
     retrieve the numeric UID (user identifier) of the user who owns
-    the session that the process is part of. See
-    <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    the login session that the process is part of. See
+    <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     For processes that are not part of a session returns -ENXIO.
     </para>
 
     <parameter>capability</parameter> was set in the effective
     capabilities mask. A positive return value means that is was
     set, zero means that it was not set, and a negative return
-    value signifies an error. See
+    value indicates an error. See
     <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     and <varname>Capabilities=</varname> and
     <varname>CapabilityBoundingSet=</varname> settings in
     processes that are not part of an audit session.</para>
 
     <para><function>sd_bus_creds_get_tty()</function> will retrieve
-    the controlling TTY. Returns -ENXIO for processes that have no
-    controlling TTY.</para>
+    the controlling TTY, without the prefixing "/dev/". Returns -ENXIO
+    for processes that have no controlling TTY.</para>
 
     <para><function>sd_bus_creds_get_unique_name()</function> will
     retrieve the D-Bus unique name. See <ulink
         <listitem><para>Given field is not specified for the described
         process or peer. This will be returned by
         <function>sd_bus_get_unit()</function>,
-        <function>sd_bus_get_user_unit()</function>,
         <function>sd_bus_get_slice()</function>,
+        <function>sd_bus_get_user_unit()</function>,
+        <function>sd_bus_get_user_slice()</function>,
         <function>sd_bus_get_session()</function>, and
         <function>sd_bus_get_owner_uid()</function> if the process is
         not part of a systemd system unit, systemd user unit, systemd
   <refsect1>
     <title>Notes</title>
 
-    <para><function>sd_bus_open_user()</function> and other functions
-    described here are available as a shared library, which can be
-    compiled and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <para><function>sd_bus_creds_get_pid()</function> and the other
+    functions described here are available as a shared library, which
+    can be compiled and linked to with the
+    <constant>libsystemd</constant> <citerefentry
+    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     file.</para>
   </refsect1>
 
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>execve</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>execve</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>proc</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
index 8c054a59051ea337c30f195fb576f061ea8880f3..a78d3f5717a91d04aadb4655737322ed091d7195 100644 (file)
@@ -45,6 +45,7 @@
   <refnamediv>
     <refname>sd_bus_creds_new_from_pid</refname>
     <refname>sd_bus_creds_get_mask</refname>
+    <refname>sd_bus_creds_get_augmented_mask</refname>
     <refname>sd_bus_creds_ref</refname>
     <refname>sd_bus_creds_unref</refname>
 
         <paramdef>const sd_bus_creds *<parameter>c</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>uint64_t <function>sd_bus_creds_get_augmented_mask</function></funcdef>
+        <paramdef>const sd_bus_creds *<parameter>c</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>sd_bus_creds *<function>sd_bus_creds_ref</function></funcdef>
         <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
 
     <para>
       <constant>SD_BUS_CREDS_PID</constant>,
+      <constant>SD_BUS_CREDS_PPID</constant>,
       <constant>SD_BUS_CREDS_TID</constant>,
       <constant>SD_BUS_CREDS_UID</constant>,
+      <constant>SD_BUS_CREDS_EUID</constant>,
+      <constant>SD_BUS_CREDS_SUID</constant>,
+      <constant>SD_BUS_CREDS_FSUID</constant>,
       <constant>SD_BUS_CREDS_GID</constant>,
+      <constant>SD_BUS_CREDS_EGID</constant>,
+      <constant>SD_BUS_CREDS_SGID</constant>,
+      <constant>SD_BUS_CREDS_FSGID</constant>,
+      <constant>SD_BUS_CREDS_SUPPLEMENTARY_GIDS</constant>,
       <constant>SD_BUS_CREDS_COMM</constant>,
       <constant>SD_BUS_CREDS_TID_COMM</constant>,
       <constant>SD_BUS_CREDS_EXE</constant>,
       <constant>SD_BUS_CREDS_CMDLINE</constant>,
       <constant>SD_BUS_CREDS_CGROUP</constant>,
       <constant>SD_BUS_CREDS_UNIT</constant>,
-      <constant>SD_BUS_CREDS_USER_UNIT</constant>,
       <constant>SD_BUS_CREDS_SLICE</constant>,
+      <constant>SD_BUS_CREDS_USER_UNIT</constant>,
+      <constant>SD_BUS_CREDS_USER_SLICE</constant>,
       <constant>SD_BUS_CREDS_SESSION</constant>,
       <constant>SD_BUS_CREDS_OWNER_UID</constant>,
       <constant>SD_BUS_CREDS_EFFECTIVE_CAPS</constant>,
       <constant>SD_BUS_CREDS_SELINUX_CONTEXT</constant>,
       <constant>SD_BUS_CREDS_AUDIT_SESSION_ID</constant>,
       <constant>SD_BUS_CREDS_AUDIT_LOGIN_UID</constant>,
+      <constant>SD_BUS_CREDS_TTY</constant>,
       <constant>SD_BUS_CREDS_UNIQUE_NAME</constant>,
       <constant>SD_BUS_CREDS_WELL_KNOWN_NAMES</constant>,
+      <constant>SD_BUS_CREDS_DESCRIPTION</constant>,
+      <constant>SD_BUS_CREDS_AUGMENT</constant>,
       <constant>_SD_BUS_CREDS_ALL</constant>
     </para>
   </refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_creds_new_from_pid()</function> creates a new
-    credentials object and fills it with information about the process
-    <parameter>pid</parameter>. This pointer to this object will
-    be stored in <parameter>ret</parameter> pointer.</para>
+    <para><function>sd_bus_creds_new_from_pid()</function> creates a
+    new credentials object and fills it with information about the
+    process <parameter>pid</parameter>. The pointer to this object
+    will be stored in <parameter>ret</parameter> pointer. Note that
+    credential objects may also be created and retrieved via
+    <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
     <para>The information that will be stored is determined by
     <parameter>creds_mask</parameter>. It may contain a subset of ORed
     constants <constant>SD_BUS_CREDS_PID</constant>,
+    <constant>SD_BUS_CREDS_PPID</constant>,
     <constant>SD_BUS_CREDS_TID</constant>,
     <constant>SD_BUS_CREDS_UID</constant>,
+    <constant>SD_BUS_CREDS_EUID</constant>,
+    <constant>SD_BUS_CREDS_SUID</constant>,
+    <constant>SD_BUS_CREDS_FSUID</constant>,
     <constant>SD_BUS_CREDS_GID</constant>,
+    <constant>SD_BUS_CREDS_EGID</constant>,
+    <constant>SD_BUS_CREDS_SGID</constant>,
+    <constant>SD_BUS_CREDS_FSGID</constant>,
+    <constant>SD_BUS_CREDS_SUPPLEMENTARY_GIDS</constant>,
     <constant>SD_BUS_CREDS_COMM</constant>,
     <constant>SD_BUS_CREDS_TID_COMM</constant>,
     <constant>SD_BUS_CREDS_EXE</constant>,
     <constant>SD_BUS_CREDS_CMDLINE</constant>,
     <constant>SD_BUS_CREDS_CGROUP</constant>,
     <constant>SD_BUS_CREDS_UNIT</constant>,
-    <constant>SD_BUS_CREDS_USER_UNIT</constant>,
     <constant>SD_BUS_CREDS_SLICE</constant>,
+    <constant>SD_BUS_CREDS_USER_UNIT</constant>,
+    <constant>SD_BUS_CREDS_USER_SLICE</constant>,
     <constant>SD_BUS_CREDS_SESSION</constant>,
     <constant>SD_BUS_CREDS_OWNER_UID</constant>,
     <constant>SD_BUS_CREDS_EFFECTIVE_CAPS</constant>,
     <constant>SD_BUS_CREDS_SELINUX_CONTEXT</constant>,
     <constant>SD_BUS_CREDS_AUDIT_SESSION_ID</constant>,
     <constant>SD_BUS_CREDS_AUDIT_LOGIN_UID</constant>,
+    <constant>SD_BUS_CREDS_TTY</constant>,
     <constant>SD_BUS_CREDS_UNIQUE_NAME</constant>,
     <constant>SD_BUS_CREDS_WELL_KNOWN_NAMES</constant>,
-    or <constant>_SD_BUS_CREDS_ALL</constant> to indicate
-    all known fields.</para>
+    <constant>SD_BUS_CREDS_DESCRIPTION</constant>. Use the special
+    value <constant>_SD_BUS_CREDS_ALL</constant> to request all
+    supported fields. The <constant>SD_BUS_CREDS_AUGMENT</constant>
+    may not be ORed into the mask for invocations of
+    <function>sd_bus_creds_new_from_pid()</function>.</para>
 
     <para>Fields can be retrieved from the credentials object using
     <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     and other functions which correspond directly to the constants
     listed above.</para>
 
-    <para>A mask of fields which were actually successfully set
-    (acquired from <filename>/proc</filename>, etc.) can be retrieved
-    with <function>sd_bus_creds_get_mask()</function>. If the
-    credentials object was created with
+    <para>A mask of fields which were actually successfully retrieved
+    can be retrieved with
+    <function>sd_bus_creds_get_mask()</function>. If the credentials
+    object was created with
     <function>sd_bus_creds_new_from_pid()</function>, this will be a
     subset of fields requested in <parameter>creds_mask</parameter>.
     </para>
 
-    <para><function>sd_bus_creds_ref</function> creates a new
+    <para>Similar to <function>sd_bus_creds_get_mask()</function> the
+    function <function>sd_bus_creds_get_augmented_mask()</function>
+    returns a bitmask of field constants. The mask indicates which
+    credential fields have been retrieved in a non-atomic fashion. For
+    credential objects created via
+    <function>sd_bus_creds_new_from_pid()</function> this mask will be
+    identical to the mask returned by
+    <function>sd_bus_creds_get_mask()</function>. However, for
+    credential objects retrieved via
+    <function>sd_bus_get_name_creds()</function> this mask will be set
+    for the credential fields that could not be determined atomically
+    at peer connection time, and which were later added by reading
+    augmenting credential data from
+    <filename>/proc</filename>. Similar, for credential objects
+    retrieved via <function>sd_bus_get_owner_creds()</function> the
+    mask is set for the fields that could not be determined atomically
+    at bus creation time, but have been augmented. Similar, for
+    credential objects retrieved via
+    <function>sd_bus_message_get_creds()</function> the mask is set
+    for the fields that could not be determined atomically at message
+    send time, but have been augmented. The mask returned by
+    <function>sd_bus_creds_get_augmented_mask()</function> is always a
+    subset of (or identical to) the mask returned by
+    <function>sd_bus_creds_get_mask()</function> for the same
+    object. The latter call hence returns all credential fields
+    available in the credential object, the former then marks the
+    subset of those that have been augmented. Note that augmented
+    fields are unsuitable for authorization decisions as they may be
+    retrieved at different times, thus being subject to races. Hence
+    augmented fields should be used exclusively for informational
+    purposes.
+    </para>
+
+    <para><function>sd_bus_creds_ref()</function> creates a new
     reference to the credentials object <parameter>c</parameter>. This
     object will not be destroyed until
-    <function>sd_bus_creds_unref</function> has been called as many
+    <function>sd_bus_creds_unref()</function> has been called as many
     times plus once more. Once the reference count has dropped to zero,
     <parameter>c</parameter> cannot be used anymore, so further
     calls to <function>sd_bus_creds_ref(c)</function> or
     <function>sd_bus_creds_unref(c)</function> are illegal.</para>
 
-    <para><function>sd_bus_creds_unref</function> destroys a reference
+    <para><function>sd_bus_creds_unref()</function> destroys a reference
     to <parameter>c</parameter>.</para>
   </refsect1>
 
     <para><function>sd_bus_creds_get_mask()</function> returns the
     mask of successfully acquired fields.</para>
 
-    <para><function>sd_bus_creds_ref</function> always returns the
+    <para><function>sd_bus_creds_get_augmented_mask()</function>
+    returns the mask of fields that have been augmented from data in
+    <filename>/proc</filename>, and are thus not suitable for
+    authorization decisions.</para>
+
+    <para><function>sd_bus_creds_ref()</function> always returns the
     argument.</para>
 
-    <para><function>sd_bus_creds_unref</function> always returns
+    <para><function>sd_bus_creds_unref()</function> always returns
     <constant>NULL</constant>.</para>
   </refsect1>
 
 
         <listitem><para>Memory allocation failed.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EOPNOTSUPP</constant></term>
+
+        <listitem><para>One of the requested fields is unknown to the local system.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
   <refsect1>
     <title>Notes</title>
 
-    <para><function>sd_bus_creds_new_from_pid()</function> is
-    available as a shared library, which can be compiled and linked to
-    with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <para><function>sd_bus_creds_new_from_pid()</function> and the
+    other calls described here are available as a shared library,
+    which can be compiled and linked to with the
+    <constant>libsystemd</constant> <citerefentry
+    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     file.</para>
   </refsect1>
 
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_tid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_uid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_gid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_comm</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_tid_comm</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_exe</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_cmdline</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_cgroup</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_unit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_user_unit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_slice</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_session</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_owner_uid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_has_effective_cap</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_has_permitted_cap</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_has_inheritable_cap</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_has_bounding_cap</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_selinux_context</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_audit_session_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_audit_login_uid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_unique_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_creds_get_well_known_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 95b347bdfdbcec7d6b7b2afdbd1ac4b24eb405ea..1cf2cb8f9ae77d08efa84a4e31bdfef3a4197218 100644 (file)
     <para><function>sd_bus_default()</function> acquires a bus
     connection object to the user bus when invoked in user context, or
     to the system bus otherwise. The connection object is associated
-    to the calling thread. Each time the function is invoked from the
-    same thread the same object is returned, but its reference count
-    is increased by one, as long as at least one reference is
+    with the calling thread. Each time the function is invoked from
+    the same thread the same object is returned, but its reference
+    count is increased by one, as long as at least one reference is
     kept. When the last reference to the connection is dropped (using
     the
     <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     not automatically terminated when the associated thread ends. It
     is important to drop the last reference to the bus connection
     explicitly before the thread ends or otherwise the connection will
-    be leaked.</para>
+    be leaked. Also, queued but unread or unwritten messages keep the
+    bus referenced, see below.</para>
 
     <para><function>sd_bus_default_user()</function> returns a user
-    bus connection object associated to the calling thread.
+    bus connection object associated with the calling thread.
     <function>sd_bus_default_system()</function> is similar, but
     connects to the system bus. Note that
     <function>sd_bus_default()</function> is identical to these two
 
   </refsect1>
 
-  <refsect1>
-    <title>Return Value</title>
-
-    <para>On success, these calls return 0 or a positive
-    integer. On failure, these calls return a negative
-    errno-style error code.</para>
-  </refsect1>
-
   <refsect1>
     <title>Reference ownership</title>
     <para>The functions <function>sd_bus_open()</function>,
     <function>sd_bus_open_system()</function>,
     <function>sd_bus_open_system_remote()</function>, and
     <function>sd_bus_open_system_machine()</function> return a new
-    object and the caller owns the sole reference. When not needed
-    anymore, this reference should be destroyed with
+    connection object and the caller owns the sole reference. When not
+    needed anymore, this reference should be destroyed with
     <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     </para>
 
     <para>The functions <function>sd_bus_default()</function>,
     <function>sd_bus_default_user()</function> and
     <function>sd_bus_default_system()</function> do not necessarily
-    create a new object, but increase the connection reference by
-    one. Use
+    create a new object, but increase the connection reference of an
+    existing connection object by one. Use
     <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     to drop the reference.</para>
+
+    <para>Queued but unwritten/unread messages also keep a reference
+    to their bus connection object. For this reason, even if an
+    application dropped all references to a bus connection it might
+    not get destroyed right-away. Until all incoming queued
+    messages are read, and until all outgoing unwritten messages are
+    written, the bus object will stay
+    alive. <function>sd_bus_flush()</function> may be used to write
+    all outgoing queued messages so they drop their references. To
+    flush the unread incoming messages use
+    <function>sd_bus_close()</function>, which will also close the bus
+    connection. When using the default bus logic it is a good idea to
+    first invoke <function>sd_bus_flush()</function> followed by
+    <function>sd_bus_close()</function> when a thread or process
+    terminates, and thus its bus connection object should be
+    freed.</para>
+
+    <para>The life-cycle of the default bus connection should be the
+    responsibility of the code that creates/owns the thread the
+    default bus connection object is associated with. Library code
+    should neither call <function>sd_bus_flush()</function> nor
+    <function>sd_bus_close()</function> on default bus objects unless
+    it does so in its own private, self-allocated thread. Library code
+    should not use the default bus object in other threads unless it
+    is clear that the program using it will life-cycle the bus
+    connection object and flush and close it before exiting from the
+    thread. In libraries where it is not clear that the calling
+    program will life-cycle the bus connection object it is hence
+    recommended to use <function>sd_bus_open_system()</function>
+    instead of <function>sd_bus_default_system()</function> and
+    related calls.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these calls return 0 or a positive
+    integer. On failure, these calls return a negative
+    errno-style error code.</para>
   </refsect1>
 
   <refsect1>
index b8cb339cec63f447fb139df0211ca29e5aa4fed9..6dc4541eb19e38afe7d4fdb6126a93c3d96ada20 100644 (file)
 
   <refnamediv>
     <refname>sd_bus_error</refname>
+    <refname>SD_BUS_ERROR_MAKE_CONST</refname>
+    <refname>SD_BUS_ERROR_NULL</refname>
     <refname>sd_bus_error_free</refname>
     <refname>sd_bus_error_set</refname>
+    <refname>sd_bus_error_setf</refname>
     <refname>sd_bus_error_set_const</refname>
     <refname>sd_bus_error_set_errno</refname>
     <refname>sd_bus_error_set_errnof</refname>
+    <refname>sd_bus_error_set_errnofv</refname>
     <refname>sd_bus_error_get_errno</refname>
     <refname>sd_bus_error_copy</refname>
     <refname>sd_bus_error_is_set</refname>
@@ -75,7 +79,7 @@
       </para>
 
       <funcprototype>
-        <funcdef>int <function>sd_bus_error_free</function></funcdef>
+        <funcdef>void <function>sd_bus_error_free</function></funcdef>
         <paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
       </funcprototype>
 
         <paramdef>...</paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_error_set_errnofv</function></funcdef>
+        <paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
+        <paramdef>int <parameter>error</parameter></paramdef>
+        <paramdef>const char *<parameter>format</parameter></paramdef>
+        <paramdef>va_list ap</paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_error_get_errno</function></funcdef>
         <paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
 
-    <para>
-      <constant>SD_BUS_ERROR_FAILED</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_NO_MEMORY</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_SERVICE_UNKNOWN</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_NAME_HAS_NO_OWNER</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_NO_REPLY</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_IO_ERROR</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_BAD_ADDRESS</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_NOT_SUPPORTED</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_LIMITS_EXCEEDED</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_ACCESS_DENIED</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_AUTH_FAILED</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_NO_SERVER</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_TIMEOUT</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_NO_NETWORK</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_ADDRESS_IN_USE</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_DISCONNECTED</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_INVALID_ARGS</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_FILE_NOT_FOUND</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_FILE_EXISTS</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_UNKNOWN_METHOD</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_UNKNOWN_OBJECT</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_UNKNOWN_INTERFACE</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_UNKNOWN_PROPERTY</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_PROPERTY_READ_ONLY</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_INVALID_SIGNATURE</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_INCONSISTENT_MESSAGE</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_MATCH_RULE_NOT_FOUND</constant>
-    </para>
-    <para>
-      <constant>SD_BUS_ERROR_MATCH_RULE_INVALID</constant>
-    </para>
-
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
     <para>The <structname>sd_bus_error</structname> structure carries
-    information for a <filename>sd-bus</filename> error. The
-    functions described below can be used to set and query fields in
-    this structure. The <structfield>name</structfield> field contains a
-    short identifier of an error. It should follow the rules for error
-    names described in the D-Bus specification, subsection <ulink
+    information about a D-Bus error condition. The functions described
+    below may be used to set and query fields in this structure. The
+    <structfield>name</structfield> field contains a short identifier
+    of an error. It should follow the rules for error names described
+    in the D-Bus specification, subsection <ulink
     url="http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names">Valid
-    Names</ulink>. The <structfield>message</structfield> is a human
-    readable string describing the details. When no longer necessary,
-    resources held by this structure should be destroyed with
-    <function>sd_bus_error_free</function>.</para>
-
-    <para><function>sd_bus_error_set</function> will return an
-    errno-like negative value returned based on parameter
-    <parameter>name</parameter> (see
-    <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
-    Various well-known D-Bus errors are converted to specific values,
-    and the remaining ones to <constant>-ENXIO</constant>. Well-known
-    D-Bus error names are available as constants
-    <constant>SD_BUS_ERROR_FAILED</constant>, etc., listed above. If
+    Names</ulink>. A number of common, standardized error names are
+    described in
+    <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    but additional domain-specific errors may be defined by
+    applications. The <structfield>message</structfield> field usually
+    contains a human readable string describing the details, but might
+    be NULL. An unset <structname>sd_bus_error</structname> structure
+    should have both fields initialized to NULL. Set an error
+    structure to <constant>SD_BUS_ERROR_NULL</constant> in order to
+    reset both fields to NULL. When no longer necessary, resources
+    held by the <structname>sd_bus_error</structname>structure should
+    be destroyed with <function>sd_bus_error_free()</function>.</para>
+
+    <para><function>sd_bus_error_set()</function> sets an error
+    structure to the specified name and message strings. The strings
+    will be copied into internal, newly allocated memory. It is
+    essential to free the error structure again when it is not
+    required anymore (see above). The function will return an
+    <varname>errno</varname>-like negative value (see <citerefentry
+    project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
+    determined from the specified error name.  Various well-known
+    D-Bus errors are converted to well-known <varname>errno</varname>
+    counterparts, and the other ones to <constant>-EIO</constant>. See
+    <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for a list of well-known error names. Additional error mappings
+    may be defined with
+    <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If
+    <parameter>e</parameter> is NULL no error structure is initialized
+    but the error is still converted into an
+    <varname>errno</varname>-style error. If
     <parameter>name</parameter> is <constant>NULL</constant>, it is
     assumed that no error occurred, and 0 is returned. This means that
     this function may be conveniently used in a
-    <function>return</function> statement.</para>
-
-    <para>If <parameter>e</parameter> is not
-    <constant>NULL</constant>, <structfield>name</structfield> and
-    <structfield>message</structfield> in the
-    <structname>sd_bus_error</structname> structure
-    <parameter>e</parameter> points at will be filled in. As described above,
-    <parameter>name</parameter> may be <constant>NULL</constant>,
-    which is treated as no error. Parameter
-    <parameter>message</parameter> may also be
-    <constant>NULL</constant>, in which case no message is specified.
-    <function>sd_bus_error_set</function> will make internal copies of
-    specified strings.</para>
-
-    <para><function>sd_bus_error_setf</function> is similar to
-    <function>sd_bus_error_set</function>, but takes a
-    <citerefentry project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    format string and corresponding arguments to generate
-    <structname>message</structname>.</para>
-
-    <para><function>sd_bus_error_set_const</function> is similar to
-    <function>sd_bus_error_set</function>, but string parameters are
-    not copied internally, and must remain valid for the lifetime of
-    <parameter>e</parameter>.</para>
-
-    <para><function>sd_bus_error_set_errno</function> will set
-    <structfield>name</structfield> based on an errno-like value.
-    <citerefentry project='die-net'><refentrytitle>strerror</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    <function>return</function> statement. If
+    <parameter>message</parameter> is NULL no message is set. This
+    call can fail if no memory may be allocated for the name and
+    message strings, in which case an
+    <constant>SD_BUS_ERROR_NO_MEMORY</constant> error might be set
+    instead and -ENOMEM returned. Do not use this call on error
+    structures that are already initialized. If you intend to reuse an
+    error structure free the old data stored in it with
+    <function>sd_bus_error_free()</function> first.</para>
+
+    <para><function>sd_bus_error_setf()</function> is similar to
+    <function>sd_bus_error_set()</function>, but takes a <citerefentry
+    project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    format string and corresponding arguments to generate the
+    <structfield>message</structfield> field.</para>
+
+    <para><function>sd_bus_error_set_const()</function> is similar to
+    <function>sd_bus_error_set()</function>, but the string parameters
+    are not copied internally, and must hence remain constant and
+    valid for the lifetime of <parameter>e</parameter>. Use this call
+    to avoid memory allocations when setting error structures. Since
+    this call does not allocate memory it will not fail with an
+    out-of-memory condition, as
+    <function>sd_bus_error_set()</function> can, as described
+    above. Alternatively, the
+    <constant>SD_BUS_ERROR_MAKE_CONST()</constant> macro may be used
+    to generate a literal, constant bus error structure
+    on-the-fly.</para>
+
+    <para><function>sd_bus_error_set_errno()</function> will set
+    <structfield>name</structfield> from an
+    <varname>errno</varname>-like value that is converted to a D-Bus
+    error. <citerefentry
+    project='die-net'><refentrytitle>strerror_r</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     will be used to set <structfield>message</structfield>. Well-known
     D-Bus error names will be used for <structfield>name</structfield>
-    if available, otherwise a name in the
-    <literal>System.Error</literal> namespace will be generated.
-    </para>
-
-    <para><function>sd_bus_error_set_errnof</function> is similar to
-    <function>sd_bus_error_set_errno</function>, but in addition to
-    <parameter>name</parameter>, takes a
-    <citerefentry project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    format and corresponding arguments.
-    <structfield>name</structfield> will be generated from
+    if applicable, otherwise a name in the
+    <literal>System.Error.</literal> namespace will be generated. The
+    sign of the specified error number is ignored. The absolute value
+    is used implicitly. The call always returns a negative value, for
+    convenient usage in <function>return</function> statements. This
+    call might fail due to lack of memory, in which case an
+    <constant>SD_BUS_ERROR_NO_MEMORY</constant> error is set instead,
+    and -ENOMEM returned.</para>
+
+    <para><function>sd_bus_error_set_errnof()</function> is similar to
+    <function>sd_bus_error_set_errno()</function>, but in addition to
+    <parameter>error</parameter>, takes a <citerefentry
+    project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    format string and corresponding arguments.  The
+    <structfield>message</structfield> field will be generated from
     <parameter>format</parameter> and the arguments.</para>
 
-    <para><function>sd_bus_error_get_errno</function> will convert
-    <structname>e-&gt;name</structname> to an errno-like value using the
-    same rules as <function>sd_bus_error_set</function>.  If
+    <para><function>sd_bus_error_set_errnofv()</function> is similar to
+    <function>sd_bus_error_set_errnof()</function> but takes the
+    format string parameters as <citerefentry
+    project='man-pages'><refentrytitle>va_arg</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    parameter list.</para>
+
+    <para><function>sd_bus_error_get_errno()</function> converts the
+    <structfield>name</structfield> field of an error structure to an
+    <varname>errno</varname>-like (positive) value using the same
+    rules as <function>sd_bus_error_set()</function>.  If
     <parameter>e</parameter> is <constant>NULL</constant>, 0 will be
     returned.</para>
 
-    <para><function>sd_bus_error_copy</function> will initialize
+    <para><function>sd_bus_error_copy()</function> will initialize
     <parameter>dst</parameter> using the values in
     <parameter>e</parameter>. If the strings in
     <parameter>e</parameter> were set using
-    <function>sd_bus_set_error_const</function>, they will be shared.
-    Otherwise, they will be copied.</para>
+    <function>sd_bus_set_error_const()</function>, they will be shared.
+    Otherwise, they will be copied. Returns a converted
+    <varname>errno</varname>-like, negative error code.</para>
 
-    <para><function>sd_bus_error_is_set</function> will return
-    <constant>true</constant> if <parameter>e</parameter> is
+    <para><function>sd_bus_error_is_set()</function> will return a
+    non-zero value if <parameter>e</parameter> is
     non-<constant>NULL</constant> and an error has been set,
     <constant>false</constant> otherwise.</para>
 
-    <para><function>sd_bus_error_has_name</function> will return true
-    if <parameter>e</parameter> is non-<constant>NULL</constant> and
-    an error with the same <parameter>name</parameter> has been set,
+    <para><function>sd_bus_error_has_name()</function> will return a
+    non-zero value if <parameter>e</parameter> is
+    non-<constant>NULL</constant> and an error with the same
+    <parameter>name</parameter> has been set,
     <constant>false</constant> otherwise.</para>
 
-    <para><function>sd_bus_error_free</function> will destroy resources
-    held by <parameter>e</parameter>. The parameter itself will not
-    be deallocated, and must be
-    <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>d
-    by the caller if necessary.</para>
+    <para><function>sd_bus_error_free()</function> will destroy
+    resources held by <parameter>e</parameter>. The parameter itself
+    will not be deallocated, and must be <citerefentry
+    project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>d
+    by the caller if necessary. The function may also be called safely
+    on unset errors (error structures with both fields set to NULL),
+    in which case it performs no operation. This call will reset the
+    error structure after freeing the data, so that all fields are set
+    to NULL. The structure may be reused afterwards.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>Functions <function>sd_bus_error_set</function>,
-    <function>sd_bus_error_setf</function>,
-    <function>sd_bus_error_set_const</function>, when successful,
+    <para>The functions <function>sd_bus_error_set()</function>,
+    <function>sd_bus_error_setf()</function>,
+    <function>sd_bus_error_set_const()</function>, when successful,
     return the negative errno value corresponding to the
     <parameter>name</parameter> parameter. Functions
-    <function>sd_bus_error_set_errno</function> and
-    <function>sd_bus_error_set_errnof</function>, when successful,
-    return the value of the <parameter>errno</parameter> parameter. If
-    an error occurs, one of the negative error values listed below
-    will be returned.</para>
-
-    <para><function>sd_bus_error_get_errno</function> returns
+    <function>sd_bus_error_set_errno()</function>,
+    <function>sd_bus_error_set_errnof()</function> and
+    <function>sd_bus_error_set_errnofv()</function>, when successful,
+    return the negative value of the <parameter>error</parameter>
+    parameter. If an error occurs, one of the negative error values
+    listed below will be returned.</para>
+
+    <para><function>sd_bus_error_get_errno()</function> returns
     <constant>false</constant> when <parameter>e</parameter> is
     <constant>NULL</constant>, and a positive errno value mapped from
     <parameter>e-&gt;name</parameter> otherwise.</para>
 
-    <para><function>sd_bus_error_copy</function> returns 0 or a
-    positive integer on success, and one of the negative error values
-    listed below otherwise.</para>
+    <para><function>sd_bus_error_copy()</function> returns 0 or a
+    positive integer on success, and a negative error value converted
+    from the error name otherwise.</para>
 
-    <para><function>sd_bus_error_is_set</function> returns
-    <constant>true</constant> when <parameter>e</parameter> and
-    <parameter>e-&gt;name</parameter> are non-<constant>NULL</constant>,
-    <constant>false</constant> otherwise.</para>
+    <para><function>sd_bus_error_is_set()</function> returns a
+    non-zero value when <parameter>e</parameter> and the
+    <structfield>name</structfield> field are
+    non-<constant>NULL</constant>, zero otherwise.</para>
 
-    <para><function>sd_bus_error_has_name</function> returns
-    <constant>true</constant> when <parameter>e</parameter> is
-    non-<constant>NULL</constant> and <parameter>e-&gt;name</parameter>
-    is equal to <parameter>name</parameter>,
-    <constant>false</constant> otherwise.</para>
+    <para><function>sd_bus_error_has_name()</function> returns a
+    non-zero value when <parameter>e</parameter> is
+    non-<constant>NULL</constant> and the
+    <structfield>name</structfield> field is equal to
+    <parameter>name</parameter>, zero otherwise.</para>
   </refsect1>
 
   <refsect1>
     <title>Reference ownership</title>
     <para><structname>sd_bus_error</structname> is not reference
     counted. Users should destroy resources held by it by calling
-    <function>sd_bus_error_free</function>.</para>
+    <function>sd_bus_error_free()</function>. Usually error structures
+    are allocated on the stack or passed in as function parameters,
+    but they may also be allocated dynamically, in which case it is
+    the duty of the caller to <citerefentry
+    project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    the memory held by the structure itself after freeing its contents
+    with <function>sd_bus_error_free()</function>.</para>
   </refsect1>
 
   <refsect1>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry project='die-net'><refentrytitle>strerror</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry project='die-net'><refentrytitle>strerror_r</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_bus_error_add_map.xml b/man/sd_bus_error_add_map.xml
new file mode 100644 (file)
index 0000000..3fca63b
--- /dev/null
@@ -0,0 +1,173 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!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 2015 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="sd_bus_error_add_map">
+
+  <refentryinfo>
+    <title>sd_bus_error_add_map</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>sd_bus_error_add_map</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_error_add_map</refname>
+    <refname>sd_bus_error_map</refname>
+    <refname>SD_BUS_ERROR_MAP</refname>
+    <refname>SD_BUS_ERROR_END</refname>
+
+    <refpurpose>Additional sd-dbus error mappings</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcsynopsisinfo>typedef struct {
+        const char *name;
+        int code;
+        ...
+} sd_bus_error_map;</funcsynopsisinfo>
+
+    </funcsynopsis>
+
+      <para>
+        <constant>SD_BUS_ERROR_MAP(<replaceable>name</replaceable>, <replaceable>code</replaceable>)</constant>
+      </para>
+      <para>
+        <constant>SD_BUS_ERROR_MAP_END</constant>
+      </para>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_error_add_map</function></funcdef>
+        <paramdef>const sd_bus_map *<parameter>map</parameter></paramdef>
+      </funcprototype>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>sd_bus_error_add_map()</function> call may be
+    used to register additional mappings for converting D-Bus errors
+    to Linux <varname>errno</varname>-style errors. The mappings
+    defined with this call are consulted by calls such as
+    <citerefentry><refentrytitle>sd_bus_error_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or
+    <citerefentry><refentrytitle>sd_bus_error_get_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>. By
+    default a number of generic, standardized mappings are known, as
+    documented in
+    <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Use
+    this call to add further, application-specific mappings.</para>
+
+    <para>The function takes a pointer to an array of
+    <structname>sd_bus_error_map</structname> structures. A reference
+    to the specified array is added to the lookup tables for error
+    mappings. Note that the structure is not copied, it is hence
+    essential that the array stays available and constant during the
+    entire remaining runtime of the process.</para>
+
+    <para>The mapping array should be put together with a series of
+    <constant>SD_BUS_ERROR_MAP()</constant> macro invocations, that
+    take a literal name string and a (positive)
+    <varname>errno</varname>-style error number. The last entry of the
+    array should be an invocation of the
+    <constant>SD_BUS_ERROR_MAP_END</constant> macro. The array should not be
+    put together without use of these two macros.</para>
+
+    <para>Note that the call is idempotent: it is safe to invoke it
+    multiple times with the parameter, which will only add the passed
+    mapping array once.</para>
+
+    <para>Note that the memory allocated by this call is not intended
+    to be freed during the lifetime of the process. It should not be
+    freed explicitly.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>sd_bus_error_add_map()</function> returns a
+    positive value when the new array was added to the lookup
+    tables. It returns zero when the same array was already added
+    before. On error, a negative <varname>errno</varname>-style error
+    code is returned. See below for known error codes.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>The specified mapping array is invalid.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Notes</title>
+
+    <para>The various error definitions described here are available
+    as a shared library, which can be compiled and linked to with the
+    <constant>libsystemd</constant> <citerefentry
+    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    file.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='die-net'><refentrytitle>strerror_r</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 7a4bfa4bc47998ae5166fbbb1bba11895a1dcfe8..0ee849dca7aa5a9c64b44c42eb55425a3f75fe99 100644 (file)
@@ -46,7 +46,8 @@
   <refnamediv>
     <refname>sd_bus_message_append</refname>
 
-    <refpurpose>Attach parts of message based on a format string</refpurpose>
+    <refpurpose>Attach fields to a D-Bus message based on a type
+    string</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para>The <function>sd_bus_message_append</function> function appends
-    a sequence of items to message <parameter>m</parameter>. The
-    format string <parameter>types</parameter> describes the types of
-    arguments that follow.</para>
+    <para>The <function>sd_bus_message_append()</function> function
+    appends a sequence of fields to the D-Bus message object
+    <parameter>m</parameter>. The type string
+    <parameter>types</parameter> describes the types of the field
+    arguments that follow. For each type specified in the type string
+    one or more arguments need to be specified, in the same order as
+    declared in the type string.</para>
 
-    <para>The format string is composed of the elements shown in the
+    <para>The type string is composed of the elements shown in the
     table below. It contains zero or more single "complete types".
     Each complete type may be one of the basic types or a fully
-    described container type. A container type may be a structure, a
-    variant type code, an array with its element type, or a dictionary
-    with its entry type. The format string is
+    described container type. A container type may be a structure with
+    the contained types, a variant, an array with its element type, or
+    a dictionary entry with the contained types. The type string is
     <constant>NUL</constant>-terminated.</para>
 
     <para>In case of a basic type, one argument of the corresponding
     rules as if they were not nested.</para>
 
     <para>A variant is denoted by <literal>v</literal>. Corresponding
-    arguments must include a format string denoting a complete type,
+    arguments must begin with a type string denoting a complete type,
     and following that, arguments corresponding to the specified type.
     </para>
 
     <para>An array is denoted by <literal>a</literal> followed by a
-    complete type. Corresponding arguments must include the size of
-    the array, and then repeated this number of times, arguments
-    corresponding to the nested type.</para>
+    complete type. Corresponding arguments must begin with the number of
+    entries in the array, followed by the entries themselves,
+    matching the element type of the array.</para>
 
     <para>A dictionary is an array of dictionary entries, denoted by
     <literal>a</literal> followed by a pair of complete types between
     <literal>{</literal> and <literal>}</literal>. The first of those
-    types must be a basic type. Corresponding arguments must include
-    the size of the dictionary, and then repeated this number of
-    times, arguments corresponding to each of the two nested
-    types.</para>
+    types must be a basic type. Corresponding arguments must begin
+    with the number of dictionary entries, followed by a pair of
+    values for each entry matching the element type of
+    the dictionary entries.</para>
+
+    <para>For further details on the D-Bus type system, please consult
+    the <ulink
+    url="http://dbus.freedesktop.org/doc/dbus-specification.html#type-system">D-Bus
+    Specification</ulink>.</para>
 
     <table>
-      <title>Item format specifiers</title>
+      <title>Item type specifiers</title>
 
-      <tgroup cols='4'>
+      <tgroup cols='5'>
         <xi:include href="sd_bus_message_append_basic.xml" xpointer="xpointer(//table[@id='format-specifiers'])//colspec" />
         <xi:include href="sd_bus_message_append_basic.xml" xpointer="xpointer(//table[@id='format-specifiers']//thead)" />
 
             <entry><constant>SD_BUS_TYPE_ARRAY</constant></entry>
             <entry>array</entry>
             <entry>determined by array type and size</entry>
+            <entry>int, followed by array contents</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_VARIANT</constant></entry>
             <entry>variant</entry>
             <entry>determined by the type argument</entry>
+            <entry>signature string, followed by variant contents</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_STRUCT_BEGIN</constant></entry>
             <entry>array start</entry>
             <entry morerows="1">determined by the nested types</entry>
+            <entry morerows="1">structure contents</entry>
           </row>
           <row>
             <entry><literal>)</literal></entry>
             <entry><constant>SD_BUS_TYPE_DICT_ENTRY_BEGIN</constant></entry>
             <entry>dictionary entry start</entry>
             <entry morerows="1">determined by the nested types</entry>
+            <entry morerows="1">dictionary contents</entry>
           </row>
           <row>
             <entry><literal>}</literal></entry>
        </tbody>
       </tgroup>
     </table>
+
   </refsect1>
 
   <refsect1>
-    <title>Types string grammar</title>
+    <title>Types String Grammar</title>
 
     <programlisting>types ::= complete_type*
 complete_type ::= basic_type | variant | structure | array | dictionary
@@ -194,7 +208,7 @@ uint32_t t = 7;
 double d = 8.0;
 sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d);</programlisting>
 
-     <para>Append a structure composed of string and a D-Bus path:</para>
+     <para>Append a structure composed of string and a D-Bus path:</para>
 
      <programlisting>sd_bus_message_append(m, "(so)", "a string", "/a/path");
 </programlisting>
@@ -242,12 +256,8 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d);</programlisting>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index c2adc6f43d39d8f60392b9cb167d1a2fcad23078..37cadb9d0f2367865451ce4a8b15ff68ae6afb3c 100644 (file)
@@ -49,7 +49,8 @@
     <refname>sd_bus_message_append_array_iovec</refname>
     <refname>sd_bus_message_append_array_space</refname>
 
-    <refpurpose>Attach an array of items to a message</refpurpose>
+    <refpurpose>Append an array of fields to a D-Bus
+    message</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -69,6 +70,8 @@
         <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>char <parameter>type</parameter></paramdef>
         <paramdef>int <parameter>memfd</parameter></paramdef>
+        <paramdef>uint64_t <parameter>offset</parameter></paramdef>
+        <paramdef>uint64_t <parameter>size</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
@@ -83,7 +86,7 @@
         <funcdef>int sd_bus_message_append_array_space</funcdef>
         <paramdef>char <parameter>type</parameter></paramdef>
         <paramdef>size_t <parameter>size</parameter></paramdef>
-        <paramdef>char void **<parameter>ptr</parameter></paramdef>
+        <paramdef>void **<parameter>ptr</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para>The <function>sd_bus_message_append_array</function> functionc
-    appends items to message <parameter>m</parameter> as the single
-    array. A container will be opened, items appended, and the
-    container closed. Parameter <parameter>type</parameter> determines
-    how pointer <parameter>p</parameter> is interpreted.
+    <para>The <function>sd_bus_message_append_array()</function>
+    function appends an array to a D-Bus message
+    <parameter>m</parameter>. A container will be opened, the array
+    contents appended, and the container closed. The parameter
+    <parameter>type</parameter> determines how the pointer
+    <parameter>p</parameter> is interpreted.
     <parameter>type</parameter> must be one of the "trivial" types
     <literal>y</literal>, <literal>n</literal>, <literal>q</literal>,
     <literal>i</literal>, <literal>u</literal>, <literal>x</literal>,
     <literal>t</literal>, <literal>d</literal> (but not
-    <literal>b</literal>), as defined by the
-    <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html#basic-types">Basic Types</ulink>
-    section of the D-Bus specification, and listed in
+    <literal>b</literal>), as defined by the <ulink
+    url="http://dbus.freedesktop.org/doc/dbus-specification.html#basic-types">Basic
+    Types</ulink> section of the D-Bus specification, and listed in
     <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     Pointer <parameter>p</parameter> must point to an array of size
     <parameter>size</parameter> bytes containing items of the
     multiple of the size of the type <parameter>type</parameter>. As a
     special case, <parameter>p</parameter> may be
     <constant>NULL</constant>, if <parameter>size</parameter> is 0.
-    </para>
-
-    <para>The memory pointed at by <parameter>p</parameter> is copied
-    into the memory area containing the message and may be changed
-    after this call.</para>
-
-    <para>The
-    <function>sd_bus_message_append_array_memfd</function> function appends
-    items to message <parameter>m</parameter>, similarly to
-    <function>sd_bus_message_append_array</function>. Contents of the
-    memory file descriptor <parameter>memfd</parameter> are used as
-    the contents of the array. Their size must be a multiple of the
-    size of the type <parameter>type</parameter>.</para>
-
-    <para>The descriptor specified with <parameter>memfd</parameter>
-    will be sealed and cannot be modified after this call.</para>
-
-    <para>The
-    <function>sd_bus_message_append_array_iovec</function> function appends
-    items to message <parameter>m</parameter>, similarly to
-    <function>sd_bus_message_append_array</function>. Contents of the
-    iovec <parameter>iov</parameter> are used as the contents of the
-    array. The total size of <parameter>iov</parameter> payload (the
-    sum of <structfield>iov_len</structfield> fields) must be a multiple
-    of the size of the type <parameter>type</parameter>.</para>
-
-    <para>The <parameter>iov</parameter> argument must point to
-    <parameter>n</parameter> <structname>struct iovec</structname>
-    structures. Each structure may have the
-    <structname>iov_base</structname> field set, in which case the
-    memory pointed to will be copied into the message, or unset, in
-    which case a block of zeros of length
+    The memory pointed to by <parameter>p</parameter> is copied into
+    the memory area containing the message and stays in possession of
+    the caller. The caller may hence freely change the data after this
+    call without affecting the message the array was appended
+    to.</para>
+
+    <para>The <function>sd_bus_message_append_array_memfd()</function>
+    function appends an array of a trivial type to message
+    <parameter>m</parameter>, similar to
+    <function>sd_bus_message_append_array()</function>. The contents
+    of the memory file descriptor <parameter>memfd</parameter>
+    starting at the specified offset and of the specified size is
+    used as the contents of the array. The offset and size must be a
+    multiple of the size of the type
+    <parameter>type</parameter>. However, as a special exception, if
+    the offset is specified as zero and the size specified as
+    UINT64_MAX the full memory file descriptor contents is used. The
+    memory file descriptor is sealed by this call if it hasn't been
+    sealed yet, and cannot be modified a after this call. See
+    <citerefentry
+    project='man-pages'><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    for details about memory file descriptors. Appending arrays with
+    memory file descriptors enables efficient zero-copy data transfer,
+    as the memory file descriptor may be passed as-is to the
+    destination, without copying the memory in it to the destination
+    process. Not all protocol transports support passing memory file
+    descriptors between participants, in which case this call will
+    automatically fall back to copying. Also, as memory file
+    descriptor passing is inefficient for smaller amounts of data
+    copying might still be enforced even where memory file descriptor
+    passing is supported.</para>
+
+    <para>The <function>sd_bus_message_append_array_iovec()</function>
+    function appends an array of a trivial type to the message
+    <parameter>m</parameter>, similar to
+    <function>sd_bus_message_append_array()</function>. Contents of
+    the IO vector array <parameter>iov</parameter> are used as the
+    contents of the array. The total size of
+    <parameter>iov</parameter> payload (the sum of
+    <structfield>iov_len</structfield> fields) must be a multiple of
+    the size of the type <parameter>type</parameter>. The
+    <parameter>iov</parameter> argument must point to
+    <parameter>n</parameter> IO vector structures. Each structure may
+    have the <structname>iov_base</structname> field set, in which
+    case the memory pointed to will be copied into the message, or
+    unset (set to zero), in which case a block of zeros of length
     <structname>iov_len</structname> bytes will be inserted. The
     memory pointed at by <parameter>iov</parameter> may be changed
     after this call.</para>
 
-    <para>The
-    <function>sd_bus_message_append_array_space</function> function appends
-    space for an array of items to message <parameter>m</parameter>.
-    It behaves the same as
-    <function>sd_bus_message_append_array</function>, but instead
-    of copying items to the message, it returns a pointer to the
-    destination area to the caller in pointer <parameter>p</parameter>.
-    </para>
+    <para>The <function>sd_bus_message_append_array_space()</function>
+    function appends space for an array of a trivial type to message
+    <parameter>m</parameter>.  It behaves the same as
+    <function>sd_bus_message_append_array()</function>, but instead of
+    copying items to the message, it returns a pointer to the
+    destination area to the caller in pointer
+    <parameter>p</parameter>. The caller should subsequently write the
+    array contents to this memory. Modifications of the memory
+    pointed to should only occur until the next operation on the bus
+    message is invoked, most importantly the memory should not be
+    altered anymore when another field has been added to the message
+    or the message has been sealed.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
     <para>On success, these calls return 0 or a positive integer. On
-    failure, they returns a negative errno-style error code.</para>
+    failure, they return a negative errno-style error code.</para>
   </refsect1>
 
   <xi:include href="sd_bus_message_append_basic.xml" xpointer="errors" />
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
       <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html">The D-Bus specification</ulink>
     </para>
   </refsect1>
index 91eaf875306f2ed3ab0a6129b2eb1e26f5a4fdb0..276953af6978f4e71e41bee6716e63da80baef12 100644 (file)
@@ -45,7 +45,7 @@
   <refnamediv>
     <refname>sd_bus_message_append_basic</refname>
 
-    <refpurpose>Attach a single part to a message</refpurpose>
+    <refpurpose>Attach a single field to a message</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -56,7 +56,7 @@
         <funcdef>int sd_bus_message_append_basic</funcdef>
         <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>char <parameter>type</parameter></paramdef>
-        <paramdef>char void *<parameter>p</parameter></paramdef>
+        <paramdef>const void *<parameter>p</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_message_append_basic</function> appends a
-    single item to the message <parameter>m</parameter>. Parameter
-    <parameter>type</parameter> determines how pointer
+    <para><function>sd_bus_message_append_basic()</function> appends a
+    single field to the message <parameter>m</parameter>. The
+    parameter <parameter>type</parameter> determines how the pointer
     <parameter>p</parameter> is interpreted.
-    <parameter>type</parameter> must be one of the basic types
-    as defined by the
-
-    <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html#basic-types">Basic Types</ulink>
-    section of the D-Bus specification, and listed in the table below.
+    <parameter>type</parameter> must be one of the basic types as
+    defined by the <ulink
+    url="http://dbus.freedesktop.org/doc/dbus-specification.html#basic-types">Basic
+    Types</ulink> section of the D-Bus specification, and listed in
+    the table below.
     </para>
 
     <table id='format-specifiers'>
-      <title>Item format specifiers</title>
+      <title>Item type specifiers</title>
 
-      <tgroup cols='4'>
+      <tgroup cols='5'>
         <colspec colname='specifier' />
         <colspec colname='constant' />
         <colspec colname='description' />
         <colspec colname='size' />
+        <colspec colname='ctype' />
         <thead>
           <row>
             <entry>Specifier</entry>
             <entry>Constant</entry>
             <entry>Description</entry>
             <entry>Size</entry>
+            <entry>Expected C Type</entry>
           </row>
         </thead>
         <tbody>
@@ -97,6 +99,7 @@
             <entry><constant>SD_BUS_TYPE_BYTE</constant></entry>
             <entry>unsigned integer</entry>
             <entry>1 byte</entry>
+            <entry>uint8_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_BOOLEAN</constant></entry>
             <entry>boolean</entry>
             <entry>4 bytes</entry>
+            <entry>int</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_INT16</constant></entry>
             <entry>signed integer</entry>
             <entry>2 bytes</entry>
+            <entry>int16_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_UINT16</constant></entry>
             <entry>unsigned integer</entry>
             <entry>2 bytes</entry>
+            <entry>uint16_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_INT32</constant></entry>
             <entry>signed integer</entry>
             <entry>4 bytes</entry>
+            <entry>int32_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_UINT32</constant></entry>
             <entry>unsigned integer</entry>
             <entry>4 bytes</entry>
+            <entry>uint32_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_INT64</constant></entry>
             <entry>signed integer</entry>
             <entry>8 bytes</entry>
+            <entry>int64_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_UINT64</constant></entry>
             <entry>unsigned integer</entry>
             <entry>8 bytes</entry>
+            <entry>uint64_t</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_DOUBLE</constant></entry>
             <entry>floating-point</entry>
             <entry>8 bytes</entry>
+            <entry>double</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_STRING</constant></entry>
             <entry>Unicode string</entry>
             <entry>variable</entry>
+            <entry>char[]</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_OBJECT_PATH</constant></entry>
             <entry>object path</entry>
             <entry>variable</entry>
+            <entry>char[]</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_SIGNATURE</constant></entry>
             <entry>signature</entry>
             <entry>variable</entry>
+            <entry>char[]</entry>
           </row>
 
           <row>
             <entry><constant>SD_BUS_TYPE_UNIX_FD</constant></entry>
             <entry>UNIX file descriptor</entry>
             <entry>4 bytes</entry>
+            <entry>int</entry>
           </row>
        </tbody>
       </tgroup>
     </table>
 
-    <para>The value of the parameter is copied into the memory area
-    containing the message and may be changed after this call. If
-    <parameter>type</parameter> is <literal>h</literal> (UNIX file
-    descriptor), it is always "consumed" by this call, and either
-    successfully appended to the message or closed.</para>
+    <para>The value of the parameter is copied into a memory area held
+    by the message object, stays in the possession of the caller and
+    may hence be freely changed after this call without affecting the
+    bus message it has been added to. If <parameter>type</parameter>
+    is <literal>h</literal> (UNIX file descriptor), the descriptor is
+    duplicated by this call and the passed descriptor stays in
+    possession of the caller.</para>
 
     <para>For types <literal>s</literal>, <literal>o</literal>, and
     <literal>g</literal>, the parameter <parameter>p</parameter> is
index 04042f21366d55640592cc727a5d9ee9091a00e8..f53ea9e41aaeecbe716bfe1b619aeaa28c64ea61 100644 (file)
@@ -44,7 +44,7 @@
 
   <refnamediv>
     <refname>sd_bus_negotiate_fds</refname>
-    <refname>sd_bus_negotiate_timestamps</refname>
+    <refname>sd_bus_negotiate_timestamp</refname>
     <refname>sd_bus_negotiate_creds</refname>
 
     <refpurpose>Control feature negotiation on bus connections</refpurpose>
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, these functions returns 0 or a
+    <para>On success, these functions return 0 or a
     positive integer. On failure, they return a negative errno-style
     error code.</para>
   </refsect1>
index 44744a00292d9d0415432e64f3a6a8d789a70505..aff2ed2e8395b2fae3821e58bbf139bae3c251af 100644 (file)
@@ -77,8 +77,8 @@
     <para><function>sd_bus_new()</function> creates a new bus
     object. This object is reference-counted, and will be destroyed
     when all references are gone. Initially, the caller of this
-    function owns the sole reference. The bus object will not be
-    connected to any bus initially. To connect it to a bus, make sure
+    function owns the sole reference and the bus object will not be
+    connected to any bus. To connect it to a bus, make sure
     to set an address with
     <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     or a related call, and then start the connection with
     well-known bus in a single function invocation.</para>
 
     <para><function>sd_bus_ref()</function> creates a new reference to
-    <parameter>bus</parameter>. This bus object will not be destroyed
-    until <function>sd_bus_unref()</function> has been called as many
-    times plus once more. Once the reference count has dropped to
-    zero, <parameter>bus</parameter> cannot be used anymore, so
-    further calls to <function>sd_bus_ref()</function> or
-    <function>sd_bus_unref()</function> are illegal.</para>
+    <parameter>bus</parameter>.</para>
 
     <para><function>sd_bus_unref()</function> destroys a reference to
-    <parameter>bus</parameter>.</para>
+    <parameter>bus</parameter>. Once the reference count has dropped
+    to zero, <parameter>bus</parameter> cannot be used anymore, so
+    further calls to <function>sd_bus_ref()</function> or
+    <function>sd_bus_unref()</function> are illegal.</para>
   </refsect1>
 
   <refsect1>
index 9b0a93d8884d1550d8f9457c7db9bb60d1d1c9e9..f07ae095559243ada0d58dd08bddcfdd6798ea81 100644 (file)
@@ -45,7 +45,7 @@
   <refnamediv>
     <refname>sd_bus_request_name</refname>
     <refname>sd_bus_release_name</refname>
-    <refpurpose>Request or release a well-known name on a bus</refpurpose>
+    <refpurpose>Request or release a well-known service name on a bus</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -71,9 +71,9 @@
     <title>Description</title>
 
     <para><function>sd_bus_request_name()</function> requests a
-    well-known name on a bus. It takes a bus connection, a valid bus
-    name and a flags parameter. The flags parameter is a combination
-    of the following flags:</para>
+    well-known service name on a bus. It takes a bus connection, a
+    valid bus name and a flags parameter. The flags parameter is a
+    combination of the following flags:</para>
 
     <variablelist>
       <varlistentry>
       <varlistentry>
         <term><constant>-EINVAL</constant></term>
 
-        <listitem><para>A specified parameter is
-        invalid.</para></listitem>
+        <listitem><para>A specified parameter is invalid. This is also
+        generated when the requested name is a special service name
+        reserved by the D-Bus specification, or when the operation is
+        requested on a connection that does not refer to a
+        bus.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index b46d47101b62142ccadbe125464d8f3892698d1e..9c6706caf8115750bf9e45f5273e8f2e6646eeb9 100644 (file)
     <refname>sd_pid_get_owner_uid</refname>
     <refname>sd_pid_get_machine_name</refname>
     <refname>sd_pid_get_slice</refname>
+    <refname>sd_pid_get_user_slice</refname>
     <refname>sd_peer_get_session</refname>
     <refname>sd_peer_get_unit</refname>
     <refname>sd_peer_get_user_unit</refname>
     <refname>sd_peer_get_owner_uid</refname>
     <refname>sd_peer_get_machine_name</refname>
     <refname>sd_peer_get_slice</refname>
-    <refpurpose>Determine session, service, owner of a
-    session, container/VM or slice of a specific
-    PID or socket peer</refpurpose>
+    <refname>sd_peer_get_user_slice</refname>
+    <refpurpose>Determine session, unit, owner of a session,
+    container/VM or slice of a specific PID or socket
+    peer</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
         <paramdef>char **<parameter>slice</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_pid_get_user_slice</function></funcdef>
+        <paramdef>pid_t <parameter>pid</parameter></paramdef>
+        <paramdef>char **<parameter>slice</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_peer_get_session</function></funcdef>
         <paramdef>int <parameter>fd</parameter></paramdef>
         <paramdef>int <parameter>fd</parameter></paramdef>
         <paramdef>char **<parameter>slice</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_peer_get_user_slice</function></funcdef>
+        <paramdef>int <parameter>fd</parameter></paramdef>
+        <paramdef>char **<parameter>slice</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
     call after use.</para>
 
     <para><function>sd_pid_get_unit()</function> may be used to
-    determine the systemd system unit (i.e. system service) identifier
-    of a process identified by the specified PID. The unit name is a
-    short string, suitable for usage in file system paths. Note that
-    not all processes are part of a system unit/service (e.g. user
-    processes, or kernel threads). For processes not being part of a
-    systemd system unit this function will fail with -ENXIO (More
-    specifically: this call will not work for processes that are part
-    of user units, use <function>sd_pid_get_user_unit()</function> for
-    that.) The returned string needs to be freed with the libc
-    <citerefentry
+    determine the systemd system unit (i.e. system service or scope
+    unit) identifier of a process identified by the specified PID. The
+    unit name is a short string, suitable for usage in file system
+    paths. Note that not all processes are part of a system
+    unit/service (e.g. user processes, or kernel threads). For
+    processes not being part of a systemd system unit this function
+    will fail with -ENXIO (More specifically: this call will not work
+    for kernel threads.) The returned string needs to be freed with
+    the libc <citerefentry
     project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     call after use.</para>
 
     <para><function>sd_pid_get_user_unit()</function> may be used to
-    determine the systemd user unit (i.e. user service) identifier of
-    a process identified by the specified PID. This is similar to
-    <function>sd_pid_get_unit()</function> but applies to user units
-    instead of system units.</para>
+    determine the systemd user unit (i.e. user service or scope unit)
+    identifier of a process identified by the specified PID. This is
+    similar to <function>sd_pid_get_unit()</function> but applies to
+    user units instead of system units.</para>
 
     <para><function>sd_pid_get_owner_uid()</function> may be used to
-    determine the Unix user identifier of the owner of the session of
-    a process identified the specified PID. Note that this function
-    will succeed for user processes which are shared between multiple
-    login sessions of the same user, where
+    determine the Unix UID (user identifier) of the owner of the
+    session of a process identified the specified PID. Note that this
+    function will succeed for user processes which are shared between
+    multiple login sessions of the same user, where
     <function>sd_pid_get_session()</function> will fail. For processes
     not being part of a login session and not being a shared process
     of a user this function will fail with -ENXIO.</para>
     <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     call after use.</para>
 
+    <para>Similar, <function>sd_pid_get_user_slice()</function>
+    returns the user slice (as managed by the user's systemd instance)
+    of a process.</para>
+
     <para>If the <varname>pid</varname> parameter of any of these
     functions is passed as 0, the operation is executed for the
     calling process.</para>
     <function>sd_peer_get_unit()</function>,
     <function>sd_peer_get_user_unit()</function>,
     <function>sd_peer_get_owner_uid()</function>,
-    <function>sd_peer_get_machine_name()</function> and
-    <function>sd_peer_get_slice()</function> calls operate similar to
-    their PID counterparts, but operate on a connected AF_UNIX socket
-    and retrieve information about the connected peer process.</para>
+    <function>sd_peer_get_machine_name()</function>,
+    <function>sd_peer_get_slice()</function> and
+    <function>sd_peer_get_user_slice()</function> calls operate
+    similar to their PID counterparts, but operate on a connected
+    AF_UNIX socket and retrieve information about the connected peer
+    process. Note that these fields are retrieved via
+    <filename>/proc</filename>, and hence are not suitable for
+    authorization purposes, as they are subject to races.</para>
   </refsect1>
 
   <refsect1>
     <function>sd_pid_get_owner_uid()</function>,
     <function>sd_pid_get_machine_name()</function>,
     <function>sd_pid_get_slice()</function>,
+    <function>sd_pid_get_user_slice()</function>,
     <function>sd_peer_get_session()</function>,
     <function>sd_peer_get_unit()</function>,
     <function>sd_peer_get_user_unit()</function>,
     <function>sd_peer_get_owner_uid()</function>,
-    <function>sd_peer_get_machine_name()</function> and
-    <function>sd_peer_get_slice()</function> interfaces are
-    available as a shared library, which can be compiled
-    and linked to with the
-    <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <function>sd_peer_get_machine_name()</function>,
+    <function>sd_peer_get_slice()</function> and
+    <function>sd_peer_get_user_slice()</function> interfaces are
+    available as a shared library, which can be compiled and linked to
+    with the <constant>libsystemd</constant> <citerefentry
+    project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     file.</para>
 
     <para>Note that the login session identifier as
index 5da3857c086f52f7105a66adcdf7297e67c97966..cb46d41902034869d2d6a489d6395cbf5f29859d 100644 (file)
@@ -67,7 +67,7 @@
     overridden to use a different setting following normal
     <citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> rules.</para>
 
-    <para>The behaviour of a specific program upon reception of a
+    <para>The behavior of a specific program upon reception of a
     signal is governed by a few factors which are described in detail
     in <citerefentry project='man-pages'><refentrytitle>core</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
     In particular, the coredump will only be processed when the
@@ -81,7 +81,7 @@
     including a backtrace if possible, and store the core (contents of
     process' memory contents) in an external file on disk in
     <filename>/var/lib/systemd/coredump</filename>, or directly in
-    the journal. This behaviour may be modified using
+    the journal. This behavior may be modified using
     <citerefentry><refentrytitle>coredump.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
     <para>Apart from the
index 8280d6c874e2d3b3c83f7f0a9aebc6a5c56edd37..dae6ee6042c6731876309f9b9b0e18dba34bded7 100644 (file)
 
         <listitem><para>Configure
         <command>systemd-journald</command>
-        behaviour. See
+        behavior. See
         <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
         </para></listitem>
       </varlistentry>
index a101006a7e2b2ff59fe76a7017669a11f69d450b..49f44d292277c72ed6d7e635b10bc7498f4c6d29 100644 (file)
@@ -73,7 +73,7 @@
           <para>The human-readable message string for this entry. This
           is supposed to be the primary text shown to the user. It is
           usually not translated (but might be in some cases), and is
-          not supposed to be parsed for meta data.</para>
+          not supposed to be parsed for metadata.</para>
         </listitem>
       </varlistentry>
 
       </varlistentry>
     </variablelist>
 
-
   </refsect1>
 
   <refsect1>
index d9b1879c5981833c8a03f6c9ce9d7b7a89fbe310..b630ef7a17ed7b2eb6c9133e2931a25f7ec3d9cf 100644 (file)
     in <filename>/etc</filename> have the highest priority, files in
     <filename>/run</filename> take precedence over files with the same
     name in <filename>/usr/lib</filename>. This can be used to
-    override a system-supplied link file with a local file if needed;
-    a symlink in <filename>/etc</filename> with the same name as a
-    link file in <filename>/usr/lib</filename>, pointing to
-    <filename>/dev/null</filename>, disables the link file
-    entirely.</para>
+    override a system-supplied link file with a local file if needed.
+    As a special case, an empty file (file size 0) or symlink with the
+    same name pointing to <filename>/dev/null</filename>, disable the
+    configuration file entirely (it is "masked").</para>
 
     <para>The link file contains a <literal>[Match]</literal> section,
     which determines if a given link file may be applied to a given
index 01c31c5ef52764082aefc48fc7d1f76e2a7181b9..92e20bd53f5bcb3b8e314648ad31bc140fd51b9e 100644 (file)
     <filename>/run</filename> take precedence over files with the same
     name in <filename>/usr/lib</filename>. This can be used to
     override a system-supplied configuration file with a local file if
-    needed; a symlink in <filename>/etc</filename> with the same name
-    as a configuration file in <filename>/usr/lib</filename>, pointing
-    to <filename>/dev/null</filename>, disables the configuration file
-    entirely.</para>
-
+    needed. As a special case, an empty file (file size 0) or symlink
+    with the same name pointing to <filename>/dev/null</filename>,
+    disable the configuration file entirely (it is "masked").</para>
   </refsect1>
 
   <refsect1>
           <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>
 
 
   </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>
 
           the tunnel.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>IPv6FlowLabel=</varname></term>
+        <listitem>
+          <para>Configures The 20-bit Flow Label (see <ulink url="https://tools.ietf.org/html/rfc6437">
+          RFC 6437</ulink>) field in the IPv6 header (see <ulink url="https://tools.ietf.org/html/rfc2460">
+          RFC 2460</ulink>), is used by a node to label packets of a flow.
+          It's only used for IPv6 Tunnels.
+          A Flow Label of zero is used to indicate packets that have
+          not been labeled. Takes following values.
+          When <literal>inherit</literal> it uses the original flowlabel,
+          or can be configured to any value between 0 to 0xFFFFF.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>CopyDSCP=</varname></term>
+        <listitem>
+          <para>A boolean. When true, the Differentiated Service Code
+          Point (DSCP) field will be copied to the inner header from
+          outer header during the decapsulation of an IPv6 tunnel
+          packet. DSCP is a field in an IP packet that enables different
+          levels of service to be assigned to network traffic.
+          Defaults to <literal>no</literal>.
+          </para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>Mode=</varname></term>
         <listitem>
         <literal>no</literal>.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <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)
+        packets. This may increase throughput significantly.
+        Defaults to
+        <literal>no</literal>.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>User=</varname></term>
         <listitem><para>User to grant access to the
         <listitem>
           <para>Specifies the frequency that Media Independent
           Interface link monitoring will occur. A value of zero
-          disables MII link monitoring. This values is rounded down to
+          disables MII link monitoring. This value is rounded down to
           the nearest millisecond. The default value is 0.</para>
         </listitem>
       </varlistentry>
         <listitem>
           <para> Specify the number of packets to transmit through a slave before
             moving to the next one. When set to 0 then a slave is chosen at
-            random.The valid range is (0 - 65535). Defaults to 1. This option
+            random. The valid range is (0 - 65535). Defaults to 1. This option
             has effect only in balance-rr mode.
           </para>
         </listitem>
index ff01da624947a65a6daa051989d4532d3d745100..e44491cc2e303adee6c2352ad86109e5a8200fa0 100644 (file)
     <filename>/run</filename> take precedence over files with the same
     name in <filename>/usr/lib</filename>. This can be used to
     override a system-supplied configuration file with a local file if
-    needed; a symlink in <filename>/etc</filename> with the same name
-    as a configuration file in <filename>/usr/lib</filename>, pointing
-    to <filename>/dev/null</filename>, disables the configuration file
-    entirely.</para>
-
+    needed. As a special case, an empty file (file size 0) or symlink
+    with the same name pointing to <filename>/dev/null</filename>,
+    disable the configuration file entirely (it is "masked").</para>
   </refsect1>
 
   <refsect1>
           <term><varname>BindCarrier=</varname></term>
           <listitem>
             <para>A port or a list of ports. When set, controls the
-            behaviour of the current interface. When all ports in the list
+            behavior of the current interface. When all ports in the list
             are in an operational down state, the current interface is brought
             down. When at least one port has carrier, the current interface
             is brought up.
         <varlistentry>
           <term><varname>SendHostname=</varname></term>
           <listitem>
-            <para>When true (the default), the machine's hostname will be sent to the DHCP
-            server</para>
+            <para>When true (the default), the machine's hostname will
+            be sent to the DHCP server.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>UseHostname=</varname></term>
           <listitem>
             <para>When true (the default), the hostname received from
-            the DHCP server will be used as the transient
-            hostname.</para>
+            the DHCP server will be used as the transient hostname.
+            </para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+        <term><varname>Hostname=</varname></term>
+        <listitem>
+          <para>Use this value for the hostname which is sent to the
+          DHCP server, instead of machine's hostname.</para>
+        </listitem>
+      </varlistentry>
         <varlistentry>
           <term><varname>UseDomains=</varname></term>
           <listitem>
       <para>The <literal>[Bridge]</literal> section accepts the
       following keys.</para>
       <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>UnicastFlood=</varname></term>
+          <listitem>
+            <para>A boolean. Controls whether the bridge should flood
+            traffic for which an FDB entry is missing and the destination
+            is unknown through this port. Defaults to on.
+            </para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>HairPin=</varname></term>
+          <listitem>
+            <para>A boolean. Configures whether traffic may be sent back
+            out of the port on which it was received. By default, this
+            flag is false, and the bridge will not forward traffic back
+            out of the receiving port.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>UseBPDU=</varname></term>
+          <listitem>
+            <para>A boolean. Configures whether STP Bridge Protocol Data Units will be
+            processed by the bridge port. Defaults to yes.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>FastLeave=</varname></term>
+          <listitem>
+            <para>A boolean. This flag allows the bridge to immediately stop multicast
+            traffic on a port that receives IGMP Leave message. It is only used with
+            IGMP snooping if enabled on the bridge. Defaults to off.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>AllowPortToBeRoot=</varname></term>
+          <listitem>
+            <para>A boolean. Configures whether a given port is allowed to
+            become a root port. Only used when STP is enabled on the bridge.
+            Defaults to on.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>Cost=</varname></term>
           <listitem>
-            <para>Each port in a bridge may have different speed. Cost
+            <para>Sets the "cost" of sending packets of this interface.
+            Each port in a bridge may have different speed and the cost
             is used to decide which link to use. Faster interfaces
-            should have lower costs</para>
+            should have lower costs.</para>
           </listitem>
         </varlistentry>
       </variablelist>
   </refsect1>
-
   <refsect1>
     <title>[BridgeFDB] Section Options</title>
       <para>The <literal>[BridgeFDB]</literal> section manages the
index 2f9add8d6ca7b38dff36f09c29e0b35e8f6b842e..b7164014f05264bd1507664eaa1a47dccd28d9e1 100644 (file)
     one takes precedence over all others.</para>
 
     <para>Each preset file shall be named in the style of
-    <filename>&lt;priority&gt;-&lt;program&gt;.conf</filename>. Files
+    <filename>&lt;priority&gt;-&lt;policy-name&gt;.preset</filename>. Files
     in <filename>/etc/</filename> override files with the same name in
     <filename>/usr/lib/</filename> and <filename>/run/</filename>.
     Files in <filename>/run/</filename> override files with the same
index 1e9778bc2a94c8587b2d8406ec0e03111f300ee1..36fa3a86be33a4eaa782145a1c683effe1f178b3 100644 (file)
         and the kernel will ignore initial ACK packets without any
         data. The argument specifies the approximate amount of time
         the kernel should wait for incoming data before falling back
-        to the normal behaviour of honouring empty ACK packets. This
+        to the normal behavior of honouring empty ACK packets. This
         option is beneficial for protocols where the client sends the
         data first (e.g. HTTP, in contrast to SMTP), because the
         server process will not be woken up unnecessarily before it
index c0863e4167d063cfbfd0f84ab408c84da41180a7..db1e7f3f37485b1ba0f5eaa25d67a2f92cab3827 100644 (file)
@@ -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)
index c79ad6520c9f8668f0992c51d7506790f23e8726..c8961de946857c498eb3d6098bc9bb31e0a2a561 100644 (file)
@@ -415,7 +415,7 @@ int main(int argc, char *argv[]) {
                         return EXIT_FAILURE;
                 }
 
-                r = write_string_file(saved, value);
+                r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
                 if (r < 0) {
                         log_error_errno(r, "Failed to write %s: %m", saved);
                         return EXIT_FAILURE;
diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c
new file mode 100644 (file)
index 0000000..bf9d8d4
--- /dev/null
@@ -0,0 +1,198 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+
+#include "bitmap.h"
+
+struct Bitmap {
+        uint64_t *bitmaps;
+        size_t n_bitmaps;
+        size_t bitmaps_allocated;
+};
+
+/* Bitmaps are only meant to store relatively small numbers
+ * (corresponding to, say, an enum), so it is ok to limit
+ * the max entry. 64k should be plenty. */
+#define BITMAPS_MAX_ENTRY 0xffff
+
+/* This indicates that we reached the end of the bitmap */
+#define BITMAP_END ((unsigned) -1)
+
+#define BITMAP_NUM_TO_OFFSET(n)           ((n) / (sizeof(uint64_t) * 8))
+#define BITMAP_NUM_TO_REM(n)              ((n) % (sizeof(uint64_t) * 8))
+#define BITMAP_OFFSET_TO_NUM(offset, rem) ((offset) * sizeof(uint64_t) * 8 + (rem))
+
+Bitmap *bitmap_new(void) {
+        return new0(Bitmap, 1);
+}
+
+void bitmap_free(Bitmap *b) {
+        if (!b)
+                return;
+
+        free(b->bitmaps);
+        free(b);
+}
+
+int bitmap_ensure_allocated(Bitmap **b) {
+        Bitmap *a;
+
+        assert(b);
+
+        if (*b)
+                return 0;
+
+        a = bitmap_new();
+        if (!a)
+                return -ENOMEM;
+
+        *b = a;
+
+        return 0;
+}
+
+int bitmap_set(Bitmap *b, unsigned n) {
+        uint64_t bitmask;
+        unsigned offset;
+
+        assert(b);
+
+        /* we refuse to allocate huge bitmaps */
+        if (n > BITMAPS_MAX_ENTRY)
+                return -ERANGE;
+
+        offset = BITMAP_NUM_TO_OFFSET(n);
+
+        if (offset >= b->n_bitmaps) {
+                if (!GREEDY_REALLOC0(b->bitmaps, b->bitmaps_allocated, offset + 1))
+                        return -ENOMEM;
+
+                b->n_bitmaps = offset + 1;
+        }
+
+        bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n);
+
+        b->bitmaps[offset] |= bitmask;
+
+        return 0;
+}
+
+void bitmap_unset(Bitmap *b, unsigned n) {
+        uint64_t bitmask;
+        unsigned offset;
+
+        if (!b)
+                return;
+
+        offset = BITMAP_NUM_TO_OFFSET(n);
+
+        if (offset >= b->n_bitmaps)
+                return;
+
+        bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n);
+
+        b->bitmaps[offset] &= ~bitmask;
+}
+
+bool bitmap_isset(Bitmap *b, unsigned n) {
+        uint64_t bitmask;
+        unsigned offset;
+
+        if (!b)
+                return false;
+
+        offset = BITMAP_NUM_TO_OFFSET(n);
+
+        if (offset >= b->n_bitmaps)
+                return false;
+
+        bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n);
+
+        return !!(b->bitmaps[offset] & bitmask);
+}
+
+bool bitmap_isclear(Bitmap *b) {
+        unsigned i;
+
+        assert(b);
+
+        for (i = 0; i < b->n_bitmaps; i++)
+                if (b->bitmaps[i] != 0)
+                        return false;
+
+        return true;
+}
+
+void bitmap_clear(Bitmap *b) {
+        assert(b);
+
+        b->n_bitmaps = 0;
+}
+
+bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
+        uint64_t bitmask;
+        unsigned offset, rem;
+
+        assert(i);
+        assert(n);
+
+        if (!b || i->idx == BITMAP_END)
+                return false;
+
+        offset = BITMAP_NUM_TO_OFFSET(i->idx);
+        rem = BITMAP_NUM_TO_REM(i->idx);
+        bitmask = UINT64_C(1) << rem;
+
+        for (; offset < b->n_bitmaps; offset ++) {
+                if (b->bitmaps[offset]) {
+                        for (; bitmask; bitmask <<= 1, rem ++) {
+                                if (b->bitmaps[offset] & bitmask) {
+                                        *n = BITMAP_OFFSET_TO_NUM(offset, rem);
+                                        i->idx = *n + 1;
+
+                                        return true;
+                                }
+                        }
+                }
+
+                rem = 0;
+                bitmask = 1;
+        }
+
+        i->idx = BITMAP_END;
+
+        return false;
+}
+
+bool bitmap_equal(Bitmap *a, Bitmap *b) {
+
+        if (!a ^ !b)
+                return false;
+
+        if (!a)
+                return true;
+
+        if (a->n_bitmaps != b->n_bitmaps)
+                return false;
+
+        return memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * a->n_bitmaps) == 0;
+}
diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h
new file mode 100644 (file)
index 0000000..2874bc9
--- /dev/null
@@ -0,0 +1,50 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+#include "hashmap.h"
+
+typedef struct Bitmap Bitmap;
+
+Bitmap *bitmap_new(void);
+
+void bitmap_free(Bitmap *b);
+
+int bitmap_ensure_allocated(Bitmap **b);
+
+int bitmap_set(Bitmap *b, unsigned n);
+void bitmap_unset(Bitmap *b, unsigned n);
+bool bitmap_isset(Bitmap *b, unsigned n);
+bool bitmap_isclear(Bitmap *b);
+void bitmap_clear(Bitmap *b);
+
+bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n);
+
+bool bitmap_equal(Bitmap *a, Bitmap *b);
+
+#define BITMAP_FOREACH(n, b, i) \
+        for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); )
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Bitmap*, bitmap_free);
+
+#define _cleanup_bitmap_free_ _cleanup_(bitmap_freep)
index 58f00e6daec180c910bb72ff4cf83efd0c7c1ec3..8dbe4da5bbbe1280518e81bca9af166549f06bf1 100644 (file)
@@ -204,7 +204,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
         if (asprintf(&p, "%u %u", lo, hi) < 0)
                 return -ENOMEM;
 
-        r = write_string_file(fn, p);
+        r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE);
         free(p);
 
         return r;
index 439c5516dc2cbfb2206c488dc123e3af4714a15e..34a30605092abdc980776c2686e675e9f0b80317 100644 (file)
@@ -646,7 +646,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
 
         snprintf(c, sizeof(c), PID_FMT"\n", pid);
 
-        return write_string_file_no_create(fs, c);
+        return write_string_file(fs, c, 0);
 }
 
 int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
@@ -820,7 +820,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
 
         sc = strstrip(contents);
         if (sc[0] == 0) {
-                r = write_string_file_no_create(fs, agent);
+                r = write_string_file(fs, agent, 0);
                 if (r < 0)
                         return r;
         } else if (!streq(sc, agent))
@@ -840,7 +840,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
 
         sc = strstrip(contents);
         if (streq(sc, "0")) {
-                r = write_string_file_no_create(fs, "1");
+                r = write_string_file(fs, "1", 0);
                 if (r < 0)
                         return r;
 
@@ -861,7 +861,7 @@ int cg_uninstall_release_agent(const char *controller) {
         if (r < 0)
                 return r;
 
-        r = write_string_file_no_create(fs, "0");
+        r = write_string_file(fs, "0", 0);
         if (r < 0)
                 return r;
 
@@ -872,7 +872,7 @@ int cg_uninstall_release_agent(const char *controller) {
         if (r < 0)
                 return r;
 
-        r = write_string_file_no_create(fs, "");
+        r = write_string_file(fs, "", 0);
         if (r < 0)
                 return r;
 
@@ -1708,7 +1708,7 @@ int cg_set_attribute(const char *controller, const char *path, const char *attri
         if (r < 0)
                 return r;
 
-        return write_string_file_no_create(p, value);
+        return write_string_file(p, value, 0);
 }
 
 int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
index 230e7e4d3fc3a793431fc5165a5c3631dbbf0d3b..e2d356d67656197ac2daa9df5d4a636fe1ba6045 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "util.h"
 #include "btrfs-util.h"
+#include "strv.h"
 #include "copy.h"
 
 #define COPY_BUFFER_SIZE (16*1024)
@@ -262,10 +263,13 @@ static int fd_copy_directory(
                 (void) copy_xattr(dirfd(d), fdt);
         }
 
-        FOREACH_DIRENT(de, d, return -errno) {
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 struct stat buf;
                 int q;
 
+                if (STR_IN_SET(de->d_name, ".", ".."))
+                        continue;
+
                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
                         r = -errno;
                         continue;
index 5ab36825c0c62eb55b3b71274d907fe5e2602c7e..fcff753ada3a6592117cf74e625d42b8a8ffbf86 100644 (file)
@@ -151,6 +151,9 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
 
                 case EXIT_BUS_ENDPOINT:
                         return "BUS_ENDPOINT";
+
+                case EXIT_SMACK_PROCESS_LABEL:
+                        return "SMACK_PROCESS_LABEL";
                 }
         }
 
index bec988ca7872c143bf794872c4612068e6c0f958..f596f1d11fc6d54be479bfd8f13c35079c5cf857 100644 (file)
@@ -31,7 +31,7 @@ int write_string_file_atomic_label(const char *fn, const char *line) {
         if (r < 0)
                 return r;
 
-        r = write_string_file_atomic(fn, line);
+        r = write_string_file(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
 
         mac_selinux_create_file_clear();
 
index 00fb6f8b5cce1b6f75190bcb992383a7fdc563a8..2216853777d164646dc32c6f01c899556fc7cce1 100644 (file)
 #include "ctype.h"
 #include "fileio.h"
 
-int write_string_stream(FILE *f, const char *line) {
+int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
         assert(f);
         assert(line);
 
         errno = 0;
 
         fputs(line, f);
-        if (!endswith(line, "\n"))
+        if (enforce_newline && !endswith(line, "\n"))
                 fputc('\n', f);
 
         fflush(f);
@@ -45,42 +45,7 @@ int write_string_stream(FILE *f, const char *line) {
         return 0;
 }
 
-int write_string_file(const char *fn, const char *line) {
-        _cleanup_fclose_ FILE *f = NULL;
-
-        assert(fn);
-        assert(line);
-
-        f = fopen(fn, "we");
-        if (!f)
-                return -errno;
-
-        return write_string_stream(f, line);
-}
-
-int write_string_file_no_create(const char *fn, const char *line) {
-        _cleanup_fclose_ FILE *f = NULL;
-        int fd;
-
-        assert(fn);
-        assert(line);
-
-        /* We manually build our own version of fopen(..., "we") that
-         * works without O_CREAT */
-        fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return -errno;
-
-        f = fdopen(fd, "we");
-        if (!f) {
-                safe_close(fd);
-                return -errno;
-        }
-
-        return write_string_stream(f, line);
-}
-
-int write_string_file_atomic(const char *fn, const char *line) {
+static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *p = NULL;
         int r;
@@ -94,7 +59,7 @@ int write_string_file_atomic(const char *fn, const char *line) {
 
         fchmod_umask(fileno(f), 0644);
 
-        r = write_string_stream(f, line);
+        r = write_string_stream(f, line, enforce_newline);
         if (r >= 0) {
                 if (rename(p, fn) < 0)
                         r = -errno;
@@ -106,6 +71,41 @@ int write_string_file_atomic(const char *fn, const char *line) {
         return r;
 }
 
+int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
+        _cleanup_fclose_ FILE *f = NULL;
+
+        assert(fn);
+        assert(line);
+
+        if (flags & WRITE_STRING_FILE_ATOMIC) {
+                assert(flags & WRITE_STRING_FILE_CREATE);
+
+                return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+        }
+
+        if (flags & WRITE_STRING_FILE_CREATE) {
+                f = fopen(fn, "we");
+                if (!f)
+                        return -errno;
+        } else {
+                int fd;
+
+                /* We manually build our own version of fopen(..., "we") that
+                 * works without O_CREAT */
+                fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0)
+                        return -errno;
+
+                f = fdopen(fd, "we");
+                if (!f) {
+                        safe_close(fd);
+                        return -errno;
+                }
+        }
+
+        return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+}
+
 int read_one_line_file(const char *fn, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
         char t[LINE_MAX], *c;
@@ -786,7 +786,7 @@ int executable_is_script(const char *path, char **interpreter) {
  */
 int get_status_field(const char *filename, const char *pattern, char **field) {
         _cleanup_free_ char *status = NULL;
-        char *t;
+        char *t, *f;
         size_t len;
         int r;
 
@@ -820,9 +820,10 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
 
         len = strcspn(t, WHITESPACE);
 
-        *field = strndup(t, len);
-        if (!*field)
+        f = strndup(t, len);
+        if (!f)
                 return -ENOMEM;
 
+        *field = f;
         return 0;
 }
index 91d4a0d2d5034ec9610435f8fc55c0773fdf4995..2e8148ff2424563bcfc60856a7591bde502601d8 100644 (file)
 
 #include "macro.h"
 
-int write_string_stream(FILE *f, const char *line);
-int write_string_file(const char *fn, const char *line);
-int write_string_file_no_create(const char *fn, const char *line);
-int write_string_file_atomic(const char *fn, const char *line);
+typedef enum {
+        WRITE_STRING_FILE_CREATE = 1,
+        WRITE_STRING_FILE_ATOMIC = 2,
+        WRITE_STRING_FILE_AVOID_NEWLINE = 4,
+} WriteStringFileFlags;
+
+int write_string_stream(FILE *f, const char *line, bool enforce_newline);
+int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags);
 
 int read_one_line_file(const char *fn, char **line);
 int read_full_file(const char *fn, char **contents, size_t *size);
index 5fa17ed2085cdc2fe3a7b992468f5917d6cd96b0..627d768b76bada29f5f7d106a13ca215a55470cf 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <inttypes.h>
+#include <stdbool.h>
 
 #define _printf_(a,b) __attribute__ ((format (printf, a, b)))
 #define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
@@ -406,12 +407,12 @@ do {                                                                    \
 
 #define IN_SET(x, y, ...)                                               \
         ({                                                              \
-                const typeof(y) _y = (y);                               \
-                const typeof(_y) _x = (x);                              \
+                static const typeof(y) _array[] = { (y), __VA_ARGS__ }; \
+                const typeof(y) _x = (x);                               \
                 unsigned _i;                                            \
                 bool _found = false;                                    \
-                for (_i = 0; _i < 1 + sizeof((const typeof(_x)[]) { __VA_ARGS__ })/sizeof(const typeof(_x)); _i++) \
-                        if (((const typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \
+                for (_i = 0; _i < ELEMENTSOF(_array); _i++)             \
+                        if (_array[_i] == _x) {                         \
                                 _found = true;                          \
                                 break;                                  \
                         }                                               \
@@ -461,6 +462,18 @@ do {                                                                    \
 #define GID_INVALID ((gid_t) -1)
 #define MODE_INVALID ((mode_t) -1)
 
+static inline bool UID_IS_INVALID(uid_t uid) {
+        /* We consider both the old 16bit -1 user and the newer 32bit
+         * -1 user invalid, since they are or used to be incompatible
+         * with syscalls such as setresuid() or chown(). */
+
+        return uid == (uid_t) ((uint32_t) -1) || uid == (uid_t) ((uint16_t) -1);
+}
+
+static inline bool GID_IS_INVALID(gid_t gid) {
+        return gid == (gid_t) ((uint32_t) -1) || gid == (gid_t) ((uint16_t) -1);
+}
+
 #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
         static inline void func##p(type *p) {                   \
                 if (*p)                                         \
index be7f6186fcfb1fd556ed85c2ddcdb4fc7adcf21e..ed6cd80c7538c4a848ce4ab168db25fb62aa9859 100644 (file)
@@ -772,7 +772,7 @@ static inline int setns(int fd, int nstype) {
 #define IFLA_VXLAN_MAX  (__IFLA_VXLAN_MAX - 1)
 #endif
 
-#if !HAVE_DECL_IFLA_IPTUN_6RD_RELAY_PREFIXLEN
+#if !HAVE_DECL_IFLA_IPTUN_ENCAP_DPORT
 #define IFLA_IPTUN_UNSPEC 0
 #define IFLA_IPTUN_LINK 1
 #define IFLA_IPTUN_LOCAL 2
@@ -788,11 +788,41 @@ static inline int setns(int fd, int nstype) {
 #define IFLA_IPTUN_6RD_RELAY_PREFIX 12
 #define IFLA_IPTUN_6RD_PREFIXLEN 13
 #define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14
-#define __IFLA_IPTUN_MAX 15
+#define IFLA_IPTUN_ENCAP_TYPE 15
+#define IFLA_IPTUN_ENCAP_FLAGS 16
+#define IFLA_IPTUN_ENCAP_SPORT 17
+#define IFLA_IPTUN_ENCAP_DPORT 18
+
+#define __IFLA_IPTUN_MAX 19
 
 #define IFLA_IPTUN_MAX  (__IFLA_IPTUN_MAX - 1)
 #endif
 
+#if !HAVE_DECL_IFLA_GRE_ENCAP_DPORT
+#define IFLA_GRE_UNSPEC 0
+#define IFLA_GRE_LINK 1
+#define IFLA_GRE_IFLAGS 2
+#define IFLA_GRE_OFLAGS 3
+#define IFLA_GRE_IKEY 4
+#define IFLA_GRE_OKEY 5
+#define IFLA_GRE_LOCAL 6
+#define IFLA_GRE_REMOTE 7
+#define IFLA_GRE_TTL 8
+#define IFLA_GRE_TOS 9
+#define IFLA_GRE_PMTUDISC 10
+#define IFLA_GRE_ENCAP_LIMIT 11
+#define IFLA_GRE_FLOWINFO 12
+#define IFLA_GRE_FLAGS 13
+#define IFLA_GRE_ENCAP_TYPE 14
+#define IFLA_GRE_ENCAP_FLAGS 15
+#define IFLA_GRE_ENCAP_SPORT 16
+#define IFLA_GRE_ENCAP_DPORT 17
+
+#define __IFLA_GRE_MAX 18
+
+#define IFLA_GRE_MAX  (__IFLA_GRE_MAX - 1)
+#endif
+
 #if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO
 #define IFLA_BRIDGE_FLAGS 0
 #define IFLA_BRIDGE_MODE 1
@@ -802,7 +832,7 @@ static inline int setns(int fd, int nstype) {
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 #endif
 
-#if !HAVE_DECL_IFLA_BRPORT_UNICAST_FLOOD
+#if !HAVE_DECL_IFLA_BRPORT_LEARNING_SYNC
 #define IFLA_BRPORT_UNSPEC 0
 #define IFLA_BRPORT_STATE 1
 #define IFLA_BRPORT_PRIORITY 2
@@ -813,7 +843,9 @@ static inline int setns(int fd, int nstype) {
 #define IFLA_BRPORT_FAST_LEAVE 7
 #define IFLA_BRPORT_LEARNING 8
 #define IFLA_BRPORT_UNICAST_FLOOD 9
-#define __IFLA_BRPORT_MAX 10
+#define IFLA_BRPORT_PROXYARP 10
+#define IFLA_BRPORT_LEARNING_SYNC 11
+#define __IFLA_BRPORT_MAX 12
 
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
 #endif
index 8f49d652664fcf2b63aa29740da6c61f741e350f..5cbfc145a48c843131b7ec5305abcfd326b73eaa 100644 (file)
@@ -656,9 +656,11 @@ int path_is_mount_point(const char *t, int flags) {
                 canonical = canonicalize_file_name(t);
                 if (!canonical)
                         return -errno;
+
+                t = canonical;
         }
 
-        r = path_get_parent(canonical ?: t, &parent);
+        r = path_get_parent(t, &parent);
         if (r < 0)
                 return r;
 
@@ -666,7 +668,7 @@ int path_is_mount_point(const char *t, int flags) {
         if (fd < 0)
                 return -errno;
 
-        return fd_is_mount_point(fd, basename(canonical ?: t), flags);
+        return fd_is_mount_point(fd, basename(t), flags);
 }
 
 int path_is_read_only_fs(const char *path) {
index 2c05f2fee4cf4d5ce47cb5fc64f510e3de035e7b..61f188467f8a15e4afe439a6c34f523a065855cc 100644 (file)
@@ -43,7 +43,10 @@ int get_process_state(pid_t pid) {
         assert(pid >= 0);
 
         p = procfs_file_alloca(pid, "stat");
+
         r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
         if (r < 0)
                 return r;
 
@@ -87,8 +90,11 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
         p = procfs_file_alloca(pid, "cmdline");
 
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         if (max_length == 0) {
                 size_t len = 0, allocated = 0;
@@ -182,8 +188,11 @@ int is_kernel_thread(pid_t pid) {
 
         p = procfs_file_alloca(pid, "cmdline");
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         count = fread(&c, 1, 1, f);
         eof = feof(f);
@@ -199,13 +208,18 @@ int is_kernel_thread(pid_t pid) {
 
 int get_process_capeff(pid_t pid, char **capeff) {
         const char *p;
+        int r;
 
         assert(capeff);
         assert(pid >= 0);
 
         p = procfs_file_alloca(pid, "status");
 
-        return get_status_field(p, "\nCapEff:", capeff);
+        r = get_status_field(p, "\nCapEff:", capeff);
+        if (r == -ENOENT)
+                return -ESRCH;
+
+        return r;
 }
 
 static int get_process_link_contents(const char *proc_file, char **name) {
@@ -215,8 +229,10 @@ static int get_process_link_contents(const char *proc_file, char **name) {
         assert(name);
 
         r = readlink_malloc(proc_file, name);
+        if (r == -ENOENT)
+                return -ESRCH;
         if (r < 0)
-                return r == -ENOENT ? -ESRCH : r;
+                return r;
 
         return 0;
 }
@@ -253,8 +269,11 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
 
         p = procfs_file_alloca(pid, "status");
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         FOREACH_LINE(line, f, return -errno) {
                 char *l;
@@ -316,8 +335,11 @@ int get_process_environ(pid_t pid, char **env) {
         p = procfs_file_alloca(pid, "environ");
 
         f = fopen(p, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         while ((c = fgetc(f)) != EOF) {
                 if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
@@ -329,10 +351,13 @@ int get_process_environ(pid_t pid, char **env) {
                         sz += cescape_char(c, outcome + sz);
         }
 
-        if (sz == 0)
-                return -ENOENT;
+        if (!outcome) {
+                outcome = strdup("");
+                if (!outcome)
+                        return -ENOMEM;
+        } else
+                outcome[sz] = '\0';
 
-        outcome[sz] = '\0';
         *env = outcome;
         outcome = NULL;
 
@@ -355,6 +380,8 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
 
         p = procfs_file_alloca(pid, "stat");
         r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
         if (r < 0)
                 return r;
 
@@ -475,8 +502,11 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
         path = procfs_file_alloca(pid, "environ");
 
         f = fopen(path, "re");
-        if (!f)
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
                 return -errno;
+        }
 
         l = strlen(field);
         r = 0;
@@ -535,7 +565,7 @@ bool pid_is_alive(pid_t pid) {
                 return false;
 
         r = get_process_state(pid);
-        if (r == -ENOENT || r == 'Z')
+        if (r == -ESRCH || r == 'Z')
                 return false;
 
         return true;
index 2e24b1ea992e0dea7deb8079ec276d8c8cc54a9a..047aa294f4c0a11610530adb2dd875ca7ec4ebeb 100644 (file)
@@ -139,7 +139,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
                 return 0;
 
         p = procfs_file_alloca(pid, "attr/current");
-        r = write_string_file(p, label);
+        r = write_string_file(p, label, 0);
         if (r < 0)
                 return r;
 #endif
index aa912bde287766e0d531192fecd3d449e0093658..1c15fbc17204885086c82f0a0a852c9c07b16dbb 100644 (file)
@@ -916,32 +916,573 @@ char *hexmem(const void *p, size_t l) {
         return r;
 }
 
-void *unhexmem(const char *p, size_t l) {
-        uint8_t *r, *z;
+int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        uint8_t *z;
         const char *x;
 
+        assert(mem);
+        assert(len);
         assert(p);
 
         z = r = malloc((l + 1) / 2 + 1);
         if (!r)
-                return NULL;
+                return -ENOMEM;
 
         for (x = p; x < p + l; x += 2) {
                 int a, b;
 
                 a = unhexchar(x[0]);
-                if (x+1 < p + l)
+                if (a < 0)
+                        return a;
+                else if (x+1 < p + l) {
                         b = unhexchar(x[1]);
-                else
+                        if (b < 0)
+                                return b;
+                } else
                         b = 0;
 
                 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
         }
 
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *len = (l + 1) / 2;
+
+        return 0;
+}
+
+/* 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";
+
+        return table[x & 31];
+}
+
+int unbase32hexchar(char c) {
+        unsigned offset;
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        offset = '9' - '0' + 1;
+
+        if (c >= 'A' && c <= 'V')
+                return c - 'A' + offset;
+
+        return -EINVAL;
+}
+
+char *base32hexmem(const void *p, size_t l, bool padding) {
+        char *r, *z;
+        const uint8_t *x;
+        size_t len;
+
+        if (padding)
+                /* five input bytes makes eight output bytes, padding is added so we must round up */
+                len = 8 * (l + 4) / 5;
+        else {
+                /* same, but round down as there is no padding */
+                len = 8 * l / 5;
+
+                switch (l % 5) {
+                case 4:
+                        len += 7;
+                        break;
+                case 3:
+                        len += 5;
+                        break;
+                case 2:
+                        len += 4;
+                        break;
+                case 1:
+                        len += 2;
+                        break;
+                }
+        }
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
+                /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
+                   x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
+                *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);  /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+                *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
+                *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5);  /* 000QQWWW */
+                *(z++) = base32hexchar((x[4] & 31));                  /* 000WWWWW */
+        }
+
+        switch (l % 5) {
+        case 4:
+                *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);   /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
+                *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
+                *(z++) = base32hexchar((x[3] & 3) << 3);              /* 000QQ000 */
+                if (padding)
+                        *(z++) = '=';
+
+                break;
+
+        case 3:
+                *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
+                *(z++) = base32hexchar((x[2] & 15) << 1);            /* 000ZZZZ0 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+
+        case 2:
+                *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
+                *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
+                *(z++) = base32hexchar((x[1] & 1) << 4);             /* 000Y0000 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+
+        case 1:
+                *(z++) = base32hexchar(x[0] >> 3);       /* 000XXXXX */
+                *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
+                if (padding) {
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                        *(z++) = '=';
+                }
+
+                break;
+        }
+
+        *z = 0;
+        return r;
+}
+
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d, e, f, g, h;
+        uint8_t *z;
+        const char *x;
+        size_t len;
+        unsigned pad = 0;
+
+        assert(p);
+
+        /* padding ensures any base32hex input has input divisible by 8 */
+        if (padding && l % 8 != 0)
+                return -EINVAL;
+
+        if (padding) {
+                /* strip the padding */
+                while (l > 0 && p[l - 1] == '=' && pad < 7) {
+                        pad ++;
+                        l --;
+                }
+        }
+
+        /* a group of eight input bytes needs five output bytes, in case of
+           padding we need to add some extra bytes */
+        len = (l / 8) * 5;
+
+        switch (l % 8) {
+        case 7:
+                len += 4;
+                break;
+        case 5:
+                len += 3;
+                break;
+        case 4:
+                len += 2;
+                break;
+        case 2:
+                len += 1;
+                break;
+        case 0:
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + (l / 8) * 8; x += 8) {
+                /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
+                   e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                f = unbase32hexchar(x[5]);
+                if (f < 0)
+                        return -EINVAL;
+
+                g = unbase32hexchar(x[6]);
+                if (g < 0)
+                        return -EINVAL;
+
+                h = unbase32hexchar(x[7]);
+                if (h < 0)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+                *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+                *(z++) = (uint8_t) g << 5 | (uint8_t) h;                         /* VVVRRRRR */
+        }
+
+        switch (l % 8) {
+        case 7:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                f = unbase32hexchar(x[5]);
+                if (f < 0)
+                        return -EINVAL;
+
+                g = unbase32hexchar(x[6]);
+                if (g < 0)
+                        return -EINVAL;
+
+                /* g == 000VV000 */
+                if (g & 7)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+                *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
+
+                break;
+        case 5:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                e = unbase32hexchar(x[4]);
+                if (e < 0)
+                        return -EINVAL;
+
+                /* e == 000SSSS0 */
+                if (e & 1)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+                *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
+
+                break;
+        case 4:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase32hexchar(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase32hexchar(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                /* d == 000W0000 */
+                if (d & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
+                *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
+
+                break;
+        case 2:
+                a = unbase32hexchar(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase32hexchar(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* b == 000YYY00 */
+                if (b & 3)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
+
+                break;
+        case 0:
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *_len = len;
+
+        return 0;
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-4 */
+char base64char(int x) {
+        static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                      "abcdefghijklmnopqrstuvwxyz"
+                                      "0123456789+/";
+        return table[x & 63];
+}
+
+int unbase64char(char c) {
+        unsigned offset;
+
+        if (c >= 'A' && c <= 'Z')
+                return c - 'A';
+
+        offset = 'Z' - 'A' + 1;
+
+        if (c >= 'a' && c <= 'z')
+                return c - 'a' + offset;
+
+        offset += 'z' - 'a' + 1;
+
+        if (c >= '0' && c <= '9')
+                return c - '0' + offset;
+
+        offset += '9' - '0' + 1;
+
+        if (c == '+')
+                return offset;
+
+        offset ++;
+
+        if (c == '/')
+                return offset;
+
+        return -EINVAL;
+}
+
+char *base64mem(const void *p, size_t l) {
+        char *r, *z;
+        const uint8_t *x;
+
+        /* three input bytes makes four output bytes, padding is added so we must round up */
+        z = r = malloc(4 * (l + 2) / 3 + 1);
+        if (!r)
+                return NULL;
+
+        for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
+                /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+                *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
+                *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+                *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
+        }
+
+        switch (l % 3) {
+        case 2:
+                *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+                *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
+                *(z++) = '=';
+
+                break;
+        case 1:
+                *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
+                *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
+                *(z++) = '=';
+                *(z++) = '=';
+
+                break;
+        }
+
         *z = 0;
         return r;
 }
 
+int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d;
+        uint8_t *z;
+        const char *x;
+        size_t len;
+
+        assert(p);
+
+        /* padding ensures any base63 input has input divisible by 4 */
+        if (l % 4 != 0)
+                return -EINVAL;
+
+        /* strip the padding */
+        if (l > 0 && p[l - 1] == '=')
+                l --;
+        if (l > 0 && p[l - 1] == '=')
+                l --;
+
+        /* a group of four input bytes needs three output bytes, in case of
+           padding we need to add two or three extra bytes */
+        len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
+
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
+
+        for (x = p; x < p + (l / 4) * 4; x += 4) {
+                /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                d = unbase64char(x[3]);
+                if (d < 0)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+                *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
+        }
+
+        switch (l % 4) {
+        case 3:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
+
+                /* c == 00ZZZZ00 */
+                if (c & 3)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+
+                break;
+        case 2:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
+
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
+
+                /* b == 00YY0000 */
+                if (b & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+
+                break;
+        case 0:
+
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        *z = 0;
+
+        *mem = r;
+        r = NULL;
+        *_len = len;
+
+        return 0;
+}
+
 char octchar(int x) {
         return '0' + (x & 7);
 }
@@ -2533,8 +3074,9 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
 
         f = fdopen(fd, "we");
         if (!f) {
-                unlink(t);
+                unlink_noerrno(t);
                 free(t);
+                safe_close(fd);
                 return -errno;
         }
 
@@ -4716,7 +5258,7 @@ int update_reboot_param_file(const char *param) {
 
         if (param) {
 
-                r = write_string_file(REBOOT_PARAM_FILE, param);
+                r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
                 if (r < 0)
                         log_error("Failed to write reboot param to "
                                   REBOOT_PARAM_FILE": %s", strerror(-r));
@@ -5192,13 +5734,19 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
                 case VALUE:
                         if (c == 0)
                                 goto finish;
-                        else if (c == '\'')
+                        else if (c == '\'') {
+                                if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                        return -ENOMEM;
+
                                 state = SINGLE_QUOTE;
-                        else if (c == '\\')
+                        else if (c == '\\')
                                 state = VALUE_ESCAPE;
-                        else if (c == '\"')
+                        else if (c == '\"') {
+                                if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                        return -ENOMEM;
+
                                 state = DOUBLE_QUOTE;
-                        else if (strchr(WHITESPACE, c))
+                        else if (strchr(WHITESPACE, c))
                                 state = SPACE;
                         else {
                                 if (!GREEDY_REALLOC(s, allocated, sz+2))
index a1d1dd15c3948b3682da88632c63271ce70bbb92..c2e5cc610b961677cbd7a0c0a7f086685ea09e72 100644 (file)
@@ -240,6 +240,10 @@ char octchar(int x) _const_;
 int unoctchar(char c) _const_;
 char decchar(int x) _const_;
 int undecchar(char c) _const_;
+char base32hexchar(int x) _const_;
+int unbase32hexchar(char c) _const_;
+char base64char(int x) _const_;
+int unbase64char(char c) _const_;
 
 char *cescape(const char *s);
 size_t cescape_char(char c, char *buf);
@@ -614,7 +618,13 @@ static inline void *mempset(void *s, int c, size_t n) {
 }
 
 char *hexmem(const void *p, size_t l);
-void *unhexmem(const char *p, size_t l);
+int unhexmem(const char *p, size_t l, void **mem, size_t *len);
+
+char *base32hexmem(const void *p, size_t l, bool padding);
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
+
+char *base64mem(const void *p, size_t l);
+int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
 
 char *strextend(char **x, ...) _sentinel_;
 char *strrep(const char *s, unsigned n);
index 1299a75ed54d242ef9b93964b7a24dfe302fff9c..a8d26716a1de41e70fc2ed8c6bd26b31ab4542b7 100644 (file)
@@ -188,7 +188,7 @@ int detect_vm(const char **id) {
         _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
         static thread_local int cached_found = -1;
         static thread_local const char *cached_id = NULL;
-        const char *_id = NULL;
+        const char *_id = NULL, *_id_cpuid = NULL;
         int r;
 
         if (_likely_(cached_found >= 0)) {
@@ -234,10 +234,26 @@ int detect_vm(const char **id) {
 
         /* this will set _id to "other" and return 0 for unknown hypervisors */
         r = detect_vm_cpuid(&_id);
-        if (r != 0)
+
+        /* finish when found a known hypervisor other than kvm */
+        if (r < 0 || (r > 0 && !streq(_id, "kvm")))
                 goto finish;
 
+        _id_cpuid = _id;
+
         r = detect_vm_dmi(&_id);
+
+        /* kvm with and without Virtualbox */
+        if (streq_ptr(_id_cpuid, "kvm")) {
+                if (r > 0 && streq(_id, "oracle"))
+                        goto finish;
+
+                _id = _id_cpuid;
+                r = 1;
+                goto finish;
+        }
+
+        /* information from dmi */
         if (r != 0)
                 goto finish;
 
index 6028ed68c04b31e66ed85fcad69648e799682111..1e216f52bd4c739deb1ac2dc637b9803c8dbcaff 100644 (file)
@@ -53,7 +53,7 @@ static int delete_rule(const char *rule) {
         if (!fn)
                 return log_oom();
 
-        return write_string_file(fn, "-1");
+        return write_string_file(fn, "-1", 0);
 }
 
 static int apply_rule(const char *rule) {
@@ -61,7 +61,7 @@ static int apply_rule(const char *rule) {
 
         delete_rule(rule);
 
-        r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule);
+        r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to add binary format: %m");
 
@@ -191,7 +191,7 @@ int main(int argc, char *argv[]) {
                 }
 
                 /* Flush out all rules */
-                write_string_file("/proc/sys/fs/binfmt_misc/status", "-1");
+                write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0);
 
                 STRV_FOREACH(f, files) {
                         k = apply_file(*f, true);
index ed69fb0cec94e4fe8dfc5142b3df689012327a16..091ea375d32a77c43b241c463c031045cfbe429f 100644 (file)
@@ -294,6 +294,8 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
         else if (r < 0)
                 return r;
 
+        printf("\n");
+
         return 0;
 }
 
@@ -888,7 +890,7 @@ static int install_loader_config(const char *esp_path) {
 
         f = fopen("/etc/machine-id", "re");
         if (!f)
-                return -errno;
+                return errno == ENOENT ? 0 : -errno;
 
         if (fgets(line, sizeof(line), f) != NULL) {
                 char *s;
index eb1a4e3b669618378dc182fa165423518fdf759e..e8cd8abd26155e86ad4085fbd0e7eff16c4557d3 100644 (file)
@@ -1495,6 +1495,7 @@ static VOID config_entry_add_osx(Config *config) {
 static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
         EFI_FILE_HANDLE linux_dir;
         EFI_STATUS err;
+        ConfigEntry *entry;
 
         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
         if (!EFI_ERROR(err)) {
@@ -1504,6 +1505,7 @@ static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_ima
                         EFI_FILE_INFO *f;
                         CHAR8 *sections[] = {
                                 (UINT8 *)".osrel",
+                                (UINT8 *)".cmdline",
                                 NULL
                         };
                         UINTN offs[ELEMENTSOF(sections)-1] = {};
@@ -1517,6 +1519,7 @@ static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_ima
                         CHAR16 *os_name = NULL;
                         CHAR16 *os_id = NULL;
                         CHAR16 *os_version = NULL;
+                        CHAR16 *os_build = NULL;
 
                         bufsize = sizeof(buf);
                         err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
@@ -1534,7 +1537,7 @@ static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_ima
                         if (StriCmp(f->FileName + len - 4, L".efi") != 0)
                                 continue;
 
-                        /* look for an .osrel section in the .efi binary */
+                        /* look for .osrel and .cmdline sections in the .efi binary */
                         err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
                         if (EFI_ERROR(err))
                                 continue;
@@ -1547,35 +1550,56 @@ static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_ima
                         line = content;
                         while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
                                 if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
+                                        FreePool(os_name);
                                         os_name = stra_to_str(value);
                                         continue;
                                 }
 
                                 if (strcmpa((CHAR8 *)"ID", key) == 0) {
+                                        FreePool(os_id);
                                         os_id = stra_to_str(value);
                                         continue;
                                 }
 
                                 if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
+                                        FreePool(os_version);
                                         os_version = stra_to_str(value);
                                         continue;
                                 }
+
+                                if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) {
+                                        FreePool(os_build);
+                                        os_build = stra_to_str(value);
+                                        continue;
+                                }
                         }
 
-                        if (os_name && os_id && os_version) {
+                        if (os_name && os_id && (os_version || os_build)) {
                                 CHAR16 *conf;
                                 CHAR16 *path;
+                                CHAR16 *cmdline;
 
-                                conf = PoolPrint(L"%s-%s", os_id, os_version);
+                                conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
                                 path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
-                                config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
+                                entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
+
+                                FreePool(content);
+                                /* read the embedded cmdline file */
+                                len = file_read(linux_dir, f->FileName, offs[1], szs[1] - 1 , &content);
+                                if (len > 0) {
+                                        cmdline = stra_to_str(content);
+                                        entry->options = cmdline;
+                                        cmdline = NULL;
+                                }
+                                FreePool(cmdline);
                                 FreePool(conf);
                                 FreePool(path);
-                                FreePool(os_name);
-                                FreePool(os_id);
-                                FreePool(os_version);
                         }
 
+                        FreePool(os_name);
+                        FreePool(os_id);
+                        FreePool(os_version);
+                        FreePool(os_build);
                         FreePool(content);
                 }
                 uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
index 3360bc85beae34431cde9cb8c3593694a3efe918..1625d51fa8098c9b982bb09a790d9cf5d1c5c7ce 100644 (file)
@@ -387,9 +387,6 @@ int main(int argc, char *argv[]) {
         for (samples = 0; !exiting && samples < arg_samples_len; samples++) {
                 int res;
                 double sample_stop;
-                struct timespec req;
-                time_t newint_s;
-                long newint_ns;
                 double elapsed;
                 double timeleft;
 
@@ -427,18 +424,17 @@ int main(int argc, char *argv[]) {
                 elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0;
                 timeleft = interval - elapsed;
 
-                newint_s = (time_t)(timeleft / 1000000000.0);
-                newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
-
                 /*
                  * check if we have not consumed our entire timeslice. If we
                  * do, don't sleep and take a new sample right away.
                  * we'll lose all the missed samples and overrun our total
                  * time
                  */
-                if (newint_ns > 0 || newint_s > 0) {
-                        req.tv_sec = newint_s;
-                        req.tv_nsec = newint_ns;
+                if (timeleft > 0) {
+                        struct timespec req;
+
+                        req.tv_sec = (time_t)(timeleft / 1000000000.0);
+                        req.tv_nsec = (long)(timeleft - (req.tv_sec * 1000000000.0));
 
                         res = nanosleep(&req, NULL);
                         if (res) {
@@ -452,7 +448,7 @@ int main(int argc, char *argv[]) {
                 } else {
                         overrun++;
                         /* calculate how many samples we lost and scrap them */
-                        arg_samples_len -= (int)(newint_ns / interval);
+                        arg_samples_len -= (int)(-timeleft / interval);
                 }
                 LIST_PREPEND(link, head, sampledata);
         }
index 4ac955da41b4f5cd6f6e93ff7c70c5c1be5adb5e..1cb5ea50086af9aaecfadfddee12c2474b3a5664 100644 (file)
@@ -33,6 +33,7 @@
 #include "strv.h"
 #include "set.h"
 #include "driver.h"
+#include "proxy.h"
 #include "synthesize.h"
 
 static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
@@ -70,7 +71,7 @@ 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);
 }
 
-int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names) {
+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;
 
         assert(a);
@@ -189,7 +190,7 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
                 if (r < 0)
                         return synthetic_reply_method_errno(m, r, NULL);
 
-                r = sd_bus_add_match(a, NULL, match, NULL, NULL);
+                r = sd_bus_add_match(a, NULL, match, proxy_match, p);
                 if (r < 0)
                         return synthetic_reply_method_errno(m, r, NULL);
 
index b8cedf5ce5c03c40a6cf7001333c74f46f9e912f..da3834f8b05a707f9359cdd25999c5a25c7b1e6f 100644 (file)
@@ -23,5 +23,6 @@
 
 #include "sd-bus.h"
 #include "bus-xml-policy.h"
+#include "proxy.h"
 
-int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names);
+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);
index 189ee969c762ba1de609af0945d88afcfc0ec568..c37b09b9c0ad33ea5b83919365fa5e8207684c1e 100644 (file)
@@ -144,9 +144,17 @@ static int proxy_create_local(Proxy *p, int in_fd, int out_fd, bool negotiate_fd
         return 0;
 }
 
+static int proxy_match_synthetic(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        Proxy *p = userdata;
+
+        p->synthetic_matched = true;
+        return 0; /* make sure to continue processing it in further handlers */
+}
+
 /*
- * dbus-1 clients receive NameOwnerChanged and directed signals without
- * subscribing to them; install the matches to receive them on kdbus.
+ * We always need NameOwnerChanged so we can synthesize NameLost and
+ * NameAcquired. Furthermore, dbus-1 always passes unicast-signals through, so
+ * subscribe unconditionally.
  */
 static int proxy_prepare_matches(Proxy *p) {
         _cleanup_free_ char *match = NULL;
@@ -172,7 +180,7 @@ static int proxy_prepare_matches(Proxy *p) {
         if (!match)
                 return log_oom();
 
-        r = sd_bus_add_match(p->destination_bus, NULL, match, NULL, NULL);
+        r = sd_bus_add_match(p->destination_bus, NULL, match, proxy_match_synthetic, p);
         if (r < 0)
                 return log_error_errno(r, "Failed to add match for NameLost: %m");
 
@@ -189,7 +197,7 @@ static int proxy_prepare_matches(Proxy *p) {
         if (!match)
                 return log_oom();
 
-        r = sd_bus_add_match(p->destination_bus, NULL, match, NULL, NULL);
+        r = sd_bus_add_match(p->destination_bus, NULL, match, proxy_match_synthetic, p);
         if (r < 0)
                 return log_error_errno(r, "Failed to add match for NameAcquired: %m");
 
@@ -202,7 +210,7 @@ static int proxy_prepare_matches(Proxy *p) {
         if (!match)
                 return log_oom();
 
-        r = sd_bus_add_match(p->destination_bus, NULL, match, NULL, NULL);
+        r = sd_bus_add_match(p->destination_bus, NULL, match, proxy_match_synthetic, p);
         if (r < 0)
                 log_error_errno(r, "Failed to add match for directed signals: %m");
                 /* FIXME: temporarily ignore error to support older kdbus versions */
@@ -679,11 +687,28 @@ static int patch_sender(sd_bus *a, sd_bus_message *m) {
 
 static int proxy_process_destination_to_local(Proxy *p) {
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        bool matched, matched_synthetic;
         int r;
 
         assert(p);
 
+        /*
+         * Usually, we would just take any message that the bus passes to us
+         * and forward it to the local connection. However, there are actually
+         * applications that fail if they receive broadcasts that they didn't
+         * subscribe to. Therefore, we actually emulate a real broadcast
+         * matching here, and discard any broadcasts that weren't matched. Our
+         * match-handlers remembers whether a message was matched by any rule,
+         * by marking it in @p->message_matched.
+         */
+
         r = sd_bus_process(p->destination_bus, &m);
+
+        matched = p->message_matched;
+        matched_synthetic = p->synthetic_matched;
+        p->message_matched = false;
+        p->synthetic_matched = false;
+
         if (r == -ECONNRESET || r == -ENOTCONN) /* Treat 'connection reset by peer' as clean exit condition */
                 return r;
         if (r < 0) {
@@ -699,12 +724,21 @@ static int proxy_process_destination_to_local(Proxy *p) {
         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected"))
                 return -ECONNRESET;
 
-        r = synthesize_name_acquired(p->destination_bus, p->local_bus, m);
+        r = synthesize_name_acquired(p, p->destination_bus, p->local_bus, m);
         if (r == -ECONNRESET || r == -ENOTCONN)
                 return r;
         if (r < 0)
                 return log_error_errno(r, "Failed to synthesize message: %m");
 
+        /* 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 " 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;
+        }
+
         patch_sender(p->destination_bus, m);
 
         if (p->policy) {
@@ -788,7 +822,7 @@ static int proxy_process_local_to_destination(Proxy *p) {
         if (r > 0)
                 return 1;
 
-        r = bus_proxy_process_driver(p->destination_bus, p->local_bus, m, p->policy, &p->local_creds, p->owned_names);
+        r = bus_proxy_process_driver(p, p->destination_bus, p->local_bus, m, p->policy, &p->local_creds, p->owned_names);
         if (r == -ECONNRESET || r == -ENOTCONN)
                 return r;
         if (r < 0)
@@ -834,6 +868,13 @@ static int proxy_process_local_to_destination(Proxy *p) {
         return 1;
 }
 
+int proxy_match(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        Proxy *p = userdata;
+
+        p->message_matched = true;
+        return 0; /* make sure to continue processing it in further handlers */
+}
+
 int proxy_run(Proxy *p) {
         int r;
 
index ff278a24652b5e0723aa723f68a08ddc223621e3..ccb951c1092cc00e8b1d323721bcbd9e73c8a9fe 100644 (file)
@@ -39,6 +39,8 @@ struct Proxy {
 
         bool got_hello : 1;
         bool queue_overflow : 1;
+        bool message_matched : 1;
+        bool synthetic_matched : 1;
 };
 
 int proxy_new(Proxy **out, int in_fd, int out_fd, const char *dest);
@@ -46,6 +48,7 @@ Proxy *proxy_free(Proxy *p);
 
 int proxy_set_policy(Proxy *p, SharedPolicy *policy, char **configuration);
 int proxy_hello_policy(Proxy *p, uid_t original_uid);
+int proxy_match(sd_bus_message *m, void *userdata, sd_bus_error *error);
 int proxy_run(Proxy *p);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Proxy*, proxy_free);
index 67bcc7a242e5dc165608a586ee7ee13ffd592c4c..3ecedfd575322798803cb3fd47d8b85c8a20a277 100644 (file)
@@ -28,6 +28,7 @@
 #include "bus-internal.h"
 #include "bus-message.h"
 #include "bus-util.h"
+#include "bus-match.h"
 #include "synthesize.h"
 
 int synthetic_driver_send(sd_bus *b, sd_bus_message *m) {
@@ -152,11 +153,12 @@ int synthetic_reply_method_return_strv(sd_bus_message *call, char **l) {
         return synthetic_driver_send(call->bus, m);
 }
 
-int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) {
+int synthesize_name_acquired(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m) {
         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
         const char *name, *old_owner, *new_owner;
         int r;
 
+        assert(p);
         assert(a);
         assert(b);
         assert(m);
@@ -216,5 +218,18 @@ int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) {
         if (r < 0)
                 return r;
 
-        return sd_bus_send(b, n, NULL);
+        /*
+         * 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;
+
+        return r;
 }
index e850350bc5ffc285f74b800b9cf9d4853838a769..b596daddf21ea717ade4f4ced0b18ca79cc7d389 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "sd-bus.h"
+#include "proxy.h"
 
 int synthetic_driver_send(sd_bus *b, sd_bus_message *m);
 
@@ -33,4 +34,4 @@ int synthetic_reply_method_errorf(sd_bus_message *call, const char *name, const
 int synthetic_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *p);
 int synthetic_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4);
 
-int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m);
+int synthesize_name_acquired(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m);
index 46a444340a22a6714f46bbf45a9c2f8689de4f2e..b8d1d2ccaf8e5f05dcc76fc4a21c1c94c18f9566 100644 (file)
@@ -197,19 +197,19 @@ int main(int argc, char *argv[]) {
                         if (arg_machine) {
                                 char *m;
                                 const char *cgroup;
-                                _cleanup_free_ char *scope = NULL;
+                                _cleanup_free_ char *unit = NULL;
                                 _cleanup_free_ char *path = NULL;
                                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
                                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
 
                                 m = strjoina("/run/systemd/machines/", arg_machine);
-                                r = parse_env_file(m, NEWLINE, "SCOPE", &scope, NULL);
+                                r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
                                 if (r < 0) {
                                         log_error_errno(r, "Failed to get machine path: %m");
                                         goto finish;
                                 }
 
-                                path = unit_dbus_path_from_name(scope);
+                                path = unit_dbus_path_from_name(unit);
                                 if (!path) {
                                         log_oom();
                                         goto finish;
@@ -219,7 +219,7 @@ int main(int argc, char *argv[]) {
                                                 bus,
                                                 "org.freedesktop.systemd1",
                                                 path,
-                                                "org.freedesktop.systemd1.Scope",
+                                                endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
                                                 "ControlGroup",
                                                 &error,
                                                 &reply,
index d630e3588285872e2b7b1d12de6923bd280ae1e9..f953c9e62413ce3c9c0cc0fbbfae345b8fe07e49 100644 (file)
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <alloca.h>
 #include <getopt.h>
+#include <signal.h>
 
 #include "path-util.h"
 #include "terminal-util.h"
diff --git a/src/console/Makefile b/src/console/Makefile
deleted file mode 120000 (symlink)
index d0b0e8e..0000000
+++ /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 (file)
index 569c011..0000000
+++ /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 (file)
index 20424eb..0000000
+++ /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 (file)
index 264a4d0..0000000
+++ /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 (file)
index 03447d1..0000000
+++ /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 (file)
index 5e9e5c7..0000000
+++ /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 (file)
index 9f69e89..0000000
+++ /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 (file)
index f85c1a0..0000000
+++ /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);
index d847dc1629d7f5f8f001af1addea299ac9512345..4af381b4b62510be609754378f8187156a16d834 100644 (file)
@@ -471,13 +471,20 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
         return r;
 }
 
+static int automount_start_expire(Automount *a);
+
 int automount_update_mount(Automount *a, MountState old_state, MountState state) {
+        int r;
+
         assert(a);
 
         switch (state) {
         case MOUNT_MOUNTED:
         case MOUNT_REMOUNTING:
                 automount_send_ready(a, a->tokens, 0);
+                r = automount_start_expire(a);
+                if (r < 0)
+                        log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
                 break;
          case MOUNT_DEAD:
          case MOUNT_UNMOUNTING:
@@ -490,6 +497,7 @@ int automount_update_mount(Automount *a, MountState old_state, MountState state)
          case MOUNT_FAILED:
                 if (old_state != state)
                         automount_send_ready(a, a->tokens, -ENODEV);
+                (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
                 break;
         default:
                 break;
@@ -633,8 +641,6 @@ static void *expire_thread(void *p) {
         return NULL;
 }
 
-static int automount_start_expire(Automount *a);
-
 static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) {
         Automount *a = AUTOMOUNT(userdata);
         _cleanup_(expire_data_freep) struct expire_data *data = NULL;
@@ -672,7 +678,10 @@ static int automount_start_expire(Automount *a) {
 
         assert(a);
 
-        timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/10, USEC_PER_SEC);
+        if (a->timeout_idle_usec == 0)
+                return 0;
+
+        timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/3, USEC_PER_SEC);
 
         if (a->expire_event_source) {
                 r = sd_event_source_set_time(a->expire_event_source, timeout);
@@ -730,10 +739,6 @@ static void automount_enter_runnning(Automount *a) {
                 }
         }
 
-        r = automount_start_expire(a);
-        if (r < 0)
-                log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
-
         automount_set_state(a, AUTOMOUNT_RUNNING);
         return;
 
@@ -904,6 +909,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         union autofs_v5_packet_union packet;
         Automount *a = AUTOMOUNT(userdata);
+        struct stat st;
         int r;
 
         assert(a);
@@ -963,6 +969,19 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
                         log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m");
                         goto fail;
                 }
+
+                /* Before we do anything, let's see if somebody is playing games with us? */
+                if (lstat(a->where, &st) < 0) {
+                        log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m");
+                        goto fail;
+                }
+
+                if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) {
+                        log_unit_info(UNIT(a), "Automount point already unmounted?");
+                        automount_send_ready(a, a->expire_tokens, 0);
+                        break;
+                }
+
                 r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
                 if (r < 0) {
                         log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r));
@@ -1066,7 +1085,6 @@ const UnitVTable automount_vtable = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Set up automount %s.",
                         [JOB_FAILED]     = "Failed to set up automount %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
                 },
                 .finished_stop_job = {
                         [JOB_DONE]       = "Unset automount %s.",
index 2085721546fa15c0770ac6475d59fefa22fb20c6..9530a8731178f8de72248d9b1e2e42a00f89a254 100644 (file)
@@ -1065,13 +1065,10 @@ const UnitVTable busname_vtable = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Listening on %s.",
                         [JOB_FAILED]     = "Failed to listen on %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
-                        [JOB_TIMEOUT]    = "Timed out starting %s.",
                 },
                 .finished_stop_job = {
                         [JOB_DONE]       = "Closed %s.",
                         [JOB_FAILED]     = "Failed stopping %s.",
-                        [JOB_TIMEOUT]    = "Timed out stopping %s.",
                 },
         },
 };
index c92db51330c5965c10ec111832ef3c63b61584fb..21721dc240731e11b7e4f3eeaef64d9d538629b9 100644 (file)
@@ -1446,7 +1446,7 @@ static int exec_child(
                  * shouldn't trip up over that. */
 
                 sprintf(t, "%i", context->oom_score_adjust);
-                r = write_string_file("/proc/self/oom_score_adj", t);
+                r = write_string_file("/proc/self/oom_score_adj", t, 0);
                 if (r == -EPERM || r == -EACCES) {
                         log_open();
                         log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m");
index 1448e5b69afcba68e069d61d9c550156ef85a51c..15f5cc0cc9327af6ad92be221735cdbc162a5f89 100644 (file)
@@ -495,10 +495,48 @@ static void job_change_type(Job *j, JobType newtype) {
         j->type = newtype;
 }
 
+static int job_perform_on_unit(Job **j) {
+        /* While we execute this operation the job might go away (for
+         * example: because it finishes immediately or is replaced by a new,
+         * conflicting job.) To make sure we don't access a freed job later on
+         * we store the id here, so that we can verify the job is still
+         * valid. */
+        Manager *m  = (*j)->manager;
+        Unit *u     = (*j)->unit;
+        JobType t   = (*j)->type;
+        uint32_t id = (*j)->id;
+        int r;
+
+        switch (t) {
+                case JOB_START:
+                        r = unit_start(u);
+                        break;
+
+                case JOB_RESTART:
+                        t = JOB_STOP;
+                case JOB_STOP:
+                        r = unit_stop(u);
+                        break;
+
+                case JOB_RELOAD:
+                        r = unit_reload(u);
+                        break;
+
+                default:
+                        assert_not_reached("Invalid job type");
+        }
+
+        /* Log if the job still exists and the start/stop/reload function
+         * actually did something. */
+        *j = manager_get_job(m, id);
+        if (*j && r > 0)
+                unit_status_emit_starting_stopping_reloading(u, t);
+
+        return r;
+}
+
 int job_run_and_invalidate(Job *j) {
         int r;
-        uint32_t id;
-        Manager *m = j->manager;
 
         assert(j);
         assert(j->installed);
@@ -517,23 +555,9 @@ int job_run_and_invalidate(Job *j) {
         job_set_state(j, JOB_RUNNING);
         job_add_to_dbus_queue(j);
 
-        /* While we execute this operation the job might go away (for
-         * example: because it is replaced by a new, conflicting
-         * job.) To make sure we don't access a freed job later on we
-         * store the id here, so that we can verify the job is still
-         * valid. */
-        id = j->id;
 
         switch (j->type) {
 
-                case JOB_START:
-                        r = unit_start(j->unit);
-
-                        /* If this unit cannot be started, then simply wait */
-                        if (r == -EBADR)
-                                r = 0;
-                        break;
-
                 case JOB_VERIFY_ACTIVE: {
                         UnitActiveState t = unit_active_state(j->unit);
                         if (UNIT_IS_ACTIVE_OR_RELOADING(t))
@@ -545,17 +569,19 @@ int job_run_and_invalidate(Job *j) {
                         break;
                 }
 
+                case JOB_START:
                 case JOB_STOP:
                 case JOB_RESTART:
-                        r = unit_stop(j->unit);
+                        r = job_perform_on_unit(&j);
 
-                        /* If this unit cannot stopped, then simply wait. */
+                        /* If the unit type does not support starting/stopping,
+                         * then simply wait. */
                         if (r == -EBADR)
                                 r = 0;
                         break;
 
                 case JOB_RELOAD:
-                        r = unit_reload(j->unit);
+                        r = job_perform_on_unit(&j);
                         break;
 
                 case JOB_NOP:
@@ -566,7 +592,6 @@ int job_run_and_invalidate(Job *j) {
                         assert_not_reached("Unknown job type");
         }
 
-        j = manager_get_job(m, id);
         if (j) {
                 if (r == -EALREADY)
                         r = job_finish_and_invalidate(j, JOB_DONE, true);
@@ -588,161 +613,110 @@ int job_run_and_invalidate(Job *j) {
 }
 
 _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
+        const char *format;
         const UnitStatusMessageFormats *format_table;
+        static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
+                [JOB_DONE]        = "Started %s.",
+                [JOB_TIMEOUT]     = "Timed out starting %s.",
+                [JOB_FAILED]      = "Failed to start %s.",
+                [JOB_DEPENDENCY]  = "Dependency failed for %s.",
+                [JOB_ASSERT]      = "Assertion failed for %s.",
+                [JOB_UNSUPPORTED] = "Starting of %s not supported.",
+        };
+        static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
+                [JOB_DONE]        = "Stopped %s.",
+                [JOB_FAILED]      = "Stopped (with error) %s.",
+                [JOB_TIMEOUT]     = "Timed out stoppping %s.",
+        };
+        static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = {
+                [JOB_DONE]        = "Reloaded %s.",
+                [JOB_FAILED]      = "Reload failed for %s.",
+                [JOB_TIMEOUT]     = "Timed out reloading %s.",
+        };
+        /* When verify-active detects the unit is inactive, report it.
+         * Most likely a DEPEND warning from a requisiting unit will
+         * occur next and it's nice to see what was requisited. */
+        static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = {
+                [JOB_SKIPPED]     = "%s is not active.",
+        };
 
         assert(u);
         assert(t >= 0);
         assert(t < _JOB_TYPE_MAX);
 
-        format_table = &UNIT_VTABLE(u)->status_message_formats;
-        if (!format_table)
-                return NULL;
+        if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) {
+                format_table = &UNIT_VTABLE(u)->status_message_formats;
+                if (format_table) {
+                        format = t == JOB_START ? format_table->finished_start_job[result] :
+                                                  format_table->finished_stop_job[result];
+                        if (format)
+                                return format;
+                }
+        }
 
+        /* Return generic strings */
         if (t == JOB_START)
-                return format_table->finished_start_job[result];
+                return generic_finished_start_job[result];
         else if (t == JOB_STOP || t == JOB_RESTART)
-                return format_table->finished_stop_job[result];
+                return generic_finished_stop_job[result];
+        else if (t == JOB_RELOAD)
+                return generic_finished_reload_job[result];
+        else if (t == JOB_VERIFY_ACTIVE)
+                return generic_finished_verify_active_job[result];
 
         return NULL;
 }
 
-_pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
+static void job_print_status_message(Unit *u, JobType t, JobResult result) {
         const char *format;
+        static const char* const job_result_status_table[_JOB_RESULT_MAX] = {
+                [JOB_DONE]        = ANSI_GREEN_ON            "  OK  " ANSI_HIGHLIGHT_OFF,
+                [JOB_TIMEOUT]     = ANSI_HIGHLIGHT_RED_ON    " TIME " ANSI_HIGHLIGHT_OFF,
+                [JOB_FAILED]      = ANSI_HIGHLIGHT_RED_ON    "FAILED" ANSI_HIGHLIGHT_OFF,
+                [JOB_DEPENDENCY]  = ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF,
+                [JOB_SKIPPED]     = ANSI_HIGHLIGHT_ON        " INFO " ANSI_HIGHLIGHT_OFF,
+                [JOB_ASSERT]      = ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF,
+                [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF,
+        };
 
         assert(u);
         assert(t >= 0);
         assert(t < _JOB_TYPE_MAX);
 
         format = job_get_status_message_format(u, t, result);
-        if (format)
-                return format;
-
-        /* Return generic strings */
-        if (t == JOB_START) {
-                if (result == JOB_DONE)
-                        return "Started %s.";
-                else if (result == JOB_TIMEOUT)
-                        return "Timed out starting %s.";
-                else if (result == JOB_FAILED)
-                        return "Failed to start %s.";
-                else if (result == JOB_DEPENDENCY)
-                        return "Dependency failed for %s.";
-                else if (result == JOB_ASSERT)
-                        return "Assertion failed for %s.";
-                else if (result == JOB_UNSUPPORTED)
-                        return "Starting of %s not supported.";
-        } else if (t == JOB_STOP || t == JOB_RESTART) {
-                if (result == JOB_DONE)
-                        return "Stopped %s.";
-                else if (result == JOB_FAILED)
-                        return "Stopped (with error) %s.";
-                else if (result == JOB_TIMEOUT)
-                        return "Timed out stoppping %s.";
-        } else if (t == JOB_RELOAD) {
-                if (result == JOB_DONE)
-                        return "Reloaded %s.";
-                else if (result == JOB_FAILED)
-                        return "Reload failed for %s.";
-                else if (result == JOB_TIMEOUT)
-                        return "Timed out reloading %s.";
-        }
-
-        return NULL;
-}
+        if (!format)
+                return;
 
-static void job_print_status_message(Unit *u, JobType t, JobResult result) {
-        const char *format;
-
-        assert(u);
-        assert(t >= 0);
-        assert(t < _JOB_TYPE_MAX);
+        if (result != JOB_DONE)
+                manager_flip_auto_status(u->manager, true);
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
+        unit_status_printf(u, job_result_status_table[result], format);
+        REENABLE_WARNING;
 
-        if (t == JOB_START) {
-                format = job_get_status_message_format(u, t, result);
-                if (!format)
-                        return;
-
-                switch (result) {
-
-                case JOB_DONE:
-                        if (u->condition_result)
-                                unit_status_printf(u, ANSI_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                case JOB_TIMEOUT:
-                        manager_flip_auto_status(u->manager, true);
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                case JOB_FAILED: {
-                        _cleanup_free_ char *quoted = NULL;
-
-                        quoted = shell_maybe_quote(u->id);
-
-                        manager_flip_auto_status(u->manager, true);
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
-                        manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
-                        break;
-                }
-
-                case JOB_DEPENDENCY:
-                        manager_flip_auto_status(u->manager, true);
-                        unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                case JOB_ASSERT:
-                        manager_flip_auto_status(u->manager, true);
-                        unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                case JOB_UNSUPPORTED:
-                        manager_flip_auto_status(u->manager, true);
-                        unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                default:
-                        ;
-                }
-
-        } else if (t == JOB_STOP || t == JOB_RESTART) {
-
-                format = job_get_status_message_format(u, t, result);
-                if (!format)
-                        return;
-
-                switch (result) {
-
-                case JOB_TIMEOUT:
-                        manager_flip_auto_status(u->manager, true);
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                case JOB_DONE:
-                case JOB_FAILED:
-                        unit_status_printf(u, ANSI_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, format);
-                        break;
-
-                default:
-                        ;
-                }
-
-        } else if (t == JOB_VERIFY_ACTIVE) {
+        if (t == JOB_START && result == JOB_FAILED) {
+                _cleanup_free_ char *quoted = shell_maybe_quote(u->id);
 
-                /* When verify-active detects the unit is inactive, report it.
-                 * Most likely a DEPEND warning from a requisiting unit will
-                 * occur next and it's nice to see what was requisited. */
-                if (result == JOB_SKIPPED)
-                        unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
+                manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
+                                      "See 'systemctl status %s' for details.", strna(quoted));
         }
-
-        REENABLE_WARNING;
 }
 
 static void job_log_status_message(Unit *u, JobType t, JobResult result) {
         const char *format;
         char buf[LINE_MAX];
+        sd_id128_t mid;
+        static const int job_result_log_level[_JOB_RESULT_MAX] = {
+                [JOB_DONE]        = LOG_INFO,
+                [JOB_CANCELED]    = LOG_INFO,
+                [JOB_TIMEOUT]     = LOG_ERR,
+                [JOB_FAILED]      = LOG_ERR,
+                [JOB_DEPENDENCY]  = LOG_WARNING,
+                [JOB_SKIPPED]     = LOG_NOTICE,
+                [JOB_INVALID]     = LOG_INFO,
+                [JOB_ASSERT]      = LOG_WARNING,
+                [JOB_UNSUPPORTED] = LOG_WARNING,
+        };
 
         assert(u);
         assert(t >= 0);
@@ -754,7 +728,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
         if (log_on_console())
                 return;
 
-        format = job_get_status_message_format_try_harder(u, t, result);
+        format = job_get_status_message_format(u, t, result);
         if (!format)
                 return;
 
@@ -762,32 +736,40 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
         snprintf(buf, sizeof(buf), format, unit_description(u));
         REENABLE_WARNING;
 
-        if (t == JOB_START) {
-                sd_id128_t mid;
-
+        if (t == JOB_START)
                 mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
-                log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
-                           LOG_MESSAGE_ID(mid),
+        else if (t == JOB_STOP || t == JOB_RESTART)
+                mid = SD_MESSAGE_UNIT_STOPPED;
+        else if (t == JOB_RELOAD)
+                mid = SD_MESSAGE_UNIT_RELOADED;
+        else {
+                log_struct(job_result_log_level[result],
                            LOG_UNIT_ID(u),
                            LOG_MESSAGE("%s", buf),
                            "RESULT=%s", job_result_to_string(result),
                            NULL);
+                return;
+        }
 
-        } else if (t == JOB_STOP)
-                log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
-                           LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
-                           LOG_UNIT_ID(u),
-                           LOG_MESSAGE("%s", buf),
-                           "RESULT=%s", job_result_to_string(result),
-                           NULL);
+        log_struct(job_result_log_level[result],
+                   LOG_MESSAGE_ID(mid),
+                   LOG_UNIT_ID(u),
+                   LOG_MESSAGE("%s", buf),
+                   "RESULT=%s", job_result_to_string(result),
+                   NULL);
+}
 
-        else if (t == JOB_RELOAD)
-                log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
-                           LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
-                           LOG_UNIT_ID(u),
-                           LOG_MESSAGE("%s", buf),
-                           "RESULT=%s", job_result_to_string(result),
-                           NULL);
+static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
+
+        /* No message if the job did not actually do anything due to failed condition. */
+        if (t == JOB_START && result == JOB_DONE && !u->condition_result)
+                return;
+
+        job_log_status_message(u, t, result);
+
+        /* Reload status messages have traditionally not been printed to console. */
+        if (t != JOB_RELOAD)
+                job_print_status_message(u, t, result);
 }
 
 static void job_fail_dependencies(Unit *u, UnitDependency d) {
@@ -825,8 +807,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
 
         log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
 
-        job_print_status_message(u, t, result);
-        job_log_status_message(u, t, result);
+        job_emit_status_message(u, t, result);
 
         job_add_to_dbus_queue(j);
 
index b3d22840cf6ffce6832859f5caaa4175fc892d11..8e26362546ba675510336b53743504a2a585c054 100644 (file)
@@ -260,7 +260,7 @@ int machine_id_setup(const char *root) {
          * /run/machine-id as a replacement */
 
         RUN_WITH_UMASK(0022) {
-                r = write_string_file(run_machine_id, id);
+                r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE);
         }
         if (r < 0) {
                 (void) unlink(run_machine_id);
index 523f0ce0206cefb252bf53a91d92197a0c855acd..6ae8b515445d299f366731ffa47502c2f6247fbd 100644 (file)
@@ -685,6 +685,26 @@ static int parse_config_file(void) {
         return 0;
 }
 
+static void manager_set_defaults(Manager *m) {
+
+        assert(m);
+
+        m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec;
+        m->default_std_output = arg_default_std_output;
+        m->default_std_error = arg_default_std_error;
+        m->default_timeout_start_usec = arg_default_timeout_start_usec;
+        m->default_timeout_stop_usec = arg_default_timeout_stop_usec;
+        m->default_restart_usec = arg_default_restart_usec;
+        m->default_start_limit_interval = arg_default_start_limit_interval;
+        m->default_start_limit_burst = arg_default_start_limit_burst;
+        m->default_cpu_accounting = arg_default_cpu_accounting;
+        m->default_blockio_accounting = arg_default_blockio_accounting;
+        m->default_memory_accounting = arg_default_memory_accounting;
+
+        manager_set_default_rlimits(m, arg_default_rlimit);
+        manager_environment_add(m, NULL, arg_default_environment);
+}
+
 static int parse_argv(int argc, char *argv[]) {
 
         enum {
@@ -1203,7 +1223,7 @@ static int write_container_id(void) {
         if (isempty(c))
                 return 0;
 
-        return write_string_file("/run/systemd/container", c);
+        return write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
 }
 
 int main(int argc, char *argv[]) {
@@ -1630,28 +1650,15 @@ int main(int argc, char *argv[]) {
         }
 
         m->confirm_spawn = arg_confirm_spawn;
-        m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec;
-        m->default_std_output = arg_default_std_output;
-        m->default_std_error = arg_default_std_error;
-        m->default_restart_usec = arg_default_restart_usec;
-        m->default_timeout_start_usec = arg_default_timeout_start_usec;
-        m->default_timeout_stop_usec = arg_default_timeout_stop_usec;
-        m->default_start_limit_interval = arg_default_start_limit_interval;
-        m->default_start_limit_burst = arg_default_start_limit_burst;
-        m->default_cpu_accounting = arg_default_cpu_accounting;
-        m->default_blockio_accounting = arg_default_blockio_accounting;
-        m->default_memory_accounting = arg_default_memory_accounting;
         m->runtime_watchdog = arg_runtime_watchdog;
         m->shutdown_watchdog = arg_shutdown_watchdog;
-
         m->userspace_timestamp = userspace_timestamp;
         m->kernel_timestamp = kernel_timestamp;
         m->initrd_timestamp = initrd_timestamp;
         m->security_start_timestamp = security_start_timestamp;
         m->security_finish_timestamp = security_finish_timestamp;
 
-        manager_set_default_rlimits(m, arg_default_rlimit);
-        manager_environment_add(m, NULL, arg_default_environment);
+        manager_set_defaults(m);
         manager_set_show_status(m, arg_show_status);
         manager_set_first_boot(m, empty_etc);
 
@@ -1763,6 +1770,13 @@ int main(int argc, char *argv[]) {
 
                 case MANAGER_RELOAD:
                         log_info("Reloading.");
+
+                        r = parse_config_file();
+                        if (r < 0)
+                                log_error("Failed to parse config file.");
+
+                        manager_set_defaults(m);
+
                         r = manager_reload(m);
                         if (r < 0)
                                 log_error_errno(r, "Failed to reload: %m");
index 851b41351e666969a28d40590ae3b7bd30df3a42..c0d1cdfbd4d18e4c4c48e55d8dbfba17cc9c8bf2 100644 (file)
@@ -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)
@@ -1025,7 +1019,7 @@ static int mount_reload(Unit *u) {
         assert(m->state == MOUNT_MOUNTED);
 
         mount_enter_remounting(m);
-        return 0;
+        return 1;
 }
 
 static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
@@ -1897,7 +1891,6 @@ const UnitVTable mount_vtable = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Mounted %s.",
                         [JOB_FAILED]     = "Failed to mount %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
                         [JOB_TIMEOUT]    = "Timed out mounting %s.",
                 },
                 .finished_stop_job = {
index 6d26d89e82dc7f84545b7ea41deb1d167a495f06..20995d920c76d6d7d1d94c68b1984b26e1f95936 100644 (file)
@@ -426,7 +426,7 @@ static void path_set_state(Path *p, PathState state) {
                 path_unwatch(p);
 
         if (state != old_state)
-                log_debug("Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
+                log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
 
         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
 }
index e9a9a020de76e0b025c12d6b9ea0f1ed2fa66306..50a90b0bace104a78d24044c9be980907476888c 100644 (file)
@@ -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;
index d72ff54daa37a2b345aec9c915e9c0eab66ced97..b790ec98befa71ca438ed10f99c2a03bad4866ad 100644 (file)
@@ -401,7 +401,6 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
                 r = service_add_fd_store(s, fd);
                 if (r < 0)
                         return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
-
                 if (r > 0) {
                         log_unit_debug(UNIT(s), "Added fd to fd store.");
                         fd = -1;
@@ -576,8 +575,10 @@ static int service_add_extras(Service *s) {
                         return r;
 
                 r = unit_watch_bus_name(UNIT(s), s->bus_name);
+                if (r == -EEXIST)
+                        return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name);
                 if (r < 0)
-                        return r;
+                        return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name);
         }
 
         if (UNIT(s)->default_dependencies) {
@@ -1974,7 +1975,7 @@ static int service_reload(Unit *u) {
         assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED);
 
         service_enter_reload(s);
-        return 0;
+        return 1;
 }
 
 _pure_ static bool service_can_reload(Unit *u) {
@@ -3229,13 +3230,10 @@ const UnitVTable service_vtable = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Started %s.",
                         [JOB_FAILED]     = "Failed to start %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
-                        [JOB_TIMEOUT]    = "Timed out starting %s.",
                 },
                 .finished_stop_job = {
                         [JOB_DONE]       = "Stopped %s.",
                         [JOB_FAILED]     = "Stopped (with error) %s.",
-                        [JOB_TIMEOUT]    = "Timed out stopping %s.",
                 },
         },
 };
index e52bf715154aa17d5baa93aa45a72c82d254f3d1..064eb5d93327b5cda413982c6dfb415a76fd5ff8 100644 (file)
@@ -297,7 +297,6 @@ const UnitVTable slice_vtable = {
         .status_message_formats = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Created slice %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
                 },
                 .finished_stop_job = {
                         [JOB_DONE]       = "Removed slice %s.",
index ddb02a1580c1f13f8660d382f9af261f0a386bd7..cbe7d0b4a9c998b53eba4002aa2069c61078f3f4 100644 (file)
@@ -221,7 +221,7 @@ int mac_smack_setup(bool *loaded_policy) {
         }
 
 #ifdef SMACK_RUN_LABEL
-        r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL);
+        r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
         if (r)
                 log_warning("Failed to set SMACK label \"%s\" on self: %s",
                             SMACK_RUN_LABEL, strerror(-r));
index 693cbc60800b32f6145d2a6fa45cafeaa37efacc..87631f8753ac857db7929f73ef256aa2511d7430 100644 (file)
@@ -2722,7 +2722,6 @@ const UnitVTable socket_vtable = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Listening on %s.",
                         [JOB_FAILED]     = "Failed to listen on %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
                         [JOB_TIMEOUT]    = "Timed out starting %s.",
                 },
                 .finished_stop_job = {
index 193c8c37675811552620bf2f7c6c88c6b44991f4..0bc3827ff061542490cb474f0c38e538a506dbdf 100644 (file)
@@ -1505,7 +1505,6 @@ const UnitVTable swap_vtable = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Activated swap %s.",
                         [JOB_FAILED]     = "Failed to activate swap %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
                         [JOB_TIMEOUT]    = "Timed out activating swap %s.",
                 },
                 .finished_stop_job = {
index 8817ef21c4c61a3daba8d24b717e9a3c19f83406..b492a7c4c7f046c50d76f16ef18258f1eaabee27 100644 (file)
@@ -227,7 +227,6 @@ const UnitVTable target_vtable = {
         .status_message_formats = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Reached target %s.",
-                        [JOB_DEPENDENCY] = "Dependency failed for %s.",
                 },
                 .finished_stop_job = {
                         [JOB_DONE]       = "Stopped target %s.",
index fac017c57d90eb43cbc16a29f08d60739b9d3c9d..dd5e80128512766ce2b6c133bcd1127155e49d08 100644 (file)
@@ -1318,42 +1318,28 @@ static bool unit_assert_test(Unit *u) {
 }
 
 _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
-        const UnitStatusMessageFormats *format_table;
-
-        assert(u);
-        assert(t >= 0);
-        assert(t < _JOB_TYPE_MAX);
-
-        if (t != JOB_START && t != JOB_STOP)
-                return NULL;
-
-        format_table = &UNIT_VTABLE(u)->status_message_formats;
-        if (!format_table)
-                return NULL;
-
-        return format_table->starting_stopping[t == JOB_STOP];
-}
-
-_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
         const char *format;
+        const UnitStatusMessageFormats *format_table;
 
         assert(u);
-        assert(t >= 0);
-        assert(t < _JOB_TYPE_MAX);
+        assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD);
 
-        format = unit_get_status_message_format(u, t);
-        if (format)
-                return format;
+        if (t != JOB_RELOAD) {
+                format_table = &UNIT_VTABLE(u)->status_message_formats;
+                if (format_table) {
+                        format = format_table->starting_stopping[t == JOB_STOP];
+                        if (format)
+                                return format;
+                }
+        }
 
         /* Return generic strings */
         if (t == JOB_START)
                 return "Starting %s.";
         else if (t == JOB_STOP)
                 return "Stopping %s.";
-        else if (t == JOB_RELOAD)
+        else
                 return "Reloading %s.";
-
-        return NULL;
 }
 
 static void unit_status_print_starting_stopping(Unit *u, JobType t) {
@@ -1361,12 +1347,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
 
         assert(u);
 
-        /* We only print status messages for selected units on
-         * selected operations. */
-
         format = unit_get_status_message_format(u, t);
-        if (!format)
-                return;
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
         unit_status_printf(u, "", format);
@@ -1388,9 +1369,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 
         /* We log status messages for all units and all operations. */
 
-        format = unit_get_status_message_format_try_harder(u, t);
-        if (!format)
-                return;
+        format = unit_get_status_message_format(u, t);
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
         snprintf(buf, sizeof(buf), format, unit_description(u));
@@ -1413,6 +1392,15 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
                    NULL);
 }
 
+void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
+
+        unit_status_log_starting_stopping_reloading(u, t);
+
+        /* Reload status messages have traditionally not been printed to console. */
+        if (t != JOB_RELOAD)
+                unit_status_print_starting_stopping(u, t);
+}
+
 /* Errors:
  *         -EBADR:     This unit type does not support starting.
  *         -EALREADY:  Unit is already started.
@@ -1423,7 +1411,6 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
 int unit_start(Unit *u) {
         UnitActiveState state;
         Unit *following;
-        int r;
 
         assert(u);
 
@@ -1477,14 +1464,7 @@ int unit_start(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        r = UNIT_VTABLE(u)->start(u);
-        if (r <= 0)
-                return r;
-
-        /* Log if the start function actually did something */
-        unit_status_log_starting_stopping_reloading(u, JOB_START);
-        unit_status_print_starting_stopping(u, JOB_START);
-        return r;
+        return UNIT_VTABLE(u)->start(u);
 }
 
 bool unit_can_start(Unit *u) {
@@ -1508,7 +1488,6 @@ bool unit_can_isolate(Unit *u) {
 int unit_stop(Unit *u) {
         UnitActiveState state;
         Unit *following;
-        int r;
 
         assert(u);
 
@@ -1527,13 +1506,7 @@ int unit_stop(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        r = UNIT_VTABLE(u)->stop(u);
-        if (r <= 0)
-                return r;
-
-        unit_status_log_starting_stopping_reloading(u, JOB_STOP);
-        unit_status_print_starting_stopping(u, JOB_STOP);
-        return r;
+        return UNIT_VTABLE(u)->stop(u);
 }
 
 /* Errors:
@@ -1544,7 +1517,6 @@ int unit_stop(Unit *u) {
 int unit_reload(Unit *u) {
         UnitActiveState state;
         Unit *following;
-        int r;
 
         assert(u);
 
@@ -1571,12 +1543,7 @@ int unit_reload(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        r = UNIT_VTABLE(u)->reload(u);
-        if (r <= 0)
-                return r;
-
-        unit_status_log_starting_stopping_reloading(u, JOB_RELOAD);
-        return r;
+        return UNIT_VTABLE(u)->reload(u);
 }
 
 bool unit_can_reload(Unit *u) {
index 9491ef64f9c36367c303d26d3b0c08eebf585a3c..e60168267f0eab84f51f537fb4496c8e13ca3f85 100644 (file)
@@ -544,6 +544,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants);
 int unit_coldplug(Unit *u);
 
 void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
+void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t);
 
 bool unit_need_daemon_reload(Unit *u);
 
index cda96d484ae4311dada2ff161debb232672fc9e8..3805b2943734b6b2ec002575718ab8affce7caea 100644 (file)
@@ -415,7 +415,7 @@ static int process_hostname(void) {
                 return 0;
 
         mkdir_parents(etc_hostname, 0755);
-        r = write_string_file(etc_hostname, arg_hostname);
+        r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE);
         if (r < 0)
                 return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
 
@@ -436,7 +436,7 @@ static int process_machine_id(void) {
                 return 0;
 
         mkdir_parents(etc_machine_id, 0755);
-        r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id));
+        r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE);
         if (r < 0)
                 return log_error_errno(r, "Failed to write machine id: %m");
 
index b46e160888523c43e113768de355fd9652622691..da5f3b647ae2a8a1375401da425271f433b6dd72 100644 (file)
@@ -183,7 +183,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
         r = write_string_file(p,
                         "# Automatically generated by systemd-gpt-auto-generator\n\n"
                         "[Unit]\n"
-                        "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
+                        "JobTimeoutSec=0\n",
+                        WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */
         if (r < 0)
                 return log_error_errno(r, "Failed to write device drop-in: %m");
 
index 43aac616b6261dc1189d037ec9da6ca84a867ee2..1f3b1699056dcb52014c11ad7fb325ff1cbf1fee 100644 (file)
@@ -65,7 +65,7 @@ int main(int argc, char *argv[]) {
                 return EXIT_FAILURE;
         }
 
-        r = write_string_file("/sys/power/resume", major_minor);
+        r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE);
         if (r < 0) {
                 log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor);
                 return EXIT_FAILURE;
index 78e3184c42b192e5728a7c9e66a89627d32807d1..67ca1ce8e407df658a28ab9a0a8d7194ed1014c7 100644 (file)
@@ -793,7 +793,7 @@ static void dkr_pull_job_on_finished_v2(PullJob *j) {
 
         } else if (i->tags_job == j) {
                 const char *url;
-                _cleanup_free_ const char *buf;
+                _cleanup_free_ char *buf;
                 _cleanup_json_variant_unref_ JsonVariant *doc = NULL;
                 JsonVariant *e = NULL;
 
index d9450ae8cdbe7fb2714271b6d47013d62b9ed6e1..9a09f401e09876b95db47a50d10a06ed7412c934 100644 (file)
@@ -132,7 +132,7 @@ static int request_meta_ensure_tmp(RequestMeta *m) {
                 if (fd < 0)
                         return fd;
 
-                m->tmp = fdopen(fd, "rw");
+                m->tmp = fdopen(fd, "w+");
                 if (!m->tmp) {
                         safe_close(fd);
                         return -errno;
index be6a5522faba7a959e4c9212f5084f9eb968d306..f7815b27961c251cff428ee5e3d9cd391b4a50d8 100644 (file)
@@ -656,13 +656,16 @@ static int journal_file_setup_field_hash_table(JournalFile *f) {
         return 0;
 }
 
-static int journal_file_map_data_hash_table(JournalFile *f) {
+int journal_file_map_data_hash_table(JournalFile *f) {
         uint64_t s, p;
         void *t;
         int r;
 
         assert(f);
 
+        if (f->data_hash_table)
+                return 0;
+
         p = le64toh(f->header->data_hash_table_offset);
         s = le64toh(f->header->data_hash_table_size);
 
@@ -678,13 +681,16 @@ static int journal_file_map_data_hash_table(JournalFile *f) {
         return 0;
 }
 
-static int journal_file_map_field_hash_table(JournalFile *f) {
+int journal_file_map_field_hash_table(JournalFile *f) {
         uint64_t s, p;
         void *t;
         int r;
 
         assert(f);
 
+        if (f->field_hash_table)
+                return 0;
+
         p = le64toh(f->header->field_hash_table_offset);
         s = le64toh(f->header->field_hash_table_size);
 
@@ -803,10 +809,18 @@ int journal_file_find_field_object_with_hash(
         assert(f);
         assert(field && size > 0);
 
+        /* If the field hash table is empty, we can't find anything */
+        if (le64toh(f->header->field_hash_table_size) <= 0)
+                return 0;
+
+        /* Map the field hash table, if it isn't mapped yet. */
+        r = journal_file_map_field_hash_table(f);
+        if (r < 0)
+                return r;
+
         osize = offsetof(Object, field.payload) + size;
 
         m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
-
         if (m <= 0)
                 return -EBADMSG;
 
@@ -866,6 +880,15 @@ int journal_file_find_data_object_with_hash(
         assert(f);
         assert(data || size == 0);
 
+        /* If there's no data hash table, then there's no entry. */
+        if (le64toh(f->header->data_hash_table_size) <= 0)
+                return 0;
+
+        /* Map the data hash table, if it isn't mapped yet. */
+        r = journal_file_map_data_hash_table(f);
+        if (r < 0)
+                return r;
+
         osize = offsetof(Object, data.payload) + size;
 
         m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
@@ -2731,14 +2754,6 @@ int journal_file_open(
 #endif
         }
 
-        r = journal_file_map_field_hash_table(f);
-        if (r < 0)
-                goto fail;
-
-        r = journal_file_map_data_hash_table(f);
-        if (r < 0)
-                goto fail;
-
         if (mmap_cache_got_sigbus(f->mmap, f->fd)) {
                 r = -EIO;
                 goto fail;
index 403c8f760c8541a5f0b6478d38a08f44c9a2d4ad..e92b75eabe8ff56bc7b30ceadb9f08b19e2ee50e 100644 (file)
@@ -229,3 +229,6 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *
 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to);
 
 bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec);
+
+int journal_file_map_data_hash_table(JournalFile *f);
+int journal_file_map_field_hash_table(JournalFile *f);
index 81a577ea276f2a59bac2265f52536faf3ea7969c..17499bbc303a76d8027f7bd3798d764316fceb38 100644 (file)
@@ -72,7 +72,7 @@ static void patch_realtime(
                 const struct stat *st,
                 unsigned long long *realtime) {
 
-        _cleanup_free_ const char *path = NULL;
+        _cleanup_free_ char *path = NULL;
         usec_t x, crtime = 0;
 
         /* The timestamp was determined by the file name, but let's
index ce734d8df7291022178f4a8535367b0eaaaf40a6..eaf006db7a7df6c8782c351f4c9bf140df65495e 100644 (file)
@@ -69,6 +69,16 @@ static void draw_progress(uint64_t p, usec_t *last_usec) {
         fflush(stdout);
 }
 
+static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
+
+        /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
+
+        if (p >= m || m == 0)
+                return scale;
+
+        return scale * p / m;
+}
+
 static void flush_progress(void) {
         unsigned n, i;
 
@@ -113,8 +123,10 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
          * other objects. */
 
         if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
-            o->object.type != OBJECT_DATA)
+            o->object.type != OBJECT_DATA) {
+                error(offset, "Found compressed object that isn't of type DATA, which is not allowed.");
                 return -EBADMSG;
+        }
 
         switch (o->object.type) {
 
@@ -123,15 +135,15 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 int compression, r;
 
                 if (le64toh(o->data.entry_offset) == 0)
-                        warning(offset, "unused data (entry_offset==0)");
+                        warning(offset, "Unused data (entry_offset==0)");
 
                 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
-                        error(offset, "bad n_entries: %"PRIu64, o->data.n_entries);
+                        error(offset, "Bad n_entries: %"PRIu64, o->data.n_entries);
                         return -EBADMSG;
                 }
 
                 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
-                        error(offset, "bad object size (<= %zu): %"PRIu64,
+                        error(offset, "Bad object size (<= %zu): %"PRIu64,
                               offsetof(DataObject, payload),
                               le64toh(o->object.size));
                         return -EBADMSG;
@@ -159,7 +171,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
 
                 if (h1 != h2) {
-                        error(offset, "invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
+                        error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
                         return -EBADMSG;
                 }
 
@@ -167,7 +179,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                     !VALID64(o->data.next_field_offset) ||
                     !VALID64(o->data.entry_offset) ||
                     !VALID64(o->data.entry_array_offset)) {
-                        error(offset, "invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
+                        error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
                               o->data.next_hash_offset,
                               o->data.next_field_offset,
                               o->data.entry_offset,
@@ -181,7 +193,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
         case OBJECT_FIELD:
                 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
                         error(offset,
-                              "bad field size (<= %zu): %"PRIu64,
+                              "Bad field size (<= %zu): %"PRIu64,
                               offsetof(FieldObject, payload),
                               le64toh(o->object.size));
                         return -EBADMSG;
@@ -190,7 +202,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 if (!VALID64(o->field.next_hash_offset) ||
                     !VALID64(o->field.head_data_offset)) {
                         error(offset,
-                              "invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
+                              "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
                               o->field.next_hash_offset,
                               o->field.head_data_offset);
                         return -EBADMSG;
@@ -200,7 +212,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
         case OBJECT_ENTRY:
                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
                         error(offset,
-                              "bad entry size (<= %zu): %"PRIu64,
+                              "Bad entry size (<= %zu): %"PRIu64,
                               offsetof(EntryObject, items),
                               le64toh(o->object.size));
                         return -EBADMSG;
@@ -208,28 +220,28 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
 
                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
                         error(offset,
-                              "invalid number items in entry: %"PRIu64,
+                              "Invalid number items in entry: %"PRIu64,
                               (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
                         return -EBADMSG;
                 }
 
                 if (le64toh(o->entry.seqnum) <= 0) {
                         error(offset,
-                              "invalid entry seqnum: %"PRIx64,
+                              "Invalid entry seqnum: %"PRIx64,
                               le64toh(o->entry.seqnum));
                         return -EBADMSG;
                 }
 
                 if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
                         error(offset,
-                              "invalid entry realtime timestamp: %"PRIu64,
+                              "Invalid entry realtime timestamp: %"PRIu64,
                               le64toh(o->entry.realtime));
                         return -EBADMSG;
                 }
 
                 if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
                         error(offset,
-                              "invalid entry monotonic timestamp: %"PRIu64,
+                              "Invalid entry monotonic timestamp: %"PRIu64,
                               le64toh(o->entry.monotonic));
                         return -EBADMSG;
                 }
@@ -238,7 +250,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         if (o->entry.items[i].object_offset == 0 ||
                             !VALID64(o->entry.items[i].object_offset)) {
                                 error(offset,
-                                      "invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
+                                      "Invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
                                       i, journal_file_entry_n_items(o),
                                       o->entry.items[i].object_offset);
                                 return -EBADMSG;
@@ -252,7 +264,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
                     (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
                         error(offset,
-                              "invalid %s hash table size: %"PRIu64,
+                              "Invalid %s hash table size: %"PRIu64,
                               o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
                               le64toh(o->object.size));
                         return -EBADMSG;
@@ -262,7 +274,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         if (o->hash_table.items[i].head_hash_offset != 0 &&
                             !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
                                 error(offset,
-                                      "invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
+                                      "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
                                       o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
                                       i, journal_file_hash_table_n_items(o),
                                       le64toh(o->hash_table.items[i].head_hash_offset));
@@ -271,7 +283,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         if (o->hash_table.items[i].tail_hash_offset != 0 &&
                             !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
                                 error(offset,
-                                      "invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
+                                      "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
                                       o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
                                       i, journal_file_hash_table_n_items(o),
                                       le64toh(o->hash_table.items[i].tail_hash_offset));
@@ -281,7 +293,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         if ((o->hash_table.items[i].head_hash_offset != 0) !=
                             (o->hash_table.items[i].tail_hash_offset != 0)) {
                                 error(offset,
-                                      "invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
+                                      "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
                                       o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
                                       i, journal_file_hash_table_n_items(o),
                                       le64toh(o->hash_table.items[i].head_hash_offset),
@@ -296,14 +308,14 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
                     (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
                         error(offset,
-                              "invalid object entry array size: %"PRIu64,
+                              "Invalid object entry array size: %"PRIu64,
                               le64toh(o->object.size));
                         return -EBADMSG;
                 }
 
                 if (!VALID64(o->entry_array.next_entry_array_offset)) {
                         error(offset,
-                              "invalid object entry array next_entry_array_offset: "OFSfmt,
+                              "Invalid object entry array next_entry_array_offset: "OFSfmt,
                               o->entry_array.next_entry_array_offset);
                         return -EBADMSG;
                 }
@@ -312,7 +324,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         if (le64toh(o->entry_array.items[i]) != 0 &&
                             !VALID64(le64toh(o->entry_array.items[i]))) {
                                 error(offset,
-                                      "invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
+                                      "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
                                       i, journal_file_entry_array_n_items(o),
                                       le64toh(o->entry_array.items[i]));
                                 return -EBADMSG;
@@ -323,14 +335,14 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
         case OBJECT_TAG:
                 if (le64toh(o->object.size) != sizeof(TagObject)) {
                         error(offset,
-                              "invalid object tag size: %"PRIu64,
+                              "Invalid object tag size: %"PRIu64,
                               le64toh(o->object.size));
                         return -EBADMSG;
                 }
 
                 if (!VALID_EPOCH(o->tag.epoch)) {
                         error(offset,
-                              "invalid object tag epoch: %"PRIu64,
+                              "Invalid object tag epoch: %"PRIu64,
                               o->tag.epoch);
                         return -EBADMSG;
                 }
@@ -403,8 +415,7 @@ static int entry_points_to_data(
         assert(entry_fd >= 0);
 
         if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
-                error(data_p,
-                      "data object references invalid entry at "OFSfmt, entry_p);
+                error(data_p, "Data object references invalid entry at "OFSfmt, entry_p);
                 return -EBADMSG;
         }
 
@@ -420,8 +431,7 @@ static int entry_points_to_data(
                 }
 
         if (!found) {
-                error(entry_p,
-                      "data object at "OFSfmt" not referenced by linked entry", data_p);
+                error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p);
                 return -EBADMSG;
         }
 
@@ -464,7 +474,7 @@ static int entry_points_to_data(
                                         x = z;
                         }
 
-                        error(entry_p, "entry object doesn't exist in main entry array");
+                        error(entry_p, "Entry object doesn't exist in main entry array");
                         return -EBADMSG;
                 }
 
@@ -494,9 +504,7 @@ static int verify_data(
 
         /* Entry array means at least two objects */
         if (a && n < 2) {
-                error(p,
-                      "entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")",
-                      a, n);
+                error(p, "Entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")", a, n);
                 return -EBADMSG;
         }
 
@@ -516,12 +524,12 @@ static int verify_data(
                 uint64_t next, m, j;
 
                 if (a == 0) {
-                        error(p, "array chain too short");
+                        error(p, "Array chain too short");
                         return -EBADMSG;
                 }
 
                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
-                        error(p, "invalid array offset "OFSfmt, a);
+                        error(p, "Invalid array offset "OFSfmt, a);
                         return -EBADMSG;
                 }
 
@@ -531,8 +539,7 @@ static int verify_data(
 
                 next = le64toh(o->entry_array.next_entry_array_offset);
                 if (next != 0 && next <= a) {
-                        error(p, "array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")",
-                              a, next);
+                        error(p, "Array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")", a, next);
                         return -EBADMSG;
                 }
 
@@ -541,7 +548,7 @@ static int verify_data(
 
                         q = le64toh(o->entry_array.items[j]);
                         if (q <= last) {
-                                error(p, "data object's entry array not sorted");
+                                error(p, "Data object's entry array not sorted");
                                 return -EBADMSG;
                         }
                         last = q;
@@ -580,11 +587,18 @@ static int verify_hash_table(
         assert(last_usec);
 
         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        if (n <= 0)
+                return 0;
+
+        r = journal_file_map_data_hash_table(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to map data hash table: %m");
+
         for (i = 0; i < n; i++) {
                 uint64_t last = 0, p;
 
                 if (show_progress)
-                        draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
+                        draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec);
 
                 p = le64toh(f->data_hash_table[i].head_hash_offset);
                 while (p != 0) {
@@ -592,8 +606,7 @@ static int verify_hash_table(
                         uint64_t next;
 
                         if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
-                                error(p, "invalid data object at hash entry %"PRIu64" of %"PRIu64,
-                                      i, n);
+                                error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n);
                                 return -EBADMSG;
                         }
 
@@ -603,14 +616,12 @@ static int verify_hash_table(
 
                         next = le64toh(o->data.next_hash_offset);
                         if (next != 0 && next <= p) {
-                                error(p, "hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64,
-                                      i, n);
+                                error(p, "Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64, i, n);
                                 return -EBADMSG;
                         }
 
                         if (le64toh(o->data.hash) % n != i) {
-                                error(p, "hash value mismatch in hash entry %"PRIu64" of %"PRIu64,
-                                      i, n);
+                                error(p, "Hash value mismatch in hash entry %"PRIu64" of %"PRIu64, i, n);
                                 return -EBADMSG;
                         }
 
@@ -623,7 +634,7 @@ static int verify_hash_table(
                 }
 
                 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
-                        error(p, "tail hash pointer mismatch in hash table");
+                        error(p, "Tail hash pointer mismatch in hash table");
                         return -EBADMSG;
                 }
         }
@@ -637,6 +648,13 @@ static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p)
         assert(f);
 
         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        if (n <= 0)
+                return 0;
+
+        r = journal_file_map_data_hash_table(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to map data hash table: %m");
+
         h = hash % n;
 
         q = le64toh(f->data_hash_table[h].head_hash_offset);
@@ -677,16 +695,16 @@ static int verify_entry(
                 h = le64toh(o->entry.items[i].hash);
 
                 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
-                        error(p, "invalid data object of entry");
-                                return -EBADMSG;
-                        }
+                        error(p, "Invalid data object of entry");
+                        return -EBADMSG;
+                }
 
                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
                 if (r < 0)
                         return r;
 
                 if (le64toh(u->data.hash) != h) {
-                        error(p, "hash mismatch for data object of entry");
+                        error(p, "Hash mismatch for data object of entry");
                         return -EBADMSG;
                 }
 
@@ -694,7 +712,7 @@ static int verify_entry(
                 if (r < 0)
                         return r;
                 if (r == 0) {
-                        error(p, "data object missing from hash table");
+                        error(p, "Data object missing from hash table");
                         return -EBADMSG;
                 }
         }
@@ -726,15 +744,15 @@ static int verify_entry_array(
                 Object *o;
 
                 if (show_progress)
-                        draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
+                        draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec);
 
                 if (a == 0) {
-                        error(a, "array chain too short at %"PRIu64" of %"PRIu64, i, n);
+                        error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n);
                         return -EBADMSG;
                 }
 
                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
-                        error(a, "invalid array %"PRIu64" of %"PRIu64, i, n);
+                        error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n);
                         return -EBADMSG;
                 }
 
@@ -744,9 +762,7 @@ static int verify_entry_array(
 
                 next = le64toh(o->entry_array.next_entry_array_offset);
                 if (next != 0 && next <= a) {
-                        error(a,
-                              "array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")",
-                              i, n, next);
+                        error(a, "Array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")", i, n, next);
                         return -EBADMSG;
                 }
 
@@ -756,15 +772,13 @@ static int verify_entry_array(
 
                         p = le64toh(o->entry_array.items[j]);
                         if (p <= last) {
-                                error(a, "entry array not sorted at %"PRIu64" of %"PRIu64,
-                                      i, n);
+                                error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n);
                                 return -EBADMSG;
                         }
                         last = p;
 
                         if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
-                                error(a, "invalid array entry at %"PRIu64" of %"PRIu64,
-                                      i, n);
+                                error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n);
                                 return -EBADMSG;
                         }
 
@@ -852,7 +866,7 @@ int journal_file_verify(
 
         for (i = 0; i < sizeof(f->header->reserved); i++)
                 if (f->header->reserved[i] != 0) {
-                        error(offsetof(Header, reserved[i]), "reserved field is non-zero");
+                        error(offsetof(Header, reserved[i]), "Reserved field is non-zero");
                         r = -EBADMSG;
                         goto fail;
                 }
@@ -861,36 +875,37 @@ int journal_file_verify(
          * superficial structure, headers, hashes. */
 
         p = le64toh(f->header->header_size);
-        while (p != 0) {
+        for (;;) {
+                /* Early exit if there are no objects in the file, at all */
+                if (le64toh(f->header->tail_object_offset) == 0)
+                        break;
+
                 if (show_progress)
-                        draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
+                        draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec);
 
                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
                 if (r < 0) {
-                        error(p, "invalid object");
+                        error(p, "Invalid object");
                         goto fail;
                 }
 
                 if (p > le64toh(f->header->tail_object_offset)) {
-                        error(offsetof(Header, tail_object_offset), "invalid tail object pointer");
+                        error(offsetof(Header, tail_object_offset), "Invalid tail object pointer");
                         r = -EBADMSG;
                         goto fail;
                 }
 
-                if (p == le64toh(f->header->tail_object_offset))
-                        found_last = true;
-
                 n_objects ++;
 
                 r = journal_file_object_verify(f, p, o);
                 if (r < 0) {
-                        error(p, "invalid object contents: %s", strerror(-r));
+                        error(p, "Envalid object contents: %s", strerror(-r));
                         goto fail;
                 }
 
                 if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
                     (o->object.flags & OBJECT_COMPRESSED_LZ4)) {
-                        error(p, "objected with double compression");
+                        error(p, "Objected with double compression");
                         r = -EINVAL;
                         goto fail;
                 }
@@ -923,7 +938,7 @@ int journal_file_verify(
 
                 case OBJECT_ENTRY:
                         if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
-                                error(p, "first entry before first tag");
+                                error(p, "First entry before first tag");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -933,21 +948,21 @@ int journal_file_verify(
                                 goto fail;
 
                         if (le64toh(o->entry.realtime) < last_tag_realtime) {
-                                error(p, "older entry after newer tag");
+                                error(p, "Older entry after newer tag");
                                 r = -EBADMSG;
                                 goto fail;
                         }
 
                         if (!entry_seqnum_set &&
                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
-                                error(p, "head entry sequence number incorrect");
+                                error(p, "Head entry sequence number incorrect");
                                 r = -EBADMSG;
                                 goto fail;
                         }
 
                         if (entry_seqnum_set &&
                             entry_seqnum >= le64toh(o->entry.seqnum)) {
-                                error(p, "entry sequence number out of synchronization");
+                                error(p, "Entry sequence number out of synchronization");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -958,7 +973,7 @@ int journal_file_verify(
                         if (entry_monotonic_set &&
                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
                             entry_monotonic > le64toh(o->entry.monotonic)) {
-                                error(p, "entry timestamp out of synchronization");
+                                error(p, "Entry timestamp out of synchronization");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -969,7 +984,7 @@ int journal_file_verify(
 
                         if (!entry_realtime_set &&
                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
-                                error(p, "head entry realtime timestamp incorrect");
+                                error(p, "Head entry realtime timestamp incorrect");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -982,7 +997,7 @@ int journal_file_verify(
 
                 case OBJECT_DATA_HASH_TABLE:
                         if (n_data_hash_tables > 1) {
-                                error(p, "more than one data hash table");
+                                error(p, "More than one data hash table");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -999,14 +1014,14 @@ int journal_file_verify(
 
                 case OBJECT_FIELD_HASH_TABLE:
                         if (n_field_hash_tables > 1) {
-                                error(p, "more than one field hash table");
+                                error(p, "More than one field hash table");
                                 r = -EBADMSG;
                                 goto fail;
                         }
 
                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
-                                error(p, "header fields for field hash table invalid");
+                                error(p, "Header fields for field hash table invalid");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -1021,7 +1036,7 @@ int journal_file_verify(
 
                         if (p == le64toh(f->header->entry_array_offset)) {
                                 if (found_main_entry_array) {
-                                        error(p, "more than one main entry array");
+                                        error(p, "More than one main entry array");
                                         r = -EBADMSG;
                                         goto fail;
                                 }
@@ -1034,19 +1049,19 @@ int journal_file_verify(
 
                 case OBJECT_TAG:
                         if (!JOURNAL_HEADER_SEALED(f->header)) {
-                                error(p, "tag object in file without sealing");
+                                error(p, "Tag object in file without sealing");
                                 r = -EBADMSG;
                                 goto fail;
                         }
 
                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
-                                error(p, "tag sequence number out of synchronization");
+                                error(p, "Tag sequence number out of synchronization");
                                 r = -EBADMSG;
                                 goto fail;
                         }
 
                         if (le64toh(o->tag.epoch) < last_epoch) {
-                                error(p, "epoch sequence out of synchronization");
+                                error(p, "Epoch sequence out of synchronization");
                                 r = -EBADMSG;
                                 goto fail;
                         }
@@ -1055,7 +1070,7 @@ int journal_file_verify(
                         if (f->seal) {
                                 uint64_t q, rt;
 
-                                debug(p, "checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
+                                debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
 
                                 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
                                 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
@@ -1102,7 +1117,7 @@ int journal_file_verify(
                                         goto fail;
 
                                 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
-                                        error(p, "tag failed verification");
+                                        error(p, "Tag failed verification");
                                         r = -EBADMSG;
                                         goto fail;
                                 }
@@ -1124,79 +1139,69 @@ int journal_file_verify(
                         n_weird ++;
                 }
 
-                if (p == le64toh(f->header->tail_object_offset))
-                        p = 0;
-                else
-                        p = p + ALIGN64(le64toh(o->object.size));
-        }
+                if (p == le64toh(f->header->tail_object_offset)) {
+                        found_last = true;
+                        break;
+                }
 
-        if (!found_last) {
-                error(le64toh(f->header->tail_object_offset), "tail object pointer dead");
+                p = p + ALIGN64(le64toh(o->object.size));
+        };
+
+        if (!found_last && le64toh(f->header->tail_object_offset) != 0) {
+                error(le64toh(f->header->tail_object_offset), "Tail object pointer dead");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (n_objects != le64toh(f->header->n_objects)) {
-                error(offsetof(Header, n_objects), "object number mismatch");
+                error(offsetof(Header, n_objects), "Object number mismatch");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (n_entries != le64toh(f->header->n_entries)) {
-                error(offsetof(Header, n_entries), "entry number mismatch");
+                error(offsetof(Header, n_entries), "Entry number mismatch");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
             n_data != le64toh(f->header->n_data)) {
-                error(offsetof(Header, n_data), "data number mismatch");
+                error(offsetof(Header, n_data), "Data number mismatch");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
             n_fields != le64toh(f->header->n_fields)) {
-                error(offsetof(Header, n_fields), "field number mismatch");
+                error(offsetof(Header, n_fields), "Field number mismatch");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
             n_tags != le64toh(f->header->n_tags)) {
-                error(offsetof(Header, n_tags), "tag number mismatch");
+                error(offsetof(Header, n_tags), "Tag number mismatch");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
-                error(offsetof(Header, n_entry_arrays), "entry array number mismatch");
-                r = -EBADMSG;
-                goto fail;
-        }
-
-        if (n_data_hash_tables != 1) {
-                error(0, "missing data hash table");
-                r = -EBADMSG;
-                goto fail;
-        }
-
-        if (n_field_hash_tables != 1) {
-                error(0, "missing field hash table");
+                error(offsetof(Header, n_entry_arrays), "Entry array number mismatch");
                 r = -EBADMSG;
                 goto fail;
         }
 
-        if (!found_main_entry_array) {
-                error(0, "missing entry array");
+        if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) {
+                error(0, "Missing entry array");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (entry_seqnum_set &&
             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
-                error(offsetof(Header, tail_entry_seqnum), "invalid tail seqnum");
+                error(offsetof(Header, tail_entry_seqnum), "Invalid tail seqnum");
                 r = -EBADMSG;
                 goto fail;
         }
@@ -1204,13 +1209,13 @@ int journal_file_verify(
         if (entry_monotonic_set &&
             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
-                error(0, "invalid tail monotonic timestamp");
+                error(0, "Invalid tail monotonic timestamp");
                 r = -EBADMSG;
                 goto fail;
         }
 
         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
-                error(0, "invalid tail realtime timestamp");
+                error(0, "Invalid tail realtime timestamp");
                 r = -EBADMSG;
                 goto fail;
         }
index 2d6ecfb75084fe2418b4164a3d6864d6185a3c9d..073cc77711335db420a900b958243346c09ca5af 100644 (file)
@@ -2066,6 +2066,10 @@ int main(int argc, char *argv[]) {
                 log_error_errno(r, "Failed to iterate through journal: %m");
                 goto finish;
         }
+        if (r == 0) {
+                printf("-- No entries --\n");
+                goto finish;
+        }
 
         if (!arg_follow)
                 pager_open_if_enabled();
index 46358e1c1a61a64f86bdc8f361e2ad738d87ed2e..28b1472ac80e497d1e143f5fbb3f17c349ce9379 100644 (file)
@@ -175,9 +175,11 @@ static uint64_t available_space(Server *s, bool verbose) {
                         fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX];
 
                 server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE,
-                                      "%s journal is using %s (max allowed %s, "
-                                      "trying to leave %s free of %s available â†’ current limit %s).",
-                                      s->system_journal ? "Permanent" : "Runtime",
+                                      "%s is currently using %s.\n"
+                                      "Maximum allowed usage is set to %s.\n"
+                                      "Leaving at least %s free (of currently available %s of space).\n"
+                                      "Enforced usage limit is thus %s.",
+                                      s->system_journal ? "Permanent journal (/var/log/journal/)" : "Runtime journal (/run/log/journal/)",
                                       format_bytes(fb1, sizeof(fb1), sum),
                                       format_bytes(fb2, sizeof(fb2), m->max_use),
                                       format_bytes(fb3, sizeof(fb3), m->keep_free),
index 9e184ac4b59e643895db203f1623835e459a84dc..6e00b1ad301d57882cf1e568063c18d122b8e838 100644 (file)
@@ -72,6 +72,8 @@ struct sd_dhcp_lease {
         char *root_path;
         uint8_t *client_id;
         size_t client_id_len;
+        uint8_t *vendor_specific;
+        size_t vendor_specific_len;
 };
 
 int dhcp_lease_new(sd_dhcp_lease **ret);
index abca9422c5c1281d3f4f53b1326c7c786c88a287..aa37e9b0b5c480741369132c2640565fa64b78c4 100644 (file)
@@ -125,6 +125,7 @@ enum {
         DHCP_OPTION_BROADCAST                   = 28,
         DHCP_OPTION_STATIC_ROUTE                = 33,
         DHCP_OPTION_NTP_SERVER                  = 42,
+        DHCP_OPTION_VENDOR_SPECIFIC             = 43,
         DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
         DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
         DHCP_OPTION_OVERLOAD                    = 52,
index d8bc76edda4b9efa4cf33b250c4d8aa00cd640e9..54417b3af38d04638242d2669d624531f898634d 100644 (file)
@@ -179,6 +179,21 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes
         return 0;
 }
 
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
+                                      size_t *data_len) {
+        assert_return(lease, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(data_len, -EINVAL);
+
+        if (!lease->vendor_specific)
+                return -ENOENT;
+
+        *data = lease->vendor_specific;
+        *data_len = lease->vendor_specific_len;
+
+        return 0;
+}
+
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
         if (lease)
                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
@@ -194,6 +209,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
                 free(lease->ntp);
                 free(lease->static_route);
                 free(lease->client_id);
+                free(lease->vendor_specific);
                 free(lease);
         }
 
@@ -435,7 +451,8 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
                 break;
 
         case DHCP_OPTION_ROUTER:
-                lease_parse_be32(option, len, &lease->router);
+                if(len >= 4)
+                        lease_parse_be32(option, 4, &lease->router);
 
                 break;
 
@@ -579,6 +596,17 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
                         return r;
 
                 break;
+
+        case DHCP_OPTION_VENDOR_SPECIFIC:
+                if (len >= 1) {
+                        free(lease->vendor_specific);
+                        lease->vendor_specific = memdup(option, len);
+                        if (!lease->vendor_specific)
+                                return -ENOMEM;
+                        lease->vendor_specific_len = len;
+                }
+
+               break;
         }
 
         return 0;
@@ -603,8 +631,8 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         _cleanup_fclose_ FILE *f = NULL;
         struct in_addr address;
         const struct in_addr *addresses;
-        const uint8_t *client_id;
-        size_t client_id_len;
+        const uint8_t *client_id, *data;
+        size_t client_id_len, data_len;
         const char *string;
         uint16_t mtu;
         struct sd_dhcp_route *routes;
@@ -690,6 +718,18 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
                 fprintf(f, "CLIENTID=%s\n", client_id_hex);
         }
 
+        r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len);
+        if (r >= 0) {
+                _cleanup_free_ char *option_hex = NULL;
+
+                option_hex = hexmem(data, data_len);
+                if (!option_hex) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+                fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
+        }
+
         r = 0;
 
         fflush(f);
@@ -712,7 +752,8 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
                             *server_address = NULL, *next_server = NULL,
                             *dns = NULL, *ntp = NULL, *mtu = NULL,
-                            *routes = NULL, *client_id_hex = NULL;
+                            *routes = NULL, *client_id_hex = NULL,
+                            *vendor_specific_hex = NULL;
         struct in_addr addr;
         int r;
 
@@ -737,6 +778,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                            "ROOT_PATH", &lease->root_path,
                            "ROUTES", &routes,
                            "CLIENTID", &client_id_hex,
+                           "VENDOR_SPECIFIC", &vendor_specific_hex,
                            NULL);
         if (r < 0) {
                 if (r == -ENOENT)
@@ -811,13 +853,21 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
         }
 
         if (client_id_hex) {
-                if (strlen (client_id_hex) % 2)
+                if (strlen(client_id_hex) % 2)
                         return -EINVAL;
 
-                lease->client_id = unhexmem (client_id_hex, strlen (client_id_hex));
-                if (!lease->client_id)
-                        return -ENOMEM;
-                lease->client_id_len = strlen (client_id_hex) / 2;
+                r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len);
+                if (r < 0)
+                        return r;
+        }
+
+        if (vendor_specific_hex) {
+                if (strlen(vendor_specific_hex) % 2)
+                        return -EINVAL;
+
+                r = unhexmem(vendor_specific_hex, strlen(vendor_specific_hex), (void**) &lease->vendor_specific, &lease->vendor_specific_len);
+                if (r < 0)
+                        return r;
         }
 
         *ret = lease;
diff --git a/src/libsystemd-terminal/.gitignore b/src/libsystemd-terminal/.gitignore
deleted file mode 100644 (file)
index 7de83bd..0000000
+++ /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 (file)
index 2aeefc2..0000000
+++ /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 (file)
index 10c13e3..0000000
+++ /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 (file)
index 46d65f0..0000000
+++ /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 (file)
index 71f0bd3..0000000
+++ /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 (file)
index 110d24e..0000000
+++ /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 (file)
index f1a18b9..0000000
+++ /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 (file)
index a02a16c..0000000
+++ /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 (file)
index 93f49e9..0000000
+++ /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 (file)
index b92a393..0000000
+++ /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 (file)
index 241677c..0000000
+++ /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 (file)
index 790a244..0000000
+++ /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 (file)
index 5f12540..0000000
+++ /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 (file)
index 251c8d7..0000000
+++ /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 (file)
index 2e9b158..0000000
+++ /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 (file)
index a5e7a38..0000000
+++ /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 (file)
index 9db1788..0000000
+++ /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 (file)
index 8c6a001..0000000
+++ /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 (file)
index bac8520..0000000
+++ /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 (file)
index 8dc1da2..0000000
+++ /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 (file)
index 0e38ff4..0000000
+++ /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 (file)
index 833a099..0000000
+++ /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 (file)
index 1a78a81..0000000
+++ /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 (file)
index d59139b..0000000
+++ /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 (file)
index e40b267..0000000
+++ /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 (file)
index 2366d38..0000000
+++ /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 (file)
index 3847a2c..0000000
+++ /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 (file)
index 0da81e8..0000000
+++ /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 (file)
index 74ee5ec..0000000
+++ /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);
index 0dbfbddcf6b419bdf8241585d846c5ba9883623d..f2092795f410b40c3142338bbc8d2495980040b2 100644 (file)
@@ -46,6 +46,8 @@
 #define BUS_ERROR_NO_MACHINE_FOR_PID "org.freedesktop.machine1.NoMachineForPID"
 #define BUS_ERROR_MACHINE_EXISTS "org.freedesktop.machine1.MachineExists"
 #define BUS_ERROR_NO_PRIVATE_NETWORKING "org.freedesktop.machine1.NoPrivateNetworking"
+#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping"
+#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping"
 
 #define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession"
 #define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID"
index a38c5c50fcaee7c654ebe1cbeadc85d0dd164dd6..c53666ddd02f396ba41b4672d25cf770446e6c37 100644 (file)
@@ -1131,7 +1131,7 @@ static int add_name_change_match(sd_bus *bus,
 
                 /* If the old name is unset or empty, then
                  * this can match against added names */
-                if (!old_owner || old_owner[0] == 0) {
+                if (isempty(old_owner)) {
                         item->type = KDBUS_ITEM_NAME_ADD;
 
                         r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
@@ -1141,7 +1141,7 @@ static int add_name_change_match(sd_bus *bus,
 
                 /* If the new name is unset or empty, then
                  * this can match against removed names */
-                if (!new_owner || new_owner[0] == 0) {
+                if (isempty(new_owner)) {
                         item->type = KDBUS_ITEM_NAME_REMOVE;
 
                         r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
@@ -1185,8 +1185,10 @@ static int add_name_change_match(sd_bus *bus,
 
                 /* If the old name is unset or empty, then this can
                  * match against added ids */
-                if (!old_owner || old_owner[0] == 0) {
+                if (isempty(old_owner)) {
                         item->type = KDBUS_ITEM_ID_ADD;
+                        if (!isempty(new_owner))
+                                item->id_change.id = new_owner_id;
 
                         r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
                         if (r < 0)
@@ -1195,8 +1197,10 @@ static int add_name_change_match(sd_bus *bus,
 
                 /* If thew new name is unset or empty, then this can
                  * match against removed ids */
-                if (!new_owner || new_owner[0] == 0) {
+                if (isempty(new_owner)) {
                         item->type = KDBUS_ITEM_ID_REMOVE;
+                        if (!isempty(old_owner))
+                                item->id_change.id = old_owner_id;
 
                         r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
                         if (r < 0)
@@ -1345,6 +1349,10 @@ int bus_add_match_internal_kernel(
                         else if (r > 0)
                                 sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t));
 
+                        /* if not a broadcast, it cannot be a name-change */
+                        if (r <= 0 || dst_id != KDBUS_DST_ID_BROADCAST)
+                                matches_name_change = false;
+
                         break;
                 }
 
index e3fac01f9284a5885b5a90a7cd39cd392496da85..6ac5ebc3daef321626c418a13afb532e67270e7f 100644 (file)
@@ -1332,8 +1332,7 @@ static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) {
         KDBUS_ITEM_FOREACH(d, k, items) {
                 if (d->type == KDBUS_ITEM_TIMESTAMP)
                         ts = &d->timestamp;
-
-                if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) {
+                else if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) {
                         if (found)
                                 return -EBADMSG;
                         found = d;
index 983e2f62cddce00a9ebd41bce7bc0fd0f79d1246..18685be8ff53d13db7776ca5114760c21bdaf3ab 100644 (file)
@@ -2161,6 +2161,7 @@ static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c)
 }
 
 static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) {
+        bool fixed_size = true;
         size_t n_variable = 0;
         unsigned i = 0;
         const char *p;
@@ -2196,6 +2197,8 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
                 /* We need to add an offset for each item that has a
                  * variable size and that is not the last one in the
                  * list */
+                if (r == 0)
+                        fixed_size = false;
                 if (r == 0 && p[n] != 0)
                         n_variable++;
 
@@ -2207,7 +2210,19 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
         assert(c->need_offsets || n_variable == 0);
 
         if (n_variable <= 0) {
-                a = message_extend_body(m, 1, 0, add_offset, false);
+                int alignment = 1;
+
+                /* Structures with fixed-size members only have to be
+                 * fixed-size themselves. But gvariant requires all fixed-size
+                 * elements to be sized a multiple of their alignment. Hence,
+                 * we must *always* add final padding after the last member so
+                 * the overall size of the structure is properly aligned. */
+                if (fixed_size)
+                        alignment = bus_gvariant_get_alignment(strempty(c->signature));
+
+                assert(alignment > 0);
+
+                a = message_extend_body(m, alignment, 0, add_offset, false);
                 if (!a)
                         return -ENOMEM;
         } else {
index 2eaa7de30628945b871d4383429a3081ee8fd1d2..c25293e5e96cb9782091ee414417fdc7a0c8a5cd 100644 (file)
@@ -68,6 +68,12 @@ static int node_vtable_get_userdata(
         return 1;
 }
 
+static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
+        assert(p);
+
+        return (uint8_t*) u + p->x.method.offset;
+}
+
 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
         assert(p);
 
@@ -167,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) {
 
@@ -198,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;
@@ -212,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) {
 
@@ -227,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;
@@ -360,6 +370,8 @@ static int method_callbacks_run(
         if (bus->nodes_modified)
                 return 0;
 
+        u = vtable_method_convert_userdata(c->vtable, u);
+
         *found_object = true;
 
         if (c->last_iteration == bus->iteration_counter)
@@ -892,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)
@@ -1158,12 +1170,16 @@ 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)
                 return 0;
 
+        r = set_put_strdup(s, m->path);
+        if (r < 0)
+                return r;
+
         r = sd_bus_message_new_method_return(m, &reply);
         if (r < 0)
                 return r;
@@ -1412,7 +1428,7 @@ static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
                 e = strrchr(path, '/');
                 assert(e);
 
-                p = strndupa(path, MAX(1, path - e));
+                p = strndupa(path, MAX(1, e - path));
 
                 parent = bus_node_allocate(bus, p);
                 if (!parent)
@@ -1463,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,
@@ -2265,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;
 
         /*
@@ -2285,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;
 
@@ -2428,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;
 
         /*
@@ -2448,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;
 
@@ -2584,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;
 
@@ -2597,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;
 
@@ -2661,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);
@@ -2673,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;
 
index c45247756691fcee116d0d50d35d661502217768..b149ea16daa2efc3cf271f71cdc19890d7f3223a 100644 (file)
@@ -273,7 +273,7 @@ _public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *descript
         return free_and_strdup(&slot->description, description);
 }
 
-_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, char **description) {
+_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description) {
         assert_return(slot, -EINVAL);
         assert_return(description, -EINVAL);
         assert_return(slot->description, -ENXIO);
index 322d57ddbba43437348b713b36a72454d3081591..735a775cb4bd77e97703d957c23918fa7ca21a9c 100644 (file)
@@ -264,6 +264,8 @@ static bool line_begins(const char *s, size_t m, const char *word) {
 
 static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
         _cleanup_free_ char *token = NULL;
+        size_t len;
+        int r;
 
         if (!b->anonymous_auth)
                 return 0;
@@ -276,11 +278,12 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
 
         if (l % 2 != 0)
                 return 0;
-        token = unhexmem(p, l);
-        if (!token)
-                return -ENOMEM;
 
-        if (memchr(token, 0, l/2))
+        r = unhexmem(p, l, (void **) &token, &len);
+        if (r < 0)
+                return 0;
+
+        if (memchr(token, 0, len))
                 return 0;
 
         return !!utf8_is_valid(token);
@@ -288,6 +291,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
 
 static int verify_external_token(sd_bus *b, const char *p, size_t l) {
         _cleanup_free_ char *token = NULL;
+        size_t len;
         uid_t u;
         int r;
 
@@ -307,11 +311,11 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
         if (l % 2 != 0)
                 return 0;
 
-        token = unhexmem(p, l);
-        if (!token)
-                return -ENOMEM;
+        r = unhexmem(p, l, (void**) &token, &len);
+        if (r < 0)
+                return 0;
 
-        if (memchr(token, 0, l/2))
+        if (memchr(token, 0, len))
                 return 0;
 
         r = parse_uid(token, &u);
index a866a561795d8d63515dd9ff6aa183214b3d315f..59deaea89f0a1d1c7a8c6a37661b9a96121a0d46 100644 (file)
@@ -131,6 +131,9 @@ int main(int argc, char *argv[]) {
         r = sd_bus_message_append(m, "a{yv}", 2, 3, "s", "foo", 5, "s", "waldo");
         assert_se(r >= 0);
 
+        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, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3");
         assert_se(r >= 0);
 
@@ -252,6 +255,22 @@ int main(int argc, char *argv[]) {
         assert_se(v == 5);
         assert_se(streq(y, "waldo"));
 
+        r = sd_bus_message_read(m, "y(ty)", &v, &u64, &u);
+        assert_se(r > 0);
+        assert_se(v == 8);
+        assert_se(u64 == 777);
+        assert_se(u == 7);
+
+        r = sd_bus_message_read(m, "y(yt)", &v, &u, &u64);
+        assert_se(r > 0);
+        assert_se(v == 9);
+        assert_se(u == 77);
+        assert_se(u64 == 7777);
+
+        r = sd_bus_message_read(m, "y", &v);
+        assert_se(r > 0);
+        assert_se(v == 10);
+
         r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d);
         assert_se(r > 0);
         assert_se(boolean);
@@ -331,7 +350,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}");
+        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);
index 52952603e455fd51652bd235b1b98c2a1c55a8d6..359984c7f3045d14ccdceb29291ffad8dc88197f 100644 (file)
@@ -115,14 +115,13 @@ static int set_handler(sd_bus *bus, const char *path, const char *interface, con
 
 static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *s = NULL;
-        const char *x;
         int r;
 
         assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
         r = sd_bus_message_append(reply, "s", s);
         assert_se(r >= 0);
 
-        assert_se(x = startswith(path, "/value/"));
+        assert_se(startswith(path, "/value/") != NULL || strcmp(path, "/value") == 0);
 
         assert_se(PTR_TO_UINT(userdata) == 30);
 
@@ -154,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);
@@ -165,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);
@@ -176,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);
@@ -187,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);
@@ -229,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;
@@ -247,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
new file mode 100644 (file)
index 0000000..369c2f3
--- /dev/null
@@ -0,0 +1,109 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 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 <fcntl.h>
+#include <stdlib.h>
+
+#include "util.h"
+#include "log.h"
+
+#include "sd-bus.h"
+#include "bus-kernel.h"
+#include "bus-util.h"
+#include "bus-dump.h"
+
+typedef struct {
+        const char *sender;
+        int matched_acquired;
+} TestProxyMatch;
+
+static int test_proxy_acquired(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        TestProxyMatch *match = userdata;
+        const char *name;
+        int r;
+
+        r = sd_bus_message_read(m, "s", &name);
+        assert_se(r >= 0);
+
+        if (!streq_ptr(match->sender, name))
+                return 0;
+
+        ++match->matched_acquired;
+        return 1;
+}
+
+static void test_proxy_matched(void) {
+        _cleanup_bus_flush_close_unref_ sd_bus *a = NULL;
+        TestProxyMatch match = {};
+        int r;
+
+        /* open bus 'a' */
+
+        r = sd_bus_new(&a);
+        assert_se(r >= 0);
+
+        r = sd_bus_set_address(a, "unix:path=/var/run/dbus/system_bus_socket");
+        assert_se(r >= 0);
+
+        r = sd_bus_set_bus_client(a, true);
+        assert_se(r >= 0);
+
+        r = sd_bus_start(a);
+        assert_se(r >= 0);
+
+        r = sd_bus_add_match(a, NULL,
+                             "type='signal',"
+                             "member='NameAcquired'",
+                             test_proxy_acquired, &match);
+        assert_se(r >= 0);
+
+        r = sd_bus_get_unique_name(a, &match.sender);
+        assert_se(r >= 0);
+
+        /* barrier to guarantee proxy/dbus-daemon handled the previous data  */
+        r = sd_bus_call_method(a,
+                               "org.freedesktop.DBus",
+                               "/org/freedesktop/DBus",
+                               "org.freedesktop.DBus",
+                               "GetId",
+                               NULL, NULL, NULL);
+        assert_se(r >= 0);
+
+        /* now we can be sure the Name* signals were sent */
+        do {
+                r = sd_bus_process(a, NULL);
+        } while (r > 0);
+        assert_se(r == 0);
+
+        assert_se(match.matched_acquired == 1);
+}
+
+int main(int argc, char **argv) {
+        if (access("/var/run/dbus/system_bus_socket", F_OK) < 0)
+                return EXIT_TEST_SKIP;
+
+        log_parse_environment();
+
+        test_proxy_matched();
+
+        return EXIT_SUCCESS;
+}
index b274f710935841f027c94c45d5dcf04dc9a58373..7cea5a074658ef5a63dda89881eba859a19dd0a2 100644 (file)
@@ -791,6 +791,9 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
                 device->subsystem_set = true;
         }
 
+        if (!device->subsystem)
+                return -ENOENT;
+
         *ret = device->subsystem;
 
         return 0;
@@ -908,6 +911,9 @@ _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
                         return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
         }
 
+        if (!device->driver)
+                return -ENOENT;
+
         *ret = device->driver;
 
         return 0;
@@ -1002,6 +1008,8 @@ _public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
                         return r;
         }
 
+        assert_return(device->sysname, -ENOENT);
+
         *ret = device->sysname;
 
         return 0;
index 6f51ebe73db0f6bd565f0de13fcfe741016dd14f..4026e2c341b1333fe3e648e0e30ee6186a8372e0 100644 (file)
@@ -94,6 +94,8 @@ struct sd_netlink {
 
 struct netlink_attribute {
         size_t offset; /* offset from hdr to attribute */
+        bool nested:1;
+        bool net_byteorder:1;
 };
 
 struct netlink_container {
index 13573dcea8ac50e2436ce69adfd80fffdbf664f9..b0ed2f28825bf9dfd7408b6f9e423edfa5af9a79 100644 (file)
@@ -38,6 +38,7 @@
 #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
 
 #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
+#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
 
 int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
         sd_netlink_message *m;
@@ -475,7 +476,7 @@ int sd_netlink_message_close_container(sd_netlink_message *m) {
         return 0;
 }
 
-static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data) {
+static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) {
         struct netlink_attribute *attribute;
         struct rtattr *rta;
 
@@ -495,6 +496,9 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
 
         *data = RTA_DATA(rta);
 
+        if (net_byteorder)
+                *net_byteorder = attribute->net_byteorder;
+
         return RTA_PAYLOAD(rta);
 }
 
@@ -508,7 +512,7 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if (strnlen(attr_data, r) >= (size_t) r)
@@ -530,7 +534,7 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t) r < sizeof(uint8_t))
@@ -543,8 +547,9 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
 }
 
 int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
-        int r;
         void *attr_data;
+        bool net_byteorder;
+        int r;
 
         assert_return(m, -EINVAL);
 
@@ -552,21 +557,26 @@ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
         if (r < 0)
                 return r;
         else if ((size_t) r < sizeof(uint16_t))
                 return -EIO;
 
-        if (data)
-                *data = *(uint16_t *) attr_data;
+        if (data) {
+                if (net_byteorder)
+                        *data = be16toh(*(uint16_t *) attr_data);
+                else
+                        *data = *(uint16_t *) attr_data;
+        }
 
         return 0;
 }
 
 int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
-        int r;
         void *attr_data;
+        bool net_byteorder;
+        int r;
 
         assert_return(m, -EINVAL);
 
@@ -574,14 +584,18 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(uint32_t))
                 return -EIO;
 
-        if (data)
-                *data = *(uint32_t *) attr_data;
+        if (data) {
+                if (net_byteorder)
+                        *data = be32toh(*(uint32_t *) attr_data);
+                else
+                        *data = *(uint32_t *) attr_data;
+        }
 
         return 0;
 }
@@ -596,7 +610,7 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct ether_addr))
@@ -618,7 +632,7 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct ifa_cacheinfo))
@@ -640,7 +654,7 @@ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type,
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct in_addr))
@@ -662,7 +676,7 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct in6_addr))
@@ -699,6 +713,8 @@ static int netlink_container_parse(sd_netlink_message *m,
                         log_debug("rtnl: message parse - overwriting repeated attribute");
 
                 attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
+                attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
+                attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
         }
 
         container->attributes = attributes;
@@ -781,7 +797,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         } else
                 return -EINVAL;
 
-        r = netlink_message_read_internal(m, type_id, &container);
+        r = netlink_message_read_internal(m, type_id, &container, NULL);
         if (r < 0)
                 return r;
         else
index 74ac2ab344d7f271a831b24a8f0e6175b98a2797..1e747abb2447db5683756f7dc5d6060cfa3fc657 100644 (file)
@@ -196,27 +196,37 @@ static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = {
         [IFLA_IPTUN_6RD_RELAY_PREFIX]    = { .type = NETLINK_TYPE_U32 },
         [IFLA_IPTUN_6RD_PREFIXLEN]       = { .type = NETLINK_TYPE_U16 },
         [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_TYPE]          = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_FLAGS]         = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_SPORT]         = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_DPORT]         = { .type = NETLINK_TYPE_U16 },
 };
 
 static  const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = {
-        [IFLA_GRE_LINK]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_IFLAGS]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_OFLAGS]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_IKEY]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_OKEY]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_LOCAL]    = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_GRE_REMOTE]   = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_GRE_TTL]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_TOS]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_LINK]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_IFLAGS]       = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_OFLAGS]       = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_IKEY]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_OKEY]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_GRE_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_GRE_TTL]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_TOS]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_PMTUDISC]     = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_FLOWINFO]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_FLAGS]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_ENCAP_TYPE]   = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ENCAP_FLAGS]  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ENCAP_SPORT]  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ENCAP_DPORT]  = { .type = NETLINK_TYPE_U16 },
 };
 
 static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = {
         [IFLA_VTI_LINK]         = { .type = NETLINK_TYPE_U32 },
         [IFLA_VTI_IKEY]         = { .type = NETLINK_TYPE_U32 },
         [IFLA_VTI_OKEY]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VTI_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR  },
-        [IFLA_VTI_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR  },
+        [IFLA_VTI_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_VTI_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR },
 };
 
 static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = {
@@ -227,7 +237,7 @@ static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = {
         [IFLA_IPTUN_FLAGS]               = { .type = NETLINK_TYPE_U32 },
         [IFLA_IPTUN_PROTO]               = { .type = NETLINK_TYPE_U8 },
         [IFLA_IPTUN_ENCAP_LIMIT]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_FLOWINFO]            = { .type = NETLINK_TYPE_U32},
+        [IFLA_IPTUN_FLOWINFO]            = { .type = NETLINK_TYPE_U32 },
 };
 
 /* these strings must match the .kind entries in the kernel */
@@ -238,6 +248,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",
@@ -264,6 +275,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),
@@ -319,8 +332,11 @@ static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1]
         [IFLA_BRPORT_MODE]              = { .type = NETLINK_TYPE_U8 },
         [IFLA_BRPORT_GUARD]             = { .type = NETLINK_TYPE_U8 },
         [IFLA_BRPORT_PROTECT]           = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_FAST_LEAVE]        = { .type = NETLINK_TYPE_U8 },
         [IFLA_BRPORT_LEARNING]          = { .type = NETLINK_TYPE_U8 },
         [IFLA_BRPORT_UNICAST_FLOOD]     = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_PROXYARP]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_LEARNING_SYNC]     = { .type = NETLINK_TYPE_U8 },
 };
 
 static const NLTypeSystem rtnl_prot_info_type_systems[AF_MAX] = {
@@ -362,9 +378,9 @@ static const NLTypeSystem rtnl_af_spec_type_system = {
 };
 
 static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
-        [IFLA_ADDRESS]          = { .type = NETLINK_TYPE_ETHER_ADDR, },
-        [IFLA_BROADCAST]        = { .type = NETLINK_TYPE_ETHER_ADDR, },
-        [IFLA_IFNAME]           = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1, },
+        [IFLA_ADDRESS]          = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [IFLA_BROADCAST]        = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [IFLA_IFNAME]           = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
         [IFLA_MTU]              = { .type = NETLINK_TYPE_U32 },
         [IFLA_LINK]             = { .type = NETLINK_TYPE_U32 },
 /*
index a210163241c34f2e5029151043d1e4c384e214f2..758ffad1b742222179fa0afc4937053feba779b6 100644 (file)
@@ -73,6 +73,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,
index 82654ee8c7a274c1209cdcfae6f6b3a046b2e528..e6371ff04d9a323e4af123047097c02e94102be0 100644 (file)
@@ -699,9 +699,12 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
          * after the user-session and want the user-session to take
          * over the VT. We need to support this for
          * backwards-compatibility, so make sure we allow new sessions
-         * on a VT that a greeter is running on.
+         * on a VT that a greeter is running on. Furthermore, to allow
+         * re-logins, we have to allow a greeter to take over a used VT for
+         * the exact same reasons.
          */
-        if (vtnr > 0 &&
+        if (c != SESSION_GREETER &&
+            vtnr > 0 &&
             vtnr < m->seat0->position_count &&
             m->seat0->positions[vtnr] &&
             m->seat0->positions[vtnr]->class != SESSION_GREETER)
@@ -1172,7 +1175,7 @@ static int trigger_device(Manager *m, struct udev_device *d) {
                 if (!t)
                         return -ENOMEM;
 
-                write_string_file(t, "change");
+                write_string_file(t, "change", WRITE_STRING_FILE_CREATE);
         }
 
         return 0;
@@ -1771,7 +1774,7 @@ static int nologin_timeout_handler(
 
         log_info("Creating /run/nologin, blocking further logins...");
 
-        r = write_string_file_atomic("/run/nologin", "System is going down.");
+        r = write_string_file("/run/nologin", "System is going down.", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
         if (r < 0)
                 log_error_errno(r, "Failed to create /run/nologin: %m");
         else
@@ -2446,8 +2449,6 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2455,6 +2456,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
index fb5d076311d410372f99494ec3eace21ca679932..495ec50be06d21e1da86505f5c394b01c78b53af 100644 (file)
@@ -290,8 +290,8 @@ int seat_switch_to_next(Seat *s) {
                 return -EINVAL;
 
         start = 1;
-        if (s->active && s->active->pos > 0)
-                start = s->active->pos;
+        if (s->active && s->active->position > 0)
+                start = s->active->position;
 
         for (i = start + 1; i < s->position_count; ++i)
                 if (s->positions[i])
@@ -311,8 +311,8 @@ int seat_switch_to_previous(Seat *s) {
                 return -EINVAL;
 
         start = 1;
-        if (s->active && s->active->pos > 0)
-                start = s->active->pos;
+        if (s->active && s->active->position > 0)
+                start = s->active->position;
 
         for (i = start - 1; i > 0; --i)
                 if (s->positions[i])
@@ -472,9 +472,9 @@ int seat_stop_sessions(Seat *s, bool force) {
 
 void seat_evict_position(Seat *s, Session *session) {
         Session *iter;
-        unsigned int pos = session->pos;
+        unsigned int pos = session->position;
 
-        session->pos = 0;
+        session->position = 0;
 
         if (pos == 0)
                 return;
@@ -486,7 +486,7 @@ void seat_evict_position(Seat *s, Session *session) {
                  * position (eg., during gdm->session transition), so let's look
                  * for it and set it on the free slot. */
                 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
-                        if (iter->pos == pos) {
+                        if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) {
                                 s->positions[pos] = iter;
                                 break;
                         }
@@ -504,15 +504,15 @@ void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
 
         seat_evict_position(s, session);
 
-        session->pos = pos;
-        if (pos > 0 && !s->positions[pos])
+        session->position = pos;
+        if (pos > 0)
                 s->positions[pos] = session;
 }
 
 static void seat_assign_position(Seat *s, Session *session) {
         unsigned int pos;
 
-        if (session->pos > 0)
+        if (session->position > 0)
                 return;
 
         for (pos = 1; pos < s->position_count; ++pos)
index 6a450b02a0a3878cf5a032cab3bc404121486122..45f4c09d3dc2ee6b069e871c4eb55a03b18e1c4b 100644 (file)
@@ -264,7 +264,7 @@ int session_save(Session *s) {
                 fprintf(f, "VTNR=%u\n", s->vtnr);
 
         if (!s->vtnr)
-                fprintf(f, "POS=%u\n", s->pos);
+                fprintf(f, "POSITION=%u\n", s->position);
 
         if (s->leader > 0)
                 fprintf(f, "LEADER="PID_FMT"\n", s->leader);
@@ -302,7 +302,7 @@ int session_load(Session *s) {
                 *seat = NULL,
                 *vtnr = NULL,
                 *state = NULL,
-                *pos = NULL,
+                *position = NULL,
                 *leader = NULL,
                 *type = NULL,
                 *class = NULL,
@@ -329,7 +329,7 @@ int session_load(Session *s) {
                            "DESKTOP",        &s->desktop,
                            "VTNR",           &vtnr,
                            "STATE",          &state,
-                           "POS",            &pos,
+                           "POSITION",       &position,
                            "LEADER",         &leader,
                            "TYPE",           &type,
                            "CLASS",          &class,
@@ -388,10 +388,10 @@ int session_load(Session *s) {
         if (!s->seat || !seat_has_vts(s->seat))
                 s->vtnr = 0;
 
-        if (pos && s->seat) {
+        if (position && s->seat) {
                 unsigned int npos;
 
-                safe_atou(pos, &npos);
+                safe_atou(position, &npos);
                 seat_claim_position(s->seat, s, npos);
         }
 
index 4bf739a44d8da7a3b89b142b183f6391a74f4680..b8565ebf515724a61b15c281b01d792848f6c993 100644 (file)
@@ -70,7 +70,7 @@ struct Session {
         Manager *manager;
 
         const char *id;
-        unsigned int pos;
+        unsigned int position;
         SessionType type;
         SessionClass class;
 
index 0f72d70b103c344a3948b6e402171f93facc2b8c..36c0e8626dd2e206477c51504a0e1f211b50c91a 100644 (file)
@@ -103,11 +103,7 @@ static int property_get_sessions(
 
         }
 
-        r = sd_bus_message_close_container(reply);
-        if (r < 0)
-                return r;
-
-        return 1;
+        return sd_bus_message_close_container(reply);
 }
 
 static int property_get_idle_hint(
index 0ad78802dd4716543ec5125c2fd9b58e4c251fbb..d8deb7bc8b439c8064747a675b837aac2bfd262a 100644 (file)
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="ActivateSessionOnSeat"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="LockSession"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="UnlockSession"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="LockSessions"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="UnlockSessions"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="KillSession"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="KillUser"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="TerminateSession"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="TerminateUser"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="TerminateSeat"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="PowerOff"/>
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="CanHybridSleep"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="ScheduleShutdown"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Manager"
+                       send_member="CancelScheduledShutdown"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="CanRebootToFirmwareSetup"/>
                        send_interface="org.freedesktop.login1.Manager"
                        send_member="FlushDevices"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Seat"
+                       send_member="Terminate"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Seat"
                        send_member="ActivateSession"/>
                        send_interface="org.freedesktop.login1.Seat"
                        send_member="SwitchToNext"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Session"
+                       send_member="Terminate"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Session"
                        send_member="Activate"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Session"
+                       send_member="Lock"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Session"
+                       send_member="Unlock"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Session"
                        send_member="SetIdleHint"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Session"
+                       send_member="Kill"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Session"
                        send_member="TakeControl"/>
                        send_interface="org.freedesktop.login1.Session"
                        send_member="PauseDeviceComplete"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.User"
+                       send_member="Terminate"/>
+
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.User"
+                       send_member="Kill"/>
+
                 <allow receive_sender="org.freedesktop.login1"/>
         </policy>
 
index 7813a0bcc72ac354a2a7523a5f037e22c2690846..dc42ffdc52aebe253e5d985813bc6dcce4fb7d44 100644 (file)
@@ -55,17 +55,12 @@ static int property_get_id(
                 sd_bus_error *error) {
 
         Machine *m = userdata;
-        int r;
 
         assert(bus);
         assert(reply);
         assert(m);
 
-        r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
-        if (r < 0)
-                return r;
-
-        return 1;
+        return sd_bus_message_append_array(reply, 'y', &m->id, 16);
 }
 
 static int property_get_state(
@@ -104,7 +99,6 @@ static int property_get_netif(
                 sd_bus_error *error) {
 
         Machine *m = userdata;
-        int r;
 
         assert(bus);
         assert(reply);
@@ -112,11 +106,7 @@ static int property_get_netif(
 
         assert_cc(sizeof(int) == sizeof(int32_t));
 
-        r = sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
-        if (r < 0)
-                return r;
-
-        return 1;
+        return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
 }
 
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
index 0e971a6789ad1d8ef9bcecf065af98c7a5e0a563..3637815fc9b15e79b35cbc4d209facd48edffe0f 100644 (file)
 #include "bus-common-errors.h"
 #include "cgroup-util.h"
 #include "btrfs-util.h"
+#include "formats-util.h"
+#include "process-util.h"
 #include "machine-image.h"
 #include "machine-pool.h"
 #include "image-dbus.h"
 #include "machined.h"
 #include "machine-dbus.h"
-#include "formats-util.h"
 
 static int property_get_pool_path(
                 sd_bus *bus,
@@ -840,6 +841,230 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu
         return bus_image_method_set_limit(message, i, error);
 }
 
+static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_fclose_ FILE *f = NULL;
+        Manager *m = userdata;
+        const char *name, *p;
+        Machine *machine;
+        uint32_t uid;
+        int r;
+
+        r = sd_bus_message_read(message, "su", &name, &uid);
+        if (r < 0)
+                return r;
+
+        if (UID_IS_INVALID(uid))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
+
+        machine = hashmap_get(m->machines, name);
+        if (!machine)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+        p = procfs_file_alloca(machine->leader, "uid_map");
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        for (;;) {
+                uid_t uid_base, uid_shift, uid_range, converted;
+                int k;
+
+                errno = 0;
+                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
+                if (k < 0 && feof(f))
+                        break;
+                if (k != 3) {
+                        if (ferror(f) && errno != 0)
+                                return -errno;
+
+                        return -EIO;
+                }
+
+                if (uid < uid_base || uid >= uid_base + uid_range)
+                        continue;
+
+                converted = uid - uid_base + uid_shift;
+                if (UID_IS_INVALID(converted))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
+
+                return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
+        }
+
+        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name);
+}
+
+static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        Machine *machine;
+        uid_t uid;
+        Iterator i;
+        int r;
+
+        r = sd_bus_message_read(message, "u", &uid);
+        if (r < 0)
+                return r;
+        if (UID_IS_INVALID(uid))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
+        if (uid < 0x10000)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid);
+
+        HASHMAP_FOREACH(machine, m->machines, i) {
+                _cleanup_fclose_ FILE *f = NULL;
+                char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+
+                xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader);
+                f = fopen(p, "re");
+                if (!f) {
+                        log_warning_errno(errno, "Failed top open %s, ignoring,", p);
+                        continue;
+                }
+
+                for (;;) {
+                        _cleanup_free_ char *o = NULL;
+                        uid_t uid_base, uid_shift, uid_range, converted;
+                        int k;
+
+                        errno = 0;
+                        k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
+                        if (k < 0 && feof(f))
+                                break;
+                        if (k != 3) {
+                                if (ferror(f) && errno != 0)
+                                        return -errno;
+
+                                return -EIO;
+                        }
+
+                        if (uid < uid_shift || uid >= uid_shift + uid_range)
+                                continue;
+
+                        converted = (uid - uid_shift + uid_base);
+                        if (UID_IS_INVALID(converted))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
+
+                        o = machine_bus_path(machine);
+                        if (!o)
+                                return -ENOMEM;
+
+                        return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
+                }
+        }
+
+        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid);
+}
+
+static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) {
+        _cleanup_fclose_ FILE *f = NULL;
+        Manager *m = groupdata;
+        const char *name, *p;
+        Machine *machine;
+        uint32_t gid;
+        int r;
+
+        r = sd_bus_message_read(message, "su", &name, &gid);
+        if (r < 0)
+                return r;
+
+        if (GID_IS_INVALID(gid))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
+
+        machine = hashmap_get(m->machines, name);
+        if (!machine)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+        p = procfs_file_alloca(machine->leader, "gid_map");
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        for (;;) {
+                gid_t gid_base, gid_shift, gid_range, converted;
+                int k;
+
+                errno = 0;
+                k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range);
+                if (k < 0 && feof(f))
+                        break;
+                if (k != 3) {
+                        if (ferror(f) && errno != 0)
+                                return -errno;
+
+                        return -EIO;
+                }
+
+                if (gid < gid_base || gid >= gid_base + gid_range)
+                        continue;
+
+                converted = gid - gid_base + gid_shift;
+                if (GID_IS_INVALID(converted))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
+
+                return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
+        }
+
+        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name);
+}
+
+static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) {
+        Manager *m = groupdata;
+        Machine *machine;
+        gid_t gid;
+        Iterator i;
+        int r;
+
+        r = sd_bus_message_read(message, "u", &gid);
+        if (r < 0)
+                return r;
+        if (GID_IS_INVALID(gid))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
+        if (gid < 0x10000)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid);
+
+        HASHMAP_FOREACH(machine, m->machines, i) {
+                _cleanup_fclose_ FILE *f = NULL;
+                char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+
+                xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader);
+                f = fopen(p, "re");
+                if (!f) {
+                        log_warning_errno(errno, "Failed top open %s, ignoring,", p);
+                        continue;
+                }
+
+                for (;;) {
+                        _cleanup_free_ char *o = NULL;
+                        gid_t gid_base, gid_shift, gid_range, converted;
+                        int k;
+
+                        errno = 0;
+                        k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range);
+                        if (k < 0 && feof(f))
+                                break;
+                        if (k != 3) {
+                                if (ferror(f) && errno != 0)
+                                        return -errno;
+
+                                return -EIO;
+                        }
+
+                        if (gid < gid_shift || gid >= gid_shift + gid_range)
+                                continue;
+
+                        converted = (gid - gid_shift + gid_base);
+                        if (GID_IS_INVALID(converted))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
+
+                        o = machine_bus_path(machine);
+                        if (!o)
+                                return -ENOMEM;
+
+                        return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
+                }
+        }
+
+        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid);
+}
+
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
@@ -869,6 +1094,10 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("MapToMachineGroup", "u", "sou", method_map_to_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_SIGNAL("MachineNew", "so", 0),
         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
         SD_BUS_VTABLE_END
index 93aaf6a377e97dd29aa45e21f3531fc46e1eb52f..d58f01507b2e525e4f7c442aa6b6031b63e468c1 100644 (file)
                        send_interface="org.freedesktop.machine1.Manager"
                        send_member="SetImageLimit"/>
 
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Manager"
+                       send_member="MapFromMachineUser"/>
+
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Manager"
+                       send_member="MapToMachineUser"/>
+
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Manager"
+                       send_member="MapFromMachineGroup"/>
+
+                <allow send_destination="org.freedesktop.machine1"
+                       send_interface="org.freedesktop.machine1.Manager"
+                       send_member="MapToMachineGroup"/>
+
                 <allow send_destination="org.freedesktop.machine1"
                        send_interface="org.freedesktop.machine1.Machine"
                        send_member="GetAddresses"/>
index d446bfa8b3fdb4d929ab0969f823167fe0b73499..4aa301b112a4afbda50b81f65ffd19d510e1ba18 100644 (file)
@@ -270,12 +270,18 @@ static int dhcp_lease_lost(Link *link) {
         if (link->network->dhcp_hostname) {
                 const char *hostname = NULL;
 
-                r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
-                if (r >= 0 && hostname) {
-                        r = link_set_hostname(link, "");
+                if (!link->network->hostname)
+                        r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
+                else
+                        hostname = link->network->hostname;
+
+                if (r >= 0 || hostname) {
+                        r = link_set_hostname(link, hostname);
                         if (r < 0)
-                                log_link_error(link,
-                                               "Failed to reset transient hostname");
+                                log_link_error_errno(link, r,
+                                                     "Failed to set transient hostname to '%s': %m",
+                                                     hostname);
+
                 }
         }
 
@@ -464,8 +470,12 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
         if (link->network->dhcp_hostname) {
                 const char *hostname;
 
-                r = sd_dhcp_lease_get_hostname(lease, &hostname);
-                if (r >= 0) {
+                if (!link->network->hostname)
+                        r = sd_dhcp_lease_get_hostname(lease, &hostname);
+                else
+                        hostname = link->network->hostname;
+
+                if (r >= 0 || hostname) {
                         r = link_set_hostname(link, hostname);
                         if (r < 0)
                                 log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
@@ -616,14 +626,19 @@ int dhcp4_configure(Link *link) {
 
         if (link->network->dhcp_sendhost) {
                 _cleanup_free_ char *hostname = NULL;
+                const char *hn = NULL;
+
+                if (!link->network->hostname)  {
+                        hostname = gethostname_malloc();
+                        if (!hostname)
+                                return -ENOMEM;
 
-                hostname = gethostname_malloc();
-                if (!hostname)
-                        return -ENOMEM;
+                        hn = hostname;
+                } else
+                        hn = link->network->hostname;
 
-                if (!is_localhost(hostname)) {
-                        r = sd_dhcp_client_set_hostname(link->dhcp_client,
-                                                        hostname);
+                if (!is_localhost(hn)) {
+                        r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
                         if (r < 0)
                                 return r;
                 }
index 5607cf470e8a1a26a4aba8d693a10d7355235fb1..f20f68b482fb2cc5cf88556853621feca3898e22 100644 (file)
@@ -846,9 +846,6 @@ static int link_set_bridge(Link *link) {
         assert(link);
         assert(link->network);
 
-        if(link->network->cost == 0)
-                return 0;
-
         r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
@@ -861,6 +858,26 @@ static int link_set_bridge(Link *link) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m");
 
+        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, !link->network->use_bpdu);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_GUARD attribute: %m");
+
+        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MODE attribute: %m");
+
+        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m");
+
+        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, !link->network->allow_port_to_be_root);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m");
+
+        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m");
+
         if(link->network->cost != 0) {
                 r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost);
                 if (r < 0)
@@ -1495,7 +1512,7 @@ static int link_set_ipv4_forward(Link *link) {
         p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding");
         v = one_zero(link_ipv4_forward_enabled(link));
 
-        r = write_string_file_no_create(p, v);
+        r = write_string_file(p, v, 0);
         if (r < 0) {
                 /* If the right value is set anyway, don't complain */
                 if (verify_one_line_file(p, v) > 0)
@@ -1524,7 +1541,7 @@ static int link_set_ipv6_forward(Link *link) {
         p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding");
         v = one_zero(link_ipv6_forward_enabled(link));
 
-        r = write_string_file_no_create(p, v);
+        r = write_string_file(p, v, 0);
         if (r < 0) {
                 /* If the right value is set anyway, don't complain */
                 if (verify_one_line_file(p, v) > 0)
@@ -1553,7 +1570,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
         p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr");
         xsprintf(buf, "%u", link->network->ipv6_privacy_extensions);
 
-        r = write_string_file_no_create(p, buf);
+        r = write_string_file(p, buf, 0);
         if (r < 0) {
                 /* If the right value is set anyway, don't complain */
                 if (verify_one_line_file(p, buf) > 0)
index 66ed2e013cde1f0c4c1243bf19ccbdc5c6ea613b..7e46293a0608712eaa522ca7e50af050b49531b5 100644 (file)
@@ -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)
@@ -36,6 +37,8 @@ Tunnel.TOS,                  config_parse_unsigned,              0,
 Tunnel.TTL,                  config_parse_unsigned,              0,                             offsetof(Tunnel, ttl)
 Tunnel.DiscoverPathMTU,      config_parse_bool,                  0,                             offsetof(Tunnel, pmtudisc)
 Tunnel.Mode,                 config_parse_ip6tnl_mode,           0,                             offsetof(Tunnel, ip6tnl_mode)
+Tunnel.IPv6FlowLabel,        config_parse_ipv6_flowlabel,        0,                             offsetof(Tunnel, ipv6_flowlabel)
+Tunnel.CopyDSCP,             config_parse_bool,                  0,                             offsetof(Tunnel, copy_dscp)
 Peer.Name,                   config_parse_ifname,                0,                             offsetof(Veth, ifname_peer)
 Peer.MACAddress,             config_parse_hwaddr,                0,                             offsetof(Veth, mac_peer)
 VXLAN.Id,                    config_parse_uint64,                0,                             offsetof(VxLan, id)
@@ -59,6 +62,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.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)
index c2c564935c0ae27842064c9ff02c26bb3b6ed88e..e17de793ce64afd3d30e1a1f6f06582714612a87 100644 (file)
@@ -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,
index d61efc16d4e436a6615fd3a996f1f88c6d0d347c..c491bfa3129aca5e42056c29ee6ed77486dfbe7b 100644 (file)
@@ -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_;
index 5533fb5c7bb8b65cfc5fe47f01e4b456a56d5a7d..7fd9ef584bac0dac46bd823e5368f9233b51f41e 100644 (file)
@@ -33,6 +33,7 @@
 #include "conf-parser.h"
 
 #define DEFAULT_TNL_HOP_LIMIT   64
+#define IP6_FLOWINFO_FLOWLABEL  htonl(0x000FFFFF)
 
 static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = {
         [NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6",
@@ -184,6 +185,16 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m");
 
+        if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) {
+                r = sd_netlink_message_append_u32(m, IFLA_GRE_FLOWINFO, t->ipv6_flowlabel);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLOWINFO attribute: %m");
+        }
+
+        r = sd_netlink_message_append_u32(m, IFLA_GRE_FLAGS, t->flags);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLAGS attribute: %m");
+
         return r;
 }
 
@@ -264,6 +275,19 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m");
 
+        if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) {
+                r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLOWINFO, t->ipv6_flowlabel);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLOWINFO attribute: %m");
+        }
+
+        if (t->copy_dscp)
+                t->flags |= IP6_TNL_F_RCV_DSCP_COPY;
+
+        r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m");
+
         switch (t->ip6tnl_mode) {
         case NETDEV_IP6_TNL_MODE_IP6IP6:
                 proto = IPPROTO_IPV6;
@@ -380,6 +404,52 @@ 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,
+                                const char *section,
+                                unsigned section_line,
+                                const char *lvalue,
+                                int ltype,
+                                const char *rvalue,
+                                void *data,
+                                void *userdata) {
+        IPv6FlowLabel *ipv6_flowlabel = data;
+        Tunnel *t = userdata;
+        IPv6FlowLabel s;
+        int k = 0;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(ipv6_flowlabel);
+
+        s = ipv6_flowlabel_from_string(rvalue);
+        if (s != _NETDEV_IPV6_FLOWLABEL_INVALID) {
+                *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);
+                if (r >= 0) {
+                        if (k > 0xFFFFF)
+                                log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
+                        else {
+                                *ipv6_flowlabel = htonl(k) & IP6_FLOWINFO_FLOWLABEL;
+                                t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
+                        }
+                }
+        }
+
+        return 0;
+}
+
 static void ipip_init(NetDev *n) {
         Tunnel *t = IPIP(n);
 
@@ -452,6 +522,7 @@ static void ip6tnl_init(NetDev *n) {
         t->ttl = DEFAULT_TNL_HOP_LIMIT;
         t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
         t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID;
+        t->ipv6_flowlabel = _NETDEV_IPV6_FLOWLABEL_INVALID;
 }
 
 const NetDevVTable ipip_vtable = {
index 88f57ac1052a6f3c5fb9e2b9eef28440f6010646..1fd2b94ae16785316562963903ffd3b2bd094b6d 100644 (file)
@@ -33,6 +33,12 @@ typedef enum Ip6TnlMode {
         _NETDEV_IP6_TNL_MODE_INVALID = -1,
 } Ip6TnlMode;
 
+typedef enum IPv6FlowLabel {
+        NETDEV_IPV6_FLOWLABEL_INHERIT = 0xFFFFF + 1,
+        _NETDEV_IPV6_FLOWLABEL_MAX,
+        _NETDEV_IPV6_FLOWLABEL_INVALID = -1,
+} IPv6FlowLabel;
+
 struct Tunnel {
         NetDev meta;
 
@@ -48,8 +54,10 @@ struct Tunnel {
         union in_addr_union remote;
 
         Ip6TnlMode ip6tnl_mode;
+        IPv6FlowLabel ipv6_flowlabel;
 
         bool pmtudisc;
+        bool copy_dscp;
 };
 
 extern const NetDevVTable ipip_vtable;
@@ -70,3 +78,23 @@ int config_parse_ip6tnl_mode(const char *unit, const char *filename,
                              unsigned section_line, const char *lvalue,
                              int ltype, const char *rvalue, void *data,
                              void *userdata);
+
+int config_parse_tunnel_address(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);
+
+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,
+                                int ltype, const char *rvalue, void *data,
+                                void *userdata);
index 378312f09181328170c75c867908dbf78ac85f3c..ba84e802fc015e4304eb35f4f925097b7dacf57e 100644 (file)
@@ -51,6 +51,9 @@ static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) {
         if (t->multi_queue)
                 ifr->ifr_flags |= IFF_MULTI_QUEUE;
 
+        if (t->vnet_hdr)
+                ifr->ifr_flags |= IFF_VNET_HDR;
+
         strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1);
 
         return 0;
index b804875bbb9b39ea6b8932f6a6801683b8d080a0..29f8bb0ea5013a4de549c3861ae6ac9c43c033dc 100644 (file)
@@ -33,6 +33,7 @@ struct TunTap {
         bool one_queue;
         bool multi_queue;
         bool packet_info;
+        bool vnet_hdr;
 };
 
 extern const NetDevVTable tun_vtable;
index fe5254e91ff6523d15da59c5dcccd00689830f4f..e7d1306f134bde39362aecb267604fe5b73e8031 100644 (file)
@@ -53,3 +53,14 @@ struct VxLan {
 };
 
 extern const NetDevVTable vxlan_vtable;
+
+int config_parse_vxlan_group_address(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);
index 6949b403c8dd2b25bb418c8d80af90a852425d37..cd31387b410ac02d389de780a8dabd00ed8d61ed 100644 (file)
@@ -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",
index a004f2fe5fe5f17c104f993792692132b89ec1e5..19fb5bb185ef13f44004e18b674042d1a8e26d84 100644 (file)
@@ -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);
index b5f8f5cfb2c36d977d3108d89e1dddc747a646a5..5717a153273ef21061981e29836be563ba679a92 100644 (file)
@@ -53,11 +53,7 @@ static int property_get_ether_addrs(
                         return r;
         }
 
-        r = sd_bus_message_close_container(reply);
-        if (r < 0)
-                return r;
-
-        return 1;
+        return sd_bus_message_close_container(reply);
 }
 
 const sd_bus_vtable network_vtable[] = {
index 787fc2ff5bec1c05e163cc3b362954cc8328efcf..8735b395812db442b1a9a2570f4491631c42c4a9 100644 (file)
@@ -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
@@ -67,11 +68,17 @@ DHCP.UseHostname,              config_parse_bool,                              0
 DHCP.UseDomains,               config_parse_bool,                              0,                             offsetof(Network, dhcp_domains)
 DHCP.UseRoutes,                config_parse_bool,                              0,                             offsetof(Network, dhcp_routes)
 DHCP.SendHostname,             config_parse_bool,                              0,                             offsetof(Network, dhcp_sendhost)
+DHCP.Hostname,                 config_parse_hostname,                          0,                             offsetof(Network, hostname)
 DHCP.RequestBroadcast,         config_parse_bool,                              0,                             offsetof(Network, dhcp_broadcast)
 DHCP.CriticalConnection,       config_parse_bool,                              0,                             offsetof(Network, dhcp_critical)
 DHCP.VendorClassIdentifier,    config_parse_string,                            0,                             offsetof(Network, dhcp_vendor_class_identifier)
 DHCP.RouteMetric,              config_parse_unsigned,                          0,                             offsetof(Network, dhcp_route_metric)
 Bridge.Cost,                   config_parse_unsigned,                          0,                             offsetof(Network, cost)
+Bridge.UseBPDU,                config_parse_bool,                              0,                             offsetof(Network, use_bpdu)
+Bridge.HairPin,                config_parse_bool,                              0,                             offsetof(Network, hairpin)
+Bridge.FastLeave,              config_parse_bool,                              0,                             offsetof(Network, fast_leave)
+Bridge.AllowPortToBeRoot,      config_parse_bool,                              0,                             offsetof(Network, allow_port_to_be_root)
+Bridge.UnicastFlood,           config_parse_bool,                              0,                             offsetof(Network, unicast_flood)
 BridgeFDB.MACAddress,          config_parse_fdb_hwaddr,                        0,                             0
 BridgeFDB.VLANId,              config_parse_fdb_vlan_id,                       0,                             0
 /* backwards compatibility: do not add new entries to this section */
index a8e9ef909c3eb20df33ff387ba410708250f8c5f..e3593fc0ea9d7ca08fb8d96753b561d2e5cda7a5 100644 (file)
@@ -107,6 +107,10 @@ static int network_load_one(Manager *manager, const char *filename) {
         network->dhcp_route_metric = DHCP_ROUTE_METRIC;
         network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
 
+        network->use_bpdu = true;
+        network->allow_port_to_be_root = true;
+        network->unicast_flood = true;
+
         network->llmnr = LLMNR_SUPPORT_YES;
 
         network->link_local = ADDRESS_FAMILY_IPV6;
@@ -207,6 +211,7 @@ void network_free(Network *network) {
 
         free(network->description);
         free(network->dhcp_vendor_class_identifier);
+        free(network->hostname);
 
         free(network->mac);
 
@@ -423,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);
@@ -809,3 +815,38 @@ int config_parse_ipv6_privacy_extensions(
 
         return 0;
 }
+
+int config_parse_hostname(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) {
+        char **hostname = data;
+        char *hn = NULL;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = config_parse_string(unit, filename, line, section, section_line,
+                                lvalue, ltype, rvalue, &hn, userdata);
+        if (r < 0)
+                return r;
+
+        if (!hostname_is_valid(hn)) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "hostname is not valid, ignoring assignment: %s", rvalue);
+
+                free(hn);
+                return 0;
+        }
+
+        *hostname = hn;
+
+        return 0;
+}
index 32c31fdf3d33a2289ce1f1036f979238bc065366..d958b4877149d875e009df87eb12e3e5296fe828 100644 (file)
@@ -66,7 +66,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hiq", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "+hi:q", options, NULL)) >= 0)
 
                 switch (c) {
 
index f98c640822ca02a90983443f3d1d26b7a2bd993b..a285a4b08ff86a75650dccb5357edb563f2e5a0e 100644 (file)
@@ -133,6 +133,7 @@ struct Network {
         AddressFamilyBoolean dhcp;
         DCHPClientIdentifier dhcp_client_identifier;
         char *dhcp_vendor_class_identifier;
+        char *hostname;
         bool dhcp_dns;
         bool dhcp_ntp;
         bool dhcp_mtu;
@@ -149,6 +150,11 @@ struct Network {
 
         bool dhcp_server;
 
+        bool use_bpdu;
+        bool hairpin;
+        bool fast_leave;
+        bool allow_port_to_be_root;
+        bool unicast_flood;
         unsigned cost;
 
         AddressFamilyBoolean ip_forward;
@@ -319,28 +325,6 @@ int config_parse_tunnel(const char *unit,
                         void *data,
                         void *userdata);
 
-int config_parse_tunnel_address(const char *unit,
-                                const char *filename,
-                                unsigned line,
-                                const char *section,
-                                unsigned section_line,
-                                const char *lvalue,
-                                int ltype,
-                                const char *rvalue,
-                                void *data,
-                                void *userdata);
-
-int config_parse_vxlan_group_address(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);
-
 extern const sd_bus_vtable network_vtable[];
 
 int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
@@ -473,3 +457,7 @@ const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
 IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
 
 int config_parse_ipv6_privacy_extensions(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);
+
+
+/* Hostname */
+int config_parse_hostname(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);
index 198de3097df1dcc7753567197d04452793165494..65b9a5071b9bcac040c4a5b06a5f008b810bfa41 100644 (file)
@@ -1702,7 +1702,7 @@ static int setup_boot_id(const char *dest) {
 
         id128_format_as_uuid(rnd, as_uuid);
 
-        r = write_string_file(from, as_uuid);
+        r = write_string_file(from, as_uuid, WRITE_STRING_FILE_CREATE);
         if (r < 0)
                 return log_error_errno(r, "Failed to write boot id: %m");
 
@@ -1785,15 +1785,13 @@ static int setup_pts(const char *dest) {
 #ifdef HAVE_SELINUX
         if (arg_selinux_apifs_context)
                 (void) asprintf(&options,
-                                "newinstance,ptmxmode=0666,mode=620,uid=" UID_FMT ",gid=" GID_FMT ",context=\"%s\"",
-                                arg_uid_shift,
+                                "newinstance,ptmxmode=0666,mode=620,gid=" GID_FMT ",context=\"%s\"",
                                 arg_uid_shift + TTY_GID,
                                 arg_selinux_apifs_context);
         else
 #endif
                 (void) asprintf(&options,
-                                "newinstance,ptmxmode=0666,mode=620,uid=" UID_FMT ",gid=" GID_FMT,
-                                arg_uid_shift,
+                                "newinstance,ptmxmode=0666,mode=620,gid=" GID_FMT,
                                 arg_uid_shift + TTY_GID);
 
         if (!options)
@@ -2507,7 +2505,7 @@ static int reset_audit_loginuid(void) {
         if (streq(p, "4294967295"))
                 return 0;
 
-        r = write_string_file("/proc/self/loginuid", "4294967295");
+        r = write_string_file("/proc/self/loginuid", "4294967295", 0);
         if (r < 0) {
                 log_error_errno(r,
                                 "Failed to reset audit login UID. This probably means that your kernel is too\n"
@@ -4447,13 +4445,13 @@ static int setup_uid_map(pid_t pid) {
 
         xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid);
         xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range);
-        r = write_string_file(uid_map, line);
+        r = write_string_file(uid_map, line, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to write UID map: %m");
 
         /* We always assign the same UID and GID ranges */
         xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid);
-        r = write_string_file(uid_map, line);
+        r = write_string_file(uid_map, line, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to write GID map: %m");
 
index f712033e6c8fec5b632416820449fec78279d8eb..cdec83d074064c82c7117250c25d0f744e78582f 100644 (file)
 #include "util.h"
 #include "nss-util.h"
 #include "bus-util.h"
+#include "bus-common-errors.h"
 #include "in-addr-util.h"
 
 NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
+NSS_GETPW_PROTOTYPES(mymachines);
+NSS_GETGR_PROTOTYPES(mymachines);
 
 static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
         unsigned c = 0;
@@ -380,4 +383,319 @@ fail:
         return NSS_STATUS_UNAVAIL;
 }
 
-NSS_GETHOSTBYNAME_FALLBACKS(mymachines)
+NSS_GETHOSTBYNAME_FALLBACKS(mymachines);
+
+enum nss_status _nss_mymachines_getpwnam_r(
+                const char *name,
+                struct passwd *pwd,
+                char *buffer, size_t buflen,
+                int *errnop) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
+        _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+        const char *p, *e, *machine;
+        uint32_t mapped;
+        uid_t uid;
+        size_t l;
+        int r;
+
+        assert(name);
+        assert(pwd);
+
+        p = startswith(name, "vu-");
+        if (!p)
+                goto not_found;
+
+        e = strrchr(p, '-');
+        if (!e || e == p)
+                goto not_found;
+
+        r = parse_uid(e + 1, &uid);
+        if (r < 0)
+                goto not_found;
+
+        machine = strndupa(p, e - p);
+        if (!machine_name_is_valid(machine))
+                goto not_found;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_call_method(bus,
+                               "org.freedesktop.machine1",
+                               "/org/freedesktop/machine1",
+                               "org.freedesktop.machine1.Manager",
+                               "MapFromMachineUser",
+                               &error,
+                               &reply,
+                               "su",
+                               machine, (uint32_t) uid);
+        if (r < 0) {
+                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
+                        goto not_found;
+
+                goto fail;
+        }
+
+        r = sd_bus_message_read(reply, "u", &mapped);
+        if (r < 0)
+                goto fail;
+
+        l = strlen(name);
+        if (buflen < l+1) {
+                *errnop = ENOMEM;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        memcpy(buffer, name, l+1);
+
+        pwd->pw_name = buffer;
+        pwd->pw_uid = mapped;
+        pwd->pw_gid = 65534; /* nobody */
+        pwd->pw_gecos = buffer;
+        pwd->pw_passwd = (char*) "*"; /* locked */
+        pwd->pw_dir = (char*) "/";
+        pwd->pw_shell = (char*) "/sbin/nologin";
+
+        *errnop = 0;
+        return NSS_STATUS_SUCCESS;
+
+not_found:
+        *errnop = 0;
+        return NSS_STATUS_NOTFOUND;
+
+fail:
+        *errnop = -r;
+        return NSS_STATUS_UNAVAIL;
+}
+
+enum nss_status _nss_mymachines_getpwuid_r(
+                uid_t uid,
+                struct passwd *pwd,
+                char *buffer, size_t buflen,
+                int *errnop) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
+        _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+        const char *machine, *object;
+        uint32_t mapped;
+        int r;
+
+        if (UID_IS_INVALID(uid)) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        /* We consider all uids < 65536 host uids */
+        if (uid < 0x10000)
+                goto not_found;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_call_method(bus,
+                               "org.freedesktop.machine1",
+                               "/org/freedesktop/machine1",
+                               "org.freedesktop.machine1.Manager",
+                               "MapToMachineUser",
+                               &error,
+                               &reply,
+                               "u",
+                               (uint32_t) uid);
+        if (r < 0) {
+                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
+                        goto not_found;
+
+                goto fail;
+        }
+
+        r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped);
+        if (r < 0)
+                goto fail;
+
+        if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
+                *errnop = ENOMEM;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        pwd->pw_name = buffer;
+        pwd->pw_uid = uid;
+        pwd->pw_gid = 65534; /* nobody */
+        pwd->pw_gecos = buffer;
+        pwd->pw_passwd = (char*) "*"; /* locked */
+        pwd->pw_dir = (char*) "/";
+        pwd->pw_shell = (char*) "/sbin/nologin";
+
+        *errnop = 0;
+        return NSS_STATUS_SUCCESS;
+
+not_found:
+        *errnop = 0;
+        return NSS_STATUS_NOTFOUND;
+
+fail:
+        *errnop = -r;
+        return NSS_STATUS_UNAVAIL;
+}
+
+enum nss_status _nss_mymachines_getgrnam_r(
+                const char *name,
+                struct group *gr,
+                char *buffer, size_t buflen,
+                int *errnop) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
+        _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+        const char *p, *e, *machine;
+        uint32_t mapped;
+        uid_t gid;
+        size_t l;
+        int r;
+
+        assert(name);
+        assert(gr);
+
+        p = startswith(name, "vg-");
+        if (!p)
+                goto not_found;
+
+        e = strrchr(p, '-');
+        if (!e || e == p)
+                goto not_found;
+
+        r = parse_gid(e + 1, &gid);
+        if (r < 0)
+                goto not_found;
+
+        machine = strndupa(p, e - p);
+        if (!machine_name_is_valid(machine))
+                goto not_found;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_call_method(bus,
+                               "org.freedesktop.machine1",
+                               "/org/freedesktop/machine1",
+                               "org.freedesktop.machine1.Manager",
+                               "MapFromMachineGroup",
+                               &error,
+                               &reply,
+                               "su",
+                               machine, (uint32_t) gid);
+        if (r < 0) {
+                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
+                        goto not_found;
+
+                goto fail;
+        }
+
+        r = sd_bus_message_read(reply, "u", &mapped);
+        if (r < 0)
+                goto fail;
+
+        l = sizeof(char*) + strlen(name) + 1;
+        if (buflen < l) {
+                *errnop = ENOMEM;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        memzero(buffer, sizeof(char*));
+        strcpy(buffer + sizeof(char*), name);
+
+        gr->gr_name = buffer + sizeof(char*);
+        gr->gr_gid = gid;
+        gr->gr_passwd = (char*) "*"; /* locked */
+        gr->gr_mem = (char**) buffer;
+
+        *errnop = 0;
+        return NSS_STATUS_SUCCESS;
+
+not_found:
+        *errnop = 0;
+        return NSS_STATUS_NOTFOUND;
+
+fail:
+        *errnop = -r;
+        return NSS_STATUS_UNAVAIL;
+}
+
+enum nss_status _nss_mymachines_getgrgid_r(
+                gid_t gid,
+                struct group *gr,
+                char *buffer, size_t buflen,
+                int *errnop) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
+        _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+        const char *machine, *object;
+        uint32_t mapped;
+        int r;
+
+        if (GID_IS_INVALID(gid)) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        /* We consider all gids < 65536 host gids */
+        if (gid < 0x10000)
+                goto not_found;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_call_method(bus,
+                               "org.freedesktop.machine1",
+                               "/org/freedesktop/machine1",
+                               "org.freedesktop.machine1.Manager",
+                               "MapToMachineGroup",
+                               &error,
+                               &reply,
+                               "u",
+                               (uint32_t) gid);
+        if (r < 0) {
+                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
+                        goto not_found;
+
+                goto fail;
+        }
+
+        r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped);
+        if (r < 0)
+                goto fail;
+
+        if (buflen < sizeof(char*) + 1) {
+                *errnop = ENOMEM;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        memzero(buffer, sizeof(char*));
+        if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) {
+                *errnop = ENOMEM;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        gr->gr_name = buffer + sizeof(char*);
+        gr->gr_gid = gid;
+        gr->gr_passwd = (char*) "*"; /* locked */
+        gr->gr_mem = (char**) buffer;
+
+        *errnop = 0;
+        return NSS_STATUS_SUCCESS;
+
+not_found:
+        *errnop = 0;
+        return NSS_STATUS_NOTFOUND;
+
+fail:
+        *errnop = -r;
+        return NSS_STATUS_UNAVAIL;
+}
index f80b51c1aaa0b5f0dcfa7cc0b10979041c84cde0..0728ac3ba73483357c98f5a877685858b246797b 100644 (file)
@@ -13,5 +13,9 @@ global:
         _nss_mymachines_gethostbyname2_r;
         _nss_mymachines_gethostbyname3_r;
         _nss_mymachines_gethostbyname4_r;
+        _nss_mymachines_getpwnam_r;
+        _nss_mymachines_getpwuid_r;
+        _nss_mymachines_getgrnam_r;
+        _nss_mymachines_getgrgid_r;
 local: *;
 };
diff --git a/src/python-systemd/.gitignore b/src/python-systemd/.gitignore
deleted file mode 100644 (file)
index 4124b7a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/id128-constants.h
-*.py[oc]
diff --git a/src/python-systemd/Makefile b/src/python-systemd/Makefile
deleted file mode 120000 (symlink)
index d0b0e8e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/python-systemd/__init__.py b/src/python-systemd/__init__.py
deleted file mode 100644 (file)
index 0d56b99..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#  -*- Mode: python; indent-tabs-mode: nil -*- */
-#
-#  This file is part of systemd.
-#
-#  Copyright 2012 David Strauss
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c
deleted file mode 100644 (file)
index 7c5f1b2..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#define PY_SSIZE_T_CLEAN
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#include <Python.h>
-#pragma GCC diagnostic pop
-
-#include <stdbool.h>
-#include <assert.h>
-#include <sys/socket.h>
-
-#include "systemd/sd-daemon.h"
-#include "pyutil.h"
-#include "macro.h"
-
-PyDoc_STRVAR(module__doc__,
-        "Python interface to the libsystemd-daemon library.\n\n"
-        "Provides _listen_fds, notify, booted, and is_* functions\n"
-        "which wrap sd_listen_fds, sd_notify, sd_booted, sd_is_* and\n"
-        "useful for socket activation and checking if the system is\n"
-        "running under systemd."
-);
-
-PyDoc_STRVAR(booted__doc__,
-             "booted() -> bool\n\n"
-             "Return True iff this system is running under systemd.\n"
-             "Wraps sd_daemon_booted(3)."
-);
-
-static PyObject* booted(PyObject *self, PyObject *args) {
-        int r;
-        assert(args == NULL);
-
-        r = sd_booted();
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-PyDoc_STRVAR(notify__doc__,
-             "notify(status, unset_environment=False) -> bool\n\n"
-             "Send a message to the init system about a status change.\n"
-             "Wraps sd_notify(3).");
-
-static PyObject* notify(PyObject *self, PyObject *args, PyObject *keywds) {
-        int r;
-        const char* msg;
-        int unset = false;
-
-        static const char* const kwlist[] = {
-                "status",
-                "unset_environment",
-                NULL,
-        };
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
-        if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|p:notify",
-                                         (char**) kwlist, &msg, &unset))
-                return NULL;
-#else
-        PyObject *obj = NULL;
-        if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|O:notify",
-                                         (char**) kwlist, &msg, &obj))
-                return NULL;
-        if (obj != NULL)
-                unset = PyObject_IsTrue(obj);
-        if (unset < 0)
-                return NULL;
-#endif
-
-        r = sd_notify(unset, msg);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-
-PyDoc_STRVAR(listen_fds__doc__,
-             "_listen_fds(unset_environment=True) -> int\n\n"
-             "Return the number of descriptors passed to this process by the init system\n"
-             "as part of the socket-based activation logic.\n"
-             "Wraps sd_listen_fds(3)."
-);
-
-static PyObject* listen_fds(PyObject *self, PyObject *args, PyObject *keywds) {
-        int r;
-        int unset = true;
-
-        static const char* const kwlist[] = {"unset_environment", NULL};
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
-        if (!PyArg_ParseTupleAndKeywords(args, keywds, "|p:_listen_fds",
-                                         (char**) kwlist, &unset))
-                return NULL;
-#else
-        PyObject *obj = NULL;
-        if (!PyArg_ParseTupleAndKeywords(args, keywds, "|O:_listen_fds",
-                                         (char**) kwlist, &obj))
-                return NULL;
-        if (obj != NULL)
-                unset = PyObject_IsTrue(obj);
-        if (unset < 0)
-                return NULL;
-#endif
-
-        r = sd_listen_fds(unset);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return long_FromLong(r);
-}
-
-PyDoc_STRVAR(is_fifo__doc__,
-             "_is_fifo(fd, path) -> bool\n\n"
-             "Returns True iff the descriptor refers to a FIFO or a pipe.\n"
-             "Wraps sd_is_fifo(3)."
-);
-
-
-static PyObject* is_fifo(PyObject *self, PyObject *args) {
-        int r;
-        int fd;
-        const char *path = NULL;
-
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-        if (!PyArg_ParseTuple(args, "i|O&:_is_fifo",
-                              &fd, Unicode_FSConverter, &path))
-                return NULL;
-#else
-        if (!PyArg_ParseTuple(args, "i|z:_is_fifo", &fd, &path))
-                return NULL;
-#endif
-
-        r = sd_is_fifo(fd, path);
-        if (set_error(r, path, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-
-PyDoc_STRVAR(is_mq__doc__,
-             "_is_mq(fd, path) -> bool\n\n"
-             "Returns True iff the descriptor refers to a POSIX message queue.\n"
-             "Wraps sd_is_mq(3)."
-);
-
-static PyObject* is_mq(PyObject *self, PyObject *args) {
-        int r;
-        int fd;
-        const char *path = NULL;
-
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-        if (!PyArg_ParseTuple(args, "i|O&:_is_mq",
-                              &fd, Unicode_FSConverter, &path))
-                return NULL;
-#else
-        if (!PyArg_ParseTuple(args, "i|z:_is_mq", &fd, &path))
-                return NULL;
-#endif
-
-        r = sd_is_mq(fd, path);
-        if (set_error(r, path, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-
-
-PyDoc_STRVAR(is_socket__doc__,
-             "_is_socket(fd, family=AF_UNSPEC, type=0, listening=-1) -> bool\n\n"
-             "Returns True iff the descriptor refers to a socket.\n"
-             "Wraps sd_is_socket(3).\n\n"
-             "Constants for `family` are defined in the socket module."
-);
-
-static PyObject* is_socket(PyObject *self, PyObject *args) {
-        int r;
-        int fd, family = AF_UNSPEC, type = 0, listening = -1;
-
-        if (!PyArg_ParseTuple(args, "i|iii:_is_socket",
-                              &fd, &family, &type, &listening))
-                return NULL;
-
-        r = sd_is_socket(fd, family, type, listening);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-
-PyDoc_STRVAR(is_socket_inet__doc__,
-             "_is_socket_inet(fd, family=AF_UNSPEC, type=0, listening=-1, port=0) -> bool\n\n"
-             "Wraps sd_is_socket_inet(3).\n\n"
-             "Constants for `family` are defined in the socket module."
-);
-
-static PyObject* is_socket_inet(PyObject *self, PyObject *args) {
-        int r;
-        int fd, family = AF_UNSPEC, type = 0, listening = -1, port = 0;
-
-        if (!PyArg_ParseTuple(args, "i|iiii:_is_socket_inet",
-                              &fd, &family, &type, &listening, &port))
-                return NULL;
-
-        if (port < 0 || port > UINT16_MAX) {
-                set_error(-EINVAL, NULL, "port must fit into uint16_t");
-                return NULL;
-        }
-
-        r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-
-PyDoc_STRVAR(is_socket_unix__doc__,
-             "_is_socket_unix(fd, type, listening, path) -> bool\n\n"
-             "Wraps sd_is_socket_unix(3)."
-);
-
-static PyObject* is_socket_unix(PyObject *self, PyObject *args) {
-        int r;
-        int fd, type = 0, listening = -1;
-        char* path = NULL;
-        Py_ssize_t length = 0;
-
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-        _cleanup_Py_DECREF_ PyObject *_path = NULL;
-        if (!PyArg_ParseTuple(args, "i|iiO&:_is_socket_unix",
-                              &fd, &type, &listening, Unicode_FSConverter, &_path))
-                return NULL;
-        if (_path) {
-                assert(PyBytes_Check(_path));
-                if (PyBytes_AsStringAndSize(_path, &path, &length))
-                        return NULL;
-        }
-#else
-        if (!PyArg_ParseTuple(args, "i|iiz#:_is_socket_unix",
-                              &fd, &type, &listening, &path, &length))
-                return NULL;
-#endif
-
-        r = sd_is_socket_unix(fd, type, listening, path, length);
-        if (set_error(r, path, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-
-static PyMethodDef methods[] = {
-        { "booted", booted, METH_NOARGS, booted__doc__},
-        { "notify", (PyCFunction) notify, METH_VARARGS | METH_KEYWORDS, notify__doc__},
-        { "_listen_fds", (PyCFunction) listen_fds, METH_VARARGS | METH_KEYWORDS, listen_fds__doc__},
-        { "_is_fifo", is_fifo, METH_VARARGS, is_fifo__doc__},
-        { "_is_mq", is_mq, METH_VARARGS, is_mq__doc__},
-        { "_is_socket", is_socket, METH_VARARGS, is_socket__doc__},
-        { "_is_socket_inet", is_socket_inet, METH_VARARGS, is_socket_inet__doc__},
-        { "_is_socket_unix", is_socket_unix, METH_VARARGS, is_socket_unix__doc__},
-        { NULL, NULL, 0, NULL }        /* Sentinel */
-};
-
-#if PY_MAJOR_VERSION < 3
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC init_daemon(void) {
-        PyObject *m;
-
-        m = Py_InitModule3("_daemon", methods, module__doc__);
-        if (m == NULL)
-                return;
-
-        PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START);
-        PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
-}
-REENABLE_WARNING;
-
-#else
-
-static struct PyModuleDef module = {
-        PyModuleDef_HEAD_INIT,
-        "_daemon", /* name of module */
-        module__doc__, /* module documentation, may be NULL */
-        0, /* size of per-interpreter state of the module */
-        methods
-};
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC PyInit__daemon(void) {
-        PyObject *m;
-
-        m = PyModule_Create(&module);
-        if (m == NULL)
-                return NULL;
-
-        if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START) ||
-            PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
-                Py_DECREF(m);
-                return NULL;
-        }
-
-        return m;
-}
-REENABLE_WARNING;
-
-#endif
diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c
deleted file mode 100644 (file)
index 456e4a2..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2012 David Strauss <david@davidstrauss.net>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  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 <Python.h>
-
-#include <alloca.h>
-#include "util.h"
-
-#define SD_JOURNAL_SUPPRESS_LOCATION
-#include "systemd/sd-journal.h"
-
-PyDoc_STRVAR(journal_sendv__doc__,
-             "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n"
-             "Send an entry to the journal."
-);
-
-static PyObject *journal_sendv(PyObject *self, PyObject *args) {
-        struct iovec *iov = NULL;
-        int argc;
-        int i, r;
-        PyObject *ret = NULL;
-        PyObject **encoded;
-
-        /* Allocate an array for the argument strings */
-        argc = PyTuple_Size(args);
-        encoded = alloca0(argc * sizeof(PyObject*));
-
-        /* Allocate sufficient iovector space for the arguments. */
-        iov = alloca(argc * sizeof(struct iovec));
-
-        /* Iterate through the Python arguments and fill the iovector. */
-        for (i = 0; i < argc; ++i) {
-                PyObject *item = PyTuple_GetItem(args, i);
-                char *stritem;
-                Py_ssize_t length;
-
-                if (PyUnicode_Check(item)) {
-                        encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict");
-                        if (encoded[i] == NULL)
-                                goto out;
-                        item = encoded[i];
-                }
-                if (PyBytes_AsStringAndSize(item, &stritem, &length))
-                        goto out;
-
-                iov[i].iov_base = stritem;
-                iov[i].iov_len = length;
-        }
-
-        /* Send the iovector to the journal. */
-        r = sd_journal_sendv(iov, argc);
-        if (r < 0) {
-                errno = -r;
-                PyErr_SetFromErrno(PyExc_IOError);
-                goto out;
-        }
-
-        /* End with success. */
-        Py_INCREF(Py_None);
-        ret = Py_None;
-
-out:
-        for (i = 0; i < argc; ++i)
-                Py_XDECREF(encoded[i]);
-
-        return ret;
-}
-
-PyDoc_STRVAR(journal_stream_fd__doc__,
-             "stream_fd(identifier, priority, level_prefix) -> fd\n\n"
-             "Open a stream to journal by calling sd_journal_stream_fd(3)."
-);
-
-static PyObject* journal_stream_fd(PyObject *self, PyObject *args) {
-        const char* identifier;
-        int priority, level_prefix;
-        int fd;
-
-        if (!PyArg_ParseTuple(args, "sii:stream_fd",
-                              &identifier, &priority, &level_prefix))
-                return NULL;
-
-        fd = sd_journal_stream_fd(identifier, priority, level_prefix);
-        if (fd < 0) {
-                errno = -fd;
-                return PyErr_SetFromErrno(PyExc_IOError);
-        }
-
-        return PyLong_FromLong(fd);
-}
-
-static PyMethodDef methods[] = {
-        { "sendv",  journal_sendv, METH_VARARGS, journal_sendv__doc__ },
-        { "stream_fd", journal_stream_fd, METH_VARARGS, journal_stream_fd__doc__ },
-        { NULL, NULL, 0, NULL }        /* Sentinel */
-};
-
-#if PY_MAJOR_VERSION < 3
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC init_journal(void) {
-        PyObject *m;
-
-        m = Py_InitModule("_journal", methods);
-        if (m == NULL)
-                return;
-
-        PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
-}
-REENABLE_WARNING;
-
-#else
-
-static struct PyModuleDef module = {
-        PyModuleDef_HEAD_INIT,
-        "_journal", /* name of module */
-        NULL, /* module documentation, may be NULL */
-        -1, /* size of per-interpreter state of the module */
-        methods
-};
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC PyInit__journal(void) {
-        PyObject *m;
-
-        m = PyModule_Create(&module);
-        if (m == NULL)
-                return NULL;
-
-        if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
-                Py_DECREF(m);
-                return NULL;
-        }
-
-        return m;
-}
-REENABLE_WARNING;
-
-#endif
diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c
deleted file mode 100644 (file)
index 3a56126..0000000
+++ /dev/null
@@ -1,1106 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Steven Hiscocks, Zbigniew JÄ™drzejewski-Szmek
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <Python.h>
-#include <structmember.h>
-#include <datetime.h>
-#include <time.h>
-#include <stdio.h>
-
-#include "systemd/sd-journal.h"
-
-#include "pyutil.h"
-#include "macro.h"
-#include "util.h"
-#include "strv.h"
-#include "build.h"
-
-typedef struct {
-        PyObject_HEAD
-        sd_journal *j;
-} Reader;
-static PyTypeObject ReaderType;
-
-PyDoc_STRVAR(module__doc__,
-             "Class to reads the systemd journal similar to journalctl.");
-
-
-#if PY_MAJOR_VERSION >= 3
-static PyTypeObject MonotonicType;
-
-PyDoc_STRVAR(MonotonicType__doc__,
-             "A tuple of (timestamp, bootid) for holding monotonic timestamps");
-
-static PyStructSequence_Field MonotonicType_fields[] = {
-        {(char*) "timestamp", (char*) "Time"},
-        {(char*) "bootid", (char*) "Unique identifier of the boot"},
-        {} /* Sentinel */
-};
-
-static PyStructSequence_Desc Monotonic_desc = {
-        (char*) "journal.Monotonic",
-        MonotonicType__doc__,
-        MonotonicType_fields,
-        2,
-};
-#endif
-
-/**
- * Convert a Python sequence object into a strv (char**), and
- * None into a NULL pointer.
- */
-static int strv_converter(PyObject* obj, void *_result) {
-        char ***result = _result;
-        Py_ssize_t i, len;
-
-        assert(result);
-
-        if (!obj)
-                return 0;
-
-        if (obj == Py_None) {
-                *result = NULL;
-                return 1;
-        }
-
-        if (!PySequence_Check(obj))
-                return 0;
-
-        len = PySequence_Length(obj);
-        *result = new0(char*, len + 1);
-        if (!*result) {
-                set_error(-ENOMEM, NULL, NULL);
-                return 0;
-        }
-
-        for (i = 0; i < len; i++) {
-                PyObject *item;
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-                int r;
-                PyObject *bytes;
-#endif
-                char *s, *s2;
-
-                item = PySequence_ITEM(obj, i);
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-                r = PyUnicode_FSConverter(item, &bytes);
-                if (r == 0)
-                        goto cleanup;
-
-                s = PyBytes_AsString(bytes);
-#else
-                s = PyString_AsString(item);
-#endif
-                if (!s)
-                        goto cleanup;
-
-                s2 = strdup(s);
-                if (!s2)
-                        log_oom();
-
-                (*result)[i] = s2;
-        }
-
-        return 1;
-
-cleanup:
-        strv_free(*result);
-        *result = NULL;
-
-        return 0;
-}
-
-static void Reader_dealloc(Reader* self) {
-        sd_journal_close(self->j);
-        Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-PyDoc_STRVAR(Reader__doc__,
-             "_Reader([flags | path | files]) -> ...\n\n"
-             "_Reader allows filtering and retrieval of Journal entries.\n"
-             "Note: this is a low-level interface, and probably not what you\n"
-             "want, use systemd.journal.Reader instead.\n\n"
-             "Argument `flags` sets open flags of the journal, which can be one\n"
-             "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
-             "journal on local machine only; RUNTIME_ONLY opens only\n"
-             "volatile journal files; and SYSTEM opens journal files of\n"
-             "system services and the kernel, and CURRENT_USER opens files\n"
-             "of the current user.\n\n"
-             "Argument `path` is the directory of journal files.\n"
-             "Argument `files` is a list of files. Note that\n"
-             "`flags`, `path`, and `files` are exclusive.\n\n"
-             "_Reader implements the context manager protocol: the journal\n"
-             "will be closed when exiting the block.");
-static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) {
-        int flags = 0, r;
-        char *path = NULL;
-        char **files = NULL;
-
-        static const char* const kwlist[] = {"flags", "path", "files", NULL};
-        if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&:__init__", (char**) kwlist,
-                                         &flags, &path, strv_converter, &files))
-                return -1;
-
-        if (!!flags + !!path + !!files > 1) {
-                PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files");
-                return -1;
-        }
-
-        if (!flags)
-                flags = SD_JOURNAL_LOCAL_ONLY;
-
-        Py_BEGIN_ALLOW_THREADS
-        if (path)
-                r = sd_journal_open_directory(&self->j, path, 0);
-        else if (files)
-                r = sd_journal_open_files(&self->j, (const char**) files, 0);
-        else
-                r = sd_journal_open(&self->j, flags);
-        Py_END_ALLOW_THREADS
-
-        return set_error(r, path, "Invalid flags or path");
-}
-
-PyDoc_STRVAR(Reader_fileno__doc__,
-             "fileno() -> int\n\n"
-             "Get a file descriptor to poll for changes in the journal.\n"
-             "This method invokes sd_journal_get_fd().\n"
-             "See man:sd_journal_get_fd(3).");
-static PyObject* Reader_fileno(Reader *self, PyObject *args) {
-        int fd;
-
-        fd = sd_journal_get_fd(self->j);
-        set_error(fd, NULL, NULL);
-        if (fd < 0)
-                return NULL;
-        return long_FromLong(fd);
-}
-
-PyDoc_STRVAR(Reader_reliable_fd__doc__,
-             "reliable_fd() -> bool\n\n"
-             "Returns True iff the journal can be polled reliably.\n"
-             "This method invokes sd_journal_reliable_fd().\n"
-             "See man:sd_journal_reliable_fd(3).");
-static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) {
-        int r;
-
-        r = sd_journal_reliable_fd(self->j);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-        return PyBool_FromLong(r);
-}
-
-PyDoc_STRVAR(Reader_get_events__doc__,
-             "get_events() -> int\n\n"
-             "Returns a mask of poll() events to wait for on the file\n"
-             "descriptor returned by .fileno().\n\n"
-             "See man:sd_journal_get_events(3) for further discussion.");
-static PyObject* Reader_get_events(Reader *self, PyObject *args) {
-        int r;
-
-        r = sd_journal_get_events(self->j);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-        return long_FromLong(r);
-}
-
-PyDoc_STRVAR(Reader_get_timeout__doc__,
-             "get_timeout() -> int or None\n\n"
-             "Returns a timeout value for usage in poll(), the time since the\n"
-             "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
-             "is necessary.\n\n"
-             "The return value must be converted to a relative timeout in\n"
-             "milliseconds if it is to be used as an argument for poll().\n"
-             "See man:sd_journal_get_timeout(3) for further discussion.");
-static PyObject* Reader_get_timeout(Reader *self, PyObject *args) {
-        int r;
-        uint64_t t;
-
-        r = sd_journal_get_timeout(self->j, &t);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        if (t == (uint64_t) -1)
-                Py_RETURN_NONE;
-
-        assert_cc(sizeof(unsigned long long) == sizeof(t));
-        return PyLong_FromUnsignedLongLong(t);
-}
-
-PyDoc_STRVAR(Reader_get_timeout_ms__doc__,
-             "get_timeout_ms() -> int\n\n"
-             "Returns a timeout value suitable for usage in poll(), the value\n"
-             "returned by .get_timeout() converted to relative ms, or -1 if\n"
-             "no timeout is necessary.");
-static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args) {
-        int r;
-        uint64_t t;
-
-        r = sd_journal_get_timeout(self->j, &t);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return absolute_timeout(t);
-}
-
-PyDoc_STRVAR(Reader_close__doc__,
-             "close() -> None\n\n"
-             "Free resources allocated by this Reader object.\n"
-             "This method invokes sd_journal_close().\n"
-             "See man:sd_journal_close(3).");
-static PyObject* Reader_close(Reader *self, PyObject *args) {
-        assert(self);
-        assert(!args);
-
-        sd_journal_close(self->j);
-        self->j = NULL;
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_get_usage__doc__,
-             "get_usage() -> int\n\n"
-             "Returns the total disk space currently used by journal\n"
-             "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
-             "passed when opening the journal this value will only reflect\n"
-             "the size of journal files of the local host, otherwise\n"
-             "of all hosts.\n\n"
-             "This method invokes sd_journal_get_usage().\n"
-             "See man:sd_journal_get_usage(3).");
-static PyObject* Reader_get_usage(Reader *self, PyObject *args) {
-        int r;
-        uint64_t bytes;
-
-        r = sd_journal_get_usage(self->j, &bytes);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        assert_cc(sizeof(unsigned long long) == sizeof(bytes));
-        return PyLong_FromUnsignedLongLong(bytes);
-}
-
-PyDoc_STRVAR(Reader___enter____doc__,
-             "__enter__() -> self\n\n"
-             "Part of the context manager protocol.\n"
-             "Returns self.\n");
-static PyObject* Reader___enter__(PyObject *self, PyObject *args) {
-        assert(self);
-        assert(!args);
-
-        Py_INCREF(self);
-        return self;
-}
-
-PyDoc_STRVAR(Reader___exit____doc__,
-             "__exit__(type, value, traceback) -> None\n\n"
-             "Part of the context manager protocol.\n"
-             "Closes the journal.\n");
-static PyObject* Reader___exit__(Reader *self, PyObject *args) {
-        return Reader_close(self, NULL);
-}
-
-PyDoc_STRVAR(Reader_next__doc__,
-             "next([skip]) -> bool\n\n"
-             "Go to the next log entry. Optional skip value means to go to\n"
-             "the `skip`\\-th log entry.\n"
-             "Returns False if at end of file, True otherwise.");
-static PyObject* Reader_next(Reader *self, PyObject *args) {
-        int64_t skip = 1LL;
-        int r;
-
-        if (!PyArg_ParseTuple(args, "|L:next", &skip))
-                return NULL;
-
-        if (skip == 0LL) {
-                PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
-                return NULL;
-        }
-
-        Py_BEGIN_ALLOW_THREADS
-        if (skip == 1LL)
-                r = sd_journal_next(self->j);
-        else if (skip == -1LL)
-                r = sd_journal_previous(self->j);
-        else if (skip > 1LL)
-                r = sd_journal_next_skip(self->j, skip);
-        else if (skip < -1LL)
-                r = sd_journal_previous_skip(self->j, -skip);
-        else
-                assert_not_reached("should not be here");
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-        return PyBool_FromLong(r);
-}
-
-PyDoc_STRVAR(Reader_previous__doc__,
-             "previous([skip]) -> bool\n\n"
-             "Go to the previous log entry. Optional skip value means to \n"
-             "go to the `skip`\\-th previous log entry.\n"
-             "Returns False if at start of file, True otherwise.");
-static PyObject* Reader_previous(Reader *self, PyObject *args) {
-        int64_t skip = 1LL;
-        if (!PyArg_ParseTuple(args, "|L:previous", &skip))
-                return NULL;
-
-        return PyObject_CallMethod((PyObject *)self, (char*) "_next",
-                                   (char*) "L", -skip);
-}
-
-static int extract(const char* msg, size_t msg_len,
-                   PyObject **key, PyObject **value) {
-        PyObject *k = NULL, *v;
-        const char *delim_ptr;
-
-        delim_ptr = memchr(msg, '=', msg_len);
-        if (!delim_ptr) {
-                PyErr_SetString(PyExc_OSError,
-                                "journal gave us a field without '='");
-                return -1;
-        }
-
-        if (key) {
-                k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
-                if (!k)
-                        return -1;
-        }
-
-        if (value) {
-                v = PyBytes_FromStringAndSize(delim_ptr + 1,
-                                              (const char*) msg + msg_len - (delim_ptr + 1));
-                if (!v) {
-                        Py_XDECREF(k);
-                        return -1;
-                }
-
-                *value = v;
-        }
-
-        if (key)
-                *key = k;
-
-        return 0;
-}
-
-PyDoc_STRVAR(Reader_get__doc__,
-             "get(str) -> str\n\n"
-             "Return data associated with this key in current log entry.\n"
-             "Throws KeyError is the data is not available.");
-static PyObject* Reader_get(Reader *self, PyObject *args) {
-        const char* field;
-        const void* msg;
-        size_t msg_len;
-        PyObject *value;
-        int r;
-
-        assert(self);
-        assert(args);
-
-        if (!PyArg_ParseTuple(args, "s:get", &field))
-                return NULL;
-
-        r = sd_journal_get_data(self->j, field, &msg, &msg_len);
-        if (r == -ENOENT) {
-                PyErr_SetString(PyExc_KeyError, field);
-                return NULL;
-        }
-        if (set_error(r, NULL, "field name is not valid") < 0)
-                return NULL;
-
-        r = extract(msg, msg_len, NULL, &value);
-        if (r < 0)
-                return NULL;
-        return value;
-}
-
-PyDoc_STRVAR(Reader_get_all__doc__,
-             "_get_all() -> dict\n\n"
-             "Return dictionary of the current log entry.");
-static PyObject* Reader_get_all(Reader *self, PyObject *args) {
-        PyObject *dict;
-        const void *msg;
-        size_t msg_len;
-        int r;
-
-        dict = PyDict_New();
-        if (!dict)
-                return NULL;
-
-        SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
-                _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL;
-
-                r = extract(msg, msg_len, &key, &value);
-                if (r < 0)
-                        goto error;
-
-                if (PyDict_Contains(dict, key)) {
-                        PyObject *cur_value = PyDict_GetItem(dict, key);
-
-                        if (PyList_CheckExact(cur_value)) {
-                                r = PyList_Append(cur_value, value);
-                                if (r < 0)
-                                        goto error;
-                        } else {
-                                _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0);
-                                if (!tmp_list)
-                                        goto error;
-
-                                r = PyList_Append(tmp_list, cur_value);
-                                if (r < 0)
-                                        goto error;
-
-                                r = PyList_Append(tmp_list, value);
-                                if (r < 0)
-                                        goto error;
-
-                                r = PyDict_SetItem(dict, key, tmp_list);
-                                if (r < 0)
-                                        goto error;
-                        }
-                } else {
-                        r = PyDict_SetItem(dict, key, value);
-                        if (r < 0)
-                                goto error;
-                }
-        }
-
-        return dict;
-
-error:
-        Py_DECREF(dict);
-        return NULL;
-}
-
-PyDoc_STRVAR(Reader_get_realtime__doc__,
-             "get_realtime() -> int\n\n"
-             "Return the realtime timestamp for the current journal entry\n"
-             "in microseconds.\n\n"
-             "Wraps sd_journal_get_realtime_usec().\n"
-             "See man:sd_journal_get_realtime_usec(3).");
-static PyObject* Reader_get_realtime(Reader *self, PyObject *args) {
-        uint64_t timestamp;
-        int r;
-
-        assert(self);
-        assert(!args);
-
-        r = sd_journal_get_realtime_usec(self->j, &timestamp);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
-        return PyLong_FromUnsignedLongLong(timestamp);
-}
-
-PyDoc_STRVAR(Reader_get_monotonic__doc__,
-             "get_monotonic() -> (timestamp, bootid)\n\n"
-             "Return the monotonic timestamp for the current journal entry\n"
-             "as a tuple of time in microseconds and bootid.\n\n"
-             "Wraps sd_journal_get_monotonic_usec().\n"
-             "See man:sd_journal_get_monotonic_usec(3).");
-static PyObject* Reader_get_monotonic(Reader *self, PyObject *args) {
-        uint64_t timestamp;
-        sd_id128_t id;
-        PyObject *monotonic, *bootid, *tuple;
-        int r;
-
-        assert(self);
-        assert(!args);
-
-        r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
-        monotonic = PyLong_FromUnsignedLongLong(timestamp);
-        bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
-#if PY_MAJOR_VERSION >= 3
-        tuple = PyStructSequence_New(&MonotonicType);
-#else
-        tuple = PyTuple_New(2);
-#endif
-        if (!monotonic || !bootid || !tuple) {
-                Py_XDECREF(monotonic);
-                Py_XDECREF(bootid);
-                Py_XDECREF(tuple);
-                return NULL;
-        }
-
-#if PY_MAJOR_VERSION >= 3
-        PyStructSequence_SET_ITEM(tuple, 0, monotonic);
-        PyStructSequence_SET_ITEM(tuple, 1, bootid);
-#else
-        PyTuple_SET_ITEM(tuple, 0, monotonic);
-        PyTuple_SET_ITEM(tuple, 1, bootid);
-#endif
-
-        return tuple;
-}
-
-PyDoc_STRVAR(Reader_add_match__doc__,
-             "add_match(match) -> None\n\n"
-             "Add a match to filter journal log entries. All matches of different\n"
-             "fields are combined with logical AND, and matches of the same field\n"
-             "are automatically combined with logical OR.\n"
-             "Match is a string of the form \"FIELD=value\".");
-static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds) {
-        char *match;
-        int match_len, r;
-        if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
-                return NULL;
-
-        r = sd_journal_add_match(self->j, match, match_len);
-        if (set_error(r, NULL, "Invalid match") < 0)
-                return NULL;
-
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_add_disjunction__doc__,
-             "add_disjunction() -> None\n\n"
-             "Inserts a logical OR between matches added since previous\n"
-             "add_disjunction() or add_conjunction() and the next\n"
-             "add_disjunction() or add_conjunction().\n\n"
-             "See man:sd_journal_add_disjunction(3) for explanation.");
-static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) {
-        int r;
-        r = sd_journal_add_disjunction(self->j);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_add_conjunction__doc__,
-             "add_conjunction() -> None\n\n"
-             "Inserts a logical AND between matches added since previous\n"
-             "add_disjunction() or add_conjunction() and the next\n"
-             "add_disjunction() or add_conjunction().\n\n"
-             "See man:sd_journal_add_disjunction(3) for explanation.");
-static PyObject* Reader_add_conjunction(Reader *self, PyObject *args) {
-        int r;
-        r = sd_journal_add_conjunction(self->j);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_flush_matches__doc__,
-             "flush_matches() -> None\n\n"
-             "Clear all current match filters.");
-static PyObject* Reader_flush_matches(Reader *self, PyObject *args) {
-        sd_journal_flush_matches(self->j);
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_seek_head__doc__,
-             "seek_head() -> None\n\n"
-             "Jump to the beginning of the journal.\n"
-             "This method invokes sd_journal_seek_head().\n"
-             "See man:sd_journal_seek_head(3).");
-static PyObject* Reader_seek_head(Reader *self, PyObject *args) {
-        int r;
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_seek_head(self->j);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_seek_tail__doc__,
-             "seek_tail() -> None\n\n"
-             "Jump to the end of the journal.\n"
-             "This method invokes sd_journal_seek_tail().\n"
-             "See man:sd_journal_seek_tail(3).");
-static PyObject* Reader_seek_tail(Reader *self, PyObject *args) {
-        int r;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_seek_tail(self->j);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_seek_realtime__doc__,
-             "seek_realtime(realtime) -> None\n\n"
-             "Seek to nearest matching journal entry to `realtime`. Argument\n"
-             "`realtime` in specified in seconds.");
-static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) {
-        uint64_t timestamp;
-        int r;
-
-        if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
-                return NULL;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_seek_realtime_usec(self->j, timestamp);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_seek_monotonic__doc__,
-             "seek_monotonic(monotonic[, bootid]) -> None\n\n"
-             "Seek to nearest matching journal entry to `monotonic`. Argument\n"
-             "`monotonic` is an timestamp from boot in microseconds.\n"
-             "Argument `bootid` is a string representing which boot the\n"
-             "monotonic time is reference to. Defaults to current bootid.");
-static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) {
-        char *bootid = NULL;
-        uint64_t timestamp;
-        sd_id128_t id;
-        int r;
-
-        if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
-                return NULL;
-
-        if (bootid) {
-                r = sd_id128_from_string(bootid, &id);
-                if (set_error(r, NULL, "Invalid bootid") < 0)
-                        return NULL;
-        } else {
-                Py_BEGIN_ALLOW_THREADS
-                r = sd_id128_get_boot(&id);
-                Py_END_ALLOW_THREADS
-
-                if (set_error(r, NULL, NULL) < 0)
-                        return NULL;
-        }
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        Py_RETURN_NONE;
-}
-
-
-PyDoc_STRVAR(Reader_process__doc__,
-             "process() -> state change (integer)\n\n"
-             "Process events and reset the readable state of the file\n"
-             "descriptor returned by .fileno().\n\n"
-             "Will return constants: NOP if no change; APPEND if new\n"
-             "entries have been added to the end of the journal; and\n"
-             "INVALIDATE if journal files have been added or removed.\n\n"
-             "See man:sd_journal_process(3) for further discussion.");
-static PyObject* Reader_process(Reader *self, PyObject *args) {
-        int r;
-
-        assert(!args);
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_process(self->j);
-        Py_END_ALLOW_THREADS
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return long_FromLong(r);
-}
-
-PyDoc_STRVAR(Reader_wait__doc__,
-             "wait([timeout]) -> state change (integer)\n\n"
-             "Wait for a change in the journal. Argument `timeout` specifies\n"
-             "the maximum number of microseconds to wait before returning\n"
-             "regardless of wheter the journal has changed. If `timeout` is -1,\n"
-             "then block forever.\n\n"
-             "Will return constants: NOP if no change; APPEND if new\n"
-             "entries have been added to the end of the journal; and\n"
-             "INVALIDATE if journal files have been added or removed.\n\n"
-             "See man:sd_journal_wait(3) for further discussion.");
-static PyObject* Reader_wait(Reader *self, PyObject *args) {
-        int r;
-        int64_t timeout;
-
-        if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
-                return NULL;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_wait(self->j, timeout);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return long_FromLong(r);
-}
-
-PyDoc_STRVAR(Reader_seek_cursor__doc__,
-             "seek_cursor(cursor) -> None\n\n"
-             "Seek to journal entry by given unique reference `cursor`.");
-static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) {
-        const char *cursor;
-        int r;
-
-        if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
-                return NULL;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_seek_cursor(self->j, cursor);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, "Invalid cursor") < 0)
-                return NULL;
-
-        Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Reader_get_cursor__doc__,
-             "get_cursor() -> str\n\n"
-             "Return a cursor string for the current journal entry.\n\n"
-             "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
-static PyObject* Reader_get_cursor(Reader *self, PyObject *args) {
-        _cleanup_free_ char *cursor = NULL;
-        int r;
-
-        assert(self);
-        assert(!args);
-
-        r = sd_journal_get_cursor(self->j, &cursor);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return unicode_FromString(cursor);
-}
-
-PyDoc_STRVAR(Reader_test_cursor__doc__,
-             "test_cursor(str) -> bool\n\n"
-             "Test whether the cursor string matches current journal entry.\n\n"
-             "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
-static PyObject* Reader_test_cursor(Reader *self, PyObject *args) {
-        const char *cursor;
-        int r;
-
-        assert(self);
-        assert(args);
-
-        if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
-                return NULL;
-
-        r = sd_journal_test_cursor(self->j, cursor);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return PyBool_FromLong(r);
-}
-
-PyDoc_STRVAR(Reader_query_unique__doc__,
-             "query_unique(field) -> a set of values\n\n"
-             "Return a set of unique values appearing in journal for the\n"
-             "given `field`. Note this does not respect any journal matches.");
-static PyObject* Reader_query_unique(Reader *self, PyObject *args) {
-        char *query;
-        int r;
-        const void *uniq;
-        size_t uniq_len;
-        PyObject *value_set, *key, *value;
-
-        if (!PyArg_ParseTuple(args, "s:query_unique", &query))
-                return NULL;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_query_unique(self->j, query);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, "Invalid field name") < 0)
-                return NULL;
-
-        value_set = PySet_New(0);
-        key = unicode_FromString(query);
-
-        SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
-                const char *delim_ptr;
-
-                delim_ptr = memchr(uniq, '=', uniq_len);
-                value = PyBytes_FromStringAndSize(
-                                delim_ptr + 1,
-                                (const char*) uniq + uniq_len - (delim_ptr + 1));
-                PySet_Add(value_set, value);
-                Py_DECREF(value);
-        }
-
-        Py_DECREF(key);
-        return value_set;
-}
-
-PyDoc_STRVAR(Reader_get_catalog__doc__,
-             "get_catalog() -> str\n\n"
-             "Retrieve a message catalog entry for the current journal entry.\n"
-             "Will throw IndexError if the entry has no MESSAGE_ID\n"
-             "and KeyError is the id is specified, but hasn't been found\n"
-             "in the catalog.\n\n"
-             "Wraps man:sd_journal_get_catalog(3).");
-static PyObject* Reader_get_catalog(Reader *self, PyObject *args) {
-        int r;
-        _cleanup_free_ char *msg = NULL;
-
-        assert(self);
-        assert(!args);
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_get_catalog(self->j, &msg);
-        Py_END_ALLOW_THREADS
-
-        if (r == -ENOENT) {
-                const void* mid;
-                size_t mid_len;
-
-                r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
-                if (r == 0) {
-                        const size_t l = sizeof("MESSAGE_ID");
-                        assert(mid_len > l);
-                        PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l),
-                                     (const char*) mid + l);
-                } else if (r == -ENOENT)
-                        PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
-                else
-                        set_error(r, NULL, NULL);
-                return NULL;
-        }
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return unicode_FromString(msg);
-}
-
-PyDoc_STRVAR(get_catalog__doc__,
-             "get_catalog(id128) -> str\n\n"
-             "Retrieve a message catalog entry for the given id.\n"
-             "Wraps man:sd_journal_get_catalog_for_message_id(3).");
-static PyObject* get_catalog(PyObject *self, PyObject *args) {
-        int r;
-        char *id_ = NULL;
-        sd_id128_t id;
-        _cleanup_free_ char *msg = NULL;
-
-        assert(args);
-
-        if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
-                return NULL;
-
-        r = sd_id128_from_string(id_, &id);
-        if (set_error(r, NULL, "Invalid id128") < 0)
-                return NULL;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_journal_get_catalog_for_message_id(id, &msg);
-        Py_END_ALLOW_THREADS
-
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return unicode_FromString(msg);
-}
-
-PyDoc_STRVAR(data_threshold__doc__,
-             "Threshold for field size truncation in bytes.\n\n"
-             "Fields longer than this will be truncated to the threshold size.\n"
-             "Defaults to 64Kb.");
-
-static PyObject* Reader_get_data_threshold(Reader *self, void *closure) {
-        size_t cvalue;
-        int r;
-
-        r = sd_journal_get_data_threshold(self->j, &cvalue);
-        if (set_error(r, NULL, NULL) < 0)
-                return NULL;
-
-        return long_FromSize_t(cvalue);
-}
-
-static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure) {
-        int r;
-
-        if (value == NULL) {
-                PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
-                return -1;
-        }
-
-        if (!long_Check(value)){
-                PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
-                return -1;
-        }
-
-        r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
-        return set_error(r, NULL, NULL);
-}
-
-PyDoc_STRVAR(closed__doc__,
-             "True iff journal is closed");
-static PyObject* Reader_get_closed(Reader *self, void *closure) {
-        return PyBool_FromLong(self->j == NULL);
-}
-
-static PyGetSetDef Reader_getsetters[] = {
-        { (char*) "data_threshold",
-          (getter) Reader_get_data_threshold,
-          (setter) Reader_set_data_threshold,
-          (char*) data_threshold__doc__,
-          NULL },
-        { (char*) "closed",
-          (getter) Reader_get_closed,
-          NULL,
-          (char*) closed__doc__,
-          NULL },
-        {} /* Sentinel */
-};
-
-static PyMethodDef Reader_methods[] = {
-        {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
-        {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
-        {"get_events",      (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
-        {"get_timeout",     (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
-        {"get_timeout_ms",  (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
-        {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
-        {"get_usage",       (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
-        {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
-        {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
-        {"_next",           (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
-        {"_previous",       (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__},
-        {"_get",            (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
-        {"_get_all",        (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__},
-        {"_get_realtime",   (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
-        {"_get_monotonic",  (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
-        {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
-        {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
-        {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__},
-        {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
-        {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
-        {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
-        {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
-        {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
-        {"process",         (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
-        {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
-        {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
-        {"_get_cursor",     (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
-        {"test_cursor",     (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
-        {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
-        {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
-        {}  /* Sentinel */
-};
-
-static PyTypeObject ReaderType = {
-        PyVarObject_HEAD_INIT(NULL, 0)
-        .tp_name = "_reader._Reader",
-        .tp_basicsize = sizeof(Reader),
-        .tp_dealloc = (destructor) Reader_dealloc,
-        .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
-        .tp_doc = Reader__doc__,
-        .tp_methods = Reader_methods,
-        .tp_getset = Reader_getsetters,
-        .tp_init = (initproc) Reader_init,
-        .tp_new = PyType_GenericNew,
-};
-
-static PyMethodDef methods[] = {
-        { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
-        {} /* Sentinel */
-};
-
-#if PY_MAJOR_VERSION >= 3
-static PyModuleDef module = {
-        PyModuleDef_HEAD_INIT,
-        "_reader",
-        module__doc__,
-        -1,
-        methods,
-};
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-static bool initialized = false;
-#endif
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-
-PyMODINIT_FUNC
-#if PY_MAJOR_VERSION >= 3
-PyInit__reader(void)
-#else
-init_reader(void)
-#endif
-{
-        PyObject* m;
-
-        PyDateTime_IMPORT;
-
-        if (PyType_Ready(&ReaderType) < 0)
-#if PY_MAJOR_VERSION >= 3
-                return NULL;
-#else
-                return;
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-        m = PyModule_Create(&module);
-        if (m == NULL)
-                return NULL;
-
-        if (!initialized) {
-                PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
-                initialized = true;
-        }
-#else
-        m = Py_InitModule3("_reader", methods, module__doc__);
-        if (m == NULL)
-                return;
-#endif
-
-        Py_INCREF(&ReaderType);
-#if PY_MAJOR_VERSION >= 3
-        Py_INCREF(&MonotonicType);
-#endif
-        if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
-#if PY_MAJOR_VERSION >= 3
-            PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
-#endif
-            PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
-            PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
-            PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
-            PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
-            PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
-            PyModule_AddIntConstant(m, "SYSTEM", SD_JOURNAL_SYSTEM) ||
-            PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) ||
-            PyModule_AddIntConstant(m, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) ||
-            PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
-#if PY_MAJOR_VERSION >= 3
-                Py_DECREF(m);
-                return NULL;
-#endif
-        }
-
-#if PY_MAJOR_VERSION >= 3
-        return m;
-#endif
-}
-
-REENABLE_WARNING;
diff --git a/src/python-systemd/daemon.py b/src/python-systemd/daemon.py
deleted file mode 100644 (file)
index 82011ca..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-from ._daemon import (__version__,
-                      booted,
-                      notify,
-                      _listen_fds,
-                      _is_fifo,
-                      _is_socket,
-                      _is_socket_inet,
-                      _is_socket_unix,
-                      _is_mq,
-                      LISTEN_FDS_START)
-from socket import AF_UNSPEC as _AF_UNSPEC
-
-def _convert_fileobj(fileobj):
-    try:
-        return fileobj.fileno()
-    except AttributeError:
-        return fileobj
-
-def is_fifo(fileobj, path=None):
-    fd = _convert_fileobj(fileobj)
-    return _is_fifo(fd, path)
-
-def is_socket(fileobj, family=_AF_UNSPEC, type=0, listening=-1):
-    fd = _convert_fileobj(fileobj)
-    return _is_socket(fd, family, type, listening)
-
-def is_socket_inet(fileobj, family=_AF_UNSPEC, type=0, listening=-1, port=0):
-    fd = _convert_fileobj(fileobj)
-    return _is_socket_inet(fd, family, type, listening, port)
-
-def is_socket_unix(fileobj, type=0, listening=-1, path=None):
-    fd = _convert_fileobj(fileobj)
-    return _is_socket_unix(fd, type, listening, path)
-
-def is_mq(fileobj, path=None):
-    fd = _convert_fileobj(fileobj)
-    return _is_mq(fd, path)
-
-def listen_fds(unset_environment=True):
-    """Return a list of socket activated descriptors
-
-    Example::
-
-      (in primary window)
-      $ systemd-activate -l 2000 python3 -c \\
-          'from systemd.daemon import listen_fds; print(listen_fds())'
-      (in another window)
-      $ telnet localhost 2000
-      (in primary window)
-      ...
-      Execing python3 (...)
-      [3]
-    """
-    num = _listen_fds(unset_environment)
-    return list(range(LISTEN_FDS_START, LISTEN_FDS_START + num))
diff --git a/src/python-systemd/docs/.gitignore b/src/python-systemd/docs/.gitignore
deleted file mode 100644 (file)
index b06a965..0000000
+++ /dev/null
@@ -1 +0,0 @@
-!layout.html
diff --git a/src/python-systemd/docs/conf.py b/src/python-systemd/docs/conf.py
deleted file mode 100644 (file)
index 1919170..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# python-systemd documentation build configuration file, created by
-# sphinx-quickstart on Sat Feb  9 13:49:42 2013.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['.']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'python-systemd'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = []
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['.']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-html_show_sourcelink = False
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'python-systemddoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'python-systemd.tex', u'python-systemd Documentation',
-   None, 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'python-systemd', u'python-systemd Documentation',
-     [], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-  ('index', 'python-systemd', u'python-systemd Documentation',
-   u'David Strauss, Zbigniew JÄ™drzejewski-Szmek, Marti Raudsepp, Steven Hiscocks', 'python-systemd', 'One line description of project.',
-   'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-
-# -- Options for Epub output ---------------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = u'python-systemd'
-epub_author = u'David Strauss, Zbigniew JÄ™drzejewski-Szmek, Marti Raudsepp, Steven Hiscocks'
-epub_publisher = u'David Strauss, Zbigniew JÄ™drzejewski-Szmek, Marti Raudsepp, Steven Hiscocks'
-epub_copyright = u'2013, David Strauss, Zbigniew JÄ™drzejewski-Szmek, Marti Raudsepp, Steven Hiscocks'
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-#epub_exclude_files = []
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
-
-
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'http://docs.python.org/': None}
diff --git a/src/python-systemd/docs/daemon.rst b/src/python-systemd/docs/daemon.rst
deleted file mode 100644 (file)
index 0ad11ed..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-`systemd.daemon` module
-=======================
-
-.. automodule:: systemd.daemon
-   :members:
-   :undoc-members:
-   :inherited-members:
-
-   .. autoattribute:: systemd.daemon.LISTEN_FDS_START
-
-   .. autofunction:: _listen_fds
-   .. autofunction:: _is_fifo
-   .. autofunction:: _is_socket
-   .. autofunction:: _is_socket_unix
-   .. autofunction:: _is_socket_inet
-   .. autofunction:: _is_mq
-   .. autofunction:: notify
-   .. autofunction:: booted
diff --git a/src/python-systemd/docs/default.css b/src/python-systemd/docs/default.css
deleted file mode 100644 (file)
index 7c097d6..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-@import url("basic.css");
-
-/* -- page layout ----------------------------------------------------------- */
-
-div.documentwrapper {
-    float: left;
-    width: 100%;
-}
-
-div.bodywrapper {
-    margin: 0 0 0 230px;
-}
-
-div.body {
-    background-color: #ffffff;
-    color: #000000;
-    padding: 0 20px 30px 20px;
-}
-
-div.footer {
-    color: #ffffff;
-    width: 100%;
-    padding: 9px 0 9px 0;
-    text-align: center;
-    font-size: 75%;
-}
-
-div.footer a {
-    color: #ffffff;
-    text-decoration: underline;
-}
-
-div.related {
-    background-color: #133f52;
-    line-height: 30px;
-    color: #ffffff;
-}
-
-div.related a {
-    color: #ffffff;
-}
-
-div.sphinxsidebar {
-    background-color: #dddddd;
-}
-
-div.sphinxsidebar p.topless {
-    margin: 5px 10px 10px 10px;
-}
-
-div.sphinxsidebar ul {
-    margin: 10px;
-    padding: 0;
-}
-
-div.sphinxsidebar input {
-    border: 1px solid #000000;
-    font-family: sans-serif;
-    font-size: 1em;
-}
-
-
-
-/* -- hyperlink styles ------------------------------------------------------ */
-
-a {
-    text-decoration: none;
-}
-
-a:hover {
-    text-decoration: underline;
-}
-
-
-
-/* -- body styles ----------------------------------------------------------- */
-
-div.body h1,
-div.body h2,
-div.body h3,
-div.body h4,
-div.body h5,
-div.body h6 {
-    font-family: 'Trebuchet MS', sans-serif;
-    background-color: #f2f2f2;
-    font-weight: normal;
-    color: #20435c;
-    border-bottom: 1px solid #ccc;
-    margin: 20px -20px 10px -20px;
-    padding: 3px 0 3px 10px;
-}
-
-div.body h1 { margin-top: 0; font-size: 200%; }
-div.body h2 { font-size: 160%; }
-div.body h3 { font-size: 140%; }
-div.body h4 { font-size: 120%; }
-div.body h5 { font-size: 110%; }
-div.body h6 { font-size: 100%; }
-
-a.headerlink {
-    color: #c60f0f;
-    font-size: 0.8em;
-    padding: 0 4px 0 4px;
-    text-decoration: none;
-}
-
-a.headerlink:hover {
-    background-color: #c60f0f;
-    color: white;
-}
-
-div.body p, div.body dd, div.body li {
-    text-align: justify;
-    line-height: 130%;
-}
-
-div.admonition p.admonition-title + p {
-    display: inline;
-}
-
-div.admonition p {
-    margin-bottom: 5px;
-}
-
-div.admonition pre {
-    margin-bottom: 5px;
-}
-
-div.admonition ul, div.admonition ol {
-    margin-bottom: 5px;
-}
-
-div.note {
-    background-color: #eee;
-    border: 1px solid #ccc;
-}
-
-div.seealso {
-    background-color: #ffc;
-    border: 1px solid #ff6;
-}
-
-div.topic {
-    background-color: #eee;
-}
-
-div.warning {
-    background-color: #ffe4e4;
-    border: 1px solid #f66;
-}
-
-p.admonition-title {
-    display: inline;
-}
-
-p.admonition-title:after {
-    content: ":";
-}
-
-pre {
-    padding: 5px;
-    background-color: #eeffcc;
-    color: #333333;
-    line-height: 120%;
-    border: 1px solid #ac9;
-    border-left: none;
-    border-right: none;
-}
-
-tt {
-    background-color: #ecf0f3;
-    padding: 0 1px 0 1px;
-    font-size: 0.95em;
-}
-
-th {
-    background-color: #ede;
-}
-
-.warning tt {
-    background: #efc2c2;
-}
-
-.note tt {
-    background: #d6d6d6;
-}
-
-.viewcode-back {
-    font-family: sans-serif;
-}
-
-div.viewcode-block:target {
-    background-color: #f4debf;
-    border-top: 1px solid #ac9;
-    border-bottom: 1px solid #ac9;
-}
diff --git a/src/python-systemd/docs/id128.rst b/src/python-systemd/docs/id128.rst
deleted file mode 100644 (file)
index 89c37f3..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-`systemd.id128` module
-======================
-
-.. automodule:: systemd.id128
-   :members:
-   :undoc-members:
-   :inherited-members:
-
-   .. autoattribute:: systemd.id128.SD_MESSAGE_COREDUMP
-   .. autoattribute:: systemd.id128.SD_MESSAGE_FORWARD_SYSLOG_MISSED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_HIBERNATE_KEY
-   .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_DROPPED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_MISSED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_START
-   .. autoattribute:: systemd.id128.SD_MESSAGE_JOURNAL_STOP
-   .. autoattribute:: systemd.id128.SD_MESSAGE_LID_CLOSED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_LID_OPENED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_OVERMOUNTING
-   .. autoattribute:: systemd.id128.SD_MESSAGE_POWER_KEY
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_START
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SEAT_STOP
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_START
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SESSION_STOP
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SHUTDOWN
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SLEEP_START
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SLEEP_STOP
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SPAWN_FAILED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_STARTUP_FINISHED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_SUSPEND_KEY
-   .. autoattribute:: systemd.id128.SD_MESSAGE_TIMEZONE_CHANGE
-   .. autoattribute:: systemd.id128.SD_MESSAGE_TIME_CHANGE
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_FAILED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_RELOADING
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STARTED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STARTING
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPED
-   .. autoattribute:: systemd.id128.SD_MESSAGE_UNIT_STOPPING
-   .. autoattribute:: systemd.id128.SD_MESSAGE_CONFIG_ERROR
-   .. autoattribute:: systemd.id128.SD_MESSAGE_BOOTCHART
diff --git a/src/python-systemd/docs/index.rst b/src/python-systemd/docs/index.rst
deleted file mode 100644 (file)
index e78d966..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-.. python-systemd documentation master file, created by
-   sphinx-quickstart on Sat Feb  9 13:49:42 2013.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
-
-Welcome to python-systemd's documentation!
-==========================================
-
-Contents:
-
-.. toctree::
-   :maxdepth: 2
-
-   journal
-   id128
-   daemon
-   login
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/src/python-systemd/docs/journal.rst b/src/python-systemd/docs/journal.rst
deleted file mode 100644 (file)
index ea74cf8..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-`systemd.journal` module
-========================
-
-.. automodule:: systemd.journal
-   :members: send, sendv, stream, stream_fd
-   :undoc-members:
-
-`JournalHandler` class
-----------------------
-
-.. autoclass:: JournalHandler
-
-Accessing the Journal
----------------------
-
-.. autoclass:: _Reader
-   :undoc-members:
-   :inherited-members:
-
-.. autoclass:: Reader
-   :undoc-members:
-   :inherited-members:
-
-   .. automethod:: __init__
-
-.. autofunction:: _get_catalog
-.. autofunction:: get_catalog
-
-.. autoclass:: Monotonic
-
-.. autoattribute:: systemd.journal.DEFAULT_CONVERTERS
-
-Example: polling for journal events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This example shows that journal events can be waited for (using
-e.g. `poll`). This makes it easy to integrate Reader in an external
-event loop:
-
-  >>> import select
-  >>> from systemd import journal
-  >>> j = journal.Reader()
-  >>> j.seek_tail()
-  >>> p = select.poll()
-  >>> p.register(j, j.get_events())
-  >>> p.poll()
-  [(3, 1)]
-  >>> j.get_next()
-
-
-Journal access types
-~~~~~~~~~~~~~~~~~~~~
-
-.. autoattribute:: systemd.journal.LOCAL_ONLY
-.. autoattribute:: systemd.journal.RUNTIME_ONLY
-.. autoattribute:: systemd.journal.SYSTEM
-.. autoattribute:: systemd.journal.CURRENT_USER
-
-Journal event types
-~~~~~~~~~~~~~~~~~~~
-
-.. autoattribute:: systemd.journal.NOP
-.. autoattribute:: systemd.journal.APPEND
-.. autoattribute:: systemd.journal.INVALIDATE
diff --git a/src/python-systemd/docs/layout.html b/src/python-systemd/docs/layout.html
deleted file mode 100644 (file)
index 930a6a7..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "!layout.html" %}
-
-{% block relbar1 %}
-  <a href="../man/systemd.index.html">Index </a>·
-  <a href="../man/systemd.directives.html">Directives </a>·
-  <a href="index.html">Python </a>·
-  <span style="float:right">systemd {{release}}</span>
-  <hr />
-{% endblock %}
-
-{# remove the lower relbar #}
-{% block relbar2 %} {% endblock %}
-
-{# remove the footer #}
-{% block footer %} {% endblock %}
diff --git a/src/python-systemd/docs/login.rst b/src/python-systemd/docs/login.rst
deleted file mode 100644 (file)
index 6b4de64..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-`systemd.login` module
-=======================
-
-.. automodule:: systemd.login
-   :members:
-
-.. autoclass:: Monitor
-   :undoc-members:
-   :inherited-members:
-
-Example: polling for events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This example shows that session/uid/seat/machine events can be waited
-for (using e.g. `poll`). This makes it easy to integrate Monitor in an
-external event loop:
-
-  >>> import select
-  >>> from systemd import login
-  >>> m = login.Monitor("machine")
-  >>> p = select.poll()
-  >>> p.register(m, m.get_events())
-  >>> login.machine_names()
-  []
-  >>> p.poll()
-  [(3, 1)]
-  >>> login.machine_names()
-  ['fedora-19.nspawn']
diff --git a/src/python-systemd/id128.c b/src/python-systemd/id128.c
deleted file mode 100644 (file)
index 5ec7309..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  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 <Python.h>
-
-#include "systemd/sd-messages.h"
-
-#include "pyutil.h"
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-
-PyDoc_STRVAR(module__doc__,
-             "Python interface to the libsystemd-id128 library.\n\n"
-             "Provides SD_MESSAGE_* constants and functions to query and generate\n"
-             "128-bit unique identifiers."
-);
-
-PyDoc_STRVAR(randomize__doc__,
-             "randomize() -> UUID\n\n"
-             "Return a new random 128-bit unique identifier.\n"
-             "Wraps sd_id128_randomize(3)."
-);
-
-PyDoc_STRVAR(get_machine__doc__,
-             "get_machine() -> UUID\n\n"
-             "Return a 128-bit unique identifier for this machine.\n"
-             "Wraps sd_id128_get_machine(3)."
-);
-
-PyDoc_STRVAR(get_boot__doc__,
-             "get_boot() -> UUID\n\n"
-             "Return a 128-bit unique identifier for this boot.\n"
-             "Wraps sd_id128_get_boot(3)."
-);
-
-static PyObject* make_uuid(sd_id128_t id) {
-        _cleanup_Py_DECREF_ PyObject
-                *uuid = NULL, *UUID = NULL, *bytes = NULL,
-                *args = NULL, *kwargs = NULL;
-
-        uuid = PyImport_ImportModule("uuid");
-        if (!uuid)
-                return NULL;
-
-        UUID = PyObject_GetAttrString(uuid, "UUID");
-        bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
-        args = Py_BuildValue("()");
-        kwargs = PyDict_New();
-        if (!UUID || !bytes || !args || !kwargs)
-                return NULL;
-
-        if (PyDict_SetItemString(kwargs, "bytes", bytes) < 0)
-                return NULL;
-
-        return PyObject_Call(UUID, args, kwargs);
-}
-
-#define helper(name)                                                    \
-        static PyObject *name(PyObject *self, PyObject *args) {         \
-                sd_id128_t id;                                          \
-                int r;                                                  \
-                                                                        \
-                assert(args == NULL);                                   \
-                                                                        \
-                r = sd_id128_##name(&id);                               \
-                if (r < 0) {                                            \
-                        errno = -r;                                     \
-                        return PyErr_SetFromErrno(PyExc_IOError);       \
-                }                                                       \
-                                                                        \
-                return make_uuid(id);                                   \
-        }
-
-helper(randomize)
-helper(get_machine)
-helper(get_boot)
-
-static PyMethodDef methods[] = {
-        { "randomize", randomize, METH_NOARGS, randomize__doc__},
-        { "get_machine", get_machine, METH_NOARGS, get_machine__doc__},
-        { "get_boot", get_boot, METH_NOARGS, get_boot__doc__},
-        { NULL, NULL, 0, NULL }        /* Sentinel */
-};
-
-static int add_id(PyObject *module, const char* name, sd_id128_t id) {
-        PyObject *obj;
-
-        obj = make_uuid(id);
-        if (!obj)
-                return -1;
-
-        return PyModule_AddObject(module, name, obj);
-}
-
-#if PY_MAJOR_VERSION < 3
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC initid128(void) {
-        PyObject *m;
-
-        m = Py_InitModule3("id128", methods, module__doc__);
-        if (m == NULL)
-                return;
-
-        /* a series of lines like 'add_id() ;' follow */
-#define JOINER ;
-#include "id128-constants.h"
-#undef JOINER
-        PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
-}
-REENABLE_WARNING;
-
-#else
-
-static struct PyModuleDef module = {
-        PyModuleDef_HEAD_INIT,
-        "id128", /* name of module */
-        module__doc__, /* module documentation, may be NULL */
-        -1, /* size of per-interpreter state of the module */
-        methods
-};
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC PyInit_id128(void) {
-        PyObject *m;
-
-        m = PyModule_Create(&module);
-        if (m == NULL)
-                return NULL;
-
-        if ( /* a series of lines like 'add_id() ||' follow */
-#define JOINER ||
-#include "id128-constants.h"
-#undef JOINER
-            PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
-                Py_DECREF(m);
-                return NULL;
-        }
-
-        return m;
-}
-REENABLE_WARNING;
-
-#endif
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
deleted file mode 100644 (file)
index dd1f229..0000000
+++ /dev/null
@@ -1,548 +0,0 @@
-#  -*- Mode: python; coding:utf-8; indent-tabs-mode: nil -*- */
-#
-#  This file is part of systemd.
-#
-#  Copyright 2012 David Strauss <david@davidstrauss.net>
-#  Copyright 2012 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
-#  Copyright 2012 Marti Raudsepp <marti@juffo.org>
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import division
-
-import sys as _sys
-import datetime as _datetime
-import uuid as _uuid
-import traceback as _traceback
-import os as _os
-import logging as _logging
-if _sys.version_info >= (3,3):
-    from collections import ChainMap as _ChainMap
-from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
-                    LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG)
-from ._journal import __version__, sendv, stream_fd
-from ._reader import (_Reader, NOP, APPEND, INVALIDATE,
-                      LOCAL_ONLY, RUNTIME_ONLY,
-                      SYSTEM, SYSTEM_ONLY, CURRENT_USER,
-                      _get_catalog)
-from . import id128 as _id128
-
-if _sys.version_info >= (3,):
-    from ._reader import Monotonic
-else:
-    Monotonic = tuple
-
-def _convert_monotonic(m):
-    return Monotonic((_datetime.timedelta(microseconds=m[0]),
-                      _uuid.UUID(bytes=m[1])))
-
-def _convert_source_monotonic(s):
-    return _datetime.timedelta(microseconds=int(s))
-
-def _convert_realtime(t):
-    return _datetime.datetime.fromtimestamp(t / 1000000)
-
-def _convert_timestamp(s):
-    return _datetime.datetime.fromtimestamp(int(s) / 1000000)
-
-def _convert_trivial(x):
-    return x
-
-if _sys.version_info >= (3,):
-    def _convert_uuid(s):
-        return _uuid.UUID(s.decode())
-else:
-    _convert_uuid = _uuid.UUID
-
-DEFAULT_CONVERTERS = {
-    'MESSAGE_ID': _convert_uuid,
-    '_MACHINE_ID': _convert_uuid,
-    '_BOOT_ID': _convert_uuid,
-    'PRIORITY': int,
-    'LEADER': int,
-    'SESSION_ID': int,
-    'USERSPACE_USEC': int,
-    'INITRD_USEC': int,
-    'KERNEL_USEC': int,
-    '_UID': int,
-    '_GID': int,
-    '_PID': int,
-    'SYSLOG_FACILITY': int,
-    'SYSLOG_PID': int,
-    '_AUDIT_SESSION': int,
-    '_AUDIT_LOGINUID': int,
-    '_SYSTEMD_SESSION': int,
-    '_SYSTEMD_OWNER_UID': int,
-    'CODE_LINE': int,
-    'ERRNO': int,
-    'EXIT_STATUS': int,
-    '_SOURCE_REALTIME_TIMESTAMP': _convert_timestamp,
-    '__REALTIME_TIMESTAMP': _convert_realtime,
-    '_SOURCE_MONOTONIC_TIMESTAMP': _convert_source_monotonic,
-    '__MONOTONIC_TIMESTAMP': _convert_monotonic,
-    '__CURSOR': _convert_trivial,
-    'COREDUMP': bytes,
-    'COREDUMP_PID': int,
-    'COREDUMP_UID': int,
-    'COREDUMP_GID': int,
-    'COREDUMP_SESSION': int,
-    'COREDUMP_SIGNAL': int,
-    'COREDUMP_TIMESTAMP': _convert_timestamp,
-}
-
-_IDENT_LETTER = set('ABCDEFGHIJKLMNOPQRTSUVWXYZ_')
-
-def _valid_field_name(s):
-    return not (set(s) - _IDENT_LETTER)
-
-class Reader(_Reader):
-    """Reader allows the access and filtering of systemd journal
-    entries. Note that in order to access the system journal, a
-    non-root user must be in the `systemd-journal` group.
-
-    Example usage to print out all informational or higher level
-    messages for systemd-udevd for this boot:
-
-    >>> j = journal.Reader()
-    >>> j.this_boot()
-    >>> j.log_level(journal.LOG_INFO)
-    >>> j.add_match(_SYSTEMD_UNIT="systemd-udevd.service")
-    >>> for entry in j:
-    ...    print(entry['MESSAGE'])
-
-    See systemd.journal-fields(7) for more info on typical fields
-    found in the journal.
-    """
-    def __init__(self, flags=0, path=None, files=None, converters=None):
-        """Create an instance of Reader, which allows filtering and
-        return of journal entries.
-
-        Argument `flags` sets open flags of the journal, which can be one
-        of, or ORed combination of constants: LOCAL_ONLY (default) opens
-        journal on local machine only; RUNTIME_ONLY opens only
-        volatile journal files; and SYSTEM_ONLY opens only
-        journal files of system services and the kernel.
-
-        Argument `path` is the directory of journal files. Note that
-        `flags` and `path` are exclusive.
-
-        Argument `converters` is a dictionary which updates the
-        DEFAULT_CONVERTERS to convert journal field values. Field
-        names are used as keys into this dictionary. The values must
-        be single argument functions, which take a `bytes` object and
-        return a converted value. When there's no entry for a field
-        name, then the default UTF-8 decoding will be attempted. If
-        the conversion fails with a ValueError, unconverted bytes
-        object will be returned. (Note that ValueEror is a superclass
-        of UnicodeDecodeError).
-
-        Reader implements the context manager protocol: the journal
-        will be closed when exiting the block.
-        """
-        super(Reader, self).__init__(flags, path, files)
-        if _sys.version_info >= (3,3):
-            self.converters = _ChainMap()
-            if converters is not None:
-                self.converters.maps.append(converters)
-            self.converters.maps.append(DEFAULT_CONVERTERS)
-        else:
-            self.converters = DEFAULT_CONVERTERS.copy()
-            if converters is not None:
-                self.converters.update(converters)
-
-    def _convert_field(self, key, value):
-        """Convert value using self.converters[key]
-
-        If `key` is not present in self.converters, a standard unicode
-        decoding will be attempted.  If the conversion (either
-        key-specific or the default one) fails with a ValueError, the
-        original bytes object will be returned.
-        """
-        convert = self.converters.get(key, bytes.decode)
-        try:
-            return convert(value)
-        except ValueError:
-            # Leave in default bytes
-            return value
-
-    def _convert_entry(self, entry):
-        """Convert entire journal entry utilising _covert_field"""
-        result = {}
-        for key, value in entry.items():
-            if isinstance(value, list):
-                result[key] = [self._convert_field(key, val) for val in value]
-            else:
-                result[key] = self._convert_field(key, value)
-        return result
-
-    def __iter__(self):
-        """Part of iterator protocol.
-        Returns self.
-        """
-        return self
-
-    def __next__(self):
-        """Part of iterator protocol.
-        Returns self.get_next() or raises StopIteration.
-        """
-        ans = self.get_next()
-        if ans:
-            return ans
-        else:
-            raise StopIteration()
-
-    if _sys.version_info < (3,):
-        next = __next__
-
-    def add_match(self, *args, **kwargs):
-        """Add one or more matches to the filter journal log entries.
-        All matches of different field are combined in a logical AND,
-        and matches of the same field are automatically combined in a
-        logical OR.
-        Matches can be passed as strings of form "FIELD=value", or
-        keyword arguments FIELD="value".
-        """
-        args = list(args)
-        args.extend(_make_line(key, val) for key, val in kwargs.items())
-        for arg in args:
-            super(Reader, self).add_match(arg)
-
-    def get_next(self, skip=1):
-        """Return the next log entry as a mapping type, currently
-        a standard dictionary of fields.
-
-        Optional skip value will return the `skip`\-th log entry.
-
-        Entries will be processed with converters specified during
-        Reader creation.
-        """
-        if super(Reader, self)._next(skip):
-            entry = super(Reader, self)._get_all()
-            if entry:
-                entry['__REALTIME_TIMESTAMP'] =  self._get_realtime()
-                entry['__MONOTONIC_TIMESTAMP']  = self._get_monotonic()
-                entry['__CURSOR']  = self._get_cursor()
-                return self._convert_entry(entry)
-        return dict()
-
-    def get_previous(self, skip=1):
-        """Return the previous log entry as a mapping type,
-        currently a standard dictionary of fields.
-
-        Optional skip value will return the -`skip`\-th log entry.
-
-        Entries will be processed with converters specified during
-        Reader creation.
-
-        Equivalent to get_next(-skip).
-        """
-        return self.get_next(-skip)
-
-    def query_unique(self, field):
-        """Return unique values appearing in the journal for given `field`.
-
-        Note this does not respect any journal matches.
-
-        Entries will be processed with converters specified during
-        Reader creation.
-        """
-        return set(self._convert_field(field, value)
-            for value in super(Reader, self).query_unique(field))
-
-    def wait(self, timeout=None):
-        """Wait for a change in the journal. `timeout` is the maximum
-        time in seconds to wait, or None, to wait forever.
-
-        Returns one of NOP (no change), APPEND (new entries have been
-        added to the end of the journal), or INVALIDATE (journal files
-        have been added or removed).
-        """
-        us = -1 if timeout is None else int(timeout * 1000000)
-        return super(Reader, self).wait(us)
-
-    def seek_realtime(self, realtime):
-        """Seek to a matching journal entry nearest to `realtime` time.
-
-        Argument `realtime` must be either an integer unix timestamp
-        or datetime.datetime instance.
-        """
-        if isinstance(realtime, _datetime.datetime):
-            realtime = float(realtime.strftime("%s.%f")) * 1000000
-        return super(Reader, self).seek_realtime(int(realtime))
-
-    def seek_monotonic(self, monotonic, bootid=None):
-        """Seek to a matching journal entry nearest to `monotonic` time.
-
-        Argument `monotonic` is a timestamp from boot in either
-        seconds or a datetime.timedelta instance. Argument `bootid`
-        is a string or UUID representing which boot the monotonic time
-        is reference to. Defaults to current bootid.
-        """
-        if isinstance(monotonic, _datetime.timedelta):
-            monotonic = monotonic.totalseconds()
-        monotonic = int(monotonic * 1000000)
-        if isinstance(bootid, _uuid.UUID):
-            bootid = bootid.hex
-        return super(Reader, self).seek_monotonic(monotonic, bootid)
-
-    def log_level(self, level):
-        """Set maximum log `level` by setting matches for PRIORITY.
-        """
-        if 0 <= level <= 7:
-            for i in range(level+1):
-                self.add_match(PRIORITY="%d" % i)
-        else:
-            raise ValueError("Log level must be 0 <= level <= 7")
-
-    def messageid_match(self, messageid):
-        """Add match for log entries with specified `messageid`.
-
-        `messageid` can be string of hexadicimal digits or a UUID
-        instance. Standard message IDs can be found in systemd.id128.
-
-        Equivalent to add_match(MESSAGE_ID=`messageid`).
-        """
-        if isinstance(messageid, _uuid.UUID):
-            messageid = messageid.hex
-        self.add_match(MESSAGE_ID=messageid)
-
-    def this_boot(self, bootid=None):
-        """Add match for _BOOT_ID equal to current boot ID or the specified boot ID.
-
-        If specified, bootid should be either a UUID or a 32 digit hex number.
-
-        Equivalent to add_match(_BOOT_ID='bootid').
-        """
-        if bootid is None:
-            bootid = _id128.get_boot().hex
-        else:
-            bootid = getattr(bootid, 'hex', bootid)
-        self.add_match(_BOOT_ID=bootid)
-
-    def this_machine(self, machineid=None):
-        """Add match for _MACHINE_ID equal to the ID of this machine.
-
-        If specified, machineid should be either a UUID or a 32 digit hex number.
-
-        Equivalent to add_match(_MACHINE_ID='machineid').
-        """
-        if machineid is None:
-            machineid = _id128.get_machine().hex
-        else:
-            machineid = getattr(machineid, 'hex', machineid)
-        self.add_match(_MACHINE_ID=machineid)
-
-
-def get_catalog(mid):
-    if isinstance(mid, _uuid.UUID):
-        mid = mid.hex
-    return _get_catalog(mid)
-
-def _make_line(field, value):
-        if isinstance(value, bytes):
-                return field.encode('utf-8') + b'=' + value
-        elif isinstance(value, int):
-                return field + '=' + str(value)
-        else:
-                return field + '=' + value
-
-def send(MESSAGE, MESSAGE_ID=None,
-         CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None,
-         **kwargs):
-        r"""Send a message to the journal.
-
-        >>> journal.send('Hello world')
-        >>> journal.send('Hello, again, world', FIELD2='Greetings!')
-        >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef')
-
-        Value of the MESSAGE argument will be used for the MESSAGE=
-        field. MESSAGE must be a string and will be sent as UTF-8 to
-        the journal.
-
-        MESSAGE_ID can be given to uniquely identify the type of
-        message. It must be a string or a uuid.UUID object.
-
-        CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to
-        identify the caller. Unless at least on of the three is given,
-        values are extracted from the stack frame of the caller of
-        send(). CODE_FILE and CODE_FUNC must be strings, CODE_LINE
-        must be an integer.
-
-        Additional fields for the journal entry can only be specified
-        as keyword arguments. The payload can be either a string or
-        bytes. A string will be sent as UTF-8, and bytes will be sent
-        as-is to the journal.
-
-        Other useful fields include PRIORITY, SYSLOG_FACILITY,
-        SYSLOG_IDENTIFIER, SYSLOG_PID.
-        """
-
-        args = ['MESSAGE=' + MESSAGE]
-
-        if MESSAGE_ID is not None:
-                id = getattr(MESSAGE_ID, 'hex', MESSAGE_ID)
-                args.append('MESSAGE_ID=' + id)
-
-        if CODE_LINE == CODE_FILE == CODE_FUNC == None:
-                CODE_FILE, CODE_LINE, CODE_FUNC = \
-                        _traceback.extract_stack(limit=2)[0][:3]
-        if CODE_FILE is not None:
-                args.append('CODE_FILE=' + CODE_FILE)
-        if CODE_LINE is not None:
-                args.append('CODE_LINE={:d}'.format(CODE_LINE))
-        if CODE_FUNC is not None:
-                args.append('CODE_FUNC=' + CODE_FUNC)
-
-        args.extend(_make_line(key, val) for key, val in kwargs.items())
-        return sendv(*args)
-
-def stream(identifier, priority=LOG_DEBUG, level_prefix=False):
-        r"""Return a file object wrapping a stream to journal.
-
-        Log messages written to this file as simple newline sepearted
-        text strings are written to the journal.
-
-        The file will be line buffered, so messages are actually sent
-        after a newline character is written.
-
-        >>> stream = journal.stream('myapp')
-        >>> stream
-        <open file '<fdopen>', mode 'w' at 0x...>
-        >>> stream.write('message...\n')
-
-        will produce the following message in the journal::
-
-          PRIORITY=7
-          SYSLOG_IDENTIFIER=myapp
-          MESSAGE=message...
-
-        Using the interface with print might be more convinient:
-
-        >>> from __future__ import print_function
-        >>> print('message...', file=stream)
-
-        priority is the syslog priority, one of `LOG_EMERG`,
-        `LOG_ALERT`, `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`,
-        `LOG_NOTICE`, `LOG_INFO`, `LOG_DEBUG`.
-
-        level_prefix is a boolean. If true, kernel-style log priority
-        level prefixes (such as '<1>') are interpreted. See
-        sd-daemon(3) for more information.
-        """
-
-        fd = stream_fd(identifier, priority, level_prefix)
-        return _os.fdopen(fd, 'w', 1)
-
-class JournalHandler(_logging.Handler):
-        """Journal handler class for the Python logging framework.
-
-        Please see the Python logging module documentation for an
-        overview: http://docs.python.org/library/logging.html.
-
-        To create a custom logger whose messages go only to journal:
-
-        >>> log = logging.getLogger('custom_logger_name')
-        >>> log.propagate = False
-        >>> log.addHandler(journal.JournalHandler())
-        >>> log.warn("Some message: %s", detail)
-
-        Note that by default, message levels `INFO` and `DEBUG` are
-        ignored by the logging framework. To enable those log levels:
-
-        >>> log.setLevel(logging.DEBUG)
-
-        To redirect all logging messages to journal regardless of where
-        they come from, attach it to the root logger:
-
-        >>> logging.root.addHandler(journal.JournalHandler())
-
-        For more complex configurations when using `dictConfig` or
-        `fileConfig`, specify `systemd.journal.JournalHandler` as the
-        handler class.  Only standard handler configuration options
-        are supported: `level`, `formatter`, `filters`.
-
-        To attach journal MESSAGE_ID, an extra field is supported:
-
-        >>> import uuid
-        >>> mid = uuid.UUID('0123456789ABCDEF0123456789ABCDEF')
-        >>> log.warn("Message with ID", extra={'MESSAGE_ID': mid})
-
-        Fields to be attached to all messages sent through this
-        handler can be specified as keyword arguments. This probably
-        makes sense only for SYSLOG_IDENTIFIER and similar fields
-        which are constant for the whole program:
-
-        >>> journal.JournalHandler(SYSLOG_IDENTIFIER='my-cool-app')
-
-        The following journal fields will be sent:
-        `MESSAGE`, `PRIORITY`, `THREAD_NAME`, `CODE_FILE`, `CODE_LINE`,
-        `CODE_FUNC`, `LOGGER` (name as supplied to getLogger call),
-        `MESSAGE_ID` (optional, see above), `SYSLOG_IDENTIFIER` (defaults
-        to sys.argv[0]).
-        """
-
-        def __init__(self, level=_logging.NOTSET, **kwargs):
-                super(JournalHandler, self).__init__(level)
-
-                for name in kwargs:
-                        if not _valid_field_name(name):
-                                raise ValueError('Invalid field name: ' + name)
-                if 'SYSLOG_IDENTIFIER' not in kwargs:
-                        kwargs['SYSLOG_IDENTIFIER'] = _sys.argv[0]
-                self._extra = kwargs
-
-        def emit(self, record):
-                """Write record as journal event.
-
-                MESSAGE is taken from the message provided by the
-                user, and PRIORITY, LOGGER, THREAD_NAME,
-                CODE_{FILE,LINE,FUNC} fields are appended
-                automatically. In addition, record.MESSAGE_ID will be
-                used if present.
-                """
-                try:
-                        msg = self.format(record)
-                        pri = self.mapPriority(record.levelno)
-                        mid = getattr(record, 'MESSAGE_ID', None)
-                        send(msg,
-                             MESSAGE_ID=mid,
-                             PRIORITY=format(pri),
-                             LOGGER=record.name,
-                             THREAD_NAME=record.threadName,
-                             CODE_FILE=record.pathname,
-                             CODE_LINE=record.lineno,
-                             CODE_FUNC=record.funcName,
-                             **self._extra)
-                except Exception:
-                        self.handleError(record)
-
-        @staticmethod
-        def mapPriority(levelno):
-                """Map logging levels to journald priorities.
-
-                Since Python log level numbers are "sparse", we have
-                to map numbers in between the standard levels too.
-                """
-                if levelno <= _logging.DEBUG:
-                        return LOG_DEBUG
-                elif levelno <= _logging.INFO:
-                        return LOG_INFO
-                elif levelno <= _logging.WARNING:
-                        return LOG_WARNING
-                elif levelno <= _logging.ERROR:
-                        return LOG_ERR
-                elif levelno <= _logging.CRITICAL:
-                        return LOG_CRIT
-                else:
-                        return LOG_ALERT
diff --git a/src/python-systemd/login.c b/src/python-systemd/login.c
deleted file mode 100644 (file)
index e844f5f..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#define PY_SSIZE_T_CLEAN
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#include <Python.h>
-#pragma GCC diagnostic pop
-
-#include "systemd/sd-login.h"
-#include "pyutil.h"
-#include "util.h"
-#include "strv.h"
-
-PyDoc_STRVAR(module__doc__,
-             "Python interface to the libsystemd-login library."
-);
-
-#define helper(name)                                                    \
-static PyObject* name(PyObject *self, PyObject *args) {                 \
-        _cleanup_strv_free_ char **list = NULL;                         \
-        int r;                                                          \
-        PyObject *ans;                                                  \
-                                                                        \
-        assert(args == NULL);                                           \
-                                                                        \
-        r = sd_get_##name(&list);                                       \
-        if (r < 0) {                                                    \
-                errno = -r;                                             \
-                return PyErr_SetFromErrno(PyExc_IOError);               \
-        }                                                               \
-                                                                        \
-        ans = PyList_New(r);                                            \
-        if (!ans)                                                       \
-                return NULL;                                            \
-                                                                        \
-        for (r--; r >= 0; r--) {                                        \
-                PyObject *s = unicode_FromString(list[r]);              \
-                if (!s) {                                               \
-                        Py_DECREF(ans);                                 \
-                        return NULL;                                    \
-                }                                                       \
-                                                                        \
-                PyList_SetItem(ans, r, s);                              \
-        }                                                               \
-                                                                        \
-        return ans;                                                     \
-}
-
-helper(seats)
-helper(sessions)
-helper(machine_names)
-#undef helper
-
-static PyObject* uids(PyObject *self, PyObject *args) {
-        _cleanup_free_ uid_t *list = NULL;
-        int r;
-        PyObject *ans;
-
-        assert(args == NULL);
-
-        r = sd_get_uids(&list);
-        if (r < 0) {
-                errno = -r;
-                return PyErr_SetFromErrno(PyExc_IOError);
-        }
-
-        ans = PyList_New(r);
-        if (!ans)
-                return NULL;
-
-        for (r--; r >= 0; r--) {
-                PyObject *s = long_FromLong(list[r]);
-                if (!s) {
-                        Py_DECREF(ans);
-                        return NULL;
-                }
-
-                PyList_SetItem(ans, r, s);
-        }
-
-        return ans;
-}
-
-PyDoc_STRVAR(seats__doc__,
-             "seats() -> list\n\n"
-             "Returns a list of currently available local seats.\n"
-             "Wraps sd_get_seats(3)."
-);
-
-PyDoc_STRVAR(sessions__doc__,
-             "sessions() -> list\n\n"
-             "Returns a list of current login sessions.\n"
-             "Wraps sd_get_sessions(3)."
-);
-
-PyDoc_STRVAR(machine_names__doc__,
-             "machine_names() -> list\n\n"
-             "Returns a list of currently running virtual machines\n"
-             "and containers on the system.\n"
-             "Wraps sd_get_machine_names(3)."
-);
-
-PyDoc_STRVAR(uids__doc__,
-             "uids() -> list\n\n"
-             "Returns a list of uids of users who currently have login sessions.\n"
-             "Wraps sd_get_uids(3)."
-);
-
-static PyMethodDef methods[] = {
-        { "seats", seats, METH_NOARGS, seats__doc__},
-        { "sessions", sessions, METH_NOARGS, sessions__doc__},
-        { "machine_names", machine_names, METH_NOARGS, machine_names__doc__},
-        { "uids", uids, METH_NOARGS, uids__doc__},
-        {} /* Sentinel */
-};
-
-
-typedef struct {
-        PyObject_HEAD
-        sd_login_monitor *monitor;
-} Monitor;
-static PyTypeObject MonitorType;
-
-static void Monitor_dealloc(Monitor* self) {
-        sd_login_monitor_unref(self->monitor);
-        Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-PyDoc_STRVAR(Monitor__doc__,
-             "Monitor([category]) -> ...\n\n"
-             "Monitor may be used to monitor login sessions, users, seats,\n"
-             "and virtual machines/containers. Monitor provides a file\n"
-             "descriptor which can be integrated in an external event loop.\n"
-             "See man:sd_login_monitor_new(3) for the details about what\n"
-             "can be monitored.");
-static int Monitor_init(Monitor *self, PyObject *args, PyObject *keywds) {
-        const char *category = NULL;
-        int r;
-
-        static const char* const kwlist[] = {"category", NULL};
-        if (!PyArg_ParseTupleAndKeywords(args, keywds, "|z:__init__", (char**) kwlist,
-                                         &category))
-                return -1;
-
-        Py_BEGIN_ALLOW_THREADS
-        r = sd_login_monitor_new(category, &self->monitor);
-        Py_END_ALLOW_THREADS
-
-        return set_error(r, NULL, "Invalid category");
-}
-
-
-PyDoc_STRVAR(Monitor_fileno__doc__,
-             "fileno() -> int\n\n"
-             "Get a file descriptor to poll for events.\n"
-             "This method wraps sd_login_monitor_get_fd(3).");
-static PyObject* Monitor_fileno(Monitor *self, PyObject *args) {
-        int fd = sd_login_monitor_get_fd(self->monitor);
-        set_error(fd, NULL, NULL);
-        if (fd < 0)
-                return NULL;
-        return long_FromLong(fd);
-}
-
-
-PyDoc_STRVAR(Monitor_get_events__doc__,
-             "get_events() -> int\n\n"
-             "Returns a mask of poll() events to wait for on the file\n"
-             "descriptor returned by .fileno().\n\n"
-             "See man:sd_login_monitor_get_events(3) for further discussion.");
-static PyObject* Monitor_get_events(Monitor *self, PyObject *args) {
-        int r = sd_login_monitor_get_events(self->monitor);
-        set_error(r, NULL, NULL);
-        if (r < 0)
-                return NULL;
-        return long_FromLong(r);
-}
-
-
-PyDoc_STRVAR(Monitor_get_timeout__doc__,
-             "get_timeout() -> int or None\n\n"
-             "Returns a timeout value for usage in poll(), the time since the\n"
-             "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
-             "is necessary.\n\n"
-             "The return value must be converted to a relative timeout in\n"
-             "milliseconds if it is to be used as an argument for poll().\n"
-             "See man:sd_login_monitor_get_timeout(3) for further discussion.");
-static PyObject* Monitor_get_timeout(Monitor *self, PyObject *args) {
-        int r;
-        uint64_t t;
-
-        r = sd_login_monitor_get_timeout(self->monitor, &t);
-        set_error(r, NULL, NULL);
-        if (r < 0)
-                return NULL;
-
-        if (t == (uint64_t) -1)
-                Py_RETURN_NONE;
-
-        assert_cc(sizeof(unsigned long long) == sizeof(t));
-        return PyLong_FromUnsignedLongLong(t);
-}
-
-
-PyDoc_STRVAR(Monitor_get_timeout_ms__doc__,
-             "get_timeout_ms() -> int\n\n"
-             "Returns a timeout value suitable for usage in poll(), the value\n"
-             "returned by .get_timeout() converted to relative ms, or -1 if\n"
-             "no timeout is necessary.");
-static PyObject* Monitor_get_timeout_ms(Monitor *self, PyObject *args) {
-        int r;
-        uint64_t t;
-
-        r = sd_login_monitor_get_timeout(self->monitor, &t);
-        set_error(r, NULL, NULL);
-        if (r < 0)
-                return NULL;
-
-        return absolute_timeout(t);
-}
-
-
-PyDoc_STRVAR(Monitor_close__doc__,
-             "close() -> None\n\n"
-             "Free resources allocated by this Monitor object.\n"
-             "This method invokes sd_login_monitor_unref().\n"
-             "See man:sd_login_monitor_unref(3).");
-static PyObject* Monitor_close(Monitor *self, PyObject *args) {
-        assert(self);
-        assert(!args);
-
-        sd_login_monitor_unref(self->monitor);
-        self->monitor = NULL;
-        Py_RETURN_NONE;
-}
-
-
-PyDoc_STRVAR(Monitor_flush__doc__,
-             "flush() -> None\n\n"
-             "Reset the wakeup state of the monitor object.\n"
-             "This method invokes sd_login_monitor_flush().\n"
-             "See man:sd_login_monitor_flush(3).");
-static PyObject* Monitor_flush(Monitor *self, PyObject *args) {
-        assert(self);
-        assert(!args);
-
-        Py_BEGIN_ALLOW_THREADS
-        sd_login_monitor_flush(self->monitor);
-        Py_END_ALLOW_THREADS
-        Py_RETURN_NONE;
-}
-
-
-PyDoc_STRVAR(Monitor___enter____doc__,
-             "__enter__() -> self\n\n"
-             "Part of the context manager protocol.\n"
-             "Returns self.\n");
-static PyObject* Monitor___enter__(PyObject *self, PyObject *args) {
-        assert(self);
-        assert(!args);
-
-        Py_INCREF(self);
-        return self;
-}
-
-
-PyDoc_STRVAR(Monitor___exit____doc__,
-             "__exit__(type, value, traceback) -> None\n\n"
-             "Part of the context manager protocol.\n"
-             "Closes the monitor..\n");
-static PyObject* Monitor___exit__(Monitor *self, PyObject *args) {
-        return Monitor_close(self, args);
-}
-
-
-static PyMethodDef Monitor_methods[] = {
-        {"fileno",          (PyCFunction) Monitor_fileno, METH_NOARGS, Monitor_fileno__doc__},
-        {"get_events",      (PyCFunction) Monitor_get_events, METH_NOARGS, Monitor_get_events__doc__},
-        {"get_timeout",     (PyCFunction) Monitor_get_timeout, METH_NOARGS, Monitor_get_timeout__doc__},
-        {"get_timeout_ms",  (PyCFunction) Monitor_get_timeout_ms, METH_NOARGS, Monitor_get_timeout_ms__doc__},
-        {"close",           (PyCFunction) Monitor_close, METH_NOARGS, Monitor_close__doc__},
-        {"flush",           (PyCFunction) Monitor_flush, METH_NOARGS, Monitor_flush__doc__},
-        {"__enter__",       (PyCFunction) Monitor___enter__, METH_NOARGS, Monitor___enter____doc__},
-        {"__exit__",        (PyCFunction) Monitor___exit__, METH_VARARGS, Monitor___exit____doc__},
-        {}  /* Sentinel */
-};
-
-static PyTypeObject MonitorType = {
-        PyVarObject_HEAD_INIT(NULL, 0)
-        .tp_name = "login.Monitor",
-        .tp_basicsize = sizeof(Monitor),
-        .tp_dealloc = (destructor) Monitor_dealloc,
-        .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
-        .tp_doc = Monitor__doc__,
-        .tp_methods = Monitor_methods,
-        .tp_init = (initproc) Monitor_init,
-        .tp_new = PyType_GenericNew,
-};
-
-#if PY_MAJOR_VERSION < 3
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC initlogin(void) {
-        PyObject *m;
-
-        if (PyType_Ready(&MonitorType) < 0)
-                return;
-
-        m = Py_InitModule3("login", methods, module__doc__);
-        if (m == NULL)
-                return;
-
-        PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
-
-        Py_INCREF(&MonitorType);
-        PyModule_AddObject(m, "Monitor", (PyObject *) &MonitorType);
-}
-REENABLE_WARNING;
-
-#else
-
-static struct PyModuleDef module = {
-        PyModuleDef_HEAD_INIT,
-        "login", /* name of module */
-        module__doc__, /* module documentation, may be NULL */
-        -1, /* size of per-interpreter state of the module */
-        methods
-};
-
-DISABLE_WARNING_MISSING_PROTOTYPES;
-PyMODINIT_FUNC PyInit_login(void) {
-        PyObject *m;
-
-        if (PyType_Ready(&MonitorType) < 0)
-                return NULL;
-
-        m = PyModule_Create(&module);
-        if (m == NULL)
-                return NULL;
-
-        if (PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
-                Py_DECREF(m);
-                return NULL;
-        }
-
-        Py_INCREF(&MonitorType);
-        if (PyModule_AddObject(m, "Monitor", (PyObject *) &MonitorType)) {
-                Py_DECREF(&MonitorType);
-                Py_DECREF(m);
-                return NULL;
-        }
-
-        return m;
-}
-REENABLE_WARNING;
-
-#endif
diff --git a/src/python-systemd/pyutil.c b/src/python-systemd/pyutil.c
deleted file mode 100644 (file)
index 722c4f5..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  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 <Python.h>
-#include "pyutil.h"
-
-void cleanup_Py_DECREFp(PyObject **p) {
-        if (!*p)
-                return;
-
-        Py_DECREF(*p);
-}
-
-PyObject* absolute_timeout(uint64_t t) {
-        if (t == (uint64_t) -1)
-                return PyLong_FromLong(-1);
-        else {
-                struct timespec ts;
-                uint64_t n;
-                int msec;
-
-                clock_gettime(CLOCK_MONOTONIC, &ts);
-                n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
-                msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
-
-                return PyLong_FromLong(msec);
-        }
-}
-
-int set_error(int r, const char* path, const char* invalid_message) {
-        if (r >= 0)
-                return r;
-        if (r == -EINVAL && invalid_message)
-                PyErr_SetString(PyExc_ValueError, invalid_message);
-        else if (r == -ENOMEM)
-                PyErr_SetString(PyExc_MemoryError, "Not enough memory");
-        else {
-                errno = -r;
-                PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
-        }
-        return -1;
-}
-
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-int Unicode_FSConverter(PyObject* obj, void *_result) {
-        PyObject **result = _result;
-
-        assert(result);
-
-        if (!obj)
-                /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so
-                 * we can assume that it was PyUnicode_FSConverter. */
-                return PyUnicode_FSConverter(obj, result);
-
-        if (obj == Py_None) {
-                *result = NULL;
-                return 1;
-        }
-
-        return PyUnicode_FSConverter(obj, result);
-}
-#endif
diff --git a/src/python-systemd/pyutil.h b/src/python-systemd/pyutil.h
deleted file mode 100644 (file)
index 1477e7b..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Zbigniew JÄ™drzejewski-Szmek <zbyszek@in.waw.pl>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifndef Py_TYPE
-/* avoid duplication warnings from errors in Python 2.7 headers */
-# include <Python.h>
-#endif
-
-void cleanup_Py_DECREFp(PyObject **p);
-PyObject* absolute_timeout(uint64_t t);
-int set_error(int r, const char* path, const char* invalid_message);
-
-#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
-int Unicode_FSConverter(PyObject* obj, void *_result);
-#endif
-
-#define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp)))
-
-#if PY_MAJOR_VERSION >=3
-# define unicode_FromStringAndSize PyUnicode_FromStringAndSize
-# define unicode_FromString PyUnicode_FromString
-# define long_FromLong PyLong_FromLong
-# define long_FromSize_t PyLong_FromSize_t
-# define long_Check PyLong_Check
-# define long_AsLong PyLong_AsLong
-#else
-/* Python 3 type naming convention is used */
-# define unicode_FromStringAndSize PyString_FromStringAndSize
-# define unicode_FromString PyString_FromString
-# define long_FromLong PyInt_FromLong
-# define long_FromSize_t PyInt_FromSize_t
-# define long_Check PyInt_Check
-# define long_AsLong PyInt_AsLong
-#endif
index f9448e3bc5fe96c9113902064ebd9f34794ca56a..0edba415b60b39a9ab997fe04e3bc0e1fe280354 100644 (file)
@@ -89,10 +89,6 @@ static int resolve_host(sd_bus *bus, const char *name) {
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_set_auto_start(req, false);
-        if (r < 0)
-                return bus_log_create_error(r);
-
         r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
         if (r < 0)
                 return bus_log_create_error(r);
index a3e740896f762a3969930701ed88012c9c85ba05..63b4b36e88d0b0fae45ef94c41e2ae4f77429d8f 100644 (file)
@@ -43,3 +43,8 @@ int dns_type_from_string(const char *s) {
 
         return sc->id;
 }
+
+/* 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);
+}
index 86951d233a3dac8fd0e7f4c87bb2736b1e30bfc8..950af36ee30027b91df4d7b61012950d3c83dece 100644 (file)
@@ -25,6 +25,7 @@
 
 const char *dns_type_to_string(int type);
 int dns_type_from_string(const char *s);
+bool dns_type_is_pseudo(int n);
 
 /* DNS record types, taken from
  * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
index bb74b1828e35ed531986b0efa979e1ff30ad1fd3..39951a362c83e5fa2d3dc80a91fb8b5982e75764 100644 (file)
@@ -32,10 +32,10 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
 
         assert(ret);
 
-        if (mtu <= 0)
+        if (mtu <= UDP_PACKET_HEADER_SIZE)
                 a = DNS_PACKET_SIZE_START;
         else
-                a = mtu;
+                a = mtu - UDP_PACKET_HEADER_SIZE;
 
         if (a < DNS_PACKET_HEADER_SIZE)
                 a = DNS_PACKET_HEADER_SIZE;
@@ -166,10 +166,17 @@ int dns_packet_validate_reply(DnsPacket *p) {
         if (DNS_PACKET_OPCODE(p) != 0)
                 return -EBADMSG;
 
-        /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */
-        if (p->protocol == DNS_PROTOCOL_LLMNR &&
-            DNS_PACKET_QDCOUNT(p) != 1)
-                return -EBADMSG;
+        switch (p->protocol) {
+        case DNS_PROTOCOL_LLMNR:
+                /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */
+                if (DNS_PACKET_QDCOUNT(p) != 1)
+                        return -EBADMSG;
+
+                break;
+
+        default:
+                break;
+        }
 
         return 1;
 }
@@ -192,18 +199,25 @@ int dns_packet_validate_query(DnsPacket *p) {
         if (DNS_PACKET_TC(p))
                 return -EBADMSG;
 
-        /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
-        if (p->protocol == DNS_PROTOCOL_LLMNR &&
-            DNS_PACKET_QDCOUNT(p) != 1)
-                return -EBADMSG;
+        switch (p->protocol) {
+        case DNS_PROTOCOL_LLMNR:
+                /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
+                if (DNS_PACKET_QDCOUNT(p) != 1)
+                        return -EBADMSG;
 
-        /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */
-        if (DNS_PACKET_ANCOUNT(p) > 0)
-                return -EBADMSG;
+                /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */
+                if (DNS_PACKET_ANCOUNT(p) > 0)
+                        return -EBADMSG;
 
-        /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */
-        if (DNS_PACKET_NSCOUNT(p) > 0)
-                return -EBADMSG;
+                /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */
+                if (DNS_PACKET_NSCOUNT(p) > 0)
+                        return -EBADMSG;
+
+                break;
+
+        default:
+                break;
+        }
 
         return 1;
 }
@@ -261,7 +275,7 @@ static void dns_packet_truncate(DnsPacket *p, size_t sz) {
         if (p->size <= sz)
                 return;
 
-        HASHMAP_FOREACH_KEY(s, n, p->names, i) {
+        HASHMAP_FOREACH_KEY(n, s, p->names, i) {
 
                 if (PTR_TO_SIZE(n) < sz)
                         continue;
@@ -488,6 +502,82 @@ fail:
         return r;
 }
 
+static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) {
+        size_t saved_size;
+        int r;
+
+        assert(p);
+        assert(types);
+        assert(length > 0);
+
+        saved_size = p->size;
+
+        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_blob(p, types, length, NULL);
+        if (r < 0)
+                goto fail;
+
+        if (start)
+                *start = saved_size;
+
+        return 0;
+fail:
+        dns_packet_truncate(p, saved_size);
+        return r;
+}
+
+static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
+        Iterator i;
+        uint8_t window = 0;
+        uint8_t entry = 0;
+        uint8_t bitmaps[32] = {};
+        unsigned n;
+        size_t saved_size;
+        int r;
+
+        assert(p);
+        assert(types);
+
+        saved_size = p->size;
+
+        BITMAP_FOREACH(n, types, i) {
+                assert(n <= 0xffff);
+
+                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;
+
+                        zero(bitmaps);
+                }
+
+                window = n >> 8;
+
+                entry = n & 255;
+
+                bitmaps[entry / 8] |= 1 << (7 - (entry % 8));
+        }
+
+        r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
+        if (r < 0)
+                goto fail;
+
+        if (start)
+                *start = saved_size;
+
+        return 0;
+fail:
+        dns_packet_truncate(p, saved_size);
+        return r;
+}
+
 int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) {
         size_t saved_size, rdlength_offset, end, rdlength;
         int r;
@@ -638,6 +728,22 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 r = dns_packet_append_uint32(p, rr->loc.altitude, NULL);
                 break;
 
+        case DNS_TYPE_DS:
+                r = dns_packet_append_uint16(p, rr->ds.key_tag, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint8(p, rr->ds.algorithm, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint8(p, rr->ds.digest_type, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_blob(p, rr->ds.digest, rr->ds.digest_size, NULL);
+                break;
+
         case DNS_TYPE_SSHFP:
                 r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL);
                 if (r < 0)
@@ -647,7 +753,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_blob(p, rr->sshfp.key, rr->sshfp.key_size, NULL);
+                r = dns_packet_append_blob(p, rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, NULL);
                 break;
 
         case DNS_TYPE_DNSKEY:
@@ -691,7 +797,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_uint8(p, rr->rrsig.key_tag, NULL);
+                r = dns_packet_append_uint16(p, rr->rrsig.key_tag, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -702,6 +808,50 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL);
                 break;
 
+        case DNS_TYPE_NSEC:
+                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_types(p, rr->nsec.types, NULL);
+                if (r < 0)
+                        goto fail;
+
+                break;
+        case DNS_TYPE_NSEC3:
+                r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint8(p, rr->nsec3.flags, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint16(p, rr->nsec3.iterations, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint8(p, rr->nsec3.salt_size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_blob(p, rr->nsec3.salt, rr->nsec3.salt_size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_uint8(p, rr->nsec3.next_hashed_name_size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_blob(p, rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_types(p, rr->nsec3.types, NULL);
+                if (r < 0)
+                        goto fail;
+
+                break;
         case _DNS_TYPE_INVALID: /* unparseable */
         default:
 
@@ -775,6 +925,42 @@ int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) {
         return 0;
 }
 
+static int dns_packet_read_memdup(
+                DnsPacket *p, size_t size,
+                void **ret, size_t *ret_size,
+                size_t *ret_start) {
+
+        const void *src;
+        size_t start;
+        int r;
+
+        assert(p);
+        assert(ret);
+
+        r = dns_packet_read(p, size, &src, &start);
+        if (r < 0)
+                return r;
+
+        if (size <= 0)
+                *ret = NULL;
+        else {
+                void *copy;
+
+                copy = memdup(src, size);
+                if (!copy)
+                        return -ENOMEM;
+
+                *ret = copy;
+        }
+
+        if (ret_size)
+                *ret_size = size;
+        if (ret_start)
+                *ret_start = start;
+
+        return 0;
+}
+
 int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
         const void *d;
         int r;
@@ -966,6 +1152,115 @@ fail:
         return r;
 }
 
+static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) {
+        uint8_t window;
+        uint8_t length;
+        const uint8_t *bitmap;
+        uint8_t bit = 0;
+        unsigned i;
+        bool found = false;
+        size_t saved_rindex;
+        int r;
+
+        assert(p);
+        assert(types);
+
+        saved_rindex = p->rindex;
+
+        r = bitmap_ensure_allocated(types);
+        if (r < 0)
+                goto fail;
+
+        r = dns_packet_read_uint8(p, &window, NULL);
+        if (r < 0)
+                goto fail;
+
+        r = dns_packet_read_uint8(p, &length, NULL);
+        if (r < 0)
+                goto fail;
+
+        if (length == 0 || length > 32)
+                return -EBADMSG;
+
+        r = dns_packet_read(p, length, (const void **)&bitmap, NULL);
+        if (r < 0)
+                goto fail;
+
+        for (i = 0; i < length; i++) {
+                uint8_t bitmask = 1 << 7;
+
+                if (!bitmap[i]) {
+                        found = false;
+                        bit += 8;
+                        continue;
+                }
+
+                found = true;
+
+                while (bitmask) {
+                        if (bitmap[i] & bitmask) {
+                                uint16_t n;
+
+                                n = (uint16_t) window << 8 | (uint16_t) bit;
+
+                                /* Ignore pseudo-types. see RFC4034 section 4.1.2 */
+                                if (dns_type_is_pseudo(n))
+                                        continue;
+
+                                r = bitmap_set(*types, n);
+                                if (r < 0)
+                                        goto fail;
+                        }
+
+                        bit ++;
+                        bitmask >>= 1;
+                }
+        }
+
+        if (!found)
+                return -EBADMSG;
+
+        if (start)
+                *start = saved_rindex;
+
+        return 0;
+fail:
+        dns_packet_rewind(p, saved_rindex);
+        return r;
+}
+
+static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) {
+        size_t saved_rindex;
+        int r;
+
+        saved_rindex = p->rindex;
+
+        while (p->rindex < saved_rindex + size) {
+                r = dns_packet_read_type_window(p, types, NULL);
+                if (r < 0)
+                        goto fail;
+
+                /* don't read past end of current RR */
+                if (p->rindex > saved_rindex + size) {
+                        r = -EBADMSG;
+                        goto fail;
+                }
+        }
+
+        if (p->rindex != saved_rindex + size) {
+                r = -EBADMSG;
+                goto fail;
+        }
+
+        if (start)
+                *start = saved_rindex;
+
+        return 0;
+fail:
+        dns_packet_rewind(p, saved_rindex);
+        return r;
+}
+
 int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
         _cleanup_free_ char *name = NULL;
         uint16_t class, type;
@@ -1008,26 +1303,6 @@ fail:
         return r;
 }
 
-static int dns_packet_read_public_key(DnsPacket *p, size_t length,
-                                      void **dp, size_t *lengthp,
-                                      size_t *start) {
-        int r;
-        const void *d;
-        void *d2;
-
-        r = dns_packet_read(p, length, &d, NULL);
-        if (r < 0)
-                return r;
-
-        d2 = memdup(d, length);
-        if (!d2)
-                return -ENOMEM;
-
-        *dp = d2;
-        *lengthp = length;
-        return 0;
-}
-
 static bool loc_size_ok(uint8_t size) {
         uint8_t m = size >> 4, e = size & 0xF;
 
@@ -1050,7 +1325,6 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
         size_t saved_rindex, offset;
         uint16_t rdlength;
-        const void *d;
         int r;
 
         assert(p);
@@ -1248,6 +1522,33 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 }
         }
 
+        case DNS_TYPE_DS:
+                r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_memdup(p, rdlength - 4,
+                                           &rr->ds.digest, &rr->ds.digest_size,
+                                           NULL);
+                if (r < 0)
+                        goto fail;
+
+                if (rr->ds.digest_size <= 0) {
+                        /* the accepted size depends on the algorithm, but for now
+                           just ensure that the value is greater than zero */
+                        r = -EBADMSG;
+                        goto fail;
+                }
+
+                break;
         case DNS_TYPE_SSHFP:
                 r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
                 if (r < 0)
@@ -1257,9 +1558,17 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_read_public_key(p, rdlength - 2,
-                                               &rr->sshfp.key, &rr->sshfp.key_size,
-                                               NULL);
+                r = dns_packet_read_memdup(p, rdlength - 2,
+                                           &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size,
+                                           NULL);
+
+                if (rr->sshfp.fingerprint_size <= 0) {
+                        /* the accepted size depends on the algorithm, but for now
+                           just ensure that the value is greater than zero */
+                        r = -EBADMSG;
+                        goto fail;
+                }
+
                 break;
 
         case DNS_TYPE_DNSKEY: {
@@ -1288,9 +1597,17 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_read_public_key(p, rdlength - 4,
-                                               &rr->dnskey.key, &rr->dnskey.key_size,
-                                               NULL);
+                r = dns_packet_read_memdup(p, rdlength - 4,
+                                           &rr->dnskey.key, &rr->dnskey.key_size,
+                                           NULL);
+
+                if (rr->dnskey.key_size <= 0) {
+                        /* the accepted size depends on the algorithm, but for now
+                           just ensure that the value is greater than zero */
+                        r = -EBADMSG;
+                        goto fail;
+                }
+
                 break;
         }
 
@@ -1327,24 +1644,87 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_read_public_key(p, offset + rdlength - p->rindex,
-                                               &rr->rrsig.signature, &rr->rrsig.signature_size,
-                                               NULL);
+                r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
+                                           &rr->rrsig.signature, &rr->rrsig.signature_size,
+                                           NULL);
+
+                if (rr->rrsig.signature_size <= 0) {
+                        /* the accepted size depends on the algorithm, but for now
+                           just ensure that the value is greater than zero */
+                        r = -EBADMSG;
+                        goto fail;
+                }
+
                 break;
 
-        default:
-        unparseable:
-                r = dns_packet_read(p, rdlength, &d, NULL);
+        case DNS_TYPE_NSEC:
+                r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL);
                 if (r < 0)
                         goto fail;
 
-                rr->generic.data = memdup(d, rdlength);
-                if (!rr->generic.data) {
-                        r = -ENOMEM;
+                r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
+                if (r < 0)
+                        goto fail;
+
+                /* 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;
                 }
 
-                rr->generic.size = rdlength;
+                break;
+
+        case DNS_TYPE_NSEC3: {
+                uint8_t size;
+
+                r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL);
+                if (r < 0)
+                        goto fail;
+
+                /* this may be zero */
+                r = dns_packet_read_uint8(p, &size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_uint8(p, &size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                if (size <= 0) {
+                        r = -EBADMSG;
+                        goto fail;
+                }
+
+                r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
+                if (r < 0)
+                        goto fail;
+
+                /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */
+
+                break;
+        }
+        default:
+        unparseable:
+                r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.size, NULL);
+                if (r < 0)
+                        goto fail;
                 break;
         }
         if (r < 0)
@@ -1466,13 +1846,15 @@ static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
 DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol);
 
 static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
-        [DNSSEC_ALGORITHM_RSAMD5]     = "RSAMD5",
-        [DNSSEC_ALGORITHM_DH]         = "DH",
-        [DNSSEC_ALGORITHM_DSA]        = "DSA",
-        [DNSSEC_ALGORITHM_ECC]        = "ECC",
-        [DNSSEC_ALGORITHM_RSASHA1]    = "RSASHA1",
-        [DNSSEC_ALGORITHM_INDIRECT]   = "INDIRECT",
-        [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
-        [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
+        [DNSSEC_ALGORITHM_RSAMD5]             = "RSAMD5",
+        [DNSSEC_ALGORITHM_DH]                 = "DH",
+        [DNSSEC_ALGORITHM_DSA]                = "DSA",
+        [DNSSEC_ALGORITHM_ECC]                = "ECC",
+        [DNSSEC_ALGORITHM_RSASHA1]            = "RSASHA1",
+        [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1]     = "DSA-NSEC3-SHA1",
+        [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
+        [DNSSEC_ALGORITHM_INDIRECT]           = "INDIRECT",
+        [DNSSEC_ALGORITHM_PRIVATEDNS]         = "PRIVATEDNS",
+        [DNSSEC_ALGORITHM_PRIVATEOID]         = "PRIVATEOID",
 };
 DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);
index c5867386c67fa9f1dbd4f597a29b99e0ca0241df..58559c85df4be6ab440b70a314706b9770430ef1 100644 (file)
@@ -21,6 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
 
+#include <netinet/udp.h>
+#include <netinet/ip.h>
 
 #include "macro.h"
 #include "sparse-endian.h"
@@ -53,6 +55,7 @@ struct DnsPacketHeader {
 };
 
 #define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader)
+#define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr))
 
 /* The various DNS protocols deviate in how large a packet can grow,
    but the TCP transport has a 16bit size field, hence that appears to
@@ -99,10 +102,18 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
 #define DNS_PACKET_ID(p) DNS_PACKET_HEADER(p)->id
 #define DNS_PACKET_QR(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 15) & 1)
 #define DNS_PACKET_OPCODE(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 11) & 15)
-#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15)
+#define DNS_PACKET_AA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 10) & 1)
 #define DNS_PACKET_TC(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 9) & 1)
-#define DNS_PACKET_C(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 10) & 1)
-#define DNS_PACKET_T(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 8) & 1)
+#define DNS_PACKET_RD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 8) & 1)
+#define DNS_PACKET_RA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 7) & 1)
+#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
+#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
+#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15)
+
+/* LLMNR defines some bits differently */
+#define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p)
+#define DNS_PACKET_LLMNR_T(p) DNS_PACKET_RD(p)
+
 #define DNS_PACKET_QDCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->qdcount)
 #define DNS_PACKET_ANCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->ancount)
 #define DNS_PACKET_NSCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->nscount)
@@ -212,6 +223,8 @@ enum {
         DNSSEC_ALGORITHM_DSA,
         DNSSEC_ALGORITHM_ECC,
         DNSSEC_ALGORITHM_RSASHA1,
+        DNSSEC_ALGORITHM_DSA_NSEC3_SHA1,
+        DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
         DNSSEC_ALGORITHM_INDIRECT = 252,
         DNSSEC_ALGORITHM_PRIVATEDNS,
         DNSSEC_ALGORITHM_PRIVATEOID,
index 4d71f5e3d400385a932ffe3e57db1fff13bec8ed..0efe740d1a97c25e39c492a59997926a616c2df5 100644 (file)
@@ -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;
index 4ba2fe9f0ed215fe07c39918d58d043b3999271a..fc98677798cb3c98dbe315551d2faff966c816c5 100644 (file)
@@ -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);
 
index c1818eef9c7f2f78dc9a58623933f5760a7683f4..ad7ca26cfea72eeba961e46b10ce855810c36c59 100644 (file)
@@ -171,19 +171,19 @@ const struct hash_ops dns_resource_key_hash_ops = {
 };
 
 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
-        char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)];
+        char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
         const char *c, *t;
         char *s;
 
         c = dns_class_to_string(key->class);
         if (!c) {
-                sprintf(cbuf, "%i", key->class);
+                sprintf(cbuf, "CLASS%u", key->class);
                 c = cbuf;
         }
 
         t = dns_type_to_string(key->type);
         if (!t){
-                sprintf(tbuf, "%i", key->type);
+                sprintf(tbuf, "TYPE%u", key->type);
                 t = tbuf;
         }
 
@@ -271,8 +271,12 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
                         free(rr->mx.exchange);
                         break;
 
+                case DNS_TYPE_DS:
+                        free(rr->ds.digest);
+                        break;
+
                 case DNS_TYPE_SSHFP:
-                        free(rr->sshfp.key);
+                        free(rr->sshfp.fingerprint);
                         break;
 
                 case DNS_TYPE_DNSKEY:
@@ -284,6 +288,17 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
                         free(rr->rrsig.signature);
                         break;
 
+                case DNS_TYPE_NSEC:
+                        free(rr->nsec.next_domain_name);
+                        bitmap_free(rr->nsec.types);
+                        break;
+
+                case DNS_TYPE_NSEC3:
+                        free(rr->nsec3.next_hashed_name);
+                        free(rr->nsec3.salt);
+                        bitmap_free(rr->nsec3.types);
+                        break;
+
                 case DNS_TYPE_LOC:
                 case DNS_TYPE_A:
                 case DNS_TYPE_AAAA:
@@ -409,11 +424,18 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
                        a->loc.longitude == b->loc.longitude &&
                        a->loc.altitude == b->loc.altitude;
 
+        case DNS_TYPE_DS:
+                return a->ds.key_tag == b->ds.key_tag &&
+                       a->ds.algorithm == b->ds.algorithm &&
+                       a->ds.digest_type == b->ds.digest_type &&
+                       a->ds.digest_size == b->ds.digest_size &&
+                       memcmp(a->ds.digest, b->ds.digest, a->ds.digest_size) == 0;
+
         case DNS_TYPE_SSHFP:
                 return a->sshfp.algorithm == b->sshfp.algorithm &&
                        a->sshfp.fptype == b->sshfp.fptype &&
-                       a->sshfp.key_size == b->sshfp.key_size &&
-                       memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0;
+                       a->sshfp.fingerprint_size == b->sshfp.fingerprint_size &&
+                       memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0;
 
         case DNS_TYPE_DNSKEY:
                 return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag &&
@@ -437,6 +459,19 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
 
                 return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
 
+        case DNS_TYPE_NSEC:
+                return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
+                       bitmap_equal(a->nsec.types, b->nsec.types);
+
+        case DNS_TYPE_NSEC3:
+                return a->nsec3.algorithm == b->nsec3.algorithm &&
+                    a->nsec3.flags == b->nsec3.flags &&
+                    a->nsec3.iterations == b->nsec3.iterations &&
+                    a->nsec3.salt_size == b->nsec3.salt_size &&
+                    memcmp(a->nsec3.salt, b->nsec3.salt, a->nsec3.salt_size) == 0 &&
+                    memcmp(a->nsec3.next_hashed_name, b->nsec3.next_hashed_name, a->nsec3.next_hashed_name_size) == 0 &&
+                    bitmap_equal(a->nsec3.types, b->nsec3.types);
+
         default:
                 return a->generic.size == b->generic.size &&
                         memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
@@ -474,6 +509,53 @@ static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t alt
         return s;
 }
 
+static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
+        struct tm tm;
+
+        assert(buf);
+        assert(l > strlen("YYYYMMDDHHmmSS"));
+
+        if (!gmtime_r(&sec, &tm))
+                return -EINVAL;
+
+        if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
+                return -EINVAL;
+
+        return 0;
+}
+
+static char *format_types(Bitmap *types) {
+        _cleanup_strv_free_ char **strv = NULL;
+        _cleanup_free_ char *str = NULL;
+        Iterator i;
+        unsigned type;
+        int r;
+
+        BITMAP_FOREACH(type, types, i) {
+                if (dns_type_to_string(type)) {
+                        r = strv_extend(&strv, dns_type_to_string(type));
+                        if (r < 0)
+                                return NULL;
+                } else {
+                        char *t;
+
+                        r = asprintf(&t, "TYPE%u", type);
+                        if (r < 0)
+                                return NULL;
+
+                        r = strv_consume(&strv, t);
+                        if (r < 0)
+                                return NULL;
+                }
+        }
+
+        str = strv_join(strv, " ");
+        if (!str)
+                return NULL;
+
+        return strjoin("( ", str, " )", NULL);
+}
+
 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
         _cleanup_free_ char *k = NULL, *t = NULL;
         char *s;
@@ -589,8 +671,23 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                         return -ENOMEM;
                 break;
 
+        case DNS_TYPE_DS:
+                t = hexmem(rr->ds.digest, rr->ds.digest_size);
+                if (!t)
+                        return -ENOMEM;
+
+                r = asprintf(&s, "%s %u %u %u %s",
+                             k,
+                             rr->ds.key_tag,
+                             rr->ds.algorithm,
+                             rr->ds.digest_type,
+                             t);
+                if (r < 0)
+                        return -ENOMEM;
+                break;
+
         case DNS_TYPE_SSHFP:
-                t = hexmem(rr->sshfp.key, rr->sshfp.key_size);
+                t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
                 if (!t)
                         return -ENOMEM;
 
@@ -608,7 +705,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
 
                 alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
 
-                t = hexmem(rr->dnskey.key, rr->dnskey.key_size);
+                t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
                 if (!t)
                         return -ENOMEM;
 
@@ -625,18 +722,27 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
 
         case DNS_TYPE_RRSIG: {
                 const char *type, *alg;
+                char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
 
                 type = dns_type_to_string(rr->rrsig.type_covered);
                 alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
 
-                t = hexmem(rr->rrsig.signature, rr->rrsig.signature_size);
+                t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
                 if (!t)
                         return -ENOMEM;
 
+                r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
+                if (r < 0)
+                        return r;
+
+                r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
+                if (r < 0)
+                        return r;
+
                 /* TYPE?? follows
                  * http://tools.ietf.org/html/rfc3597#section-5 */
 
-                r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %u %u %u %s %s",
+                r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
                              k,
                              type ?: "TYPE",
                              type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
@@ -644,8 +750,8 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
                              rr->rrsig.labels,
                              rr->rrsig.original_ttl,
-                             rr->rrsig.expiration,
-                             rr->rrsig.inception,
+                             expiration,
+                             inception,
                              rr->rrsig.key_tag,
                              rr->rrsig.signer,
                              t);
@@ -654,13 +760,57 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                 break;
         }
 
+        case DNS_TYPE_NSEC:
+                t = format_types(rr->nsec.types);
+                if (!t)
+                        return -ENOMEM;
+
+                r = asprintf(&s, "%s %s %s",
+                             k,
+                             rr->nsec.next_domain_name,
+                             t);
+                if (r < 0)
+                        return -ENOMEM;
+                break;
+
+        case DNS_TYPE_NSEC3: {
+                _cleanup_free_ char *salt = NULL, *hash = NULL;
+
+                if (rr->nsec3.salt_size > 0) {
+                        salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
+                        if (!salt)
+                                return -ENOMEM;
+                }
+
+                hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
+                if (!hash)
+                        return -ENOMEM;
+
+                t = format_types(rr->nsec3.types);
+                if (!t)
+                        return -ENOMEM;
+
+                r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
+                             k,
+                             rr->nsec3.algorithm,
+                             rr->nsec3.flags,
+                             rr->nsec3.iterations,
+                             rr->nsec3.salt_size > 0 ? salt : "-",
+                             hash,
+                             t);
+                if (r < 0)
+                        return -ENOMEM;
+
+                break;
+        }
+
         default:
                 t = hexmem(rr->generic.data, rr->generic.size);
                 if (!t)
                         return -ENOMEM;
 
-                s = strjoin(k, " ", t, NULL);
-                if (!s)
+                r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
+                if (r < 0)
                         return -ENOMEM;
                 break;
         }
@@ -690,7 +840,7 @@ int dns_class_from_string(const char *s, uint16_t *class) {
         if (strcaseeq(s, "IN"))
                 *class = DNS_CLASS_IN;
         else if (strcaseeq(s, "ANY"))
-                *class = DNS_TYPE_ANY;
+                *class = DNS_CLASS_ANY;
         else
                 return -EINVAL;
 
index 26796c842b9b7cb7102c646f54f9407772c7448d..0f40f3ceef1410bd6907846a2912caa69a31b70c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <netinet/in.h>
 
+#include "bitmap.h"
 #include "hashmap.h"
 #include "in-addr-util.h"
 #include "dns-type.h"
@@ -52,7 +53,7 @@ struct DnsResourceRecord {
         union {
                 struct {
                         void *data;
-                        uint16_t size;
+                        size_t size;
                 } generic;
 
                 struct {
@@ -108,11 +109,20 @@ struct DnsResourceRecord {
                         uint32_t altitude;
                 } loc;
 
+                struct {
+                        uint16_t key_tag;
+                        uint8_t algorithm;
+                        uint8_t digest_type;
+                        void *digest;
+                        size_t digest_size;
+                } ds;
+
+                /* https://tools.ietf.org/html/rfc4255#section-3.1 */
                 struct {
                         uint8_t algorithm;
                         uint8_t fptype;
-                        void *key;
-                        size_t key_size;
+                        void *fingerprint;
+                        size_t fingerprint_size;
                 } sshfp;
 
                 /* http://tools.ietf.org/html/rfc4034#section-2.1 */
@@ -137,6 +147,22 @@ struct DnsResourceRecord {
                         void *signature;
                         size_t signature_size;
                 } rrsig;
+
+                struct {
+                        char *next_domain_name;
+                        Bitmap *types;
+                } nsec;
+
+                struct {
+                        uint8_t algorithm;
+                        uint8_t flags;
+                        uint16_t iterations;
+                        void *salt;
+                        size_t salt_size;
+                        void *next_hashed_name;
+                        size_t next_hashed_name_size;
+                        Bitmap *types;
+                } nsec3;
         };
 };
 
index c25ac2216dcc1df73875edc984ce84dc5cbad951..4bc415702882fdc80b345ec1b27056012636d72e 100644 (file)
@@ -28,6 +28,7 @@
 #include "random-util.h"
 #include "hostname-util.h"
 #include "dns-domain.h"
+#include "resolved-llmnr.h"
 #include "resolved-dns-scope.h"
 
 #define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
@@ -124,17 +125,17 @@ void dns_scope_next_dns_server(DnsScope *s) {
                 manager_next_dns_server(s->manager);
 }
 
-int dns_scope_emit(DnsScope *s, DnsPacket *p) {
+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;
@@ -143,33 +144,18 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
                 mtu = manager_find_mtu(s->manager);
 
         if (s->protocol == DNS_PROTOCOL_DNS) {
-                DnsServer *srv;
-
                 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 > mtu)
+                if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
                         return -EMSGSIZE;
 
-                if (family == AF_INET)
-                        fd = manager_dns_ipv4_fd(s->manager);
-                else if (family == AF_INET6)
-                        fd = manager_dns_ipv6_fd(s->manager);
-                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) {
 
@@ -180,7 +166,7 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
                         return -EBUSY;
 
                 family = s->family;
-                port = 5355;
+                port = LLMNR_PORT;
 
                 if (family == AF_INET) {
                         addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
@@ -192,17 +178,18 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) {
                         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;
-
         return 1;
 }
 
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
+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 = {};
         socklen_t salen;
@@ -213,8 +200,6 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
         assert((family == AF_UNSPEC) == !address);
 
         if (family == AF_UNSPEC) {
-                DnsServer *srv;
-
                 srv = dns_scope_get_dns_server(s);
                 if (!srv)
                         return -ESRCH;
@@ -247,13 +232,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);
@@ -287,12 +274,23 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
         if (r < 0 && errno != EINPROGRESS)
                 return -errno;
 
+        if (server)
+                *server = srv;
+
         ret = fd;
         fd = -1;
 
         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;
 
@@ -315,6 +313,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 &&
@@ -415,19 +418,6 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
         return 0;
 }
 
-int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
-        assert(s);
-        assert(address);
-
-        if (s->protocol != DNS_PROTOCOL_DNS)
-                return 1;
-
-        if (s->link)
-                return !!link_find_dns_server(s->link,  family, address);
-        else
-                return !!manager_find_dns_server(s->manager, family, address);
-}
-
 static int dns_scope_make_reply_packet(
                 DnsScope *s,
                 uint16_t id,
@@ -546,7 +536,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
                 return;
         }
 
-        if (DNS_PACKET_C(p)) {
+        if (DNS_PACKET_LLMNR_C(p)) {
                 /* Somebody notified us about a possible conflict */
                 dns_scope_verify_conflicts(s, p);
                 return;
@@ -695,7 +685,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
                         return 0;
                 }
 
-                r = dns_scope_emit(scope, p);
+                r = dns_scope_emit(scope, -1, p);
                 if (r < 0)
                         log_debug_errno(r, "Failed to send conflict packet: %m");
         }
@@ -760,10 +750,10 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
         if (DNS_PACKET_RRCOUNT(p) <= 0)
                 return;
 
-        if (DNS_PACKET_C(p) != 0)
+        if (DNS_PACKET_LLMNR_C(p) != 0)
                 return;
 
-        if (DNS_PACKET_T(p) != 0)
+        if (DNS_PACKET_LLMNR_T(p) != 0)
                 return;
 
         if (manager_our_packet(scope->manager, p))
index cfbde1343f49641234e6aa6764d5750f2409fb2b..29479ad55090e2464ed309782858f0182ea5fdcc 100644 (file)
@@ -65,12 +65,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, DnsPacket *p);
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port);
+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);
-int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address);
 
 DnsServer *dns_scope_get_dns_server(DnsScope *s);
 void dns_scope_next_dns_server(DnsScope *s);
index 9a62a63258a6765b8fb291b6269a8383bd9955e7..92e48ae442d25b0ccd3bbf0722aac3a3688a03c9 100644 (file)
@@ -41,6 +41,7 @@ int dns_server_new(
         if (!s)
                 return -ENOMEM;
 
+        s->n_ref = 1;
         s->type = type;
         s->family = family;
         s->address = *in_addr;
@@ -74,33 +75,46 @@ int dns_server_new(
         return 0;
 }
 
-DnsServer* dns_server_free(DnsServer *s)  {
+DnsServer* dns_server_ref(DnsServer *s)  {
         if (!s)
                 return NULL;
 
-        if (s->link) {
-                if (s->type == DNS_SERVER_LINK)
-                        LIST_REMOVE(servers, s->link->dns_servers, s);
+        assert(s->n_ref > 0);
 
-                if (s->link->current_dns_server == s)
-                        link_set_dns_server(s->link, NULL);
-        }
+        s->n_ref ++;
 
-        if (s->manager) {
-                if (s->type == DNS_SERVER_SYSTEM)
-                        LIST_REMOVE(servers, s->manager->dns_servers, s);
-                else if (s->type == DNS_SERVER_FALLBACK)
-                        LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+        return s;
+}
+
+static DnsServer* dns_server_free(DnsServer *s)  {
+        if (!s)
+                return NULL;
 
-                if (s->manager->current_dns_server == s)
-                        manager_set_dns_server(s->manager, NULL);
-        }
+        if (s->link && s->link->current_dns_server == s)
+                link_set_dns_server(s->link, NULL);
+
+        if (s->manager && s->manager->current_dns_server == s)
+                manager_set_dns_server(s->manager, NULL);
 
         free(s);
 
         return NULL;
 }
 
+DnsServer* dns_server_unref(DnsServer *s)  {
+        if (!s)
+                return NULL;
+
+        assert(s->n_ref > 0);
+
+        if (s->n_ref == 1)
+                dns_server_free(s);
+        else
+                s->n_ref --;
+
+        return NULL;
+}
+
 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;
index 70ff35b08fb8efc7a1c90a792c133b49253e935f..06059e8829ee8016efb97e58831c57ad5099db50 100644 (file)
@@ -37,6 +37,8 @@ typedef enum DnsServerType {
 struct DnsServer {
         Manager *manager;
 
+        unsigned n_ref;
+
         DnsServerType type;
 
         Link *link;
@@ -57,6 +59,9 @@ int dns_server_new(
                 int family,
                 const union in_addr_union *address);
 
-DnsServer* dns_server_free(DnsServer *s);
+DnsServer* dns_server_ref(DnsServer *s);
+DnsServer* dns_server_unref(DnsServer *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
 
 extern const struct hash_ops dns_server_hash_ops;
index 214938986d14dc37cdce9efb8dbeeccbfc0f39ba..8a93b265c6b04fe01c13c4343b0fad3a7134f0fd 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "af-list.h"
 
+#include "resolved-llmnr.h"
 #include "resolved-dns-transaction.h"
 #include "random-util.h"
 
@@ -38,6 +39,10 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
         dns_packet_unref(t->received);
         dns_answer_unref(t->cached);
 
+        sd_event_source_unref(t->dns_event_source);
+        safe_close(t->dns_fd);
+
+        dns_server_unref(t->server);
         dns_stream_free(t->stream);
 
         if (t->scope) {
@@ -87,6 +92,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
         if (!t)
                 return -ENOMEM;
 
+        t->dns_fd = -1;
+
         t->question = dns_question_ref(q);
 
         do
@@ -236,6 +243,7 @@ static int on_stream_complete(DnsStream *s, int error) {
 }
 
 static int dns_transaction_open_tcp(DnsTransaction *t) {
+        DnsServer *server = NULL;
         _cleanup_close_ int fd = -1;
         int r;
 
@@ -245,12 +253,12 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
                 return 0;
 
         if (t->scope->protocol == DNS_PROTOCOL_DNS)
-                fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
+                fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
         else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
 
                 /* When we already received a query to this (but it was truncated), send to its sender address */
                 if (t->received)
-                        fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
+                        fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
                 else {
                         union in_addr_union address;
                         int family = AF_UNSPEC;
@@ -264,7 +272,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
                         if (r == 0)
                                 return -EINVAL;
 
-                        fd = dns_scope_tcp_socket(t->scope, family, &address, 5355);
+                        fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
                 }
         } else
                 return -EAFNOSUPPORT;
@@ -284,6 +292,9 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
                 return r;
         }
 
+
+        dns_server_unref(t->server);
+        t->server = dns_server_ref(server);
         t->received = dns_packet_unref(t->received);
         t->stream->complete = on_stream_complete;
         t->stream->transaction = t;
@@ -297,6 +308,16 @@ 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) {
         int r;
 
@@ -323,25 +344,12 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 /* Tentative packets are not full responses but still
                  * useful for identifying uniqueness conflicts during
                  * probing. */
-                if (DNS_PACKET_T(p)) {
+                if (DNS_PACKET_LLMNR_T(p)) {
                         dns_transaction_tentative(t, p);
                         return;
                 }
         }
 
-        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 one of
-                 * a valid DNS server */
-
-                if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
-                        return;
-
-                if (p->sender_port != 53)
-                        return;
-        }
-
         if (t->received != p) {
                 dns_packet_unref(t->received);
                 t->received = dns_packet_ref(p);
@@ -378,7 +386,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) {
@@ -397,6 +405,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 return;
         }
 
+        /* Only consider responses with equivalent query section to the request */
+        r = dns_question_is_equal(p->question, t->question);
+        if (r <= 0) {
+                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                return;
+        }
+
         /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
         dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
 
@@ -406,6 +421,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;
@@ -414,7 +479,7 @@ 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);
 
         r = dns_transaction_go(t);
         if (r < 0)
@@ -571,7 +636,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 r = dns_transaction_open_tcp(t);
         } else {
                 /* Try via UDP, and if that fails due to large size try via TCP */
-                r = dns_scope_emit(t->scope, t->sent);
+                r = dns_transaction_emit(t);
                 if (r == -EMSGSIZE)
                         r = dns_transaction_open_tcp(t);
         }
@@ -579,15 +644,14 @@ int dns_transaction_go(DnsTransaction *t) {
                 /* No servers to send this to? */
                 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
                 return 0;
-        }
-        if (r < 0) {
+        } else if (r < 0) {
                 if (t->scope->protocol != DNS_PROTOCOL_DNS) {
                         dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
                         return 0;
                 }
 
                 /* 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);
         }
index f6d539d315165787b86cc7ef684177a57a95b25e..a8f4267bc84463a4fdf949a44d7d67f214d3f1c3 100644 (file)
@@ -61,6 +61,12 @@ struct DnsTransaction {
         sd_event_source *timeout_event_source;
         unsigned n_attempts;
 
+        int dns_fd;
+        sd_event_source *dns_event_source;
+
+        /* the active server */
+        DnsServer *server;
+
         /* TCP connection logic, if we need it */
         DnsStream *stream;
 
index ff8dc3a5bc3062e4f1c0cb0e7d541a5a99db225e..d66b3a88fc400448074552010f364f02988dd87e 100644 (file)
@@ -58,7 +58,6 @@ int link_new(Manager *m, Link **ret, int ifindex) {
 }
 
 Link *link_free(Link *l) {
-
         if (!l)
                 return NULL;
 
@@ -68,8 +67,12 @@ Link *link_free(Link *l) {
         if (l->manager)
                 hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
 
-        while (l->dns_servers)
-                dns_server_free(l->dns_servers);
+        while (l->dns_servers) {
+                DnsServer *s = l->dns_servers;
+
+                LIST_REMOVE(servers, l->dns_servers, s);
+                dns_server_unref(s);
+        }
 
         dns_scope_free(l->unicast_scope);
         dns_scope_free(l->llmnr_ipv4_scope);
@@ -182,14 +185,20 @@ static int link_update_dns_servers(Link *l) {
         }
 
         LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
-                if (s->marked)
-                        dns_server_free(s);
+                if (s->marked) {
+                        LIST_REMOVE(servers, l->dns_servers, s);
+                        dns_server_unref(s);
+                }
 
         return 0;
 
 clear:
-        while (l->dns_servers)
-                dns_server_free(l->dns_servers);
+        while (l->dns_servers) {
+                s = l->dns_servers;
+
+                LIST_REMOVE(servers, l->dns_servers, s);
+                dns_server_unref(s);
+        }
 
         return r;
 }
diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c
new file mode 100644 (file)
index 0000000..8afaf8d
--- /dev/null
@@ -0,0 +1,473 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Tom Gundersen <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  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 <resolv.h>
+#include <netinet/in.h>
+
+#include "resolved-manager.h"
+#include "resolved-llmnr.h"
+
+void manager_llmnr_stop(Manager *m) {
+        assert(m);
+
+        m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source);
+        m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
+
+        m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source);
+        m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
+
+        m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
+        m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
+
+        m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
+        m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
+}
+
+int manager_llmnr_start(Manager *m) {
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_support == SUPPORT_NO)
+                return 0;
+
+        r = manager_llmnr_ipv4_udp_fd(m);
+        if (r == -EADDRINUSE)
+                goto eaddrinuse;
+        if (r < 0)
+                return r;
+
+        r = manager_llmnr_ipv4_tcp_fd(m);
+        if (r == -EADDRINUSE)
+                goto eaddrinuse;
+        if (r < 0)
+                return r;
+
+        if (socket_ipv6_is_supported()) {
+                r = manager_llmnr_ipv6_udp_fd(m);
+                if (r == -EADDRINUSE)
+                        goto eaddrinuse;
+                if (r < 0)
+                        return r;
+
+                r = manager_llmnr_ipv6_tcp_fd(m);
+                if (r == -EADDRINUSE)
+                        goto eaddrinuse;
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+
+eaddrinuse:
+        log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support.");
+        m->llmnr_support = SUPPORT_NO;
+        manager_llmnr_stop(m);
+
+        return 0;
+}
+
+static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        DnsTransaction *t = NULL;
+        Manager *m = userdata;
+        DnsScope *scope;
+        int r;
+
+        r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
+        if (r <= 0)
+                return r;
+
+        scope = manager_find_scope(m, p);
+        if (!scope) {
+                log_warning("Got LLMNR UDP packet on unknown scope. Ignoring.");
+                return 0;
+        }
+
+        if (dns_packet_validate_reply(p) > 0) {
+                log_debug("Got LLMNR reply packet for id %u", DNS_PACKET_ID(p));
+
+                dns_scope_check_conflicts(scope, p);
+
+                t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
+                if (t)
+                        dns_transaction_process_reply(t, p);
+
+        } else if (dns_packet_validate_query(p) > 0)  {
+                log_debug("Got LLMNR query packet for id %u", DNS_PACKET_ID(p));
+
+                dns_scope_process_query(scope, NULL, p);
+        } else
+                log_debug("Invalid LLMNR UDP packet.");
+
+        return 0;
+}
+
+int manager_llmnr_ipv4_udp_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(LLMNR_PORT),
+        };
+        static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_ipv4_udp_fd >= 0)
+                return m->llmnr_ipv4_udp_fd;
+
+        m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->llmnr_ipv4_udp_fd < 0)
+                return -errno;
+
+        /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        /* Disable Don't-Fragment bit in the IP header */
+        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m);
+        if (r < 0)
+                goto fail;
+
+        return m->llmnr_ipv4_udp_fd;
+
+fail:
+        m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
+        return r;
+}
+
+int manager_llmnr_ipv6_udp_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(LLMNR_PORT),
+        };
+        static const int one = 1, ttl = 255;
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_ipv6_udp_fd >= 0)
+                return m->llmnr_ipv6_udp_fd;
+
+        m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->llmnr_ipv6_udp_fd < 0)
+                return -errno;
+
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
+        if (r < 0)  {
+                r = -errno;
+                goto fail;
+        }
+
+        return m->llmnr_ipv6_udp_fd;
+
+fail:
+        m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
+        return r;
+}
+
+static int on_llmnr_stream_packet(DnsStream *s) {
+        DnsScope *scope;
+
+        assert(s);
+
+        scope = manager_find_scope(s->manager, s->read_packet);
+        if (!scope) {
+                log_warning("Got LLMNR TCP packet on unknown scope. Ignroing.");
+                return 0;
+        }
+
+        if (dns_packet_validate_query(s->read_packet) > 0) {
+                log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet));
+
+                dns_scope_process_query(scope, s, s->read_packet);
+
+                /* If no reply packet was set, we free the stream */
+                if (s->write_packet)
+                        return 0;
+        } else
+                log_debug("Invalid LLMNR TCP packet.");
+
+        dns_stream_free(s);
+        return 0;
+}
+
+static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        DnsStream *stream;
+        Manager *m = userdata;
+        int cfd, r;
+
+        cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+        if (cfd < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                return -errno;
+        }
+
+        r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd);
+        if (r < 0) {
+                safe_close(cfd);
+                return r;
+        }
+
+        stream->on_packet = on_llmnr_stream_packet;
+        return 0;
+}
+
+int manager_llmnr_ipv4_tcp_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in.sin_family = AF_INET,
+                .in.sin_port = htobe16(LLMNR_PORT),
+        };
+        static const int one = 1, pmtu = IP_PMTUDISC_DONT;
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_ipv4_tcp_fd >= 0)
+                return m->llmnr_ipv4_tcp_fd;
+
+        m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->llmnr_ipv4_tcp_fd < 0)
+                return -errno;
+
+        /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        /* Disable Don't-Fragment bit in the IP header */
+        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN);
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m);
+        if (r < 0)
+                goto fail;
+
+        return m->llmnr_ipv4_tcp_fd;
+
+fail:
+        m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
+        return r;
+}
+
+int manager_llmnr_ipv6_tcp_fd(Manager *m) {
+        union sockaddr_union sa = {
+                .in6.sin6_family = AF_INET6,
+                .in6.sin6_port = htobe16(LLMNR_PORT),
+        };
+        static const int one = 1;
+        int r;
+
+        assert(m);
+
+        if (m->llmnr_ipv6_tcp_fd >= 0)
+                return m->llmnr_ipv6_tcp_fd;
+
+        m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->llmnr_ipv6_tcp_fd < 0)
+                return -errno;
+
+        /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN);
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
+        if (r < 0)  {
+                r = -errno;
+                goto fail;
+        }
+
+        return m->llmnr_ipv6_tcp_fd;
+
+fail:
+        m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
+        return r;
+}
diff --git a/src/resolve/resolved-llmnr.h b/src/resolve/resolved-llmnr.h
new file mode 100644 (file)
index 0000000..d489d48
--- /dev/null
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-manager.h"
+
+#define LLMNR_PORT 5355
+
+int manager_llmnr_ipv4_udp_fd(Manager *m);
+int manager_llmnr_ipv6_udp_fd(Manager *m);
+int manager_llmnr_ipv4_tcp_fd(Manager *m);
+int manager_llmnr_ipv6_tcp_fd(Manager *m);
+
+void manager_llmnr_stop(Manager *m);
+int manager_llmnr_start(Manager *m);
index dee5e6192219c2206f033a38ea985cd90b3d59ae..5be01d3cb8a7ae079d56e3ad59476e0099d579e0 100644 (file)
@@ -38,6 +38,7 @@
 #include "resolved-conf.h"
 #include "resolved-bus.h"
 #include "resolved-manager.h"
+#include "resolved-llmnr.h"
 
 #define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC)
 
@@ -393,66 +394,6 @@ static int manager_watch_hostname(Manager *m) {
         return 0;
 }
 
-static void manager_llmnr_stop(Manager *m) {
-        assert(m);
-
-        m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source);
-        m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
-
-        m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source);
-        m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
-
-        m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
-        m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
-
-        m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
-        m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
-}
-
-static int manager_llmnr_start(Manager *m) {
-        int r;
-
-        assert(m);
-
-        if (m->llmnr_support == SUPPORT_NO)
-                return 0;
-
-        r = manager_llmnr_ipv4_udp_fd(m);
-        if (r == -EADDRINUSE)
-                goto eaddrinuse;
-        if (r < 0)
-                return r;
-
-        r = manager_llmnr_ipv4_tcp_fd(m);
-        if (r == -EADDRINUSE)
-                goto eaddrinuse;
-        if (r < 0)
-                return r;
-
-        if (socket_ipv6_is_supported()) {
-                r = manager_llmnr_ipv6_udp_fd(m);
-                if (r == -EADDRINUSE)
-                        goto eaddrinuse;
-                if (r < 0)
-                        return r;
-
-                r = manager_llmnr_ipv6_tcp_fd(m);
-                if (r == -EADDRINUSE)
-                        goto eaddrinuse;
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-
-eaddrinuse:
-        log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support.");
-        m->llmnr_support = SUPPORT_NO;
-        manager_llmnr_stop(m);
-
-        return 0;
-}
-
 int manager_new(Manager **ret) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
@@ -463,7 +404,6 @@ int manager_new(Manager **ret) {
         if (!m)
                 return -ENOMEM;
 
-        m->dns_ipv4_fd = m->dns_ipv6_fd = -1;
         m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
         m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
         m->hostname_fd = -1;
@@ -545,11 +485,6 @@ Manager *manager_free(Manager *m) {
         sd_event_source_unref(m->network_event_source);
         sd_network_monitor_unref(m->network_monitor);
 
-        sd_event_source_unref(m->dns_ipv4_event_source);
-        sd_event_source_unref(m->dns_ipv6_event_source);
-        safe_close(m->dns_ipv4_fd);
-        safe_close(m->dns_ipv6_fd);
-
         manager_llmnr_stop(m);
 
         sd_bus_slot_unref(m->prepare_for_sleep_slot);
@@ -662,8 +597,10 @@ int manager_read_resolv_conf(Manager *m) {
         }
 
         LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers)
-                if (s->marked)
-                        dns_server_free(s);
+                if (s->marked) {
+                        LIST_REMOVE(servers, m->dns_servers, s);
+                        dns_server_unref(s);
+                }
 
         /* Whenever /etc/resolv.conf changes, start using the first
          * DNS server of it. This is useful to deal with broken
@@ -678,8 +615,12 @@ int manager_read_resolv_conf(Manager *m) {
         return 0;
 
 clear:
-        while (m->dns_servers)
-                dns_server_free(m->dns_servers);
+        while (m->dns_servers) {
+                s = m->dns_servers;
+
+                LIST_REMOVE(servers, m->dns_servers, s);
+                dns_server_unref(s);
+        }
 
         return r;
 }
@@ -971,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;
@@ -982,97 +925,38 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
         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 = NULL;
-        Manager *m = userdata;
+static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
         int r;
 
-        r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
-        if (r <= 0)
-                return r;
+        assert(fd >= 0);
+        assert(mh);
 
-        if (dns_packet_validate_reply(p) > 0) {
-                t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
-                if (!t)
+        for (;;) {
+                if (sendmsg(fd, mh, flags) >= 0)
                         return 0;
 
-                dns_transaction_process_reply(t, p);
-
-        } else
-                log_debug("Invalid DNS packet.");
-
-        return 0;
-}
-
-int manager_dns_ipv4_fd(Manager *m) {
-        const int one = 1;
-        int r;
-
-        assert(m);
-
-        if (m->dns_ipv4_fd >= 0)
-                return m->dns_ipv4_fd;
-
-        m->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->dns_ipv4_fd < 0)
-                return -errno;
-
-        r = setsockopt(m->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = sd_event_add_io(m->event, &m->dns_ipv4_event_source, m->dns_ipv4_fd, EPOLLIN, on_dns_packet, m);
-        if (r < 0)
-                goto fail;
-
-        return m->dns_ipv4_fd;
-
-fail:
-        m->dns_ipv4_fd = safe_close(m->dns_ipv4_fd);
-        return r;
-}
-
-int manager_dns_ipv6_fd(Manager *m) {
-        const int one = 1;
-        int r;
-
-        assert(m);
-
-        if (m->dns_ipv6_fd >= 0)
-                return m->dns_ipv6_fd;
+                if (errno == EINTR)
+                        continue;
 
-        m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->dns_ipv6_fd < 0)
-                return -errno;
+                if (errno != EAGAIN)
+                        return -errno;
 
-        r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
+                r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -ETIMEDOUT;
         }
-
-        r = sd_event_add_io(m->event, &m->dns_ipv6_event_source, m->dns_ipv6_fd, EPOLLIN, on_dns_packet, m);
-        if (r < 0)
-                goto fail;
-
-        return m->dns_ipv6_fd;
-
-fail:
-        m->dns_ipv6_fd = safe_close(m->dns_ipv6_fd);
-        return r;
 }
 
-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(mh);
+        assert(message);
 
         for (;;) {
-                if (sendmsg(fd, mh, flags) >= 0)
+                if (write(fd, message, length) >= 0)
                         return 0;
 
                 if (errno == EINTR)
@@ -1089,6 +973,18 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
         }
 }
 
+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,
@@ -1316,393 +1212,6 @@ uint32_t manager_find_mtu(Manager *m) {
         return mtu;
 }
 
-static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-        DnsTransaction *t = NULL;
-        Manager *m = userdata;
-        DnsScope *scope;
-        int r;
-
-        r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
-        if (r <= 0)
-                return r;
-
-        scope = manager_find_scope(m, p);
-        if (!scope) {
-                log_warning("Got LLMNR UDP packet on unknown scope. Ignoring.");
-                return 0;
-        }
-
-        if (dns_packet_validate_reply(p) > 0) {
-                log_debug("Got reply packet for id %u", DNS_PACKET_ID(p));
-
-                dns_scope_check_conflicts(scope, p);
-
-                t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
-                if (t)
-                        dns_transaction_process_reply(t, p);
-
-        } else if (dns_packet_validate_query(p) > 0)  {
-                log_debug("Got query packet for id %u", DNS_PACKET_ID(p));
-
-                dns_scope_process_query(scope, NULL, p);
-        } else
-                log_debug("Invalid LLMNR UDP packet.");
-
-        return 0;
-}
-
-int manager_llmnr_ipv4_udp_fd(Manager *m) {
-        union sockaddr_union sa = {
-                .in.sin_family = AF_INET,
-                .in.sin_port = htobe16(5355),
-        };
-        static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
-        int r;
-
-        assert(m);
-
-        if (m->llmnr_ipv4_udp_fd >= 0)
-                return m->llmnr_ipv4_udp_fd;
-
-        m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->llmnr_ipv4_udp_fd < 0)
-                return -errno;
-
-        /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        /* Disable Don't-Fragment bit in the IP header */
-        r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m);
-        if (r < 0)
-                goto fail;
-
-        return m->llmnr_ipv4_udp_fd;
-
-fail:
-        m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
-        return r;
-}
-
-int manager_llmnr_ipv6_udp_fd(Manager *m) {
-        union sockaddr_union sa = {
-                .in6.sin6_family = AF_INET6,
-                .in6.sin6_port = htobe16(5355),
-        };
-        static const int one = 1, ttl = 255;
-        int r;
-
-        assert(m);
-
-        if (m->llmnr_ipv6_udp_fd >= 0)
-                return m->llmnr_ipv6_udp_fd;
-
-        m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->llmnr_ipv6_udp_fd < 0)
-                return -errno;
-
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
-        if (r < 0)  {
-                r = -errno;
-                goto fail;
-        }
-
-        return m->llmnr_ipv6_udp_fd;
-
-fail:
-        m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
-        return r;
-}
-
-static int on_llmnr_stream_packet(DnsStream *s) {
-        DnsScope *scope;
-
-        assert(s);
-
-        scope = manager_find_scope(s->manager, s->read_packet);
-        if (!scope) {
-                log_warning("Got LLMNR TCP packet on unknown scope. Ignroing.");
-                return 0;
-        }
-
-        if (dns_packet_validate_query(s->read_packet) > 0) {
-                log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet));
-
-                dns_scope_process_query(scope, s, s->read_packet);
-
-                /* If no reply packet was set, we free the stream */
-                if (s->write_packet)
-                        return 0;
-        } else
-                log_debug("Invalid LLMNR TCP packet.");
-
-        dns_stream_free(s);
-        return 0;
-}
-
-static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        DnsStream *stream;
-        Manager *m = userdata;
-        int cfd, r;
-
-        cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
-        if (cfd < 0) {
-                if (errno == EAGAIN || errno == EINTR)
-                        return 0;
-
-                return -errno;
-        }
-
-        r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd);
-        if (r < 0) {
-                safe_close(cfd);
-                return r;
-        }
-
-        stream->on_packet = on_llmnr_stream_packet;
-        return 0;
-}
-
-int manager_llmnr_ipv4_tcp_fd(Manager *m) {
-        union sockaddr_union sa = {
-                .in.sin_family = AF_INET,
-                .in.sin_port = htobe16(5355),
-        };
-        static const int one = 1, pmtu = IP_PMTUDISC_DONT;
-        int r;
-
-        assert(m);
-
-        if (m->llmnr_ipv4_tcp_fd >= 0)
-                return m->llmnr_ipv4_tcp_fd;
-
-        m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->llmnr_ipv4_tcp_fd < 0)
-                return -errno;
-
-        /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        /* Disable Don't-Fragment bit in the IP header */
-        r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN);
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m);
-        if (r < 0)
-                goto fail;
-
-        return m->llmnr_ipv4_tcp_fd;
-
-fail:
-        m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
-        return r;
-}
-
-int manager_llmnr_ipv6_tcp_fd(Manager *m) {
-        union sockaddr_union sa = {
-                .in6.sin6_family = AF_INET6,
-                .in6.sin6_port = htobe16(5355),
-        };
-        static const int one = 1;
-        int r;
-
-        assert(m);
-
-        if (m->llmnr_ipv6_tcp_fd >= 0)
-                return m->llmnr_ipv6_tcp_fd;
-
-        m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (m->llmnr_ipv6_tcp_fd < 0)
-                return -errno;
-
-        /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN);
-        if (r < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
-        if (r < 0)  {
-                r = -errno;
-                goto fail;
-        }
-
-        return m->llmnr_ipv6_tcp_fd;
-
-fail:
-        m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
-        return r;
-}
-
 int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) {
         LinkAddress *a;
 
@@ -1827,15 +1336,25 @@ void manager_verify_all(Manager *m) {
 }
 
 void manager_flush_dns_servers(Manager *m, DnsServerType t) {
+        DnsServer *s;
+
         assert(m);
 
         if (t == DNS_SERVER_SYSTEM)
-                while (m->dns_servers)
-                        dns_server_free(m->dns_servers);
+                while (m->dns_servers) {
+                        s = m->dns_servers;
+
+                        LIST_REMOVE(servers, m->dns_servers, s);
+                        dns_server_unref(s);
+                }
 
         if (t == DNS_SERVER_FALLBACK)
-                while (m->fallback_dns_servers)
-                        dns_server_free(m->fallback_dns_servers);
+                while (m->fallback_dns_servers) {
+                        s = m->fallback_dns_servers;
+
+                        LIST_REMOVE(servers, m->fallback_dns_servers, s);
+                        dns_server_unref(s);
+                }
 }
 
 static const char* const support_table[_SUPPORT_MAX] = {
index 0f4ffad141f1cd8d41a153316ef6215fe83ee183..53b5acb33cf1082d718e5e307cfa69fddf0d1ef8 100644 (file)
@@ -65,12 +65,6 @@ struct Manager {
         unsigned n_dns_streams;
 
         /* Unicast dns */
-        int dns_ipv4_fd;
-        int dns_ipv6_fd;
-
-        sd_event_source *dns_ipv4_event_source;
-        sd_event_source *dns_ipv6_event_source;
-
         LIST_HEAD(DnsServer, dns_servers);
         LIST_HEAD(DnsServer, fallback_dns_servers);
         DnsServer *current_dns_server;
@@ -125,16 +119,10 @@ 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);
 
-int manager_dns_ipv4_fd(Manager *m);
-int manager_dns_ipv6_fd(Manager *m);
-int manager_llmnr_ipv4_udp_fd(Manager *m);
-int manager_llmnr_ipv6_udp_fd(Manager *m);
-int manager_llmnr_ipv4_tcp_fd(Manager *m);
-int manager_llmnr_ipv6_tcp_fd(Manager *m);
-
 int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);
 LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr);
 
index 5a90c778fb4033f2e25c1c24356b46efa1295b98..904dec6bfcc7aeda3991ed8d5644eec1c73747b8 100644 (file)
@@ -127,7 +127,7 @@ int main(int argc, char *argv[]) {
                         return EXIT_SUCCESS;
                 }
 
-                r = write_string_file(saved, value);
+                r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
                 if (r < 0) {
                         log_error_errno(r, "Failed to write %s: %m", saved);
                         return EXIT_FAILURE;
index 20a44ce4e11bf4ba84ffd6446ad193c095cae370..8a0dec154070ffb32b2e3d0c297d909ccf0ef8c9 100644 (file)
@@ -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;
index 00caf5d7001034dd71b91f767272b5e500461f1e..bd50ad3e6d473537b1ea26d82cf04d878f72b6b3 100644 (file)
@@ -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);
 
index 0d6ecf52cfe690bf59725648d0d4474c2bdfd718..347cd30b09dd41558039d6b4301e5f55a0e9d798 100644 (file)
@@ -125,7 +125,19 @@ static int get_os_indications(uint64_t *os_indication) {
                 return r;
 
         r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
-        if (r < 0)
+        if (r == -ENOENT) {
+                /* Some firmware implementations that do support
+                 * OsIndications and report that with
+                 * OsIndicationsSupported will remove the
+                 * OsIndications variable when it is unset. Let's
+                 * pretend it's 0 then, to hide this implementation
+                 * detail. Note that this call will return -ENOENT
+                 * then only if the support for OsIndications is
+                 * missing entirely, as determined by
+                 * efi_reboot_to_firmware_supported() above. */
+                *os_indication = 0;
+                return 0;
+        } else if (r < 0)
                 return r;
         else if (s != sizeof(uint64_t))
                 return -EINVAL;
index c37cf1948a07bf68ea3108d6024b5d5860aae464..3d2b5ae77fef417f84e71a7bf448b90d73144434 100644 (file)
@@ -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);
index 230a98604013caefb6151fd42ce97c6df9cc5887..3657aa5d9c5693d5747236fddabdafba6f6fd259 100644 (file)
@@ -24,6 +24,9 @@
 #include <nss.h>
 #include <netdb.h>
 #include <resolv.h>
+#include <pwd.h>
+#include <grp.h>
+
 
 #define NSS_GETHOSTBYNAME_PROTOTYPES(module)            \
 enum nss_status _nss_##module##_gethostbyname4_r(       \
@@ -109,7 +112,8 @@ enum nss_status _nss_##module##_gethostbyname_r(        \
                         NULL,                           \
                         NULL);                          \
        return ret;                                      \
-}
+}                                                       \
+struct __useless_struct_to_allow_trailing_semicolon__
 
 #define NSS_GETHOSTBYADDR_FALLBACKS(module)             \
 enum nss_status _nss_##module##_gethostbyaddr_r(        \
@@ -125,4 +129,29 @@ enum nss_status _nss_##module##_gethostbyaddr_r(        \
                         buffer, buflen,                 \
                         errnop, h_errnop,               \
                         NULL);                          \
-}
+}                                                       \
+struct __useless_struct_to_allow_trailing_semicolon__
+
+#define NSS_GETPW_PROTOTYPES(module)                    \
+enum nss_status _nss_##module##_getpwnam_r(             \
+                const char *name,                       \
+                struct passwd *pwd,                     \
+                char *buffer, size_t buflen,            \
+                int *errnop) _public_;                  \
+enum nss_status _nss_mymachines_getpwuid_r(             \
+                uid_t uid,                              \
+                struct passwd *pwd,                     \
+                char *buffer, size_t buflen,            \
+                int *errnop) _public_
+
+#define NSS_GETGR_PROTOTYPES(module)                    \
+enum nss_status _nss_##module##_getgrnam_r(             \
+                const char *name,                       \
+                struct group *gr,                       \
+                char *buffer, size_t buflen,            \
+                int *errnop) _public_;                  \
+enum nss_status _nss_##module##_getgrgid_r(             \
+                gid_t gid,                              \
+                struct group *gr,                       \
+                char *buffer, size_t buflen,            \
+                int *errnop) _public_
index 55f4e48601436757d0bd7ed31ae062bb296bc5f9..1de0b94fd5fbb0ac4440018cd9d1c13f5039f2a8 100644 (file)
@@ -66,7 +66,7 @@ int sysctl_write(const char *property, const char *value) {
         log_debug("Setting '%s' to '%s'", property, value);
 
         p = strjoina("/proc/sys/", property);
-        return write_string_file(p, value);
+        return write_string_file(p, value, 0);
 }
 
 int sysctl_read(const char *property, char **content) {
index eee6bc89829ab9a8f2d24255e28d5ecc804451b6..2b2310152d54ab22d55f08f7e87ccddd2f36ebfb 100644 (file)
@@ -42,7 +42,7 @@ static int write_mode(char **modes) {
         STRV_FOREACH(mode, modes) {
                 int k;
 
-                k = write_string_file("/sys/power/disk", *mode);
+                k = write_string_file("/sys/power/disk", *mode, 0);
                 if (k == 0)
                         return 0;
 
@@ -65,7 +65,7 @@ static int write_state(FILE **f, char **states) {
         STRV_FOREACH(state, states) {
                 int k;
 
-                k = write_string_stream(*f, *state);
+                k = write_string_stream(*f, *state, true);
                 if (k == 0)
                         return 0;
                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
index a03221502a175b7019197bd7baef5901b0cbd59c..bb4b1eb748aadcafbfc674d0c6c0c4c0c754688c 100644 (file)
@@ -67,6 +67,7 @@ struct sd_bus_vtable {
                         const char *signature;
                         const char *result;
                         sd_bus_message_handler_t handler;
+                        size_t offset;
                 } method;
                 struct {
                         const char *member;
@@ -89,7 +90,7 @@ struct sd_bus_vtable {
                 .x.start.element_size = sizeof(sd_bus_vtable),          \
         }
 
-#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags)   \
+#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags)   \
         {                                                               \
                 .type = _SD_BUS_VTABLE_METHOD,                          \
                 .flags = _flags,                                        \
@@ -97,7 +98,10 @@ struct sd_bus_vtable {
                 .x.method.signature = _signature,                       \
                 .x.method.result = _result,                             \
                 .x.method.handler = _handler,                           \
+                .x.method.offset = _offset,                             \
         }
+#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags)   \
+        SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags)
 
 #define SD_BUS_SIGNAL(_member, _signature, _flags)                      \
         {                                                               \
index f34893171fbaa8e67c113fbe21799d6562cbffee..5439a1903b6c2017aaea134e680325a939e0f515 100644 (file)
@@ -205,7 +205,7 @@ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot);
 void *sd_bus_slot_get_userdata(sd_bus_slot *slot);
 void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata);
 int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description);
-int sd_bus_slot_get_description(sd_bus_slot *slot, char **description);
+int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description);
 
 sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot);
 sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *bus);
index 4296b91d8a29539482f43bb9864708189b097973..5afa50a9d0e729cf7e734db256028cecbf955074 100644 (file)
@@ -45,6 +45,8 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
 int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn);
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
+                                      size_t *data_len);
 int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
                                 size_t *client_id_len);
 
diff --git a/src/test/test-bitmap.c b/src/test/test-bitmap.c
new file mode 100644 (file)
index 0000000..96deede
--- /dev/null
@@ -0,0 +1,105 @@
+/***
+  This file is part of systemd
+
+  Copyright 2015 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bitmap.h"
+
+int main(int argc, const char *argv[]) {
+        _cleanup_bitmap_free_ Bitmap *b = NULL;
+        Iterator it;
+        unsigned n = (unsigned) -1, i = 0;
+
+        b = bitmap_new();
+        assert_se(b);
+
+        assert_se(bitmap_ensure_allocated(&b) == 0);
+        bitmap_free(b);
+        b = NULL;
+        assert_se(bitmap_ensure_allocated(&b) == 0);
+
+        assert_se(bitmap_isset(b, 0) == false);
+        assert_se(bitmap_isset(b, 1) == false);
+        assert_se(bitmap_isset(b, 256) == false);
+        assert_se(bitmap_isclear(b) == true);
+
+        assert_se(bitmap_set(b, 0) == 0);
+        assert_se(bitmap_isset(b, 0) == true);
+        assert_se(bitmap_isclear(b) == false);
+        bitmap_unset(b, 0);
+        assert_se(bitmap_isset(b, 0) == false);
+        assert_se(bitmap_isclear(b) == true);
+
+        assert_se(bitmap_set(b, 1) == 0);
+        assert_se(bitmap_isset(b, 1) == true);
+        assert_se(bitmap_isclear(b) == false);
+        bitmap_unset(b, 1);
+        assert_se(bitmap_isset(b, 1) == false);
+        assert_se(bitmap_isclear(b) == true);
+
+        assert_se(bitmap_set(b, 256) == 0);
+        assert_se(bitmap_isset(b, 256) == true);
+        assert_se(bitmap_isclear(b) == false);
+        bitmap_unset(b, 256);
+        assert_se(bitmap_isset(b, 256) == false);
+        assert_se(bitmap_isclear(b) == true);
+
+        assert_se(bitmap_set(b, 32) == 0);
+        bitmap_unset(b, 0);
+        assert_se(bitmap_isset(b, 32) == true);
+        bitmap_unset(b, 32);
+
+        BITMAP_FOREACH(n, NULL, it)
+                assert_not_reached("NULL bitmap");
+
+        assert_se(bitmap_set(b, 0) == 0);
+        assert_se(bitmap_set(b, 1) == 0);
+        assert_se(bitmap_set(b, 256) == 0);
+
+        BITMAP_FOREACH(n, b, it) {
+                assert_se(n == i);
+                if (i == 0)
+                        i = 1;
+                else if (i == 1)
+                        i = 256;
+                else if (i == 256)
+                        i = (unsigned) -1;
+        }
+
+        assert_se(i == (unsigned) -1);
+
+        i = 0;
+
+        BITMAP_FOREACH(n, b, it) {
+                assert_se(n == i);
+                if (i == 0)
+                        i = 1;
+                else if (i == 1)
+                        i = 256;
+                else if (i == 256)
+                        i = (unsigned) -1;
+        }
+
+        assert_se(i == (unsigned) -1);
+
+        bitmap_clear(b);
+        assert_se(bitmap_isclear(b) == true);
+
+        assert_se(bitmap_set(b, (unsigned) -1) == -ERANGE);
+
+        return 0;
+}
index 838ffcba3dd2a9ce29249723fc581993bad6cd00..e4771c9dd73749dfb4d513d766186ed810fb5b69 100644 (file)
@@ -68,7 +68,7 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 log_error_errno(r, "Failed to make subvolume: %m");
 
-        r = write_string_file("/xxxtest/afile", "ljsadhfljasdkfhlkjdsfha");
+        r = write_string_file("/xxxtest/afile", "ljsadhfljasdkfhlkjdsfha", WRITE_STRING_FILE_CREATE);
         if (r < 0)
                 log_error_errno(r, "Failed to write file: %m");
 
index b1385b8b8776997cb3fca2bd53fcdaa6b98e4419..b73c958ec5755893e637fc1f1de0b4ec2bcdb2a2 100644 (file)
@@ -43,7 +43,7 @@ static void test_copy_file(void) {
         assert_se(fd >= 0);
         close(fd);
 
-        assert_se(write_string_file(fn, "foo bar bar bar foo") == 0);
+        assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
 
         assert_se(copy_file(fn, fn_copy, 0, 0644, 0) == 0);
 
@@ -67,7 +67,7 @@ static void test_copy_file_fd(void) {
         out_fd = mkostemp_safe(out_fn, O_RDWR);
         assert_se(out_fd >= 0);
 
-        assert_se(write_string_file(in_fn, text) == 0);
+        assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
         assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, true) < 0);
         assert_se(copy_file_fd(in_fn, out_fd, true) >= 0);
         assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
@@ -94,7 +94,7 @@ static void test_copy_tree(void) {
                 char *f = strjoina(original_dir, *p);
 
                 assert_se(mkdir_parents(f, 0755) >= 0);
-                assert_se(write_string_file(f, "file") == 0);
+                assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
         }
 
         STRV_FOREACH_PAIR(link, p, links) {
index 527cdd3b549bb1d58714f16b2b6edc61fe9d439b..0042722c99a0e961b4b637b7aad7f66af84df83a 100644 (file)
@@ -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();
index 4c31b776bd97ea35e640a616a0e53c39d2d8d26a..be3a87958f9f426e5dcabc4d57750af527ee88c4 100644 (file)
@@ -302,17 +302,27 @@ static void test_write_string_stream(void) {
 
         f = fdopen(fd, "r");
         assert_se(f);
-        assert_se(write_string_stream(f, "boohoo") < 0);
+        assert_se(write_string_stream(f, "boohoo", true) < 0);
 
         f = freopen(fn, "r+", f);
         assert_se(f);
 
-        assert_se(write_string_stream(f, "boohoo") == 0);
+        assert_se(write_string_stream(f, "boohoo", true) == 0);
         rewind(f);
 
         assert_se(fgets(buf, sizeof(buf), f));
         assert_se(streq(buf, "boohoo\n"));
 
+        f = freopen(fn, "w+", f);
+        assert_se(f);
+
+        assert_se(write_string_stream(f, "boohoo", false) == 0);
+        rewind(f);
+
+        assert_se(fgets(buf, sizeof(buf), f));
+        printf(">%s<", buf);
+        assert_se(streq(buf, "boohoo"));
+
         unlink(fn);
 }
 
@@ -324,7 +334,7 @@ static void test_write_string_file(void) {
         fd = mkostemp_safe(fn, O_RDWR);
         assert_se(fd >= 0);
 
-        assert_se(write_string_file(fn, "boohoo") == 0);
+        assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
 
         assert_se(read(fd, buf, sizeof(buf)) == 7);
         assert_se(streq(buf, "boohoo\n"));
@@ -340,8 +350,8 @@ static void test_write_string_file_no_create(void) {
         fd = mkostemp_safe(fn, O_RDWR);
         assert_se(fd >= 0);
 
-        assert_se(write_string_file_no_create("/a/file/which/does/not/exists/i/guess", "boohoo") < 0);
-        assert_se(write_string_file_no_create(fn, "boohoo") == 0);
+        assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
+        assert_se(write_string_file(fn, "boohoo", 0) == 0);
 
         assert_se(read(fd, buf, sizeof(buf)) == strlen("boohoo\n"));
         assert_se(streq(buf, "boohoo\n"));
@@ -367,8 +377,8 @@ static void test_load_env_file_pairs(void) {
                         "ANSI_COLOR=\"0;36\"\n"
                         "HOME_URL=\"https://www.archlinux.org/\"\n"
                         "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n"
-                        "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n"
-                        );
+                        "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n",
+                        WRITE_STRING_FILE_CREATE);
         assert_se(r == 0);
 
         f = fdopen(fd, "r");
index ad9ea3bcce85792c44b10e7cfebf5207498aa062..f43433baa1817031fcfe89a8422298a9bc168f1e 100644 (file)
@@ -390,6 +390,39 @@ static void test_unhexchar(void) {
         assert_se(unhexchar('0') == 0x0);
 }
 
+static void test_base32hexchar(void) {
+        assert_se(base32hexchar(0) == '0');
+        assert_se(base32hexchar(9) == '9');
+        assert_se(base32hexchar(10) == 'A');
+        assert_se(base32hexchar(31) == 'V');
+}
+
+static void test_unbase32hexchar(void) {
+        assert_se(unbase32hexchar('0') == 0);
+        assert_se(unbase32hexchar('9') == 9);
+        assert_se(unbase32hexchar('A') == 10);
+        assert_se(unbase32hexchar('V') == 31);
+        assert_se(unbase32hexchar('=') == -EINVAL);
+}
+
+static void test_base64char(void) {
+        assert_se(base64char(0) == 'A');
+        assert_se(base64char(26) == 'a');
+        assert_se(base64char(63) == '/');
+}
+
+static void test_unbase64char(void) {
+        assert_se(unbase64char('A') == 0);
+        assert_se(unbase64char('Z') == 25);
+        assert_se(unbase64char('a') == 26);
+        assert_se(unbase64char('z') == 51);
+        assert_se(unbase64char('0') == 52);
+        assert_se(unbase64char('9') == 61);
+        assert_se(unbase64char('+') == 62);
+        assert_se(unbase64char('/') == 63);
+        assert_se(unbase64char('=') == -EINVAL);
+}
+
 static void test_octchar(void) {
         assert_se(octchar(00) == '0');
         assert_se(octchar(07) == '7');
@@ -410,6 +443,264 @@ static void test_undecchar(void) {
         assert_se(undecchar('9') == 9);
 }
 
+static void test_unhexmem(void) {
+        const char *hex = "efa214921";
+        const char *hex_invalid = "efa214921o";
+        _cleanup_free_ char *hex2 = NULL;
+        _cleanup_free_ void *mem = NULL;
+        size_t len;
+
+        assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
+        assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
+        assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL);
+
+        assert_se((hex2 = hexmem(mem, len)));
+
+        free(mem);
+
+        assert_se(memcmp(hex, hex2, strlen(hex)) == 0);
+
+        free(hex2);
+
+        assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0);
+        assert_se((hex2 = hexmem(mem, len)));
+        assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0);
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-10 */
+static void test_base32hexmem(void) {
+        char *b32;
+
+        b32 = base32hexmem("", strlen(""), true);
+        assert_se(b32);
+        assert_se(streq(b32, ""));
+        free(b32);
+
+        b32 = base32hexmem("f", strlen("f"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CO======"));
+        free(b32);
+
+        b32 = base32hexmem("fo", strlen("fo"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNG===="));
+        free(b32);
+
+        b32 = base32hexmem("foo", strlen("foo"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMU==="));
+        free(b32);
+
+        b32 = base32hexmem("foob", strlen("foob"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOG="));
+        free(b32);
+
+        b32 = base32hexmem("fooba", strlen("fooba"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1"));
+        free(b32);
+
+        b32 = base32hexmem("foobar", strlen("foobar"), true);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1E8======"));
+        free(b32);
+
+        b32 = base32hexmem("", strlen(""), false);
+        assert_se(b32);
+        assert_se(streq(b32, ""));
+        free(b32);
+
+        b32 = base32hexmem("f", strlen("f"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CO"));
+        free(b32);
+
+        b32 = base32hexmem("fo", strlen("fo"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNG"));
+        free(b32);
+
+        b32 = base32hexmem("foo", strlen("foo"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMU"));
+        free(b32);
+
+        b32 = base32hexmem("foob", strlen("foob"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOG"));
+        free(b32);
+
+        b32 = base32hexmem("fooba", strlen("fooba"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1"));
+        free(b32);
+
+        b32 = base32hexmem("foobar", strlen("foobar"), false);
+        assert_se(b32);
+        assert_se(streq(b32, "CPNMUOJ1E8"));
+        free(b32);
+}
+
+static void test_unbase32hexmem(void) {
+        void *mem;
+        size_t len;
+
+        assert_se(unbase32hexmem("", strlen(""), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), ""));
+        free(mem);
+
+        assert_se(unbase32hexmem("CO======", strlen("CO======"), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "f"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNG====", strlen("CPNG===="), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMU===", strlen("CPNMU==="), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foob"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fooba"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), true, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foobar"));
+        free(mem);
+
+        assert_se(unbase32hexmem("A", strlen("A"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("A=======", strlen("A======="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAA=====", strlen("AAA====="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAA==", strlen("AAAAAA=="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AB======", strlen("AB======"), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAB====", strlen("AAAB===="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAB===", strlen("AAAAB==="), true, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAAB=", strlen("AAAAAAB="), true, &mem, &len) == -EINVAL);
+
+        assert_se(unbase32hexmem("", strlen(""), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), ""));
+        free(mem);
+
+        assert_se(unbase32hexmem("CO", strlen("CO"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "f"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNG", strlen("CPNG"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMU", strlen("CPNMU"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foo"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOG", strlen("CPNMUOG"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foob"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fooba"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOJ1E8", strlen("CPNMUOJ1E8"), false, &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foobar"));
+        free(mem);
+
+        assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAA", strlen("AAA"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAA", strlen("AAAAAA"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AB", strlen("AB"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAB", strlen("AAAB"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAB", strlen("AAAAB"), false, &mem, &len) == -EINVAL);
+        assert_se(unbase32hexmem("AAAAAAB", strlen("AAAAAAB"), false, &mem, &len) == -EINVAL);
+}
+
+/* https://tools.ietf.org/html/rfc4648#section-10 */
+static void test_base64mem(void) {
+        char *b64;
+
+        b64 = base64mem("", strlen(""));
+        assert_se(b64);
+        assert_se(streq(b64, ""));
+        free(b64);
+
+        b64 = base64mem("f", strlen("f"));
+        assert_se(b64);
+        assert_se(streq(b64, "Zg=="));
+        free(b64);
+
+        b64 = base64mem("fo", strlen("fo"));
+        assert_se(b64);
+        assert_se(streq(b64, "Zm8="));
+        free(b64);
+
+        b64 = base64mem("foo", strlen("foo"));
+        assert_se(b64);
+        assert_se(streq(b64, "Zm9v"));
+        free(b64);
+
+        b64 = base64mem("foob", strlen("foob"));
+        assert_se(b64);
+        assert_se(streq(b64, "Zm9vYg=="));
+        free(b64);
+
+        b64 = base64mem("fooba", strlen("fooba"));
+        assert_se(b64);
+        assert_se(streq(b64, "Zm9vYmE="));
+        free(b64);
+
+        b64 = base64mem("foobar", strlen("foobar"));
+        assert_se(b64);
+        assert_se(streq(b64, "Zm9vYmFy"));
+        free(b64);
+}
+
+static void test_unbase64mem(void) {
+        void *mem;
+        size_t len;
+
+        assert_se(unbase64mem("", strlen(""), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), ""));
+        free(mem);
+
+        assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "f"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fo"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foo"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foob"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "fooba"));
+        free(mem);
+
+        assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0);
+        assert_se(streq(strndupa(mem, len), "foobar"));
+        free(mem);
+
+        assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL);
+        assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL);
+        assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL);
+        assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL);
+}
+
 static void test_cescape(void) {
         _cleanup_free_ char *escaped;
 
@@ -565,14 +856,14 @@ static void test_read_hostname_config(void) {
         close(fd);
 
         /* simple hostname */
-        write_string_file(path, "foo");
+        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;
 
         /* with comment */
-        write_string_file(path, "# comment\nfoo");
+        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"));
@@ -580,7 +871,7 @@ static void test_read_hostname_config(void) {
         hostname = NULL;
 
         /* with comment and extra whitespace */
-        write_string_file(path, "# comment\n\n foo ");
+        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"));
@@ -588,7 +879,7 @@ static void test_read_hostname_config(void) {
         hostname = NULL;
 
         /* cleans up name */
-        write_string_file(path, "!foo/bar.com");
+        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"));
@@ -597,7 +888,7 @@ static void test_read_hostname_config(void) {
 
         /* no value set */
         hostname = (char*) 0x1234;
-        write_string_file(path, "# nothing here\n");
+        write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE);
         assert_se(read_hostname_config(path, &hostname) == -ENOENT);
         assert_se(hostname == (char*) 0x1234);  /* does not touch argument on error */
 
@@ -1191,11 +1482,11 @@ static void test_execute_directory(void) {
         masked = strjoina(template_lo, "/masked");
         mask = strjoina(template_hi, "/masked");
 
-        assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works") == 0);
-        assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2") == 0);
-        assert_se(write_string_file(overridden, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed") == 0);
-        assert_se(write_string_file(override, "#!/bin/sh\necho 'Executing '$0") == 0);
-        assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed") == 0);
+        assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(overridden, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(override, "#!/bin/sh\necho 'Executing '$0", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0);
         assert_se(symlink("/dev/null", mask) == 0);
         assert_se(chmod(name, 0755) == 0);
         assert_se(chmod(name2, 0755) == 0);
@@ -1398,6 +1689,17 @@ static void test_unquote_first_word(void) {
         assert_se(streq(t, "\\w+\b"));
         free(t);
         assert_se(p == original + 5);
+
+        p = original = "-N ''";
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+        assert_se(streq(t, "-N"));
+        free(t);
+        assert_se(p == original + 3);
+
+        assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+        assert_se(streq(t, ""));
+        free(t);
+        assert_se(p == original + 5);
 }
 
 static void test_unquote_first_word_and_warn(void) {
@@ -1804,10 +2106,19 @@ int main(int argc, char *argv[]) {
         test_in_charset();
         test_hexchar();
         test_unhexchar();
+        test_base32hexchar();
+        test_unbase32hexchar();
+        test_base64char();
+        test_unbase64char();
         test_octchar();
         test_unoctchar();
         test_decchar();
         test_undecchar();
+        test_unhexmem();
+        test_base32hexmem();
+        test_unbase32hexmem();
+        test_base64mem();
+        test_unbase64mem();
         test_cescape();
         test_cunescape();
         test_foreach_word();
index 42f757c4b701d637c0dfe2cd1ca7f8a3528092aa..271984b5a845c361b8dd819eec824c3775f8a016 100644 (file)
@@ -955,9 +955,10 @@ static int path_set_attribute(Item *item, const char *path) {
 
         r = chattr_fd(fd, f, item->attribute_mask);
         if (r < 0)
-                return log_error_errno(r,
-                        "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
-                        path, item->attribute_value, item->attribute_mask);
+                log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING,
+                               r,
+                               "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
+                               path, item->attribute_value, item->attribute_mask);
 
         return 0;
 }
index 7ba0b7fc8f2048e171feaf8eb46f1617424fe6c0..c6a2c56e77313258cf074316d12c0d80a40a514a 100644 (file)
@@ -409,7 +409,6 @@ int main(int argc, char *argv[])
         union {
                 uint8_t  byte[512];
                 uint16_t wyde[256];
-                uint64_t octa[64];
         } identify;
         char model[41];
         char model_enc[256];
@@ -638,10 +637,20 @@ int main(int argc, char *argv[])
                  * All other values are reserved.
                  */
                 word = identify.wyde[108];
-                if ((word & 0xf000) == 0x5000)
+                if ((word & 0xf000) == 0x5000) {
+                        uint64_t wwwn;
+
+                        wwwn   = identify.wyde[108];
+                        wwwn <<= 16;
+                        wwwn  |= identify.wyde[109];
+                        wwwn <<= 16;
+                        wwwn  |= identify.wyde[110];
+                        wwwn <<= 16;
+                        wwwn  |= identify.wyde[111];
                         printf("ID_WWN=0x%1$" PRIx64 "\n"
                                "ID_WWN_WITH_EXTENSION=0x%1$" PRIx64 "\n",
-                               identify.octa[108/4]);
+                               wwwn);
+                }
 
                 /* from Linux's include/linux/ata.h */
                 if (identify.wyde[0] == 0x848a ||
index b656204c464cdde654510ea4dc66af855518d9f8..72109d93d2129c52b11da7fefe67832b0e3ff94d 100644 (file)
@@ -33,7 +33,7 @@ static sd_hwdb *hwdb;
 int udev_builtin_hwdb_lookup(struct udev_device *dev,
                              const char *prefix, const char *modalias,
                              const char *filter, bool test) {
-        _cleanup_free_ const char *lookup = NULL;
+        _cleanup_free_ char *lookup = NULL;
         const char *key, *value;
         int n = 0;
 
index fabc6538001795c97f17a80686211fecb4dc5ba7..4f625251d66197f97465360498801d9e1dd95a90 100644 (file)
@@ -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);
index e27fb1fd9eb9a428bff2e54b887028c4e45e5720..d0b8bad48e680661676c648ba7b1746d49befb0d 100644 (file)
@@ -398,7 +398,7 @@ static void worker_spawn(Manager *manager, struct event *event) {
                 prctl(PR_SET_PDEATHSIG, SIGTERM);
 
                 /* reset OOM score, we only protect the main daemon */
-                write_string_file("/proc/self/oom_score_adj", "0");
+                write_string_file("/proc/self/oom_score_adj", "0", 0);
 
                 for (;;) {
                         struct udev_event *udev_event;
@@ -1091,7 +1091,7 @@ static int synthesize_change(struct udev_device *dev) {
                  */
                 log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
                 strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
-                write_string_file(filename, "change");
+                write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
 
                 udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
                         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
@@ -1106,7 +1106,7 @@ static int synthesize_change(struct udev_device *dev) {
                         log_debug("device %s closed, synthesising partition '%s' 'change'",
                                   udev_device_get_devnode(dev), udev_device_get_devnode(d));
                         strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL);
-                        write_string_file(filename, "change");
+                        write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
                 }
 
                 return 0;
@@ -1114,7 +1114,7 @@ static int synthesize_change(struct udev_device *dev) {
 
         log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
         strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
-        write_string_file(filename, "change");
+        write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
 
         return 0;
 }
@@ -1358,6 +1358,7 @@ static int listen_fds(int *rctrl, int *rnetlink) {
  *   udev.event-timeout=<number of seconds>    seconds to wait before terminating an event
  */
 static int parse_proc_cmdline_item(const char *key, const char *value) {
+        const char *full_key = key;
         int r;
 
         assert(key);
@@ -1377,25 +1378,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                 int prio;
 
                 prio = util_log_priority(value);
+                if (prio < 0)
+                        goto invalid;
                 log_set_max_level(prio);
         } else if (streq(key, "children-max")) {
                 r = safe_atou(value, &arg_children_max);
                 if (r < 0)
-                        log_warning("invalid udev.children-max ignored: %s", value);
+                        goto invalid;
         } else if (streq(key, "exec-delay")) {
                 r = safe_atoi(value, &arg_exec_delay);
                 if (r < 0)
-                        log_warning("invalid udev.exec-delay ignored: %s", value);
+                        goto invalid;
         } else if (streq(key, "event-timeout")) {
                 r = safe_atou64(value, &arg_event_timeout_usec);
                 if (r < 0)
-                        log_warning("invalid udev.event-timeout ignored: %s", value);
-                else {
-                        arg_event_timeout_usec *= USEC_PER_SEC;
-                        arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
-                }
+                        goto invalid;
+                arg_event_timeout_usec *= USEC_PER_SEC;
+                arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
         }
 
+        return 0;
+invalid:
+        log_warning("invalid %s ignored: %s", full_key, value);
         return 0;
 }
 
@@ -1432,7 +1436,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "c:de:DtN:hV", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) {
                 int r;
 
                 switch (c) {
@@ -1747,7 +1751,7 @@ int main(int argc, char *argv[]) {
 
                 setsid();
 
-                write_string_file("/proc/self/oom_score_adj", "-1000");
+                write_string_file("/proc/self/oom_score_adj", "-1000", 0);
         }
 
         r = run(fd_ctrl, fd_uevent, cgroup);
index 1c31769fde9b447904987a7fef476589ff21e113..e80a7771de6ae63d24bf65cc71aceedd08d8a935 100644 (file)
@@ -65,7 +65,7 @@ int main(int argc, char*argv[]) {
         } else if (streq(argv[1], "stop")) {
                 int r;
 
-                r = write_string_file_atomic("/run/nologin", "System is going down.");
+                r = write_string_file("/run/nologin", "System is going down.", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
                 if (r < 0) {
                         log_error_errno(r, "Failed to create /run/nologin: %m");
                         return EXIT_FAILURE;
index f7728dcfff411fc1ec2b33bed22a2a9a0c12238c..7bdc158ad76f2816b3f375308174fded11b765a9 100644 (file)
@@ -56,7 +56,7 @@ static int disable_utf8(int fd) {
         if (k < 0)
                 r = k;
 
-        k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
+        k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0);
         if (k < 0)
                 r = k;
 
@@ -89,7 +89,7 @@ static int enable_utf8(int fd) {
         if (k < 0)
                 r = k;
 
-        k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
+        k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0);
         if (k < 0)
                 r = k;
 
index b447b01f586fa5f24d7748f85fa825db974a190d..d9d51af9292b373ba8bf821dd599138b5bbe3b6d 100644 (file)
@@ -35,7 +35,7 @@ z /var/log/journal 2755 root systemd-journal - -
 z /var/log/journal/%m 2755 root systemd-journal - -
 m4_ifdef(`HAVE_ACL',``
 a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
-A+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
+a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
 '')m4_dnl
 
 d /var/lib/systemd 0755 root root -
diff --git a/tools/compile-unifont.py b/tools/compile-unifont.py
deleted file mode 100755 (executable)
index 5464c53..0000000
+++ /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)
index 52b9b1cd034b22557e918c57390a7916a8f16c6c..8dc3cbdede3fb234778f2fbbc0ed64c3384189fc 100644 (file)
@@ -16,7 +16,7 @@ Before=shutdown.target
 [Service]
 Environment=HOME=/root
 WorkingDirectory=/root
-ExecStartPre=-/bin/plymouth quit
+ExecStartPre=-/bin/plymouth --wait quit
 ExecStartPre=-/bin/echo -e 'Welcome to emergency mode! After logging in, type "journalctl -xb" to view\\nsystem logs, "systemctl reboot" to reboot, "systemctl default" or ^D to\\ntry again to boot into default mode.'
 ExecStart=-/bin/sh -c "@SULOGIN@; @SYSTEMCTL@ --job-mode=fail --no-block default"
 Type=idle
index 19c33959d613c78c241e39daf2cd58cb79009e27..fb1f383cdca0fa642cbc00b7a3517f9dea8eb487 100644 (file)
@@ -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
index 64d9130c24e3be64b0e0ca7f0b5b5a305121d3b7..35be713ade1a46fb1a34aae17b57bd862e733068 100644 (file)
@@ -12,7 +12,7 @@ ConditionCapability=CAP_NET_ADMIN
 DefaultDependencies=no
 # dbus.service can be dropped once on kdbus, and systemd-udevd.service can be
 # dropped once tuntap is moved to netlink
-After=systemd-udevd.service dbus.service network-pre.target systemd-sysusers.service
+After=systemd-udevd.service dbus.service network-pre.target systemd-sysusers.service systemd-sysctl.service
 Before=network.target multi-user.target shutdown.target
 Conflicts=shutdown.target
 Wants=network.target
index 6111b10ccf59c06e22260431490db5cf2f594db1..ce9df9e7e1a2e987f75adbb24bbc9c016a315b58 100644 (file)
@@ -1,3 +1,2 @@
 /systemd-exit.service
 /systemd-bus-proxyd.service
-/systemd-consoled.service
diff --git a/units/user/systemd-consoled.service.in b/units/user/systemd-consoled.service.in
deleted file mode 100644 (file)
index fd7938a..0000000
+++ /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