summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Pitt <martin.pitt@ubuntu.com>2015-07-29 15:16:01 +0200
committerMartin Pitt <martin.pitt@ubuntu.com>2015-07-29 15:16:01 +0200
commit7035cd9e226bce7909e1365039431a4d21e21045 (patch)
tree7402a205a32d8937bb50a8149b939e71028e7ff8 /src
parentfb183854ab9b5774094a753ad3f46b653a9055da (diff)
downloadsystemd-7035cd9e226bce7909e1365039431a4d21e21045.tar.gz
Imported Upstream version 223
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze.c238
-rw-r--r--src/backlight/backlight.c2
-rw-r--r--src/basic/bitmap.c198
-rw-r--r--src/basic/bitmap.h50
-rw-r--r--src/basic/capability.c2
-rw-r--r--src/basic/cgroup-util.c12
-rw-r--r--src/basic/copy.c6
-rw-r--r--src/basic/exit-status.c3
-rw-r--r--src/basic/fileio-label.c2
-rw-r--r--src/basic/fileio.c85
-rw-r--r--src/basic/fileio.h12
-rw-r--r--src/basic/macro.h21
-rw-r--r--src/basic/missing.h40
-rw-r--r--src/basic/path-util.c6
-rw-r--r--src/basic/process-util.c52
-rw-r--r--src/basic/smack-util.c2
-rw-r--r--src/basic/util.c570
-rw-r--r--src/basic/util.h12
-rw-r--r--src/basic/virt.c20
-rw-r--r--src/binfmt/binfmt.c6
-rw-r--r--src/boot/bootctl.c4
-rw-r--r--src/boot/efi/boot.c38
-rw-r--r--src/bootchart/bootchart.c16
-rw-r--r--src/bus-proxyd/driver.c5
-rw-r--r--src/bus-proxyd/driver.h3
-rw-r--r--src/bus-proxyd/proxy.c55
-rw-r--r--src/bus-proxyd/proxy.h3
-rw-r--r--src/bus-proxyd/synthesize.c19
-rw-r--r--src/bus-proxyd/synthesize.h3
-rw-r--r--src/cgls/cgls.c8
-rw-r--r--src/cgtop/cgtop.c1
l---------src/console/Makefile1
-rw-r--r--src/console/consoled-display.c81
-rw-r--r--src/console/consoled-manager.c284
-rw-r--r--src/console/consoled-session.c279
-rw-r--r--src/console/consoled-terminal.c358
-rw-r--r--src/console/consoled-workspace.c167
-rw-r--r--src/console/consoled.c66
-rw-r--r--src/console/consoled.h164
-rw-r--r--src/core/automount.c34
-rw-r--r--src/core/busname.c3
-rw-r--r--src/core/execute.c2
-rw-r--r--src/core/job.c313
-rw-r--r--src/core/machine-id-setup.c2
-rw-r--r--src/core/main.c44
-rw-r--r--src/core/mount.c9
-rw-r--r--src/core/path.c2
-rw-r--r--src/core/selinux-access.c12
-rw-r--r--src/core/service.c10
-rw-r--r--src/core/slice.c1
-rw-r--r--src/core/smack-setup.c2
-rw-r--r--src/core/socket.c1
-rw-r--r--src/core/swap.c1
-rw-r--r--src/core/target.c1
-rw-r--r--src/core/unit.c81
-rw-r--r--src/core/unit.h1
-rw-r--r--src/firstboot/firstboot.c4
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c3
-rw-r--r--src/hibernate-resume/hibernate-resume.c2
-rw-r--r--src/import/pull-dkr.c2
-rw-r--r--src/journal-remote/journal-gatewayd.c2
-rw-r--r--src/journal/journal-file.c37
-rw-r--r--src/journal/journal-file.h3
-rw-r--r--src/journal/journal-vacuum.c2
-rw-r--r--src/journal/journal-verify.c233
-rw-r--r--src/journal/journalctl.c4
-rw-r--r--src/journal/journald-server.c8
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h2
-rw-r--r--src/libsystemd-network/dhcp-protocol.h1
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c68
-rw-r--r--src/libsystemd-terminal/.gitignore1
-rw-r--r--src/libsystemd-terminal/evcat.c488
-rw-r--r--src/libsystemd-terminal/grdev-drm.c3092
-rw-r--r--src/libsystemd-terminal/grdev-internal.h251
-rw-r--r--src/libsystemd-terminal/grdev.c1359
-rw-r--r--src/libsystemd-terminal/grdev.h199
-rw-r--r--src/libsystemd-terminal/idev-evdev.c859
-rw-r--r--src/libsystemd-terminal/idev-internal.h188
-rw-r--r--src/libsystemd-terminal/idev-keyboard.c1159
-rw-r--r--src/libsystemd-terminal/idev.c799
-rw-r--r--src/libsystemd-terminal/idev.h221
-rw-r--r--src/libsystemd-terminal/modeset.c482
-rw-r--r--src/libsystemd-terminal/subterm.c981
-rw-r--r--src/libsystemd-terminal/sysview-internal.h144
-rw-r--r--src/libsystemd-terminal/sysview.c1554
-rw-r--r--src/libsystemd-terminal/sysview.h162
-rw-r--r--src/libsystemd-terminal/term-charset.c488
-rw-r--r--src/libsystemd-terminal/term-internal.h650
-rw-r--r--src/libsystemd-terminal/term-page.c2091
-rw-r--r--src/libsystemd-terminal/term-parser.c1702
-rw-r--r--src/libsystemd-terminal/term-screen.c4333
-rw-r--r--src/libsystemd-terminal/term-wcwidth.c312
-rw-r--r--src/libsystemd-terminal/term.h183
-rw-r--r--src/libsystemd-terminal/test-term-page.c459
-rw-r--r--src/libsystemd-terminal/test-term-parser.c141
-rw-r--r--src/libsystemd-terminal/test-unifont.c125
-rw-r--r--src/libsystemd-terminal/unifont-def.h137
-rw-r--r--src/libsystemd-terminal/unifont.c238
-rw-r--r--src/libsystemd-terminal/unifont.h54
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h2
-rw-r--r--src/libsystemd/sd-bus/bus-control.c16
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c3
-rw-r--r--src/libsystemd/sd-bus/bus-message.c17
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c96
-rw-r--r--src/libsystemd/sd-bus/bus-slot.c2
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c20
-rw-r--r--src/libsystemd/sd-bus/test-bus-marshal.c21
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c21
-rw-r--r--src/libsystemd/sd-bus/test-bus-proxy.c109
-rw-r--r--src/libsystemd/sd-device/sd-device.c8
-rw-r--r--src/libsystemd/sd-netlink/netlink-internal.h2
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c48
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c48
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h1
-rw-r--r--src/login/logind-dbus.c15
-rw-r--r--src/login/logind-seat.c20
-rw-r--r--src/login/logind-session.c10
-rw-r--r--src/login/logind-session.h2
-rw-r--r--src/login/logind-user-dbus.c6
-rw-r--r--src/login/org.freedesktop.login1.conf72
-rw-r--r--src/machine/machine-dbus.c14
-rw-r--r--src/machine/machined-dbus.c231
-rw-r--r--src/machine/org.freedesktop.machine1.conf16
-rw-r--r--src/network/networkd-dhcp4.c41
-rw-r--r--src/network/networkd-link.c29
-rw-r--r--src/network/networkd-netdev-gperf.gperf4
-rw-r--r--src/network/networkd-netdev-macvlan.c26
-rw-r--r--src/network/networkd-netdev-macvlan.h1
-rw-r--r--src/network/networkd-netdev-tunnel.c71
-rw-r--r--src/network/networkd-netdev-tunnel.h28
-rw-r--r--src/network/networkd-netdev-tuntap.c3
-rw-r--r--src/network/networkd-netdev-tuntap.h1
-rw-r--r--src/network/networkd-netdev-vxlan.h11
-rw-r--r--src/network/networkd-netdev.c2
-rw-r--r--src/network/networkd-netdev.h2
-rw-r--r--src/network/networkd-network-bus.c6
-rw-r--r--src/network/networkd-network-gperf.gperf7
-rw-r--r--src/network/networkd-network.c41
-rw-r--r--src/network/networkd-wait-online.c2
-rw-r--r--src/network/networkd.h32
-rw-r--r--src/nspawn/nspawn.c14
-rw-r--r--src/nss-mymachines/nss-mymachines.c320
-rw-r--r--src/nss-mymachines/nss-mymachines.sym4
-rw-r--r--src/python-systemd/.gitignore2
l---------src/python-systemd/Makefile1
-rw-r--r--src/python-systemd/__init__.py18
-rw-r--r--src/python-systemd/_daemon.c331
-rw-r--r--src/python-systemd/_journal.c157
-rw-r--r--src/python-systemd/_reader.c1106
-rw-r--r--src/python-systemd/daemon.py55
-rw-r--r--src/python-systemd/docs/.gitignore1
-rw-r--r--src/python-systemd/docs/conf.py279
-rw-r--r--src/python-systemd/docs/daemon.rst18
-rw-r--r--src/python-systemd/docs/default.css196
-rw-r--r--src/python-systemd/docs/id128.rst40
-rw-r--r--src/python-systemd/docs/index.rst24
-rw-r--r--src/python-systemd/docs/journal.rst64
-rw-r--r--src/python-systemd/docs/layout.html15
-rw-r--r--src/python-systemd/docs/login.rst28
-rw-r--r--src/python-systemd/id128.c163
-rw-r--r--src/python-systemd/journal.py548
-rw-r--r--src/python-systemd/login.c376
-rw-r--r--src/python-systemd/pyutil.c80
-rw-r--r--src/python-systemd/pyutil.h54
-rw-r--r--src/resolve-host/resolve-host.c4
-rw-r--r--src/resolve/dns-type.c5
-rw-r--r--src/resolve/dns-type.h1
-rw-r--r--src/resolve/resolved-dns-packet.c510
-rw-r--r--src/resolve/resolved-dns-packet.h19
-rw-r--r--src/resolve/resolved-dns-question.c40
-rw-r--r--src/resolve/resolved-dns-question.h2
-rw-r--r--src/resolve/resolved-dns-rr.c180
-rw-r--r--src/resolve/resolved-dns-rr.h32
-rw-r--r--src/resolve/resolved-dns-scope.c90
-rw-r--r--src/resolve/resolved-dns-scope.h6
-rw-r--r--src/resolve/resolved-dns-server.c44
-rw-r--r--src/resolve/resolved-dns-server.h7
-rw-r--r--src/resolve/resolved-dns-transaction.c110
-rw-r--r--src/resolve/resolved-dns-transaction.h6
-rw-r--r--src/resolve/resolved-link.c23
-rw-r--r--src/resolve/resolved-llmnr.c473
-rw-r--r--src/resolve/resolved-llmnr.h34
-rw-r--r--src/resolve/resolved-manager.c601
-rw-r--r--src/resolve/resolved-manager.h14
-rw-r--r--src/rfkill/rfkill.c2
-rw-r--r--src/shared/dns-domain.c95
-rw-r--r--src/shared/dns-domain.h2
-rw-r--r--src/shared/efivars.c14
-rw-r--r--src/shared/install.c5
-rw-r--r--src/shared/nss-util.h33
-rw-r--r--src/shared/sysctl-util.c2
-rw-r--r--src/sleep/sleep.c4
-rw-r--r--src/systemd/sd-bus-vtable.h6
-rw-r--r--src/systemd/sd-bus.h2
-rw-r--r--src/systemd/sd-dhcp-lease.h2
-rw-r--r--src/test/test-bitmap.c105
-rw-r--r--src/test/test-btrfs.c2
-rw-r--r--src/test/test-copy.c6
-rw-r--r--src/test/test-dns-domain.c76
-rw-r--r--src/test/test-fileio.c24
-rw-r--r--src/test/test-util.c331
-rw-r--r--src/tmpfiles/tmpfiles.c7
-rw-r--r--src/udev/ata_id/ata_id.c15
-rw-r--r--src/udev/udev-builtin-hwdb.c2
-rw-r--r--src/udev/udev-builtin.c20
-rw-r--r--src/udev/udevd.c30
-rw-r--r--src/user-sessions/user-sessions.c2
-rw-r--r--src/vconsole/vconsole-setup.c4
208 files changed, 5202 insertions, 29427 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index c0863e4167..db1e7f3f37 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -88,6 +88,18 @@ struct boot_times {
usec_t generators_finish_time;
usec_t unitsload_start_time;
usec_t unitsload_finish_time;
+
+ /*
+ * If we're analyzing the user instance, all timestamps will be offset
+ * by its own start-up timestamp, which may be arbitrarily big.
+ * With "plot", this causes arbitrarily wide output SVG files which almost
+ * completely consist of empty space. Thus we cancel out this offset.
+ *
+ * This offset is subtracted from times above by acquire_boot_times(),
+ * but it still needs to be subtracted from unit-specific timestamps
+ * (so it is stored here for reference).
+ */
+ usec_t reverse_offset;
};
struct unit_times {
@@ -188,95 +200,13 @@ static void free_unit_times(struct unit_times *t, unsigned n) {
free(t);
}
-static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- int r, c = 0;
- struct unit_times *unit_times = NULL;
- size_t size = 0;
- UnitInfo u;
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnits",
- &error, &reply,
- NULL);
- if (r < 0) {
- log_error("Failed to list units: %s", bus_error_message(&error, -r));
- goto fail;
- }
+static void subtract_timestamp(usec_t *a, usec_t b) {
+ assert(a);
- r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
- if (r < 0) {
- bus_log_parse_error(r);
- goto fail;
+ if (*a > 0) {
+ assert(*a >= b);
+ *a -= b;
}
-
- while ((r = bus_parse_unit_info(reply, &u)) > 0) {
- struct unit_times *t;
-
- if (!GREEDY_REALLOC(unit_times, size, c+1)) {
- r = log_oom();
- goto fail;
- }
-
- t = unit_times+c;
- t->name = NULL;
-
- assert_cc(sizeof(usec_t) == sizeof(uint64_t));
-
- if (bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "InactiveExitTimestampMonotonic",
- &t->activating) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "ActiveEnterTimestampMonotonic",
- &t->activated) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "ActiveExitTimestampMonotonic",
- &t->deactivating) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "InactiveEnterTimestampMonotonic",
- &t->deactivated) < 0) {
- r = -EIO;
- goto fail;
- }
-
- if (t->activated >= t->activating)
- t->time = t->activated - t->activating;
- else if (t->deactivated >= t->activating)
- t->time = t->deactivated - t->activating;
- else
- t->time = 0;
-
- if (t->activating == 0)
- continue;
-
- t->name = strdup(u.id);
- if (t->name == NULL) {
- r = log_oom();
- goto fail;
- }
- c++;
- }
- if (r < 0) {
- bus_log_parse_error(r);
- goto fail;
- }
-
- *out = unit_times;
- return c;
-
-fail:
- if (unit_times)
- free_unit_times(unit_times, (unsigned) c);
- return r;
}
static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
@@ -355,10 +285,30 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
return -EINPROGRESS;
}
- if (times.initrd_time)
- times.kernel_done_time = times.initrd_time;
- else
- times.kernel_done_time = times.userspace_time;
+ if (arg_user) {
+ /*
+ * User-instance-specific timestamps processing
+ * (see comment to reverse_offset in struct boot_times).
+ */
+ times.reverse_offset = times.userspace_time;
+
+ times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
+ subtract_timestamp(&times.finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.security_start_time, times.reverse_offset);
+ subtract_timestamp(&times.security_finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.generators_start_time, times.reverse_offset);
+ subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
+ subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
+ } else {
+ if (times.initrd_time)
+ times.kernel_done_time = times.initrd_time;
+ else
+ times.kernel_done_time = times.userspace_time;
+ }
cached = true;
@@ -378,6 +328,107 @@ static void free_host_info(struct host_info *hi) {
free(hi);
}
+static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r, c = 0;
+ struct boot_times *boot_times = NULL;
+ struct unit_times *unit_times = NULL;
+ size_t size = 0;
+ UnitInfo u;
+
+ r = acquire_boot_times(bus, &boot_times);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits",
+ &error, &reply,
+ NULL);
+ if (r < 0) {
+ log_error("Failed to list units: %s", bus_error_message(&error, -r));
+ goto fail;
+ }
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto fail;
+ }
+
+ while ((r = bus_parse_unit_info(reply, &u)) > 0) {
+ struct unit_times *t;
+
+ if (!GREEDY_REALLOC(unit_times, size, c+1)) {
+ r = log_oom();
+ goto fail;
+ }
+
+ t = unit_times+c;
+ t->name = NULL;
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+
+ if (bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "InactiveExitTimestampMonotonic",
+ &t->activating) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveEnterTimestampMonotonic",
+ &t->activated) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveExitTimestampMonotonic",
+ &t->deactivating) < 0 ||
+ bus_get_uint64_property(bus, u.unit_path,
+ "org.freedesktop.systemd1.Unit",
+ "InactiveEnterTimestampMonotonic",
+ &t->deactivated) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ subtract_timestamp(&t->activating, boot_times->reverse_offset);
+ subtract_timestamp(&t->activated, boot_times->reverse_offset);
+ subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+ subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+
+ if (t->activated >= t->activating)
+ t->time = t->activated - t->activating;
+ else if (t->deactivated >= t->activating)
+ t->time = t->deactivated - t->activating;
+ else
+ t->time = 0;
+
+ if (t->activating == 0)
+ continue;
+
+ t->name = strdup(u.id);
+ if (t->name == NULL) {
+ r = log_oom();
+ goto fail;
+ }
+ c++;
+ }
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto fail;
+ }
+
+ *out = unit_times;
+ return c;
+
+fail:
+ if (unit_times)
+ free_unit_times(unit_times, (unsigned) c);
+ return r;
+}
+
static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
int r;
struct host_info *host;
@@ -450,10 +501,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
- if (t->kernel_time > 0)
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
- else
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
+ strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
ptr = strdup(buf);
if (!ptr)
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index c79ad6520c..c8961de946 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -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
index 0000000000..bf9d8d4d7c
--- /dev/null
+++ b/src/basic/bitmap.c
@@ -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
index 0000000000..2874bc99f7
--- /dev/null
+++ b/src/basic/bitmap.h
@@ -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)
diff --git a/src/basic/capability.c b/src/basic/capability.c
index 58f00e6dae..8dbe4da5bb 100644
--- a/src/basic/capability.c
+++ b/src/basic/capability.c
@@ -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;
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 439c5516dc..34a3060509 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -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) {
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 230e7e4d3f..e2d356d676 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -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;
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index 5ab36825c0..fcff753ada 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -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";
}
}
diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c
index bec988ca78..f596f1d11f 100644
--- a/src/basic/fileio-label.c
+++ b/src/basic/fileio-label.c
@@ -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();
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 00fb6f8b5c..2216853777 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -27,14 +27,14 @@
#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;
}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 91d4a0d2d5..2e8148ff24 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -25,10 +25,14 @@
#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);
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 5fa17ed208..627d768b76 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -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) \
diff --git a/src/basic/missing.h b/src/basic/missing.h
index be7f6186fc..ed6cd80c75 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -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
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 8f49d65266..5cbfc145a4 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -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) {
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 2c05f2fee4..61f188467f 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -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;
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
index 2e24b1ea99..047aa294f4 100644
--- a/src/basic/smack-util.c
+++ b/src/basic/smack-util.c
@@ -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
diff --git a/src/basic/util.c b/src/basic/util.c
index aa912bde28..1c15fbc172 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -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))
diff --git a/src/basic/util.h b/src/basic/util.h
index a1d1dd15c3..c2e5cc610b 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -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);
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 1299a75ed5..a8d26716a1 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -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;
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index 6028ed68c0..1e216f52bd 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -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);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index ed69fb0cec..091ea375d3 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -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;
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index eb1a4e3b66..e8cd8abd26 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -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);
diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c
index 3360bc85be..1625d51fa8 100644
--- a/src/bootchart/bootchart.c
+++ b/src/bootchart/bootchart.c
@@ -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);
}
diff --git a/src/bus-proxyd/driver.c b/src/bus-proxyd/driver.c
index 4ac955da41..1cb5ea5008 100644
--- a/src/bus-proxyd/driver.c
+++ b/src/bus-proxyd/driver.c
@@ -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);
diff --git a/src/bus-proxyd/driver.h b/src/bus-proxyd/driver.h
index b8cedf5ce5..da3834f8b0 100644
--- a/src/bus-proxyd/driver.h
+++ b/src/bus-proxyd/driver.h
@@ -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);
diff --git a/src/bus-proxyd/proxy.c b/src/bus-proxyd/proxy.c
index 189ee969c7..c37b09b9c0 100644
--- a/src/bus-proxyd/proxy.c
+++ b/src/bus-proxyd/proxy.c
@@ -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;
diff --git a/src/bus-proxyd/proxy.h b/src/bus-proxyd/proxy.h
index ff278a2465..ccb951c109 100644
--- a/src/bus-proxyd/proxy.h
+++ b/src/bus-proxyd/proxy.h
@@ -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);
diff --git a/src/bus-proxyd/synthesize.c b/src/bus-proxyd/synthesize.c
index 67bcc7a242..3ecedfd575 100644
--- a/src/bus-proxyd/synthesize.c
+++ b/src/bus-proxyd/synthesize.c
@@ -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;
}
diff --git a/src/bus-proxyd/synthesize.h b/src/bus-proxyd/synthesize.h
index e850350bc5..b596daddf2 100644
--- a/src/bus-proxyd/synthesize.h
+++ b/src/bus-proxyd/synthesize.h
@@ -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);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index 46a444340a..b8d1d2ccaf 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -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,
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index d630e35882..f953c9e624 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -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
index d0b0e8e008..0000000000
--- a/src/console/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/console/consoled-display.c b/src/console/consoled-display.c
deleted file mode 100644
index 569c011dc0..0000000000
--- a/src/console/consoled-display.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include "consoled.h"
-#include "grdev.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-
-int display_new(Display **out, Session *s, grdev_display *display) {
- _cleanup_(display_freep) Display *d = NULL;
-
- assert(out);
- assert(s);
- assert(display);
-
- d = new0(Display, 1);
- if (!d)
- return -ENOMEM;
-
- d->session = s;
- d->grdev = display;
- d->width = grdev_display_get_width(display);
- d->height = grdev_display_get_height(display);
- LIST_PREPEND(displays_by_session, d->session->display_list, d);
-
- grdev_display_enable(display);
-
- *out = d;
- d = NULL;
- return 0;
-}
-
-Display *display_free(Display *d) {
- if (!d)
- return NULL;
-
- LIST_REMOVE(displays_by_session, d->session->display_list, d);
- free(d);
-
- return NULL;
-}
-
-void display_refresh(Display *d) {
- assert(d);
-
- d->width = grdev_display_get_width(d->grdev);
- d->height = grdev_display_get_height(d->grdev);
-}
-
-void display_render(Display *d, Workspace *w) {
- const grdev_display_target *target;
-
- assert(d);
- assert(w);
-
- GRDEV_DISPLAY_FOREACH_TARGET(d->grdev, target) {
- if (workspace_draw(w, target))
- grdev_display_flip_target(d->grdev, target);
- }
-}
diff --git a/src/console/consoled-manager.c b/src/console/consoled-manager.c
deleted file mode 100644
index 20424eb267..0000000000
--- a/src/console/consoled-manager.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "log.h"
-#include "signal-util.h"
-#include "util.h"
-#include "consoled.h"
-#include "idev.h"
-#include "grdev.h"
-#include "sysview.h"
-#include "unifont.h"
-
-int manager_new(Manager **out) {
- _cleanup_(manager_freep) Manager *m = NULL;
- int r;
-
- assert(out);
-
- m = new0(Manager, 1);
- if (!m)
- return -ENOMEM;
-
- r = sd_event_default(&m->event);
- if (r < 0)
- return r;
-
- r = sd_event_set_watchdog(m->event, true);
- if (r < 0)
- return r;
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGQUIT, SIGINT, SIGWINCH, SIGCHLD, -1);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGQUIT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_open_system(&m->sysbus);
- if (r < 0)
- return r;
-
- r = sd_bus_attach_event(m->sysbus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return r;
-
- r = unifont_new(&m->uf);
- if (r < 0)
- return r;
-
- r = sysview_context_new(&m->sysview,
- SYSVIEW_CONTEXT_SCAN_LOGIND |
- SYSVIEW_CONTEXT_SCAN_EVDEV |
- SYSVIEW_CONTEXT_SCAN_DRM,
- m->event,
- m->sysbus,
- NULL);
- if (r < 0)
- return r;
-
- r = grdev_context_new(&m->grdev, m->event, m->sysbus);
- if (r < 0)
- return r;
-
- r = idev_context_new(&m->idev, m->event, m->sysbus);
- if (r < 0)
- return r;
-
- *out = m;
- m = NULL;
- return 0;
-}
-
-Manager *manager_free(Manager *m) {
- if (!m)
- return NULL;
-
- assert(!m->workspace_list);
-
- m->idev = idev_context_unref(m->idev);
- m->grdev = grdev_context_unref(m->grdev);
- m->sysview = sysview_context_free(m->sysview);
- m->uf = unifont_unref(m->uf);
- m->sysbus = sd_bus_unref(m->sysbus);
- m->event = sd_event_unref(m->event);
- free(m);
-
- return NULL;
-}
-
-static int manager_sysview_session_filter(Manager *m, sysview_event *event) {
- const char *sid = event->session_filter.id;
- _cleanup_free_ char *desktop = NULL;
- int r;
-
- assert(sid);
-
- r = sd_session_get_desktop(sid, &desktop);
- if (r < 0)
- return 0;
-
- return streq(desktop, "systemd-console");
-}
-
-static int manager_sysview_session_add(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_add.session;
- Session *s;
- int r;
-
- r = sysview_session_take_control(session);
- if (r < 0)
- return log_error_errno(r, "Cannot request session control on '%s': %m",
- sysview_session_get_name(session));
-
- r = session_new(&s, m, session);
- if (r < 0) {
- log_error_errno(r, "Cannot create session on '%s': %m",
- sysview_session_get_name(session));
- sysview_session_release_control(session);
- return r;
- }
-
- sysview_session_set_userdata(session, s);
-
- return 0;
-}
-
-static int manager_sysview_session_remove(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_remove.session;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_free(s);
-
- return 0;
-}
-
-static int manager_sysview_session_attach(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_attach.session;
- sysview_device *device = event->session_attach.device;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_add_device(s, device);
-
- return 0;
-}
-
-static int manager_sysview_session_detach(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_detach.session;
- sysview_device *device = event->session_detach.device;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_remove_device(s, device);
-
- return 0;
-}
-
-static int manager_sysview_session_refresh(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_refresh.session;
- sysview_device *device = event->session_refresh.device;
- struct udev_device *ud = event->session_refresh.ud;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- session_refresh_device(s, device, ud);
-
- return 0;
-}
-
-static int manager_sysview_session_control(Manager *m, sysview_event *event) {
- sysview_session *session = event->session_control.session;
- int error = event->session_control.error;
- Session *s;
-
- s = sysview_session_get_userdata(session);
- if (!s)
- return 0;
-
- if (error < 0) {
- log_error_errno(error, "Cannot take session control on '%s': %m",
- sysview_session_get_name(session));
- session_free(s);
- sysview_session_set_userdata(session, NULL);
- return error;
- }
-
- return 0;
-}
-
-static int manager_sysview_fn(sysview_context *sysview, void *userdata, sysview_event *event) {
- Manager *m = userdata;
- int r;
-
- assert(m);
-
- switch (event->type) {
- case SYSVIEW_EVENT_SESSION_FILTER:
- r = manager_sysview_session_filter(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_ADD:
- r = manager_sysview_session_add(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_REMOVE:
- r = manager_sysview_session_remove(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_ATTACH:
- r = manager_sysview_session_attach(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_DETACH:
- r = manager_sysview_session_detach(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_REFRESH:
- r = manager_sysview_session_refresh(m, event);
- break;
- case SYSVIEW_EVENT_SESSION_CONTROL:
- r = manager_sysview_session_control(m, event);
- break;
- default:
- r = 0;
- break;
- }
-
- return r;
-}
-
-int manager_run(Manager *m) {
- int r;
-
- assert(m);
-
- r = sysview_context_start(m->sysview, manager_sysview_fn, m);
- if (r < 0)
- return r;
-
- r = sd_event_loop(m->event);
-
- sysview_context_stop(m->sysview);
- return r;
-}
diff --git a/src/console/consoled-session.c b/src/console/consoled-session.c
deleted file mode 100644
index 264a4d009a..0000000000
--- a/src/console/consoled-session.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include "consoled.h"
-#include "grdev.h"
-#include "idev.h"
-#include "list.h"
-#include "macro.h"
-#include "sd-event.h"
-#include "sysview.h"
-#include "util.h"
-
-static bool session_feed_keyboard(Session *s, idev_data *data) {
- idev_data_keyboard *kdata = &data->keyboard;
-
- if (!data->resync && kdata->value == 1 && kdata->n_syms == 1) {
- uint32_t nr;
- sysview_seat *seat;
-
- /* handle VT-switch requests */
- nr = 0;
-
- switch (kdata->keysyms[0]) {
- case XKB_KEY_F1 ... XKB_KEY_F12:
- if (IDEV_KBDMATCH(kdata,
- IDEV_KBDMOD_CTRL | IDEV_KBDMOD_ALT,
- kdata->keysyms[0]))
- nr = kdata->keysyms[0] - XKB_KEY_F1 + 1;
- break;
- case XKB_KEY_XF86Switch_VT_1 ... XKB_KEY_XF86Switch_VT_12:
- nr = kdata->keysyms[0] - XKB_KEY_XF86Switch_VT_1 + 1;
- break;
- }
-
- if (nr != 0) {
- seat = sysview_session_get_seat(s->sysview);
- sysview_seat_switch_to(seat, nr);
- return true;
- }
- }
-
- return false;
-}
-
-static bool session_feed(Session *s, idev_data *data) {
- switch (data->type) {
- case IDEV_DATA_KEYBOARD:
- return session_feed_keyboard(s, data);
- default:
- return false;
- }
-}
-
-static int session_idev_fn(idev_session *idev, void *userdata, idev_event *event) {
- Session *s = userdata;
-
- switch (event->type) {
- case IDEV_EVENT_DEVICE_ADD:
- idev_device_enable(event->device_add.device);
- break;
- case IDEV_EVENT_DEVICE_REMOVE:
- idev_device_disable(event->device_remove.device);
- break;
- case IDEV_EVENT_DEVICE_DATA:
- if (!session_feed(s, &event->device_data.data))
- workspace_feed(s->active_ws, &event->device_data.data);
- break;
- }
-
- return 0;
-}
-
-static void session_grdev_fn(grdev_session *grdev, void *userdata, grdev_event *event) {
- grdev_display *display;
- Session *s = userdata;
- Display *d;
- int r;
-
- switch (event->type) {
- case GRDEV_EVENT_DISPLAY_ADD:
- display = event->display_add.display;
-
- r = display_new(&d, s, display);
- if (r < 0) {
- log_error_errno(r, "Cannot create display '%s' on '%s': %m",
- grdev_display_get_name(display), sysview_session_get_name(s->sysview));
- break;
- }
-
- grdev_display_set_userdata(display, d);
- workspace_refresh(s->active_ws);
- break;
- case GRDEV_EVENT_DISPLAY_REMOVE:
- display = event->display_remove.display;
- d = grdev_display_get_userdata(display);
- if (!d)
- break;
-
- display_free(d);
- workspace_refresh(s->active_ws);
- break;
- case GRDEV_EVENT_DISPLAY_CHANGE:
- display = event->display_remove.display;
- d = grdev_display_get_userdata(display);
- if (!d)
- break;
-
- display_refresh(d);
- workspace_refresh(s->active_ws);
- break;
- case GRDEV_EVENT_DISPLAY_FRAME:
- display = event->display_remove.display;
- d = grdev_display_get_userdata(display);
- if (!d)
- break;
-
- session_dirty(s);
- break;
- }
-}
-
-static int session_redraw_fn(sd_event_source *src, void *userdata) {
- Session *s = userdata;
- Display *d;
-
- LIST_FOREACH(displays_by_session, d, s->display_list)
- display_render(d, s->active_ws);
-
- grdev_session_commit(s->grdev);
-
- return 0;
-}
-
-int session_new(Session **out, Manager *m, sysview_session *session) {
- _cleanup_(session_freep) Session *s = NULL;
- int r;
-
- assert(out);
- assert(m);
- assert(session);
-
- s = new0(Session, 1);
- if (!s)
- return -ENOMEM;
-
- s->manager = m;
- s->sysview = session;
-
- r = grdev_session_new(&s->grdev,
- m->grdev,
- GRDEV_SESSION_MANAGED,
- sysview_session_get_name(session),
- session_grdev_fn,
- s);
- if (r < 0)
- return r;
-
- r = idev_session_new(&s->idev,
- m->idev,
- IDEV_SESSION_MANAGED,
- sysview_session_get_name(session),
- session_idev_fn,
- s);
- if (r < 0)
- return r;
-
- r = workspace_new(&s->my_ws, m);
- if (r < 0)
- return r;
-
- s->active_ws = workspace_attach(s->my_ws, s);
-
- r = sd_event_add_defer(m->event, &s->redraw_src, session_redraw_fn, s);
- if (r < 0)
- return r;
-
- grdev_session_enable(s->grdev);
- idev_session_enable(s->idev);
-
- *out = s;
- s = NULL;
- return 0;
-}
-
-Session *session_free(Session *s) {
- if (!s)
- return NULL;
-
- assert(!s->display_list);
-
- sd_event_source_unref(s->redraw_src);
-
- workspace_detach(s->active_ws, s);
- workspace_unref(s->my_ws);
-
- idev_session_free(s->idev);
- grdev_session_free(s->grdev);
- free(s);
-
- return NULL;
-}
-
-void session_dirty(Session *s) {
- int r;
-
- assert(s);
-
- r = sd_event_source_set_enabled(s->redraw_src, SD_EVENT_ONESHOT);
- if (r < 0)
- log_error_errno(r, "Cannot enable redraw-source: %m");
-}
-
-void session_add_device(Session *s, sysview_device *device) {
- unsigned int type;
-
- assert(s);
- assert(device);
-
- type = sysview_device_get_type(device);
- switch (type) {
- case SYSVIEW_DEVICE_DRM:
- grdev_session_add_drm(s->grdev, sysview_device_get_ud(device));
- break;
- case SYSVIEW_DEVICE_EVDEV:
- idev_session_add_evdev(s->idev, sysview_device_get_ud(device));
- break;
- }
-}
-
-void session_remove_device(Session *s, sysview_device *device) {
- unsigned int type;
-
- assert(s);
- assert(device);
-
- type = sysview_device_get_type(device);
- switch (type) {
- case SYSVIEW_DEVICE_DRM:
- grdev_session_remove_drm(s->grdev, sysview_device_get_ud(device));
- break;
- case SYSVIEW_DEVICE_EVDEV:
- idev_session_remove_evdev(s->idev, sysview_device_get_ud(device));
- break;
- }
-}
-
-void session_refresh_device(Session *s, sysview_device *device, struct udev_device *ud) {
- unsigned int type;
-
- assert(s);
- assert(device);
-
- type = sysview_device_get_type(device);
- switch (type) {
- case SYSVIEW_DEVICE_DRM:
- grdev_session_hotplug_drm(s->grdev, sysview_device_get_ud(device));
- break;
- }
-}
diff --git a/src/console/consoled-terminal.c b/src/console/consoled-terminal.c
deleted file mode 100644
index 03447d1b92..0000000000
--- a/src/console/consoled-terminal.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include "consoled.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-
-static int terminal_write_fn(term_screen *screen, void *userdata, const void *buf, size_t size) {
- Terminal *t = userdata;
- int r;
-
- if (t->pty) {
- r = pty_write(t->pty, buf, size);
- if (r < 0)
- return log_oom();
- }
-
- return 0;
-}
-
-static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size) {
- Terminal *t = userdata;
- int r;
-
- switch (event) {
- case PTY_CHILD:
- log_debug("PTY child exited");
- t->pty = pty_unref(t->pty);
- break;
- case PTY_DATA:
- r = term_screen_feed_text(t->screen, ptr, size);
- if (r < 0)
- log_error_errno(r, "Cannot update screen state: %m");
-
- workspace_dirty(t->workspace);
- break;
- }
-
- return 0;
-}
-
-int terminal_new(Terminal **out, Workspace *w) {
- _cleanup_(terminal_freep) Terminal *t = NULL;
- int r;
-
- assert(w);
-
- t = new0(Terminal, 1);
- if (!t)
- return -ENOMEM;
-
- t->workspace = w;
- LIST_PREPEND(terminals_by_workspace, w->terminal_list, t);
-
- r = term_parser_new(&t->parser, true);
- if (r < 0)
- return r;
-
- r = term_screen_new(&t->screen, terminal_write_fn, t, NULL, NULL);
- if (r < 0)
- return r;
-
- r = term_screen_set_answerback(t->screen, "systemd-console");
- if (r < 0)
- return r;
-
- if (out)
- *out = t;
- t = NULL;
- return 0;
-}
-
-Terminal *terminal_free(Terminal *t) {
- if (!t)
- return NULL;
-
- assert(t->workspace);
-
- if (t->pty) {
- (void) pty_signal(t->pty, SIGHUP);
- pty_close(t->pty);
- pty_unref(t->pty);
- }
- term_screen_unref(t->screen);
- term_parser_free(t->parser);
- LIST_REMOVE(terminals_by_workspace, t->workspace->terminal_list, t);
- free(t);
-
- return NULL;
-}
-
-void terminal_resize(Terminal *t) {
- uint32_t width, height, fw, fh;
- int r;
-
- assert(t);
-
- width = t->workspace->width;
- height = t->workspace->height;
- fw = unifont_get_width(t->workspace->manager->uf);
- fh = unifont_get_height(t->workspace->manager->uf);
-
- width = (fw > 0) ? width / fw : 0;
- height = (fh > 0) ? height / fh : 0;
-
- if (t->pty) {
- r = pty_resize(t->pty, width, height);
- if (r < 0)
- log_error_errno(r, "Cannot resize pty: %m");
- }
-
- r = term_screen_resize(t->screen, width, height);
- if (r < 0)
- log_error_errno(r, "Cannot resize screen: %m");
-}
-
-void terminal_run(Terminal *t) {
- pid_t pid;
-
- assert(t);
-
- if (t->pty)
- return;
-
- pid = pty_fork(&t->pty,
- t->workspace->manager->event,
- terminal_pty_fn,
- t,
- term_screen_get_width(t->screen),
- term_screen_get_height(t->screen));
- if (pid < 0) {
- log_error_errno(pid, "Cannot fork PTY: %m");
- return;
- } else if (pid == 0) {
- /* child */
-
- char **argv = (char*[]){
- (char*)getenv("SHELL") ? : (char*)_PATH_BSHELL,
- NULL
- };
-
- setenv("TERM", "xterm-256color", 1);
- setenv("COLORTERM", "systemd-console", 1);
-
- execve(argv[0], argv, environ);
- log_error_errno(errno, "Cannot exec %s (%d): %m", argv[0], -errno);
- _exit(1);
- }
-}
-
-static void terminal_feed_keyboard(Terminal *t, idev_data *data) {
- idev_data_keyboard *kdata = &data->keyboard;
- int r;
-
- if (!data->resync && (kdata->value == 1 || kdata->value == 2)) {
- assert_cc(TERM_KBDMOD_CNT == (int)IDEV_KBDMOD_CNT);
- assert_cc(TERM_KBDMOD_IDX_SHIFT == (int)IDEV_KBDMOD_IDX_SHIFT &&
- TERM_KBDMOD_IDX_CTRL == (int)IDEV_KBDMOD_IDX_CTRL &&
- TERM_KBDMOD_IDX_ALT == (int)IDEV_KBDMOD_IDX_ALT &&
- TERM_KBDMOD_IDX_LINUX == (int)IDEV_KBDMOD_IDX_LINUX &&
- TERM_KBDMOD_IDX_CAPS == (int)IDEV_KBDMOD_IDX_CAPS);
-
- r = term_screen_feed_keyboard(t->screen,
- kdata->keysyms,
- kdata->n_syms,
- kdata->ascii,
- kdata->codepoints,
- kdata->mods);
- if (r < 0)
- log_error_errno(r, "Cannot feed keyboard data to screen: %m");
- }
-}
-
-void terminal_feed(Terminal *t, idev_data *data) {
- switch (data->type) {
- case IDEV_DATA_KEYBOARD:
- terminal_feed_keyboard(t, data);
- break;
- }
-}
-
-static void terminal_fill(uint8_t *dst,
- uint32_t width,
- uint32_t height,
- uint32_t stride,
- uint32_t value) {
- uint32_t i, j, *px;
-
- for (j = 0; j < height; ++j) {
- px = (uint32_t*)dst;
-
- for (i = 0; i < width; ++i)
- *px++ = value;
-
- dst += stride;
- }
-}
-
-static void terminal_blend(uint8_t *dst,
- uint32_t width,
- uint32_t height,
- uint32_t dst_stride,
- const uint8_t *src,
- uint32_t src_stride,
- uint32_t fg,
- uint32_t bg) {
- uint32_t i, j, *px;
-
- for (j = 0; j < height; ++j) {
- px = (uint32_t*)dst;
-
- for (i = 0; i < width; ++i) {
- if (!src || src[i / 8] & (1 << (7 - i % 8)))
- *px = fg;
- else
- *px = bg;
-
- ++px;
- }
-
- src += src_stride;
- dst += dst_stride;
- }
-}
-
-typedef struct {
- const grdev_display_target *target;
- unifont *uf;
- uint32_t cell_width;
- uint32_t cell_height;
- bool dirty;
-} TerminalDrawContext;
-
-static int terminal_draw_cell(term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width) {
- TerminalDrawContext *ctx = userdata;
- const grdev_display_target *target = ctx->target;
- grdev_fb *fb = target->back;
- uint32_t xpos, ypos, width, height;
- uint32_t fg, bg;
- unifont_glyph g;
- uint8_t *dst;
- int r;
-
- if (n_ch > 0) {
- r = unifont_lookup(ctx->uf, &g, *ch);
- if (r < 0)
- r = unifont_lookup(ctx->uf, &g, 0xfffd);
- if (r < 0)
- unifont_fallback(&g);
- }
-
- xpos = x * ctx->cell_width;
- ypos = y * ctx->cell_height;
-
- if (xpos >= fb->width || ypos >= fb->height)
- return 0;
-
- width = MIN(fb->width - xpos, ctx->cell_width * ch_width);
- height = MIN(fb->height - ypos, ctx->cell_height);
-
- term_attr_to_argb32(attr, &fg, &bg, NULL);
-
- ctx->dirty = true;
-
- dst = fb->maps[0];
- dst += fb->strides[0] * ypos + sizeof(uint32_t) * xpos;
-
- if (n_ch < 1) {
- terminal_fill(dst,
- width,
- height,
- fb->strides[0],
- bg);
- } else {
- if (width > g.width)
- terminal_fill(dst + sizeof(uint32_t) * g.width,
- width - g.width,
- height,
- fb->strides[0],
- bg);
- if (height > g.height)
- terminal_fill(dst + fb->strides[0] * g.height,
- width,
- height - g.height,
- fb->strides[0],
- bg);
-
- terminal_blend(dst,
- width,
- height,
- fb->strides[0],
- g.data,
- g.stride,
- fg,
- bg);
- }
-
- return 0;
-}
-
-bool terminal_draw(Terminal *t, const grdev_display_target *target) {
- TerminalDrawContext ctx = { };
- uint64_t age;
-
- assert(t);
- assert(target);
-
- /* start up terminal on first frame */
- terminal_run(t);
-
- ctx.target = target;
- ctx.uf = t->workspace->manager->uf;
- ctx.cell_width = unifont_get_width(ctx.uf);
- ctx.cell_height = unifont_get_height(ctx.uf);
- ctx.dirty = false;
-
- if (target->front) {
- /* if the frontbuffer is new enough, no reason to redraw */
- age = term_screen_get_age(t->screen);
- if (age != 0 && age <= target->front->data.u64)
- return false;
- } else {
- /* force flip if no frontbuffer is set, yet */
- ctx.dirty = true;
- }
-
- term_screen_draw(t->screen, terminal_draw_cell, &ctx, &target->back->data.u64);
-
- return ctx.dirty;
-}
diff --git a/src/console/consoled-workspace.c b/src/console/consoled-workspace.c
deleted file mode 100644
index 5e9e5c7c49..0000000000
--- a/src/console/consoled-workspace.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include "consoled.h"
-#include "grdev.h"
-#include "idev.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-
-int workspace_new(Workspace **out, Manager *m) {
- _cleanup_(workspace_unrefp) Workspace *w = NULL;
- int r;
-
- assert(out);
-
- w = new0(Workspace, 1);
- if (!w)
- return -ENOMEM;
-
- w->ref = 1;
- w->manager = m;
- LIST_PREPEND(workspaces_by_manager, m->workspace_list, w);
-
- r = terminal_new(&w->current, w);
- if (r < 0)
- return r;
-
- *out = w;
- w = NULL;
- return 0;
-}
-
-static void workspace_cleanup(Workspace *w) {
- Terminal *t;
-
- assert(w);
- assert(w->ref == 0);
- assert(w->manager);
- assert(!w->session_list);
-
- w->current = NULL;
- while ((t = w->terminal_list))
- terminal_free(t);
-
- LIST_REMOVE(workspaces_by_manager, w->manager->workspace_list, w);
- free(w);
-}
-
-Workspace *workspace_ref(Workspace *w) {
- assert(w);
-
- ++w->ref;
- return w;
-}
-
-Workspace *workspace_unref(Workspace *w) {
- if (!w)
- return NULL;
-
- assert(w->ref > 0);
-
- if (--w->ref == 0)
- workspace_cleanup(w);
-
- return NULL;
-}
-
-Workspace *workspace_attach(Workspace *w, Session *s) {
- assert(w);
- assert(s);
-
- LIST_PREPEND(sessions_by_workspace, w->session_list, s);
- workspace_refresh(w);
- return workspace_ref(w);
-}
-
-Workspace *workspace_detach(Workspace *w, Session *s) {
- assert(w);
- assert(s);
- assert(s->active_ws == w);
-
- LIST_REMOVE(sessions_by_workspace, w->session_list, s);
- workspace_refresh(w);
- return workspace_unref(w);
-}
-
-void workspace_refresh(Workspace *w) {
- uint32_t width, height;
- Terminal *t;
- Session *s;
- Display *d;
-
- assert(w);
-
- width = 0;
- height = 0;
-
- /* find out minimum dimension of all attached displays */
- LIST_FOREACH(sessions_by_workspace, s, w->session_list) {
- LIST_FOREACH(displays_by_session, d, s->display_list) {
- assert(d->width > 0 && d->height > 0);
-
- if (width == 0 || d->width < width)
- width = d->width;
- if (height == 0 || d->height < height)
- height = d->height;
- }
- }
-
- /* either both are zero, or none is zero */
- assert(!(!width ^ !height));
-
- /* update terminal-sizes if dimensions changed */
- if (w->width != width || w->height != height) {
- w->width = width;
- w->height = height;
-
- LIST_FOREACH(terminals_by_workspace, t, w->terminal_list)
- terminal_resize(t);
-
- workspace_dirty(w);
- }
-}
-
-void workspace_dirty(Workspace *w) {
- Session *s;
-
- assert(w);
-
- LIST_FOREACH(sessions_by_workspace, s, w->session_list)
- session_dirty(s);
-}
-
-void workspace_feed(Workspace *w, idev_data *data) {
- assert(w);
- assert(data);
-
- terminal_feed(w->current, data);
-}
-
-bool workspace_draw(Workspace *w, const grdev_display_target *target) {
- assert(w);
- assert(target);
-
- return terminal_draw(w->current, target);
-}
diff --git a/src/console/consoled.c b/src/console/consoled.c
deleted file mode 100644
index 9f69e8983f..0000000000
--- a/src/console/consoled.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdlib.h>
-#include "sd-daemon.h"
-#include "log.h"
-#include "signal-util.h"
-#include "consoled.h"
-
-int main(int argc, char *argv[]) {
- _cleanup_(manager_freep) Manager *m = NULL;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto out;
- }
-
- r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Could not create manager: %m");
- goto out;
- }
-
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- r = manager_run(m);
- if (r < 0) {
- log_error_errno(r, "Cannot run manager: %m");
- goto out;
- }
-
-out:
- sd_notify(false,
- "STATUS=Shutting down...");
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/console/consoled.h b/src/console/consoled.h
deleted file mode 100644
index f85c1a0791..0000000000
--- a/src/console/consoled.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "grdev.h"
-#include "idev.h"
-#include "list.h"
-#include "macro.h"
-#include "pty.h"
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sysview.h"
-#include "term.h"
-#include "unifont.h"
-
-typedef struct Manager Manager;
-typedef struct Session Session;
-typedef struct Display Display;
-typedef struct Workspace Workspace;
-typedef struct Terminal Terminal;
-
-/*
- * Terminals
- */
-
-struct Terminal {
- Workspace *workspace;
- LIST_FIELDS(Terminal, terminals_by_workspace);
-
- term_utf8 utf8;
- term_parser *parser;
- term_screen *screen;
- Pty *pty;
-};
-
-int terminal_new(Terminal **out, Workspace *w);
-Terminal *terminal_free(Terminal *t);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Terminal*, terminal_free);
-
-void terminal_resize(Terminal *t);
-void terminal_run(Terminal *t);
-void terminal_feed(Terminal *t, idev_data *data);
-bool terminal_draw(Terminal *t, const grdev_display_target *target);
-
-/*
- * Workspaces
- */
-
-struct Workspace {
- unsigned long ref;
- Manager *manager;
- LIST_FIELDS(Workspace, workspaces_by_manager);
-
- LIST_HEAD(Terminal, terminal_list);
- Terminal *current;
-
- LIST_HEAD(Session, session_list);
- uint32_t width;
- uint32_t height;
-};
-
-int workspace_new(Workspace **out, Manager *m);
-Workspace *workspace_ref(Workspace *w);
-Workspace *workspace_unref(Workspace *w);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Workspace*, workspace_unref);
-
-Workspace *workspace_attach(Workspace *w, Session *s);
-Workspace *workspace_detach(Workspace *w, Session *s);
-void workspace_refresh(Workspace *w);
-
-void workspace_dirty(Workspace *w);
-void workspace_feed(Workspace *w, idev_data *data);
-bool workspace_draw(Workspace *w, const grdev_display_target *target);
-
-/*
- * Displays
- */
-
-struct Display {
- Session *session;
- LIST_FIELDS(Display, displays_by_session);
- grdev_display *grdev;
- uint32_t width;
- uint32_t height;
-};
-
-int display_new(Display **out, Session *s, grdev_display *grdev);
-Display *display_free(Display *d);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Display*, display_free);
-
-void display_refresh(Display *d);
-void display_render(Display *d, Workspace *w);
-
-/*
- * Sessions
- */
-
-struct Session {
- Manager *manager;
- sysview_session *sysview;
- grdev_session *grdev;
- idev_session *idev;
-
- LIST_FIELDS(Session, sessions_by_workspace);
- Workspace *my_ws;
- Workspace *active_ws;
-
- LIST_HEAD(Display, display_list);
- sd_event_source *redraw_src;
-};
-
-int session_new(Session **out, Manager *m, sysview_session *session);
-Session *session_free(Session *s);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Session*, session_free);
-
-void session_dirty(Session *s);
-
-void session_add_device(Session *s, sysview_device *device);
-void session_remove_device(Session *s, sysview_device *device);
-void session_refresh_device(Session *s, sysview_device *device, struct udev_device *ud);
-
-/*
- * Managers
- */
-
-struct Manager {
- sd_event *event;
- sd_bus *sysbus;
- unifont *uf;
- sysview_context *sysview;
- grdev_context *grdev;
- idev_context *idev;
- LIST_HEAD(Workspace, workspace_list);
-};
-
-int manager_new(Manager **out);
-Manager *manager_free(Manager *m);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
-
-int manager_run(Manager *m);
diff --git a/src/core/automount.c b/src/core/automount.c
index d847dc1629..4af381b4b6 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -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.",
diff --git a/src/core/busname.c b/src/core/busname.c
index 2085721546..9530a87311 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -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.",
},
},
};
diff --git a/src/core/execute.c b/src/core/execute.c
index c92db51330..21721dc240 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -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");
diff --git a/src/core/job.c b/src/core/job.c
index 1448e5b69a..15f5cc0cc9 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -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);
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index b3d22840cf..8e26362546 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -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);
diff --git a/src/core/main.c b/src/core/main.c
index 523f0ce020..6ae8b51544 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -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");
diff --git a/src/core/mount.c b/src/core/mount.c
index 851b41351e..c0d1cdfbd4 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -834,8 +834,6 @@ static void mount_enter_unmounting(Mount *m) {
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r < 0)
goto fail;
@@ -886,8 +884,6 @@ static void mount_enter_mounting(Mount *m) {
r = exec_command_set(m->control_command, MOUNT_PATH,
m->parameters_fragment.what, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
if (r >= 0 && m->parameters_fragment.fstype)
@@ -934,8 +930,6 @@ static void mount_enter_remounting(Mount *m) {
r = exec_command_set(m->control_command, MOUNT_PATH,
m->parameters_fragment.what, m->where,
"-o", o, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
if (r >= 0 && m->parameters_fragment.fstype)
@@ -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 = {
diff --git a/src/core/path.c b/src/core/path.c
index 6d26d89e82..20995d920c 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -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);
}
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index e9a9a020de..50a90b0bac 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -302,12 +302,12 @@ int mac_selinux_unit_access_check_strv(
int r;
STRV_FOREACH(i, units) {
- u = manager_get_unit(m, *i);
- if (u) {
- r = mac_selinux_unit_access_check(u, message, permission, error);
- if (r < 0)
- return r;
- }
+ r = manager_load_unit(m, *i, NULL, error, &u);
+ if (r < 0)
+ return r;
+ r = mac_selinux_unit_access_check(u, message, permission, error);
+ if (r < 0)
+ return r;
}
#endif
return 0;
diff --git a/src/core/service.c b/src/core/service.c
index d72ff54daa..b790ec98be 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -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.",
},
},
};
diff --git a/src/core/slice.c b/src/core/slice.c
index e52bf71515..064eb5d933 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -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.",
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index ddb02a1580..cbe7d0b4a9 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -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));
diff --git a/src/core/socket.c b/src/core/socket.c
index 693cbc6080..87631f8753 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -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 = {
diff --git a/src/core/swap.c b/src/core/swap.c
index 193c8c3767..0bc3827ff0 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -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 = {
diff --git a/src/core/target.c b/src/core/target.c
index 8817ef21c4..b492a7c4c7 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -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.",
diff --git a/src/core/unit.c b/src/core/unit.c
index fac017c57d..dd5e801285 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -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) {
diff --git a/src/core/unit.h b/src/core/unit.h
index 9491ef64f9..e60168267f 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -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);
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index cda96d484a..3805b29437 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -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");
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index b46e160888..da5f3b647a 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -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");
diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c
index 43aac616b6..1f3b169905 100644
--- a/src/hibernate-resume/hibernate-resume.c
+++ b/src/hibernate-resume/hibernate-resume.c
@@ -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;
diff --git a/src/import/pull-dkr.c b/src/import/pull-dkr.c
index 78e3184c42..67ca1ce8e4 100644
--- a/src/import/pull-dkr.c
+++ b/src/import/pull-dkr.c
@@ -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;
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index d9450ae8cd..9a09f401e0 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -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;
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index be6a5522fa..f7815b2796 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -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;
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 403c8f760c..e92b75eabe 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -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);
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 81a577ea27..17499bbc30 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -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
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index ce734d8df7..eaf006db7a 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -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;
}
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 2d6ecfb750..073cc77711 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -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();
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 46358e1c1a..28b1472ac8 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -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),
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index 9e184ac4b5..6e00b1ad30 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -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);
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index abca9422c5..aa37e9b0b5 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -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,
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index d8bc76edda..54417b3af3 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -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
index 7de83bd3e9..0000000000
--- a/src/libsystemd-terminal/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/unifont-glyph-array.bin
diff --git a/src/libsystemd-terminal/evcat.c b/src/libsystemd-terminal/evcat.c
deleted file mode 100644
index 2aeefc2e16..0000000000
--- a/src/libsystemd-terminal/evcat.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Event Catenation
- * The evcat tool catenates input events of all requested devices and prints
- * them to standard-output. It's only meant for debugging of input-related
- * problems.
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <libevdev/libevdev.h>
-#include <linux/kd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <termios.h>
-#include <unistd.h>
-#include <xkbcommon/xkbcommon.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "build.h"
-#include "event-util.h"
-#include "macro.h"
-#include "signal-util.h"
-#include "util.h"
-#include "idev.h"
-#include "sysview.h"
-#include "term-internal.h"
-
-typedef struct Evcat Evcat;
-
-struct Evcat {
- char *session;
- char *seat;
- sd_event *event;
- sd_bus *bus;
- sysview_context *sysview;
- idev_context *idev;
- idev_session *idev_session;
-
- bool managed : 1;
-};
-
-static Evcat *evcat_free(Evcat *e) {
- if (!e)
- return NULL;
-
- e->idev_session = idev_session_free(e->idev_session);
- e->idev = idev_context_unref(e->idev);
- e->sysview = sysview_context_free(e->sysview);
- e->bus = sd_bus_unref(e->bus);
- e->event = sd_event_unref(e->event);
- free(e->seat);
- free(e->session);
- free(e);
-
- tcflush(0, TCIOFLUSH);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Evcat*, evcat_free);
-
-static bool is_managed(const char *session) {
- unsigned int vtnr;
- struct stat st;
- long mode;
- int r;
-
- /* Using logind's Controller API is highly fragile if there is already
- * a session controller running. If it is registered as controller
- * itself, TakeControl will simply fail. But if its a legacy controller
- * that does not use logind's controller API, we must never register
- * our own controller. Otherwise, we really mess up the VT. Therefore,
- * only run in managed mode if there's no-one else. */
-
- if (geteuid() == 0)
- return false;
-
- if (!isatty(1))
- return false;
-
- if (!session)
- return false;
-
- r = sd_session_get_vt(session, &vtnr);
- if (r < 0 || vtnr < 1 || vtnr > 63)
- return false;
-
- mode = 0;
- r = ioctl(1, KDGETMODE, &mode);
- if (r < 0 || mode != KD_TEXT)
- return false;
-
- r = fstat(1, &st);
- if (r < 0 || minor(st.st_rdev) != vtnr)
- return false;
-
- return true;
-}
-
-static int evcat_new(Evcat **out) {
- _cleanup_(evcat_freep) Evcat *e = NULL;
- int r;
-
- assert(out);
-
- e = new0(Evcat, 1);
- if (!e)
- return log_oom();
-
- r = sd_pid_get_session(getpid(), &e->session);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve logind session: %m");
-
- r = sd_session_get_seat(e->session, &e->seat);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve seat of logind session: %m");
-
- e->managed = is_managed(e->session);
-
- r = sd_event_default(&e->event);
- if (r < 0)
- return r;
-
- r = sd_bus_open_system(&e->bus);
- if (r < 0)
- return r;
-
- r = sd_bus_attach_event(e->bus, e->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return r;
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(e->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(e->event, NULL, SIGINT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sysview_context_new(&e->sysview,
- SYSVIEW_CONTEXT_SCAN_LOGIND |
- SYSVIEW_CONTEXT_SCAN_EVDEV,
- e->event,
- e->bus,
- NULL);
- if (r < 0)
- return r;
-
- r = idev_context_new(&e->idev, e->event, e->bus);
- if (r < 0)
- return r;
-
- *out = e;
- e = NULL;
- return 0;
-}
-
-static void kdata_print(idev_data *data) {
- idev_data_keyboard *k = &data->keyboard;
- char buf[128];
- uint32_t i, c;
- int cwidth;
-
- /* Key-press state: UP/DOWN/REPEAT */
- printf(" %-6s", k->value == 0 ? "UP" :
- k->value == 1 ? "DOWN" :
- "REPEAT");
-
- /* Resync state */
- printf(" | %-6s", data->resync ? "RESYNC" : "");
-
- /* Keycode that triggered the event */
- printf(" | %5u", (unsigned)k->keycode);
-
- /* Well-known name of the keycode */
- printf(" | %-20s", libevdev_event_code_get_name(EV_KEY, k->keycode) ? : "<unknown>");
-
- /* Well-known modifiers */
- printf(" | %-5s", (k->mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
- printf(" %-4s", (k->mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
- printf(" %-3s", (k->mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
- printf(" %-5s", (k->mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
- printf(" %-4s", (k->mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
-
- /* Consumed modifiers */
- printf(" | %-5s", (k->consumed_mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
- printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
- printf(" %-3s", (k->consumed_mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
- printf(" %-5s", (k->consumed_mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
- printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
-
- /* Resolved symbols */
- printf(" |");
- for (i = 0; i < k->n_syms; ++i) {
- buf[0] = 0;
- xkb_keysym_get_name(k->keysyms[i], buf, sizeof(buf));
-
- if (is_locale_utf8()) {
- c = k->codepoints[i];
- if (c < 0x110000 && c > 0x20 && (c < 0x7f || c > 0x9f)) {
- /* "%4lc" doesn't work well, so hard-code it */
- cwidth = mk_wcwidth(c);
- while (cwidth++ < 2)
- printf(" ");
-
- printf(" '%lc':", (wchar_t)c);
- } else {
- printf(" ");
- }
- }
-
- printf(" XKB_KEY_%-30s", buf);
- }
-
- printf("\n");
-}
-
-static bool kdata_is_exit(idev_data *data) {
- idev_data_keyboard *k = &data->keyboard;
-
- if (k->value != 1)
- return false;
- if (k->n_syms != 1)
- return false;
-
- return k->codepoints[0] == 'q';
-}
-
-static int evcat_idev_fn(idev_session *session, void *userdata, idev_event *ev) {
- Evcat *e = userdata;
-
- switch (ev->type) {
- case IDEV_EVENT_DEVICE_ADD:
- idev_device_enable(ev->device_add.device);
- break;
- case IDEV_EVENT_DEVICE_REMOVE:
- idev_device_disable(ev->device_remove.device);
- break;
- case IDEV_EVENT_DEVICE_DATA:
- switch (ev->device_data.data.type) {
- case IDEV_DATA_KEYBOARD:
- if (kdata_is_exit(&ev->device_data.data))
- sd_event_exit(e->event, 0);
- else
- kdata_print(&ev->device_data.data);
-
- break;
- }
-
- break;
- }
-
- return 0;
-}
-
-static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
- unsigned int flags, type;
- Evcat *e = userdata;
- sysview_device *d;
- const char *name;
- int r;
-
- switch (ev->type) {
- case SYSVIEW_EVENT_SESSION_FILTER:
- if (streq_ptr(e->session, ev->session_filter.id))
- return 1;
-
- break;
- case SYSVIEW_EVENT_SESSION_ADD:
- assert(!e->idev_session);
-
- name = sysview_session_get_name(ev->session_add.session);
- flags = 0;
-
- if (e->managed)
- flags |= IDEV_SESSION_MANAGED;
-
- r = idev_session_new(&e->idev_session,
- e->idev,
- flags,
- name,
- evcat_idev_fn,
- e);
- if (r < 0)
- return log_error_errno(r, "Cannot create idev session: %m");
-
- if (e->managed) {
- r = sysview_session_take_control(ev->session_add.session);
- if (r < 0)
- return log_error_errno(r, "Cannot request session control: %m");
- }
-
- idev_session_enable(e->idev_session);
-
- break;
- case SYSVIEW_EVENT_SESSION_REMOVE:
- idev_session_disable(e->idev_session);
- e->idev_session = idev_session_free(e->idev_session);
- if (sd_event_get_exit_code(e->event, &r) == -ENODATA)
- sd_event_exit(e->event, 0);
- break;
- case SYSVIEW_EVENT_SESSION_ATTACH:
- d = ev->session_attach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_EVDEV) {
- r = idev_session_add_evdev(e->idev_session, sysview_device_get_ud(d));
- if (r < 0)
- return log_error_errno(r, "Cannot add evdev device to idev: %m");
- }
-
- break;
- case SYSVIEW_EVENT_SESSION_DETACH:
- d = ev->session_detach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_EVDEV) {
- r = idev_session_remove_evdev(e->idev_session, sysview_device_get_ud(d));
- if (r < 0)
- return log_error_errno(r, "Cannot remove evdev device from idev: %m");
- }
-
- break;
- case SYSVIEW_EVENT_SESSION_CONTROL:
- r = ev->session_control.error;
- if (r < 0)
- return log_error_errno(r, "Cannot acquire session control: %m");
-
- r = ioctl(1, KDSKBMODE, K_UNICODE);
- if (r < 0)
- return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m");
-
- r = ioctl(1, KDSETMODE, KD_TEXT);
- if (r < 0)
- return log_error_errno(errno, "Cannot set KD_TEXT on stdout: %m");
-
- printf("\n");
-
- break;
- }
-
- return 0;
-}
-
-static int evcat_run(Evcat *e) {
- struct termios in_attr, saved_attr;
- int r;
-
- assert(e);
-
- if (!e->managed && geteuid() > 0)
- log_warning("You run in unmanaged mode without being root. This is likely to produce no output..");
-
- printf("evcat - Read and catenate events from selected input devices\n"
- " Running on seat '%s' in user-session '%s'\n"
- " Exit by pressing ^C or 'q'\n\n",
- e->seat ? : "seat0", e->session ? : "<none>");
-
- r = sysview_context_start(e->sysview, evcat_sysview_fn, e);
- if (r < 0)
- goto out;
-
- r = tcgetattr(0, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- saved_attr = in_attr;
- in_attr.c_lflag &= ~ECHO;
-
- r = tcsetattr(0, TCSANOW, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- r = sd_event_loop(e->event);
- tcsetattr(0, TCSANOW, &saved_attr);
- printf("exiting..\n");
-
-out:
- sysview_context_stop(e->sysview);
- return r;
-}
-
-static int help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Read and catenate events from selected input devices.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- , program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- };
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- {},
- };
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- switch (c) {
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
-
- if (argc > optind) {
- log_error("Too many arguments");
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_(evcat_freep) Evcat *e = NULL;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- setlocale(LC_ALL, "");
- if (!is_locale_utf8())
- log_warning("Locale is not set to UTF-8. Codepoints will not be printed!");
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- r = evcat_new(&e);
- if (r < 0)
- goto finish;
-
- r = evcat_run(e);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
deleted file mode 100644
index 10c13e348a..0000000000
--- a/src/libsystemd-terminal/grdev-drm.c
+++ /dev/null
@@ -1,3092 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-/* Yuck! DRM headers need system headers included first.. but we have to
- * include it before util/missing.h to avoid redefining ioctl bits */
-#include <drm.h>
-#include <drm_fourcc.h>
-#include <drm_mode.h>
-
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "util.h"
-#include "bus-util.h"
-#include "grdev.h"
-#include "grdev-internal.h"
-
-#define GRDRM_MAX_TRIES (16)
-
-typedef struct grdrm_object grdrm_object;
-typedef struct grdrm_plane grdrm_plane;
-typedef struct grdrm_connector grdrm_connector;
-typedef struct grdrm_encoder grdrm_encoder;
-typedef struct grdrm_crtc grdrm_crtc;
-
-typedef struct grdrm_fb grdrm_fb;
-typedef struct grdrm_pipe grdrm_pipe;
-typedef struct grdrm_card grdrm_card;
-typedef struct unmanaged_card unmanaged_card;
-typedef struct managed_card managed_card;
-
-/*
- * Objects
- */
-
-enum {
- GRDRM_TYPE_CRTC,
- GRDRM_TYPE_ENCODER,
- GRDRM_TYPE_CONNECTOR,
- GRDRM_TYPE_PLANE,
- GRDRM_TYPE_CNT
-};
-
-struct grdrm_object {
- grdrm_card *card;
- uint32_t id;
- uint32_t index;
- unsigned int type;
- void (*free_fn) (grdrm_object *object);
-
- bool present : 1;
- bool assigned : 1;
-};
-
-struct grdrm_plane {
- grdrm_object object;
-
- struct {
- uint32_t used_crtc;
- uint32_t used_fb;
- uint32_t gamma_size;
-
- uint32_t n_crtcs;
- uint32_t max_crtcs;
- uint32_t *crtcs;
- uint32_t n_formats;
- uint32_t max_formats;
- uint32_t *formats;
- } kern;
-};
-
-struct grdrm_connector {
- grdrm_object object;
-
- struct {
- uint32_t type;
- uint32_t type_id;
- uint32_t used_encoder;
- uint32_t connection;
- uint32_t mm_width;
- uint32_t mm_height;
- uint32_t subpixel;
-
- uint32_t n_encoders;
- uint32_t max_encoders;
- uint32_t *encoders;
- uint32_t n_modes;
- uint32_t max_modes;
- struct drm_mode_modeinfo *modes;
- uint32_t n_props;
- uint32_t max_props;
- uint32_t *prop_ids;
- uint64_t *prop_values;
- } kern;
-};
-
-struct grdrm_encoder {
- grdrm_object object;
-
- struct {
- uint32_t type;
- uint32_t used_crtc;
-
- uint32_t n_crtcs;
- uint32_t max_crtcs;
- uint32_t *crtcs;
- uint32_t n_clones;
- uint32_t max_clones;
- uint32_t *clones;
- } kern;
-};
-
-struct grdrm_crtc {
- grdrm_object object;
-
- struct {
- uint32_t used_fb;
- uint32_t fb_offset_x;
- uint32_t fb_offset_y;
- uint32_t gamma_size;
-
- uint32_t n_used_connectors;
- uint32_t max_used_connectors;
- uint32_t *used_connectors;
-
- bool mode_set;
- struct drm_mode_modeinfo mode;
- } kern;
-
- struct {
- bool set;
- uint32_t fb;
- uint32_t fb_x;
- uint32_t fb_y;
- uint32_t gamma;
-
- uint32_t n_connectors;
- uint32_t *connectors;
-
- bool mode_set;
- struct drm_mode_modeinfo mode;
- } old;
-
- struct {
- struct drm_mode_modeinfo mode;
- uint32_t n_connectors;
- uint32_t max_connectors;
- uint32_t *connectors;
- } set;
-
- grdrm_pipe *pipe;
-
- bool applied : 1;
-};
-
-#define GRDRM_OBJECT_INIT(_card, _id, _index, _type, _free_fn) ((grdrm_object){ \
- .card = (_card), \
- .id = (_id), \
- .index = (_index), \
- .type = (_type), \
- .free_fn = (_free_fn), \
- })
-
-grdrm_object *grdrm_find_object(grdrm_card *card, uint32_t id);
-int grdrm_object_add(grdrm_object *object);
-grdrm_object *grdrm_object_free(grdrm_object *object);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdrm_object*, grdrm_object_free);
-
-int grdrm_plane_new(grdrm_plane **out, grdrm_card *card, uint32_t id, uint32_t index);
-int grdrm_connector_new(grdrm_connector **out, grdrm_card *card, uint32_t id, uint32_t index);
-int grdrm_encoder_new(grdrm_encoder **out, grdrm_card *card, uint32_t id, uint32_t index);
-int grdrm_crtc_new(grdrm_crtc **out, grdrm_card *card, uint32_t id, uint32_t index);
-
-#define plane_from_object(_obj) container_of((_obj), grdrm_plane, object)
-#define connector_from_object(_obj) container_of((_obj), grdrm_connector, object)
-#define encoder_from_object(_obj) container_of((_obj), grdrm_encoder, object)
-#define crtc_from_object(_obj) container_of((_obj), grdrm_crtc, object)
-
-/*
- * Framebuffers
- */
-
-struct grdrm_fb {
- grdev_fb base;
- grdrm_card *card;
- uint32_t id;
- uint32_t handles[4];
- uint32_t offsets[4];
- uint32_t sizes[4];
- uint32_t flipid;
-};
-
-static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_modeinfo *mode);
-grdrm_fb *grdrm_fb_free(grdrm_fb *fb);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdrm_fb*, grdrm_fb_free);
-
-#define fb_from_base(_fb) container_of((_fb), grdrm_fb, base)
-
-/*
- * Pipes
- */
-
-struct grdrm_pipe {
- grdev_pipe base;
- grdrm_crtc *crtc;
- uint32_t counter;
-};
-
-#define grdrm_pipe_from_base(_e) container_of((_e), grdrm_pipe, base)
-
-#define GRDRM_PIPE_NAME_MAX (GRDRM_CARD_NAME_MAX + 1 + DECIMAL_STR_MAX(uint32_t))
-
-static const grdev_pipe_vtable grdrm_pipe_vtable;
-
-static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_modeinfo *mode, size_t n_fbs);
-
-/*
- * Cards
- */
-
-struct grdrm_card {
- grdev_card base;
-
- int fd;
- sd_event_source *fd_src;
-
- uint32_t n_crtcs;
- uint32_t n_encoders;
- uint32_t n_connectors;
- uint32_t n_planes;
- uint32_t max_ids;
- Hashmap *object_map;
-
- bool async_hotplug : 1;
- bool hotplug : 1;
- bool running : 1;
- bool ready : 1;
- bool cap_dumb : 1;
- bool cap_monotonic : 1;
-};
-
-struct unmanaged_card {
- grdrm_card card;
- char *devnode;
-};
-
-struct managed_card {
- grdrm_card card;
- dev_t devnum;
-
- sd_bus_slot *slot_pause_device;
- sd_bus_slot *slot_resume_device;
- sd_bus_slot *slot_take_device;
-
- bool requested : 1; /* TakeDevice() was sent */
- bool acquired : 1; /* TakeDevice() was successful */
- bool master : 1; /* we are DRM-Master */
-};
-
-#define grdrm_card_from_base(_e) container_of((_e), grdrm_card, base)
-#define unmanaged_card_from_base(_e) \
- container_of(grdrm_card_from_base(_e), unmanaged_card, card)
-#define managed_card_from_base(_e) \
- container_of(grdrm_card_from_base(_e), managed_card, card)
-
-#define GRDRM_CARD_INIT(_vtable, _session) ((grdrm_card){ \
- .base = GRDEV_CARD_INIT((_vtable), (_session)), \
- .fd = -1, \
- .max_ids = 32, \
- })
-
-#define GRDRM_CARD_NAME_MAX (6 + DECIMAL_STR_MAX(unsigned) * 2)
-
-static const grdev_card_vtable unmanaged_card_vtable;
-static const grdev_card_vtable managed_card_vtable;
-
-static int grdrm_card_open(grdrm_card *card, int dev_fd);
-static void grdrm_card_close(grdrm_card *card);
-static bool grdrm_card_async(grdrm_card *card, int r);
-
-/*
- * The page-flip event of the kernel provides 64bit of arbitrary user-data. As
- * drivers tend to drop events on intermediate deep mode-sets or because we
- * might receive events during session activation, we try to avoid allocaing
- * dynamic data on those events. Instead, we safe the CRTC id plus a 32bit
- * counter in there. This way, we only get 32bit counters, not 64bit, but that
- * should be more than enough. On the bright side, we no longer care whether we
- * lose events. No memory leaks will occur.
- * Modern DRM drivers might be fixed to no longer leak events, but we want to
- * be safe. And associating dynamically allocated data with those events is
- * kinda ugly, anyway.
- */
-
-static uint64_t grdrm_encode_vblank_data(uint32_t id, uint32_t counter) {
- return id | ((uint64_t)counter << 32);
-}
-
-static void grdrm_decode_vblank_data(uint64_t data, uint32_t *out_id, uint32_t *out_counter) {
- if (out_id)
- *out_id = data & 0xffffffffU;
- if (out_counter)
- *out_counter = (data >> 32) & 0xffffffffU;
-}
-
-static bool grdrm_modes_compatible(const struct drm_mode_modeinfo *a, const struct drm_mode_modeinfo *b) {
- assert(a);
- assert(b);
-
- /* Test whether both modes are compatible according to our internal
- * assumptions on modes. This comparison is highly dependent on how
- * we treat modes in grdrm. If we export mode details, we need to
- * make this comparison much stricter. */
-
- if (a->hdisplay != b->hdisplay)
- return false;
- if (a->vdisplay != b->vdisplay)
- return false;
- if (a->vrefresh != b->vrefresh)
- return false;
-
- return true;
-}
-
-/*
- * Objects
- */
-
-grdrm_object *grdrm_find_object(grdrm_card *card, uint32_t id) {
- assert_return(card, NULL);
-
- return id > 0 ? hashmap_get(card->object_map, UINT32_TO_PTR(id)) : NULL;
-}
-
-int grdrm_object_add(grdrm_object *object) {
- int r;
-
- assert(object);
- assert(object->card);
- assert(object->id > 0);
- assert(IN_SET(object->type, GRDRM_TYPE_CRTC, GRDRM_TYPE_ENCODER, GRDRM_TYPE_CONNECTOR, GRDRM_TYPE_PLANE));
- assert(object->free_fn);
-
- if (object->index >= 32)
- log_debug("grdrm: %s: object index exceeds 32bit masks: type=%u, index=%" PRIu32,
- object->card->base.name, object->type, object->index);
-
- r = hashmap_put(object->card->object_map, UINT32_TO_PTR(object->id), object);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-grdrm_object *grdrm_object_free(grdrm_object *object) {
- if (!object)
- return NULL;
-
- assert(object->card);
- assert(object->id > 0);
- assert(IN_SET(object->type, GRDRM_TYPE_CRTC, GRDRM_TYPE_ENCODER, GRDRM_TYPE_CONNECTOR, GRDRM_TYPE_PLANE));
- assert(object->free_fn);
-
- hashmap_remove_value(object->card->object_map, UINT32_TO_PTR(object->id), object);
-
- object->free_fn(object);
- return NULL;
-}
-
-/*
- * Planes
- */
-
-static void plane_free(grdrm_object *object) {
- grdrm_plane *plane = plane_from_object(object);
-
- free(plane->kern.formats);
- free(plane->kern.crtcs);
- free(plane);
-}
-
-int grdrm_plane_new(grdrm_plane **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_plane *plane;
- int r;
-
- assert(card);
-
- plane = new0(grdrm_plane, 1);
- if (!plane)
- return -ENOMEM;
-
- object = &plane->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_PLANE, plane_free);
-
- plane->kern.max_crtcs = 32;
- plane->kern.crtcs = new0(uint32_t, plane->kern.max_crtcs);
- if (!plane->kern.crtcs)
- return -ENOMEM;
-
- plane->kern.max_formats = 32;
- plane->kern.formats = new0(uint32_t, plane->kern.max_formats);
- if (!plane->kern.formats)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = plane;
- object = NULL;
- return 0;
-}
-
-static int grdrm_plane_resync(grdrm_plane *plane) {
- grdrm_card *card = plane->object.card;
- size_t tries;
- int r;
-
- assert(plane);
-
- for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
- struct drm_mode_get_plane res;
- grdrm_object *object;
- bool resized = false;
- Iterator iter;
-
- zero(res);
- res.plane_id = plane->object.id;
- res.format_type_ptr = PTR_TO_UINT64(plane->kern.formats);
- res.count_format_types = plane->kern.max_formats;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANE, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: plane %u removed during resync",
- card->base.name, plane->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve plane %u: %m",
- card->base.name, plane->object.id);
- }
-
- return r;
- }
-
- plane->kern.n_crtcs = 0;
- memzero(plane->kern.crtcs, sizeof(uint32_t) * plane->kern.max_crtcs);
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CRTC || object->index >= 32)
- continue;
- if (!(res.possible_crtcs & (1 << object->index)))
- continue;
- if (plane->kern.n_crtcs >= 32) {
- log_debug("grdrm: %s: possible_crtcs of plane %" PRIu32 " exceeds 32bit mask",
- card->base.name, plane->object.id);
- continue;
- }
-
- plane->kern.crtcs[plane->kern.n_crtcs++] = object->id;
- }
-
- if (res.count_format_types > plane->kern.max_formats) {
- uint32_t max, *t;
-
- max = ALIGN_POWER2(res.count_format_types);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive plane resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- t = realloc(plane->kern.formats, sizeof(*t) * max);
- if (!t)
- return -ENOMEM;
-
- plane->kern.formats = t;
- plane->kern.max_formats = max;
- resized = true;
- }
-
- if (resized)
- continue;
-
- plane->kern.n_formats = res.count_format_types;
- plane->kern.used_crtc = res.crtc_id;
- plane->kern.used_fb = res.fb_id;
- plane->kern.gamma_size = res.gamma_size;
-
- break;
- }
-
- if (tries >= GRDRM_MAX_TRIES) {
- log_debug("grdrm: %s: plane %u not settled for retrieval", card->base.name, plane->object.id);
- return -EFAULT;
- }
-
- return 0;
-}
-
-/*
- * Connectors
- */
-
-static void connector_free(grdrm_object *object) {
- grdrm_connector *connector = connector_from_object(object);
-
- free(connector->kern.prop_values);
- free(connector->kern.prop_ids);
- free(connector->kern.modes);
- free(connector->kern.encoders);
- free(connector);
-}
-
-int grdrm_connector_new(grdrm_connector **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_connector *connector;
- int r;
-
- assert(card);
-
- connector = new0(grdrm_connector, 1);
- if (!connector)
- return -ENOMEM;
-
- object = &connector->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_CONNECTOR, connector_free);
-
- connector->kern.max_encoders = 32;
- connector->kern.encoders = new0(uint32_t, connector->kern.max_encoders);
- if (!connector->kern.encoders)
- return -ENOMEM;
-
- connector->kern.max_modes = 32;
- connector->kern.modes = new0(struct drm_mode_modeinfo, connector->kern.max_modes);
- if (!connector->kern.modes)
- return -ENOMEM;
-
- connector->kern.max_props = 32;
- connector->kern.prop_ids = new0(uint32_t, connector->kern.max_props);
- connector->kern.prop_values = new0(uint64_t, connector->kern.max_props);
- if (!connector->kern.prop_ids || !connector->kern.prop_values)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = connector;
- object = NULL;
- return 0;
-}
-
-static int grdrm_connector_resync(grdrm_connector *connector) {
- grdrm_card *card = connector->object.card;
- size_t tries;
- int r;
-
- assert(connector);
-
- for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
- struct drm_mode_get_connector res;
- bool resized = false;
- uint32_t max;
-
- zero(res);
- res.connector_id = connector->object.id;
- res.encoders_ptr = PTR_TO_UINT64(connector->kern.encoders);
- res.props_ptr = PTR_TO_UINT64(connector->kern.prop_ids);
- res.prop_values_ptr = PTR_TO_UINT64(connector->kern.prop_values);
- res.count_encoders = connector->kern.max_encoders;
- res.count_props = connector->kern.max_props;
-
- /* The kernel reads modes from the EDID information only if we
- * pass count_modes==0. This is a legacy hack for libdrm (which
- * called every ioctl twice). Now we have to adopt.. *sigh*.
- * If we never received an hotplug event, there's no reason to
- * sync modes. EDID reads are heavy, so skip that if not
- * required. */
- if (card->hotplug) {
- if (tries > 0) {
- res.modes_ptr = PTR_TO_UINT64(connector->kern.modes);
- res.count_modes = connector->kern.max_modes;
- } else {
- resized = true;
- }
- }
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETCONNECTOR, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: connector %u removed during resync",
- card->base.name, connector->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve connector %u: %m",
- card->base.name, connector->object.id);
- }
-
- return r;
- }
-
- if (res.count_encoders > connector->kern.max_encoders) {
- uint32_t *t;
-
- max = ALIGN_POWER2(res.count_encoders);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- t = realloc(connector->kern.encoders, sizeof(*t) * max);
- if (!t)
- return -ENOMEM;
-
- connector->kern.encoders = t;
- connector->kern.max_encoders = max;
- resized = true;
- }
-
- if (res.count_modes > connector->kern.max_modes) {
- struct drm_mode_modeinfo *t;
-
- max = ALIGN_POWER2(res.count_modes);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- t = realloc(connector->kern.modes, sizeof(*t) * max);
- if (!t)
- return -ENOMEM;
-
- connector->kern.modes = t;
- connector->kern.max_modes = max;
- resized = true;
- }
-
- if (res.count_props > connector->kern.max_props) {
- uint32_t *tids;
- uint64_t *tvals;
-
- max = ALIGN_POWER2(res.count_props);
- if (!max || max > UINT16_MAX) {
- log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
- return -ERANGE;
- }
-
- tids = realloc(connector->kern.prop_ids, sizeof(*tids) * max);
- if (!tids)
- return -ENOMEM;
- connector->kern.prop_ids = tids;
-
- tvals = realloc(connector->kern.prop_values, sizeof(*tvals) * max);
- if (!tvals)
- return -ENOMEM;
- connector->kern.prop_values = tvals;
-
- connector->kern.max_props = max;
- resized = true;
- }
-
- if (resized)
- continue;
-
- connector->kern.n_encoders = res.count_encoders;
- connector->kern.n_props = res.count_props;
- connector->kern.type = res.connector_type;
- connector->kern.type_id = res.connector_type_id;
- connector->kern.used_encoder = res.encoder_id;
- connector->kern.connection = res.connection;
- connector->kern.mm_width = res.mm_width;
- connector->kern.mm_height = res.mm_height;
- connector->kern.subpixel = res.subpixel;
- if (res.modes_ptr == PTR_TO_UINT64(connector->kern.modes))
- connector->kern.n_modes = res.count_modes;
-
- break;
- }
-
- if (tries >= GRDRM_MAX_TRIES) {
- log_debug("grdrm: %s: connector %u not settled for retrieval", card->base.name, connector->object.id);
- return -EFAULT;
- }
-
- return 0;
-}
-
-/*
- * Encoders
- */
-
-static void encoder_free(grdrm_object *object) {
- grdrm_encoder *encoder = encoder_from_object(object);
-
- free(encoder->kern.clones);
- free(encoder->kern.crtcs);
- free(encoder);
-}
-
-int grdrm_encoder_new(grdrm_encoder **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_encoder *encoder;
- int r;
-
- assert(card);
-
- encoder = new0(grdrm_encoder, 1);
- if (!encoder)
- return -ENOMEM;
-
- object = &encoder->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_ENCODER, encoder_free);
-
- encoder->kern.max_crtcs = 32;
- encoder->kern.crtcs = new0(uint32_t, encoder->kern.max_crtcs);
- if (!encoder->kern.crtcs)
- return -ENOMEM;
-
- encoder->kern.max_clones = 32;
- encoder->kern.clones = new0(uint32_t, encoder->kern.max_clones);
- if (!encoder->kern.clones)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = encoder;
- object = NULL;
- return 0;
-}
-
-static int grdrm_encoder_resync(grdrm_encoder *encoder) {
- grdrm_card *card = encoder->object.card;
- struct drm_mode_get_encoder res;
- grdrm_object *object;
- Iterator iter;
- int r;
-
- assert(encoder);
-
- zero(res);
- res.encoder_id = encoder->object.id;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETENCODER, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: encoder %u removed during resync",
- card->base.name, encoder->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve encoder %u: %m",
- card->base.name, encoder->object.id);
- }
-
- return r;
- }
-
- encoder->kern.type = res.encoder_type;
- encoder->kern.used_crtc = res.crtc_id;
-
- encoder->kern.n_crtcs = 0;
- memzero(encoder->kern.crtcs, sizeof(uint32_t) * encoder->kern.max_crtcs);
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CRTC || object->index >= 32)
- continue;
- if (!(res.possible_crtcs & (1 << object->index)))
- continue;
- if (encoder->kern.n_crtcs >= 32) {
- log_debug("grdrm: %s: possible_crtcs exceeds 32bit mask", card->base.name);
- continue;
- }
-
- encoder->kern.crtcs[encoder->kern.n_crtcs++] = object->id;
- }
-
- encoder->kern.n_clones = 0;
- memzero(encoder->kern.clones, sizeof(uint32_t) * encoder->kern.max_clones);
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_ENCODER || object->index >= 32)
- continue;
- if (!(res.possible_clones & (1 << object->index)))
- continue;
- if (encoder->kern.n_clones >= 32) {
- log_debug("grdrm: %s: possible_encoders exceeds 32bit mask", card->base.name);
- continue;
- }
-
- encoder->kern.clones[encoder->kern.n_clones++] = object->id;
- }
-
- return 0;
-}
-
-/*
- * Crtcs
- */
-
-static void crtc_free(grdrm_object *object) {
- grdrm_crtc *crtc = crtc_from_object(object);
-
- if (crtc->pipe)
- grdev_pipe_free(&crtc->pipe->base);
- free(crtc->set.connectors);
- free(crtc->old.connectors);
- free(crtc->kern.used_connectors);
- free(crtc);
-}
-
-int grdrm_crtc_new(grdrm_crtc **out, grdrm_card *card, uint32_t id, uint32_t index) {
- _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
- grdrm_crtc *crtc;
- int r;
-
- assert(card);
-
- crtc = new0(grdrm_crtc, 1);
- if (!crtc)
- return -ENOMEM;
-
- object = &crtc->object;
- *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_CRTC, crtc_free);
-
- crtc->kern.max_used_connectors = 32;
- crtc->kern.used_connectors = new0(uint32_t, crtc->kern.max_used_connectors);
- if (!crtc->kern.used_connectors)
- return -ENOMEM;
-
- crtc->old.connectors = new0(uint32_t, crtc->kern.max_used_connectors);
- if (!crtc->old.connectors)
- return -ENOMEM;
-
- r = grdrm_object_add(object);
- if (r < 0)
- return r;
-
- if (out)
- *out = crtc;
- object = NULL;
- return 0;
-}
-
-static int grdrm_crtc_resync(grdrm_crtc *crtc) {
- grdrm_card *card = crtc->object.card;
- struct drm_mode_crtc res = { .crtc_id = crtc->object.id };
- int r;
-
- assert(crtc);
-
- /* make sure we can cache any combination later */
- if (card->n_connectors > crtc->kern.max_used_connectors) {
- uint32_t max, *t;
-
- max = ALIGN_POWER2(card->n_connectors);
- if (!max)
- return -ENOMEM;
-
- t = realloc_multiply(crtc->kern.used_connectors, sizeof(*t), max);
- if (!t)
- return -ENOMEM;
-
- crtc->kern.used_connectors = t;
- crtc->kern.max_used_connectors = max;
-
- if (!crtc->old.set) {
- crtc->old.connectors = calloc(sizeof(*t), max);
- if (!crtc->old.connectors)
- return -ENOMEM;
- }
- }
-
- /* GETCRTC doesn't return connectors. We have to read all
- * encoder-state and deduce the setup ourselves.. */
- crtc->kern.n_used_connectors = 0;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETCRTC, &res);
- if (r < 0) {
- r = -errno;
- if (r == -ENOENT) {
- card->async_hotplug = true;
- r = 0;
- log_debug("grdrm: %s: crtc %u removed during resync",
- card->base.name, crtc->object.id);
- } else {
- log_debug_errno(errno, "grdrm: %s: cannot retrieve crtc %u: %m",
- card->base.name, crtc->object.id);
- }
-
- return r;
- }
-
- crtc->kern.used_fb = res.fb_id;
- crtc->kern.fb_offset_x = res.x;
- crtc->kern.fb_offset_y = res.y;
- crtc->kern.gamma_size = res.gamma_size;
- crtc->kern.mode_set = res.mode_valid;
- crtc->kern.mode = res.mode;
-
- return 0;
-}
-
-static void grdrm_crtc_assign(grdrm_crtc *crtc, grdrm_connector *connector) {
- uint32_t n_connectors;
- int r;
-
- assert(crtc);
- assert(!crtc->object.assigned);
- assert(!connector || !connector->object.assigned);
-
- /* always mark both as assigned; even if assignments cannot be set */
- crtc->object.assigned = true;
- if (connector)
- connector->object.assigned = true;
-
- /* we will support hw clone mode in the future */
- n_connectors = connector ? 1 : 0;
-
- /* bail out if configuration is preserved */
- if (crtc->set.n_connectors == n_connectors &&
- (n_connectors == 0 || crtc->set.connectors[0] == connector->object.id))
- return;
-
- crtc->applied = false;
- crtc->set.n_connectors = 0;
-
- if (n_connectors > crtc->set.max_connectors) {
- uint32_t max, *t;
-
- max = ALIGN_POWER2(n_connectors);
- if (!max) {
- r = -ENOMEM;
- goto error;
- }
-
- t = realloc(crtc->set.connectors, sizeof(*t) * max);
- if (!t) {
- r = -ENOMEM;
- goto error;
- }
-
- crtc->set.connectors = t;
- crtc->set.max_connectors = max;
- }
-
- if (connector) {
- struct drm_mode_modeinfo *m, *pref = NULL;
- uint32_t i;
-
- for (i = 0; i < connector->kern.n_modes; ++i) {
- m = &connector->kern.modes[i];
-
- /* ignore 3D modes by default */
- if (m->flags & DRM_MODE_FLAG_3D_MASK)
- continue;
-
- if (!pref) {
- pref = m;
- continue;
- }
-
- /* use PREFERRED over non-PREFERRED */
- if ((pref->type & DRM_MODE_TYPE_PREFERRED) &&
- !(m->type & DRM_MODE_TYPE_PREFERRED))
- continue;
-
- /* use DRIVER over non-PREFERRED|DRIVER */
- if ((pref->type & DRM_MODE_TYPE_DRIVER) &&
- !(m->type & (DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED)))
- continue;
-
- /* always prefer higher resolution */
- if (pref->hdisplay > m->hdisplay ||
- (pref->hdisplay == m->hdisplay && pref->vdisplay > m->vdisplay))
- continue;
-
- pref = m;
- }
-
- if (pref) {
- crtc->set.mode = *pref;
- crtc->set.n_connectors = 1;
- crtc->set.connectors[0] = connector->object.id;
- log_debug("grdrm: %s: assigned connector %" PRIu32 " to crtc %" PRIu32 " with mode %s",
- crtc->object.card->base.name, connector->object.id, crtc->object.id, pref->name);
- } else {
- log_debug("grdrm: %s: connector %" PRIu32 " to be assigned but has no valid mode",
- crtc->object.card->base.name, connector->object.id);
- }
- }
-
- return;
-
-error:
- log_debug("grdrm: %s: cannot assign crtc %" PRIu32 ": %s",
- crtc->object.card->base.name, crtc->object.id, strerror(-r));
-}
-
-static void grdrm_crtc_expose(grdrm_crtc *crtc) {
- grdrm_pipe *pipe;
- grdrm_fb *fb;
- size_t i;
- int r;
-
- assert(crtc);
- assert(crtc->object.assigned);
-
- if (crtc->set.n_connectors < 1) {
- if (crtc->pipe)
- grdev_pipe_free(&crtc->pipe->base);
- crtc->pipe = NULL;
- return;
- }
-
- pipe = crtc->pipe;
- if (pipe) {
- if (pipe->base.width != crtc->set.mode.hdisplay ||
- pipe->base.height != crtc->set.mode.vdisplay ||
- pipe->base.vrefresh != crtc->set.mode.vrefresh) {
- grdev_pipe_free(&pipe->base);
- crtc->pipe = NULL;
- pipe = NULL;
- }
- }
-
- if (crtc->pipe) {
- pipe->base.front = NULL;
- pipe->base.back = NULL;
- for (i = 0; i < pipe->base.max_fbs; ++i) {
- fb = fb_from_base(pipe->base.fbs[i]);
- if (fb->id == crtc->kern.used_fb)
- pipe->base.front = &fb->base;
- else if (!fb->flipid)
- pipe->base.back = &fb->base;
- }
- } else {
- r = grdrm_pipe_new(&pipe, crtc, &crtc->set.mode, 2);
- if (r < 0) {
- log_debug("grdrm: %s: cannot create pipe for crtc %" PRIu32 ": %s",
- crtc->object.card->base.name, crtc->object.id, strerror(-r));
- return;
- }
-
- for (i = 0; i < pipe->base.max_fbs; ++i) {
- r = grdrm_fb_new(&fb, crtc->object.card, &crtc->set.mode);
- if (r < 0) {
- log_debug("grdrm: %s: cannot allocate framebuffer for crtc %" PRIu32 ": %s",
- crtc->object.card->base.name, crtc->object.id, strerror(-r));
- grdev_pipe_free(&pipe->base);
- return;
- }
-
- pipe->base.fbs[i] = &fb->base;
- }
-
- pipe->base.front = NULL;
- pipe->base.back = pipe->base.fbs[0];
- crtc->pipe = pipe;
- }
-
- grdev_pipe_ready(&crtc->pipe->base, true);
-}
-
-static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb *basefb) {
- struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- grdrm_pipe *pipe = crtc->pipe;
- grdrm_fb *fb;
- int r;
-
- assert(crtc);
- assert(basefb);
- assert(pipe);
-
- fb = fb_from_base(basefb);
-
- set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->set.connectors);
- set_crtc.count_connectors = crtc->set.n_connectors;
- set_crtc.fb_id = fb->id;
- set_crtc.x = 0;
- set_crtc.y = 0;
- set_crtc.mode_valid = 1;
- set_crtc.mode = crtc->set.mode;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot set crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- grdrm_card_async(card, r);
- return;
- }
-
- if (!crtc->applied) {
- log_debug("grdrm: %s: crtc %" PRIu32 " applied via deep modeset",
- card->base.name, crtc->object.id);
- crtc->applied = true;
- }
-
- pipe->base.back = NULL;
- pipe->base.front = &fb->base;
- fb->flipid = 0;
- ++pipe->counter;
- pipe->base.flipping = false;
- pipe->base.flip = false;
-
- /* We cannot schedule dummy page-flips on pipes, hence, the
- * application would have to schedule their own frame-timers.
- * To avoid duplicating that everywhere, we schedule our own
- * timer and raise a fake FRAME event when it fires. */
- grdev_pipe_schedule(&pipe->base, 1);
-}
-
-static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb *basefb) {
- struct drm_mode_crtc_page_flip page_flip = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- grdrm_pipe *pipe = crtc->pipe;
- grdrm_fb *fb;
- uint32_t cnt;
- int r;
-
- assert(crtc);
- assert(basefb);
- assert(pipe);
-
- if (!crtc->applied) {
- if (!grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode))
- return 0;
-
- /* TODO: Theoretically, we should be able to page-flip to our
- * framebuffer here. We didn't perform any deep modeset, but the
- * DRM driver is really supposed to reject our page-flip in case
- * the FB is not compatible. We then properly fall back to a
- * deep modeset.
- * As it turns out, drivers don't to this. Therefore, we need to
- * perform a full modeset on enter now. We might avoid this in
- * the future with fixed drivers.. */
-
- return 0;
- }
-
- fb = fb_from_base(basefb);
-
- cnt = ++pipe->counter ? : ++pipe->counter;
- page_flip.fb_id = fb->id;
- page_flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
- page_flip.user_data = grdrm_encode_vblank_data(crtc->object.id, cnt);
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_PAGE_FLIP, &page_flip);
- if (r < 0) {
- r = -errno;
- /* Avoid excessive logging on EINVAL; it is currently not
- * possible to see whether cards support page-flipping, so
- * avoid logging on each frame. */
- if (r != -EINVAL)
- log_debug_errno(errno, "grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- if (grdrm_card_async(card, r))
- return r;
-
- return 0;
- }
-
- if (!crtc->applied) {
- log_debug("grdrm: %s: crtc %" PRIu32 " applied via page flip",
- card->base.name, crtc->object.id);
- crtc->applied = true;
- }
-
- pipe->base.flipping = true;
- pipe->base.flip = false;
- pipe->counter = cnt;
- fb->flipid = cnt;
- pipe->base.back = NULL;
-
- /* Raise fake FRAME event if it takes longer than 2
- * frames to receive the pageflip event. We assume the
- * queue ran over or some other error happened. */
- grdev_pipe_schedule(&pipe->base, 2);
-
- return 1;
-}
-
-static void grdrm_crtc_commit(grdrm_crtc *crtc) {
- struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- grdrm_pipe *pipe;
- grdev_fb *fb;
- int r;
-
- assert(crtc);
- assert(crtc->object.assigned);
-
- pipe = crtc->pipe;
- if (!pipe) {
- /* If a crtc is not assigned any connector, we want any
- * previous setup to be cleared, so make sure the CRTC is
- * disabled. Otherwise, there might be content on the CRTC
- * while we run, which is not what we want.
- * If you want to avoid modesets on specific CRTCs, you should
- * still keep their assignment, but never enable the resulting
- * pipe. This way, we wouldn't touch it at all. */
- if (!crtc->applied) {
- crtc->applied = true;
- r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot shutdown crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- grdrm_card_async(card, r);
- return;
- }
-
- log_debug("grdrm: %s: crtc %" PRIu32 " applied via shutdown",
- card->base.name, crtc->object.id);
- }
-
- return;
- }
-
- /* we always fully ignore disabled pipes */
- if (!pipe->base.enabled)
- return;
-
- assert(crtc->set.n_connectors > 0);
-
- if (pipe->base.flip)
- fb = pipe->base.back;
- else if (!crtc->applied)
- fb = pipe->base.front;
- else
- return;
-
- if (!fb)
- return;
-
- r = grdrm_crtc_commit_flip(crtc, fb);
- if (r == 0) {
- /* in case we couldn't page-flip, perform deep modeset */
- grdrm_crtc_commit_deep(crtc, fb);
- }
-}
-
-static void grdrm_crtc_restore(grdrm_crtc *crtc) {
- struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
- grdrm_card *card = crtc->object.card;
- int r;
-
- if (!crtc->old.set)
- return;
-
- set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->old.connectors);
- set_crtc.count_connectors = crtc->old.n_connectors;
- set_crtc.fb_id = crtc->old.fb;
- set_crtc.x = crtc->old.fb_x;
- set_crtc.y = crtc->old.fb_y;
- set_crtc.gamma_size = crtc->old.gamma;
- set_crtc.mode_valid = crtc->old.mode_set;
- set_crtc.mode = crtc->old.mode;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot restore crtc %" PRIu32 ": %m",
- card->base.name, crtc->object.id);
-
- grdrm_card_async(card, r);
- return;
- }
-
- if (crtc->pipe) {
- ++crtc->pipe->counter;
- crtc->pipe->base.front = NULL;
- crtc->pipe->base.flipping = false;
- }
-
- log_debug("grdrm: %s: crtc %" PRIu32 " restored", card->base.name, crtc->object.id);
-}
-
-static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct drm_event_vblank *event) {
- bool flipped = false;
- grdrm_pipe *pipe;
- size_t i;
-
- assert(crtc);
- assert(event);
-
- pipe = crtc->pipe;
- if (!pipe)
- return;
-
- /* We got a page-flip event. To be safe, we reset all FBs on the same
- * pipe that have smaller flipids than the flip we got as we know they
- * are executed in order. We need to do this to guarantee
- * queue-overflows or other missed events don't cause starvation.
- * Furthermore, if we find the exact FB this event is for, *and* this
- * is the most recent event, we mark it as front FB and raise a
- * frame event. */
-
- for (i = 0; i < pipe->base.max_fbs; ++i) {
- grdrm_fb *fb;
-
- if (!pipe->base.fbs[i])
- continue;
-
- fb = fb_from_base(pipe->base.fbs[i]);
- if (counter != 0 && counter == pipe->counter && fb->flipid == counter) {
- pipe->base.front = &fb->base;
- fb->flipid = 0;
- flipped = true;
- } else if (counter - fb->flipid < UINT16_MAX) {
- fb->flipid = 0;
- }
- }
-
- if (flipped) {
- crtc->pipe->base.flipping = false;
- grdev_pipe_frame(&pipe->base);
- }
-}
-
-/*
- * Framebuffers
- */
-
-static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_modeinfo *mode) {
- _cleanup_(grdrm_fb_freep) grdrm_fb *fb = NULL;
- struct drm_mode_create_dumb create_dumb = { };
- struct drm_mode_map_dumb map_dumb = { };
- struct drm_mode_fb_cmd2 add_fb = { };
- unsigned int i;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(card, -EINVAL);
-
- fb = new0(grdrm_fb, 1);
- if (!fb)
- return -ENOMEM;
-
- /* TODO: we should choose a compatible format of the previous CRTC
- * setting to allow page-flip to it. Only choose fallback if the
- * previous setting was crap (non xrgb32'ish). */
-
- fb->card = card;
- fb->base.format = DRM_FORMAT_XRGB8888;
- fb->base.width = mode->hdisplay;
- fb->base.height = mode->vdisplay;
-
- for (i = 0; i < ELEMENTSOF(fb->base.maps); ++i)
- fb->base.maps[i] = MAP_FAILED;
-
- create_dumb.width = fb->base.width;
- create_dumb.height = fb->base.height;
- create_dumb.bpp = 32;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
- if (r < 0) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot create dumb buffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- fb->handles[0] = create_dumb.handle;
- fb->base.strides[0] = create_dumb.pitch;
- fb->sizes[0] = create_dumb.size;
-
- map_dumb.handle = fb->handles[0];
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
- if (r < 0) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot map dumb buffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- fb->base.maps[0] = mmap(0, fb->sizes[0], PROT_WRITE, MAP_SHARED, card->fd, map_dumb.offset);
- if (fb->base.maps[0] == MAP_FAILED) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot memory-map dumb buffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- memzero(fb->base.maps[0], fb->sizes[0]);
-
- add_fb.width = fb->base.width;
- add_fb.height = fb->base.height;
- add_fb.pixel_format = fb->base.format;
- add_fb.flags = 0;
- memcpy(add_fb.handles, fb->handles, sizeof(fb->handles));
- memcpy(add_fb.pitches, fb->base.strides, sizeof(fb->base.strides));
- memcpy(add_fb.offsets, fb->offsets, sizeof(fb->offsets));
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_ADDFB2, &add_fb);
- if (r < 0) {
- r = negative_errno();
- log_debug_errno(errno, "grdrm: %s: cannot add framebuffer %" PRIu32 "x%" PRIu32": %m",
- card->base.name, fb->base.width, fb->base.height);
- return r;
- }
-
- fb->id = add_fb.fb_id;
-
- *out = fb;
- fb = NULL;
- return 0;
-}
-
-grdrm_fb *grdrm_fb_free(grdrm_fb *fb) {
- unsigned int i;
- int r;
-
- if (!fb)
- return NULL;
-
- assert(fb->card);
-
- if (fb->base.free_fn)
- fb->base.free_fn(fb->base.data.ptr);
-
- if (fb->id > 0 && fb->card->fd >= 0) {
- r = ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id);
- if (r < 0)
- log_debug_errno(errno, "grdrm: %s: cannot delete framebuffer %" PRIu32 ": %m",
- fb->card->base.name, fb->id);
- }
-
- for (i = 0; i < ELEMENTSOF(fb->handles); ++i) {
- struct drm_mode_destroy_dumb destroy_dumb = { };
-
- if (fb->base.maps[i] != MAP_FAILED)
- munmap(fb->base.maps[i], fb->sizes[i]);
-
- if (fb->handles[i] > 0 && fb->card->fd >= 0) {
- destroy_dumb.handle = fb->handles[i];
- r = ioctl(fb->card->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
- if (r < 0)
- log_debug_errno(errno, "grdrm: %s: cannot destroy dumb-buffer %" PRIu32 ": %m",
- fb->card->base.name, fb->handles[i]);
- }
- }
-
- free(fb);
-
- return NULL;
-}
-
-/*
- * Pipes
- */
-
-static void grdrm_pipe_name(char *out, grdrm_crtc *crtc) {
- /* @out must be at least of size GRDRM_PIPE_NAME_MAX */
- sprintf(out, "%s/%" PRIu32, crtc->object.card->base.name, crtc->object.id);
-}
-
-static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_modeinfo *mode, size_t n_fbs) {
- _cleanup_(grdev_pipe_freep) grdev_pipe *basepipe = NULL;
- grdrm_card *card = crtc->object.card;
- char name[GRDRM_PIPE_NAME_MAX];
- grdrm_pipe *pipe;
- int r;
-
- assert_return(crtc, -EINVAL);
- assert_return(grdev_is_drm_card(&card->base), -EINVAL);
-
- pipe = new0(grdrm_pipe, 1);
- if (!pipe)
- return -ENOMEM;
-
- basepipe = &pipe->base;
- pipe->base = GRDEV_PIPE_INIT(&grdrm_pipe_vtable, &card->base);
- pipe->crtc = crtc;
- pipe->base.width = mode->hdisplay;
- pipe->base.height = mode->vdisplay;
- pipe->base.vrefresh = mode->vrefresh ? : 25;
-
- grdrm_pipe_name(name, crtc);
- r = grdev_pipe_add(&pipe->base, name, n_fbs);
- if (r < 0)
- return r;
-
- if (out)
- *out = pipe;
- basepipe = NULL;
- return 0;
-}
-
-static void grdrm_pipe_free(grdev_pipe *basepipe) {
- grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
- size_t i;
-
- assert(pipe->crtc);
-
- for (i = 0; i < pipe->base.max_fbs; ++i)
- if (pipe->base.fbs[i])
- grdrm_fb_free(fb_from_base(pipe->base.fbs[i]));
-
- free(pipe);
-}
-
-static grdev_fb *grdrm_pipe_target(grdev_pipe *basepipe) {
- grdrm_fb *fb;
- size_t i;
-
- if (!basepipe->back) {
- for (i = 0; i < basepipe->max_fbs; ++i) {
- if (!basepipe->fbs[i])
- continue;
-
- fb = fb_from_base(basepipe->fbs[i]);
- if (&fb->base == basepipe->front)
- continue;
- if (basepipe->flipping && fb->flipid)
- continue;
-
- basepipe->back = &fb->base;
- break;
- }
- }
-
- return basepipe->back;
-}
-
-static void grdrm_pipe_enable(grdev_pipe *basepipe) {
- grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
-
- pipe->crtc->applied = false;
-}
-
-static void grdrm_pipe_disable(grdev_pipe *basepipe) {
- grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
-
- pipe->crtc->applied = false;
-}
-
-static const grdev_pipe_vtable grdrm_pipe_vtable = {
- .free = grdrm_pipe_free,
- .target = grdrm_pipe_target,
- .enable = grdrm_pipe_enable,
- .disable = grdrm_pipe_disable,
-};
-
-/*
- * Cards
- */
-
-static void grdrm_name(char *out, dev_t devnum) {
- /* @out must be at least of size GRDRM_CARD_NAME_MAX */
- sprintf(out, "drm/%u:%u", major(devnum), minor(devnum));
-}
-
-static void grdrm_card_print(grdrm_card *card) {
- grdrm_object *object;
- grdrm_crtc *crtc;
- grdrm_encoder *encoder;
- grdrm_connector *connector;
- grdrm_plane *plane;
- Iterator iter;
- uint32_t i;
- char *p, *buf;
-
- log_debug("grdrm: %s: state dump", card->base.name);
-
- log_debug(" crtcs:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
-
- if (crtc->kern.mode_set)
- log_debug(" mode: %dx%d", crtc->kern.mode.hdisplay, crtc->kern.mode.vdisplay);
- else
- log_debug(" mode: <none>");
- }
-
- log_debug(" encoders:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_ENCODER)
- continue;
-
- encoder = encoder_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
-
- if (encoder->kern.used_crtc)
- log_debug(" crtc: %u", encoder->kern.used_crtc);
- else
- log_debug(" crtc: <none>");
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * encoder->kern.n_crtcs + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < encoder->kern.n_crtcs; ++i)
- p += sprintf(p, " %" PRIu32, encoder->kern.crtcs[i]);
-
- log_debug(" possible crtcs:%s", buf);
- free(buf);
- }
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * encoder->kern.n_clones + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < encoder->kern.n_clones; ++i)
- p += sprintf(p, " %" PRIu32, encoder->kern.clones[i]);
-
- log_debug(" possible clones:%s", buf);
- free(buf);
- }
- }
-
- log_debug(" connectors:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- connector = connector_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
- log_debug(" type: %" PRIu32 "-%" PRIu32 " connection: %" PRIu32 " subpixel: %" PRIu32 " extents: %" PRIu32 "x%" PRIu32,
- connector->kern.type, connector->kern.type_id, connector->kern.connection, connector->kern.subpixel,
- connector->kern.mm_width, connector->kern.mm_height);
-
- if (connector->kern.used_encoder)
- log_debug(" encoder: %" PRIu32, connector->kern.used_encoder);
- else
- log_debug(" encoder: <none>");
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * connector->kern.n_encoders + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < connector->kern.n_encoders; ++i)
- p += sprintf(p, " %" PRIu32, connector->kern.encoders[i]);
-
- log_debug(" possible encoders:%s", buf);
- free(buf);
- }
-
- for (i = 0; i < connector->kern.n_modes; ++i) {
- struct drm_mode_modeinfo *mode = &connector->kern.modes[i];
- log_debug(" mode: %" PRIu32 "x%" PRIu32, mode->hdisplay, mode->vdisplay);
- }
- }
-
- log_debug(" planes:");
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (object->type != GRDRM_TYPE_PLANE)
- continue;
-
- plane = plane_from_object(object);
- log_debug(" (id: %u index: %d)", object->id, object->index);
- log_debug(" gamma-size: %" PRIu32, plane->kern.gamma_size);
-
- if (plane->kern.used_crtc)
- log_debug(" crtc: %" PRIu32, plane->kern.used_crtc);
- else
- log_debug(" crtc: <none>");
-
- buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * plane->kern.n_crtcs + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < plane->kern.n_crtcs; ++i)
- p += sprintf(p, " %" PRIu32, plane->kern.crtcs[i]);
-
- log_debug(" possible crtcs:%s", buf);
- free(buf);
- }
-
- buf = malloc((DECIMAL_STR_MAX(unsigned int) + 3) * plane->kern.n_formats + 1);
- if (buf) {
- buf[0] = 0;
- p = buf;
-
- for (i = 0; i < plane->kern.n_formats; ++i)
- p += sprintf(p, " 0x%x", (unsigned int)plane->kern.formats[i]);
-
- log_debug(" possible formats:%s", buf);
- free(buf);
- }
- }
-}
-
-static int grdrm_card_resync(grdrm_card *card) {
- _cleanup_free_ uint32_t *crtc_ids = NULL, *encoder_ids = NULL, *connector_ids = NULL, *plane_ids = NULL;
- uint32_t allocated = 0;
- grdrm_object *object;
- Iterator iter;
- size_t tries;
- int r;
-
- assert(card);
-
- card->async_hotplug = false;
- allocated = 0;
-
- /* mark existing objects for possible removal */
- HASHMAP_FOREACH(object, card->object_map, iter)
- object->present = false;
-
- for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
- struct drm_mode_get_plane_res pres;
- struct drm_mode_card_res res;
- uint32_t i, max;
-
- if (allocated < card->max_ids) {
- free(crtc_ids);
- free(encoder_ids);
- free(connector_ids);
- free(plane_ids);
- crtc_ids = new0(uint32_t, card->max_ids);
- encoder_ids = new0(uint32_t, card->max_ids);
- connector_ids = new0(uint32_t, card->max_ids);
- plane_ids = new0(uint32_t, card->max_ids);
-
- if (!crtc_ids || !encoder_ids || !connector_ids || !plane_ids)
- return -ENOMEM;
-
- allocated = card->max_ids;
- }
-
- zero(res);
- res.crtc_id_ptr = PTR_TO_UINT64(crtc_ids);
- res.connector_id_ptr = PTR_TO_UINT64(connector_ids);
- res.encoder_id_ptr = PTR_TO_UINT64(encoder_ids);
- res.count_crtcs = allocated;
- res.count_encoders = allocated;
- res.count_connectors = allocated;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot retrieve drm resources: %m",
- card->base.name);
- return r;
- }
-
- zero(pres);
- pres.plane_id_ptr = PTR_TO_UINT64(plane_ids);
- pres.count_planes = allocated;
-
- r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &pres);
- if (r < 0) {
- r = -errno;
- log_debug_errno(errno, "grdrm: %s: cannot retrieve drm plane-resources: %m",
- card->base.name);
- return r;
- }
-
- max = MAX(MAX(res.count_crtcs, res.count_encoders),
- MAX(res.count_connectors, pres.count_planes));
- if (max > allocated) {
- uint32_t n;
-
- n = ALIGN_POWER2(max);
- if (!n || n > UINT16_MAX) {
- log_debug("grdrm: %s: excessive DRM resource limit: %" PRIu32,
- card->base.name, max);
- return -ERANGE;
- }
-
- /* retry with resized buffers */
- card->max_ids = n;
- continue;
- }
-
- /* mark available objects as present */
-
- for (i = 0; i < res.count_crtcs; ++i) {
- object = grdrm_find_object(card, crtc_ids[i]);
- if (object && object->type == GRDRM_TYPE_CRTC) {
- object->present = true;
- object->index = i;
- crtc_ids[i] = 0;
- }
- }
-
- for (i = 0; i < res.count_encoders; ++i) {
- object = grdrm_find_object(card, encoder_ids[i]);
- if (object && object->type == GRDRM_TYPE_ENCODER) {
- object->present = true;
- object->index = i;
- encoder_ids[i] = 0;
- }
- }
-
- for (i = 0; i < res.count_connectors; ++i) {
- object = grdrm_find_object(card, connector_ids[i]);
- if (object && object->type == GRDRM_TYPE_CONNECTOR) {
- object->present = true;
- object->index = i;
- connector_ids[i] = 0;
- }
- }
-
- for (i = 0; i < pres.count_planes; ++i) {
- object = grdrm_find_object(card, plane_ids[i]);
- if (object && object->type == GRDRM_TYPE_PLANE) {
- object->present = true;
- object->index = i;
- plane_ids[i] = 0;
- }
- }
-
- /* drop removed objects */
-
- HASHMAP_FOREACH(object, card->object_map, iter)
- if (!object->present)
- grdrm_object_free(object);
-
- /* add new objects */
-
- card->n_crtcs = res.count_crtcs;
- for (i = 0; i < res.count_crtcs; ++i) {
- if (crtc_ids[i] < 1)
- continue;
-
- r = grdrm_crtc_new(NULL, card, crtc_ids[i], i);
- if (r < 0)
- return r;
- }
-
- card->n_encoders = res.count_encoders;
- for (i = 0; i < res.count_encoders; ++i) {
- if (encoder_ids[i] < 1)
- continue;
-
- r = grdrm_encoder_new(NULL, card, encoder_ids[i], i);
- if (r < 0)
- return r;
- }
-
- card->n_connectors = res.count_connectors;
- for (i = 0; i < res.count_connectors; ++i) {
- if (connector_ids[i] < 1)
- continue;
-
- r = grdrm_connector_new(NULL, card, connector_ids[i], i);
- if (r < 0)
- return r;
- }
-
- card->n_planes = pres.count_planes;
- for (i = 0; i < pres.count_planes; ++i) {
- if (plane_ids[i] < 1)
- continue;
-
- r = grdrm_plane_new(NULL, card, plane_ids[i], i);
- if (r < 0)
- return r;
- }
-
- /* re-sync objects after object_map is synced */
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- switch (object->type) {
- case GRDRM_TYPE_CRTC:
- r = grdrm_crtc_resync(crtc_from_object(object));
- break;
- case GRDRM_TYPE_ENCODER:
- r = grdrm_encoder_resync(encoder_from_object(object));
- break;
- case GRDRM_TYPE_CONNECTOR:
- r = grdrm_connector_resync(connector_from_object(object));
- break;
- case GRDRM_TYPE_PLANE:
- r = grdrm_plane_resync(plane_from_object(object));
- break;
- default:
- assert_not_reached("grdrm: invalid object type");
- r = 0;
- }
-
- if (r < 0)
- return r;
-
- if (card->async_hotplug)
- break;
- }
-
- /* if modeset objects change during sync, start over */
- if (card->async_hotplug) {
- card->async_hotplug = false;
- continue;
- }
-
- /* cache crtc/connector relationship */
- HASHMAP_FOREACH(object, card->object_map, iter) {
- grdrm_connector *connector;
- grdrm_encoder *encoder;
- grdrm_crtc *crtc;
-
- if (object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- connector = connector_from_object(object);
- if (connector->kern.connection != 1 || connector->kern.used_encoder < 1)
- continue;
-
- object = grdrm_find_object(card, connector->kern.used_encoder);
- if (!object || object->type != GRDRM_TYPE_ENCODER)
- continue;
-
- encoder = encoder_from_object(object);
- if (encoder->kern.used_crtc < 1)
- continue;
-
- object = grdrm_find_object(card, encoder->kern.used_crtc);
- if (!object || object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
- assert(crtc->kern.n_used_connectors < crtc->kern.max_used_connectors);
- crtc->kern.used_connectors[crtc->kern.n_used_connectors++] = connector->object.id;
- }
-
- /* cache old crtc settings for later restore */
- HASHMAP_FOREACH(object, card->object_map, iter) {
- grdrm_crtc *crtc;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
-
- /* Save data if it is the first time we refresh the CRTC. This data can
- * be used optionally to restore any previous configuration. For
- * instance, it allows us to restore VT configurations after we close
- * our session again. */
- if (!crtc->old.set) {
- crtc->old.fb = crtc->kern.used_fb;
- crtc->old.fb_x = crtc->kern.fb_offset_x;
- crtc->old.fb_y = crtc->kern.fb_offset_y;
- crtc->old.gamma = crtc->kern.gamma_size;
- crtc->old.n_connectors = crtc->kern.n_used_connectors;
- if (crtc->old.n_connectors)
- memcpy(crtc->old.connectors, crtc->kern.used_connectors, sizeof(uint32_t) * crtc->old.n_connectors);
- crtc->old.mode_set = crtc->kern.mode_set;
- crtc->old.mode = crtc->kern.mode;
- crtc->old.set = true;
- }
- }
-
- /* everything synced */
- break;
- }
-
- if (tries >= GRDRM_MAX_TRIES) {
- /*
- * Ugh! We were unable to sync the DRM card state due to heavy
- * hotplugging. This should never happen, so print a debug
- * message and bail out. The next uevent will trigger
- * this again.
- */
-
- log_debug("grdrm: %s: hotplug-storm when syncing card", card->base.name);
- return -EFAULT;
- }
-
- return 0;
-}
-
-static bool card_configure_crtc(grdrm_crtc *crtc, grdrm_connector *connector) {
- grdrm_card *card = crtc->object.card;
- grdrm_encoder *encoder;
- grdrm_object *object;
- uint32_t i, j;
-
- if (crtc->object.assigned || connector->object.assigned)
- return false;
- if (connector->kern.connection != 1)
- return false;
-
- for (i = 0; i < connector->kern.n_encoders; ++i) {
- object = grdrm_find_object(card, connector->kern.encoders[i]);
- if (!object || object->type != GRDRM_TYPE_ENCODER)
- continue;
-
- encoder = encoder_from_object(object);
- for (j = 0; j < encoder->kern.n_crtcs; ++j) {
- if (encoder->kern.crtcs[j] == crtc->object.id) {
- grdrm_crtc_assign(crtc, connector);
- return true;
- }
- }
- }
-
- return false;
-}
-
-static void grdrm_card_configure(grdrm_card *card) {
- /*
- * Modeset Configuration
- * This is where we update our modeset configuration and assign
- * connectors to CRTCs. This means, each connector that we want to
- * enable needs a CRTC, disabled (or unavailable) connectors are left
- * alone in the dark. Once all CRTCs are assigned, the remaining CRTCs
- * are disabled.
- * Sounds trivial, but there're several caveats:
- *
- * * Multiple connectors can be driven by the same CRTC. This is
- * known as 'hardware clone mode'. Advantage over software clone
- * mode is that only a single CRTC is needed to drive multiple
- * displays. However, few hardware supports this and it's a huge
- * headache to configure on dynamic demands. Therefore, we only
- * support it if configured statically beforehand.
- *
- * * CRTCs are not created equal. Some might be much more powerful
- * than others, including more advanced plane support. So far, our
- * CRTC selection is random. You need to supply static
- * configuration if you want special setups. So far, there is no
- * proper way to do advanced CRTC selection on dynamic demands. It
- * is not really clear which demands require what CRTC, so, like
- * everyone else, we do random CRTC selection unless explicitly
- * states otherwise.
- *
- * * Each Connector has a list of possible encoders that can drive
- * it, and each encoder has a list of possible CRTCs. If this graph
- * is a tree, assignment is trivial. However, if not, we cannot
- * reliably decide on configurations beforehand. The encoder is
- * always selected by the kernel, so we have to actually set a mode
- * to know which encoder is used. There is no way to ask the kernel
- * whether a given configuration is possible. This will change with
- * atomic-modesetting, but until then, we keep our configurations
- * simple and assume they work all just fine. If one fails
- * unexpectedly, we print a warning and disable it.
- *
- * Configuring a card consists of several steps:
- *
- * 1) First of all, we apply any user-configuration. If a user wants
- * a fixed configuration, we apply it and preserve it.
- * So far, we don't support user configuration files, so this step
- * is skipped.
- *
- * 2) Secondly, we need to apply any quirks from hwdb. Some hardware
- * might only support limited configurations or require special
- * CRTC/Connector mappings. We read this from hwdb and apply it, if
- * present.
- * So far, we don't support this as there is no known quirk, so
- * this step is skipped.
- *
- * 3) As deep modesets are expensive, we try to avoid them if
- * possible. Therefore, we read the current configuration from the
- * kernel and try to preserve it, if compatible with our demands.
- * If not, we break it and reassign it in a following step.
- *
- * 4) The main step involves configuring all remaining objects. By
- * default, all available connectors are enabled, except for those
- * disabled by user-configuration. We lookup a suitable CRTC for
- * each connector and assign them. As there might be more
- * connectors than CRTCs, we apply some ordering so users can
- * select which connectors are more important right now.
- * So far, we only apply the default ordering, more might be added
- * in the future.
- */
-
- grdrm_object *object;
- grdrm_crtc *crtc;
- Iterator i, j;
-
- /* clear assignments */
- HASHMAP_FOREACH(object, card->object_map, i)
- object->assigned = false;
-
- /* preserve existing configurations */
- HASHMAP_FOREACH(object, card->object_map, i) {
- if (object->type != GRDRM_TYPE_CRTC || object->assigned)
- continue;
-
- crtc = crtc_from_object(object);
-
- if (crtc->applied) {
- /* If our mode is set, preserve it. If no connector is
- * set, modeset either failed or the pipe is unused. In
- * both cases, leave it alone. It might be tried again
- * below in case there're remaining connectors.
- * Otherwise, try restoring the assignments. If they
- * are no longer valid, leave the pipe untouched. */
-
- if (crtc->set.n_connectors < 1)
- continue;
-
- assert(crtc->set.n_connectors == 1);
-
- object = grdrm_find_object(card, crtc->set.connectors[0]);
- if (!object || object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- card_configure_crtc(crtc, connector_from_object(object));
- } else if (crtc->kern.mode_set && crtc->kern.n_used_connectors != 1) {
- /* If our mode is not set on the pipe, we know the kern
- * information is valid. Try keeping it. If it's not
- * possible, leave the pipe untouched for later
- * assignements. */
-
- object = grdrm_find_object(card, crtc->kern.used_connectors[0]);
- if (!object || object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- card_configure_crtc(crtc, connector_from_object(object));
- }
- }
-
- /* assign remaining objects */
- HASHMAP_FOREACH(object, card->object_map, i) {
- if (object->type != GRDRM_TYPE_CRTC || object->assigned)
- continue;
-
- crtc = crtc_from_object(object);
-
- HASHMAP_FOREACH(object, card->object_map, j) {
- if (object->type != GRDRM_TYPE_CONNECTOR)
- continue;
-
- if (card_configure_crtc(crtc, connector_from_object(object)))
- break;
- }
-
- if (!crtc->object.assigned)
- grdrm_crtc_assign(crtc, NULL);
- }
-
- /* expose configuration */
- HASHMAP_FOREACH(object, card->object_map, i) {
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- grdrm_crtc_expose(crtc_from_object(object));
- }
-}
-
-static void grdrm_card_hotplug(grdrm_card *card) {
- int r;
-
- assert(card);
-
- if (!card->running)
- return;
-
- log_debug("grdrm: %s/%s: reconfigure card", card->base.session->name, card->base.name);
-
- card->ready = false;
- r = grdrm_card_resync(card);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot re-sync card: %m",
- card->base.session->name, card->base.name);
- return;
- }
-
- grdev_session_pin(card->base.session);
-
- /* debug statement to print card information */
- if (0)
- grdrm_card_print(card);
-
- grdrm_card_configure(card);
- card->ready = true;
- card->hotplug = false;
-
- grdev_session_unpin(card->base.session);
-}
-
-static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- grdrm_card *card = userdata;
- struct drm_event_vblank *vblank;
- struct drm_event *event;
- uint32_t id, counter;
- grdrm_object *object;
- char buf[4096];
- size_t len;
- ssize_t l;
-
- if (revents & (EPOLLHUP | EPOLLERR)) {
- /* Immediately close device on HUP; no need to flush pending
- * data.. there're no events we care about here. */
- log_debug("grdrm: %s/%s: HUP", card->base.session->name, card->base.name);
- grdrm_card_close(card);
- return 0;
- }
-
- if (revents & (EPOLLIN)) {
- l = read(card->fd, buf, sizeof(buf));
- if (l < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- log_debug_errno(errno, "grdrm: %s/%s: read error: %m",
- card->base.session->name, card->base.name);
- grdrm_card_close(card);
- return 0;
- }
-
- for (len = l; len > 0; len -= event->length) {
- event = (void*)buf;
-
- if (len < sizeof(*event) || len < event->length) {
- log_debug("grdrm: %s/%s: truncated event",
- card->base.session->name, card->base.name);
- break;
- }
-
- switch (event->type) {
- case DRM_EVENT_FLIP_COMPLETE:
- vblank = (void*)event;
- if (event->length < sizeof(*vblank)) {
- log_debug("grdrm: %s/%s: truncated vblank event",
- card->base.session->name, card->base.name);
- break;
- }
-
- grdrm_decode_vblank_data(vblank->user_data, &id, &counter);
- object = grdrm_find_object(card, id);
- if (!object || object->type != GRDRM_TYPE_CRTC)
- break;
-
- grdrm_crtc_flip_complete(crtc_from_object(object), counter, vblank);
- break;
- }
- }
- }
-
- return 0;
-}
-
-static int grdrm_card_add(grdrm_card *card, const char *name) {
- assert(card);
- assert(card->fd < 0);
-
- card->object_map = hashmap_new(&trivial_hash_ops);
- if (!card->object_map)
- return -ENOMEM;
-
- return grdev_card_add(&card->base, name);
-}
-
-static void grdrm_card_destroy(grdrm_card *card) {
- assert(card);
- assert(!card->running);
- assert(card->fd < 0);
- assert(hashmap_size(card->object_map) == 0);
-
- hashmap_free(card->object_map);
-}
-
-static void grdrm_card_commit(grdev_card *basecard) {
- grdrm_card *card = grdrm_card_from_base(basecard);
- grdrm_object *object;
- Iterator iter;
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (!card->ready)
- break;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- grdrm_crtc_commit(crtc_from_object(object));
- }
-}
-
-static void grdrm_card_restore(grdev_card *basecard) {
- grdrm_card *card = grdrm_card_from_base(basecard);
- grdrm_object *object;
- Iterator iter;
-
- HASHMAP_FOREACH(object, card->object_map, iter) {
- if (!card->ready)
- break;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- grdrm_crtc_restore(crtc_from_object(object));
- }
-}
-
-static void grdrm_card_enable(grdrm_card *card) {
- assert(card);
-
- if (card->fd < 0 || card->running)
- return;
-
- /* ignore cards without DUMB_BUFFER capability */
- if (!card->cap_dumb)
- return;
-
- assert(card->fd_src);
-
- log_debug("grdrm: %s/%s: enable", card->base.session->name, card->base.name);
-
- card->running = true;
- sd_event_source_set_enabled(card->fd_src, SD_EVENT_ON);
- grdrm_card_hotplug(card);
-}
-
-static void grdrm_card_disable(grdrm_card *card) {
- grdrm_object *object;
- Iterator iter;
-
- assert(card);
-
- if (card->fd < 0 || !card->running)
- return;
-
- assert(card->fd_src);
-
- log_debug("grdrm: %s/%s: disable", card->base.session->name, card->base.name);
-
- card->running = false;
- card->ready = false;
- sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
-
- /* stop all pipes */
- HASHMAP_FOREACH(object, card->object_map, iter) {
- grdrm_crtc *crtc;
-
- if (object->type != GRDRM_TYPE_CRTC)
- continue;
-
- crtc = crtc_from_object(object);
- crtc->applied = false;
- if (crtc->pipe)
- grdev_pipe_ready(&crtc->pipe->base, false);
- }
-}
-
-static int grdrm_card_open(grdrm_card *card, int dev_fd) {
- _cleanup_(grdev_session_unpinp) grdev_session *pin = NULL;
- _cleanup_close_ int fd = dev_fd;
- struct drm_get_cap cap;
- int r, flags;
-
- assert(card);
- assert(dev_fd >= 0);
- assert(card->fd != dev_fd);
-
- pin = grdev_session_pin(card->base.session);
- grdrm_card_close(card);
-
- log_debug("grdrm: %s/%s: open", card->base.session->name, card->base.name);
-
- r = fd_nonblock(fd, true);
- if (r < 0)
- return r;
-
- r = fd_cloexec(fd, true);
- if (r < 0)
- return r;
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0)
- return -errno;
- if ((flags & O_ACCMODE) != O_RDWR)
- return -EACCES;
-
- r = sd_event_add_io(card->base.session->context->event,
- &card->fd_src,
- fd,
- EPOLLHUP | EPOLLERR | EPOLLIN,
- grdrm_card_io_fn,
- card);
- if (r < 0)
- return r;
-
- sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
-
- card->hotplug = true;
- card->fd = fd;
- fd = -1;
-
- /* cache DUMB_BUFFER capability */
- cap.capability = DRM_CAP_DUMB_BUFFER;
- cap.value = 0;
- r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap);
- card->cap_dumb = r >= 0 && cap.value;
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot retrieve DUMB_BUFFER capability: %m",
- card->base.session->name, card->base.name);
- else if (!card->cap_dumb)
- log_debug("grdrm: %s/%s: DUMB_BUFFER capability not supported",
- card->base.session->name, card->base.name);
-
- /* cache TIMESTAMP_MONOTONIC capability */
- cap.capability = DRM_CAP_TIMESTAMP_MONOTONIC;
- cap.value = 0;
- r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap);
- card->cap_monotonic = r >= 0 && cap.value;
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot retrieve TIMESTAMP_MONOTONIC capability: %m",
- card->base.session->name, card->base.name);
- else if (!card->cap_monotonic)
- log_debug("grdrm: %s/%s: TIMESTAMP_MONOTONIC is disabled globally, fix this NOW!",
- card->base.session->name, card->base.name);
-
- return 0;
-}
-
-static void grdrm_card_close(grdrm_card *card) {
- grdrm_object *object;
-
- if (card->fd < 0)
- return;
-
- log_debug("grdrm: %s/%s: close", card->base.session->name, card->base.name);
-
- grdrm_card_disable(card);
-
- card->fd_src = sd_event_source_unref(card->fd_src);
- card->fd = safe_close(card->fd);
-
- grdev_session_pin(card->base.session);
- while ((object = hashmap_first(card->object_map)))
- grdrm_object_free(object);
- grdev_session_unpin(card->base.session);
-}
-
-static bool grdrm_card_async(grdrm_card *card, int r) {
- switch (r) {
- case -EACCES:
- /* If we get EACCES on runtime DRM calls, we lost DRM-Master
- * (or we did something terribly wrong). Immediately disable
- * the card, so we stop all pipes and wait to be activated
- * again. */
- grdrm_card_disable(card);
- break;
- case -ENOENT:
- /* DRM objects can be hotplugged at any time. If an object is
- * removed that we use, we remember that state so a following
- * call can test for this.
- * Note that we also get a uevent as followup, this will resync
- * the whole device. */
- card->async_hotplug = true;
- break;
- }
-
- return !card->ready;
-}
-
-/*
- * Unmanaged Cards
- * The unmanaged DRM card opens the device node for a given DRM device
- * directly (/dev/dri/cardX) and thus needs sufficient privileges. It opens
- * the device only if we really require it and releases it as soon as we're
- * disabled or closed.
- * The unmanaged element can be used in all situations where you have direct
- * access to DRM device nodes. Unlike managed DRM elements, it can be used
- * outside of user sessions and in emergency situations where logind is not
- * available.
- */
-
-static void unmanaged_card_enable(grdev_card *basecard) {
- unmanaged_card *cu = unmanaged_card_from_base(basecard);
- int r, fd;
-
- if (cu->card.fd < 0) {
- /* try open on activation if it failed during allocation */
- fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- /* not fatal; simply ignore the device */
- log_debug_errno(errno, "grdrm: %s/%s: cannot open node %s: %m",
- basecard->session->name, basecard->name, cu->devnode);
- return;
- }
-
- /* we might already be DRM-Master by open(); that's fine */
-
- r = grdrm_card_open(&cu->card, fd);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- basecard->session->name, basecard->name);
- return;
- }
- }
-
- r = ioctl(cu->card.fd, DRM_IOCTL_SET_MASTER, 0);
- if (r < 0) {
- log_debug_errno(errno, "grdrm: %s/%s: cannot acquire DRM-Master: %m",
- basecard->session->name, basecard->name);
- return;
- }
-
- grdrm_card_enable(&cu->card);
-}
-
-static void unmanaged_card_disable(grdev_card *basecard) {
- unmanaged_card *cu = unmanaged_card_from_base(basecard);
-
- grdrm_card_disable(&cu->card);
-}
-
-static int unmanaged_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
- _cleanup_(grdev_card_freep) grdev_card *basecard = NULL;
- char name[GRDRM_CARD_NAME_MAX];
- unmanaged_card *cu;
- const char *devnode;
- dev_t devnum;
- int r, fd;
-
- assert_return(session, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnode = udev_device_get_devnode(ud);
- devnum = udev_device_get_devnum(ud);
- if (!devnode || devnum == 0)
- return -ENODEV;
-
- grdrm_name(name, devnum);
-
- cu = new0(unmanaged_card, 1);
- if (!cu)
- return -ENOMEM;
-
- basecard = &cu->card.base;
- cu->card = GRDRM_CARD_INIT(&unmanaged_card_vtable, session);
-
- cu->devnode = strdup(devnode);
- if (!cu->devnode)
- return -ENOMEM;
-
- r = grdrm_card_add(&cu->card, name);
- if (r < 0)
- return r;
-
- /* try to open but ignore errors */
- fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- /* not fatal; allow uaccess based control on activation */
- log_debug_errno(errno, "grdrm: %s/%s: cannot open node %s: %m",
- basecard->session->name, basecard->name, cu->devnode);
- } else {
- /* We might get DRM-Master implicitly on open(); drop it immediately
- * so we acquire it only once we're actually enabled. We don't
- * really care whether this call fails or not, but let's log any
- * weird errors, anyway. */
- r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
- if (r < 0 && errno != EACCES && errno != EINVAL)
- log_debug_errno(errno, "grdrm: %s/%s: cannot drop DRM-Master: %m",
- basecard->session->name, basecard->name);
-
- r = grdrm_card_open(&cu->card, fd);
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- basecard->session->name, basecard->name);
- }
-
- if (out)
- *out = basecard;
- basecard = NULL;
- return 0;
-}
-
-static void unmanaged_card_free(grdev_card *basecard) {
- unmanaged_card *cu = unmanaged_card_from_base(basecard);
-
- assert(!basecard->enabled);
-
- grdrm_card_close(&cu->card);
- grdrm_card_destroy(&cu->card);
- free(cu->devnode);
- free(cu);
-}
-
-static const grdev_card_vtable unmanaged_card_vtable = {
- .free = unmanaged_card_free,
- .enable = unmanaged_card_enable,
- .disable = unmanaged_card_disable,
- .commit = grdrm_card_commit,
- .restore = grdrm_card_restore,
-};
-
-/*
- * Managed Cards
- * The managed DRM card uses systemd-logind to acquire DRM devices. This
- * means, we do not open the device node /dev/dri/cardX directly. Instead,
- * logind passes us a file-descriptor whenever our session is activated. Thus,
- * we don't need access to the device node directly.
- * Furthermore, whenever the session is put asleep, logind revokes the
- * file-descriptor so we loose access to the device.
- * Managed DRM cards should be preferred over unmanaged DRM cards whenever
- * you run inside a user session with exclusive device access.
- */
-
-static void managed_card_enable(grdev_card *card) {
- managed_card *cm = managed_card_from_base(card);
-
- /* If the device is manually re-enabled, we try to resume our card
- * management. Note that we have no control over DRM-Master and the fd,
- * so we have to take over the state from the last logind event. */
-
- if (cm->master)
- grdrm_card_enable(&cm->card);
-}
-
-static void managed_card_disable(grdev_card *card) {
- managed_card *cm = managed_card_from_base(card);
-
- /* If the device is manually disabled, we keep the FD but put our card
- * management asleep. This way, we can wake up at any time, but don't
- * touch the device while asleep. */
-
- grdrm_card_disable(&cm->card);
-}
-
-static int managed_card_pause_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_card *cm = userdata;
- grdev_session *session = cm->card.base.session;
- uint32_t major, minor;
- const char *mode;
- int r;
-
- /*
- * We get PauseDevice() signals from logind whenever a device we
- * requested was, or is about to be, paused. Arguments are major/minor
- * number of the device and the mode of the operation.
- * In case the event is not about our device, we ignore it. Otherwise,
- * we treat it as asynchronous DRM-DROP-MASTER. Note that we might have
- * already handled an EACCES error from a modeset ioctl, in which case
- * we already disabled the device.
- *
- * @mode can be one of the following:
- * "pause": The device is about to be paused. We must react
- * immediately and respond with PauseDeviceComplete(). Once
- * we replied, logind will pause the device. Note that
- * logind might apply any kind of timeout and force pause
- * the device if we don't respond in a timely manner. In
- * this case, we will receive a second PauseDevice event
- * with @mode set to "force" (or similar).
- * "force": The device was disabled forecfully by logind. DRM-Master
- * was already dropped. This is just an asynchronous
- * notification so we can put the device asleep (in case
- * we didn't already notice the dropped DRM-Master).
- * "gone": This is like "force" but is sent if the device was
- * paused due to a device-removal event.
- *
- * We always handle PauseDevice signals as "force" as we properly
- * support asynchronously dropping DRM-Master, anyway. But in case
- * logind sent mode "pause", we also call PauseDeviceComplete() to
- * immediately acknowledge the request.
- */
-
- r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
- if (r < 0) {
- log_debug("grdrm: %s/%s: erroneous PauseDevice signal",
- session->name, cm->card.base.name);
- return 0;
- }
-
- /* not our device? */
- if (makedev(major, minor) != cm->devnum)
- return 0;
-
- cm->master = false;
- grdrm_card_disable(&cm->card);
-
- if (streq(mode, "pause")) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- /*
- * Sending PauseDeviceComplete() is racy if logind triggers the
- * timeout. That is, if we take too long and logind pauses the
- * device by sending a forced PauseDevice, our
- * PauseDeviceComplete call will be stray. That's fine, though.
- * logind ignores such stray calls. Only if logind also sent a
- * further PauseDevice() signal, it might match our call
- * incorrectly to the newer PauseDevice(). That's fine, too, as
- * we handle that event asynchronously, anyway. Therefore,
- * whatever happens, we're fine. Yay!
- */
-
- r = sd_bus_message_new_method_call(session->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "PauseDeviceComplete");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major, minor);
- if (r >= 0)
- r = sd_bus_send(session->context->sysbus, m, NULL);
- }
-
- if (r < 0)
- log_debug_errno(r, "grdrm: %s/%s: cannot send PauseDeviceComplete: %m",
- session->name, cm->card.base.name);
- }
-
- return 0;
-}
-
-static int managed_card_resume_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_card *cm = userdata;
- grdev_session *session = cm->card.base.session;
- uint32_t major, minor;
- int r, fd;
-
- /*
- * We get ResumeDevice signals whenever logind resumed a previously
- * paused device. The arguments contain the major/minor number of the
- * related device and a new file-descriptor for the freshly opened
- * device-node.
- * If the signal is not about our device, we simply ignore it.
- * Otherwise, we immediately resume the device. Note that we drop the
- * new file-descriptor as we already have one from TakeDevice(). logind
- * preserves the file-context across pause/resume for DRM but only
- * drops/acquires DRM-Master accordingly. This way, our context (like
- * DRM-FBs and BOs) is preserved.
- */
-
- r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
- if (r < 0) {
- log_debug("grdrm: %s/%s: erroneous ResumeDevice signal",
- session->name, cm->card.base.name);
- return 0;
- }
-
- /* not our device? */
- if (makedev(major, minor) != cm->devnum)
- return 0;
-
- if (cm->card.fd < 0) {
- /* This shouldn't happen. We should already own an FD from
- * TakeDevice(). However, let's be safe and use this FD in case
- * we really don't have one. There is no harm in doing this
- * and our code works fine this way. */
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "grdrm: %s/%s: cannot duplicate fd: %m",
- session->name, cm->card.base.name);
- return 0;
- }
-
- r = grdrm_card_open(&cm->card, fd);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- session->name, cm->card.base.name);
- return 0;
- }
- }
-
- cm->master = true;
- if (cm->card.base.enabled)
- grdrm_card_enable(&cm->card);
-
- return 0;
-}
-
-static int managed_card_setup_bus(managed_card *cm) {
- grdev_session *session = cm->card.base.session;
- _cleanup_free_ char *match = NULL;
- int r;
-
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='PauseDevice',"
- "path='", session->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(session->context->sysbus,
- &cm->slot_pause_device,
- match,
- managed_card_pause_device_fn,
- cm);
- if (r < 0)
- return r;
-
- free(match);
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='ResumeDevice',"
- "path='", session->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(session->context->sysbus,
- &cm->slot_resume_device,
- match,
- managed_card_resume_device_fn,
- cm);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int managed_card_take_device_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_card *cm = userdata;
- grdev_session *session = cm->card.base.session;
- int r, paused, fd;
-
- cm->slot_take_device = sd_bus_slot_unref(cm->slot_take_device);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("grdrm: %s/%s: TakeDevice failed: %s: %s",
- session->name, cm->card.base.name, error->name, error->message);
- return 0;
- }
-
- cm->acquired = true;
-
- r = sd_bus_message_read(reply, "hb", &fd, &paused);
- if (r < 0) {
- log_debug("grdrm: %s/%s: erroneous TakeDevice reply",
- session->name, cm->card.base.name);
- return 0;
- }
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "grdrm: %s/%s: cannot duplicate fd: %m",
- session->name, cm->card.base.name);
- return 0;
- }
-
- r = grdrm_card_open(&cm->card, fd);
- if (r < 0) {
- log_debug_errno(r, "grdrm: %s/%s: cannot open: %m",
- session->name, cm->card.base.name);
- return 0;
- }
-
- if (!paused && cm->card.base.enabled)
- grdrm_card_enable(&cm->card);
-
- return 0;
-}
-
-static void managed_card_take_device(managed_card *cm) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- grdev_session *session = cm->card.base.session;
- int r;
-
- r = sd_bus_message_new_method_call(session->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "TakeDevice");
- if (r < 0)
- goto error;
-
- r = sd_bus_message_append(m, "uu", major(cm->devnum), minor(cm->devnum));
- if (r < 0)
- goto error;
-
- r = sd_bus_call_async(session->context->sysbus,
- &cm->slot_take_device,
- m,
- managed_card_take_device_fn,
- cm,
- 0);
- if (r < 0)
- goto error;
-
- cm->requested = true;
- return;
-
-error:
- log_debug_errno(r, "grdrm: %s/%s: cannot send TakeDevice request: %m",
- session->name, cm->card.base.name);
-}
-
-static void managed_card_release_device(managed_card *cm) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- grdev_session *session = cm->card.base.session;
- int r;
-
- /*
- * If TakeDevice() is pending or was successful, make sure to
- * release the device again. We don't care for return-values,
- * so send it without waiting or callbacks.
- * If a failed TakeDevice() is pending, but someone else took
- * the device on the same bus-connection, we might incorrectly
- * release their device. This is an unlikely race, though.
- * Furthermore, you really shouldn't have two users of the
- * controller-API on the same session, on the same devices, *AND* on
- * the same bus-connection. So we don't care for that race..
- */
-
- grdrm_card_close(&cm->card);
- cm->requested = false;
-
- if (!cm->acquired && !cm->slot_take_device)
- return;
-
- cm->slot_take_device = sd_bus_slot_unref(cm->slot_take_device);
- cm->acquired = false;
-
- r = sd_bus_message_new_method_call(session->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "ReleaseDevice");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major(cm->devnum), minor(cm->devnum));
- if (r >= 0)
- r = sd_bus_send(session->context->sysbus, m, NULL);
- }
-
- if (r < 0 && r != -ENOTCONN)
- log_debug_errno(r, "grdrm: %s/%s: cannot send ReleaseDevice: %m",
- session->name, cm->card.base.name);
-}
-
-static int managed_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
- _cleanup_(grdev_card_freep) grdev_card *basecard = NULL;
- char name[GRDRM_CARD_NAME_MAX];
- managed_card *cm;
- dev_t devnum;
- int r;
-
- assert_return(session, -EINVAL);
- assert_return(session->managed, -EINVAL);
- assert_return(session->context->sysbus, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return -ENODEV;
-
- grdrm_name(name, devnum);
-
- cm = new0(managed_card, 1);
- if (!cm)
- return -ENOMEM;
-
- basecard = &cm->card.base;
- cm->card = GRDRM_CARD_INIT(&managed_card_vtable, session);
- cm->devnum = devnum;
-
- r = managed_card_setup_bus(cm);
- if (r < 0)
- return r;
-
- r = grdrm_card_add(&cm->card, name);
- if (r < 0)
- return r;
-
- managed_card_take_device(cm);
-
- if (out)
- *out = basecard;
- basecard = NULL;
- return 0;
-}
-
-static void managed_card_free(grdev_card *basecard) {
- managed_card *cm = managed_card_from_base(basecard);
-
- assert(!basecard->enabled);
-
- managed_card_release_device(cm);
- cm->slot_resume_device = sd_bus_slot_unref(cm->slot_resume_device);
- cm->slot_pause_device = sd_bus_slot_unref(cm->slot_pause_device);
- grdrm_card_destroy(&cm->card);
- free(cm);
-}
-
-static const grdev_card_vtable managed_card_vtable = {
- .free = managed_card_free,
- .enable = managed_card_enable,
- .disable = managed_card_disable,
- .commit = grdrm_card_commit,
- .restore = grdrm_card_restore,
-};
-
-/*
- * Generic Constructor
- * Instead of relying on the caller to choose between managed and unmanaged
- * DRM devices, the grdev_drm_new() constructor does that for you (by
- * looking at session->managed).
- */
-
-bool grdev_is_drm_card(grdev_card *basecard) {
- return basecard && (basecard->vtable == &unmanaged_card_vtable ||
- basecard->vtable == &managed_card_vtable);
-}
-
-grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum) {
- char name[GRDRM_CARD_NAME_MAX];
-
- assert_return(session, NULL);
- assert_return(devnum != 0, NULL);
-
- grdrm_name(name, devnum);
- return grdev_find_card(session, name);
-}
-
-int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
- assert_return(session, -EINVAL);
- assert_return(ud, -EINVAL);
-
- return session->managed ? managed_card_new(out, session, ud) : unmanaged_card_new(out, session, ud);
-}
-
-void grdev_drm_card_hotplug(grdev_card *basecard, struct udev_device *ud) {
- const char *p, *action;
- grdrm_card *card;
- dev_t devnum;
-
- assert(basecard);
- assert(grdev_is_drm_card(basecard));
- assert(ud);
-
- card = grdrm_card_from_base(basecard);
-
- action = udev_device_get_action(ud);
- if (!action || streq(action, "add") || streq(action, "remove")) {
- /* If we get add/remove events on DRM nodes without devnum, we
- * got hotplugged DRM objects so refresh the device. */
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0) {
- card->hotplug = true;
- grdrm_card_hotplug(card);
- }
- } else if (streq_ptr(action, "change")) {
- /* A change event with HOTPLUG=1 is sent whenever a connector
- * changed state. Refresh the device to update our state. */
- p = udev_device_get_property_value(ud, "HOTPLUG");
- if (streq_ptr(p, "1")) {
- card->hotplug = true;
- grdrm_card_hotplug(card);
- }
- }
-}
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
deleted file mode 100644
index 46d65f0248..0000000000
--- a/src/libsystemd-terminal/grdev-internal.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-#include "util.h"
-#include "grdev.h"
-
-typedef struct grdev_tile grdev_tile;
-typedef struct grdev_display_cache grdev_display_cache;
-
-typedef struct grdev_pipe_vtable grdev_pipe_vtable;
-typedef struct grdev_pipe grdev_pipe;
-typedef struct grdev_card_vtable grdev_card_vtable;
-typedef struct grdev_card grdev_card;
-
-/*
- * DRM cards
- */
-
-bool grdev_is_drm_card(grdev_card *card);
-grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum);
-int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud);
-void grdev_drm_card_hotplug(grdev_card *card, struct udev_device *ud);
-
-/*
- * Displays
- */
-
-enum {
- GRDEV_TILE_LEAF,
- GRDEV_TILE_NODE,
- GRDEV_TILE_CNT
-};
-
-struct grdev_tile {
- LIST_FIELDS(grdev_tile, children_by_node);
- grdev_tile *parent;
- grdev_display *display;
-
- uint32_t x;
- uint32_t y;
- unsigned int rotate;
- unsigned int flip;
- uint32_t cache_w;
- uint32_t cache_h;
-
- unsigned int type;
-
- union {
- struct {
- grdev_pipe *pipe;
- } leaf;
-
- struct {
- size_t n_children;
- LIST_HEAD(grdev_tile, child_list);
- } node;
- };
-};
-
-int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe);
-int grdev_tile_new_node(grdev_tile **out);
-grdev_tile *grdev_tile_free(grdev_tile *tile);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_tile*, grdev_tile_free);
-
-struct grdev_display {
- grdev_session *session;
- char *name;
- void *userdata;
-
- size_t n_leafs;
- grdev_tile *tile;
-
- size_t n_pipes;
- size_t max_pipes;
-
- uint32_t width;
- uint32_t height;
-
- struct grdev_display_cache {
- grdev_pipe *pipe;
- grdev_display_target target;
-
- bool incomplete : 1;
- } *pipes;
-
- bool enabled : 1;
- bool public : 1;
- bool modified : 1;
- bool framed : 1;
-};
-
-grdev_display *grdev_find_display(grdev_session *session, const char *name);
-
-int grdev_display_new(grdev_display **out, grdev_session *session, const char *name);
-grdev_display *grdev_display_free(grdev_display *display);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_display*, grdev_display_free);
-
-/*
- * Pipes
- */
-
-struct grdev_pipe_vtable {
- void (*free) (grdev_pipe *pipe);
- void (*enable) (grdev_pipe *pipe);
- void (*disable) (grdev_pipe *pipe);
- grdev_fb *(*target) (grdev_pipe *pipe);
-};
-
-struct grdev_pipe {
- const grdev_pipe_vtable *vtable;
- grdev_card *card;
- char *name;
-
- grdev_tile *tile;
- grdev_display_cache *cache;
- sd_event_source *vsync_src;
-
- uint32_t width;
- uint32_t height;
- uint32_t vrefresh;
-
- size_t max_fbs;
- grdev_fb *front;
- grdev_fb *back;
- grdev_fb **fbs;
-
- bool enabled : 1;
- bool running : 1;
- bool flip : 1;
- bool flipping : 1;
-};
-
-#define GRDEV_PIPE_INIT(_vtable, _card) ((grdev_pipe){ \
- .vtable = (_vtable), \
- .card = (_card), \
- })
-
-grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name);
-
-int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs);
-grdev_pipe *grdev_pipe_free(grdev_pipe *pipe);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_pipe*, grdev_pipe_free);
-
-void grdev_pipe_ready(grdev_pipe *pipe, bool running);
-void grdev_pipe_frame(grdev_pipe *pipe);
-void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames);
-
-/*
- * Cards
- */
-
-struct grdev_card_vtable {
- void (*free) (grdev_card *card);
- void (*enable) (grdev_card *card);
- void (*disable) (grdev_card *card);
- void (*commit) (grdev_card *card);
- void (*restore) (grdev_card *card);
-};
-
-struct grdev_card {
- const grdev_card_vtable *vtable;
- grdev_session *session;
- char *name;
-
- Hashmap *pipe_map;
-
- bool enabled : 1;
- bool modified : 1;
-};
-
-#define GRDEV_CARD_INIT(_vtable, _session) ((grdev_card){ \
- .vtable = (_vtable), \
- .session = (_session), \
- })
-
-grdev_card *grdev_find_card(grdev_session *session, const char *name);
-
-int grdev_card_add(grdev_card *card, const char *name);
-grdev_card *grdev_card_free(grdev_card *card);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_card*, grdev_card_free);
-
-/*
- * Sessions
- */
-
-struct grdev_session {
- grdev_context *context;
- char *name;
- char *path;
- grdev_event_fn event_fn;
- void *userdata;
-
- unsigned long n_pins;
-
- Hashmap *card_map;
- Hashmap *display_map;
-
- bool custom : 1;
- bool managed : 1;
- bool enabled : 1;
- bool modified : 1;
-};
-
-grdev_session *grdev_session_pin(grdev_session *session);
-grdev_session *grdev_session_unpin(grdev_session *session);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_session*, grdev_session_unpin);
-
-/*
- * Contexts
- */
-
-struct grdev_context {
- unsigned long ref;
- sd_event *event;
- sd_bus *sysbus;
-
- Hashmap *session_map;
-};
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
deleted file mode 100644
index 71f0bd31e7..0000000000
--- a/src/libsystemd-terminal/grdev.c
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "login-util.h"
-#include "macro.h"
-#include "util.h"
-#include "grdev.h"
-#include "grdev-internal.h"
-
-static void pipe_enable(grdev_pipe *pipe);
-static void pipe_disable(grdev_pipe *pipe);
-static void card_modified(grdev_card *card);
-static void session_frame(grdev_session *session, grdev_display *display);
-
-/*
- * Displays
- */
-
-static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
- if (!tile)
- return NULL;
-
- while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
- tile = tile->node.child_list;
-
- return tile;
-}
-
-#define TILE_FOREACH(_root, _i) \
- for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent)
-
-#define TILE_FOREACH_SAFE(_root, _i, _next) \
- for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next)
-
-static void tile_link(grdev_tile *tile, grdev_tile *parent) {
- grdev_display *display;
- grdev_tile *t;
-
- assert(tile);
- assert(!tile->parent);
- assert(!tile->display);
- assert(parent);
- assert(parent->type == GRDEV_TILE_NODE);
-
- display = parent->display;
-
- assert(!display || !display->enabled);
-
- ++parent->node.n_children;
- LIST_PREPEND(children_by_node, parent->node.child_list, tile);
- tile->parent = parent;
-
- if (display) {
- display->modified = true;
- TILE_FOREACH(tile, t) {
- t->display = display;
- if (t->type == GRDEV_TILE_LEAF) {
- ++display->n_leafs;
- if (display->enabled)
- pipe_enable(t->leaf.pipe);
- }
- }
- }
-}
-
-static void tile_unlink(grdev_tile *tile) {
- grdev_tile *parent, *t;
- grdev_display *display;
-
- assert(tile);
-
- display = tile->display;
- parent = tile->parent;
- if (!parent) {
- assert(!display);
- return;
- }
-
- assert(parent->type == GRDEV_TILE_NODE);
- assert(parent->display == display);
- assert(parent->node.n_children > 0);
-
- --parent->node.n_children;
- LIST_REMOVE(children_by_node, parent->node.child_list, tile);
- tile->parent = NULL;
-
- if (display) {
- display->modified = true;
- TILE_FOREACH(tile, t) {
- t->display = NULL;
- if (t->type == GRDEV_TILE_LEAF) {
- --display->n_leafs;
- t->leaf.pipe->cache = NULL;
- pipe_disable(t->leaf.pipe);
- }
- }
- }
-
- /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
- * we must take care to not leave them around. Therefore, whenever we
- * unlink any part of a tree, we also destroy the parent, in case it's
- * now stale.
- * Parents are stale if they have no children and either have no display
- * or if they are intermediate nodes (i.e, they have a parent).
- * This means, you can easily create trees, but you can never partially
- * move or destruct them so far. They're always reduced to minimal form
- * if you cut them. This might change later, but so far we didn't need
- * partial destruction or the ability to move whole trees. */
-
- if (parent->node.n_children < 1 && (parent->parent || !parent->display))
- grdev_tile_free(parent);
-}
-
-static int tile_new(grdev_tile **out) {
- _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
-
- assert(out);
-
- tile = new0(grdev_tile, 1);
- if (!tile)
- return -ENOMEM;
-
- tile->type = (unsigned)-1;
-
- *out = tile;
- tile = NULL;
- return 0;
-}
-
-int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
- _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(pipe, -EINVAL);
- assert_return(!pipe->tile, -EINVAL);
-
- r = tile_new(&tile);
- if (r < 0)
- return r;
-
- tile->type = GRDEV_TILE_LEAF;
- tile->leaf.pipe = pipe;
-
- if (out)
- *out = tile;
- tile = NULL;
- return 0;
-}
-
-int grdev_tile_new_node(grdev_tile **out) {
- _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
- int r;
-
- assert_return(out, -EINVAL);
-
- r = tile_new(&tile);
- if (r < 0)
- return r;
-
- tile->type = GRDEV_TILE_NODE;
-
- *out = tile;
- tile = NULL;
- return 0;
-}
-
-grdev_tile *grdev_tile_free(grdev_tile *tile) {
- if (!tile)
- return NULL;
-
- tile_unlink(tile);
-
- switch (tile->type) {
- case GRDEV_TILE_LEAF:
- assert(!tile->parent);
- assert(!tile->display);
- assert(tile->leaf.pipe);
-
- break;
- case GRDEV_TILE_NODE:
- assert(!tile->parent);
- assert(!tile->display);
- assert(tile->node.n_children == 0);
-
- break;
- }
-
- free(tile);
-
- return NULL;
-}
-
-grdev_display *grdev_find_display(grdev_session *session, const char *name) {
- assert_return(session, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(session->display_map, name);
-}
-
-int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
- _cleanup_(grdev_display_freep) grdev_display *display = NULL;
- int r;
-
- assert(session);
- assert(name);
-
- display = new0(grdev_display, 1);
- if (!display)
- return -ENOMEM;
-
- display->session = session;
-
- display->name = strdup(name);
- if (!display->name)
- return -ENOMEM;
-
- r = grdev_tile_new_node(&display->tile);
- if (r < 0)
- return r;
-
- display->tile->display = display;
-
- r = hashmap_put(session->display_map, display->name, display);
- if (r < 0)
- return r;
-
- if (out)
- *out = display;
- display = NULL;
- return 0;
-}
-
-grdev_display *grdev_display_free(grdev_display *display) {
- if (!display)
- return NULL;
-
- assert(!display->public);
- assert(!display->enabled);
- assert(!display->modified);
- assert(display->n_leafs == 0);
- assert(display->n_pipes == 0);
-
- if (display->name)
- hashmap_remove_value(display->session->display_map, display->name, display);
-
- if (display->tile) {
- display->tile->display = NULL;
- grdev_tile_free(display->tile);
- }
-
- free(display->pipes);
- free(display->name);
- free(display);
-
- return NULL;
-}
-
-void grdev_display_set_userdata(grdev_display *display, void *userdata) {
- assert(display);
-
- display->userdata = userdata;
-}
-
-void *grdev_display_get_userdata(grdev_display *display) {
- assert_return(display, NULL);
-
- return display->userdata;
-}
-
-const char *grdev_display_get_name(grdev_display *display) {
- assert_return(display, NULL);
-
- return display->name;
-}
-
-uint32_t grdev_display_get_width(grdev_display *display) {
- assert_return(display, 0);
-
- return display->width;
-}
-
-uint32_t grdev_display_get_height(grdev_display *display) {
- assert_return(display, 0);
-
- return display->height;
-}
-
-bool grdev_display_is_enabled(grdev_display *display) {
- return display && display->enabled;
-}
-
-void grdev_display_enable(grdev_display *display) {
- grdev_tile *t;
-
- assert(display);
-
- if (!display->enabled) {
- display->enabled = true;
- TILE_FOREACH(display->tile, t)
- if (t->type == GRDEV_TILE_LEAF)
- pipe_enable(t->leaf.pipe);
- }
-}
-
-void grdev_display_disable(grdev_display *display) {
- grdev_tile *t;
-
- assert(display);
-
- if (display->enabled) {
- display->enabled = false;
- TILE_FOREACH(display->tile, t)
- if (t->type == GRDEV_TILE_LEAF)
- pipe_disable(t->leaf.pipe);
- }
-}
-
-const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) {
- grdev_display_cache *cache;
- size_t idx;
-
- assert_return(display, NULL);
- assert_return(!display->modified, NULL);
- assert_return(display->enabled, NULL);
-
- if (prev) {
- cache = container_of(prev, grdev_display_cache, target);
-
- assert(cache->pipe);
- assert(cache->pipe->tile->display == display);
- assert(display->pipes >= cache);
-
- idx = cache - display->pipes + 1;
- } else {
- idx = 0;
- }
-
- for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
- grdev_display_target *target;
- grdev_pipe *pipe;
- grdev_fb *fb;
-
- pipe = cache->pipe;
- target = &cache->target;
-
- if (!pipe->running || !pipe->enabled)
- continue;
-
- /* find suitable back-buffer */
- if (!pipe->back) {
- if (!pipe->vtable->target)
- continue;
- if (!(fb = pipe->vtable->target(pipe)))
- continue;
-
- assert(fb == pipe->back);
- }
-
- target->front = pipe->front;
- target->back = pipe->back;
-
- return target;
- }
-
- return NULL;
-}
-
-void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target) {
- grdev_display_cache *cache;
-
- assert(display);
- assert(!display->modified);
- assert(display->enabled);
- assert(target);
-
- cache = container_of(target, grdev_display_cache, target);
-
- assert(cache->pipe);
- assert(cache->pipe->tile->display == display);
-
- cache->pipe->flip = true;
-}
-
-static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
- uint32_t x, y, width, height;
- grdev_display_target *t;
-
- assert(c);
- assert(l);
- assert(l->cache_w >= c->target.width + c->target.x);
- assert(l->cache_h >= c->target.height + c->target.y);
-
- t = &c->target;
-
- /* rotate child */
-
- t->rotate = (t->rotate + l->rotate) & 0x3;
-
- x = t->x;
- y = t->y;
- width = t->width;
- height = t->height;
-
- switch (l->rotate) {
- case GRDEV_ROTATE_0:
- break;
- case GRDEV_ROTATE_90:
- t->x = l->cache_h - (height + y);
- t->y = x;
- t->width = height;
- t->height = width;
- break;
- case GRDEV_ROTATE_180:
- t->x = l->cache_w - (width + x);
- t->y = l->cache_h - (height + y);
- break;
- case GRDEV_ROTATE_270:
- t->x = y;
- t->y = l->cache_w - (width + x);
- t->width = height;
- t->height = width;
- break;
- }
-
- /* flip child */
-
- t->flip ^= l->flip;
-
- if (l->flip & GRDEV_FLIP_HORIZONTAL)
- t->x = l->cache_w - (t->width + t->x);
- if (l->flip & GRDEV_FLIP_VERTICAL)
- t->y = l->cache_h - (t->height + t->y);
-
- /* move child */
-
- t->x += l->x;
- t->y += l->y;
-}
-
-static void display_cache_targets(grdev_display *display) {
- grdev_display_cache *c;
- grdev_tile *tile;
-
- assert(display);
-
- /* depth-first with children before parent */
- for (tile = tile_leftmost(display->tile);
- tile;
- tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
- if (tile->type == GRDEV_TILE_LEAF) {
- grdev_pipe *p;
-
- /* We're at a leaf and no parent has been cached, yet.
- * Copy the pipe information into the target cache and
- * update our global pipe-caches if required. */
-
- assert(tile->leaf.pipe);
- assert(display->n_pipes + 1 <= display->max_pipes);
-
- p = tile->leaf.pipe;
- c = &display->pipes[display->n_pipes++];
-
- zero(*c);
- c->pipe = p;
- c->pipe->cache = c;
- c->target.width = p->width;
- c->target.height = p->height;
- tile->cache_w = p->width;
- tile->cache_h = p->height;
-
- /* all new tiles are incomplete due to geometry changes */
- c->incomplete = true;
-
- display_cache_apply(c, tile);
- } else {
- grdev_tile *child, *l;
-
- /* We're now at a node with all its children already
- * computed (depth-first, child before parent). We
- * first need to know the size of our tile, then we
- * recurse into all leafs and update their cache. */
-
- tile->cache_w = 0;
- tile->cache_h = 0;
-
- LIST_FOREACH(children_by_node, child, tile->node.child_list) {
- if (child->x + child->cache_w > tile->cache_w)
- tile->cache_w = child->x + child->cache_w;
- if (child->y + child->cache_h > tile->cache_h)
- tile->cache_h = child->y + child->cache_h;
- }
-
- assert(tile->cache_w > 0);
- assert(tile->cache_h > 0);
-
- TILE_FOREACH(tile, l)
- if (l->type == GRDEV_TILE_LEAF)
- display_cache_apply(l->leaf.pipe->cache, tile);
- }
- }
-}
-
-static bool display_cache(grdev_display *display) {
- grdev_tile *tile;
- size_t n;
- void *t;
- int r;
-
- assert(display);
-
- if (!display->modified)
- return false;
-
- display->modified = false;
- display->framed = false;
- display->n_pipes = 0;
- display->width = 0;
- display->height = 0;
-
- if (display->n_leafs < 1)
- return false;
-
- TILE_FOREACH(display->tile, tile)
- if (tile->type == GRDEV_TILE_LEAF)
- tile->leaf.pipe->cache = NULL;
-
- if (display->n_leafs > display->max_pipes) {
- n = ALIGN_POWER2(display->n_leafs);
- if (!n) {
- r = -ENOMEM;
- goto out;
- }
-
- t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
- if (!t) {
- r = -ENOMEM;
- goto out;
- }
-
- display->pipes = t;
- display->max_pipes = n;
- }
-
- display_cache_targets(display);
- display->width = display->tile->cache_w;
- display->height = display->tile->cache_h;
-
- r = 0;
-
-out:
- if (r < 0)
- log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m",
- display->session->name, display->name);
- return true;
-}
-
-/*
- * Pipes
- */
-
-grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
- assert_return(card, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(card->pipe_map, name);
-}
-
-static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
- grdev_pipe *pipe = userdata;
-
- grdev_pipe_frame(pipe);
- return 0;
-}
-
-int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
- int r;
-
- assert_return(pipe, -EINVAL);
- assert_return(pipe->vtable, -EINVAL);
- assert_return(pipe->vtable->free, -EINVAL);
- assert_return(pipe->card, -EINVAL);
- assert_return(pipe->card->session, -EINVAL);
- assert_return(!pipe->cache, -EINVAL);
- assert_return(pipe->width > 0, -EINVAL);
- assert_return(pipe->height > 0, -EINVAL);
- assert_return(pipe->vrefresh > 0, -EINVAL);
- assert_return(!pipe->enabled, -EINVAL);
- assert_return(!pipe->running, -EINVAL);
- assert_return(name, -EINVAL);
-
- pipe->name = strdup(name);
- if (!pipe->name)
- return -ENOMEM;
-
- if (n_fbs > 0) {
- pipe->fbs = new0(grdev_fb*, n_fbs);
- if (!pipe->fbs)
- return -ENOMEM;
-
- pipe->max_fbs = n_fbs;
- }
-
- r = grdev_tile_new_leaf(&pipe->tile, pipe);
- if (r < 0)
- return r;
-
- r = sd_event_add_time(pipe->card->session->context->event,
- &pipe->vsync_src,
- CLOCK_MONOTONIC,
- 0,
- 10 * USEC_PER_MSEC,
- pipe_vsync_fn,
- pipe);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
- if (r < 0)
- return r;
-
- r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
- if (r < 0)
- return r;
-
- card_modified(pipe->card);
- return 0;
-}
-
-grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
- grdev_pipe tmp;
-
- if (!pipe)
- return NULL;
-
- assert(pipe->card);
- assert(pipe->vtable);
- assert(pipe->vtable->free);
-
- if (pipe->name)
- hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
- if (pipe->tile)
- tile_unlink(pipe->tile);
-
- assert(!pipe->cache);
-
- tmp = *pipe;
- pipe->vtable->free(pipe);
-
- sd_event_source_unref(tmp.vsync_src);
- grdev_tile_free(tmp.tile);
- card_modified(tmp.card);
- free(tmp.fbs);
- free(tmp.name);
-
- return NULL;
-}
-
-static void pipe_enable(grdev_pipe *pipe) {
- assert(pipe);
-
- if (!pipe->enabled) {
- pipe->enabled = true;
- if (pipe->vtable->enable)
- pipe->vtable->enable(pipe);
- }
-}
-
-static void pipe_disable(grdev_pipe *pipe) {
- assert(pipe);
-
- if (pipe->enabled) {
- pipe->enabled = false;
- if (pipe->vtable->disable)
- pipe->vtable->disable(pipe);
- }
-}
-
-void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
- assert(pipe);
-
- /* grdev_pipe_ready() is used by backends to notify about pipe state
- * changed. If a pipe is ready, it can be fully used by us (available,
- * enabled and accessible). Backends can disable pipes at any time
- * (like for async revocation), but can only enable them from parent
- * context. Otherwise, we might call user-callbacks recursively. */
-
- if (pipe->running == running)
- return;
-
- pipe->running = running;
-
- /* runtime events for unused pipes are not interesting */
- if (pipe->cache && pipe->enabled) {
- grdev_display *display = pipe->tile->display;
-
- assert(display);
-
- if (running)
- session_frame(display->session, display);
- else
- pipe->cache->incomplete = true;
- }
-}
-
-void grdev_pipe_frame(grdev_pipe *pipe) {
- grdev_display *display;
-
- assert(pipe);
-
- /* if pipe is unused, ignore any frame events */
- if (!pipe->cache || !pipe->enabled)
- return;
-
- display = pipe->tile->display;
- assert(display);
-
- grdev_pipe_schedule(pipe, 0);
- session_frame(display->session, display);
-}
-
-void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
- int r;
- uint64_t ts;
-
- if (!frames) {
- sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
- return;
- }
-
- r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
- if (r < 0)
- goto error;
-
- ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
-
- r = sd_event_source_set_time(pipe->vsync_src, ts);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
- if (r < 0)
- goto error;
-
- return;
-
-error:
- log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m",
- pipe->card->session->name, pipe->card->name, pipe->name);
-}
-
-/*
- * Cards
- */
-
-grdev_card *grdev_find_card(grdev_session *session, const char *name) {
- assert_return(session, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(session->card_map, name);
-}
-
-int grdev_card_add(grdev_card *card, const char *name) {
- int r;
-
- assert_return(card, -EINVAL);
- assert_return(card->vtable, -EINVAL);
- assert_return(card->vtable->free, -EINVAL);
- assert_return(card->session, -EINVAL);
- assert_return(name, -EINVAL);
-
- card->name = strdup(name);
- if (!card->name)
- return -ENOMEM;
-
- card->pipe_map = hashmap_new(&string_hash_ops);
- if (!card->pipe_map)
- return -ENOMEM;
-
- r = hashmap_put(card->session->card_map, card->name, card);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-grdev_card *grdev_card_free(grdev_card *card) {
- grdev_card tmp;
-
- if (!card)
- return NULL;
-
- assert(!card->enabled);
- assert(card->vtable);
- assert(card->vtable->free);
-
- if (card->name)
- hashmap_remove_value(card->session->card_map, card->name, card);
-
- tmp = *card;
- card->vtable->free(card);
-
- assert(hashmap_size(tmp.pipe_map) == 0);
-
- hashmap_free(tmp.pipe_map);
- free(tmp.name);
-
- return NULL;
-}
-
-static void card_modified(grdev_card *card) {
- assert(card);
- assert(card->session->n_pins > 0);
-
- card->modified = true;
-}
-
-static void grdev_card_enable(grdev_card *card) {
- assert(card);
-
- if (!card->enabled) {
- card->enabled = true;
- if (card->vtable->enable)
- card->vtable->enable(card);
- }
-}
-
-static void grdev_card_disable(grdev_card *card) {
- assert(card);
-
- if (card->enabled) {
- card->enabled = false;
- if (card->vtable->disable)
- card->vtable->disable(card);
- }
-}
-
-/*
- * Sessions
- */
-
-static void session_raise(grdev_session *session, grdev_event *event) {
- session->event_fn(session, session->userdata, event);
-}
-
-static void session_raise_display_add(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_ADD,
- .display_add = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_REMOVE,
- .display_remove = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_raise_display_change(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_CHANGE,
- .display_change = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
- grdev_event event = {
- .type = GRDEV_EVENT_DISPLAY_FRAME,
- .display_frame = {
- .display = display,
- },
- };
-
- session_raise(session, &event);
-}
-
-static void session_add_card(grdev_session *session, grdev_card *card) {
- assert(session);
- assert(card);
-
- log_debug("grdev: %s: add card '%s'", session->name, card->name);
-
- /* Cards are not exposed to users, but managed internally. Cards are
- * enabled if the session is enabled, and will track that state. The
- * backend can probe the card at any time, but only if enabled. It
- * will then add pipes according to hardware state.
- * That is, the card may create pipes as soon as we enable it here. */
-
- if (session->enabled)
- grdev_card_enable(card);
-}
-
-static void session_remove_card(grdev_session *session, grdev_card *card) {
- assert(session);
- assert(card);
-
- log_debug("grdev: %s: remove card '%s'", session->name, card->name);
-
- /* As cards are not exposed, it can never be accessed by outside
- * users and we can simply remove it. Disabling the card does not
- * necessarily drop all pipes of the card. This is usually deferred
- * to card destruction (as pipes are cached as long as FDs remain
- * open). Therefore, the card destruction might cause pipes, and thus
- * visible displays, to be removed. */
-
- grdev_card_disable(card);
- grdev_card_free(card);
-}
-
-static void session_add_display(grdev_session *session, grdev_display *display) {
- assert(session);
- assert(display);
- assert(!display->enabled);
-
- log_debug("grdev: %s: add display '%s'", session->name, display->name);
-
- /* Displays are the main entity for public API users. We create them
- * independent of card backends and they wrap any underlying display
- * architecture. Displays are public at all times, thus, may be entered
- * by outside users at any time. */
-
- display->public = true;
- session_raise_display_add(session, display);
-}
-
-static void session_remove_display(grdev_session *session, grdev_display *display) {
- assert(session);
- assert(display);
-
- log_debug("grdev: %s: remove display '%s'", session->name, display->name);
-
- /* Displays are public, so we have to be careful when removing them.
- * We first tell users about their removal, disable them and then drop
- * them. We now, after the notification, no external access will
- * happen. Therefore, we can release the tiles afterwards safely. */
-
- if (display->public) {
- display->public = false;
- session_raise_display_remove(session, display);
- }
-
- grdev_display_disable(display);
- grdev_display_free(display);
-}
-
-static void session_change_display(grdev_session *session, grdev_display *display) {
- bool changed;
-
- assert(session);
- assert(display);
-
- changed = display_cache(display);
-
- if (display->n_leafs == 0) {
- session_remove_display(session, display);
- } else if (!display->public) {
- session_add_display(session, display);
- session_frame(session, display);
- } else if (changed) {
- session_raise_display_change(session, display);
- session_frame(session, display);
- } else if (display->framed) {
- session_frame(session, display);
- }
-}
-
-static void session_frame(grdev_session *session, grdev_display *display) {
- assert(session);
- assert(display);
-
- display->framed = false;
-
- if (!display->enabled || !session->enabled)
- return;
-
- if (session->n_pins > 0)
- display->framed = true;
- else
- session_raise_display_frame(session, display);
-}
-
-int grdev_session_new(grdev_session **out,
- grdev_context *context,
- unsigned int flags,
- const char *name,
- grdev_event_fn event_fn,
- void *userdata) {
- _cleanup_(grdev_session_freep) grdev_session *session = NULL;
- int r;
-
- assert(out);
- assert(context);
- assert(name);
- assert(event_fn);
- assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
- assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
- assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
-
- session = new0(grdev_session, 1);
- if (!session)
- return -ENOMEM;
-
- session->context = grdev_context_ref(context);
- session->custom = flags & GRDEV_SESSION_CUSTOM;
- session->managed = flags & GRDEV_SESSION_MANAGED;
- session->event_fn = event_fn;
- session->userdata = userdata;
-
- session->name = strdup(name);
- if (!session->name)
- return -ENOMEM;
-
- if (session->managed) {
- r = sd_bus_path_encode("/org/freedesktop/login1/session",
- session->name, &session->path);
- if (r < 0)
- return r;
- }
-
- session->card_map = hashmap_new(&string_hash_ops);
- if (!session->card_map)
- return -ENOMEM;
-
- session->display_map = hashmap_new(&string_hash_ops);
- if (!session->display_map)
- return -ENOMEM;
-
- r = hashmap_put(context->session_map, session->name, session);
- if (r < 0)
- return r;
-
- *out = session;
- session = NULL;
- return 0;
-}
-
-grdev_session *grdev_session_free(grdev_session *session) {
- grdev_card *card;
-
- if (!session)
- return NULL;
-
- grdev_session_disable(session);
-
- while ((card = hashmap_first(session->card_map)))
- session_remove_card(session, card);
-
- assert(hashmap_size(session->display_map) == 0);
-
- if (session->name)
- hashmap_remove_value(session->context->session_map, session->name, session);
-
- hashmap_free(session->display_map);
- hashmap_free(session->card_map);
- session->context = grdev_context_unref(session->context);
- free(session->path);
- free(session->name);
- free(session);
-
- return NULL;
-}
-
-bool grdev_session_is_enabled(grdev_session *session) {
- return session && session->enabled;
-}
-
-void grdev_session_enable(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (!session->enabled) {
- session->enabled = true;
- HASHMAP_FOREACH(card, session->card_map, iter)
- grdev_card_enable(card);
- }
-}
-
-void grdev_session_disable(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (session->enabled) {
- session->enabled = false;
- HASHMAP_FOREACH(card, session->card_map, iter)
- grdev_card_disable(card);
- }
-}
-
-void grdev_session_commit(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (!session->enabled)
- return;
-
- HASHMAP_FOREACH(card, session->card_map, iter)
- if (card->vtable->commit)
- card->vtable->commit(card);
-}
-
-void grdev_session_restore(grdev_session *session) {
- grdev_card *card;
- Iterator iter;
-
- assert(session);
-
- if (!session->enabled)
- return;
-
- HASHMAP_FOREACH(card, session->card_map, iter)
- if (card->vtable->restore)
- card->vtable->restore(card);
-}
-
-void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
- grdev_card *card;
- dev_t devnum;
- int r;
-
- assert(session);
- assert(ud);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return grdev_session_hotplug_drm(session, ud);
-
- card = grdev_find_drm_card(session, devnum);
- if (card)
- return;
-
- r = grdev_drm_card_new(&card, session, ud);
- if (r < 0) {
- log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m",
- session->name, udev_device_get_syspath(ud));
- return;
- }
-
- session_add_card(session, card);
-}
-
-void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
- grdev_card *card;
- dev_t devnum;
-
- assert(session);
- assert(ud);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return grdev_session_hotplug_drm(session, ud);
-
- card = grdev_find_drm_card(session, devnum);
- if (!card)
- return;
-
- session_remove_card(session, card);
-}
-
-void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
- grdev_card *card = NULL;
- struct udev_device *p;
- dev_t devnum;
-
- assert(session);
- assert(ud);
-
- for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- continue;
-
- card = grdev_find_drm_card(session, devnum);
- if (card)
- break;
- }
-
- if (!card)
- return;
-
- grdev_drm_card_hotplug(card, ud);
-}
-
-static void session_configure(grdev_session *session) {
- grdev_display *display;
- grdev_tile *tile;
- grdev_card *card;
- grdev_pipe *pipe;
- Iterator i, j;
- int r;
-
- assert(session);
-
- /*
- * Whenever backends add or remove pipes, we set session->modified and
- * require them to pin the session while modifying it. On release, we
- * reconfigure the device and re-assign displays to all modified pipes.
- *
- * So far, we configure each pipe as a separate display. We do not
- * support user-configuration, nor have we gotten any reports from
- * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
- * we get reports, we keep the logic to a minimum.
- */
-
- /* create new displays for all unconfigured pipes */
- HASHMAP_FOREACH(card, session->card_map, i) {
- if (!card->modified)
- continue;
-
- card->modified = false;
-
- HASHMAP_FOREACH(pipe, card->pipe_map, j) {
- tile = pipe->tile;
- if (tile->display)
- continue;
-
- assert(!tile->parent);
-
- display = grdev_find_display(session, pipe->name);
- if (display && display->tile) {
- log_debug("grdev: %s/%s: occupied display for pipe %s",
- session->name, card->name, pipe->name);
- continue;
- } else if (!display) {
- r = grdev_display_new(&display, session, pipe->name);
- if (r < 0) {
- log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m",
- session->name, card->name, pipe->name);
- continue;
- }
- }
-
- tile_link(pipe->tile, display->tile);
- }
- }
-
- /* update displays */
- HASHMAP_FOREACH(display, session->display_map, i)
- session_change_display(session, display);
-}
-
-grdev_session *grdev_session_pin(grdev_session *session) {
- assert(session);
-
- ++session->n_pins;
- return session;
-}
-
-grdev_session *grdev_session_unpin(grdev_session *session) {
- if (!session)
- return NULL;
-
- assert(session->n_pins > 0);
-
- if (--session->n_pins == 0)
- session_configure(session);
-
- return NULL;
-}
-
-/*
- * Contexts
- */
-
-int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
- _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
-
- assert_return(out, -EINVAL);
- assert_return(event, -EINVAL);
-
- context = new0(grdev_context, 1);
- if (!context)
- return -ENOMEM;
-
- context->ref = 1;
- context->event = sd_event_ref(event);
-
- if (sysbus)
- context->sysbus = sd_bus_ref(sysbus);
-
- context->session_map = hashmap_new(&string_hash_ops);
- if (!context->session_map)
- return -ENOMEM;
-
- *out = context;
- context = NULL;
- return 0;
-}
-
-static void context_cleanup(grdev_context *context) {
- assert(hashmap_size(context->session_map) == 0);
-
- hashmap_free(context->session_map);
- context->sysbus = sd_bus_unref(context->sysbus);
- context->event = sd_event_unref(context->event);
- free(context);
-}
-
-grdev_context *grdev_context_ref(grdev_context *context) {
- assert_return(context, NULL);
- assert_return(context->ref > 0, NULL);
-
- ++context->ref;
- return context;
-}
-
-grdev_context *grdev_context_unref(grdev_context *context) {
- if (!context)
- return NULL;
-
- assert_return(context->ref > 0, NULL);
-
- if (--context->ref == 0)
- context_cleanup(context);
-
- return NULL;
-}
diff --git a/src/libsystemd-terminal/grdev.h b/src/libsystemd-terminal/grdev.h
deleted file mode 100644
index 110d24e6d5..0000000000
--- a/src/libsystemd-terminal/grdev.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Graphics Devices
- * The grdev layer provides generic access to graphics devices. The device
- * types are hidden in the implementation and exported in a generic way. The
- * grdev_session object forms the base layer. It loads, configures and prepares
- * any graphics devices associated with that session. Each session is totally
- * independent of other sessions and can be controlled separately.
- * The target devices on a session are called display. A display always
- * corresponds to a real display regardless how many pipes are needed to drive
- * that display. That is, an exported display might internally be created out
- * of arbitrary combinations of target pipes. However, this is meant as
- * implementation detail and API users must never assume details below the
- * display-level. That is, a display is the most low-level object exported.
- * Therefore, pipe-configuration and any low-level modesetting is hidden from
- * the public API. It is provided by the implementation, and it is the
- * implementation that decides how pipes are driven.
- *
- * The API users are free to ignore specific displays or combine them to create
- * larger screens. This often requires user-configuration so is dictated by
- * policy. The underlying pipe-configuration might be affected by these
- * high-level policies, but is never directly controlled by those. That means,
- * depending on the displays you use, it might affect how underlying resources
- * are assigned. However, users can never directly apply policies to the pipes,
- * but only to displays. In case specific hardware needs quirks on the pipe
- * level, we support that via hwdb, not via public user configuration.
- *
- * Right now, displays are limited to rgb32 memory-mapped framebuffers on the
- * primary plane. However, the grdev implementation can be easily extended to
- * allow more powerful access (including hardware-acceleration for 2D and 3D
- * compositing). So far, this wasn't needed so it is not exposed.
- */
-
-#pragma once
-
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "util.h"
-
-typedef struct grdev_fb grdev_fb;
-typedef struct grdev_display_target grdev_display_target;
-typedef struct grdev_display grdev_display;
-
-typedef struct grdev_event grdev_event;
-typedef struct grdev_session grdev_session;
-typedef struct grdev_context grdev_context;
-
-enum {
- /* clockwise rotation; we treat this is abelian group Z4 with ADD */
- GRDEV_ROTATE_0 = 0,
- GRDEV_ROTATE_90 = 1,
- GRDEV_ROTATE_180 = 2,
- GRDEV_ROTATE_270 = 3,
-};
-
-enum {
- /* flip states; we treat this as abelian group V4 with XOR */
- GRDEV_FLIP_NONE = 0x0,
- GRDEV_FLIP_HORIZONTAL = 0x1,
- GRDEV_FLIP_VERTICAL = 0x2,
-};
-
-/*
- * Displays
- */
-
-struct grdev_fb {
- uint32_t width;
- uint32_t height;
- uint32_t format;
- int32_t strides[4];
- void *maps[4];
-
- union {
- void *ptr;
- uint64_t u64;
- } data;
-
- void (*free_fn) (void *ptr);
-};
-
-struct grdev_display_target {
- uint32_t x;
- uint32_t y;
- uint32_t width;
- uint32_t height;
- unsigned int rotate;
- unsigned int flip;
- grdev_fb *front;
- grdev_fb *back;
-};
-
-void grdev_display_set_userdata(grdev_display *display, void *userdata);
-void *grdev_display_get_userdata(grdev_display *display);
-
-const char *grdev_display_get_name(grdev_display *display);
-uint32_t grdev_display_get_width(grdev_display *display);
-uint32_t grdev_display_get_height(grdev_display *display);
-
-bool grdev_display_is_enabled(grdev_display *display);
-void grdev_display_enable(grdev_display *display);
-void grdev_display_disable(grdev_display *display);
-
-const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev);
-void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target);
-
-#define GRDEV_DISPLAY_FOREACH_TARGET(_display, _t) \
- for ((_t) = grdev_display_next_target((_display), NULL); \
- (_t); \
- (_t) = grdev_display_next_target((_display), (_t)))
-
-/*
- * Events
- */
-
-enum {
- GRDEV_EVENT_DISPLAY_ADD,
- GRDEV_EVENT_DISPLAY_REMOVE,
- GRDEV_EVENT_DISPLAY_CHANGE,
- GRDEV_EVENT_DISPLAY_FRAME,
-};
-
-typedef void (*grdev_event_fn) (grdev_session *session, void *userdata, grdev_event *ev);
-
-struct grdev_event {
- unsigned int type;
- union {
- struct {
- grdev_display *display;
- } display_add, display_remove, display_change;
-
- struct {
- grdev_display *display;
- } display_frame;
- };
-};
-
-/*
- * Sessions
- */
-
-enum {
- GRDEV_SESSION_CUSTOM = (1 << 0),
- GRDEV_SESSION_MANAGED = (1 << 1),
-};
-
-int grdev_session_new(grdev_session **out,
- grdev_context *context,
- unsigned int flags,
- const char *name,
- grdev_event_fn event_fn,
- void *userdata);
-grdev_session *grdev_session_free(grdev_session *session);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_session*, grdev_session_free);
-
-bool grdev_session_is_enabled(grdev_session *session);
-void grdev_session_enable(grdev_session *session);
-void grdev_session_disable(grdev_session *session);
-
-void grdev_session_commit(grdev_session *session);
-void grdev_session_restore(grdev_session *session);
-
-void grdev_session_add_drm(grdev_session *session, struct udev_device *ud);
-void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud);
-void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud);
-
-/*
- * Contexts
- */
-
-int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus);
-grdev_context *grdev_context_ref(grdev_context *context);
-grdev_context *grdev_context_unref(grdev_context *context);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_context*, grdev_context_unref);
diff --git a/src/libsystemd-terminal/idev-evdev.c b/src/libsystemd-terminal/idev-evdev.c
deleted file mode 100644
index f1a18b91d3..0000000000
--- a/src/libsystemd-terminal/idev-evdev.c
+++ /dev/null
@@ -1,859 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <fcntl.h>
-#include <libevdev/libevdev.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "macro.h"
-#include "util.h"
-#include "bus-util.h"
-#include "idev.h"
-#include "idev-internal.h"
-
-typedef struct idev_evdev idev_evdev;
-typedef struct unmanaged_evdev unmanaged_evdev;
-typedef struct managed_evdev managed_evdev;
-
-struct idev_evdev {
- idev_element element;
- struct libevdev *evdev;
- int fd;
- sd_event_source *fd_src;
- sd_event_source *idle_src;
-
- bool unsync : 1; /* not in-sync with kernel */
- bool resync : 1; /* re-syncing with kernel */
- bool running : 1;
-};
-
-struct unmanaged_evdev {
- idev_evdev evdev;
- char *devnode;
-};
-
-struct managed_evdev {
- idev_evdev evdev;
- dev_t devnum;
- sd_bus_slot *slot_take_device;
-
- bool requested : 1; /* TakeDevice() was sent */
- bool acquired : 1; /* TakeDevice() was successful */
-};
-
-#define idev_evdev_from_element(_e) container_of((_e), idev_evdev, element)
-#define unmanaged_evdev_from_element(_e) \
- container_of(idev_evdev_from_element(_e), unmanaged_evdev, evdev)
-#define managed_evdev_from_element(_e) \
- container_of(idev_evdev_from_element(_e), managed_evdev, evdev)
-
-#define IDEV_EVDEV_INIT(_vtable, _session) ((idev_evdev){ \
- .element = IDEV_ELEMENT_INIT((_vtable), (_session)), \
- .fd = -1, \
- })
-
-#define IDEV_EVDEV_NAME_MAX (8 + DECIMAL_STR_MAX(unsigned) * 2)
-
-static const idev_element_vtable unmanaged_evdev_vtable;
-static const idev_element_vtable managed_evdev_vtable;
-
-static int idev_evdev_resume(idev_evdev *evdev, int dev_fd);
-static void idev_evdev_pause(idev_evdev *evdev, bool release);
-
-/*
- * Virtual Evdev Element
- * The virtual evdev element is the base class of all other evdev elements. It
- * uses libevdev to access the kernel evdev API. It supports asynchronous
- * access revocation, re-syncing if events got dropped and more.
- * This element cannot be used by itself. There must be a wrapper around it
- * which opens a file-descriptor and passes it to the virtual evdev element.
- */
-
-static void idev_evdev_name(char *out, dev_t devnum) {
- /* @out must be at least of size IDEV_EVDEV_NAME_MAX */
- sprintf(out, "evdev/%u:%u", major(devnum), minor(devnum));
-}
-
-static int idev_evdev_feed_resync(idev_evdev *evdev) {
- idev_data data = {
- .type = IDEV_DATA_RESYNC,
- .resync = evdev->resync,
- };
-
- return idev_element_feed(&evdev->element, &data);
-}
-
-static int idev_evdev_feed_evdev(idev_evdev *evdev, struct input_event *event) {
- idev_data data = {
- .type = IDEV_DATA_EVDEV,
- .resync = evdev->resync,
- .evdev = {
- .event = *event,
- },
- };
-
- return idev_element_feed(&evdev->element, &data);
-}
-
-static void idev_evdev_hup(idev_evdev *evdev) {
- /*
- * On HUP, we close the current fd via idev_evdev_pause(). This drops
- * the event-sources from the main-loop and effectively puts the
- * element asleep. If the HUP is part of a hotplug-event, a following
- * udev-notification will destroy the element. Otherwise, the HUP is
- * either result of access-revokation or a serious error.
- * For unmanaged devices, we should never receive HUP (except for
- * unplug-events). But if we do, something went seriously wrong and we
- * shouldn't try to be clever.
- * Instead, we simply stay asleep and wait for the device to be
- * disabled and then re-enabled (or closed and re-opened). This will
- * re-open the device node and restart the device.
- * For managed devices, a HUP usually means our device-access was
- * revoked. In that case, we simply put the device asleep and wait for
- * logind to notify us once the device is alive again. logind also
- * passes us a new fd. Hence, we don't have to re-enable the device.
- *
- * Long story short: The only thing we have to do here, is close() the
- * file-descriptor and remove it from the main-loop. Everything else is
- * handled via additional events we receive.
- */
-
- idev_evdev_pause(evdev, true);
-}
-
-static int idev_evdev_io(idev_evdev *evdev) {
- idev_element *e = &evdev->element;
- struct input_event ev;
- unsigned int flags;
- int r, error = 0;
-
- /*
- * Read input-events via libevdev until the input-queue is drained. In
- * case we're disabled, don't do anything. The input-queue might
- * overflow, but we don't care as we have to resync after wake-up,
- * anyway.
- * TODO: libevdev should give us a hint how many events to read. We
- * really want to avoid starvation, so we shouldn't read forever in
- * case we cannot keep up with the kernel.
- * TODO: Make sure libevdev always reports SYN_DROPPED to us, regardless
- * whether any event was synced afterwards.
- */
-
- flags = LIBEVDEV_READ_FLAG_NORMAL;
- while (e->enabled) {
- if (evdev->unsync) {
- /* immediately resync, even if in sync right now */
- evdev->unsync = false;
- evdev->resync = false;
- flags = LIBEVDEV_READ_FLAG_NORMAL;
- r = libevdev_next_event(evdev->evdev, flags | LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
- if (r < 0 && r != -EAGAIN) {
- r = 0;
- goto error;
- } else if (r != LIBEVDEV_READ_STATUS_SYNC) {
- log_debug("idev-evdev: %s/%s: cannot force resync: %d",
- e->session->name, e->name, r);
- }
- } else {
- r = libevdev_next_event(evdev->evdev, flags, &ev);
- }
-
- if (evdev->resync && r == -EAGAIN) {
- /* end of re-sync */
- evdev->resync = false;
- flags = LIBEVDEV_READ_FLAG_NORMAL;
- } else if (r == -EAGAIN) {
- /* no data available */
- break;
- } else if (r < 0) {
- /* read error */
- goto error;
- } else if (r == LIBEVDEV_READ_STATUS_SYNC) {
- if (evdev->resync) {
- /* sync-event */
- r = idev_evdev_feed_evdev(evdev, &ev);
- if (r != 0) {
- error = r;
- break;
- }
- } else {
- /* start of sync */
- evdev->resync = true;
- flags = LIBEVDEV_READ_FLAG_SYNC;
- r = idev_evdev_feed_resync(evdev);
- if (r != 0) {
- error = r;
- break;
- }
- }
- } else {
- /* normal event */
- r = idev_evdev_feed_evdev(evdev, &ev);
- if (r != 0) {
- error = r;
- break;
- }
- }
- }
-
- if (error < 0)
- log_debug_errno(error, "idev-evdev: %s/%s: error on data event: %m",
- e->session->name, e->name);
- return error;
-
-error:
- idev_evdev_hup(evdev);
- return 0; /* idev_evdev_hup() handles the error so discard it */
-}
-
-static int idev_evdev_event_fn(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- idev_evdev *evdev = userdata;
-
- /* fetch data as long as EPOLLIN is signalled */
- if (revents & EPOLLIN)
- return idev_evdev_io(evdev);
-
- if (revents & (EPOLLHUP | EPOLLERR))
- idev_evdev_hup(evdev);
-
- return 0;
-}
-
-static int idev_evdev_idle_fn(sd_event_source *s, void *userdata) {
- idev_evdev *evdev = userdata;
-
- /*
- * The idle-event is raised whenever we have to re-sync the libevdev
- * state from the kernel. We simply call into idev_evdev_io() which
- * flushes the state and re-syncs it if @unsync is set.
- * State has to be synced whenever our view of the kernel device is
- * out of date. This is the case when we open the device, if the
- * kernel's receive buffer overflows, or on other exceptional
- * situations. Events during re-syncs must be forwarded to the upper
- * layers so they can update their view of the device. However, such
- * events must only be handled passively, as they might be out-of-order
- * and/or re-ordered. Therefore, we mark them as 'sync' events.
- */
-
- if (!evdev->unsync)
- return 0;
-
- return idev_evdev_io(evdev);
-}
-
-static void idev_evdev_destroy(idev_evdev *evdev) {
- assert(evdev);
- assert(evdev->fd < 0);
-
- libevdev_free(evdev->evdev);
- evdev->evdev = NULL;
-}
-
-static void idev_evdev_enable(idev_evdev *evdev) {
- assert(evdev);
- assert(evdev->fd_src);
- assert(evdev->idle_src);
-
- if (evdev->running)
- return;
- if (evdev->fd < 0 || evdev->element.n_open < 1 || !evdev->element.enabled)
- return;
-
- evdev->running = true;
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_ON);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_ONESHOT);
-}
-
-static void idev_evdev_disable(idev_evdev *evdev) {
- assert(evdev);
- assert(evdev->fd_src);
- assert(evdev->idle_src);
-
- if (!evdev->running)
- return;
-
- evdev->running = false;
- idev_evdev_feed_resync(evdev);
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
-}
-
-static int idev_evdev_resume(idev_evdev *evdev, int dev_fd) {
- idev_element *e = &evdev->element;
- _cleanup_close_ int fd = dev_fd;
- int r, flags;
-
- if (fd < 0 || evdev->fd == fd) {
- fd = -1;
- idev_evdev_enable(evdev);
- return 0;
- }
-
- idev_evdev_pause(evdev, true);
- log_debug("idev-evdev: %s/%s: resume", e->session->name, e->name);
-
- r = fd_nonblock(fd, true);
- if (r < 0)
- return r;
-
- r = fd_cloexec(fd, true);
- if (r < 0)
- return r;
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0)
- return -errno;
-
- flags &= O_ACCMODE;
- if (flags == O_WRONLY)
- return -EACCES;
-
- evdev->element.readable = true;
- evdev->element.writable = !(flags & O_RDONLY);
-
- /*
- * TODO: We *MUST* re-sync the device so we get a delta of the changed
- * state while we didn't read events from the device. This works just
- * fine with libevdev_change_fd(), however, libevdev_new_from_fd() (or
- * libevdev_set_fd()) don't pass us events for the initial device
- * state. So even if we force a re-sync, we will not get the delta for
- * the initial device state.
- * We really need to fix libevdev to support that!
- */
- if (evdev->evdev)
- r = libevdev_change_fd(evdev->evdev, fd);
- else
- r = libevdev_new_from_fd(fd, &evdev->evdev);
-
- if (r < 0)
- return r;
-
- r = sd_event_add_io(e->session->context->event,
- &evdev->fd_src,
- fd,
- EPOLLHUP | EPOLLERR | EPOLLIN,
- idev_evdev_event_fn,
- evdev);
- if (r < 0)
- return r;
-
- r = sd_event_add_defer(e->session->context->event,
- &evdev->idle_src,
- idev_evdev_idle_fn,
- evdev);
- if (r < 0) {
- evdev->fd_src = sd_event_source_unref(evdev->fd_src);
- return r;
- }
-
- sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
- sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
-
- evdev->unsync = true;
- evdev->fd = fd;
- fd = -1;
-
- idev_evdev_enable(evdev);
- return 0;
-}
-
-static void idev_evdev_pause(idev_evdev *evdev, bool release) {
- idev_element *e = &evdev->element;
-
- if (evdev->fd < 0)
- return;
-
- log_debug("idev-evdev: %s/%s: pause", e->session->name, e->name);
-
- idev_evdev_disable(evdev);
- if (release) {
- evdev->idle_src = sd_event_source_unref(evdev->idle_src);
- evdev->fd_src = sd_event_source_unref(evdev->fd_src);
- evdev->fd = safe_close(evdev->fd);
- }
-}
-
-/*
- * Unmanaged Evdev Element
- * The unmanaged evdev element opens the evdev node for a given input device
- * directly (/dev/input/eventX) and thus needs sufficient privileges. It opens
- * the device only if we really require it and releases it as soon as we're
- * disabled or closed.
- * The unmanaged element can be used in all situations where you have direct
- * access to input device nodes. Unlike managed evdev elements, it can be used
- * outside of user sessions and in emergency situations where logind is not
- * available.
- */
-
-static void unmanaged_evdev_resume(idev_element *e) {
- unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
- int r, fd;
-
- /*
- * Unmanaged devices can be acquired on-demand. Therefore, don't
- * acquire it unless someone opened the device *and* we're enabled.
- */
- if (e->n_open < 1 || !e->enabled)
- return;
-
- fd = eu->evdev.fd;
- if (fd < 0) {
- fd = open(eu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- if (errno != EACCES && errno != EPERM) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m",
- e->session->name, e->name, eu->devnode);
- return;
- }
-
- fd = open(eu->devnode, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m",
- e->session->name, e->name, eu->devnode);
- return;
- }
-
- e->readable = true;
- e->writable = false;
- } else {
- e->readable = true;
- e->writable = true;
- }
- }
-
- r = idev_evdev_resume(&eu->evdev, fd);
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
- e->session->name, e->name);
-}
-
-static void unmanaged_evdev_pause(idev_element *e) {
- unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
-
- /*
- * Release the device if the device is disabled or there is no-one who
- * opened it. This guarantees we stay only available if we're opened
- * *and* enabled.
- */
-
- idev_evdev_pause(&eu->evdev, true);
-}
-
-static int unmanaged_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
- _cleanup_(idev_element_freep) idev_element *e = NULL;
- char name[IDEV_EVDEV_NAME_MAX];
- unmanaged_evdev *eu;
- const char *devnode;
- dev_t devnum;
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnode = udev_device_get_devnode(ud);
- devnum = udev_device_get_devnum(ud);
- if (!devnode || devnum == 0)
- return -ENODEV;
-
- idev_evdev_name(name, devnum);
-
- eu = new0(unmanaged_evdev, 1);
- if (!eu)
- return -ENOMEM;
-
- e = &eu->evdev.element;
- eu->evdev = IDEV_EVDEV_INIT(&unmanaged_evdev_vtable, s);
-
- eu->devnode = strdup(devnode);
- if (!eu->devnode)
- return -ENOMEM;
-
- r = idev_element_add(e, name);
- if (r < 0)
- return r;
-
- if (out)
- *out = e;
- e = NULL;
- return 0;
-}
-
-static void unmanaged_evdev_free(idev_element *e) {
- unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
-
- idev_evdev_destroy(&eu->evdev);
- free(eu->devnode);
- free(eu);
-}
-
-static const idev_element_vtable unmanaged_evdev_vtable = {
- .free = unmanaged_evdev_free,
- .enable = unmanaged_evdev_resume,
- .disable = unmanaged_evdev_pause,
- .open = unmanaged_evdev_resume,
- .close = unmanaged_evdev_pause,
-};
-
-/*
- * Managed Evdev Element
- * The managed evdev element uses systemd-logind to acquire evdev devices. This
- * means, we do not open the device node /dev/input/eventX directly. Instead,
- * logind passes us a file-descriptor whenever our session is activated. Thus,
- * we don't need access to the device node directly.
- * Furthermore, whenever the session is put asleep, logind revokes the
- * file-descriptor so we loose access to the device.
- * Managed evdev elements should be preferred over unmanaged elements whenever
- * you run inside a user session with exclusive device access.
- */
-
-static int managed_evdev_take_device_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- managed_evdev *em = userdata;
- idev_element *e = &em->evdev.element;
- idev_session *s = e->session;
- int r, paused, fd;
-
- em->slot_take_device = sd_bus_slot_unref(em->slot_take_device);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("idev-evdev: %s/%s: TakeDevice failed: %s: %s",
- s->name, e->name, error->name, error->message);
- return 0;
- }
-
- em->acquired = true;
-
- r = sd_bus_message_read(reply, "hb", &fd, &paused);
- if (r < 0) {
- log_debug("idev-evdev: %s/%s: erroneous TakeDevice reply", s->name, e->name);
- return 0;
- }
-
- /* If the device is paused, ignore it; we will get the next fd via
- * ResumeDevice signals. */
- if (paused)
- return 0;
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name);
- return 0;
- }
-
- r = idev_evdev_resume(&em->evdev, fd);
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
- s->name, e->name);
-
- return 0;
-}
-
-static void managed_evdev_enable(idev_element *e) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- idev_context *c = s->context;
- int r;
-
- /*
- * Acquiring managed devices is heavy, so do it only once we're
- * enabled *and* opened by someone.
- */
- if (e->n_open < 1 || !e->enabled)
- return;
-
- /* bail out if already pending */
- if (em->requested)
- return;
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- s->path,
- "org.freedesktop.login1.Session",
- "TakeDevice");
- if (r < 0)
- goto error;
-
- r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
- if (r < 0)
- goto error;
-
- r = sd_bus_call_async(c->sysbus,
- &em->slot_take_device,
- m,
- managed_evdev_take_device_fn,
- em,
- 0);
- if (r < 0)
- goto error;
-
- em->requested = true;
- return;
-
-error:
- log_debug_errno(r, "idev-evdev: %s/%s: cannot send TakeDevice request: %m",
- s->name, e->name);
-}
-
-static void managed_evdev_disable(idev_element *e) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- idev_context *c = s->context;
- int r;
-
- /*
- * Releasing managed devices is heavy. Once acquired, we get
- * notifications for sleep/wake-up events, so there's no reason to
- * release it if disabled but opened. However, if a device is closed,
- * we release it immediately as we don't care for sleep/wake-up events
- * then (even if we're actually enabled).
- */
-
- idev_evdev_pause(&em->evdev, false);
-
- if (e->n_open > 0 || !em->requested)
- return;
-
- /*
- * If TakeDevice() is pending or was successful, make sure to
- * release the device again. We don't care for return-values,
- * so send it without waiting or callbacks.
- * If a failed TakeDevice() is pending, but someone else took
- * the device on the same bus-connection, we might incorrectly
- * release their device. This is an unlikely race, though.
- * Furthermore, you really shouldn't have two users of the
- * controller-API on the same session, on the same devices, *AND* on
- * the same bus-connection. So we don't care for that race..
- */
-
- idev_evdev_pause(&em->evdev, true);
- em->requested = false;
-
- if (!em->acquired && !em->slot_take_device)
- return;
-
- em->slot_take_device = sd_bus_slot_unref(em->slot_take_device);
- em->acquired = false;
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- s->path,
- "org.freedesktop.login1.Session",
- "ReleaseDevice");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
- if (r >= 0)
- r = sd_bus_send(c->sysbus, m, NULL);
- }
-
- if (r < 0 && r != -ENOTCONN)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot send ReleaseDevice: %m",
- s->name, e->name);
-}
-
-static void managed_evdev_resume(idev_element *e, int fd) {
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- int r;
-
- /*
- * We get ResumeDevice signals whenever logind resumed a previously
- * paused device. The arguments contain the major/minor number of the
- * related device and a new file-descriptor for the freshly opened
- * device-node. We take the file-descriptor and immediately resume the
- * device.
- */
-
- fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (fd < 0) {
- log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
- s->name, e->name);
- return;
- }
-
- r = idev_evdev_resume(&em->evdev, fd);
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m",
- s->name, e->name);
-
- return;
-}
-
-static void managed_evdev_pause(idev_element *e, const char *mode) {
- managed_evdev *em = managed_evdev_from_element(e);
- idev_session *s = e->session;
- idev_context *c = s->context;
- int r;
-
- /*
- * We get PauseDevice() signals from logind whenever a device we
- * requested was, or is about to be, paused. Arguments are major/minor
- * number of the device and the mode of the operation.
- * We treat it as asynchronous access-revocation (as if we got HUP on
- * the device fd). Note that we might have already treated the HUP
- * event via EPOLLHUP, whichever comes first.
- *
- * @mode can be one of the following:
- * "pause": The device is about to be paused. We must react
- * immediately and respond with PauseDeviceComplete(). Once
- * we replied, logind will pause the device. Note that
- * logind might apply any kind of timeout and force pause
- * the device if we don't respond in a timely manner. In
- * this case, we will receive a second PauseDevice event
- * with @mode set to "force" (or similar).
- * "force": The device was disabled forecfully by logind. Access is
- * already revoked. This is just an asynchronous
- * notification so we can put the device asleep (in case
- * we didn't already notice the access revocation).
- * "gone": This is like "force" but is sent if the device was
- * paused due to a device-removal event.
- *
- * We always handle PauseDevice signals as "force" as we properly
- * support asynchronous access revocation, anyway. But in case logind
- * sent mode "pause", we also call PauseDeviceComplete() to immediately
- * acknowledge the request.
- */
-
- idev_evdev_pause(&em->evdev, true);
-
- if (streq(mode, "pause")) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-
- /*
- * Sending PauseDeviceComplete() is racy if logind triggers the
- * timeout. That is, if we take too long and logind pauses the
- * device by sending a forced PauseDevice, our
- * PauseDeviceComplete call will be stray. That's fine, though.
- * logind ignores such stray calls. Only if logind also sent a
- * further PauseDevice() signal, it might match our call
- * incorrectly to the newer PauseDevice(). That's fine, too, as
- * we handle that event asynchronously, anyway. Therefore,
- * whatever happens, we're fine. Yay!
- */
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- s->path,
- "org.freedesktop.login1.Session",
- "PauseDeviceComplete");
- if (r >= 0) {
- r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
- if (r >= 0)
- r = sd_bus_send(c->sysbus, m, NULL);
- }
-
- if (r < 0)
- log_debug_errno(r, "idev-evdev: %s/%s: cannot send PauseDeviceComplete: %m",
- s->name, e->name);
- }
-}
-
-static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
- _cleanup_(idev_element_freep) idev_element *e = NULL;
- char name[IDEV_EVDEV_NAME_MAX];
- managed_evdev *em;
- dev_t devnum;
- int r;
-
- assert_return(s, -EINVAL);
- assert_return(s->managed, -EINVAL);
- assert_return(s->context->sysbus, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return -ENODEV;
-
- idev_evdev_name(name, devnum);
-
- em = new0(managed_evdev, 1);
- if (!em)
- return -ENOMEM;
-
- e = &em->evdev.element;
- em->evdev = IDEV_EVDEV_INIT(&managed_evdev_vtable, s);
- em->devnum = devnum;
-
- r = idev_element_add(e, name);
- if (r < 0)
- return r;
-
- if (out)
- *out = e;
- e = NULL;
- return 0;
-}
-
-static void managed_evdev_free(idev_element *e) {
- managed_evdev *em = managed_evdev_from_element(e);
-
- idev_evdev_destroy(&em->evdev);
- free(em);
-}
-
-static const idev_element_vtable managed_evdev_vtable = {
- .free = managed_evdev_free,
- .enable = managed_evdev_enable,
- .disable = managed_evdev_disable,
- .open = managed_evdev_enable,
- .close = managed_evdev_disable,
- .resume = managed_evdev_resume,
- .pause = managed_evdev_pause,
-};
-
-/*
- * Generic Constructor
- * Instead of relying on the caller to choose between managed and unmanaged
- * evdev devices, the idev_evdev_new() constructor does that for you (by
- * looking at s->managed).
- */
-
-bool idev_is_evdev(idev_element *e) {
- return e && (e->vtable == &unmanaged_evdev_vtable ||
- e->vtable == &managed_evdev_vtable);
-}
-
-idev_element *idev_find_evdev(idev_session *s, dev_t devnum) {
- char name[IDEV_EVDEV_NAME_MAX];
-
- assert_return(s, NULL);
- assert_return(devnum != 0, NULL);
-
- idev_evdev_name(name, devnum);
- return idev_find_element(s, name);
-}
-
-int idev_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
- assert_return(s, -EINVAL);
- assert_return(ud, -EINVAL);
-
- return s->managed ? managed_evdev_new(out, s, ud) : unmanaged_evdev_new(out, s, ud);
-}
diff --git a/src/libsystemd-terminal/idev-internal.h b/src/libsystemd-terminal/idev-internal.h
deleted file mode 100644
index a02a16c408..0000000000
--- a/src/libsystemd-terminal/idev-internal.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <xkbcommon/xkbcommon.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-#include "util.h"
-#include "idev.h"
-
-typedef struct idev_link idev_link;
-typedef struct idev_device_vtable idev_device_vtable;
-typedef struct idev_element idev_element;
-typedef struct idev_element_vtable idev_element_vtable;
-
-/*
- * Evdev Elements
- */
-
-bool idev_is_evdev(idev_element *e);
-idev_element *idev_find_evdev(idev_session *s, dev_t devnum);
-int idev_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud);
-
-/*
- * Keyboard Devices
- */
-
-bool idev_is_keyboard(idev_device *d);
-idev_device *idev_find_keyboard(idev_session *s, const char *name);
-int idev_keyboard_new(idev_device **out, idev_session *s, const char *name);
-
-/*
- * Element Links
- */
-
-struct idev_link {
- /* element-to-device connection */
- LIST_FIELDS(idev_link, links_by_element);
- idev_element *element;
-
- /* device-to-element connection */
- LIST_FIELDS(idev_link, links_by_device);
- idev_device *device;
-};
-
-/*
- * Devices
- */
-
-struct idev_device_vtable {
- void (*free) (idev_device *d);
- void (*attach) (idev_device *d, idev_link *l);
- void (*detach) (idev_device *d, idev_link *l);
- int (*feed) (idev_device *d, idev_data *data);
-};
-
-struct idev_device {
- const idev_device_vtable *vtable;
- idev_session *session;
- char *name;
-
- LIST_HEAD(idev_link, links);
-
- bool public : 1;
- bool enabled : 1;
-};
-
-#define IDEV_DEVICE_INIT(_vtable, _session) ((idev_device){ \
- .vtable = (_vtable), \
- .session = (_session), \
- })
-
-idev_device *idev_find_device(idev_session *s, const char *name);
-
-int idev_device_add(idev_device *d, const char *name);
-idev_device *idev_device_free(idev_device *d);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_device*, idev_device_free);
-
-int idev_device_feed(idev_device *d, idev_data *data);
-void idev_device_feedback(idev_device *d, idev_data *data);
-
-/*
- * Elements
- */
-
-struct idev_element_vtable {
- void (*free) (idev_element *e);
- void (*enable) (idev_element *e);
- void (*disable) (idev_element *e);
- void (*open) (idev_element *e);
- void (*close) (idev_element *e);
- void (*resume) (idev_element *e, int fd);
- void (*pause) (idev_element *e, const char *mode);
- void (*feedback) (idev_element *e, idev_data *data);
-};
-
-struct idev_element {
- const idev_element_vtable *vtable;
- idev_session *session;
- unsigned long n_open;
- char *name;
-
- LIST_HEAD(idev_link, links);
-
- bool enabled : 1;
- bool readable : 1;
- bool writable : 1;
-};
-
-#define IDEV_ELEMENT_INIT(_vtable, _session) ((idev_element){ \
- .vtable = (_vtable), \
- .session = (_session), \
- })
-
-idev_element *idev_find_element(idev_session *s, const char *name);
-
-int idev_element_add(idev_element *e, const char *name);
-idev_element *idev_element_free(idev_element *e);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_element*, idev_element_free);
-
-int idev_element_feed(idev_element *e, idev_data *data);
-void idev_element_feedback(idev_element *e, idev_data *data);
-
-/*
- * Sessions
- */
-
-struct idev_session {
- idev_context *context;
- char *name;
- char *path;
- sd_bus_slot *slot_resume_device;
- sd_bus_slot *slot_pause_device;
-
- Hashmap *element_map;
- Hashmap *device_map;
-
- idev_event_fn event_fn;
- void *userdata;
-
- bool custom : 1;
- bool managed : 1;
- bool enabled : 1;
-};
-
-idev_session *idev_find_session(idev_context *c, const char *name);
-int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data);
-
-/*
- * Contexts
- */
-
-struct idev_context {
- unsigned long ref;
- sd_event *event;
- sd_bus *sysbus;
-
- Hashmap *session_map;
- Hashmap *data_map;
-};
diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
deleted file mode 100644
index 93f49e9458..0000000000
--- a/src/libsystemd-terminal/idev-keyboard.c
+++ /dev/null
@@ -1,1159 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-compose.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "util.h"
-#include "bus-util.h"
-#include "idev.h"
-#include "idev-internal.h"
-#include "term-internal.h"
-
-typedef struct kbdtbl kbdtbl;
-typedef struct kbdmap kbdmap;
-typedef struct kbdctx kbdctx;
-typedef struct idev_keyboard idev_keyboard;
-
-struct kbdtbl {
- unsigned long ref;
- struct xkb_compose_table *xkb_compose_table;
-};
-
-struct kbdmap {
- unsigned long ref;
- struct xkb_keymap *xkb_keymap;
- xkb_mod_index_t modmap[IDEV_KBDMOD_CNT];
- xkb_led_index_t ledmap[IDEV_KBDLED_CNT];
-};
-
-struct kbdctx {
- unsigned long ref;
- idev_context *context;
- struct xkb_context *xkb_context;
- struct kbdmap *kbdmap;
- struct kbdtbl *kbdtbl;
-
- sd_bus_slot *slot_locale_props_changed;
- sd_bus_slot *slot_locale_get_all;
-
- char *locale_lang;
- char *locale_x11_model;
- char *locale_x11_layout;
- char *locale_x11_variant;
- char *locale_x11_options;
- char *last_x11_model;
- char *last_x11_layout;
- char *last_x11_variant;
- char *last_x11_options;
-};
-
-struct idev_keyboard {
- idev_device device;
- kbdctx *kbdctx;
- kbdmap *kbdmap;
- kbdtbl *kbdtbl;
-
- struct xkb_state *xkb_state;
- struct xkb_compose_state *xkb_compose;
-
- usec_t repeat_delay;
- usec_t repeat_rate;
- sd_event_source *repeat_timer;
-
- uint32_t n_syms;
- idev_data evdata;
- idev_data repdata;
- uint32_t *compose_res;
-
- bool repeating : 1;
-};
-
-#define keyboard_from_device(_d) container_of((_d), idev_keyboard, device)
-
-#define KBDCTX_KEY "keyboard.context" /* hashmap key for global kbdctx */
-#define KBDXKB_SHIFT (8) /* xkb shifts evdev key-codes by 8 */
-#define KBDKEY_UP (0) /* KEY UP event value */
-#define KBDKEY_DOWN (1) /* KEY DOWN event value */
-#define KBDKEY_REPEAT (2) /* KEY REPEAT event value */
-
-static const idev_device_vtable keyboard_vtable;
-
-static int keyboard_update_kbdmap(idev_keyboard *k);
-static int keyboard_update_kbdtbl(idev_keyboard *k);
-
-/*
- * Keyboard Compose Tables
- */
-
-static kbdtbl *kbdtbl_ref(kbdtbl *kt) {
- if (kt) {
- assert_return(kt->ref > 0, NULL);
- ++kt->ref;
- }
-
- return kt;
-}
-
-static kbdtbl *kbdtbl_unref(kbdtbl *kt) {
- if (!kt)
- return NULL;
-
- assert_return(kt->ref > 0, NULL);
-
- if (--kt->ref > 0)
- return NULL;
-
- xkb_compose_table_unref(kt->xkb_compose_table);
- free(kt);
-
- return 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdtbl*, kbdtbl_unref);
-
-static int kbdtbl_new_from_locale(kbdtbl **out, kbdctx *kc, const char *locale) {
- _cleanup_(kbdtbl_unrefp) kbdtbl *kt = NULL;
-
- assert_return(out, -EINVAL);
- assert_return(locale, -EINVAL);
-
- kt = new0(kbdtbl, 1);
- if (!kt)
- return -ENOMEM;
-
- kt->ref = 1;
-
- kt->xkb_compose_table = xkb_compose_table_new_from_locale(kc->xkb_context,
- locale,
- XKB_COMPOSE_COMPILE_NO_FLAGS);
- if (!kt->xkb_compose_table)
- return errno > 0 ? -errno : -EFAULT;
-
- *out = kt;
- kt = NULL;
- return 0;
-}
-
-/*
- * Keyboard Keymaps
- */
-
-static const char * const kbdmap_modmap[IDEV_KBDMOD_CNT] = {
- [IDEV_KBDMOD_IDX_SHIFT] = XKB_MOD_NAME_SHIFT,
- [IDEV_KBDMOD_IDX_CTRL] = XKB_MOD_NAME_CTRL,
- [IDEV_KBDMOD_IDX_ALT] = XKB_MOD_NAME_ALT,
- [IDEV_KBDMOD_IDX_LINUX] = XKB_MOD_NAME_LOGO,
- [IDEV_KBDMOD_IDX_CAPS] = XKB_MOD_NAME_CAPS,
-};
-
-static const char * const kbdmap_ledmap[IDEV_KBDLED_CNT] = {
- [IDEV_KBDLED_IDX_NUM] = XKB_LED_NAME_NUM,
- [IDEV_KBDLED_IDX_CAPS] = XKB_LED_NAME_CAPS,
- [IDEV_KBDLED_IDX_SCROLL] = XKB_LED_NAME_SCROLL,
-};
-
-static kbdmap *kbdmap_ref(kbdmap *km) {
- assert_return(km, NULL);
- assert_return(km->ref > 0, NULL);
-
- ++km->ref;
- return km;
-}
-
-static kbdmap *kbdmap_unref(kbdmap *km) {
- if (!km)
- return NULL;
-
- assert_return(km->ref > 0, NULL);
-
- if (--km->ref > 0)
- return NULL;
-
- xkb_keymap_unref(km->xkb_keymap);
- free(km);
-
- return 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdmap*, kbdmap_unref);
-
-static int kbdmap_new_from_names(kbdmap **out,
- kbdctx *kc,
- const char *model,
- const char *layout,
- const char *variant,
- const char *options) {
- _cleanup_(kbdmap_unrefp) kbdmap *km = NULL;
- struct xkb_rule_names rmlvo = { };
- unsigned int i;
-
- assert_return(out, -EINVAL);
-
- km = new0(kbdmap, 1);
- if (!km)
- return -ENOMEM;
-
- km->ref = 1;
-
- rmlvo.rules = "evdev";
- rmlvo.model = model;
- rmlvo.layout = layout;
- rmlvo.variant = variant;
- rmlvo.options = options;
-
- errno = 0;
- km->xkb_keymap = xkb_keymap_new_from_names(kc->xkb_context, &rmlvo, 0);
- if (!km->xkb_keymap)
- return errno > 0 ? -errno : -EFAULT;
-
- for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
- const char *t = kbdmap_modmap[i];
-
- if (t)
- km->modmap[i] = xkb_keymap_mod_get_index(km->xkb_keymap, t);
- else
- km->modmap[i] = XKB_MOD_INVALID;
- }
-
- for (i = 0; i < IDEV_KBDLED_CNT; ++i) {
- const char *t = kbdmap_ledmap[i];
-
- if (t)
- km->ledmap[i] = xkb_keymap_led_get_index(km->xkb_keymap, t);
- else
- km->ledmap[i] = XKB_LED_INVALID;
- }
-
- *out = km;
- km = NULL;
- return 0;
-}
-
-/*
- * Keyboard Context
- */
-
-static int kbdctx_refresh_compose_table(kbdctx *kc, const char *lang) {
- kbdtbl *kt;
- idev_session *s;
- idev_device *d;
- Iterator i, j;
- int r;
-
- if (!lang)
- lang = "C";
-
- if (streq_ptr(kc->locale_lang, lang))
- return 0;
-
- r = free_and_strdup(&kc->locale_lang, lang);
- if (r < 0)
- return r;
-
- log_debug("idev-keyboard: new default compose table: [ %s ]", lang);
-
- r = kbdtbl_new_from_locale(&kt, kc, lang);
- if (r < 0) {
- /* TODO: We need to catch the case where no compose-file is
- * available. xkb doesn't tell us so far.. so we must not treat
- * it as a hard-failure but just continue. Preferably, we want
- * xkb to tell us exactly whether compilation failed or whether
- * there is no compose file available for this locale. */
- log_debug_errno(r, "idev-keyboard: cannot load compose-table for '%s': %m",
- lang);
- r = 0;
- kt = NULL;
- }
-
- kbdtbl_unref(kc->kbdtbl);
- kc->kbdtbl = kt;
-
- HASHMAP_FOREACH(s, kc->context->session_map, i)
- HASHMAP_FOREACH(d, s->device_map, j)
- if (idev_is_keyboard(d))
- keyboard_update_kbdtbl(keyboard_from_device(d));
-
- return 0;
-}
-
-static void move_str(char **dest, char **src) {
- free(*dest);
- *dest = *src;
- *src = NULL;
-}
-
-static int kbdctx_refresh_keymap(kbdctx *kc) {
- idev_session *s;
- idev_device *d;
- Iterator i, j;
- kbdmap *km;
- int r;
-
- if (kc->kbdmap &&
- streq_ptr(kc->locale_x11_model, kc->last_x11_model) &&
- streq_ptr(kc->locale_x11_layout, kc->last_x11_layout) &&
- streq_ptr(kc->locale_x11_variant, kc->last_x11_variant) &&
- streq_ptr(kc->locale_x11_options, kc->last_x11_options))
- return 0 ;
-
- move_str(&kc->last_x11_model, &kc->locale_x11_model);
- move_str(&kc->last_x11_layout, &kc->locale_x11_layout);
- move_str(&kc->last_x11_variant, &kc->locale_x11_variant);
- move_str(&kc->last_x11_options, &kc->locale_x11_options);
-
- log_debug("idev-keyboard: new default keymap: [%s / %s / %s / %s]",
- kc->last_x11_model, kc->last_x11_layout, kc->last_x11_variant, kc->last_x11_options);
-
- /* TODO: add a fallback keymap that's compiled-in */
- r = kbdmap_new_from_names(&km, kc, kc->last_x11_model, kc->last_x11_layout,
- kc->last_x11_variant, kc->last_x11_options);
- if (r < 0)
- return log_debug_errno(r, "idev-keyboard: cannot create keymap from locale1: %m");
-
- kbdmap_unref(kc->kbdmap);
- kc->kbdmap = km;
-
- HASHMAP_FOREACH(s, kc->context->session_map, i)
- HASHMAP_FOREACH(d, s->device_map, j)
- if (idev_is_keyboard(d))
- keyboard_update_kbdmap(keyboard_from_device(d));
-
- return 0;
-}
-
-static int kbdctx_set_locale(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- kbdctx *kc = userdata;
- const char *s, *ctype = NULL, *lang = NULL;
- int r;
-
- r = sd_bus_message_enter_container(m, 'a', "s");
- if (r < 0)
- goto error;
-
- while ((r = sd_bus_message_read(m, "s", &s)) > 0) {
- if (!ctype)
- ctype = startswith(s, "LC_CTYPE=");
- if (!lang)
- lang = startswith(s, "LANG=");
- }
-
- if (r < 0)
- goto error;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- goto error;
-
- kbdctx_refresh_compose_table(kc, ctype ? : lang);
- r = 0;
-
-error:
- if (r < 0)
- log_debug_errno(r, "idev-keyboard: cannot parse locale property from locale1: %m");
-
- return r;
-}
-
-static const struct bus_properties_map kbdctx_locale_map[] = {
- { "Locale", "as", kbdctx_set_locale, 0 },
- { "X11Model", "s", NULL, offsetof(kbdctx, locale_x11_model) },
- { "X11Layout", "s", NULL, offsetof(kbdctx, locale_x11_layout) },
- { "X11Variant", "s", NULL, offsetof(kbdctx, locale_x11_variant) },
- { "X11Options", "s", NULL, offsetof(kbdctx, locale_x11_options) },
- { },
-};
-
-static int kbdctx_locale_get_all_fn(sd_bus_message *m,
- void *userdata,
- sd_bus_error *ret_err) {
- kbdctx *kc = userdata;
- int r;
-
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
- if (sd_bus_message_is_method_error(m, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(m);
-
- log_debug("idev-keyboard: GetAll() on locale1 failed: %s: %s",
- error->name, error->message);
- return 0;
- }
-
- r = bus_message_map_all_properties(m, kbdctx_locale_map, kc);
- if (r < 0) {
- log_debug("idev-keyboard: erroneous GetAll() reply from locale1");
- return 0;
- }
-
- kbdctx_refresh_keymap(kc);
- return 0;
-}
-
-static int kbdctx_query_locale(kbdctx *kc) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
- r = sd_bus_message_new_method_call(kc->context->sysbus,
- &m,
- "org.freedesktop.locale1",
- "/org/freedesktop/locale1",
- "org.freedesktop.DBus.Properties",
- "GetAll");
- if (r < 0)
- goto error;
-
- r = sd_bus_message_append(m, "s", "org.freedesktop.locale1");
- if (r < 0)
- goto error;
-
- r = sd_bus_call_async(kc->context->sysbus,
- &kc->slot_locale_get_all,
- m,
- kbdctx_locale_get_all_fn,
- kc,
- 0);
- if (r < 0)
- goto error;
-
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: cannot send GetAll to locale1: %m");
-}
-
-static int kbdctx_locale_props_changed_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_err) {
- kbdctx *kc = userdata;
- int r;
-
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
-
- /* skip interface name */
- r = sd_bus_message_skip(signal, "s");
- if (r < 0)
- goto error;
-
- r = bus_message_map_properties_changed(signal, kbdctx_locale_map, kc);
- if (r < 0)
- goto error;
-
- if (r > 0) {
- r = kbdctx_query_locale(kc);
- if (r < 0)
- return r;
- }
-
- kbdctx_refresh_keymap(kc);
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: cannot handle PropertiesChanged from locale1: %m");
-}
-
-static int kbdctx_setup_bus(kbdctx *kc) {
- int r;
-
- r = sd_bus_add_match(kc->context->sysbus,
- &kc->slot_locale_props_changed,
- "type='signal',"
- "sender='org.freedesktop.locale1',"
- "interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged',"
- "path='/org/freedesktop/locale1'",
- kbdctx_locale_props_changed_fn,
- kc);
- if (r < 0)
- return log_debug_errno(r, "idev-keyboard: cannot setup locale1 link: %m");
-
- return kbdctx_query_locale(kc);
-}
-
-static void kbdctx_log_fn(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
- char buf[LINE_MAX];
- int sd_lvl;
-
- if (lvl >= XKB_LOG_LEVEL_DEBUG)
- sd_lvl = LOG_DEBUG;
- else if (lvl >= XKB_LOG_LEVEL_INFO)
- sd_lvl = LOG_INFO;
- else if (lvl >= XKB_LOG_LEVEL_WARNING)
- sd_lvl = LOG_INFO; /* most XKB warnings really are informational */
- else
- /* XKB_LOG_LEVEL_ERROR and worse */
- sd_lvl = LOG_ERR;
-
- snprintf(buf, sizeof(buf), "idev-xkb: %s", format);
- log_internalv(sd_lvl, 0, __FILE__, __LINE__, __func__, buf, args);
-}
-
-static kbdctx *kbdctx_ref(kbdctx *kc) {
- assert_return(kc, NULL);
- assert_return(kc->ref > 0, NULL);
-
- ++kc->ref;
- return kc;
-}
-
-static kbdctx *kbdctx_unref(kbdctx *kc) {
- if (!kc)
- return NULL;
-
- assert_return(kc->ref > 0, NULL);
-
- if (--kc->ref > 0)
- return NULL;
-
- free(kc->last_x11_options);
- free(kc->last_x11_variant);
- free(kc->last_x11_layout);
- free(kc->last_x11_model);
- free(kc->locale_x11_options);
- free(kc->locale_x11_variant);
- free(kc->locale_x11_layout);
- free(kc->locale_x11_model);
- free(kc->locale_lang);
- kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
- kc->slot_locale_props_changed = sd_bus_slot_unref(kc->slot_locale_props_changed);
- kc->kbdtbl = kbdtbl_unref(kc->kbdtbl);
- kc->kbdmap = kbdmap_unref(kc->kbdmap);
- xkb_context_unref(kc->xkb_context);
- hashmap_remove_value(kc->context->data_map, KBDCTX_KEY, kc);
- free(kc);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(kbdctx*, kbdctx_unref);
-
-static int kbdctx_new(kbdctx **out, idev_context *c) {
- _cleanup_(kbdctx_unrefp) kbdctx *kc = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(c, -EINVAL);
-
- kc = new0(kbdctx, 1);
- if (!kc)
- return -ENOMEM;
-
- kc->ref = 1;
- kc->context = c;
-
- errno = 0;
- kc->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- if (!kc->xkb_context)
- return errno > 0 ? -errno : -EFAULT;
-
- xkb_context_set_log_fn(kc->xkb_context, kbdctx_log_fn);
- xkb_context_set_log_level(kc->xkb_context, XKB_LOG_LEVEL_DEBUG);
-
- r = kbdctx_refresh_keymap(kc);
- if (r < 0)
- return r;
-
- r = kbdctx_refresh_compose_table(kc, NULL);
- if (r < 0)
- return r;
-
- if (c->sysbus) {
- r = kbdctx_setup_bus(kc);
- if (r < 0)
- return r;
- }
-
- r = hashmap_put(c->data_map, KBDCTX_KEY, kc);
- if (r < 0)
- return r;
-
- *out = kc;
- kc = NULL;
- return 0;
-}
-
-static int get_kbdctx(idev_context *c, kbdctx **out) {
- kbdctx *kc;
-
- assert_return(c, -EINVAL);
- assert_return(out, -EINVAL);
-
- kc = hashmap_get(c->data_map, KBDCTX_KEY);
- if (kc) {
- *out = kbdctx_ref(kc);
- return 0;
- }
-
- return kbdctx_new(out, c);
-}
-
-/*
- * Keyboard Devices
- */
-
-bool idev_is_keyboard(idev_device *d) {
- return d && d->vtable == &keyboard_vtable;
-}
-
-idev_device *idev_find_keyboard(idev_session *s, const char *name) {
- char *kname;
-
- assert_return(s, NULL);
- assert_return(name, NULL);
-
- kname = strjoina("keyboard/", name);
- return hashmap_get(s->device_map, kname);
-}
-
-static int keyboard_raise_data(idev_keyboard *k, idev_data *data) {
- idev_device *d = &k->device;
- int r;
-
- r = idev_session_raise_device_data(d->session, d, data);
- if (r < 0)
- log_debug_errno(r, "idev-keyboard: %s/%s: error while raising data event: %m",
- d->session->name, d->name);
-
- return r;
-}
-
-static int keyboard_resize_bufs(idev_keyboard *k, uint32_t n_syms) {
- uint32_t *t;
-
- if (n_syms <= k->n_syms)
- return 0;
-
- t = realloc(k->compose_res, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->compose_res = t;
-
- t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->evdata.keyboard.keysyms = t;
-
- t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->evdata.keyboard.codepoints = t;
-
- t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->repdata.keyboard.keysyms = t;
-
- t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms);
- if (!t)
- return -ENOMEM;
- k->repdata.keyboard.codepoints = t;
-
- k->n_syms = n_syms;
- return 0;
-}
-
-static unsigned int keyboard_read_compose(idev_keyboard *k, const xkb_keysym_t **out) {
- _cleanup_free_ char *t = NULL;
- term_utf8 u8 = { };
- char buf[256], *p;
- size_t flen = 0;
- int i, r;
-
- r = xkb_compose_state_get_utf8(k->xkb_compose, buf, sizeof(buf));
- if (r >= (int)sizeof(buf)) {
- t = malloc(r + 1);
- if (!t)
- return 0;
-
- xkb_compose_state_get_utf8(k->xkb_compose, t, r + 1);
- p = t;
- } else {
- p = buf;
- }
-
- for (i = 0; i < r; ++i) {
- uint32_t *ucs;
- size_t len, j;
-
- len = term_utf8_decode(&u8, &ucs, p[i]);
- if (len > 0) {
- r = keyboard_resize_bufs(k, flen + len);
- if (r < 0)
- return 0;
-
- for (j = 0; j < len; ++j)
- k->compose_res[flen++] = ucs[j];
- }
- }
-
- *out = k->compose_res;
- return flen;
-}
-
-static void keyboard_arm(idev_keyboard *k, usec_t usecs) {
- int r;
-
- if (usecs != 0) {
- usecs += now(CLOCK_MONOTONIC);
- r = sd_event_source_set_time(k->repeat_timer, usecs);
- if (r >= 0)
- sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_ONESHOT);
- } else {
- sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
- }
-}
-
-static int keyboard_repeat_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
- idev_keyboard *k = userdata;
-
- /* never feed REPEAT keys into COMPOSE */
-
- keyboard_arm(k, k->repeat_rate);
- return keyboard_raise_data(k, &k->repdata);
-}
-
-int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
- _cleanup_(idev_device_freep) idev_device *d = NULL;
- idev_keyboard *k;
- char *kname;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(s, -EINVAL);
- assert_return(name, -EINVAL);
-
- k = new0(idev_keyboard, 1);
- if (!k)
- return -ENOMEM;
-
- d = &k->device;
- k->device = IDEV_DEVICE_INIT(&keyboard_vtable, s);
- k->repeat_delay = 250 * USEC_PER_MSEC;
- k->repeat_rate = 30 * USEC_PER_MSEC;
-
- /* TODO: add key-repeat configuration */
-
- r = get_kbdctx(s->context, &k->kbdctx);
- if (r < 0)
- return r;
-
- r = keyboard_update_kbdmap(k);
- if (r < 0)
- return r;
-
- r = keyboard_update_kbdtbl(k);
- if (r < 0)
- return r;
-
- r = keyboard_resize_bufs(k, 8);
- if (r < 0)
- return r;
-
- r = sd_event_add_time(s->context->event,
- &k->repeat_timer,
- CLOCK_MONOTONIC,
- 0,
- 10 * USEC_PER_MSEC,
- keyboard_repeat_timer_fn,
- k);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
- if (r < 0)
- return r;
-
- kname = strjoina("keyboard/", name);
- r = idev_device_add(d, kname);
- if (r < 0)
- return r;
-
- if (out)
- *out = d;
- d = NULL;
- return 0;
-}
-
-static void keyboard_free(idev_device *d) {
- idev_keyboard *k = keyboard_from_device(d);
-
- xkb_compose_state_unref(k->xkb_compose);
- xkb_state_unref(k->xkb_state);
- free(k->repdata.keyboard.codepoints);
- free(k->repdata.keyboard.keysyms);
- free(k->evdata.keyboard.codepoints);
- free(k->evdata.keyboard.keysyms);
- free(k->compose_res);
- k->repeat_timer = sd_event_source_unref(k->repeat_timer);
- k->kbdtbl = kbdtbl_unref(k->kbdtbl);
- k->kbdmap = kbdmap_unref(k->kbdmap);
- k->kbdctx = kbdctx_unref(k->kbdctx);
- free(k);
-}
-
-static int8_t guess_ascii(struct xkb_state *state, uint32_t code, uint32_t n_syms, const uint32_t *syms) {
- xkb_layout_index_t n_lo, lo;
- xkb_level_index_t lv;
- struct xkb_keymap *keymap;
- const xkb_keysym_t *s;
- int num;
-
- if (n_syms == 1 && syms[0] < 128 && syms[0] > 0)
- return syms[0];
-
- keymap = xkb_state_get_keymap(state);
- n_lo = xkb_keymap_num_layouts_for_key(keymap, code + KBDXKB_SHIFT);
-
- for (lo = 0; lo < n_lo; ++lo) {
- lv = xkb_state_key_get_level(state, code + KBDXKB_SHIFT, lo);
- num = xkb_keymap_key_get_syms_by_level(keymap, code + KBDXKB_SHIFT, lo, lv, &s);
- if (num == 1 && s[0] < 128 && s[0] > 0)
- return s[0];
- }
-
- return -1;
-}
-
-static int keyboard_fill(idev_keyboard *k,
- idev_data *dst,
- bool resync,
- uint16_t code,
- uint32_t value,
- uint32_t n_syms,
- const uint32_t *keysyms) {
- idev_data_keyboard *kev;
- uint32_t i;
- int r;
-
- assert(dst == &k->evdata || dst == &k->repdata);
-
- r = keyboard_resize_bufs(k, n_syms);
- if (r < 0)
- return r;
-
- dst->type = IDEV_DATA_KEYBOARD;
- dst->resync = resync;
- kev = &dst->keyboard;
- kev->ascii = guess_ascii(k->xkb_state, code, n_syms, keysyms);
- kev->value = value;
- kev->keycode = code;
- kev->mods = 0;
- kev->consumed_mods = 0;
- kev->n_syms = n_syms;
- memcpy(kev->keysyms, keysyms, sizeof(*keysyms) * n_syms);
-
- for (i = 0; i < n_syms; ++i) {
- kev->codepoints[i] = xkb_keysym_to_utf32(keysyms[i]);
- if (!kev->codepoints[i])
- kev->codepoints[i] = 0xffffffffUL;
- }
-
- for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
- if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
- continue;
-
- r = xkb_state_mod_index_is_active(k->xkb_state, k->kbdmap->modmap[i], XKB_STATE_MODS_EFFECTIVE);
- if (r > 0)
- kev->mods |= 1 << i;
-
- r = xkb_state_mod_index_is_consumed(k->xkb_state, code + KBDXKB_SHIFT, k->kbdmap->modmap[i]);
- if (r > 0)
- kev->consumed_mods |= 1 << i;
- }
-
- return 0;
-}
-
-static void keyboard_repeat(idev_keyboard *k) {
- idev_data *evdata = &k->evdata;
- idev_data *repdata = &k->repdata;
- idev_data_keyboard *evkbd = &evdata->keyboard;
- idev_data_keyboard *repkbd = &repdata->keyboard;
- const xkb_keysym_t *keysyms;
- idev_device *d = &k->device;
- bool repeats;
- int r, num;
-
- if (evdata->resync) {
- /*
- * We received a re-sync event. During re-sync, any number of
- * key-events may have been lost and sync-events may be
- * re-ordered. Always disable key-repeat for those events. Any
- * following event will trigger it again.
- */
-
- k->repeating = false;
- keyboard_arm(k, 0);
- return;
- }
-
- repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);
-
- if (k->repeating && repkbd->keycode == evkbd->keycode) {
- /*
- * We received an event for the key we currently repeat. If it
- * was released, stop key-repeat. Otherwise, ignore the event.
- */
-
- if (evkbd->value == KBDKEY_UP) {
- k->repeating = false;
- keyboard_arm(k, 0);
- }
- } else if (evkbd->value == KBDKEY_DOWN && repeats) {
- /*
- * We received a key-down event for a key that repeats. The
- * previous condition caught keys we already repeat, so we know
- * this is a different key or no key-repeat is running. Start
- * new key-repeat.
- */
-
- errno = 0;
- num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
- if (num < 0)
- r = errno > 0 ? errno : -EFAULT;
- else
- r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);
-
- if (r < 0) {
- log_debug_errno(r, "idev-keyboard: %s/%s: cannot set key-repeat: %m",
- d->session->name, d->name);
- k->repeating = false;
- keyboard_arm(k, 0);
- } else {
- k->repeating = true;
- keyboard_arm(k, k->repeat_delay);
- }
- } else if (k->repeating && !repeats) {
- /*
- * We received an event for a key that does not repeat, but we
- * currently repeat a previously received key. The new key is
- * usually a modifier, but might be any kind of key. In this
- * case, we continue repeating the old key, but update the
- * symbols according to the new state.
- */
-
- errno = 0;
- num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
- if (num < 0)
- r = errno > 0 ? errno : -EFAULT;
- else
- r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);
-
- if (r < 0) {
- log_debug_errno(r, "idev-keyboard: %s/%s: cannot update key-repeat: %m",
- d->session->name, d->name);
- k->repeating = false;
- keyboard_arm(k, 0);
- }
- }
-}
-
-static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
- struct input_event *ev = &data->evdev.event;
- enum xkb_state_component compch;
- enum xkb_compose_status cstatus;
- const xkb_keysym_t *keysyms;
- idev_device *d = &k->device;
- int num, r;
-
- if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
- return 0;
-
- /* TODO: We should audit xkb-actions, whether they need @resync as
- * flag. Most actions should just be executed, however, there might
- * be actions that depend on modifier-orders. Those should be
- * suppressed. */
-
- num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
- compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);
-
- if (compch & XKB_STATE_LEDS) {
- /* TODO: update LEDs */
- }
-
- if (num < 0) {
- r = num;
- goto error;
- }
-
- if (k->xkb_compose && ev->value == KBDKEY_DOWN) {
- if (num == 1 && !data->resync) {
- xkb_compose_state_feed(k->xkb_compose, keysyms[0]);
- cstatus = xkb_compose_state_get_status(k->xkb_compose);
- } else {
- cstatus = XKB_COMPOSE_CANCELLED;
- }
-
- switch (cstatus) {
- case XKB_COMPOSE_NOTHING:
- /* keep produced keysyms and forward unchanged */
- break;
- case XKB_COMPOSE_COMPOSING:
- /* consumed by compose-state, drop keysym */
- keysyms = NULL;
- num = 0;
- break;
- case XKB_COMPOSE_COMPOSED:
- /* compose-state produced sth, replace keysym */
- num = keyboard_read_compose(k, &keysyms);
- xkb_compose_state_reset(k->xkb_compose);
- break;
- case XKB_COMPOSE_CANCELLED:
- /* canceled compose, reset, forward cancellation sym */
- xkb_compose_state_reset(k->xkb_compose);
- break;
- }
- } else if (k->xkb_compose &&
- num == 1 &&
- keysyms[0] == XKB_KEY_Multi_key &&
- !data->resync &&
- ev->value == KBDKEY_UP) {
- /* Reset compose state on Multi-Key UP events. This effectively
- * requires you to hold the key during the whole sequence. I
- * think it's pretty handy to avoid accidental
- * Compose-sequences, but this may break Compose for disabled
- * people. We really need to make this opional! (TODO) */
- xkb_compose_state_reset(k->xkb_compose);
- }
-
- if (ev->value == KBDKEY_UP) {
- /* never produce keysyms for UP */
- keysyms = NULL;
- num = 0;
- }
-
- r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
- if (r < 0)
- goto error;
-
- keyboard_repeat(k);
- return keyboard_raise_data(k, &k->evdata);
-
-error:
- log_debug_errno(r, "idev-keyboard: %s/%s: cannot handle event: %m",
- d->session->name, d->name);
- k->repeating = false;
- keyboard_arm(k, 0);
- return 0;
-}
-
-static int keyboard_feed(idev_device *d, idev_data *data) {
- idev_keyboard *k = keyboard_from_device(d);
-
- switch (data->type) {
- case IDEV_DATA_RESYNC:
- /*
- * If the underlying device is re-synced, key-events might be
- * sent re-ordered. Thus, we don't know which key was pressed
- * last. Key-repeat might get confused, hence, disable it
- * during re-syncs. The first following event will enable it
- * again.
- */
-
- k->repeating = false;
- keyboard_arm(k, 0);
- return 0;
- case IDEV_DATA_EVDEV:
- return keyboard_feed_evdev(k, data);
- default:
- return 0;
- }
-}
-
-static int keyboard_update_kbdmap(idev_keyboard *k) {
- idev_device *d = &k->device;
- struct xkb_state *state;
- kbdmap *km;
- int r;
-
- assert(k);
-
- km = k->kbdctx->kbdmap;
- if (km == k->kbdmap)
- return 0;
-
- errno = 0;
- state = xkb_state_new(km->xkb_keymap);
- if (!state) {
- r = errno > 0 ? -errno : -EFAULT;
- goto error;
- }
-
- kbdmap_unref(k->kbdmap);
- k->kbdmap = kbdmap_ref(km);
- xkb_state_unref(k->xkb_state);
- k->xkb_state = state;
-
- /* TODO: On state-change, we should trigger a resync so the whole
- * event-state is flushed into the new xkb-state. libevdev currently
- * does not support that, though. */
-
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new keymap: %m",
- d->session->name, d->name);
-}
-
-static int keyboard_update_kbdtbl(idev_keyboard *k) {
- idev_device *d = &k->device;
- struct xkb_compose_state *compose = NULL;
- kbdtbl *kt;
- int r;
-
- assert(k);
-
- kt = k->kbdctx->kbdtbl;
- if (kt == k->kbdtbl)
- return 0;
-
- if (kt) {
- errno = 0;
- compose = xkb_compose_state_new(kt->xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
- if (!compose) {
- r = errno > 0 ? -errno : -EFAULT;
- goto error;
- }
- }
-
- kbdtbl_unref(k->kbdtbl);
- k->kbdtbl = kbdtbl_ref(kt);
- xkb_compose_state_unref(k->xkb_compose);
- k->xkb_compose = compose;
-
- return 0;
-
-error:
- return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new compose table: %m",
- d->session->name, d->name);
-}
-
-static const idev_device_vtable keyboard_vtable = {
- .free = keyboard_free,
- .feed = keyboard_feed,
-};
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
deleted file mode 100644
index b92a393b69..0000000000
--- a/src/libsystemd-terminal/idev.c
+++ /dev/null
@@ -1,799 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "login-util.h"
-#include "macro.h"
-#include "util.h"
-#include "idev.h"
-#include "idev-internal.h"
-
-static void element_open(idev_element *e);
-static void element_close(idev_element *e);
-
-/*
- * Devices
- */
-
-idev_device *idev_find_device(idev_session *s, const char *name) {
- assert_return(s, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(s->device_map, name);
-}
-
-int idev_device_add(idev_device *d, const char *name) {
- int r;
-
- assert_return(d, -EINVAL);
- assert_return(d->vtable, -EINVAL);
- assert_return(d->session, -EINVAL);
- assert_return(name, -EINVAL);
-
- d->name = strdup(name);
- if (!d->name)
- return -ENOMEM;
-
- r = hashmap_put(d->session->device_map, d->name, d);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-idev_device *idev_device_free(idev_device *d) {
- idev_device tmp;
-
- if (!d)
- return NULL;
-
- assert(!d->enabled);
- assert(!d->public);
- assert(!d->links);
- assert(d->vtable);
- assert(d->vtable->free);
-
- if (d->name)
- hashmap_remove_value(d->session->device_map, d->name, d);
-
- tmp = *d;
- d->vtable->free(d);
-
- free(tmp.name);
-
- return NULL;
-}
-
-int idev_device_feed(idev_device *d, idev_data *data) {
- assert(d);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- if (d->vtable->feed)
- return d->vtable->feed(d, data);
- else
- return 0;
-}
-
-void idev_device_feedback(idev_device *d, idev_data *data) {
- idev_link *l;
-
- assert(d);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- LIST_FOREACH(links_by_device, l, d->links)
- idev_element_feedback(l->element, data);
-}
-
-static void device_attach(idev_device *d, idev_link *l) {
- assert(d);
- assert(l);
-
- if (d->vtable->attach)
- d->vtable->attach(d, l);
-
- if (d->enabled)
- element_open(l->element);
-}
-
-static void device_detach(idev_device *d, idev_link *l) {
- assert(d);
- assert(l);
-
- if (d->enabled)
- element_close(l->element);
-
- if (d->vtable->detach)
- d->vtable->detach(d, l);
-}
-
-void idev_device_enable(idev_device *d) {
- idev_link *l;
-
- assert(d);
-
- if (!d->enabled) {
- d->enabled = true;
- LIST_FOREACH(links_by_device, l, d->links)
- element_open(l->element);
- }
-}
-
-void idev_device_disable(idev_device *d) {
- idev_link *l;
-
- assert(d);
-
- if (d->enabled) {
- d->enabled = false;
- LIST_FOREACH(links_by_device, l, d->links)
- element_close(l->element);
- }
-}
-
-/*
- * Elements
- */
-
-idev_element *idev_find_element(idev_session *s, const char *name) {
- assert_return(s, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(s->element_map, name);
-}
-
-int idev_element_add(idev_element *e, const char *name) {
- int r;
-
- assert_return(e, -EINVAL);
- assert_return(e->vtable, -EINVAL);
- assert_return(e->session, -EINVAL);
- assert_return(name, -EINVAL);
-
- e->name = strdup(name);
- if (!e->name)
- return -ENOMEM;
-
- r = hashmap_put(e->session->element_map, e->name, e);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-idev_element *idev_element_free(idev_element *e) {
- idev_element tmp;
-
- if (!e)
- return NULL;
-
- assert(!e->enabled);
- assert(!e->links);
- assert(e->n_open == 0);
- assert(e->vtable);
- assert(e->vtable->free);
-
- if (e->name)
- hashmap_remove_value(e->session->element_map, e->name, e);
-
- tmp = *e;
- e->vtable->free(e);
-
- free(tmp.name);
-
- return NULL;
-}
-
-int idev_element_feed(idev_element *e, idev_data *data) {
- int r, error = 0;
- idev_link *l;
-
- assert(e);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- LIST_FOREACH(links_by_element, l, e->links) {
- r = idev_device_feed(l->device, data);
- if (r != 0)
- error = r;
- }
-
- return error;
-}
-
-void idev_element_feedback(idev_element *e, idev_data *data) {
- assert(e);
- assert(data);
- assert(data->type < IDEV_DATA_CNT);
-
- if (e->vtable->feedback)
- e->vtable->feedback(e, data);
-}
-
-static void element_open(idev_element *e) {
- assert(e);
-
- if (e->n_open++ == 0 && e->vtable->open)
- e->vtable->open(e);
-}
-
-static void element_close(idev_element *e) {
- assert(e);
- assert(e->n_open > 0);
-
- if (--e->n_open == 0 && e->vtable->close)
- e->vtable->close(e);
-}
-
-static void element_enable(idev_element *e) {
- assert(e);
-
- if (!e->enabled) {
- e->enabled = true;
- if (e->vtable->enable)
- e->vtable->enable(e);
- }
-}
-
-static void element_disable(idev_element *e) {
- assert(e);
-
- if (e->enabled) {
- e->enabled = false;
- if (e->vtable->disable)
- e->vtable->disable(e);
- }
-}
-
-static void element_resume(idev_element *e, int fd) {
- assert(e);
- assert(fd >= 0);
-
- if (e->vtable->resume)
- e->vtable->resume(e, fd);
-}
-
-static void element_pause(idev_element *e, const char *mode) {
- assert(e);
- assert(mode);
-
- if (e->vtable->pause)
- e->vtable->pause(e, mode);
-}
-
-/*
- * Sessions
- */
-
-static int session_raise(idev_session *s, idev_event *ev) {
- return s->event_fn(s, s->userdata, ev);
-}
-
-static int session_raise_device_add(idev_session *s, idev_device *d) {
- idev_event event = {
- .type = IDEV_EVENT_DEVICE_ADD,
- .device_add = {
- .device = d,
- },
- };
-
- return session_raise(s, &event);
-}
-
-static int session_raise_device_remove(idev_session *s, idev_device *d) {
- idev_event event = {
- .type = IDEV_EVENT_DEVICE_REMOVE,
- .device_remove = {
- .device = d,
- },
- };
-
- return session_raise(s, &event);
-}
-
-int idev_session_raise_device_data(idev_session *s, idev_device *d, idev_data *data) {
- idev_event event = {
- .type = IDEV_EVENT_DEVICE_DATA,
- .device_data = {
- .device = d,
- .data = *data,
- },
- };
-
- return session_raise(s, &event);
-}
-
-static int session_add_device(idev_session *s, idev_device *d) {
- int r;
-
- assert(s);
- assert(d);
-
- log_debug("idev: %s: add device '%s'", s->name, d->name);
-
- d->public = true;
- r = session_raise_device_add(s, d);
- if (r != 0) {
- d->public = false;
- goto error;
- }
-
- return 0;
-
-error:
- if (r < 0)
- log_debug_errno(r, "idev: %s: error while adding device '%s': %m",
- s->name, d->name);
- return r;
-}
-
-static int session_remove_device(idev_session *s, idev_device *d) {
- int r, error = 0;
-
- assert(s);
- assert(d);
-
- log_debug("idev: %s: remove device '%s'", s->name, d->name);
-
- d->public = false;
- r = session_raise_device_remove(s, d);
- if (r != 0)
- error = r;
-
- idev_device_disable(d);
-
- if (error < 0)
- log_debug_errno(error, "idev: %s: error while removing device '%s': %m",
- s->name, d->name);
- idev_device_free(d);
- return error;
-}
-
-static int session_add_element(idev_session *s, idev_element *e) {
- assert(s);
- assert(e);
-
- log_debug("idev: %s: add element '%s'", s->name, e->name);
-
- if (s->enabled)
- element_enable(e);
-
- return 0;
-}
-
-static int session_remove_element(idev_session *s, idev_element *e) {
- int r, error = 0;
- idev_device *d;
- idev_link *l;
-
- assert(s);
- assert(e);
-
- log_debug("idev: %s: remove element '%s'", s->name, e->name);
-
- while ((l = e->links)) {
- d = l->device;
- LIST_REMOVE(links_by_device, d->links, l);
- LIST_REMOVE(links_by_element, e->links, l);
- device_detach(d, l);
-
- if (!d->links) {
- r = session_remove_device(s, d);
- if (r != 0)
- error = r;
- }
-
- l->device = NULL;
- l->element = NULL;
- free(l);
- }
-
- element_disable(e);
-
- if (error < 0)
- log_debug_errno(r, "idev: %s: error while removing element '%s': %m",
- s->name, e->name);
- idev_element_free(e);
- return error;
-}
-
-idev_session *idev_find_session(idev_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->session_map, name);
-}
-
-static int session_resume_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- idev_session *s = userdata;
- idev_element *e;
- uint32_t major, minor;
- int r, fd;
-
- r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
- if (r < 0) {
- log_debug("idev: %s: erroneous ResumeDevice signal", s->name);
- return 0;
- }
-
- e = idev_find_evdev(s, makedev(major, minor));
- if (!e)
- return 0;
-
- element_resume(e, fd);
- return 0;
-}
-
-static int session_pause_device_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- idev_session *s = userdata;
- idev_element *e;
- uint32_t major, minor;
- const char *mode;
- int r;
-
- r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
- if (r < 0) {
- log_debug("idev: %s: erroneous PauseDevice signal", s->name);
- return 0;
- }
-
- e = idev_find_evdev(s, makedev(major, minor));
- if (!e)
- return 0;
-
- element_pause(e, mode);
- return 0;
-}
-
-static int session_setup_bus(idev_session *s) {
- _cleanup_free_ char *match = NULL;
- int r;
-
- if (!s->managed)
- return 0;
-
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='ResumeDevice',"
- "path='", s->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(s->context->sysbus,
- &s->slot_resume_device,
- match,
- session_resume_device_fn,
- s);
- if (r < 0)
- return r;
-
- free(match);
- match = strjoin("type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Session',"
- "member='PauseDevice',"
- "path='", s->path, "'",
- NULL);
- if (!match)
- return -ENOMEM;
-
- r = sd_bus_add_match(s->context->sysbus,
- &s->slot_pause_device,
- match,
- session_pause_device_fn,
- s);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int idev_session_new(idev_session **out,
- idev_context *c,
- unsigned int flags,
- const char *name,
- idev_event_fn event_fn,
- void *userdata) {
- _cleanup_(idev_session_freep) idev_session *s = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(c, -EINVAL);
- assert_return(name, -EINVAL);
- assert_return(event_fn, -EINVAL);
- assert_return((flags & IDEV_SESSION_CUSTOM) == !session_id_valid(name), -EINVAL);
- assert_return(!(flags & IDEV_SESSION_CUSTOM) || !(flags & IDEV_SESSION_MANAGED), -EINVAL);
- assert_return(!(flags & IDEV_SESSION_MANAGED) || c->sysbus, -EINVAL);
-
- s = new0(idev_session, 1);
- if (!s)
- return -ENOMEM;
-
- s->context = idev_context_ref(c);
- s->custom = flags & IDEV_SESSION_CUSTOM;
- s->managed = flags & IDEV_SESSION_MANAGED;
- s->event_fn = event_fn;
- s->userdata = userdata;
-
- s->name = strdup(name);
- if (!s->name)
- return -ENOMEM;
-
- if (s->managed) {
- r = sd_bus_path_encode("/org/freedesktop/login1/session", s->name, &s->path);
- if (r < 0)
- return r;
- }
-
- s->element_map = hashmap_new(&string_hash_ops);
- if (!s->element_map)
- return -ENOMEM;
-
- s->device_map = hashmap_new(&string_hash_ops);
- if (!s->device_map)
- return -ENOMEM;
-
- r = session_setup_bus(s);
- if (r < 0)
- return r;
-
- r = hashmap_put(c->session_map, s->name, s);
- if (r < 0)
- return r;
-
- *out = s;
- s = NULL;
- return 0;
-}
-
-idev_session *idev_session_free(idev_session *s) {
- idev_element *e;
-
- if (!s)
- return NULL;
-
- while ((e = hashmap_first(s->element_map)))
- session_remove_element(s, e);
-
- assert(hashmap_size(s->device_map) == 0);
-
- if (s->name)
- hashmap_remove_value(s->context->session_map, s->name, s);
-
- s->slot_pause_device = sd_bus_slot_unref(s->slot_pause_device);
- s->slot_resume_device = sd_bus_slot_unref(s->slot_resume_device);
- s->context = idev_context_unref(s->context);
- hashmap_free(s->device_map);
- hashmap_free(s->element_map);
- free(s->path);
- free(s->name);
- free(s);
-
- return NULL;
-}
-
-bool idev_session_is_enabled(idev_session *s) {
- return s && s->enabled;
-}
-
-void idev_session_enable(idev_session *s) {
- idev_element *e;
- Iterator i;
-
- assert(s);
-
- if (!s->enabled) {
- s->enabled = true;
- HASHMAP_FOREACH(e, s->element_map, i)
- element_enable(e);
- }
-}
-
-void idev_session_disable(idev_session *s) {
- idev_element *e;
- Iterator i;
-
- assert(s);
-
- if (s->enabled) {
- s->enabled = false;
- HASHMAP_FOREACH(e, s->element_map, i)
- element_disable(e);
- }
-}
-
-static int add_link(idev_element *e, idev_device *d) {
- idev_link *l;
-
- assert(e);
- assert(d);
-
- l = new0(idev_link, 1);
- if (!l)
- return -ENOMEM;
-
- l->element = e;
- l->device = d;
- LIST_PREPEND(links_by_element, e->links, l);
- LIST_PREPEND(links_by_device, d->links, l);
- device_attach(d, l);
-
- return 0;
-}
-
-static int guess_type(struct udev_device *d) {
- const char *id_key;
-
- id_key = udev_device_get_property_value(d, "ID_INPUT_KEY");
- if (streq_ptr(id_key, "1"))
- return IDEV_DEVICE_KEYBOARD;
-
- return IDEV_DEVICE_CNT;
-}
-
-int idev_session_add_evdev(idev_session *s, struct udev_device *ud) {
- idev_element *e;
- idev_device *d;
- dev_t devnum;
- int r, type;
-
- assert_return(s, -EINVAL);
- assert_return(ud, -EINVAL);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return 0;
-
- e = idev_find_evdev(s, devnum);
- if (e)
- return 0;
-
- r = idev_evdev_new(&e, s, ud);
- if (r < 0)
- return r;
-
- r = session_add_element(s, e);
- if (r != 0)
- return r;
-
- type = guess_type(ud);
- if (type < 0)
- return type;
-
- switch (type) {
- case IDEV_DEVICE_KEYBOARD:
- d = idev_find_keyboard(s, e->name);
- if (d) {
- log_debug("idev: %s: keyboard for new evdev element '%s' already available",
- s->name, e->name);
- return 0;
- }
-
- r = idev_keyboard_new(&d, s, e->name);
- if (r < 0)
- return r;
-
- r = add_link(e, d);
- if (r < 0) {
- idev_device_free(d);
- return r;
- }
-
- return session_add_device(s, d);
- default:
- /* unknown elements are silently ignored */
- return 0;
- }
-}
-
-int idev_session_remove_evdev(idev_session *s, struct udev_device *ud) {
- idev_element *e;
- dev_t devnum;
-
- assert(s);
- assert(ud);
-
- devnum = udev_device_get_devnum(ud);
- if (devnum == 0)
- return 0;
-
- e = idev_find_evdev(s, devnum);
- if (!e)
- return 0;
-
- return session_remove_element(s, e);
-}
-
-/*
- * Contexts
- */
-
-int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus) {
- _cleanup_(idev_context_unrefp) idev_context *c = NULL;
-
- assert_return(out, -EINVAL);
- assert_return(event, -EINVAL);
-
- c = new0(idev_context, 1);
- if (!c)
- return -ENOMEM;
-
- c->ref = 1;
- c->event = sd_event_ref(event);
-
- if (sysbus)
- c->sysbus = sd_bus_ref(sysbus);
-
- c->session_map = hashmap_new(&string_hash_ops);
- if (!c->session_map)
- return -ENOMEM;
-
- c->data_map = hashmap_new(&string_hash_ops);
- if (!c->data_map)
- return -ENOMEM;
-
- *out = c;
- c = NULL;
- return 0;
-}
-
-static void context_cleanup(idev_context *c) {
- assert(hashmap_size(c->data_map) == 0);
- assert(hashmap_size(c->session_map) == 0);
-
- hashmap_free(c->data_map);
- hashmap_free(c->session_map);
- c->sysbus = sd_bus_unref(c->sysbus);
- c->event = sd_event_unref(c->event);
- free(c);
-}
-
-idev_context *idev_context_ref(idev_context *c) {
- assert_return(c, NULL);
- assert_return(c->ref > 0, NULL);
-
- ++c->ref;
- return c;
-}
-
-idev_context *idev_context_unref(idev_context *c) {
- if (!c)
- return NULL;
-
- assert_return(c->ref > 0, NULL);
-
- if (--c->ref == 0)
- context_cleanup(c);
-
- return NULL;
-}
diff --git a/src/libsystemd-terminal/idev.h b/src/libsystemd-terminal/idev.h
deleted file mode 100644
index 241677cbbe..0000000000
--- a/src/libsystemd-terminal/idev.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * IDev
- */
-
-#pragma once
-
-#include <libudev.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <xkbcommon/xkbcommon.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-
-typedef struct idev_data idev_data;
-typedef struct idev_data_evdev idev_data_evdev;
-typedef struct idev_data_keyboard idev_data_keyboard;
-
-typedef struct idev_event idev_event;
-typedef struct idev_device idev_device;
-typedef struct idev_session idev_session;
-typedef struct idev_context idev_context;
-
-/*
- * Types
- */
-
-enum {
- IDEV_ELEMENT_EVDEV,
- IDEV_ELEMENT_CNT
-};
-
-enum {
- IDEV_DEVICE_KEYBOARD,
- IDEV_DEVICE_CNT
-};
-
-/*
- * Evdev Elements
- */
-
-struct idev_data_evdev {
- struct input_event event;
-};
-
-/*
- * Keyboard Devices
- */
-
-struct xkb_state;
-
-enum {
- IDEV_KBDMOD_IDX_SHIFT,
- IDEV_KBDMOD_IDX_CTRL,
- IDEV_KBDMOD_IDX_ALT,
- IDEV_KBDMOD_IDX_LINUX,
- IDEV_KBDMOD_IDX_CAPS,
- IDEV_KBDMOD_CNT,
-
- IDEV_KBDMOD_SHIFT = 1 << IDEV_KBDMOD_IDX_SHIFT,
- IDEV_KBDMOD_CTRL = 1 << IDEV_KBDMOD_IDX_CTRL,
- IDEV_KBDMOD_ALT = 1 << IDEV_KBDMOD_IDX_ALT,
- IDEV_KBDMOD_LINUX = 1 << IDEV_KBDMOD_IDX_LINUX,
- IDEV_KBDMOD_CAPS = 1 << IDEV_KBDMOD_IDX_CAPS,
-};
-
-enum {
- IDEV_KBDLED_IDX_NUM,
- IDEV_KBDLED_IDX_CAPS,
- IDEV_KBDLED_IDX_SCROLL,
- IDEV_KBDLED_CNT,
-
- IDEV_KBDLED_NUM = 1 << IDEV_KBDLED_IDX_NUM,
- IDEV_KBDLED_CAPS = 1 << IDEV_KBDLED_IDX_CAPS,
- IDEV_KBDLED_SCROLL = 1 << IDEV_KBDLED_IDX_SCROLL,
-};
-
-struct idev_data_keyboard {
- struct xkb_state *xkb_state;
- int8_t ascii;
- uint8_t value;
- uint16_t keycode;
- uint32_t mods;
- uint32_t consumed_mods;
- uint32_t n_syms;
- uint32_t *keysyms;
- uint32_t *codepoints;
-};
-
-static inline bool idev_kbdmatch(idev_data_keyboard *kdata,
- uint32_t mods, uint32_t n_syms,
- const uint32_t *syms) {
- const uint32_t significant = IDEV_KBDMOD_SHIFT |
- IDEV_KBDMOD_CTRL |
- IDEV_KBDMOD_ALT |
- IDEV_KBDMOD_LINUX;
- uint32_t real;
-
- if (n_syms != kdata->n_syms)
- return false;
-
- real = kdata->mods & ~kdata->consumed_mods & significant;
- if (real != mods)
- return false;
-
- return !memcmp(syms, kdata->keysyms, n_syms * sizeof(*syms));
-}
-
-#define IDEV_KBDMATCH(_kdata, _mods, _sym) \
- idev_kbdmatch((_kdata), (_mods), 1, (const uint32_t[]){ (_sym) })
-
-/*
- * Data Packets
- */
-
-enum {
- IDEV_DATA_RESYNC,
- IDEV_DATA_EVDEV,
- IDEV_DATA_KEYBOARD,
- IDEV_DATA_CNT
-};
-
-struct idev_data {
- unsigned int type;
- bool resync : 1;
-
- union {
- idev_data_evdev evdev;
- idev_data_keyboard keyboard;
- };
-};
-
-/*
- * Events
- */
-
-enum {
- IDEV_EVENT_DEVICE_ADD,
- IDEV_EVENT_DEVICE_REMOVE,
- IDEV_EVENT_DEVICE_DATA,
- IDEV_EVENT_CNT
-};
-
-struct idev_event {
- unsigned int type;
- union {
- struct {
- idev_device *device;
- } device_add, device_remove;
-
- struct {
- idev_device *device;
- idev_data data;
- } device_data;
- };
-};
-
-typedef int (*idev_event_fn) (idev_session *s, void *userdata, idev_event *ev);
-
-/*
- * Devices
- */
-
-void idev_device_enable(idev_device *d);
-void idev_device_disable(idev_device *d);
-
-/*
- * Sessions
- */
-
-enum {
- IDEV_SESSION_CUSTOM = (1 << 0),
- IDEV_SESSION_MANAGED = (1 << 1),
-};
-
-int idev_session_new(idev_session **out,
- idev_context *c,
- unsigned int flags,
- const char *name,
- idev_event_fn event_fn,
- void *userdata);
-idev_session *idev_session_free(idev_session *s);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_session*, idev_session_free);
-
-bool idev_session_is_enabled(idev_session *s);
-void idev_session_enable(idev_session *s);
-void idev_session_disable(idev_session *s);
-
-int idev_session_add_evdev(idev_session *s, struct udev_device *ud);
-int idev_session_remove_evdev(idev_session *s, struct udev_device *ud);
-
-/*
- * Contexts
- */
-
-int idev_context_new(idev_context **out, sd_event *event, sd_bus *sysbus);
-idev_context *idev_context_ref(idev_context *c);
-idev_context *idev_context_unref(idev_context *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(idev_context*, idev_context_unref);
diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
deleted file mode 100644
index 790a244772..0000000000
--- a/src/libsystemd-terminal/modeset.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Modeset Testing
- * The modeset tool attaches to the session of the caller and shows a
- * test-pattern on all displays of this session. It is meant as debugging tool
- * for the grdev infrastructure.
- */
-
-#include <drm_fourcc.h>
-#include <errno.h>
-#include <getopt.h>
-#include <linux/kd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <termios.h>
-#include <unistd.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "build.h"
-#include "macro.h"
-#include "random-util.h"
-#include "signal-util.h"
-#include "util.h"
-#include "grdev.h"
-#include "sysview.h"
-
-typedef struct Modeset Modeset;
-
-struct Modeset {
- char *session;
- char *seat;
- sd_event *event;
- sd_bus *bus;
- sd_event_source *exit_src;
- sysview_context *sysview;
- grdev_context *grdev;
- grdev_session *grdev_session;
-
- uint8_t r, g, b;
- bool r_up, g_up, b_up;
-
- bool my_tty : 1;
- bool managed : 1;
-};
-
-static int modeset_exit_fn(sd_event_source *source, void *userdata) {
- Modeset *m = userdata;
-
- if (m->grdev_session)
- grdev_session_restore(m->grdev_session);
-
- return 0;
-}
-
-static Modeset *modeset_free(Modeset *m) {
- if (!m)
- return NULL;
-
- m->grdev_session = grdev_session_free(m->grdev_session);
- m->grdev = grdev_context_unref(m->grdev);
- m->sysview = sysview_context_free(m->sysview);
- m->exit_src = sd_event_source_unref(m->exit_src);
- m->bus = sd_bus_unref(m->bus);
- m->event = sd_event_unref(m->event);
- free(m->seat);
- free(m->session);
- free(m);
-
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Modeset*, modeset_free);
-
-static bool is_my_tty(const char *session) {
- unsigned int vtnr;
- struct stat st;
- long mode;
- int r;
-
- /* Using logind's Controller API is highly fragile if there is already
- * a session controller running. If it is registered as controller
- * itself, TakeControl will simply fail. But if its a legacy controller
- * that does not use logind's controller API, we must never register
- * our own controller. Otherwise, we really mess up the VT. Therefore,
- * only run in managed mode if there's no-one else. Furthermore, never
- * try to access graphics devices if there's someone else. Unlike input
- * devices, graphics devies cannot be shared easily. */
-
- if (!isatty(1))
- return false;
-
- if (!session)
- return false;
-
- r = sd_session_get_vt(session, &vtnr);
- if (r < 0 || vtnr < 1 || vtnr > 63)
- return false;
-
- mode = 0;
- r = ioctl(1, KDGETMODE, &mode);
- if (r < 0 || mode != KD_TEXT)
- return false;
-
- r = fstat(1, &st);
- if (r < 0 || minor(st.st_rdev) != vtnr)
- return false;
-
- return true;
-}
-
-static int modeset_new(Modeset **out) {
- _cleanup_(modeset_freep) Modeset *m = NULL;
- int r;
-
- assert(out);
-
- m = new0(Modeset, 1);
- if (!m)
- return log_oom();
-
- r = sd_pid_get_session(getpid(), &m->session);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve logind session: %m");
-
- r = sd_session_get_seat(m->session, &m->seat);
- if (r < 0)
- return log_error_errno(r, "Cannot retrieve seat of logind session: %m");
-
- m->my_tty = is_my_tty(m->session);
- m->managed = m->my_tty && geteuid() > 0;
-
- m->r = rand() % 0xff;
- m->g = rand() % 0xff;
- m->b = rand() % 0xff;
- m->r_up = m->g_up = m->b_up = true;
-
- r = sd_event_default(&m->event);
- if (r < 0)
- return r;
-
- r = sd_bus_open_system(&m->bus);
- if (r < 0)
- return r;
-
- r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return r;
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
- if (r < 0)
- return r;
-
- r = sd_event_add_exit(m->event, &m->exit_src, modeset_exit_fn, m);
- if (r < 0)
- return r;
-
- /* schedule before sd-bus close */
- r = sd_event_source_set_priority(m->exit_src, -10);
- if (r < 0)
- return r;
-
- r = sysview_context_new(&m->sysview,
- SYSVIEW_CONTEXT_SCAN_LOGIND |
- SYSVIEW_CONTEXT_SCAN_DRM,
- m->event,
- m->bus,
- NULL);
- if (r < 0)
- return r;
-
- r = grdev_context_new(&m->grdev, m->event, m->bus);
- if (r < 0)
- return r;
-
- *out = m;
- m = NULL;
- return 0;
-}
-
-static uint8_t next_color(bool *up, uint8_t cur, unsigned int mod) {
- uint8_t next;
-
- /* generate smoothly morphing colors */
-
- next = cur + (*up ? 1 : -1) * (rand() % mod);
- if ((*up && next < cur) || (!*up && next > cur)) {
- *up = !*up;
- next = cur;
- }
-
- return next;
-}
-
-static void modeset_draw(Modeset *m, const grdev_display_target *t) {
- uint32_t j, k, *b;
- uint8_t *l;
-
- assert(t->back->format == DRM_FORMAT_XRGB8888 || t->back->format == DRM_FORMAT_ARGB8888);
- assert(!t->rotate);
- assert(!t->flip);
-
- l = t->back->maps[0];
- for (j = 0; j < t->height; ++j) {
- for (k = 0; k < t->width; ++k) {
- b = (uint32_t*)l;
- b[k] = (0xff << 24) | (m->r << 16) | (m->g << 8) | m->b;
- }
-
- l += t->back->strides[0];
- }
-}
-
-static void modeset_render(Modeset *m, grdev_display *d) {
- const grdev_display_target *t;
-
- m->r = next_color(&m->r_up, m->r, 4);
- m->g = next_color(&m->g_up, m->g, 3);
- m->b = next_color(&m->b_up, m->b, 2);
-
- GRDEV_DISPLAY_FOREACH_TARGET(d, t) {
- modeset_draw(m, t);
- grdev_display_flip_target(d, t);
- }
-
- grdev_session_commit(m->grdev_session);
-}
-
-static void modeset_grdev_fn(grdev_session *session, void *userdata, grdev_event *ev) {
- Modeset *m = userdata;
-
- switch (ev->type) {
- case GRDEV_EVENT_DISPLAY_ADD:
- grdev_display_enable(ev->display_add.display);
- break;
- case GRDEV_EVENT_DISPLAY_REMOVE:
- break;
- case GRDEV_EVENT_DISPLAY_CHANGE:
- break;
- case GRDEV_EVENT_DISPLAY_FRAME:
- modeset_render(m, ev->display_frame.display);
- break;
- }
-}
-
-static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
- unsigned int flags, type;
- Modeset *m = userdata;
- sysview_device *d;
- const char *name;
- int r;
-
- switch (ev->type) {
- case SYSVIEW_EVENT_SESSION_FILTER:
- if (streq_ptr(m->session, ev->session_filter.id))
- return 1;
-
- break;
- case SYSVIEW_EVENT_SESSION_ADD:
- assert(!m->grdev_session);
-
- name = sysview_session_get_name(ev->session_add.session);
- flags = 0;
-
- if (m->managed)
- flags |= GRDEV_SESSION_MANAGED;
-
- r = grdev_session_new(&m->grdev_session,
- m->grdev,
- flags,
- name,
- modeset_grdev_fn,
- m);
- if (r < 0)
- return log_error_errno(r, "Cannot create grdev session: %m");
-
- if (m->managed) {
- r = sysview_session_take_control(ev->session_add.session);
- if (r < 0)
- return log_error_errno(r, "Cannot request session control: %m");
- }
-
- grdev_session_enable(m->grdev_session);
-
- break;
- case SYSVIEW_EVENT_SESSION_REMOVE:
- if (!m->grdev_session)
- return 0;
-
- grdev_session_restore(m->grdev_session);
- grdev_session_disable(m->grdev_session);
- m->grdev_session = grdev_session_free(m->grdev_session);
- if (sd_event_get_exit_code(m->event, &r) == -ENODATA)
- sd_event_exit(m->event, 0);
- break;
- case SYSVIEW_EVENT_SESSION_ATTACH:
- d = ev->session_attach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_DRM)
- grdev_session_add_drm(m->grdev_session, sysview_device_get_ud(d));
-
- break;
- case SYSVIEW_EVENT_SESSION_DETACH:
- d = ev->session_detach.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_DRM)
- grdev_session_remove_drm(m->grdev_session, sysview_device_get_ud(d));
-
- break;
- case SYSVIEW_EVENT_SESSION_REFRESH:
- d = ev->session_refresh.device;
- type = sysview_device_get_type(d);
- if (type == SYSVIEW_DEVICE_DRM)
- grdev_session_hotplug_drm(m->grdev_session, ev->session_refresh.ud);
-
- break;
- case SYSVIEW_EVENT_SESSION_CONTROL:
- r = ev->session_control.error;
- if (r < 0)
- return log_error_errno(r, "Cannot acquire session control: %m");
-
- r = ioctl(1, KDSKBMODE, K_UNICODE);
- if (r < 0)
- return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m");
-
- break;
- }
-
- return 0;
-}
-
-static int modeset_run(Modeset *m) {
- struct termios in_attr, saved_attr;
- int r;
-
- assert(m);
-
- if (!m->my_tty) {
- log_warning("You need to run this program on a free VT");
- return -EACCES;
- }
-
- if (!m->managed && geteuid() > 0)
- log_warning("You run in unmanaged mode without being root. This is likely to fail..");
-
- printf("modeset - Show test pattern on selected graphics devices\n"
- " Running on seat '%s' in user-session '%s'\n"
- " Exit by pressing ^C\n\n",
- m->seat ? : "seat0", m->session ? : "<none>");
-
- r = sysview_context_start(m->sysview, modeset_sysview_fn, m);
- if (r < 0)
- goto out;
-
- r = tcgetattr(0, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- saved_attr = in_attr;
- in_attr.c_lflag &= ~ECHO;
-
- r = tcsetattr(0, TCSANOW, &in_attr);
- if (r < 0) {
- r = -errno;
- goto out;
- }
-
- r = sd_event_loop(m->event);
- tcsetattr(0, TCSANOW, &saved_attr);
- printf("exiting..\n");
-
-out:
- sysview_context_stop(m->sysview);
- return r;
-}
-
-static int help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Show test pattern on all selected graphics devices.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- , program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- };
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- {},
- };
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
- switch (c) {
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
-
- if (argc > optind) {
- log_error("Too many arguments");
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_(modeset_freep) Modeset *m = NULL;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- initialize_srand();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- r = modeset_new(&m);
- if (r < 0)
- goto finish;
-
- r = modeset_run(m);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/libsystemd-terminal/subterm.c b/src/libsystemd-terminal/subterm.c
deleted file mode 100644
index 5f12540111..0000000000
--- a/src/libsystemd-terminal/subterm.c
+++ /dev/null
@@ -1,981 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Stacked Terminal-Emulator
- * This is an interactive test of the term_screen implementation. It runs a
- * fully-fletched terminal-emulator inside of a parent TTY. That is, instead of
- * rendering the terminal as X11-window, it renders it as sub-window in the
- * parent TTY. Think of this like what "GNU-screen" does.
- */
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include "sd-event.h"
-#include "macro.h"
-#include "pty.h"
-#include "ring.h"
-#include "signal-util.h"
-#include "utf8.h"
-#include "util.h"
-#include "term-internal.h"
-
-typedef struct Output Output;
-typedef struct Terminal Terminal;
-
-struct Output {
- int fd;
- unsigned int width;
- unsigned int height;
- unsigned int in_width;
- unsigned int in_height;
- unsigned int cursor_x;
- unsigned int cursor_y;
-
- char obuf[4096];
- size_t n_obuf;
-
- bool resized : 1;
- bool in_menu : 1;
-};
-
-struct Terminal {
- sd_event *event;
- sd_event_source *frame_timer;
- Output *output;
- term_utf8 utf8;
- term_parser *parser;
- term_screen *screen;
-
- int in_fd;
- int out_fd;
- struct termios saved_in_attr;
- struct termios saved_out_attr;
-
- Pty *pty;
- Ring out_ring;
-
- bool is_scheduled : 1;
- bool is_dirty : 1;
- bool is_menu : 1;
-};
-
-/*
- * Output Handling
- */
-
-#define BORDER_HORIZ "\xe2\x94\x81"
-#define BORDER_VERT "\xe2\x94\x83"
-#define BORDER_VERT_RIGHT "\xe2\x94\xa3"
-#define BORDER_VERT_LEFT "\xe2\x94\xab"
-#define BORDER_DOWN_RIGHT "\xe2\x94\x8f"
-#define BORDER_DOWN_LEFT "\xe2\x94\x93"
-#define BORDER_UP_RIGHT "\xe2\x94\x97"
-#define BORDER_UP_LEFT "\xe2\x94\x9b"
-
-static int output_winch(Output *o) {
- struct winsize wsz = { };
- int r;
-
- assert_return(o, -EINVAL);
-
- r = ioctl(o->fd, TIOCGWINSZ, &wsz);
- if (r < 0)
- return log_error_errno(errno, "error: cannot read window-size: %m");
-
- if (wsz.ws_col != o->width || wsz.ws_row != o->height) {
- o->width = wsz.ws_col;
- o->height = wsz.ws_row;
- o->in_width = MAX(o->width, 2U) - 2;
- o->in_height = MAX(o->height, 6U) - 6;
- o->resized = true;
- }
-
- return 0;
-}
-
-static int output_flush(Output *o) {
- int r;
-
- if (o->n_obuf < 1)
- return 0;
-
- r = loop_write(o->fd, o->obuf, o->n_obuf, false);
- if (r < 0)
- return log_error_errno(r, "error: cannot write to TTY: %m");
-
- o->n_obuf = 0;
-
- return 0;
-}
-
-static int output_write(Output *o, const void *buf, size_t size) {
- ssize_t len;
- int r;
-
- assert_return(o, -EINVAL);
- assert_return(buf || size < 1, -EINVAL);
-
- if (size < 1)
- return 0;
-
- if (o->n_obuf + size > o->n_obuf && o->n_obuf + size <= sizeof(o->obuf)) {
- memcpy(o->obuf + o->n_obuf, buf, size);
- o->n_obuf += size;
- return 0;
- }
-
- r = output_flush(o);
- if (r < 0)
- return r;
-
- len = loop_write(o->fd, buf, size, false);
- if (len < 0)
- return log_error_errno(len, "error: cannot write to TTY (%zd): %m", len);
-
- return 0;
-}
-
-_printf_(3,0)
-static int output_vnprintf(Output *o, size_t max, const char *format, va_list args) {
- char buf[max];
- int r;
-
- assert_return(o, -EINVAL);
- assert_return(format, -EINVAL);
- assert_return(max <= 4096, -EINVAL);
-
- r = MIN(vsnprintf(buf, max, format, args), (int) max);
-
- return output_write(o, buf, r);
-}
-
-_printf_(3,4)
-static int output_nprintf(Output *o, size_t max, const char *format, ...) {
- va_list args;
- int r;
-
- va_start(args, format);
- r = output_vnprintf(o, max, format, args);
- va_end(args);
-
- return r;
-}
-
-_printf_(2,0)
-static int output_vprintf(Output *o, const char *format, va_list args) {
- char buf[4096];
- int r;
-
- assert_return(o, -EINVAL);
- assert_return(format, -EINVAL);
-
- r = vsnprintf(buf, sizeof(buf), format, args);
-
- assert_return(r < (ssize_t)sizeof(buf), -ENOBUFS);
-
- return output_write(o, buf, r);
-}
-
-_printf_(2,3)
-static int output_printf(Output *o, const char *format, ...) {
- va_list args;
- int r;
-
- va_start(args, format);
- r = output_vprintf(o, format, args);
- va_end(args);
-
- return r;
-}
-
-static int output_move_to(Output *o, unsigned int x, unsigned int y) {
- int r;
-
- assert_return(o, -EINVAL);
-
- /* force the \e[H code as o->cursor_x/y might be out-of-date */
-
- r = output_printf(o, "\e[%u;%uH", y + 1, x + 1);
- if (r < 0)
- return r;
-
- o->cursor_x = x;
- o->cursor_y = y;
- return 0;
-}
-
-static int output_print_line(Output *o, size_t len) {
- const char line[] =
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ
- BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ BORDER_HORIZ;
- const size_t max = (sizeof(line) - 1) / (sizeof(BORDER_HORIZ) - 1);
- size_t i;
- int r = 0;
-
- assert_return(o, -EINVAL);
-
- for ( ; len > 0; len -= i) {
- i = (len > max) ? max : len;
- r = output_write(o, line, i * (sizeof(BORDER_HORIZ) - 1));
- if (r < 0)
- break;
- }
-
- return r;
-}
-
-_printf_(2,3)
-static int output_frame_printl(Output *o, const char *format, ...) {
- va_list args;
- int r;
-
- assert(o);
- assert(format);
-
- /* out of frame? */
- if (o->cursor_y < 3 || o->cursor_y >= o->height - 3)
- return 0;
-
- va_start(args, format);
- r = output_vnprintf(o, o->width - 2, format, args);
- va_end(args);
-
- if (r < 0)
- return r;
-
- return output_move_to(o, 1, o->cursor_y + 1);
-}
-
-static Output *output_free(Output *o) {
- if (!o)
- return NULL;
-
- /* re-enable cursor */
- output_printf(o, "\e[?25h");
- /* disable alternate screen buffer */
- output_printf(o, "\e[?1049l");
- output_flush(o);
-
- /* o->fd is owned by the caller */
- free(o);
-
- return NULL;
-}
-
-static int output_new(Output **out, int fd) {
- Output *o;
- int r;
-
- assert_return(out, -EINVAL);
-
- o = new0(Output, 1);
- if (!o)
- return log_oom();
-
- o->fd = fd;
-
- r = output_winch(o);
- if (r < 0)
- goto error;
-
- /* enable alternate screen buffer */
- r = output_printf(o, "\e[?1049h");
- if (r < 0)
- goto error;
-
- /* always hide cursor */
- r = output_printf(o, "\e[?25l");
- if (r < 0)
- goto error;
-
- r = output_flush(o);
- if (r < 0)
- goto error;
-
- *out = o;
- return 0;
-
-error:
- output_free(o);
- return r;
-}
-
-static void output_draw_frame(Output *o) {
- unsigned int i;
-
- assert(o);
-
- /* print header-frame */
-
- output_printf(o, BORDER_DOWN_RIGHT);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_DOWN_LEFT
- "\r\n"
- BORDER_VERT
- "\e[2;%uH" /* cursor-position: 2/x */
- BORDER_VERT
- "\r\n"
- BORDER_VERT_RIGHT,
- o->width);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_VERT_LEFT
- "\r\n");
-
- /* print body-frame */
-
- for (i = 0; i < o->in_height; ++i) {
- output_printf(o, BORDER_VERT
- "\e[%u;%uH" /* cursor-position: 2/x */
- BORDER_VERT
- "\r\n",
- i + 4, o->width);
- }
-
- /* print footer-frame */
-
- output_printf(o, BORDER_VERT_RIGHT);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_VERT_LEFT
- "\r\n"
- BORDER_VERT
- "\e[%u;%uH" /* cursor-position: 2/x */
- BORDER_VERT
- "\r\n"
- BORDER_UP_RIGHT,
- o->height - 1, o->width);
- output_print_line(o, o->width - 2);
- output_printf(o, BORDER_UP_LEFT);
-
- /* print header/footer text */
-
- output_printf(o, "\e[2;3H");
- output_nprintf(o, o->width - 4, "systemd - embedded terminal emulator");
- output_printf(o, "\e[%u;3H", o->height - 1);
- output_nprintf(o, o->width - 4, "press ^C to enter menu");
-}
-
-static void output_draw_menu(Output *o) {
- assert(o);
-
- output_frame_printl(o, "%s", "");
- output_frame_printl(o, " Menu: (the following keys are recognized)");
- output_frame_printl(o, " q: quit");
- output_frame_printl(o, " ^C: send ^C to the PTY");
-}
-
-static int output_draw_cell_fn(term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width) {
- Output *o = userdata;
- size_t k, ulen;
- char utf8[4];
-
- if (x >= o->in_width || y >= o->in_height)
- return 0;
-
- if (x == 0 && y != 0)
- output_printf(o, "\e[m\r\n" BORDER_VERT);
-
- switch (attr->fg.ccode) {
- case TERM_CCODE_DEFAULT:
- output_printf(o, "\e[39m");
- break;
- case TERM_CCODE_256:
- output_printf(o, "\e[38;5;%um", attr->fg.c256);
- break;
- case TERM_CCODE_RGB:
- output_printf(o, "\e[38;2;%u;%u;%um", attr->fg.red, attr->fg.green, attr->fg.blue);
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
- output_printf(o, "\e[%um", attr->fg.ccode - TERM_CCODE_BLACK + 30);
- break;
- case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
- output_printf(o, "\e[%um", attr->fg.ccode - TERM_CCODE_LIGHT_BLACK + 90);
- break;
- }
-
- switch (attr->bg.ccode) {
- case TERM_CCODE_DEFAULT:
- output_printf(o, "\e[49m");
- break;
- case TERM_CCODE_256:
- output_printf(o, "\e[48;5;%um", attr->bg.c256);
- break;
- case TERM_CCODE_RGB:
- output_printf(o, "\e[48;2;%u;%u;%um", attr->bg.red, attr->bg.green, attr->bg.blue);
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_WHITE:
- output_printf(o, "\e[%um", attr->bg.ccode - TERM_CCODE_BLACK + 40);
- break;
- case TERM_CCODE_LIGHT_BLACK ... TERM_CCODE_LIGHT_WHITE:
- output_printf(o, "\e[%um", attr->bg.ccode - TERM_CCODE_LIGHT_BLACK + 100);
- break;
- }
-
- output_printf(o, "\e[%u;%u;%u;%u;%u;%um",
- attr->bold ? 1 : 22,
- attr->italic ? 3 : 23,
- attr->underline ? 4 : 24,
- attr->inverse ? 7 : 27,
- attr->blink ? 5 : 25,
- attr->hidden ? 8 : 28);
-
- if (n_ch < 1) {
- output_printf(o, " ");
- } else {
- for (k = 0; k < n_ch; ++k) {
- ulen = utf8_encode_unichar(utf8, ch[k]);
- output_write(o, utf8, ulen);
- }
- }
-
- return 0;
-}
-
-static void output_draw_screen(Output *o, term_screen *s) {
- assert(o);
- assert(s);
-
- term_screen_draw(s, output_draw_cell_fn, o, NULL);
-
- output_printf(o, "\e[m");
-}
-
-static void output_draw(Output *o, bool menu, term_screen *screen) {
- assert(o);
-
- /*
- * This renders the contenst of the terminal. The layout contains a
- * header, the main body and a footer. Around all areas we draw a
- * border. It looks something like this:
- *
- * +----------------------------------------------------+
- * | Header |
- * +----------------------------------------------------+
- * | |
- * | |
- * | |
- * | Body |
- * | |
- * | |
- * ~ ~
- * ~ ~
- * +----------------------------------------------------+
- * | Footer |
- * +----------------------------------------------------+
- *
- * The body is the part that grows vertically.
- *
- * We need at least 6 vertical lines to render the screen. This would
- * leave 0 lines for the body. Therefore, we require 7 lines so there's
- * at least one body line. Similarly, we need 2 horizontal cells for the
- * frame, so we require 3.
- * If the window is too small, we print an error message instead.
- */
-
- if (o->in_width < 1 || o->in_height < 1) {
- output_printf(o, "\e[2J" /* erase-in-display: whole screen */
- "\e[H"); /* cursor-position: home */
- output_printf(o, "error: screen too small, need at least 3x7 cells");
- output_flush(o);
- return;
- }
-
- /* hide cursor */
- output_printf(o, "\e[?25l");
-
- /* frame-content is contant; only resizes can change it */
- if (o->resized || o->in_menu != menu) {
- output_printf(o, "\e[2J" /* erase-in-display: whole screen */
- "\e[H"); /* cursor-position: home */
- output_draw_frame(o);
- o->resized = false;
- o->in_menu = menu;
- }
-
- /* move cursor to child's position */
- output_move_to(o, 1, 3);
-
- if (menu)
- output_draw_menu(o);
- else
- output_draw_screen(o, screen);
-
- /*
- * Hack: sd-term was not written to support TTY as output-objects, thus
- * expects callers to use term_screen_feed_keyboard(). However, we
- * forward TTY input directly. Hence, we're not notified about keypad
- * changes. Update the related modes djring redraw to keep them at least
- * in sync.
- */
- if (screen->flags & TERM_FLAG_CURSOR_KEYS)
- output_printf(o, "\e[?1h");
- else
- output_printf(o, "\e[?1l");
-
- if (screen->flags & TERM_FLAG_KEYPAD_MODE)
- output_printf(o, "\e=");
- else
- output_printf(o, "\e>");
-
- output_flush(o);
-}
-
-/*
- * Terminal Handling
- */
-
-static void terminal_dirty(Terminal *t) {
- usec_t usec;
- int r;
-
- assert(t);
-
- if (t->is_scheduled) {
- t->is_dirty = true;
- return;
- }
-
- /* 16ms timer */
- r = sd_event_now(t->event, CLOCK_MONOTONIC, &usec);
- assert(r >= 0);
-
- usec += 16 * USEC_PER_MSEC;
- r = sd_event_source_set_time(t->frame_timer, usec);
- if (r >= 0) {
- r = sd_event_source_set_enabled(t->frame_timer, SD_EVENT_ONESHOT);
- if (r >= 0)
- t->is_scheduled = true;
- }
-
- t->is_dirty = false;
- output_draw(t->output, t->is_menu, t->screen);
-}
-
-static int terminal_frame_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
- Terminal *t = userdata;
-
- t->is_scheduled = false;
- if (t->is_dirty)
- terminal_dirty(t);
-
- return 0;
-}
-
-static int terminal_winch_fn(sd_event_source *source, const struct signalfd_siginfo *ssi, void *userdata) {
- Terminal *t = userdata;
- int r;
-
- output_winch(t->output);
-
- if (t->pty) {
- r = pty_resize(t->pty, t->output->in_width, t->output->in_height);
- if (r < 0)
- log_error_errno(r, "error: pty_resize() (%d): %m", r);
- }
-
- r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height);
- if (r < 0)
- log_error_errno(r, "error: term_screen_resize() (%d): %m", r);
-
- terminal_dirty(t);
-
- return 0;
-}
-
-static int terminal_push_tmp(Terminal *t, uint32_t ucs4) {
- char buf[4];
- size_t len;
- int r;
-
- assert(t);
-
- len = utf8_encode_unichar(buf, ucs4);
- if (len < 1)
- return 0;
-
- r = ring_push(&t->out_ring, buf, len);
- if (r < 0)
- log_oom();
-
- return r;
-}
-
-static int terminal_write_tmp(Terminal *t) {
- struct iovec vec[2];
- size_t num, i;
- int r;
-
- assert(t);
-
- num = ring_peek(&t->out_ring, vec);
- if (num < 1)
- return 0;
-
- if (t->pty) {
- for (i = 0; i < num; ++i) {
- r = pty_write(t->pty, vec[i].iov_base, vec[i].iov_len);
- if (r < 0)
- return log_error_errno(r, "error: cannot write to PTY (%d): %m", r);
- }
- }
-
- ring_flush(&t->out_ring);
- return 0;
-}
-
-static void terminal_discard_tmp(Terminal *t) {
- assert(t);
-
- ring_flush(&t->out_ring);
-}
-
-static int terminal_menu(Terminal *t, const term_seq *seq) {
- switch (seq->type) {
- case TERM_SEQ_IGNORE:
- break;
- case TERM_SEQ_GRAPHIC:
- switch (seq->terminator) {
- case 'q':
- sd_event_exit(t->event, 0);
- return 0;
- }
-
- break;
- case TERM_SEQ_CONTROL:
- switch (seq->terminator) {
- case 0x03:
- terminal_push_tmp(t, 0x03);
- terminal_write_tmp(t);
- break;
- }
-
- break;
- }
-
- t->is_menu = false;
- terminal_dirty(t);
-
- return 0;
-}
-
-static int terminal_io_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- Terminal *t = userdata;
- char buf[4096];
- ssize_t len, i;
- int r, type;
-
- len = read(fd, buf, sizeof(buf));
- if (len < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- log_error_errno(errno, "error: cannot read from TTY (%d): %m", -errno);
- return -errno;
- }
-
- for (i = 0; i < len; ++i) {
- const term_seq *seq;
- uint32_t *str;
- size_t n_str, j;
-
- n_str = term_utf8_decode(&t->utf8, &str, buf[i]);
- for (j = 0; j < n_str; ++j) {
- type = term_parser_feed(t->parser, &seq, str[j]);
- if (type < 0)
- return log_error_errno(type, "error: term_parser_feed() (%d): %m", type);
-
- if (!t->is_menu) {
- r = terminal_push_tmp(t, str[j]);
- if (r < 0)
- return r;
- }
-
- if (type == TERM_SEQ_NONE) {
- /* We only intercept one-char sequences, so in
- * case term_parser_feed() couldn't parse a
- * sequence, it is waiting for more data. We
- * know it can never be a one-char sequence
- * then, so we can safely forward the data.
- * This avoids withholding ESC or other values
- * that may be one-shot depending on the
- * application. */
- r = terminal_write_tmp(t);
- if (r < 0)
- return r;
- } else if (t->is_menu) {
- r = terminal_menu(t, seq);
- if (r < 0)
- return r;
- } else if (seq->type == TERM_SEQ_CONTROL && seq->terminator == 0x03) { /* ^C opens the menu */
- terminal_discard_tmp(t);
- t->is_menu = true;
- terminal_dirty(t);
- } else {
- r = terminal_write_tmp(t);
- if (r < 0)
- return r;
- }
- }
- }
-
- return 0;
-}
-
-static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size) {
- Terminal *t = userdata;
- int r;
-
- switch (event) {
- case PTY_CHILD:
- sd_event_exit(t->event, 0);
- break;
- case PTY_DATA:
- r = term_screen_feed_text(t->screen, ptr, size);
- if (r < 0)
- return log_error_errno(r, "error: term_screen_feed_text() (%d): %m", r);
-
- terminal_dirty(t);
- break;
- }
-
- return 0;
-}
-
-static int terminal_write_fn(term_screen *screen, void *userdata, const void *buf, size_t size) {
- Terminal *t = userdata;
- int r;
-
- if (!t->pty)
- return 0;
-
- r = ring_push(&t->out_ring, buf, size);
- if (r < 0)
- log_oom();
-
- return r;
-}
-
-static int terminal_cmd_fn(term_screen *screen, void *userdata, unsigned int cmd, const term_seq *seq) {
- return 0;
-}
-
-static Terminal *terminal_free(Terminal *t) {
- if (!t)
- return NULL;
-
- ring_clear(&t->out_ring);
- term_screen_unref(t->screen);
- term_parser_free(t->parser);
- output_free(t->output);
- sd_event_source_unref(t->frame_timer);
- sd_event_unref(t->event);
- tcsetattr(t->in_fd, TCSANOW, &t->saved_in_attr);
- tcsetattr(t->out_fd, TCSANOW, &t->saved_out_attr);
- free(t);
-
- return NULL;
-}
-
-static int terminal_new(Terminal **out, int in_fd, int out_fd) {
- struct termios in_attr, out_attr;
- Terminal *t;
- int r;
-
- assert_return(out, -EINVAL);
-
- r = tcgetattr(in_fd, &in_attr);
- if (r < 0)
- return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno);
-
- r = tcgetattr(out_fd, &out_attr);
- if (r < 0)
- return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno);
-
- t = new0(Terminal, 1);
- if (!t)
- return log_oom();
-
- t->in_fd = in_fd;
- t->out_fd = out_fd;
- memcpy(&t->saved_in_attr, &in_attr, sizeof(in_attr));
- memcpy(&t->saved_out_attr, &out_attr, sizeof(out_attr));
-
- cfmakeraw(&in_attr);
- cfmakeraw(&out_attr);
-
- r = tcsetattr(t->in_fd, TCSANOW, &in_attr);
- if (r < 0) {
- log_error_errno(r, "error: tcsetattr() (%d): %m", r);
- goto error;
- }
-
- r = tcsetattr(t->out_fd, TCSANOW, &out_attr);
- if (r < 0) {
- log_error_errno(r, "error: tcsetattr() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_default(&t->event);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_default() (%d): %m", r);
- goto error;
- }
-
- r = sigprocmask_many(SIG_BLOCK, NULL, SIGINT, SIGQUIT, SIGTERM, SIGWINCH, SIGCHLD, -1);
- if (r < 0) {
- log_error_errno(r, "error: sigprocmask_many() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGINT, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGQUIT, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGTERM, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_signal(t->event, NULL, SIGWINCH, terminal_winch_fn, t);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r);
- goto error;
- }
-
- /* force initial redraw on event-loop enter */
- t->is_dirty = true;
- r = sd_event_add_time(t->event, &t->frame_timer, CLOCK_MONOTONIC, 0, 0, terminal_frame_timer_fn, t);
- if (r < 0) {
- log_error_errno(r, "error: sd_event_add_time() (%d): %m", r);
- goto error;
- }
-
- r = output_new(&t->output, out_fd);
- if (r < 0)
- goto error;
-
- r = term_parser_new(&t->parser, true);
- if (r < 0)
- goto error;
-
- r = term_screen_new(&t->screen, terminal_write_fn, t, terminal_cmd_fn, t);
- if (r < 0)
- goto error;
-
- r = term_screen_set_answerback(t->screen, "systemd-subterm");
- if (r < 0)
- goto error;
-
- r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height);
- if (r < 0) {
- log_error_errno(r, "error: term_screen_resize() (%d): %m", r);
- goto error;
- }
-
- r = sd_event_add_io(t->event, NULL, in_fd, EPOLLIN, terminal_io_fn, t);
- if (r < 0)
- goto error;
-
- *out = t;
- return 0;
-
-error:
- terminal_free(t);
- return r;
-}
-
-static int terminal_run(Terminal *t) {
- pid_t pid;
-
- assert_return(t, -EINVAL);
-
- pid = pty_fork(&t->pty, t->event, terminal_pty_fn, t, t->output->in_width, t->output->in_height);
- if (pid < 0)
- return log_error_errno(pid, "error: cannot fork PTY (%d): %m", pid);
- else if (pid == 0) {
- /* child */
-
- char **argv = (char*[]){
- (char*)getenv("SHELL") ? : (char*)_PATH_BSHELL,
- NULL
- };
-
- setenv("TERM", "xterm-256color", 1);
- setenv("COLORTERM", "systemd-subterm", 1);
-
- execve(argv[0], argv, environ);
- log_error_errno(errno, "error: cannot exec %s (%d): %m", argv[0], -errno);
- _exit(1);
- }
-
- /* parent */
-
- return sd_event_loop(t->event);
-}
-
-/*
- * Context Handling
- */
-
-int main(int argc, char *argv[]) {
- Terminal *t = NULL;
- int r;
-
- r = terminal_new(&t, 0, 1);
- if (r < 0)
- goto out;
-
- r = terminal_run(t);
- if (r < 0)
- goto out;
-
-out:
- if (r < 0)
- log_error_errno(r, "error: terminal failed (%d): %m", r);
- terminal_free(t);
- return -r;
-}
diff --git a/src/libsystemd-terminal/sysview-internal.h b/src/libsystemd-terminal/sysview-internal.h
deleted file mode 100644
index 251c8d7300..0000000000
--- a/src/libsystemd-terminal/sysview-internal.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-#include "macro.h"
-#include "util.h"
-#include "sysview.h"
-
-/*
- * Devices
- */
-
-struct sysview_device {
- sysview_seat *seat;
- char *name;
- unsigned int type;
-
- union {
- struct {
- struct udev_device *ud;
- } evdev, drm;
- };
-};
-
-sysview_device *sysview_find_device(sysview_context *c, const char *name);
-
-int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name);
-sysview_device *sysview_device_free(sysview_device *device);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_device*, sysview_device_free);
-
-/*
- * Sessions
- */
-
-struct sysview_session {
- sysview_seat *seat;
- char *name;
- char *path;
- void *userdata;
-
- sd_bus_slot *slot_take_control;
-
- bool custom : 1;
- bool public : 1;
- bool wants_control : 1;
- bool has_control : 1;
-};
-
-sysview_session *sysview_find_session(sysview_context *c, const char *name);
-
-int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name);
-sysview_session *sysview_session_free(sysview_session *session);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_session*, sysview_session_free);
-
-/*
- * Seats
- */
-
-struct sysview_seat {
- sysview_context *context;
- char *name;
- char *path;
-
- Hashmap *session_map;
- Hashmap *device_map;
-
- bool scanned : 1;
- bool public : 1;
-};
-
-sysview_seat *sysview_find_seat(sysview_context *c, const char *name);
-
-int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name);
-sysview_seat *sysview_seat_free(sysview_seat *seat);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_seat*, sysview_seat_free);
-
-/*
- * Contexts
- */
-
-struct sysview_context {
- sd_event *event;
- sd_bus *sysbus;
- struct udev *ud;
- uint64_t custom_sid;
- unsigned int n_probe;
-
- Hashmap *seat_map;
- Hashmap *session_map;
- Hashmap *device_map;
-
- sd_event_source *scan_src;
- sysview_event_fn event_fn;
- void *userdata;
-
- /* udev scanner */
- struct udev_monitor *ud_monitor;
- sd_event_source *ud_monitor_src;
-
- /* logind scanner */
- sd_bus_slot *ld_slot_manager_signal;
- sd_bus_slot *ld_slot_list_seats;
- sd_bus_slot *ld_slot_list_sessions;
-
- bool scan_logind : 1;
- bool scan_evdev : 1;
- bool scan_drm : 1;
- bool running : 1;
- bool scanned : 1;
- bool rescan : 1;
- bool settled : 1;
-};
-
-int sysview_context_rescan(sysview_context *c);
diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
deleted file mode 100644
index 2e9b15859a..0000000000
--- a/src/libsystemd-terminal/sysview.c
+++ /dev/null
@@ -1,1554 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <libudev.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-#include "sd-login.h"
-#include "macro.h"
-#include "udev-util.h"
-#include "util.h"
-#include "bus-util.h"
-#include "sysview.h"
-#include "sysview-internal.h"
-
-static int context_raise_session_control(sysview_context *c, sysview_session *session, int error);
-
-/*
- * Devices
- */
-
-sysview_device *sysview_find_device(sysview_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->device_map, name);
-}
-
-int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name) {
- _cleanup_(sysview_device_freep) sysview_device *device = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
- assert_return(name, -EINVAL);
-
- device = new0(sysview_device, 1);
- if (!device)
- return -ENOMEM;
-
- device->seat = seat;
- device->type = (unsigned)-1;
-
- device->name = strdup(name);
- if (!device->name)
- return -ENOMEM;
-
- r = hashmap_put(seat->context->device_map, device->name, device);
- if (r < 0)
- return r;
-
- r = hashmap_put(seat->device_map, device->name, device);
- if (r < 0)
- return r;
-
- if (out)
- *out = device;
- device = NULL;
- return 0;
-}
-
-sysview_device *sysview_device_free(sysview_device *device) {
- if (!device)
- return NULL;
-
- if (device->name) {
- hashmap_remove_value(device->seat->device_map, device->name, device);
- hashmap_remove_value(device->seat->context->device_map, device->name, device);
- }
-
- switch (device->type) {
- case SYSVIEW_DEVICE_EVDEV:
- device->evdev.ud = udev_device_unref(device->evdev.ud);
- break;
- case SYSVIEW_DEVICE_DRM:
- device->drm.ud = udev_device_unref(device->drm.ud);
- break;
- }
-
- free(device->name);
- free(device);
-
- return NULL;
-}
-
-const char *sysview_device_get_name(sysview_device *device) {
- assert_return(device, NULL);
-
- return device->name;
-}
-
-unsigned int sysview_device_get_type(sysview_device *device) {
- assert_return(device, (unsigned)-1);
-
- return device->type;
-}
-
-struct udev_device *sysview_device_get_ud(sysview_device *device) {
- assert_return(device, NULL);
-
- switch (device->type) {
- case SYSVIEW_DEVICE_EVDEV:
- return device->evdev.ud;
- case SYSVIEW_DEVICE_DRM:
- return device->drm.ud;
- default:
- assert_return(0, NULL);
- }
-}
-
-static int device_new_ud(sysview_device **out, sysview_seat *seat, unsigned int type, struct udev_device *ud) {
- _cleanup_(sysview_device_freep) sysview_device *device = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
- assert_return(ud, -EINVAL);
-
- r = sysview_device_new(&device, seat, udev_device_get_syspath(ud));
- if (r < 0)
- return r;
-
- device->type = type;
-
- switch (type) {
- case SYSVIEW_DEVICE_EVDEV:
- device->evdev.ud = udev_device_ref(ud);
- break;
- case SYSVIEW_DEVICE_DRM:
- device->drm.ud = udev_device_ref(ud);
- break;
- default:
- assert_not_reached("sysview: invalid udev-device type");
- }
-
- if (out)
- *out = device;
- device = NULL;
- return 0;
-}
-
-/*
- * Sessions
- */
-
-sysview_session *sysview_find_session(sysview_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->session_map, name);
-}
-
-int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name) {
- _cleanup_(sysview_session_freep) sysview_session *session = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
-
- session = new0(sysview_session, 1);
- if (!session)
- return -ENOMEM;
-
- session->seat = seat;
-
- if (name) {
- /*
- * If a name is given, we require it to be a logind session
- * name. The session will be put in managed mode and we use
- * logind to request controller access.
- */
-
- session->name = strdup(name);
- if (!session->name)
- return -ENOMEM;
-
- r = sd_bus_path_encode("/org/freedesktop/login1/session",
- session->name, &session->path);
- if (r < 0)
- return r;
-
- session->custom = false;
- } else {
- /*
- * No session name was given. We assume this is an unmanaged
- * session controlled by the application. We don't use logind
- * at all and leave session management to the application. The
- * name of the session-object is set to a unique random string
- * that does not clash with the logind namespace.
- */
-
- r = asprintf(&session->name, "@custom%" PRIu64,
- ++seat->context->custom_sid);
- if (r < 0)
- return -ENOMEM;
-
- session->custom = true;
- }
-
- r = hashmap_put(seat->context->session_map, session->name, session);
- if (r < 0)
- return r;
-
- r = hashmap_put(seat->session_map, session->name, session);
- if (r < 0)
- return r;
-
- if (out)
- *out = session;
- session = NULL;
- return 0;
-}
-
-sysview_session *sysview_session_free(sysview_session *session) {
- if (!session)
- return NULL;
-
- assert(!session->public);
- assert(!session->wants_control);
-
- if (session->name) {
- hashmap_remove_value(session->seat->session_map, session->name, session);
- hashmap_remove_value(session->seat->context->session_map, session->name, session);
- }
-
- free(session->path);
- free(session->name);
- free(session);
-
- return NULL;
-}
-
-void sysview_session_set_userdata(sysview_session *session, void *userdata) {
- assert(session);
-
- session->userdata = userdata;
-}
-
-void *sysview_session_get_userdata(sysview_session *session) {
- assert_return(session, NULL);
-
- return session->userdata;
-}
-
-const char *sysview_session_get_name(sysview_session *session) {
- assert_return(session, NULL);
-
- return session->name;
-}
-
-sysview_seat *sysview_session_get_seat(sysview_session *session) {
- assert_return(session, NULL);
-
- return session->seat;
-}
-
-static int session_take_control_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_session *session = userdata;
- int r, error;
-
- session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *e = sd_bus_message_get_error(reply);
-
- log_debug("sysview: %s: TakeControl failed: %s: %s",
- session->name, e->name, e->message);
- error = -sd_bus_error_get_errno(e);
- } else {
- session->has_control = true;
- error = 0;
- }
-
- r = context_raise_session_control(session->seat->context, session, error);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while signalling session control '%d' on session '%s': %m",
- error, session->name);
-
- return 0;
-}
-
-int sysview_session_take_control(sysview_session *session) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(session, -EINVAL);
- assert_return(!session->custom, -EINVAL);
-
- if (session->wants_control)
- return 0;
-
- r = sd_bus_message_new_method_call(session->seat->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "TakeControl");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "b", 0);
- if (r < 0)
- return r;
-
- r = sd_bus_call_async(session->seat->context->sysbus,
- &session->slot_take_control,
- m,
- session_take_control_fn,
- session,
- 0);
- if (r < 0)
- return r;
-
- session->wants_control = true;
- return 0;
-}
-
-void sysview_session_release_control(sysview_session *session) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(session);
- assert(!session->custom);
-
- if (!session->wants_control)
- return;
-
- session->wants_control = false;
-
- if (!session->has_control && !session->slot_take_control)
- return;
-
- session->has_control = false;
- session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
-
- r = sd_bus_message_new_method_call(session->seat->context->sysbus,
- &m,
- "org.freedesktop.login1",
- session->path,
- "org.freedesktop.login1.Session",
- "ReleaseControl");
- if (r >= 0)
- r = sd_bus_send(session->seat->context->sysbus, m, NULL);
-
- if (r < 0 && r != -ENOTCONN)
- log_debug_errno(r, "sysview: %s: cannot send ReleaseControl: %m",
- session->name);
-}
-
-/*
- * Seats
- */
-
-sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
- assert_return(c, NULL);
- assert_return(name, NULL);
-
- return hashmap_get(c->seat_map, name);
-}
-
-int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
- _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(name, -EINVAL);
-
- seat = new0(sysview_seat, 1);
- if (!seat)
- return -ENOMEM;
-
- seat->context = c;
-
- seat->name = strdup(name);
- if (!seat->name)
- return -ENOMEM;
-
- r = sd_bus_path_encode("/org/freedesktop/login1/seat", seat->name, &seat->path);
- if (r < 0)
- return r;
-
- seat->session_map = hashmap_new(&string_hash_ops);
- if (!seat->session_map)
- return -ENOMEM;
-
- seat->device_map = hashmap_new(&string_hash_ops);
- if (!seat->device_map)
- return -ENOMEM;
-
- r = hashmap_put(c->seat_map, seat->name, seat);
- if (r < 0)
- return r;
-
- if (out)
- *out = seat;
- seat = NULL;
- return 0;
-}
-
-sysview_seat *sysview_seat_free(sysview_seat *seat) {
- if (!seat)
- return NULL;
-
- assert(!seat->public);
- assert(hashmap_size(seat->device_map) == 0);
- assert(hashmap_size(seat->session_map) == 0);
-
- if (seat->name)
- hashmap_remove_value(seat->context->seat_map, seat->name, seat);
-
- hashmap_free(seat->device_map);
- hashmap_free(seat->session_map);
- free(seat->path);
- free(seat->name);
- free(seat);
-
- return NULL;
-}
-
-const char *sysview_seat_get_name(sysview_seat *seat) {
- assert_return(seat, NULL);
-
- return seat->name;
-}
-
-int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert_return(seat, -EINVAL);
- assert_return(seat->context->sysbus, -EINVAL);
-
- r = sd_bus_message_new_method_call(seat->context->sysbus,
- &m,
- "org.freedesktop.login1",
- seat->path,
- "org.freedesktop.login1.Seat",
- "SwitchTo");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "u", nr);
- if (r < 0)
- return r;
-
- return sd_bus_send(seat->context->sysbus, m, NULL);
-}
-
-/*
- * Contexts
- */
-
-static int context_raise(sysview_context *c, sysview_event *event, int def) {
- return c->running ? c->event_fn(c, c->userdata, event) : def;
-}
-
-static int context_raise_settle(sysview_context *c) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SETTLE,
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SEAT_ADD,
- .seat_add = {
- .seat = seat,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SEAT_REMOVE,
- .seat_remove = {
- .seat = seat,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_filter(sysview_context *c,
- const char *id,
- const char *seatid,
- const char *username,
- unsigned int uid) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_FILTER,
- .session_filter = {
- .id = id,
- .seatid = seatid,
- .username = username,
- .uid = uid,
- }
- };
-
- return context_raise(c, &event, 1);
-}
-
-static int context_raise_session_add(sysview_context *c, sysview_session *session) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_ADD,
- .session_add = {
- .session = session,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_REMOVE,
- .session_remove = {
- .session = session,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_CONTROL,
- .session_control = {
- .session = session,
- .error = error,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_ATTACH,
- .session_attach = {
- .session = session,
- .device = device,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_DETACH,
- .session_detach = {
- .session = session,
- .device = device,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
- sysview_event event = {
- .type = SYSVIEW_EVENT_SESSION_REFRESH,
- .session_refresh = {
- .session = session,
- .device = device,
- .ud = ud,
- }
- };
-
- return context_raise(c, &event, 0);
-}
-
-static void context_settle(sysview_context *c) {
- int r;
-
- if (c->n_probe <= 0 || --c->n_probe > 0)
- return;
-
- log_debug("sysview: settle");
-
- c->settled = true;
-
- r = context_raise_settle(c);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed on settle: %m");
-}
-
-static void context_add_device(sysview_context *c, sysview_device *device) {
- sysview_session *session;
- Iterator i;
- int r;
-
- assert(c);
- assert(device);
-
- log_debug("sysview: add device '%s' on seat '%s'",
- device->name, device->seat->name);
-
- HASHMAP_FOREACH(session, device->seat->session_map, i) {
- if (!session->public)
- continue;
-
- r = context_raise_session_attach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m",
- device->name, session->name);
- }
-}
-
-static void context_remove_device(sysview_context *c, sysview_device *device) {
- sysview_session *session;
- Iterator i;
- int r;
-
- assert(c);
- assert(device);
-
- log_debug("sysview: remove device '%s'", device->name);
-
- HASHMAP_FOREACH(session, device->seat->session_map, i) {
- if (!session->public)
- continue;
-
- r = context_raise_session_detach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m",
- device->name, session->name);
- }
-
- sysview_device_free(device);
-}
-
-static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
- sysview_session *session;
- Iterator i;
- int r;
-
- assert(c);
- assert(device);
-
- log_debug("sysview: change device '%s'", device->name);
-
- HASHMAP_FOREACH(session, device->seat->session_map, i) {
- if (!session->public)
- continue;
-
- r = context_raise_session_refresh(c, session, device, ud);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m",
- device->name, session->name);
- }
-}
-
-static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
- sysview_session *session;
- sysview_device *device;
- Iterator i;
- int r;
-
- assert(c);
- assert(seat);
- assert(id);
-
- session = sysview_find_session(c, id);
- if (session)
- return;
-
- log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
-
- r = sysview_session_new(&session, seat, id);
- if (r < 0)
- goto error;
-
- if (!seat->scanned) {
- r = sysview_context_rescan(c);
- if (r < 0)
- goto error;
- }
-
- if (seat->public) {
- session->public = true;
- r = context_raise_session_add(c, session);
- if (r < 0) {
- log_debug_errno(r, "sysview: callback failed while adding session '%s': %m",
- session->name);
- session->public = false;
- goto error;
- }
-
- HASHMAP_FOREACH(device, seat->device_map, i) {
- r = context_raise_session_attach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m",
- device->name, session->name);
- }
- }
-
- return;
-
-error:
- if (r < 0)
- log_debug_errno(r, "sysview: error while adding session '%s': %m",
- id);
-}
-
-static void context_remove_session(sysview_context *c, sysview_session *session) {
- sysview_device *device;
- Iterator i;
- int r;
-
- assert(c);
- assert(session);
-
- log_debug("sysview: remove session '%s'", session->name);
-
- if (session->public) {
- HASHMAP_FOREACH(device, session->seat->device_map, i) {
- r = context_raise_session_detach(c, session, device);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m",
- device->name, session->name);
- }
-
- session->public = false;
- r = context_raise_session_remove(c, session);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while removing session '%s': %m",
- session->name);
- }
-
- if (!session->custom)
- sysview_session_release_control(session);
-
- sysview_session_free(session);
-}
-
-static void context_add_seat(sysview_context *c, const char *id) {
- sysview_seat *seat;
- int r;
-
- assert(c);
- assert(id);
-
- seat = sysview_find_seat(c, id);
- if (seat)
- return;
-
- log_debug("sysview: add seat '%s'", id);
-
- r = sysview_seat_new(&seat, c, id);
- if (r < 0)
- goto error;
-
- seat->public = true;
- r = context_raise_seat_add(c, seat);
- if (r < 0) {
- log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m",
- seat->name);
- seat->public = false;
- }
-
- return;
-
-error:
- if (r < 0)
- log_debug_errno(r, "sysview: error while adding seat '%s': %m",
- id);
-}
-
-static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
- sysview_session *session;
- sysview_device *device;
- int r;
-
- assert(c);
- assert(seat);
-
- log_debug("sysview: remove seat '%s'", seat->name);
-
- while ((device = hashmap_first(seat->device_map)))
- context_remove_device(c, device);
-
- while ((session = hashmap_first(seat->session_map)))
- context_remove_session(c, session);
-
- if (seat->public) {
- seat->public = false;
- r = context_raise_seat_remove(c, seat);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m",
- seat->name);
- }
-
- sysview_seat_free(seat);
-}
-
-int sysview_context_new(sysview_context **out,
- unsigned int flags,
- sd_event *event,
- sd_bus *sysbus,
- struct udev *ud) {
- _cleanup_(sysview_context_freep) sysview_context *c = NULL;
- int r;
-
- assert_return(out, -EINVAL);
- assert_return(event, -EINVAL);
-
- log_debug("sysview: new");
-
- c = new0(sysview_context, 1);
- if (!c)
- return -ENOMEM;
-
- c->event = sd_event_ref(event);
- if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
- c->scan_logind = true;
- if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
- c->scan_evdev = true;
- if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
- c->scan_drm = true;
-
- if (sysbus) {
- c->sysbus = sd_bus_ref(sysbus);
- } else if (c->scan_logind) {
- r = sd_bus_open_system(&c->sysbus);
- if (r < 0)
- return r;
- }
-
- if (ud) {
- c->ud = udev_ref(ud);
- } else if (c->scan_evdev || c->scan_drm) {
- errno = 0;
- c->ud = udev_new();
- if (!c->ud)
- return errno > 0 ? -errno : -EFAULT;
- }
-
- c->seat_map = hashmap_new(&string_hash_ops);
- if (!c->seat_map)
- return -ENOMEM;
-
- c->session_map = hashmap_new(&string_hash_ops);
- if (!c->session_map)
- return -ENOMEM;
-
- c->device_map = hashmap_new(&string_hash_ops);
- if (!c->device_map)
- return -ENOMEM;
-
- *out = c;
- c = NULL;
- return 0;
-}
-
-sysview_context *sysview_context_free(sysview_context *c) {
- if (!c)
- return NULL;
-
- log_debug("sysview: free");
-
- sysview_context_stop(c);
-
- assert(hashmap_size(c->device_map) == 0);
- assert(hashmap_size(c->session_map) == 0);
- assert(hashmap_size(c->seat_map) == 0);
-
- hashmap_free(c->device_map);
- hashmap_free(c->session_map);
- hashmap_free(c->seat_map);
- c->ud = udev_unref(c->ud);
- c->sysbus = sd_bus_unref(c->sysbus);
- c->event = sd_event_unref(c->event);
- free(c);
-
- return NULL;
-}
-
-static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
- int r;
-
- if (c->scan_evdev) {
- r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
- if (r < 0)
- return r;
- }
-
- if (c->scan_drm) {
- r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
- int r;
-
- if (c->scan_evdev) {
- r = udev_enumerate_add_match_subsystem(e, "input");
- if (r < 0)
- return r;
- }
-
- if (c->scan_drm) {
- r = udev_enumerate_add_match_subsystem(e, "drm");
- if (r < 0)
- return r;
- }
-
- r = udev_enumerate_add_match_is_initialized(e);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
- const char *syspath, *sysname, *subsystem, *action, *seatname;
- sysview_device *device;
- int r;
-
- syspath = udev_device_get_syspath(d);
- sysname = udev_device_get_sysname(d);
- subsystem = udev_device_get_subsystem(d);
- action = udev_device_get_action(d);
-
- /* not interested in custom devices without syspath/etc */
- if (!syspath || !sysname || !subsystem)
- return 0;
-
- device = sysview_find_device(c, syspath);
-
- if (streq_ptr(action, "remove")) {
- if (!device)
- return 0;
-
- context_remove_device(c, device);
- } else if (streq_ptr(action, "change")) {
- if (!device)
- return 0;
-
- context_change_device(c, device, d);
- } else if (!action || streq_ptr(action, "add")) {
- struct udev_device *p;
- unsigned int type, t;
- sysview_seat *seat;
-
- if (device)
- return 0;
-
- if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
- type = SYSVIEW_DEVICE_EVDEV;
- else if (streq(subsystem, "drm") && startswith(sysname, "card"))
- type = SYSVIEW_DEVICE_DRM;
- else
- type = (unsigned)-1;
-
- if (type >= SYSVIEW_DEVICE_CNT)
- return 0;
-
- p = d;
- seatname = NULL;
- do {
- seatname = udev_device_get_property_value(p, "ID_SEAT");
- if (seatname)
- break;
- } while ((p = udev_device_get_parent(p)));
-
- seat = sysview_find_seat(c, seatname ? : "seat0");
- if (!seat)
- return 0;
-
- r = device_new_ud(&device, seat, type, d);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m",
- syspath);
-
- context_add_device(c, device);
- }
-
- return 0;
-}
-
-static int context_ud_monitor_fn(sd_event_source *s,
- int fd,
- uint32_t revents,
- void *userdata) {
- sysview_context *c = userdata;
- struct udev_device *d;
- int r;
-
- if (revents & EPOLLIN) {
- while ((d = udev_monitor_receive_device(c->ud_monitor))) {
- r = context_ud_hotplug(c, d);
- udev_device_unref(d);
- if (r != 0)
- return r;
- }
-
- /* as long as EPOLLIN is signalled, read pending data */
- return 0;
- }
-
- if (revents & (EPOLLHUP | EPOLLERR)) {
- log_debug("sysview: HUP on udev-monitor");
- c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
- }
-
- return 0;
-}
-
-static int context_ud_start(sysview_context *c) {
- int r, fd;
-
- if (!c->ud)
- return 0;
-
- errno = 0;
- c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
- if (!c->ud_monitor)
- return errno > 0 ? -errno : -EFAULT;
-
- r = context_ud_prepare_monitor(c, c->ud_monitor);
- if (r < 0)
- return r;
-
- r = udev_monitor_enable_receiving(c->ud_monitor);
- if (r < 0)
- return r;
-
- fd = udev_monitor_get_fd(c->ud_monitor);
- r = sd_event_add_io(c->event,
- &c->ud_monitor_src,
- fd,
- EPOLLHUP | EPOLLERR | EPOLLIN,
- context_ud_monitor_fn,
- c);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static void context_ud_stop(sysview_context *c) {
- c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
- c->ud_monitor = udev_monitor_unref(c->ud_monitor);
-}
-
-static int context_ud_scan(sysview_context *c) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *entry;
- struct udev_device *d;
- int r;
-
- if (!c->ud_monitor)
- return 0;
-
- errno = 0;
- e = udev_enumerate_new(c->ud);
- if (!e)
- return errno > 0 ? -errno : -EFAULT;
-
- r = context_ud_prepare_scan(c, e);
- if (r < 0)
- return r;
-
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return r;
-
- udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
- const char *name;
-
- name = udev_list_entry_get_name(entry);
-
- errno = 0;
- d = udev_device_new_from_syspath(c->ud, name);
- if (!d) {
- r = errno > 0 ? -errno : -EFAULT;
- log_debug_errno(r, "sysview: cannot create udev-device for %s: %m",
- name);
- continue;
- }
-
- r = context_ud_hotplug(c, d);
- udev_device_unref(d);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
- const char *id, *path;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m");
-
- context_add_seat(c, id);
- return 0;
-}
-
-static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
- const char *id, *path;
- sysview_seat *seat;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m");
-
- seat = sysview_find_seat(c, id);
- if (!seat)
- return 0;
-
- context_remove_seat(c, seat);
- return 0;
-}
-
-static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
- _cleanup_free_ char *seatid = NULL, *username = NULL;
- const char *id, *path;
- sysview_seat *seat;
- uid_t uid;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m");
-
- /*
- * As the dbus message didn't contain enough information, we
- * read missing bits via sd-login. Note that this might race session
- * destruction, so we handle ENOENT properly.
- */
-
- /* ENOENT is also returned for sessions without seats */
- r = sd_session_get_seat(id, &seatid);
- if (r == -ENOENT)
- return 0;
- else if (r < 0)
- goto error;
-
- seat = sysview_find_seat(c, seatid);
- if (!seat)
- return 0;
-
- r = sd_session_get_uid(id, &uid);
- if (r == -ENOENT)
- return 0;
- else if (r < 0)
- goto error;
-
- username = lookup_uid(uid);
- if (!username) {
- r = -ENOMEM;
- goto error;
- }
-
- r = context_raise_session_filter(c, id, seatid, username, uid);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
- id);
- else if (r > 0)
- context_add_session(c, seat, id);
-
- return 0;
-
-error:
- return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m",
- id);
-}
-
-static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
- sysview_session *session;
- const char *id, *path;
- int r;
-
- r = sd_bus_message_read(signal, "so", &id, &path);
- if (r < 0)
- return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m");
-
- session = sysview_find_session(c, id);
- if (!session)
- return 0;
-
- context_remove_session(c, session);
- return 0;
-}
-
-static int context_ld_manager_signal_fn(sd_bus_message *signal,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_context *c = userdata;
-
- if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
- return context_ld_seat_new(c, signal);
- else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
- return context_ld_seat_removed(c, signal);
- else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
- return context_ld_session_new(c, signal);
- else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
- return context_ld_session_removed(c, signal);
- else
- return 0;
-}
-
-static int context_ld_start(sysview_context *c) {
- int r;
-
- if (!c->scan_logind)
- return 0;
-
- r = sd_bus_add_match(c->sysbus,
- &c->ld_slot_manager_signal,
- "type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Manager',"
- "path='/org/freedesktop/login1'",
- context_ld_manager_signal_fn,
- c);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static void context_ld_stop(sysview_context *c) {
- c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
- c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
- c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
-}
-
-static int context_ld_list_seats_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_context *c = userdata;
- int r;
-
- c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("sysview: ListSeats on logind failed: %s: %s",
- error->name, error->message);
- r = -sd_bus_error_get_errno(error);
- goto settle;
- }
-
- r = sd_bus_message_enter_container(reply, 'a', "(so)");
- if (r < 0)
- goto error;
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
- const char *id, *path;
-
- r = sd_bus_message_read(reply, "so", &id, &path);
- if (r < 0)
- goto error;
-
- context_add_seat(c, id);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
- }
-
- if (r < 0)
- goto error;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
-
- r = 0;
- goto settle;
-
-error:
- log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
-settle:
- context_settle(c);
- return r;
-}
-
-static int context_ld_list_sessions_fn(sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error) {
- sysview_context *c = userdata;
- int r;
-
- c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
-
- if (sd_bus_message_is_method_error(reply, NULL)) {
- const sd_bus_error *error = sd_bus_message_get_error(reply);
-
- log_debug("sysview: ListSessions on logind failed: %s: %s",
- error->name, error->message);
- r = -sd_bus_error_get_errno(error);
- goto settle;
- }
-
- r = sd_bus_message_enter_container(reply, 'a', "(susso)");
- if (r < 0)
- goto error;
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
- const char *id, *username, *seatid, *path;
- sysview_seat *seat;
- unsigned int uid;
-
- r = sd_bus_message_read(reply,
- "susso",
- &id,
- &uid,
- &username,
- &seatid,
- &path);
- if (r < 0)
- goto error;
-
- seat = sysview_find_seat(c, seatid);
- if (seat) {
- r = context_raise_session_filter(c, id, seatid, username, uid);
- if (r < 0)
- log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
- id);
- else if (r > 0)
- context_add_session(c, seat, id);
- }
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
- }
-
- if (r < 0)
- goto error;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto error;
-
- r = 0;
- goto settle;
-
-error:
- log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
-settle:
- context_settle(c);
- return r;
-}
-
-static int context_ld_scan(sysview_context *c) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- if (!c->ld_slot_manager_signal)
- return 0;
-
- /* request seat list */
-
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "ListSeats");
- if (r < 0)
- return r;
-
- r = sd_bus_call_async(c->sysbus,
- &c->ld_slot_list_seats,
- m,
- context_ld_list_seats_fn,
- c,
- 0);
- if (r < 0)
- return r;
-
- if (!c->settled)
- ++c->n_probe;
-
- /* request session list */
-
- m = sd_bus_message_unref(m);
- r = sd_bus_message_new_method_call(c->sysbus,
- &m,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "ListSessions");
- if (r < 0)
- return r;
-
- r = sd_bus_call_async(c->sysbus,
- &c->ld_slot_list_sessions,
- m,
- context_ld_list_sessions_fn,
- c,
- 0);
- if (r < 0)
- return r;
-
- if (!c->settled)
- ++c->n_probe;
-
- return 0;
-}
-
-bool sysview_context_is_running(sysview_context *c) {
- return c && c->running;
-}
-
-int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
- int r;
-
- assert_return(c, -EINVAL);
- assert_return(event_fn, -EINVAL);
-
- if (c->running)
- return -EALREADY;
-
- log_debug("sysview: start");
-
- c->running = true;
- c->event_fn = event_fn;
- c->userdata = userdata;
-
- r = context_ld_start(c);
- if (r < 0)
- goto error;
-
- r = context_ud_start(c);
- if (r < 0)
- goto error;
-
- r = sysview_context_rescan(c);
- if (r < 0)
- goto error;
-
- return 0;
-
-error:
- sysview_context_stop(c);
- return r;
-}
-
-void sysview_context_stop(sysview_context *c) {
- sysview_session *session;
- sysview_device *device;
- sysview_seat *seat;
-
- assert(c);
-
- if (!c->running)
- return;
-
- log_debug("sysview: stop");
-
- while ((device = hashmap_first(c->device_map)))
- context_remove_device(c, device);
-
- while ((session = hashmap_first(c->session_map)))
- context_remove_session(c, session);
-
- while ((seat = hashmap_first(c->seat_map)))
- context_remove_seat(c, seat);
-
- c->running = false;
- c->scanned = false;
- c->settled = false;
- c->n_probe = 0;
- c->event_fn = NULL;
- c->userdata = NULL;
- c->scan_src = sd_event_source_unref(c->scan_src);
- context_ud_stop(c);
- context_ld_stop(c);
-}
-
-static int context_scan_fn(sd_event_source *s, void *userdata) {
- sysview_context *c = userdata;
- sysview_seat *seat;
- Iterator i;
- int r;
-
- c->rescan = false;
-
- if (!c->scanned) {
- r = context_ld_scan(c);
- if (r < 0)
- return log_debug_errno(r, "sysview: logind scan failed: %m");
- }
-
- /* skip device scans if no sessions are available */
- if (hashmap_size(c->session_map) > 0) {
- r = context_ud_scan(c);
- if (r < 0)
- return log_debug_errno(r, "sysview: udev scan failed: %m");
-
- HASHMAP_FOREACH(seat, c->seat_map, i)
- seat->scanned = true;
- }
-
- c->scanned = true;
- context_settle(c);
-
- return 0;
-}
-
-int sysview_context_rescan(sysview_context *c) {
- assert(c);
-
- if (!c->running)
- return 0;
-
- if (!c->rescan) {
- c->rescan = true;
- if (!c->settled)
- ++c->n_probe;
- }
-
- if (c->scan_src)
- return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
- else
- return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);
-}
diff --git a/src/libsystemd-terminal/sysview.h b/src/libsystemd-terminal/sysview.h
deleted file mode 100644
index a5e7a38df3..0000000000
--- a/src/libsystemd-terminal/sysview.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * System View
- * The sysview interface scans and monitors the system for seats, sessions and
- * devices. It basically mirrors the state of logind on the application side.
- * It's meant as base for session services that require managed device access.
- * The logind controller API is employed to allow unprivileged access to all
- * devices of a user.
- * Furthermore, the sysview interface can be used for system services that run
- * in situations where logind is not available, but session-like services are
- * needed. For instance, the initrd does not run logind but might require
- * graphics access. It cannot run session services, though. The sysview
- * interface pretends that a session is available and provides the same
- * interface as to normal session services.
- */
-
-#pragma once
-
-#include <stdbool.h>
-#include "sd-bus.h"
-#include "sd-event.h"
-
-typedef struct sysview_event sysview_event;
-typedef struct sysview_device sysview_device;
-typedef struct sysview_session sysview_session;
-typedef struct sysview_seat sysview_seat;
-typedef struct sysview_context sysview_context;
-
-/*
- * Events
- */
-
-enum {
- SYSVIEW_EVENT_SETTLE,
-
- SYSVIEW_EVENT_SEAT_ADD,
- SYSVIEW_EVENT_SEAT_REMOVE,
-
- SYSVIEW_EVENT_SESSION_FILTER,
- SYSVIEW_EVENT_SESSION_ADD,
- SYSVIEW_EVENT_SESSION_REMOVE,
- SYSVIEW_EVENT_SESSION_ATTACH,
- SYSVIEW_EVENT_SESSION_DETACH,
- SYSVIEW_EVENT_SESSION_REFRESH,
- SYSVIEW_EVENT_SESSION_CONTROL,
-};
-
-struct sysview_event {
- unsigned int type;
-
- union {
- struct {
- sysview_seat *seat;
- } seat_add, seat_remove;
-
- struct {
- const char *id;
- const char *seatid;
- const char *username;
- unsigned int uid;
- } session_filter;
-
- struct {
- sysview_session *session;
- } session_add, session_remove;
-
- struct {
- sysview_session *session;
- sysview_device *device;
- } session_attach, session_detach;
-
- struct {
- sysview_session *session;
- sysview_device *device;
- struct udev_device *ud;
- } session_refresh;
-
- struct {
- sysview_session *session;
- int error;
- } session_control;
- };
-};
-
-typedef int (*sysview_event_fn) (sysview_context *c, void *userdata, sysview_event *e);
-
-/*
- * Devices
- */
-
-enum {
- SYSVIEW_DEVICE_EVDEV,
- SYSVIEW_DEVICE_DRM,
- SYSVIEW_DEVICE_CNT
-};
-
-const char *sysview_device_get_name(sysview_device *device);
-unsigned int sysview_device_get_type(sysview_device *device);
-struct udev_device *sysview_device_get_ud(sysview_device *device);
-
-/*
- * Sessions
- */
-
-void sysview_session_set_userdata(sysview_session *session, void *userdata);
-void *sysview_session_get_userdata(sysview_session *session);
-
-const char *sysview_session_get_name(sysview_session *session);
-sysview_seat *sysview_session_get_seat(sysview_session *session);
-
-int sysview_session_take_control(sysview_session *session);
-void sysview_session_release_control(sysview_session *session);
-
-/*
- * Seats
- */
-
-const char *sysview_seat_get_name(sysview_seat *seat);
-int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr);
-
-/*
- * Contexts
- */
-
-enum {
- SYSVIEW_CONTEXT_SCAN_LOGIND = (1 << 0),
- SYSVIEW_CONTEXT_SCAN_EVDEV = (1 << 1),
- SYSVIEW_CONTEXT_SCAN_DRM = (1 << 2),
-};
-
-int sysview_context_new(sysview_context **out,
- unsigned int flags,
- sd_event *event,
- sd_bus *sysbus,
- struct udev *ud);
-sysview_context *sysview_context_free(sysview_context *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sysview_context*, sysview_context_free);
-
-bool sysview_context_is_running(sysview_context *c);
-int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata);
-void sysview_context_stop(sysview_context *c);
diff --git a/src/libsystemd-terminal/term-charset.c b/src/libsystemd-terminal/term-charset.c
deleted file mode 100644
index 9db178861c..0000000000
--- a/src/libsystemd-terminal/term-charset.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * VTE Character Sets
- * These are predefined charactersets that can be loaded into GL and GR. By
- * default we use unicode_lower and unicode_upper, that is, both sets have the
- * exact unicode mapping. unicode_lower is effectively ASCII and unicode_upper
- * as defined by the unicode standard (I guess, ISO 8859-1).
- * Several other character sets are defined here. However, all of them are
- * limited to the 96 character space of GL or GR. Everything beyond GR (which
- * was not supported by the classic VTs by DEC but is available in VT emulators
- * that support unicode/UTF8) is always mapped to unicode and cannot be changed
- * by these character sets. Even mapping GL and GR is only available for
- * backwards compatibility as new applications can use the Unicode functionality
- * of the VTE.
- *
- * Moreover, mapping GR is almost unnecessary to support. In fact, Unicode UTF-8
- * support in VTE works by reading every incoming data as UTF-8 stream. This
- * maps GL/ASCII to ASCII, as UTF-8 is backwards compatible to ASCII, however,
- * everything that has the 8th bit set is a >=2-byte haracter in UTF-8. That is,
- * this is in no way backwards compatible to >=VT220 8bit support. Therefore, if
- * someone maps a character set into GR and wants to use them with this VTE,
- * then they must already send UTF-8 characters to use GR (all GR characters are
- * 8-bits). Hence, they can easily also send the correct UTF-8 character for the
- * unicode mapping.
- * The only advantage is that most characters in many sets are 3-byte UTF-8
- * characters and by mapping the set into GR/GL you can use 2 or 1 byte UTF-8
- * characters which saves bandwidth.
- * Another reason is, if you have older applications that use the VT220 8-bit
- * support and you put a ASCII/8bit-extension to UTF-8 converter in between, you
- * need these mappings to have the application behave correctly if it uses GL/GR
- * mappings extensively.
- *
- * Anyway, we support GL/GR mappings so here are the most commonly used maps as
- * defined by Unicode-standard, DEC-private maps and other famous charmaps.
- *
- * Characters 1-32 are always the control characters (part of CL) and cannot be
- * mapped. Characters 34-127 (94 characters) are part of GL and can be mapped.
- * Characters 33 and 128 are not part of GL and always mapped by the VTE.
- * However, for GR they can be mapped differently (96 chars) so we have to
- * include them. The mapper has to take care not to use them in GL.
- */
-
-#include "term-internal.h"
-
-/*
- * Lower Unicode character set. This maps the characters to the basic ASCII
- * characters 33-126. These are all graphics characters defined in ASCII.
- */
-term_charset term_unicode_lower = {
- [0] = 32,
- [1] = 33,
- [2] = 34,
- [3] = 35,
- [4] = 36,
- [5] = 37,
- [6] = 38,
- [7] = 39,
- [8] = 40,
- [9] = 41,
- [10] = 42,
- [11] = 43,
- [12] = 44,
- [13] = 45,
- [14] = 46,
- [15] = 47,
- [16] = 48,
- [17] = 49,
- [18] = 50,
- [19] = 51,
- [20] = 52,
- [21] = 53,
- [22] = 54,
- [23] = 55,
- [24] = 56,
- [25] = 57,
- [26] = 58,
- [27] = 59,
- [28] = 60,
- [29] = 61,
- [30] = 62,
- [31] = 63,
- [32] = 64,
- [33] = 65,
- [34] = 66,
- [35] = 67,
- [36] = 68,
- [37] = 69,
- [38] = 70,
- [39] = 71,
- [40] = 72,
- [41] = 73,
- [42] = 74,
- [43] = 75,
- [44] = 76,
- [45] = 77,
- [46] = 78,
- [47] = 79,
- [48] = 80,
- [49] = 81,
- [50] = 82,
- [51] = 83,
- [52] = 84,
- [53] = 85,
- [54] = 86,
- [55] = 87,
- [56] = 88,
- [57] = 89,
- [58] = 90,
- [59] = 91,
- [60] = 92,
- [61] = 93,
- [62] = 94,
- [63] = 95,
- [64] = 96,
- [65] = 97,
- [66] = 98,
- [67] = 99,
- [68] = 100,
- [69] = 101,
- [70] = 102,
- [71] = 103,
- [72] = 104,
- [73] = 105,
- [74] = 106,
- [75] = 107,
- [76] = 108,
- [77] = 109,
- [78] = 110,
- [79] = 111,
- [80] = 112,
- [81] = 113,
- [82] = 114,
- [83] = 115,
- [84] = 116,
- [85] = 117,
- [86] = 118,
- [87] = 119,
- [88] = 120,
- [89] = 121,
- [90] = 122,
- [91] = 123,
- [92] = 124,
- [93] = 125,
- [94] = 126,
- [95] = 127,
-};
-
-/*
- * Upper Unicode Table
- * This maps all characters to the upper unicode characters 161-254. These are
- * not compatible to any older 8 bit character sets. See the Unicode standard
- * for the definitions of each symbol.
- */
-term_charset term_unicode_upper = {
- [0] = 160,
- [1] = 161,
- [2] = 162,
- [3] = 163,
- [4] = 164,
- [5] = 165,
- [6] = 166,
- [7] = 167,
- [8] = 168,
- [9] = 169,
- [10] = 170,
- [11] = 171,
- [12] = 172,
- [13] = 173,
- [14] = 174,
- [15] = 175,
- [16] = 176,
- [17] = 177,
- [18] = 178,
- [19] = 179,
- [20] = 180,
- [21] = 181,
- [22] = 182,
- [23] = 183,
- [24] = 184,
- [25] = 185,
- [26] = 186,
- [27] = 187,
- [28] = 188,
- [29] = 189,
- [30] = 190,
- [31] = 191,
- [32] = 192,
- [33] = 193,
- [34] = 194,
- [35] = 195,
- [36] = 196,
- [37] = 197,
- [38] = 198,
- [39] = 199,
- [40] = 200,
- [41] = 201,
- [42] = 202,
- [43] = 203,
- [44] = 204,
- [45] = 205,
- [46] = 206,
- [47] = 207,
- [48] = 208,
- [49] = 209,
- [50] = 210,
- [51] = 211,
- [52] = 212,
- [53] = 213,
- [54] = 214,
- [55] = 215,
- [56] = 216,
- [57] = 217,
- [58] = 218,
- [59] = 219,
- [60] = 220,
- [61] = 221,
- [62] = 222,
- [63] = 223,
- [64] = 224,
- [65] = 225,
- [66] = 226,
- [67] = 227,
- [68] = 228,
- [69] = 229,
- [70] = 230,
- [71] = 231,
- [72] = 232,
- [73] = 233,
- [74] = 234,
- [75] = 235,
- [76] = 236,
- [77] = 237,
- [78] = 238,
- [79] = 239,
- [80] = 240,
- [81] = 241,
- [82] = 242,
- [83] = 243,
- [84] = 244,
- [85] = 245,
- [86] = 246,
- [87] = 247,
- [88] = 248,
- [89] = 249,
- [90] = 250,
- [91] = 251,
- [92] = 252,
- [93] = 253,
- [94] = 254,
- [95] = 255,
-};
-
-/*
- * The DEC supplemental graphics set. For its definition see here:
- * http://vt100.net/docs/vt220-rm/table2-3b.html
- * Its basically a mixture of common European symbols that are not part of
- * ASCII. Most often, this is mapped into GR to extend the basci ASCII part.
- *
- * This is very similar to unicode_upper, however, few symbols differ so do not
- * mix them up!
- */
-term_charset term_dec_supplemental_graphics = {
- [0] = -1, /* undefined */
- [1] = 161,
- [2] = 162,
- [3] = 163,
- [4] = 0,
- [5] = 165,
- [6] = 0,
- [7] = 167,
- [8] = 164,
- [9] = 169,
- [10] = 170,
- [11] = 171,
- [12] = 0,
- [13] = 0,
- [14] = 0,
- [15] = 0,
- [16] = 176,
- [17] = 177,
- [18] = 178,
- [19] = 179,
- [20] = 0,
- [21] = 181,
- [22] = 182,
- [23] = 183,
- [24] = 0,
- [25] = 185,
- [26] = 186,
- [27] = 187,
- [28] = 188,
- [29] = 189,
- [30] = 0,
- [31] = 191,
- [32] = 192,
- [33] = 193,
- [34] = 194,
- [35] = 195,
- [36] = 196,
- [37] = 197,
- [38] = 198,
- [39] = 199,
- [40] = 200,
- [41] = 201,
- [42] = 202,
- [43] = 203,
- [44] = 204,
- [45] = 205,
- [46] = 206,
- [47] = 207,
- [48] = 0,
- [49] = 209,
- [50] = 210,
- [51] = 211,
- [52] = 212,
- [53] = 213,
- [54] = 214,
- [55] = 338,
- [56] = 216,
- [57] = 217,
- [58] = 218,
- [59] = 219,
- [60] = 220,
- [61] = 376,
- [62] = 0,
- [63] = 223,
- [64] = 224,
- [65] = 225,
- [66] = 226,
- [67] = 227,
- [68] = 228,
- [69] = 229,
- [70] = 230,
- [71] = 231,
- [72] = 232,
- [73] = 233,
- [74] = 234,
- [75] = 235,
- [76] = 236,
- [77] = 237,
- [78] = 238,
- [79] = 239,
- [80] = 0,
- [81] = 241,
- [82] = 242,
- [83] = 243,
- [84] = 244,
- [85] = 245,
- [86] = 246,
- [87] = 339,
- [88] = 248,
- [89] = 249,
- [90] = 250,
- [91] = 251,
- [92] = 252,
- [93] = 255,
- [94] = 0,
- [95] = -1, /* undefined */
-};
-
-/*
- * DEC special graphics character set. See here for its definition:
- * http://vt100.net/docs/vt220-rm/table2-4.html
- * This contains several characters to create ASCII drawings and similar. Its
- * commonly mapped into GR to extend the basic ASCII characters.
- *
- * Lower 62 characters map to ASCII 33-64, everything beyond is special and
- * commonly used for ASCII drawings. It depends on the Unicode Standard 3.2 for
- * the extended horizontal scan-line characters 3, 5, 7, and 9.
- */
-term_charset term_dec_special_graphics = {
- [0] = -1, /* undefined */
- [1] = 33,
- [2] = 34,
- [3] = 35,
- [4] = 36,
- [5] = 37,
- [6] = 38,
- [7] = 39,
- [8] = 40,
- [9] = 41,
- [10] = 42,
- [11] = 43,
- [12] = 44,
- [13] = 45,
- [14] = 46,
- [15] = 47,
- [16] = 48,
- [17] = 49,
- [18] = 50,
- [19] = 51,
- [20] = 52,
- [21] = 53,
- [22] = 54,
- [23] = 55,
- [24] = 56,
- [25] = 57,
- [26] = 58,
- [27] = 59,
- [28] = 60,
- [29] = 61,
- [30] = 62,
- [31] = 63,
- [32] = 64,
- [33] = 65,
- [34] = 66,
- [35] = 67,
- [36] = 68,
- [37] = 69,
- [38] = 70,
- [39] = 71,
- [40] = 72,
- [41] = 73,
- [42] = 74,
- [43] = 75,
- [44] = 76,
- [45] = 77,
- [46] = 78,
- [47] = 79,
- [48] = 80,
- [49] = 81,
- [50] = 82,
- [51] = 83,
- [52] = 84,
- [53] = 85,
- [54] = 86,
- [55] = 87,
- [56] = 88,
- [57] = 89,
- [58] = 90,
- [59] = 91,
- [60] = 92,
- [61] = 93,
- [62] = 94,
- [63] = 0,
- [64] = 9830,
- [65] = 9618,
- [66] = 9225,
- [67] = 9228,
- [68] = 9229,
- [69] = 9226,
- [70] = 176,
- [71] = 177,
- [72] = 9252,
- [73] = 9227,
- [74] = 9496,
- [75] = 9488,
- [76] = 9484,
- [77] = 9492,
- [78] = 9532,
- [79] = 9146,
- [80] = 9147,
- [81] = 9472,
- [82] = 9148,
- [83] = 9149,
- [84] = 9500,
- [85] = 9508,
- [86] = 9524,
- [87] = 9516,
- [88] = 9474,
- [89] = 8804,
- [90] = 8805,
- [91] = 960,
- [92] = 8800,
- [93] = 163,
- [94] = 8901,
- [95] = -1, /* undefined */
-};
diff --git a/src/libsystemd-terminal/term-internal.h b/src/libsystemd-terminal/term-internal.h
deleted file mode 100644
index 8c6a00188c..0000000000
--- a/src/libsystemd-terminal/term-internal.h
+++ /dev/null
@@ -1,650 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "term.h"
-#include "util.h"
-
-typedef struct term_char term_char_t;
-typedef struct term_charbuf term_charbuf_t;
-
-typedef struct term_cell term_cell;
-typedef struct term_line term_line;
-
-typedef struct term_page term_page;
-typedef struct term_history term_history;
-
-typedef uint32_t term_charset[96];
-typedef struct term_state term_state;
-
-/*
- * Miscellaneous
- * Sundry things and external helpers.
- */
-
-int mk_wcwidth(wchar_t ucs4);
-int mk_wcwidth_cjk(wchar_t ucs4);
-int mk_wcswidth(const wchar_t *str, size_t len);
-int mk_wcswidth_cjk(const wchar_t *str, size_t len);
-
-/*
- * Characters
- * Each cell in a terminal page contains only a single character. This is
- * usually a single UCS-4 value. However, Unicode allows combining-characters,
- * therefore, the number of UCS-4 characters per cell must be unlimited. The
- * term_char_t object wraps the internal combining char API so it can be
- * treated as a single object.
- */
-
-struct term_char {
- /* never access this value directly */
- uint64_t _value;
-};
-
-struct term_charbuf {
- /* 3 bytes + zero-terminator */
- uint32_t buf[4];
-};
-
-#define TERM_CHAR_INIT(_val) ((term_char_t){ ._value = (_val) })
-#define TERM_CHAR_NULL TERM_CHAR_INIT(0)
-
-term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4);
-term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4);
-term_char_t term_char_dup(term_char_t ch);
-term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4);
-
-const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b);
-unsigned int term_char_lookup_width(term_char_t ch);
-
-/* true if @ch is TERM_CHAR_NULL, otherwise false */
-static inline bool term_char_is_null(term_char_t ch) {
- return ch._value == 0;
-}
-
-/* true if @ch is dynamically allocated and needs to be freed */
-static inline bool term_char_is_allocated(term_char_t ch) {
- return !term_char_is_null(ch) && !(ch._value & 0x1);
-}
-
-/* true if (a == b), otherwise false; this is (a == b), NOT (*a == *b) */
-static inline bool term_char_same(term_char_t a, term_char_t b) {
- return a._value == b._value;
-}
-
-/* true if (*a == *b), otherwise false; this is implied by (a == b) */
-static inline bool term_char_equal(term_char_t a, term_char_t b) {
- const uint32_t *sa, *sb;
- term_charbuf_t ca, cb;
- size_t na, nb;
-
- sa = term_char_resolve(a, &na, &ca);
- sb = term_char_resolve(b, &nb, &cb);
- return na == nb && !memcmp(sa, sb, sizeof(*sa) * na);
-}
-
-/* free @ch in case it is dynamically allocated */
-static inline term_char_t term_char_free(term_char_t ch) {
- if (term_char_is_allocated(ch))
- term_char_set(ch, 0);
-
- return TERM_CHAR_NULL;
-}
-
-/* gcc _cleanup_ helpers */
-#define _term_char_free_ _cleanup_(term_char_freep)
-static inline void term_char_freep(term_char_t *p) {
- term_char_free(*p);
-}
-
-/*
- * Cells
- * The term_cell structure respresents a single cell in a terminal page. It
- * contains the stored character, the age of the cell and all its attributes.
- */
-
-struct term_cell {
- term_char_t ch; /* stored char or TERM_CHAR_NULL */
- term_age_t age; /* cell age or TERM_AGE_NULL */
- term_attr attr; /* cell attributes */
- unsigned int cwidth; /* cached term_char_lookup_width(cell->ch) */
-};
-
-/*
- * Lines
- * Instead of storing cells in a 2D array, we store them in an array of
- * dynamically allocated lines. This way, scrolling can be implemented very
- * fast without moving any cells at all. Similarly, the scrollback-buffer is
- * much simpler to implement.
- * We use term_line to store a single line. It contains an array of cells, a
- * fill-state which remembers the amount of blanks on the right side, a
- * separate age just for the line which can overwrite the age for all cells,
- * and some management data.
- */
-
-struct term_line {
- term_line *lines_next; /* linked-list for histories */
- term_line *lines_prev; /* linked-list for histories */
-
- unsigned int width; /* visible width of line */
- unsigned int n_cells; /* # of allocated cells */
- term_cell *cells; /* cell-array */
-
- term_age_t age; /* line age */
- unsigned int fill; /* # of valid cells; starting left */
-};
-
-int term_line_new(term_line **out);
-term_line *term_line_free(term_line *line);
-
-#define _term_line_free_ _cleanup_(term_line_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_line*, term_line_free);
-
-int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width);
-void term_line_set_width(term_line *line, unsigned int width);
-void term_line_write(term_line *line, unsigned int pos_x, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode);
-void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
-void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age);
-void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age);
-void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected);
-void term_line_reset(term_line *line, const term_attr *attr, term_age_t age);
-
-void term_line_link(term_line *line, term_line **first, term_line **last);
-void term_line_link_tail(term_line *line, term_line **first, term_line **last);
-void term_line_unlink(term_line *line, term_line **first, term_line **last);
-
-#define TERM_LINE_LINK(_line, _head) term_line_link((_line), &(_head)->lines_first, &(_head)->lines_last)
-#define TERM_LINE_LINK_TAIL(_line, _head) term_line_link_tail((_line), &(_head)->lines_first, &(_head)->lines_last)
-#define TERM_LINE_UNLINK(_line, _head) term_line_unlink((_line), &(_head)->lines_first, &(_head)->lines_last)
-
-/*
- * Pages
- * A page represents the 2D table containing all cells of a terminal. It stores
- * lines as an array of pointers so scrolling becomes a simple line-shuffle
- * operation.
- * Scrolling is always targeted only at the scroll-region defined via scroll_idx
- * and scroll_num. The fill-state keeps track of the number of touched lines in
- * the scroll-region. @width and @height describe the visible region of the page
- * and are guaranteed to be allocated at all times.
- */
-
-struct term_page {
- term_age_t age; /* page age */
-
- term_line **lines; /* array of line-pointers */
- term_line **line_cache; /* cache for temporary operations */
- unsigned int n_lines; /* # of allocated lines */
-
- unsigned int width; /* width of visible area */
- unsigned int height; /* height of visible area */
- unsigned int scroll_idx; /* scrolling-region start index */
- unsigned int scroll_num; /* scrolling-region length in lines */
- unsigned int scroll_fill; /* # of valid scroll-lines */
-};
-
-int term_page_new(term_page **out);
-term_page *term_page_free(term_page *page);
-
-#define _term_page_free_ _cleanup_(term_page_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_page*, term_page_free);
-
-term_cell *term_page_get_cell(term_page *page, unsigned int x, unsigned int y);
-
-int term_page_reserve(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age);
-void term_page_resize(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age, term_history *history);
-void term_page_write(term_page *page, unsigned int pos_x, unsigned int pos_y, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode);
-void term_page_insert_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age);
-void term_page_delete_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age);
-void term_page_append_combchar(term_page *page, unsigned int pos_x, unsigned int pos_y, uint32_t ucs4, term_age_t age);
-void term_page_erase(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int to_x, unsigned int to_y, const term_attr *attr, term_age_t age, bool keep_protected);
-void term_page_reset(term_page *page, const term_attr *attr, term_age_t age);
-
-void term_page_set_scroll_region(term_page *page, unsigned int idx, unsigned int num);
-void term_page_scroll_up(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
-void term_page_scroll_down(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history);
-void term_page_insert_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
-void term_page_delete_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age);
-
-/*
- * Histories
- * Scroll-back buffers use term_history objects to store scroll-back lines. A
- * page is independent of the history used. All page operations that modify a
- * history take it as separate argument. You're free to pass NULL at all times
- * if no history should be used.
- * Lines are stored in a linked list as no complex operations are ever done on
- * history lines, besides pushing/poping. Note that history lines do not have a
- * guaranteed minimum length. Any kind of line might be stored there. Missing
- * cells should be cleared to the background color.
- */
-
-struct term_history {
- term_line *lines_first;
- term_line *lines_last;
- unsigned int n_lines;
- unsigned int max_lines;
-};
-
-int term_history_new(term_history **out);
-term_history *term_history_free(term_history *history);
-
-#define _term_history_free_ _cleanup_(term_history_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_history*, term_history_free);
-
-void term_history_clear(term_history *history);
-void term_history_trim(term_history *history, unsigned int max);
-void term_history_push(term_history *history, term_line *line);
-term_line *term_history_pop(term_history *history, unsigned int reserve_width, const term_attr *attr, term_age_t age);
-unsigned int term_history_peek(term_history *history, unsigned int max, unsigned int reserve_width, const term_attr *attr, term_age_t age);
-
-/*
- * Parsers
- * The term_parser object parses control-sequences for both host and terminal
- * side. Based on this parser, there is a set of command-parsers that take a
- * term_seq sequence and returns the command it represents. This is different
- * for host and terminal side so a different set of parsers is provided.
- */
-
-enum {
- TERM_SEQ_NONE, /* placeholder, no sequence parsed */
-
- TERM_SEQ_IGNORE, /* no-op character */
- TERM_SEQ_GRAPHIC, /* graphic character */
- TERM_SEQ_CONTROL, /* control character */
- TERM_SEQ_ESCAPE, /* escape sequence */
- TERM_SEQ_CSI, /* control sequence function */
- TERM_SEQ_DCS, /* device control string */
- TERM_SEQ_OSC, /* operating system control */
-
- TERM_SEQ_CNT
-};
-
-enum {
- /* these must be kept compatible to (1U << (ch - 0x20)) */
-
- TERM_SEQ_FLAG_SPACE = (1U << 0), /* char: */
- TERM_SEQ_FLAG_BANG = (1U << 1), /* char: ! */
- TERM_SEQ_FLAG_DQUOTE = (1U << 2), /* char: " */
- TERM_SEQ_FLAG_HASH = (1U << 3), /* char: # */
- TERM_SEQ_FLAG_CASH = (1U << 4), /* char: $ */
- TERM_SEQ_FLAG_PERCENT = (1U << 5), /* char: % */
- TERM_SEQ_FLAG_AND = (1U << 6), /* char: & */
- TERM_SEQ_FLAG_SQUOTE = (1U << 7), /* char: ' */
- TERM_SEQ_FLAG_POPEN = (1U << 8), /* char: ( */
- TERM_SEQ_FLAG_PCLOSE = (1U << 9), /* char: ) */
- TERM_SEQ_FLAG_MULT = (1U << 10), /* char: * */
- TERM_SEQ_FLAG_PLUS = (1U << 11), /* char: + */
- TERM_SEQ_FLAG_COMMA = (1U << 12), /* char: , */
- TERM_SEQ_FLAG_MINUS = (1U << 13), /* char: - */
- TERM_SEQ_FLAG_DOT = (1U << 14), /* char: . */
- TERM_SEQ_FLAG_SLASH = (1U << 15), /* char: / */
-
- /* 16-35 is reserved for numbers; unused */
-
- /* COLON is reserved = (1U << 26), char: : */
- /* SEMICOLON is reserved = (1U << 27), char: ; */
- TERM_SEQ_FLAG_LT = (1U << 28), /* char: < */
- TERM_SEQ_FLAG_EQUAL = (1U << 29), /* char: = */
- TERM_SEQ_FLAG_GT = (1U << 30), /* char: > */
- TERM_SEQ_FLAG_WHAT = (1U << 31), /* char: ? */
-};
-
-enum {
- TERM_CMD_NONE, /* placeholder */
- TERM_CMD_GRAPHIC, /* graphics character */
-
- TERM_CMD_BEL, /* bell */
- TERM_CMD_BS, /* backspace */
- TERM_CMD_CBT, /* cursor-backward-tabulation */
- TERM_CMD_CHA, /* cursor-horizontal-absolute */
- TERM_CMD_CHT, /* cursor-horizontal-forward-tabulation */
- TERM_CMD_CNL, /* cursor-next-line */
- TERM_CMD_CPL, /* cursor-previous-line */
- TERM_CMD_CR, /* carriage-return */
- TERM_CMD_CUB, /* cursor-backward */
- TERM_CMD_CUD, /* cursor-down */
- TERM_CMD_CUF, /* cursor-forward */
- TERM_CMD_CUP, /* cursor-position */
- TERM_CMD_CUU, /* cursor-up */
- TERM_CMD_DA1, /* primary-device-attributes */
- TERM_CMD_DA2, /* secondary-device-attributes */
- TERM_CMD_DA3, /* tertiary-device-attributes */
- TERM_CMD_DC1, /* device-control-1 or XON */
- TERM_CMD_DC3, /* device-control-3 or XOFF */
- TERM_CMD_DCH, /* delete-character */
- TERM_CMD_DECALN, /* screen-alignment-pattern */
- TERM_CMD_DECANM, /* ansi-mode */
- TERM_CMD_DECBI, /* back-index */
- TERM_CMD_DECCARA, /* change-attributes-in-rectangular-area */
- TERM_CMD_DECCRA, /* copy-rectangular-area */
- TERM_CMD_DECDC, /* delete-column */
- TERM_CMD_DECDHL_BH, /* double-width-double-height-line: bottom half */
- TERM_CMD_DECDHL_TH, /* double-width-double-height-line: top half */
- TERM_CMD_DECDWL, /* double-width-single-height-line */
- TERM_CMD_DECEFR, /* enable-filter-rectangle */
- TERM_CMD_DECELF, /* enable-local-functions */
- TERM_CMD_DECELR, /* enable-locator-reporting */
- TERM_CMD_DECERA, /* erase-rectangular-area */
- TERM_CMD_DECFI, /* forward-index */
- TERM_CMD_DECFRA, /* fill-rectangular-area */
- TERM_CMD_DECIC, /* insert-column */
- TERM_CMD_DECID, /* return-terminal-id */
- TERM_CMD_DECINVM, /* invoke-macro */
- TERM_CMD_DECKBD, /* keyboard-language-selection */
- TERM_CMD_DECKPAM, /* keypad-application-mode */
- TERM_CMD_DECKPNM, /* keypad-numeric-mode */
- TERM_CMD_DECLFKC, /* local-function-key-control */
- TERM_CMD_DECLL, /* load-leds */
- TERM_CMD_DECLTOD, /* load-time-of-day */
- TERM_CMD_DECPCTERM, /* pcterm-mode */
- TERM_CMD_DECPKA, /* program-key-action */
- TERM_CMD_DECPKFMR, /* program-key-free-memory-report */
- TERM_CMD_DECRARA, /* reverse-attributes-in-rectangular-area */
- TERM_CMD_DECRC, /* restore-cursor */
- TERM_CMD_DECREQTPARM, /* request-terminal-parameters */
- TERM_CMD_DECRPKT, /* report-key-type */
- TERM_CMD_DECRQCRA, /* request-checksum-of-rectangular-area */
- TERM_CMD_DECRQDE, /* request-display-extent */
- TERM_CMD_DECRQKT, /* request-key-type */
- TERM_CMD_DECRQLP, /* request-locator-position */
- TERM_CMD_DECRQM_ANSI, /* request-mode-ansi */
- TERM_CMD_DECRQM_DEC, /* request-mode-dec */
- TERM_CMD_DECRQPKFM, /* request-program-key-free-memory */
- TERM_CMD_DECRQPSR, /* request-presentation-state-report */
- TERM_CMD_DECRQTSR, /* request-terminal-state-report */
- TERM_CMD_DECRQUPSS, /* request-user-preferred-supplemental-set */
- TERM_CMD_DECSACE, /* select-attribute-change-extent */
- TERM_CMD_DECSASD, /* select-active-status-display */
- TERM_CMD_DECSC, /* save-cursor */
- TERM_CMD_DECSCA, /* select-character-protection-attribute */
- TERM_CMD_DECSCL, /* select-conformance-level */
- TERM_CMD_DECSCP, /* select-communication-port */
- TERM_CMD_DECSCPP, /* select-columns-per-page */
- TERM_CMD_DECSCS, /* select-communication-speed */
- TERM_CMD_DECSCUSR, /* set-cursor-style */
- TERM_CMD_DECSDDT, /* select-disconnect-delay-time */
- TERM_CMD_DECSDPT, /* select-digital-printed-data-type */
- TERM_CMD_DECSED, /* selective-erase-in-display */
- TERM_CMD_DECSEL, /* selective-erase-in-line */
- TERM_CMD_DECSERA, /* selective-erase-rectangular-area */
- TERM_CMD_DECSFC, /* select-flow-control */
- TERM_CMD_DECSKCV, /* set-key-click-volume */
- TERM_CMD_DECSLCK, /* set-lock-key-style */
- TERM_CMD_DECSLE, /* select-locator-events */
- TERM_CMD_DECSLPP, /* set-lines-per-page */
- TERM_CMD_DECSLRM_OR_SC, /* set-left-and-right-margins or save-cursor */
- TERM_CMD_DECSMBV, /* set-margin-bell-volume */
- TERM_CMD_DECSMKR, /* select-modifier-key-reporting */
- TERM_CMD_DECSNLS, /* set-lines-per-screen */
- TERM_CMD_DECSPP, /* set-port-parameter */
- TERM_CMD_DECSPPCS, /* select-pro-printer-character-set */
- TERM_CMD_DECSPRTT, /* select-printer-type */
- TERM_CMD_DECSR, /* secure-reset */
- TERM_CMD_DECSRFR, /* select-refresh-rate */
- TERM_CMD_DECSSCLS, /* set-scroll-speed */
- TERM_CMD_DECSSDT, /* select-status-display-line-type */
- TERM_CMD_DECSSL, /* select-setup-language */
- TERM_CMD_DECST8C, /* set-tab-at-every-8-columns */
- TERM_CMD_DECSTBM, /* set-top-and-bottom-margins */
- TERM_CMD_DECSTR, /* soft-terminal-reset */
- TERM_CMD_DECSTRL, /* set-transmit-rate-limit */
- TERM_CMD_DECSWBV, /* set-warning-bell-volume */
- TERM_CMD_DECSWL, /* single-width-single-height-line */
- TERM_CMD_DECTID, /* select-terminal-id */
- TERM_CMD_DECTME, /* terminal-mode-emulation */
- TERM_CMD_DECTST, /* invoke-confidence-test */
- TERM_CMD_DL, /* delete-line */
- TERM_CMD_DSR_ANSI, /* device-status-report-ansi */
- TERM_CMD_DSR_DEC, /* device-status-report-dec */
- TERM_CMD_ECH, /* erase-character */
- TERM_CMD_ED, /* erase-in-display */
- TERM_CMD_EL, /* erase-in-line */
- TERM_CMD_ENQ, /* enquiry */
- TERM_CMD_EPA, /* end-of-guarded-area */
- TERM_CMD_FF, /* form-feed */
- TERM_CMD_HPA, /* horizontal-position-absolute */
- TERM_CMD_HPR, /* horizontal-position-relative */
- TERM_CMD_HT, /* horizontal-tab */
- TERM_CMD_HTS, /* horizontal-tab-set */
- TERM_CMD_HVP, /* horizontal-and-vertical-position */
- TERM_CMD_ICH, /* insert-character */
- TERM_CMD_IL, /* insert-line */
- TERM_CMD_IND, /* index */
- TERM_CMD_LF, /* line-feed */
- TERM_CMD_LS1R, /* locking-shift-1-right */
- TERM_CMD_LS2, /* locking-shift-2 */
- TERM_CMD_LS2R, /* locking-shift-2-right */
- TERM_CMD_LS3, /* locking-shift-3 */
- TERM_CMD_LS3R, /* locking-shift-3-right */
- TERM_CMD_MC_ANSI, /* media-copy-ansi */
- TERM_CMD_MC_DEC, /* media-copy-dec */
- TERM_CMD_NEL, /* next-line */
- TERM_CMD_NP, /* next-page */
- TERM_CMD_NULL, /* null */
- TERM_CMD_PP, /* preceding-page */
- TERM_CMD_PPA, /* page-position-absolute */
- TERM_CMD_PPB, /* page-position-backward */
- TERM_CMD_PPR, /* page-position-relative */
- TERM_CMD_RC, /* restore-cursor */
- TERM_CMD_REP, /* repeat */
- TERM_CMD_RI, /* reverse-index */
- TERM_CMD_RIS, /* reset-to-initial-state */
- TERM_CMD_RM_ANSI, /* reset-mode-ansi */
- TERM_CMD_RM_DEC, /* reset-mode-dec */
- TERM_CMD_S7C1T, /* set-7bit-c1-terminal */
- TERM_CMD_S8C1T, /* set-8bit-c1-terminal */
- TERM_CMD_SCS, /* select-character-set */
- TERM_CMD_SD, /* scroll-down */
- TERM_CMD_SGR, /* select-graphics-rendition */
- TERM_CMD_SI, /* shift-in */
- TERM_CMD_SM_ANSI, /* set-mode-ansi */
- TERM_CMD_SM_DEC, /* set-mode-dec */
- TERM_CMD_SO, /* shift-out */
- TERM_CMD_SPA, /* start-of-protected-area */
- TERM_CMD_SS2, /* single-shift-2 */
- TERM_CMD_SS3, /* single-shift-3 */
- TERM_CMD_ST, /* string-terminator */
- TERM_CMD_SU, /* scroll-up */
- TERM_CMD_SUB, /* substitute */
- TERM_CMD_TBC, /* tab-clear */
- TERM_CMD_VPA, /* vertical-line-position-absolute */
- TERM_CMD_VPR, /* vertical-line-position-relative */
- TERM_CMD_VT, /* vertical-tab */
- TERM_CMD_XTERM_CLLHP, /* xterm-cursor-lower-left-hp-bugfix */
- TERM_CMD_XTERM_IHMT, /* xterm-initiate-highlight-mouse-tracking */
- TERM_CMD_XTERM_MLHP, /* xterm-memory-lock-hp-bugfix */
- TERM_CMD_XTERM_MUHP, /* xterm-memory-unlock-hp-bugfix */
- TERM_CMD_XTERM_RPM, /* xterm-restore-private-mode */
- TERM_CMD_XTERM_RRV, /* xterm-reset-resource-value */
- TERM_CMD_XTERM_RTM, /* xterm-reset-title-mode */
- TERM_CMD_XTERM_SACL1, /* xterm-set-ansi-conformance-level-1 */
- TERM_CMD_XTERM_SACL2, /* xterm-set-ansi-conformance-level-2 */
- TERM_CMD_XTERM_SACL3, /* xterm-set-ansi-conformance-level-3 */
- TERM_CMD_XTERM_SDCS, /* xterm-set-default-character-set */
- TERM_CMD_XTERM_SGFX, /* xterm-sixel-graphics */
- TERM_CMD_XTERM_SPM, /* xterm-set-private-mode */
- TERM_CMD_XTERM_SRV, /* xterm-set-resource-value */
- TERM_CMD_XTERM_STM, /* xterm-set-title-mode */
- TERM_CMD_XTERM_SUCS, /* xterm-set-utf8-character-set */
- TERM_CMD_XTERM_WM, /* xterm-window-management */
-
- TERM_CMD_CNT
-};
-
-enum {
- /*
- * Charsets: DEC marks charsets according to "Digital Equ. Corp.".
- * NRCS marks charsets according to the "National Replacement
- * Character Sets". ISO marks charsets according to ISO-8859.
- * The USERDEF charset is special and can be modified by the host.
- */
-
- TERM_CHARSET_NONE,
-
- /* 96-compat charsets */
- TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
- TERM_CHARSET_BRITISH_NRCS = TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL,
- TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
- TERM_CHARSET_AMERICAN_NRCS = TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL,
- TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL,
- TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL,
- TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL,
- TERM_CHARSET_ISO_LATIN_CYRILLIC,
-
- TERM_CHARSET_96_CNT,
-
- /* 94-compat charsets */
- TERM_CHARSET_DEC_SPECIAL_GRAPHIC = TERM_CHARSET_96_CNT,
- TERM_CHARSET_DEC_SUPPLEMENTAL,
- TERM_CHARSET_DEC_TECHNICAL,
- TERM_CHARSET_CYRILLIC_DEC,
- TERM_CHARSET_DUTCH_NRCS,
- TERM_CHARSET_FINNISH_NRCS,
- TERM_CHARSET_FRENCH_NRCS,
- TERM_CHARSET_FRENCH_CANADIAN_NRCS,
- TERM_CHARSET_GERMAN_NRCS,
- TERM_CHARSET_GREEK_DEC,
- TERM_CHARSET_GREEK_NRCS,
- TERM_CHARSET_HEBREW_DEC,
- TERM_CHARSET_HEBREW_NRCS,
- TERM_CHARSET_ITALIAN_NRCS,
- TERM_CHARSET_NORWEGIAN_DANISH_NRCS,
- TERM_CHARSET_PORTUGUESE_NRCS,
- TERM_CHARSET_RUSSIAN_NRCS,
- TERM_CHARSET_SCS_NRCS,
- TERM_CHARSET_SPANISH_NRCS,
- TERM_CHARSET_SWEDISH_NRCS,
- TERM_CHARSET_SWISS_NRCS,
- TERM_CHARSET_TURKISH_DEC,
- TERM_CHARSET_TURKISH_NRCS,
-
- TERM_CHARSET_94_CNT,
-
- /* special charsets */
- TERM_CHARSET_USERPREF_SUPPLEMENTAL = TERM_CHARSET_94_CNT,
-
- TERM_CHARSET_CNT,
-};
-
-extern term_charset term_unicode_lower;
-extern term_charset term_unicode_upper;
-extern term_charset term_dec_supplemental_graphics;
-extern term_charset term_dec_special_graphics;
-
-#define TERM_PARSER_ARG_MAX (16)
-#define TERM_PARSER_ST_MAX (4096)
-
-struct term_seq {
- unsigned int type;
- unsigned int command;
- uint32_t terminator;
- unsigned int intermediates;
- unsigned int charset;
- unsigned int n_args;
- int args[TERM_PARSER_ARG_MAX];
- unsigned int n_st;
- char *st;
-};
-
-struct term_parser {
- term_seq seq;
- size_t st_alloc;
- unsigned int state;
-
- bool is_host : 1;
-};
-
-/*
- * Screens
- * A term_screen object represents the terminal-side of the communication. It
- * connects the term-parser and term-pages and handles all required commands.
- * All state is managed by it.
- */
-
-enum {
- TERM_FLAG_7BIT_MODE = (1U << 0), /* 7bit mode (default: on) */
- TERM_FLAG_HIDE_CURSOR = (1U << 1), /* hide cursor caret (default: off) */
- TERM_FLAG_INHIBIT_TPARM = (1U << 2), /* do not send TPARM unrequested (default: off) */
- TERM_FLAG_NEWLINE_MODE = (1U << 3), /* perform carriage-return on line-feeds (default: off) */
- TERM_FLAG_PENDING_WRAP = (1U << 4), /* wrap-around is pending */
- TERM_FLAG_KEYPAD_MODE = (1U << 5), /* application-keypad mode (default: off) */
- TERM_FLAG_CURSOR_KEYS = (1U << 6), /* enable application cursor-keys (default: off) */
-};
-
-enum {
- TERM_CONFORMANCE_LEVEL_VT52,
- TERM_CONFORMANCE_LEVEL_VT100,
- TERM_CONFORMANCE_LEVEL_VT400,
- TERM_CONFORMANCE_LEVEL_CNT,
-};
-
-struct term_state {
- unsigned int cursor_x;
- unsigned int cursor_y;
- term_attr attr;
- term_charset **gl;
- term_charset **gr;
- term_charset **glt;
- term_charset **grt;
-
- bool auto_wrap : 1;
- bool origin_mode : 1;
-};
-
-struct term_screen {
- unsigned long ref;
- term_age_t age;
-
- term_page *page;
- term_page *page_main;
- term_page *page_alt;
- term_history *history;
- term_history *history_main;
-
- unsigned int n_tabs;
- uint8_t *tabs;
-
- term_utf8 utf8;
- term_parser *parser;
-
- term_screen_write_fn write_fn;
- void *write_fn_data;
- term_screen_cmd_fn cmd_fn;
- void *cmd_fn_data;
-
- unsigned int flags;
- unsigned int conformance_level;
- term_attr default_attr;
-
- term_charset *g0;
- term_charset *g1;
- term_charset *g2;
- term_charset *g3;
-
- char *answerback;
-
- term_state state;
- term_state saved;
- term_state saved_alt;
-};
diff --git a/src/libsystemd-terminal/term-page.c b/src/libsystemd-terminal/term-page.c
deleted file mode 100644
index bac85200f1..0000000000
--- a/src/libsystemd-terminal/term-page.c
+++ /dev/null
@@ -1,2091 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Terminal Page/Line/Cell/Char Handling
- * This file implements page handling of a terminal. It is split into pages,
- * lines, cells and characters. Each object is independent of the next upper
- * object.
- *
- * The Terminal layer keeps each line of a terminal separate and dynamically
- * allocated. This allows us to move lines from main-screen to history-buffers
- * very fast. Same is true for scrolling, top/bottom borders and other buffer
- * operations.
- *
- * While lines are dynamically allocated, cells are not. This would be a waste
- * of memory and causes heavy fragmentation. Furthermore, cells are moved much
- * less frequently than lines so the performance-penalty is pretty small.
- * However, to support combining-characters, we have to initialize and cleanup
- * cells properly and cannot just release the underlying memory. Therefore,
- * cells are treated as proper objects despite being allocated in arrays.
- *
- * Each cell has a set of attributes and a stored character. This is usually a
- * single Unicode character stored as 32bit UCS-4 char. However, we need to
- * support Unicode combining-characters, therefore this gets more complicated.
- * Characters themselves are represented by a "term_char_t" object. It
- * should be treated as a normal integer and passed by value. The
- * surrounding struct is just to hide the internals. A term-char can contain a
- * base character together with up to 2 combining-chars in a single integer.
- * Only if you need more combining-chars (very unlikely!) a term-char is a
- * pointer to an allocated storage. This requires you to always free term-char
- * objects once no longer used (even though this is a no-op most of the time).
- * Furthermore, term-char objects are not ref-counted so you must duplicate them
- * in case you want to store it somewhere and retain a copy yourself. By
- * convention, all functions that take a term-char object will not duplicate
- * it but implicitly take ownership of the passed value. It's up to the caller
- * to duplicate it beforehand, in case it wants to retain a copy.
- *
- * If it turns out, that more than 2 comb-chars become common in specific
- * languages, we can try to optimize this. One idea is to ref-count allocated
- * characters and store them in a hash-table (like gnome's libvte3 does). This
- * way we will never have two allocated chars for the same content. Or we can
- * simply put two uint64_t into a "term_char_t". This will slow down operations
- * on systems that don't need that many comb-chars, but avoid the dynamic
- * allocations on others.
- * Anyhow, until we have proper benchmarks, we will keep the current code. It
- * seems to compete very well with other solutions so far.
- *
- * The page-layer is a one-dimensional array of lines. Considering that each
- * line is a one-dimensional array of cells, the page layer provides the
- * two-dimensional cell-page required for terminals. The page itself only
- * operates on lines. All cell-related operations are forwarded to the correct
- * line.
- * A page does not contain any cursor tracking. It only provides the raw
- * operations to shuffle lines and modify the page.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-
-/* maximum UCS-4 character */
-#define CHAR_UCS4_MAX (0x10ffff)
-/* mask for valid UCS-4 characters (21bit) */
-#define CHAR_UCS4_MASK (0x1fffff)
-/* UCS-4 replacement character */
-#define CHAR_UCS4_REPLACEMENT (0xfffd)
-
-/* real storage behind "term_char_t" in case it's not packed */
-typedef struct term_character {
- uint8_t n;
- uint32_t codepoints[];
-} term_character;
-
-/*
- * char_pack() takes 3 UCS-4 values and packs them into a term_char_t object.
- * Note that UCS-4 chars only take 21 bits, so we still have the LSB as marker.
- * We set it to 1 so others can distinguish it from pointers.
- */
-static inline term_char_t char_pack(uint32_t v1, uint32_t v2, uint32_t v3) {
- uint64_t packed, u1, u2, u3;
-
- u1 = v1;
- u2 = v2;
- u3 = v3;
-
- packed = 0x01;
- packed |= (u1 & (uint64_t)CHAR_UCS4_MASK) << 43;
- packed |= (u2 & (uint64_t)CHAR_UCS4_MASK) << 22;
- packed |= (u3 & (uint64_t)CHAR_UCS4_MASK) << 1;
-
- return TERM_CHAR_INIT(packed);
-}
-
-#define char_pack1(_v1) char_pack2((_v1), CHAR_UCS4_MAX + 1)
-#define char_pack2(_v1, _v2) char_pack3((_v1), (_v2), CHAR_UCS4_MAX + 1)
-#define char_pack3(_v1, _v2, _v3) char_pack((_v1), (_v2), (_v3))
-
-/*
- * char_unpack() is the inverse of char_pack(). It extracts the 3 stored UCS-4
- * characters and returns them. Note that this does not validate the passed
- * term_char_t. That's the responsibility of the caller.
- * This returns the number of characters actually packed. This obviously is a
- * number between 0 and 3 (inclusive).
- */
-static inline uint8_t char_unpack(term_char_t packed, uint32_t *out_v1, uint32_t *out_v2, uint32_t *out_v3) {
- uint32_t v1, v2, v3;
-
- v1 = (packed._value >> 43) & (uint64_t)CHAR_UCS4_MASK;
- v2 = (packed._value >> 22) & (uint64_t)CHAR_UCS4_MASK;
- v3 = (packed._value >> 1) & (uint64_t)CHAR_UCS4_MASK;
-
- if (out_v1)
- *out_v1 = v1;
- if (out_v2)
- *out_v2 = v2;
- if (out_v3)
- *out_v3 = v3;
-
- return (v1 > CHAR_UCS4_MAX) ? 0 :
- ((v2 > CHAR_UCS4_MAX) ? 1 :
- ((v3 > CHAR_UCS4_MAX) ? 2 :
- 3));
-}
-
-/* cast a term_char_t to a term_character* */
-static inline term_character *char_to_ptr(term_char_t ch) {
- return (term_character*)(unsigned long)ch._value;
-}
-
-/* cast a term_character* to a term_char_t */
-static inline term_char_t char_from_ptr(term_character *c) {
- return TERM_CHAR_INIT((unsigned long)c);
-}
-
-/*
- * char_alloc() allocates a properly aligned term_character object and returns
- * a pointer to it. NULL is returned on allocation errors. The object will have
- * enough room for @n following UCS-4 chars.
- * Note that we allocate (n+1) characters and set the last one to 0 in case
- * anyone prints this string for debugging.
- */
-static term_character *char_alloc(uint8_t n) {
- term_character *c;
- int r;
-
- r = posix_memalign((void**)&c,
- MAX(sizeof(void*), (size_t)2),
- sizeof(*c) + sizeof(*c->codepoints) * (n + 1));
- if (r)
- return NULL;
-
- c->n = n;
- c->codepoints[n] = 0;
-
- return c;
-}
-
-/*
- * char_free() frees the memory allocated via char_alloc(). It is safe to call
- * this on any term_char_t, only allocated characters are freed.
- */
-static inline void char_free(term_char_t ch) {
- if (term_char_is_allocated(ch))
- free(char_to_ptr(ch));
-}
-
-/*
- * This appends @append_ucs4 to the existing character @base and returns
- * it as a new character. In case that's not possible, @base is returned. The
- * caller can use term_char_same() to test whether the returned character was
- * freshly allocated or not.
- */
-static term_char_t char_build(term_char_t base, uint32_t append_ucs4) {
- /* soft-limit for combining-chars; hard-limit is currently 255 */
- const size_t climit = 64;
- term_character *c;
- uint32_t buf[3], *t;
- uint8_t n;
-
- /* ignore invalid UCS-4 */
- if (append_ucs4 > CHAR_UCS4_MAX)
- return base;
-
- if (term_char_is_null(base)) {
- return char_pack1(append_ucs4);
- } else if (!term_char_is_allocated(base)) {
- /* unpack and try extending the packed character */
- n = char_unpack(base, &buf[0], &buf[1], &buf[2]);
-
- switch (n) {
- case 0:
- return char_pack1(append_ucs4);
- case 1:
- if (climit < 2)
- return base;
-
- return char_pack2(buf[0], append_ucs4);
- case 2:
- if (climit < 3)
- return base;
-
- return char_pack3(buf[0], buf[1], append_ucs4);
- default:
- /* fallthrough */
- break;
- }
-
- /* already fully packed, we need to allocate a new one */
- t = buf;
- } else {
- /* already an allocated type, we need to allocate a new one */
- c = char_to_ptr(base);
- t = c->codepoints;
- n = c->n;
- }
-
- /* bail out if soft-limit is reached */
- if (n >= climit)
- return base;
-
- /* allocate new char */
- c = char_alloc(n + 1);
- if (!c)
- return base;
-
- memcpy(c->codepoints, t, sizeof(*t) * n);
- c->codepoints[n] = append_ucs4;
-
- return char_from_ptr(c);
-}
-
-/**
- * term_char_set() - Reset character to a single UCS-4 character
- * @previous: term-char to reset
- * @append_ucs4: UCS-4 char to set
- *
- * This frees all resources in @previous and re-initializes it to @append_ucs4.
- * The new char is returned.
- *
- * Usually, this is used like this:
- * obj->ch = term_char_set(obj->ch, ucs4);
- *
- * Returns: The previous character reset to @append_ucs4.
- */
-term_char_t term_char_set(term_char_t previous, uint32_t append_ucs4) {
- char_free(previous);
- return char_build(TERM_CHAR_NULL, append_ucs4);
-}
-
-/**
- * term_char_merge() - Merge UCS-4 char at the end of an existing char
- * @base: existing term-char
- * @append_ucs4: UCS-4 character to append
- *
- * This appends @append_ucs4 to @base and returns the result. @base is
- * invalidated by this function and must no longer be used. The returned value
- * replaces the old one.
- *
- * Usually, this is used like this:
- * obj->ch = term_char_merge(obj->ch, ucs4);
- *
- * Returns: The new merged character.
- */
-term_char_t term_char_merge(term_char_t base, uint32_t append_ucs4) {
- term_char_t ch;
-
- ch = char_build(base, append_ucs4);
- if (!term_char_same(ch, base))
- term_char_free(base);
-
- return ch;
-}
-
-/**
- * term_char_dup() - Duplicate character
- * @ch: character to duplicate
- *
- * This duplicates a term-character. In case the character is not allocated,
- * nothing is done. Otherwise, the underlying memory is copied and returned. You
- * need to call term_char_free() on the returned character to release it again.
- * On allocation errors, a replacement character is returned. Therefore, the
- * caller can safely assume that this function always succeeds.
- *
- * Returns: The duplicated term-character.
- */
-term_char_t term_char_dup(term_char_t ch) {
- term_character *c, *newc;
-
- if (!term_char_is_allocated(ch))
- return ch;
-
- c = char_to_ptr(ch);
- newc = char_alloc(c->n);
- if (!newc)
- return char_pack1(CHAR_UCS4_REPLACEMENT);
-
- memcpy(newc->codepoints, c->codepoints, sizeof(*c->codepoints) * c->n);
- return char_from_ptr(newc);
-}
-
-/**
- * term_char_dup_append() - Duplicate tsm-char with UCS-4 character appended
- * @base: existing term-char
- * @append_ucs4: UCS-4 character to append
- *
- * This is similar to term_char_merge(), but it returns a separately allocated
- * character. That is, @base will stay valid after this returns and is not
- * touched. In case the append-operation fails, @base is duplicated and
- * returned. That is, the returned char is always independent of @base.
- *
- * Returns: Newly allocated character with @append_ucs4 appended to @base.
- */
-term_char_t term_char_dup_append(term_char_t base, uint32_t append_ucs4) {
- term_char_t ch;
-
- ch = char_build(base, append_ucs4);
- if (term_char_same(ch, base))
- ch = term_char_dup(base);
-
- return ch;
-}
-
-/**
- * term_char_resolve() - Retrieve the UCS-4 string for a term-char
- * @ch: character to resolve
- * @s: storage for size of string or NULL
- * @b: storage for string or NULL
- *
- * This takes a term-character and returns the UCS-4 string associated with it.
- * In case @ch is not allocated, the string is stored in @b (in case @b is NULL
- * static storage is used). Otherwise, a pointer to the allocated storage is
- * returned.
- *
- * The returned string is only valid as long as @ch and @b are valid. The string
- * is zero-terminated and can safely be printed via long-character printf().
- * The length of the string excluding the zero-character is returned in @s.
- *
- * This never returns NULL. Even if the size is 0, this points to a buffer of at
- * least a zero-terminator.
- *
- * Returns: The UCS-4 string-representation of @ch, and its size in @s.
- */
-const uint32_t *term_char_resolve(term_char_t ch, size_t *s, term_charbuf_t *b) {
- static term_charbuf_t static_b;
- term_character *c;
- uint32_t *cache;
- size_t len;
-
- if (b)
- cache = b->buf;
- else
- cache = static_b.buf;
-
- if (term_char_is_null(ch)) {
- len = 0;
- cache[0] = 0;
- } else if (term_char_is_allocated(ch)) {
- c = char_to_ptr(ch);
- len = c->n;
- cache = c->codepoints;
- } else {
- len = char_unpack(ch, &cache[0], &cache[1], &cache[2]);
- cache[len] = 0;
- }
-
- if (s)
- *s = len;
-
- return cache;
-}
-
-/**
- * term_char_lookup_width() - Lookup cell-width of a character
- * @ch: character to return cell-width for
- *
- * This is an equivalent of wcwidth() for term_char_t. It can deal directly
- * with UCS-4 and combining-characters and avoids the mess that is wchar_t and
- * locale handling.
- *
- * Returns: 0 for unprintable characters, >0 for everything else.
- */
-unsigned int term_char_lookup_width(term_char_t ch) {
- term_charbuf_t b;
- const uint32_t *str;
- unsigned int max;
- size_t i, len;
- int r;
-
- max = 0;
- str = term_char_resolve(ch, &len, &b);
-
- for (i = 0; i < len; ++i) {
- /*
- * Oh god, C99 locale handling strikes again: wcwidth() expects
- * wchar_t, but there is no way for us to know the
- * internal encoding of wchar_t. Moreover, it is nearly
- * impossible to convert UCS-4 into wchar_t (except for iconv,
- * which is way too much overhead).
- * Therefore, we use our own copy of wcwidth(). Lets just hope
- * that glibc will one day export it's internal UCS-4 and UTF-8
- * helpers for direct use.
- */
- assert_cc(sizeof(wchar_t) >= 4);
- r = mk_wcwidth((wchar_t)str[i]);
- if (r > 0 && (unsigned int)r > max)
- max = r;
- }
-
- return max;
-}
-
-/**
- * term_cell_init() - Initialize a new cell
- * @cell: cell to initialize
- * @ch: character to set on the cell or TERM_CHAR_NULL
- * @cwidth: character width of @ch
- * @attr: attributes to set on the cell or NULL
- * @age: age to set on the cell or TERM_AGE_NULL
- *
- * This initializes a new cell. The backing-memory of the cell must be allocated
- * by the caller beforehand. The caller is responsible to destroy the cell via
- * term_cell_destroy() before freeing the backing-memory.
- *
- * It is safe (and supported!) to use:
- * zero(*c);
- * instead of:
- * term_cell_init(c, TERM_CHAR_NULL, NULL, TERM_AGE_NULL);
- *
- * Note that this call takes ownership of @ch. If you want to use it yourself
- * after this call, you need to duplicate it before calling this.
- */
-static void term_cell_init(term_cell *cell, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age) {
- assert(cell);
-
- cell->ch = ch;
- cell->cwidth = cwidth;
- cell->age = age;
-
- if (attr)
- memcpy(&cell->attr, attr, sizeof(*attr));
- else
- zero(cell->attr);
-}
-
-/**
- * term_cell_destroy() - Destroy previously initialized cell
- * @cell: cell to destroy or NULL
- *
- * This releases all resources associated with a cell. The backing memory is
- * kept as-is. It's the responsibility of the caller to manage it.
- *
- * You must not call any other cell operations on this cell after this call
- * returns. You must re-initialize the cell via term_cell_init() before you can
- * use it again.
- *
- * If @cell is NULL, this is a no-op.
- */
-static void term_cell_destroy(term_cell *cell) {
- if (!cell)
- return;
-
- term_char_free(cell->ch);
-}
-
-/**
- * term_cell_set() - Change contents of a cell
- * @cell: cell to modify
- * @ch: character to set on the cell or cell->ch
- * @cwidth: character width of @ch or cell->cwidth
- * @attr: attributes to set on the cell or NULL
- * @age: age to set on the cell or cell->age
- *
- * This changes the contents of a cell. It can be used to change the character,
- * attributes and age. To keep the current character, pass cell->ch as @ch. To
- * reset the current attributes, pass NULL. To keep the current age, pass
- * cell->age.
- *
- * This call takes ownership of @ch. You need to duplicate it first, in case you
- * want to use it for your own purposes after this call.
- *
- * The cell must have been initialized properly before calling this. See
- * term_cell_init().
- */
-static void term_cell_set(term_cell *cell, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age) {
- assert(cell);
-
- if (!term_char_same(ch, cell->ch)) {
- term_char_free(cell->ch);
- cell->ch = ch;
- }
-
- cell->cwidth = cwidth;
- cell->age = age;
-
- if (attr)
- memcpy(&cell->attr, attr, sizeof(*attr));
- else
- zero(cell->attr);
-}
-
-/**
- * term_cell_append() - Append a combining-char to a cell
- * @cell: cell to modify
- * @ucs4: UCS-4 character to append to the cell
- * @age: new age to set on the cell or cell->age
- *
- * This appends a combining-character to a cell. No validation of the UCS-4
- * character is done, so this can be used to append any character. Additionally,
- * this can update the age of the cell.
- *
- * The cell must have been initialized properly before calling this. See
- * term_cell_init().
- */
-static void term_cell_append(term_cell *cell, uint32_t ucs4, term_age_t age) {
- assert(cell);
-
- cell->ch = term_char_merge(cell->ch, ucs4);
- cell->age = age;
-}
-
-/**
- * term_cell_init_n() - Initialize an array of cells
- * @cells: pointer to an array of cells to initialize
- * @n: number of cells
- * @attr: attributes to set on all cells or NULL
- * @age: age to set on all cells
- *
- * This is the same as term_cell_init() but initializes an array of cells.
- * Furthermore, this always sets the character to TERM_CHAR_NULL.
- * If you want to set a specific characters on all cells, you need to hard-code
- * this loop and duplicate the character for each cell.
- */
-static void term_cell_init_n(term_cell *cells, unsigned int n, const term_attr *attr, term_age_t age) {
- for ( ; n > 0; --n, ++cells)
- term_cell_init(cells, TERM_CHAR_NULL, 0, attr, age);
-}
-
-/**
- * term_cell_destroy_n() - Destroy an array of cells
- * @cells: pointer to an array of cells to destroy
- * @n: number of cells
- *
- * This is the same as term_cell_destroy() but destroys an array of cells.
- */
-static void term_cell_destroy_n(term_cell *cells, unsigned int n) {
- for ( ; n > 0; --n, ++cells)
- term_cell_destroy(cells);
-}
-
-/**
- * term_cell_clear_n() - Clear contents of an array of cells
- * @cells: pointer to an array of cells to modify
- * @n: number of cells
- * @attr: attributes to set on all cells or NULL
- * @age: age to set on all cells
- *
- * This is the same as term_cell_set() but operates on an array of cells. Note
- * that all characters are always set to TERM_CHAR_NULL, unlike term_cell_set()
- * which takes the character as argument.
- * If you want to set a specific characters on all cells, you need to hard-code
- * this loop and duplicate the character for each cell.
- */
-static void term_cell_clear_n(term_cell *cells, unsigned int n, const term_attr *attr, term_age_t age) {
- for ( ; n > 0; --n, ++cells)
- term_cell_set(cells, TERM_CHAR_NULL, 0, attr, age);
-}
-
-/**
- * term_line_new() - Allocate a new line
- * @out: place to store pointer to new line
- *
- * This allocates and initialized a new line. The line is unlinked and
- * independent of any page. It can be used for any purpose. The initial
- * cell-count is set to 0.
- *
- * The line has to be freed via term_line_free() once it's no longer needed.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_line_new(term_line **out) {
- _term_line_free_ term_line *line = NULL;
-
- assert_return(out, -EINVAL);
-
- line = new0(term_line, 1);
- if (!line)
- return -ENOMEM;
-
- *out = line;
- line = NULL;
- return 0;
-}
-
-/**
- * term_line_free() - Free a line
- * @line: line to free or NULL
- *
- * This frees a line that was previously allocated via term_line_free(). All its
- * cells are released, too.
- *
- * If @line is NULL, this is a no-op.
- */
-term_line *term_line_free(term_line *line) {
- if (!line)
- return NULL;
-
- term_cell_destroy_n(line->cells, line->n_cells);
- free(line->cells);
- free(line);
-
- return NULL;
-}
-
-/**
- * term_line_reserve() - Pre-allocate cells for a line
- * @line: line to pre-allocate cells for
- * @width: numbers of cells the line shall have pre-allocated
- * @attr: attribute for all allocated cells or NULL
- * @age: current age for all modifications
- * @protect_width: width to protect from erasure
- *
- * This pre-allocates cells for this line. Please note that @width is the number
- * of cells the line is guaranteed to have allocated after this call returns.
- * It's not the number of cells that are added, neither is it the new width of
- * the line.
- *
- * This function never frees memory. That is, reducing the line-width will
- * always succeed, same is true for increasing the width to a previously set
- * width.
- *
- * @attr and @age are used to initialize new cells. Additionally, any
- * existing cell outside of the protected area specified by @protect_width are
- * cleared and reset with @attr and @age.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_line_reserve(term_line *line, unsigned int width, const term_attr *attr, term_age_t age, unsigned int protect_width) {
- unsigned int min_width;
- term_cell *t;
-
- assert_return(line, -EINVAL);
-
- /* reset existing cells if required */
- min_width = MIN(line->n_cells, width);
- if (min_width > protect_width)
- term_cell_clear_n(line->cells + protect_width,
- min_width - protect_width,
- attr,
- age);
-
- /* allocate new cells if required */
-
- if (width > line->n_cells) {
- t = realloc_multiply(line->cells, sizeof(*t), width);
- if (!t)
- return -ENOMEM;
-
- if (!attr && !age)
- memzero(t + line->n_cells,
- sizeof(*t) * (width - line->n_cells));
- else
- term_cell_init_n(t + line->n_cells,
- width - line->n_cells,
- attr,
- age);
-
- line->cells = t;
- line->n_cells = width;
- }
-
- line->fill = MIN(line->fill, protect_width);
-
- return 0;
-}
-
-/**
- * term_line_set_width() - Change width of a line
- * @line: line to modify
- * @width: new width
- *
- * This changes the actual width of a line. It is the caller's responsibility
- * to use term_line_reserve() to make sure enough space is allocated. If @width
- * is greater than the allocated size, it is cropped.
- *
- * This does not modify any cells. Use term_line_reserve() or term_line_erase()
- * to clear any newly added cells.
- *
- * NOTE: The fill state is cropped at line->width. Therefore, if you increase
- * the line-width afterwards, but there is a multi-cell character at the
- * end of the line that got cropped, then the fill-state will _not_ be
- * adjusted.
- * This means, the fill-state always includes the cells up to the start
- * of the right-most character, but it might or might not cover it until
- * its end. This should be totally fine, though. You should never access
- * multi-cell tails directly, anyway.
- */
-void term_line_set_width(term_line *line, unsigned int width) {
- assert(line);
-
- if (width > line->n_cells)
- width = line->n_cells;
-
- line->width = width;
- line->fill = MIN(line->fill, width);
-}
-
-/**
- * line_insert() - Insert characters and move existing cells to the right
- * @from: position to insert cells at
- * @num: number of cells to insert
- * @head_char: character that is set on the first cell
- * @head_cwidth: character-length of @head_char
- * @attr: attribute for all inserted cells or NULL
- * @age: current age for all modifications
- *
- * The INSERT operation (or writes with INSERT_MODE) writes data at a specific
- * position on a line and shifts the existing cells to the right. Cells that are
- * moved beyond the right hand border are discarded.
- *
- * This helper contains the actual INSERT implementation which is independent of
- * the data written. It works on cells, not on characters. The first cell is set
- * to @head_char, all others are reset to TERM_CHAR_NULL. See each caller for a
- * more detailed description.
- */
-static inline void line_insert(term_line *line, unsigned int from, unsigned int num, term_char_t head_char, unsigned int head_cwidth, const term_attr *attr, term_age_t age) {
- unsigned int i, rem, move;
-
- if (from >= line->width)
- return;
- if (from + num < from || from + num > line->width)
- num = line->width - from;
- if (!num)
- return;
-
- move = line->width - from - num;
- rem = MIN(num, move);
-
- if (rem > 0) {
- /*
- * Make room for @num cells; shift cells to the right if
- * required. @rem is the number of remaining cells that we will
- * knock off on the right and overwrite during the right shift.
- *
- * For INSERT_MODE, @num/@rem are usually 1 or 2, @move is 50%
- * of the line on average. Therefore, the actual move is quite
- * heavy and we can safely invalidate cells manually instead of
- * the whole line.
- * However, for INSERT operations, any parameters are
- * possible. But we cannot place any assumption on its usage
- * across applications, so we just handle it the same as
- * INSERT_MODE and do per-cell invalidation.
- */
-
- /* destroy cells that are knocked off on the right */
- term_cell_destroy_n(line->cells + line->width - rem, rem);
-
- /* move remaining bulk of cells */
- memmove(line->cells + from + num,
- line->cells + from,
- sizeof(*line->cells) * move);
-
- /* invalidate cells */
- for (i = 0; i < move; ++i)
- line->cells[from + num + i].age = age;
-
- /* initialize fresh head-cell */
- term_cell_init(line->cells + from,
- head_char,
- head_cwidth,
- attr,
- age);
-
- /* initialize fresh tail-cells */
- term_cell_init_n(line->cells + from + 1,
- num - 1,
- attr,
- age);
-
- /* adjust fill-state */
- line->fill = MIN(line->width,
- MAX(line->fill + num,
- from + num));
- } else {
- /* modify head-cell */
- term_cell_set(line->cells + from,
- head_char,
- head_cwidth,
- attr,
- age);
-
- /* reset tail-cells */
- term_cell_clear_n(line->cells + from + 1,
- num - 1,
- attr,
- age);
-
- /* adjust fill-state */
- line->fill = line->width;
- }
-}
-
-/**
- * term_line_write() - Write to a single, specific cell
- * @line: line to write to
- * @pos_x: x-position of cell in @line to write to
- * @ch: character to write to the cell
- * @cwidth: character width of @ch
- * @attr: attributes to set on the cell or NULL
- * @age: current age for all modifications
- * @insert_mode: true if INSERT-MODE is enabled
- *
- * This writes to a specific cell in a line. The cell is addressed by its
- * X-position @pos_x. If that cell does not exist, this is a no-op.
- *
- * @ch and @attr are set on this cell.
- *
- * If @insert_mode is true, this inserts the character instead of overwriting
- * existing data (existing data is now moved to the right before writing).
- *
- * This function is the low-level handler of normal writes to a terminal.
- */
-void term_line_write(term_line *line, unsigned int pos_x, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode) {
- unsigned int len;
-
- assert(line);
-
- if (pos_x >= line->width)
- return;
-
- len = MAX(1U, cwidth);
- if (pos_x + len < pos_x || pos_x + len > line->width)
- len = line->width - pos_x;
- if (!len)
- return;
-
- if (insert_mode) {
- /* Use line_insert() to insert the character-head and fill
- * the remains with NULLs. */
- line_insert(line, pos_x, len, ch, cwidth, attr, age);
- } else {
- /* modify head-cell */
- term_cell_set(line->cells + pos_x, ch, cwidth, attr, age);
-
- /* reset tail-cells */
- term_cell_clear_n(line->cells + pos_x + 1,
- len - 1,
- attr,
- age);
-
- /* adjust fill-state */
- line->fill = MIN(line->width,
- MAX(line->fill,
- pos_x + len));
- }
-}
-
-/**
- * term_line_insert() - Insert empty cells
- * @line: line to insert empty cells into
- * @from: x-position where to insert cells
- * @num: number of cells to insert
- * @attr: attributes to set on the cells or NULL
- * @age: current age for all modifications
- *
- * This inserts @num empty cells at position @from in line @line. All existing
- * cells to the right are shifted to make room for the new cells. Cells that get
- * pushed beyond the right hand border are discarded.
- */
-void term_line_insert(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age) {
- /* use line_insert() to insert @num empty cells */
- return line_insert(line, from, num, TERM_CHAR_NULL, 0, attr, age);
-}
-
-/**
- * term_line_delete() - Delete cells from line
- * @line: line to delete cells from
- * @from: position to delete cells at
- * @num: number of cells to delete
- * @attr: attributes to set on any new cells
- * @age: current age for all modifications
- *
- * Delete cells from a line. All cells to the right of the deleted cells are
- * shifted to the left to fill the empty space. New cells appearing on the right
- * hand border are cleared and initialized with @attr.
- */
-void term_line_delete(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age) {
- unsigned int rem, move, i;
-
- assert(line);
-
- if (from >= line->width)
- return;
- if (from + num < from || from + num > line->width)
- num = line->width - from;
- if (!num)
- return;
-
- /* destroy and move as many upfront as possible */
- move = line->width - from - num;
- rem = MIN(num, move);
- if (rem > 0) {
- /* destroy to be removed cells */
- term_cell_destroy_n(line->cells + from, rem);
-
- /* move tail upfront */
- memmove(line->cells + from,
- line->cells + from + num,
- sizeof(*line->cells) * move);
-
- /* invalidate copied cells */
- for (i = 0; i < move; ++i)
- line->cells[from + i].age = age;
-
- /* initialize tail that was moved away */
- term_cell_init_n(line->cells + line->width - rem,
- rem,
- attr,
- age);
-
- /* reset remaining cells in case the move was too small */
- if (num > move)
- term_cell_clear_n(line->cells + from + move,
- num - move,
- attr,
- age);
- } else {
- /* reset cells */
- term_cell_clear_n(line->cells + from,
- num,
- attr,
- age);
- }
-
- /* adjust fill-state */
- if (from + num < line->fill)
- line->fill -= num;
- else if (from < line->fill)
- line->fill = from;
-}
-
-/**
- * term_line_append_combchar() - Append combining char to existing cell
- * @line: line to modify
- * @pos_x: position of cell to append combining char to
- * @ucs4: combining character to append
- * @age: current age for all modifications
- *
- * Unicode allows trailing combining characters, which belong to the
- * char in front of them. The caller is responsible of detecting
- * combining characters and calling term_line_append_combchar() instead of
- * term_line_write(). This simply appends the char to the correct cell then.
- * If the cell is not in the visible area, this call is skipped.
- *
- * Note that control-sequences are not 100% compatible with combining
- * characters as they require delayed parsing. However, we must handle
- * control-sequences immediately. Therefore, there might be trailing
- * combining chars that should be discarded by the parser.
- * However, to prevent programming errors, we're also being pedantic
- * here and discard weirdly placed combining chars. This prevents
- * situations were invalid content is parsed into the terminal and you
- * might end up with cells containing only combining chars.
- *
- * Long story short: To get combining-characters working with old-fashioned
- * terminal-emulation, we parse them exclusively for direct cell-writes. Other
- * combining-characters are usually simply discarded and ignored.
- */
-void term_line_append_combchar(term_line *line, unsigned int pos_x, uint32_t ucs4, term_age_t age) {
- assert(line);
-
- if (pos_x >= line->width)
- return;
-
- /* Unused cell? Skip appending any combining chars then. */
- if (term_char_is_null(line->cells[pos_x].ch))
- return;
-
- term_cell_append(line->cells + pos_x, ucs4, age);
-}
-
-/**
- * term_line_erase() - Erase parts of a line
- * @line: line to modify
- * @from: position to start the erase
- * @num: number of cells to erase
- * @attr: attributes to initialize erased cells with
- * @age: current age for all modifications
- * @keep_protected: true if protected cells should be kept
- *
- * This is the standard erase operation. It clears all cells in the targeted
- * area and re-initializes them. Cells to the right are not shifted left, you
- * must use DELETE to achieve that. Cells outside the visible area are skipped.
- *
- * If @keep_protected is true, protected cells will not be erased.
- */
-void term_line_erase(term_line *line, unsigned int from, unsigned int num, const term_attr *attr, term_age_t age, bool keep_protected) {
- term_cell *cell;
- unsigned int i, last_protected;
-
- assert(line);
-
- if (from >= line->width)
- return;
- if (from + num < from || from + num > line->width)
- num = line->width - from;
- if (!num)
- return;
-
- last_protected = 0;
- for (i = 0; i < num; ++i) {
- cell = line->cells + from + i;
- if (keep_protected && cell->attr.protect) {
- /* only count protected-cells inside the fill-region */
- if (from + i < line->fill)
- last_protected = from + i;
-
- continue;
- }
-
- term_cell_set(cell, TERM_CHAR_NULL, 0, attr, age);
- }
-
- /* Adjust fill-state. This is a bit tricks, we can only adjust it in
- * case the erase-region starts inside the fill-region and ends at the
- * tail or beyond the fill-region. Otherwise, the current fill-state
- * stays as it was.
- * Furthermore, we must account for protected cells. The loop above
- * ensures that protected-cells are only accounted for if they're
- * inside the fill-region. */
- if (from < line->fill && from + num >= line->fill)
- line->fill = MAX(from, last_protected);
-}
-
-/**
- * term_line_reset() - Reset a line
- * @line: line to reset
- * @attr: attributes to initialize all cells with
- * @age: current age for all modifications
- *
- * This resets all visible cells of a line and sets their attributes and ages
- * to @attr and @age. This is equivalent to erasing a whole line via
- * term_line_erase().
- */
-void term_line_reset(term_line *line, const term_attr *attr, term_age_t age) {
- assert(line);
-
- return term_line_erase(line, 0, line->width, attr, age, 0);
-}
-
-/**
- * term_line_link() - Link line in front of a list
- * @line: line to link
- * @first: member pointing to first entry
- * @last: member pointing to last entry
- *
- * This links a line into a list of lines. The line is inserted at the front and
- * must not be linked, yet. See the TERM_LINE_LINK() macro for an easier usage of
- * this.
- */
-void term_line_link(term_line *line, term_line **first, term_line **last) {
- assert(line);
- assert(first);
- assert(last);
- assert(!line->lines_prev);
- assert(!line->lines_next);
-
- line->lines_prev = NULL;
- line->lines_next = *first;
- if (*first)
- (*first)->lines_prev = line;
- else
- *last = line;
- *first = line;
-}
-
-/**
- * term_line_link_tail() - Link line at tail of a list
- * @line: line to link
- * @first: member pointing to first entry
- * @last: member pointing to last entry
- *
- * Same as term_line_link() but links the line at the tail.
- */
-void term_line_link_tail(term_line *line, term_line **first, term_line **last) {
- assert(line);
- assert(first);
- assert(last);
- assert(!line->lines_prev);
- assert(!line->lines_next);
-
- line->lines_next = NULL;
- line->lines_prev = *last;
- if (*last)
- (*last)->lines_next = line;
- else
- *first = line;
- *last = line;
-}
-
-/**
- * term_line_unlink() - Unlink line from a list
- * @line: line to unlink
- * @first: member pointing to first entry
- * @last: member pointing to last entry
- *
- * This unlinks a previously linked line. See TERM_LINE_UNLINK() for an easier to
- * use macro.
- */
-void term_line_unlink(term_line *line, term_line **first, term_line **last) {
- assert(line);
- assert(first);
- assert(last);
-
- if (line->lines_prev)
- line->lines_prev->lines_next = line->lines_next;
- else
- *first = line->lines_next;
- if (line->lines_next)
- line->lines_next->lines_prev = line->lines_prev;
- else
- *last = line->lines_prev;
-
- line->lines_prev = NULL;
- line->lines_next = NULL;
-}
-
-/**
- * term_page_new() - Allocate new page
- * @out: storage for pointer to new page
- *
- * Allocate a new page. The initial dimensions are 0/0.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_page_new(term_page **out) {
- _term_page_free_ term_page *page = NULL;
-
- assert_return(out, -EINVAL);
-
- page = new0(term_page, 1);
- if (!page)
- return -ENOMEM;
-
- *out = page;
- page = NULL;
- return 0;
-}
-
-/**
- * term_page_free() - Free page
- * @page: page to free or NULL
- *
- * Free a previously allocated page and all associated data. If @page is NULL,
- * this is a no-op.
- *
- * Returns: NULL
- */
-term_page *term_page_free(term_page *page) {
- unsigned int i;
-
- if (!page)
- return NULL;
-
- for (i = 0; i < page->n_lines; ++i)
- term_line_free(page->lines[i]);
-
- free(page->line_cache);
- free(page->lines);
- free(page);
-
- return NULL;
-}
-
-/**
- * term_page_get_cell() - Return pointer to requested cell
- * @page: page to operate on
- * @x: x-position of cell
- * @y: y-position of cell
- *
- * This returns a pointer to the cell at position @x/@y. You're free to modify
- * this cell as much as you like. However, once you call any other function on
- * the page, you must drop the pointer to the cell.
- *
- * Returns: Pointer to the cell or NULL if out of the visible area.
- */
-term_cell *term_page_get_cell(term_page *page, unsigned int x, unsigned int y) {
- assert_return(page, NULL);
-
- if (x >= page->width)
- return NULL;
- if (y >= page->height)
- return NULL;
-
- return &page->lines[y]->cells[x];
-}
-
-/**
- * page_scroll_up() - Scroll up
- * @page: page to operate on
- * @new_width: width to use for any new line moved into the visible area
- * @num: number of lines to scroll up
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for old lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are cleared and reset
- * with the given attributes. Old lines are moved into the history if non-NULL.
- * If a new line is allocated, moved from the history buffer or moved from
- * outside the visible region into the visible region, this call makes sure it
- * has at least @width cells allocated. If a possible memory-allocation fails,
- * the previous line is reused. This has the side effect, that it will not be
- * linked into the history buffer.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-static void page_scroll_up(term_page *page, unsigned int new_width, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- term_line *line, **cache;
- unsigned int i;
- int r;
-
- assert(page);
-
- if (num > page->scroll_num)
- num = page->scroll_num;
- if (num < 1)
- return;
-
- /* Better safe than sorry: avoid under-allocating lines, even when
- * resizing. */
- new_width = MAX(new_width, page->width);
-
- cache = page->line_cache;
-
- /* Try moving lines into history and allocate new lines for each moved
- * line. In case allocation fails, or if we have no history, reuse the
- * line.
- * We keep the lines in the line-cache so we can safely move the
- * remaining lines around. */
- for (i = 0; i < num; ++i) {
- line = page->lines[page->scroll_idx + i];
-
- r = -EAGAIN;
- if (history) {
- r = term_line_new(&cache[i]);
- if (r >= 0) {
- r = term_line_reserve(cache[i],
- new_width,
- attr,
- age,
- 0);
- if (r < 0)
- term_line_free(cache[i]);
- else
- term_line_set_width(cache[i], page->width);
- }
- }
-
- if (r >= 0) {
- term_history_push(history, line);
- } else {
- cache[i] = line;
- term_line_reset(line, attr, age);
- }
- }
-
- if (num < page->scroll_num) {
- memmove(page->lines + page->scroll_idx,
- page->lines + page->scroll_idx + num,
- sizeof(*page->lines) * (page->scroll_num - num));
-
- /* update age of moved lines */
- for (i = 0; i < page->scroll_num - num; ++i)
- page->lines[page->scroll_idx + i]->age = age;
- }
-
- /* copy remaining lines from cache; age is already updated */
- memcpy(page->lines + page->scroll_idx + page->scroll_num - num,
- cache,
- sizeof(*cache) * num);
-
- /* update fill */
- page->scroll_fill -= MIN(page->scroll_fill, num);
-}
-
-/**
- * page_scroll_down() - Scroll down
- * @page: page to operate on
- * @new_width: width to use for any new line moved into the visible area
- * @num: number of lines to scroll down
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for new lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are retrieved from
- * the history or cleared if the history is empty or NULL.
- *
- * Usually, scroll-down implies that new lines are cleared. Therefore, you're
- * highly encouraged to set @history to NULL. However, if you resize a terminal,
- * you might want to include history-lines in the new area. In that case, you
- * should set @history to non-NULL.
- *
- * If a new line is allocated, moved from the history buffer or moved from
- * outside the visible region into the visible region, this call makes sure it
- * has at least @width cells allocated. If a possible memory-allocation fails,
- * the previous line is reused. This will have the side-effect that lines from
- * the history will not get visible on-screen but kept in history.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-static void page_scroll_down(term_page *page, unsigned int new_width, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- term_line *line, **cache, *t;
- unsigned int i, last_idx;
-
- assert(page);
-
- if (num > page->scroll_num)
- num = page->scroll_num;
- if (num < 1)
- return;
-
- /* Better safe than sorry: avoid under-allocating lines, even when
- * resizing. */
- new_width = MAX(new_width, page->width);
-
- cache = page->line_cache;
- last_idx = page->scroll_idx + page->scroll_num - 1;
-
- /* Try pulling out lines from history; if history is empty or if no
- * history is given, we reuse the to-be-removed lines. Otherwise, those
- * lines are released. */
- for (i = 0; i < num; ++i) {
- line = page->lines[last_idx - i];
-
- t = NULL;
- if (history)
- t = term_history_pop(history, new_width, attr, age);
-
- if (t) {
- cache[num - 1 - i] = t;
- term_line_free(line);
- } else {
- cache[num - 1 - i] = line;
- term_line_reset(line, attr, age);
- }
- }
-
- if (num < page->scroll_num) {
- memmove(page->lines + page->scroll_idx + num,
- page->lines + page->scroll_idx,
- sizeof(*page->lines) * (page->scroll_num - num));
-
- /* update age of moved lines */
- for (i = 0; i < page->scroll_num - num; ++i)
- page->lines[page->scroll_idx + num + i]->age = age;
- }
-
- /* copy remaining lines from cache; age is already updated */
- memcpy(page->lines + page->scroll_idx,
- cache,
- sizeof(*cache) * num);
-
- /* update fill; but only if there's already content in it */
- if (page->scroll_fill > 0)
- page->scroll_fill = MIN(page->scroll_num,
- page->scroll_fill + num);
-}
-
-/**
- * page_reserve() - Reserve page area
- * @page: page to modify
- * @cols: required columns (width)
- * @rows: required rows (height)
- * @attr: attributes for newly allocated cells
- * @age: age to set on any modified cells
- *
- * This allocates the required amount of lines and cells to guarantee that the
- * page has at least the demanded dimensions of @cols x @rows. Note that this
- * never shrinks the page-memory. We keep cells allocated for performance
- * reasons.
- *
- * Additionally to allocating lines, this also clears any newly added cells so
- * you can safely change the size afterwards without clearing new cells.
- *
- * Note that you must be careful what operations you call on the page between
- * page_reserve() and updating page->width/height. Any newly allocated line (or
- * shifted line) might not meet your new width/height expectations.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_page_reserve(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age) {
- _term_line_free_ term_line *line = NULL;
- unsigned int i, min_lines;
- term_line **t;
- int r;
-
- assert_return(page, -EINVAL);
-
- /*
- * First make sure the first MIN(page->n_lines, rows) lines have at
- * least the required width of @cols. This does not modify any visible
- * cells in the existing @page->width x @page->height area, therefore,
- * we can safely bail out afterwards in case anything else fails.
- * Note that lines in between page->height and page->n_lines might be
- * shorter than page->width. Hence, we need to resize them all, but we
- * can skip some of them for better performance.
- */
- min_lines = MIN(page->n_lines, rows);
- for (i = 0; i < min_lines; ++i) {
- /* lines below page->height have at least page->width cells */
- if (cols < page->width && i < page->height)
- continue;
-
- r = term_line_reserve(page->lines[i],
- cols,
- attr,
- age,
- (i < page->height) ? page->width : 0);
- if (r < 0)
- return r;
- }
-
- /*
- * We now know the first @min_lines lines have at least width @cols and
- * are prepared for resizing. We now only have to allocate any
- * additional lines below @min_lines in case @rows is greater than
- * page->n_lines.
- */
- if (rows > page->n_lines) {
- t = realloc_multiply(page->lines, sizeof(*t), rows);
- if (!t)
- return -ENOMEM;
- page->lines = t;
-
- t = realloc_multiply(page->line_cache, sizeof(*t), rows);
- if (!t)
- return -ENOMEM;
- page->line_cache = t;
-
- while (page->n_lines < rows) {
- r = term_line_new(&line);
- if (r < 0)
- return r;
-
- r = term_line_reserve(line, cols, attr, age, 0);
- if (r < 0)
- return r;
-
- page->lines[page->n_lines++] = line;
- line = NULL;
- }
- }
-
- return 0;
-}
-
-/**
- * term_page_resize() - Resize page
- * @page: page to modify
- * @cols: number of columns (width)
- * @rows: number of rows (height)
- * @attr: attributes for newly allocated cells
- * @age: age to set on any modified cells
- * @history: history buffer to use for new/old lines or NULL
- *
- * This changes the visible dimensions of a page. You must have called
- * term_page_reserve() beforehand, otherwise, this will fail.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-void term_page_resize(term_page *page, unsigned int cols, unsigned int rows, const term_attr *attr, term_age_t age, term_history *history) {
- unsigned int i, num, empty, max, old_height;
- term_line *line;
-
- assert(page);
- assert(page->n_lines >= rows);
-
- old_height = page->height;
-
- if (rows < old_height) {
- /*
- * If we decrease the terminal-height, we emulate a scroll-up.
- * This way, existing data from the scroll-area is moved into
- * the history, making space at the bottom to reduce the screen
- * height. In case the scroll-fill indicates empty lines, we
- * reduce the amount of scrolled lines.
- * Once scrolled, we have to move the lower margin from below
- * the scroll area up so it is preserved.
- */
-
- /* move lines to history if scroll region is filled */
- num = old_height - rows;
- empty = page->scroll_num - page->scroll_fill;
- if (num > empty)
- page_scroll_up(page,
- cols,
- num - empty,
- attr,
- age,
- history);
-
- /* move lower margin up; drop its lines if not enough space */
- num = LESS_BY(old_height, page->scroll_idx + page->scroll_num);
- max = LESS_BY(rows, page->scroll_idx);
- num = MIN(num, max);
- if (num > 0) {
- unsigned int top, bottom;
-
- top = rows - num;
- bottom = page->scroll_idx + page->scroll_num;
-
- /* might overlap; must run topdown, not bottomup */
- for (i = 0; i < num; ++i) {
- line = page->lines[top + i];
- page->lines[top + i] = page->lines[bottom + i];
- page->lines[bottom + i] = line;
- }
- }
-
- /* update vertical extents */
- page->height = rows;
- page->scroll_idx = MIN(page->scroll_idx, rows);
- page->scroll_num -= MIN(page->scroll_num, old_height - rows);
- /* fill is already up-to-date or 0 due to scroll-up */
- } else if (rows > old_height) {
- /*
- * If we increase the terminal-height, we emulate a scroll-down
- * and fetch new lines from the history.
- * New lines are always accounted to the scroll-region. Thus we
- * have to preserve the lower margin first, by moving it down.
- */
-
- /* move lower margin down */
- num = LESS_BY(old_height, page->scroll_idx + page->scroll_num);
- if (num > 0) {
- unsigned int top, bottom;
-
- top = page->scroll_idx + page->scroll_num;
- bottom = top + (rows - old_height);
-
- /* might overlap; must run bottomup, not topdown */
- for (i = num; i-- > 0; ) {
- line = page->lines[top + i];
- page->lines[top + i] = page->lines[bottom + i];
- page->lines[bottom + i] = line;
- }
- }
-
- /* update vertical extents */
- page->height = rows;
- page->scroll_num = MIN(LESS_BY(rows, page->scroll_idx),
- page->scroll_num + (rows - old_height));
-
- /* check how many lines can be received from history */
- if (history)
- num = term_history_peek(history,
- rows - old_height,
- cols,
- attr,
- age);
- else
- num = 0;
-
- /* retrieve new lines from history if available */
- if (num > 0)
- page_scroll_down(page,
- cols,
- num,
- attr,
- age,
- history);
- }
-
- /* set horizontal extents */
- page->width = cols;
- for (i = 0; i < page->height; ++i)
- term_line_set_width(page->lines[i], cols);
-}
-
-/**
- * term_page_write() - Write to a single cell
- * @page: page to operate on
- * @pos_x: x-position of cell to write to
- * @pos_y: y-position of cell to write to
- * @ch: character to write
- * @cwidth: character-width of @ch
- * @attr: attributes to set on the cell or NULL
- * @age: age to use for all modifications
- * @insert_mode: true if INSERT-MODE is enabled
- *
- * This writes a character to a specific cell. If the cell is beyond bounds,
- * this is a no-op. @attr and @age are used to update the cell. @flags can be
- * used to alter the behavior of this function.
- *
- * This is a wrapper around term_line_write().
- *
- * This call does not wrap around lines. That is, this only operates on a single
- * line.
- */
-void term_page_write(term_page *page, unsigned int pos_x, unsigned int pos_y, term_char_t ch, unsigned int cwidth, const term_attr *attr, term_age_t age, bool insert_mode) {
- assert(page);
-
- if (pos_y >= page->height)
- return;
-
- term_line_write(page->lines[pos_y], pos_x, ch, cwidth, attr, age, insert_mode);
-}
-
-/**
- * term_page_insert_cells() - Insert cells into a line
- * @page: page to operate on
- * @from_x: x-position where to insert new cells
- * @from_y: y-position where to insert new cells
- * @num: number of cells to insert
- * @attr: attributes to set on new cells or NULL
- * @age: age to use for all modifications
- *
- * This inserts new cells into a given line. This is a wrapper around
- * term_line_insert().
- *
- * This call does not wrap around lines. That is, this only operates on a single
- * line.
- */
-void term_page_insert_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age) {
- assert(page);
-
- if (from_y >= page->height)
- return;
-
- term_line_insert(page->lines[from_y], from_x, num, attr, age);
-}
-
-/**
- * term_page_delete_cells() - Delete cells from a line
- * @page: page to operate on
- * @from_x: x-position where to delete cells
- * @from_y: y-position where to delete cells
- * @num: number of cells to delete
- * @attr: attributes to set on new cells or NULL
- * @age: age to use for all modifications
- *
- * This deletes cells from a given line. This is a wrapper around
- * term_line_delete().
- *
- * This call does not wrap around lines. That is, this only operates on a single
- * line.
- */
-void term_page_delete_cells(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int num, const term_attr *attr, term_age_t age) {
- assert(page);
-
- if (from_y >= page->height)
- return;
-
- term_line_delete(page->lines[from_y], from_x, num, attr, age);
-}
-
-/**
- * term_page_append_combchar() - Append combining-character to a cell
- * @page: page to operate on
- * @pos_x: x-position of target cell
- * @pos_y: y-position of target cell
- * @ucs4: combining character to append
- * @age: age to use for all modifications
- *
- * This appends a combining-character to a specific cell. This is a wrapper
- * around term_line_append_combchar().
- */
-void term_page_append_combchar(term_page *page, unsigned int pos_x, unsigned int pos_y, uint32_t ucs4, term_age_t age) {
- assert(page);
-
- if (pos_y >= page->height)
- return;
-
- term_line_append_combchar(page->lines[pos_y], pos_x, ucs4, age);
-}
-
-/**
- * term_page_erase() - Erase parts of a page
- * @page: page to operate on
- * @from_x: x-position where to start erasure (inclusive)
- * @from_y: y-position where to start erasure (inclusive)
- * @to_x: x-position where to stop erasure (inclusive)
- * @to_y: y-position where to stop erasure (inclusive)
- * @attr: attributes to set on cells
- * @age: age to use for all modifications
- * @keep_protected: true if protected cells should be kept
- *
- * This erases all cells starting at @from_x/@from_y up to @to_x/@to_y. Note
- * that this wraps around line-boundaries so lines between @from_y and @to_y
- * are cleared entirely.
- *
- * Lines outside the visible area are left untouched.
- */
-void term_page_erase(term_page *page, unsigned int from_x, unsigned int from_y, unsigned int to_x, unsigned int to_y, const term_attr *attr, term_age_t age, bool keep_protected) {
- unsigned int i, from, to;
-
- assert(page);
-
- for (i = from_y; i <= to_y && i < page->height; ++i) {
- from = 0;
- to = page->width;
-
- if (i == from_y)
- from = from_x;
- if (i == to_y)
- to = to_x;
-
- term_line_erase(page->lines[i],
- from,
- LESS_BY(to, from),
- attr,
- age,
- keep_protected);
- }
-}
-
-/**
- * term_page_reset() - Reset page
- * @page: page to modify
- * @attr: attributes to set on cells
- * @age: age to use for all modifications
- *
- * This erases the whole visible page. See term_page_erase().
- */
-void term_page_reset(term_page *page, const term_attr *attr, term_age_t age) {
- assert(page);
-
- return term_page_erase(page,
- 0, 0,
- page->width - 1, page->height - 1,
- attr,
- age,
- 0);
-}
-
-/**
- * term_page_set_scroll_region() - Set scroll region
- * @page: page to operate on
- * @idx: start-index of scroll region
- * @num: number of lines in scroll region
- *
- * This sets the scroll region of a page. Whenever an operation needs to scroll
- * lines, it scrolls them inside of that region. Lines outside the region are
- * left untouched. In case a scroll-operation is targeted outside of this
- * region, it will implicitly get a scroll-region of only one line (i.e., no
- * scroll region at all).
- *
- * Note that the scroll-region is clipped to the current page-extents. Growing
- * or shrinking the page always accounts new/old lines to the scroll region and
- * moves top/bottom margins accordingly so they're preserved.
- */
-void term_page_set_scroll_region(term_page *page, unsigned int idx, unsigned int num) {
- assert(page);
-
- if (page->height < 1) {
- page->scroll_idx = 0;
- page->scroll_num = 0;
- } else {
- page->scroll_idx = MIN(idx, page->height - 1);
- page->scroll_num = MIN(num, page->height - page->scroll_idx);
- }
-}
-
-/**
- * term_page_scroll_up() - Scroll up
- * @page: page to operate on
- * @num: number of lines to scroll up
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for old lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are cleared and reset
- * with the given attributes. Old lines are moved into the history if non-NULL.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-void term_page_scroll_up(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- page_scroll_up(page, page->width, num, attr, age, history);
-}
-
-/**
- * term_page_scroll_down() - Scroll down
- * @page: page to operate on
- * @num: number of lines to scroll down
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- * @history: history to use for new lines or NULL
- *
- * This scrolls the scroll-region by @num lines. New lines are retrieved from
- * the history or cleared if the history is empty or NULL.
- *
- * Usually, scroll-down implies that new lines are cleared. Therefore, you're
- * highly encouraged to set @history to NULL. However, if you resize a terminal,
- * you might want to include history-lines in the new area. In that case, you
- * should set @history to non-NULL.
- *
- * If the scroll-region is empty, this is a no-op.
- */
-void term_page_scroll_down(term_page *page, unsigned int num, const term_attr *attr, term_age_t age, term_history *history) {
- page_scroll_down(page, page->width, num, attr, age, history);
-}
-
-/**
- * term_page_insert_lines() - Insert new lines
- * @page: page to operate on
- * @pos_y: y-position where to insert new lines
- * @num: number of lines to insert
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- *
- * This inserts @num new lines at position @pos_y. If @pos_y is beyond
- * boundaries or @num is 0, this is a no-op.
- * All lines below @pos_y are moved down to make space for the new lines. Lines
- * on the bottom are dropped. Note that this only moves lines above or inside
- * the scroll-region. If @pos_y is below the scroll-region, a scroll-region of
- * one line is implied (which means the line is simply cleared).
- */
-void term_page_insert_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age) {
- unsigned int scroll_idx, scroll_num;
-
- assert(page);
-
- if (pos_y >= page->height)
- return;
- if (num >= page->height)
- num = page->height;
-
- /* remember scroll-region */
- scroll_idx = page->scroll_idx;
- scroll_num = page->scroll_num;
-
- /* set scroll-region temporarily so we can reuse scroll_down() */
- {
- page->scroll_idx = pos_y;
- if (pos_y >= scroll_idx + scroll_num)
- page->scroll_num = 1;
- else if (pos_y >= scroll_idx)
- page->scroll_num -= pos_y - scroll_idx;
- else
- page->scroll_num += scroll_idx - pos_y;
-
- term_page_scroll_down(page, num, attr, age, NULL);
- }
-
- /* reset scroll-region */
- page->scroll_idx = scroll_idx;
- page->scroll_num = scroll_num;
-}
-
-/**
- * term_page_delete_lines() - Delete lines
- * @page: page to operate on
- * @pos_y: y-position where to delete lines
- * @num: number of lines to delete
- * @attr: attributes to set on new lines
- * @age: age to use for all modifications
- *
- * This deletes @num lines at position @pos_y. If @pos_y is beyond boundaries or
- * @num is 0, this is a no-op.
- * All lines below @pos_y are moved up into the newly made space. New lines
- * on the bottom are clear. Note that this only moves lines above or inside
- * the scroll-region. If @pos_y is below the scroll-region, a scroll-region of
- * one line is implied (which means the line is simply cleared).
- */
-void term_page_delete_lines(term_page *page, unsigned int pos_y, unsigned int num, const term_attr *attr, term_age_t age) {
- unsigned int scroll_idx, scroll_num;
-
- assert(page);
-
- if (pos_y >= page->height)
- return;
- if (num >= page->height)
- num = page->height;
-
- /* remember scroll-region */
- scroll_idx = page->scroll_idx;
- scroll_num = page->scroll_num;
-
- /* set scroll-region temporarily so we can reuse scroll_up() */
- {
- page->scroll_idx = pos_y;
- if (pos_y >= scroll_idx + scroll_num)
- page->scroll_num = 1;
- else if (pos_y > scroll_idx)
- page->scroll_num -= pos_y - scroll_idx;
- else
- page->scroll_num += scroll_idx - pos_y;
-
- term_page_scroll_up(page, num, attr, age, NULL);
- }
-
- /* reset scroll-region */
- page->scroll_idx = scroll_idx;
- page->scroll_num = scroll_num;
-}
-
-/**
- * term_history_new() - Create new history object
- * @out: storage for pointer to new history
- *
- * Create a new history object. Histories are used to store scrollback-lines
- * from VTE pages. You're highly recommended to set a history-limit on
- * history->max_lines and trim it via term_history_trim(), otherwise history
- * allocations are unlimited.
- *
- * Returns: 0 on success, negative error code on failure.
- */
-int term_history_new(term_history **out) {
- _term_history_free_ term_history *history = NULL;
-
- assert_return(out, -EINVAL);
-
- history = new0(term_history, 1);
- if (!history)
- return -ENOMEM;
-
- history->max_lines = 4096;
-
- *out = history;
- history = NULL;
- return 0;
-}
-
-/**
- * term_history_free() - Free history
- * @history: history to free
- *
- * Clear and free history. You must not access the object afterwards.
- *
- * Returns: NULL
- */
-term_history *term_history_free(term_history *history) {
- if (!history)
- return NULL;
-
- term_history_clear(history);
- free(history);
- return NULL;
-}
-
-/**
- * term_history_clear() - Clear history
- * @history: history to clear
- *
- * Remove all linked lines from a history and reset it to its initial state.
- */
-void term_history_clear(term_history *history) {
- return term_history_trim(history, 0);
-}
-
-/**
- * term_history_trim() - Trim history
- * @history: history to trim
- * @max: maximum number of lines to be left in history
- *
- * This removes lines from the history until it is smaller than @max. Lines are
- * removed from the top.
- */
-void term_history_trim(term_history *history, unsigned int max) {
- term_line *line;
-
- if (!history)
- return;
-
- while (history->n_lines > max && (line = history->lines_first)) {
- TERM_LINE_UNLINK(line, history);
- term_line_free(line);
- --history->n_lines;
- }
-}
-
-/**
- * term_history_push() - Push line into history
- * @history: history to work on
- * @line: line to push into history
- *
- * This pushes a line into the given history. It is linked at the tail. In case
- * the history is limited, the top-most line might be freed.
- */
-void term_history_push(term_history *history, term_line *line) {
- assert(history);
- assert(line);
-
- TERM_LINE_LINK_TAIL(line, history);
- if (history->max_lines > 0 && history->n_lines >= history->max_lines) {
- line = history->lines_first;
- TERM_LINE_UNLINK(line, history);
- term_line_free(line);
- } else {
- ++history->n_lines;
- }
-}
-
-/**
- * term_history_pop() - Retrieve last line from history
- * @history: history to work on
- * @new_width: width to reserve and set on the line
- * @attr: attributes to use for cell reservation
- * @age: age to use for cell reservation
- *
- * This unlinks the last linked line of the history and returns it. This also
- * makes sure the line has the given width pre-allocated (see
- * term_line_reserve()). If the pre-allocation fails, this returns NULL, so it
- * is treated like there's no line in history left. This simplifies
- * history-handling on the caller's side in case of allocation errors. No need
- * to throw lines away just because the reservation failed. We can keep them in
- * history safely, and make them available as scrollback.
- *
- * Returns: Line from history or NULL
- */
-term_line *term_history_pop(term_history *history, unsigned int new_width, const term_attr *attr, term_age_t age) {
- term_line *line;
- int r;
-
- assert_return(history, NULL);
-
- line = history->lines_last;
- if (!line)
- return NULL;
-
- r = term_line_reserve(line, new_width, attr, age, line->width);
- if (r < 0)
- return NULL;
-
- term_line_set_width(line, new_width);
- TERM_LINE_UNLINK(line, history);
- --history->n_lines;
-
- return line;
-}
-
-/**
- * term_history_peek() - Return number of available history-lines
- * @history: history to work on
- * @max: maximum number of lines to look at
- * @reserve_width: width to reserve on the line
- * @attr: attributes to use for cell reservation
- * @age: age to use for cell reservation
- *
- * This returns the number of available lines in the history given as @history.
- * It returns at most @max. For each line that is looked at, the line is
- * verified to have at least @reserve_width cells. Valid cells are preserved,
- * new cells are initialized with @attr and @age. In case an allocation fails,
- * we bail out and return the number of lines that are valid so far.
- *
- * Usually, this function should be used before running a loop on
- * term_history_pop(). This function guarantees that term_history_pop() (with
- * the same arguments) will succeed at least the returned number of times.
- *
- * Returns: Number of valid lines that can be received via term_history_pop().
- */
-unsigned int term_history_peek(term_history *history, unsigned int max, unsigned int reserve_width, const term_attr *attr, term_age_t age) {
- unsigned int num;
- term_line *line;
- int r;
-
- assert(history);
-
- num = 0;
- line = history->lines_last;
-
- while (num < max && line) {
- r = term_line_reserve(line, reserve_width, attr, age, line->width);
- if (r < 0)
- break;
-
- ++num;
- line = line->lines_prev;
- }
-
- return num;
-}
diff --git a/src/libsystemd-terminal/term-parser.c b/src/libsystemd-terminal/term-parser.c
deleted file mode 100644
index 8dc1da2f9c..0000000000
--- a/src/libsystemd-terminal/term-parser.c
+++ /dev/null
@@ -1,1702 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Terminal Parser
- * This file contains a bunch of UTF-8 helpers and the main ctlseq-parser. The
- * parser is a simple state-machine that correctly parses all CSI, DCS, OSC, ST
- * control sequences and generic escape sequences.
- * The parser itself does not perform any actions but lets the caller react to
- * detected sequences.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-
-static const uint8_t default_palette[18][3] = {
- { 0, 0, 0 }, /* black */
- { 205, 0, 0 }, /* red */
- { 0, 205, 0 }, /* green */
- { 205, 205, 0 }, /* yellow */
- { 0, 0, 238 }, /* blue */
- { 205, 0, 205 }, /* magenta */
- { 0, 205, 205 }, /* cyan */
- { 229, 229, 229 }, /* light grey */
- { 127, 127, 127 }, /* dark grey */
- { 255, 0, 0 }, /* light red */
- { 0, 255, 0 }, /* light green */
- { 255, 255, 0 }, /* light yellow */
- { 92, 92, 255 }, /* light blue */
- { 255, 0, 255 }, /* light magenta */
- { 0, 255, 255 }, /* light cyan */
- { 255, 255, 255 }, /* white */
-
- { 229, 229, 229 }, /* light grey */
- { 0, 0, 0 }, /* black */
-};
-
-static uint32_t term_color_to_argb32(const term_color *color, const term_attr *attr, const uint8_t *palette) {
- static const uint8_t bval[] = {
- 0x00, 0x5f, 0x87,
- 0xaf, 0xd7, 0xff,
- };
- uint8_t r, g, b, t;
-
- assert(color);
-
- if (!palette)
- palette = (void*)default_palette;
-
- switch (color->ccode) {
- case TERM_CCODE_RGB:
- r = color->red;
- g = color->green;
- b = color->blue;
-
- break;
- case TERM_CCODE_256:
- t = color->c256;
- if (t < 16) {
- r = palette[t * 3 + 0];
- g = palette[t * 3 + 1];
- b = palette[t * 3 + 2];
- } else if (t < 232) {
- t -= 16;
- b = bval[t % 6];
- t /= 6;
- g = bval[t % 6];
- t /= 6;
- r = bval[t % 6];
- } else {
- t = (t - 232) * 10 + 8;
- r = t;
- g = t;
- b = t;
- }
-
- break;
- case TERM_CCODE_BLACK ... TERM_CCODE_LIGHT_WHITE:
- t = color->ccode - TERM_CCODE_BLACK;
-
- /* bold causes light colors (only for foreground colors) */
- if (t < 8 && attr->bold && color == &attr->fg)
- t += 8;
-
- r = palette[t * 3 + 0];
- g = palette[t * 3 + 1];
- b = palette[t * 3 + 2];
- break;
- case TERM_CCODE_DEFAULT:
- /* fallthrough */
- default:
- t = 16 + !(color == &attr->fg);
- r = palette[t * 3 + 0];
- g = palette[t * 3 + 1];
- b = palette[t * 3 + 2];
- break;
- }
-
- return (0xff << 24) | (r << 16) | (g << 8) | b;
-}
-
-/**
- * term_attr_to_argb32() - Encode terminal colors as native ARGB32 value
- * @color: Terminal attributes to work on
- * @fg: Storage for foreground color (or NULL)
- * @bg: Storage for background color (or NULL)
- * @palette: The color palette to use (or NULL for default)
- *
- * This encodes the colors attr->fg and attr->bg as native-endian ARGB32 values
- * and returns them. Any color conversions are automatically applied.
- */
-void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette) {
- uint32_t f, b, t;
-
- assert(attr);
-
- f = term_color_to_argb32(&attr->fg, attr, palette);
- b = term_color_to_argb32(&attr->bg, attr, palette);
-
- if (attr->inverse) {
- t = f;
- f = b;
- b = t;
- }
-
- if (fg)
- *fg = f;
- if (bg)
- *bg = b;
-}
-
-/**
- * term_utf8_decode() - Try decoding the next UCS-4 character
- * @p: decoder object to operate on or NULL
- * @out_len: output storage for pointer to decoded UCS-4 string or NULL
- * @c: next char to push into decoder
- *
- * This decodes a UTF-8 stream. It must be called for each input-byte of the
- * UTF-8 stream and returns a UCS-4 stream. A pointer to the parsed UCS-4
- * string is stored in @out_buf if non-NULL. The length of this string (number
- * of parsed UCS4 characters) is returned as result. The string is not
- * zero-terminated! Furthermore, the string is only valid until the next
- * invocation of this function. It is also bound to the parser state @p and
- * must not be freed nor written to by the caller.
- *
- * This function is highly optimized to work with terminal-emulators. Instead
- * of being strict about UTF-8 validity, this tries to perform a fallback to
- * ISO-8859-1 in case a wrong series was detected. Therefore, this function
- * might return multiple UCS-4 characters by parsing just a single UTF-8 byte.
- *
- * The parser state @p should be allocated and managed by the caller. There're
- * no helpers to do that for you. To initialize it, simply reset it to all
- * zero. You can reset or free the object at any point in time.
- *
- * Returns: Number of parsed UCS4 characters
- */
-size_t term_utf8_decode(term_utf8 *p, uint32_t **out_buf, char c) {
- static uint32_t ucs4_null = 0;
- uint32_t t, *res = NULL;
- uint8_t byte;
- size_t len = 0;
-
- if (!p)
- goto out;
-
- byte = c;
-
- if (!p->valid || p->i_bytes >= p->n_bytes) {
- /*
- * If the previous sequence was invalid or fully parsed, start
- * parsing a fresh new sequence.
- */
-
- if ((byte & 0xE0) == 0xC0) {
- /* start of two byte sequence */
- t = byte & 0x1F;
- p->n_bytes = 2;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF0) == 0xE0) {
- /* start of three byte sequence */
- t = byte & 0x0F;
- p->n_bytes = 3;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF8) == 0xF0) {
- /* start of four byte sequence */
- t = byte & 0x07;
- p->n_bytes = 4;
- p->i_bytes = 1;
- p->valid = 1;
- } else {
- /* Either of:
- * - single ASCII 7-bit char
- * - out-of-sync continuation byte
- * - overlong encoding
- * All of them are treated as single byte ISO-8859-1 */
- t = byte;
- p->n_bytes = 1;
- p->i_bytes = 1;
- p->valid = 0;
- }
-
- p->chars[0] = byte;
- p->ucs4 = t << (6 * (p->n_bytes - p->i_bytes));
- } else {
- /*
- * ..otherwise, try to continue the previous sequence..
- */
-
- if ((byte & 0xC0) == 0x80) {
- /*
- * Valid continuation byte. Append to sequence and
- * update the ucs4 cache accordingly.
- */
-
- t = byte & 0x3F;
- p->chars[p->i_bytes++] = byte;
- p->ucs4 |= t << (6 * (p->n_bytes - p->i_bytes));
- } else {
- /*
- * Invalid continuation? Treat cached sequence as
- * ISO-8859-1, but parse the new char as valid new
- * starting character. If it's a new single-byte UTF-8
- * sequence, we immediately return it in the same run,
- * otherwise, we might suffer from starvation.
- */
-
- if ((byte & 0xE0) == 0xC0 ||
- (byte & 0xF0) == 0xE0 ||
- (byte & 0xF8) == 0xF0) {
- /*
- * New multi-byte sequence. Move to-be-returned
- * data at the end and start new sequence. Only
- * return the old sequence.
- */
-
- memmove(p->chars + 1,
- p->chars,
- sizeof(*p->chars) * p->i_bytes);
- res = p->chars + 1;
- len = p->i_bytes;
-
- if ((byte & 0xE0) == 0xC0) {
- /* start of two byte sequence */
- t = byte & 0x1F;
- p->n_bytes = 2;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF0) == 0xE0) {
- /* start of three byte sequence */
- t = byte & 0x0F;
- p->n_bytes = 3;
- p->i_bytes = 1;
- p->valid = 1;
- } else if ((byte & 0xF8) == 0xF0) {
- /* start of four byte sequence */
- t = byte & 0x07;
- p->n_bytes = 4;
- p->i_bytes = 1;
- p->valid = 1;
- } else
- assert_not_reached("Should not happen");
-
- p->chars[0] = byte;
- p->ucs4 = t << (6 * (p->n_bytes - p->i_bytes));
-
- goto out;
- } else {
- /*
- * New single byte sequence, append to output
- * and return combined sequence.
- */
-
- p->chars[p->i_bytes++] = byte;
- p->valid = 0;
- }
- }
- }
-
- /*
- * Check whether a full sequence (valid or invalid) has been parsed and
- * then return it. Otherwise, return nothing.
- */
- if (p->valid) {
- /* still parsing? then bail out */
- if (p->i_bytes < p->n_bytes)
- goto out;
-
- res = &p->ucs4;
- len = 1;
- } else {
- res = p->chars;
- len = p->i_bytes;
- }
-
- p->valid = 0;
- p->i_bytes = 0;
- p->n_bytes = 0;
-
-out:
- if (out_buf)
- *out_buf = res ? : &ucs4_null;
- return len;
-}
-
-/*
- * Command Parser
- * The ctl-seq parser "term_parser" only detects whole sequences, it does not
- * detect the specific command. Once a sequence is parsed, the command-parsers
- * are used to figure out their meaning. Note that this depends on whether we
- * run on the host or terminal side.
- */
-
-static unsigned int term_parse_host_control(const term_seq *seq) {
- assert_return(seq, TERM_CMD_NONE);
-
- switch (seq->terminator) {
- case 0x00: /* NUL */
- return TERM_CMD_NULL;
- case 0x05: /* ENQ */
- return TERM_CMD_ENQ;
- case 0x07: /* BEL */
- return TERM_CMD_BEL;
- case 0x08: /* BS */
- return TERM_CMD_BS;
- case 0x09: /* HT */
- return TERM_CMD_HT;
- case 0x0a: /* LF */
- return TERM_CMD_LF;
- case 0x0b: /* VT */
- return TERM_CMD_VT;
- case 0x0c: /* FF */
- return TERM_CMD_FF;
- case 0x0d: /* CR */
- return TERM_CMD_CR;
- case 0x0e: /* SO */
- return TERM_CMD_SO;
- case 0x0f: /* SI */
- return TERM_CMD_SI;
- case 0x11: /* DC1 */
- return TERM_CMD_DC1;
- case 0x13: /* DC3 */
- return TERM_CMD_DC3;
- case 0x18: /* CAN */
- /* this is already handled by the state-machine */
- break;
- case 0x1a: /* SUB */
- return TERM_CMD_SUB;
- case 0x1b: /* ESC */
- /* this is already handled by the state-machine */
- break;
- case 0x1f: /* DEL */
- /* this is already handled by the state-machine */
- break;
- case 0x84: /* IND */
- return TERM_CMD_IND;
- case 0x85: /* NEL */
- return TERM_CMD_NEL;
- case 0x88: /* HTS */
- return TERM_CMD_HTS;
- case 0x8d: /* RI */
- return TERM_CMD_RI;
- case 0x8e: /* SS2 */
- return TERM_CMD_SS2;
- case 0x8f: /* SS3 */
- return TERM_CMD_SS3;
- case 0x90: /* DCS */
- /* this is already handled by the state-machine */
- break;
- case 0x96: /* SPA */
- return TERM_CMD_SPA;
- case 0x97: /* EPA */
- return TERM_CMD_EPA;
- case 0x98: /* SOS */
- /* this is already handled by the state-machine */
- break;
- case 0x9a: /* DECID */
- return TERM_CMD_DECID;
- case 0x9b: /* CSI */
- /* this is already handled by the state-machine */
- break;
- case 0x9c: /* ST */
- return TERM_CMD_ST;
- case 0x9d: /* OSC */
- /* this is already handled by the state-machine */
- break;
- case 0x9e: /* PM */
- /* this is already handled by the state-machine */
- break;
- case 0x9f: /* APC */
- /* this is already handled by the state-machine */
- break;
- }
-
- return TERM_CMD_NONE;
-}
-
-static inline int charset_from_cmd(uint32_t raw, unsigned int flags, bool require_96) {
- static const struct {
- uint32_t raw;
- unsigned int flags;
- } charset_cmds[] = {
- /* 96-compat charsets */
- [TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL] = { .raw = 'A', .flags = 0 },
- [TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL] = { .raw = 'B', .flags = 0 },
- [TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL] = { .raw = 'M', .flags = 0 },
- [TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL] = { .raw = 'F', .flags = 0 },
- [TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL] = { .raw = 'H', .flags = 0 },
- [TERM_CHARSET_ISO_LATIN_CYRILLIC] = { .raw = 'L', .flags = 0 },
-
- /* 94-compat charsets */
- [TERM_CHARSET_DEC_SPECIAL_GRAPHIC] = { .raw = '0', .flags = 0 },
- [TERM_CHARSET_DEC_SUPPLEMENTAL] = { .raw = '5', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_DEC_TECHNICAL] = { .raw = '>', .flags = 0 },
- [TERM_CHARSET_CYRILLIC_DEC] = { .raw = '4', .flags = TERM_SEQ_FLAG_AND },
- [TERM_CHARSET_DUTCH_NRCS] = { .raw = '4', .flags = 0 },
- [TERM_CHARSET_FINNISH_NRCS] = { .raw = '5', .flags = 0 },
- [TERM_CHARSET_FRENCH_NRCS] = { .raw = 'R', .flags = 0 },
- [TERM_CHARSET_FRENCH_CANADIAN_NRCS] = { .raw = '9', .flags = 0 },
- [TERM_CHARSET_GERMAN_NRCS] = { .raw = 'K', .flags = 0 },
- [TERM_CHARSET_GREEK_DEC] = { .raw = '?', .flags = TERM_SEQ_FLAG_DQUOTE },
- [TERM_CHARSET_GREEK_NRCS] = { .raw = '>', .flags = TERM_SEQ_FLAG_DQUOTE },
- [TERM_CHARSET_HEBREW_DEC] = { .raw = '4', .flags = TERM_SEQ_FLAG_DQUOTE },
- [TERM_CHARSET_HEBREW_NRCS] = { .raw = '=', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_ITALIAN_NRCS] = { .raw = 'Y', .flags = 0 },
- [TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = '`', .flags = 0 },
- [TERM_CHARSET_PORTUGUESE_NRCS] = { .raw = '6', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_RUSSIAN_NRCS] = { .raw = '5', .flags = TERM_SEQ_FLAG_AND },
- [TERM_CHARSET_SCS_NRCS] = { .raw = '3', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_SPANISH_NRCS] = { .raw = 'Z', .flags = 0 },
- [TERM_CHARSET_SWEDISH_NRCS] = { .raw = '7', .flags = 0 },
- [TERM_CHARSET_SWISS_NRCS] = { .raw = '=', .flags = 0 },
- [TERM_CHARSET_TURKISH_DEC] = { .raw = '0', .flags = TERM_SEQ_FLAG_PERCENT },
- [TERM_CHARSET_TURKISH_NRCS] = { .raw = '2', .flags = TERM_SEQ_FLAG_PERCENT },
-
- /* special charsets */
- [TERM_CHARSET_USERPREF_SUPPLEMENTAL] = { .raw = '<', .flags = 0 },
-
- /* secondary choices */
- [TERM_CHARSET_CNT + TERM_CHARSET_FINNISH_NRCS] = { .raw = 'C', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_FRENCH_NRCS] = { .raw = 'f', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_FRENCH_CANADIAN_NRCS] = { .raw = 'Q', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = 'E', .flags = 0 },
- [TERM_CHARSET_CNT + TERM_CHARSET_SWEDISH_NRCS] = { .raw = 'H', .flags = 0 }, /* unused; conflicts with ISO_HEBREW */
-
- /* tertiary choices */
- [TERM_CHARSET_CNT + TERM_CHARSET_CNT + TERM_CHARSET_NORWEGIAN_DANISH_NRCS] = { .raw = '6', .flags = 0 },
- };
- size_t i, cs;
-
- /*
- * Secondary choice on SWEDISH_NRCS and primary choice on
- * ISO_HEBREW_SUPPLEMENTAL have a conflict: raw=="H", flags==0.
- * We always choose the ISO 96-compat set, which is what VT510 does.
- */
-
- for (i = 0; i < ELEMENTSOF(charset_cmds); ++i) {
- if (charset_cmds[i].raw == raw && charset_cmds[i].flags == flags) {
- cs = i;
- while (cs >= TERM_CHARSET_CNT)
- cs -= TERM_CHARSET_CNT;
-
- if (!require_96 || cs < TERM_CHARSET_96_CNT || cs >= TERM_CHARSET_94_CNT)
- return cs;
- }
- }
-
- return -ENOENT;
-}
-
-/* true if exactly one bit in @value is set */
-static inline bool exactly_one_bit_set(unsigned int value) {
- return __builtin_popcount(value) == 1;
-}
-
-static unsigned int term_parse_host_escape(const term_seq *seq, unsigned int *cs_out) {
- unsigned int t, flags;
- int cs;
-
- assert_return(seq, TERM_CMD_NONE);
-
- flags = seq->intermediates;
- t = TERM_SEQ_FLAG_POPEN | TERM_SEQ_FLAG_PCLOSE | TERM_SEQ_FLAG_MULT |
- TERM_SEQ_FLAG_PLUS | TERM_SEQ_FLAG_MINUS | TERM_SEQ_FLAG_DOT |
- TERM_SEQ_FLAG_SLASH;
-
- if (exactly_one_bit_set(flags & t)) {
- switch (flags & t) {
- case TERM_SEQ_FLAG_POPEN:
- case TERM_SEQ_FLAG_PCLOSE:
- case TERM_SEQ_FLAG_MULT:
- case TERM_SEQ_FLAG_PLUS:
- cs = charset_from_cmd(seq->terminator, flags & ~t, false);
- break;
- case TERM_SEQ_FLAG_MINUS:
- case TERM_SEQ_FLAG_DOT:
- case TERM_SEQ_FLAG_SLASH:
- cs = charset_from_cmd(seq->terminator, flags & ~t, true);
- break;
- default:
- cs = -ENOENT;
- break;
- }
-
- if (cs >= 0) {
- if (cs_out)
- *cs_out = cs;
- return TERM_CMD_SCS;
- }
-
- /* looked like a charset-cmd but wasn't; continue */
- }
-
- switch (seq->terminator) {
- case '3':
- if (flags == TERM_SEQ_FLAG_HASH) /* DECDHL top-half */
- return TERM_CMD_DECDHL_TH;
- break;
- case '4':
- if (flags == TERM_SEQ_FLAG_HASH) /* DECDHL bottom-half */
- return TERM_CMD_DECDHL_BH;
- break;
- case '5':
- if (flags == TERM_SEQ_FLAG_HASH) /* DECSWL */
- return TERM_CMD_DECSWL;
- break;
- case '6':
- if (flags == 0) /* DECBI */
- return TERM_CMD_DECBI;
- else if (flags == TERM_SEQ_FLAG_HASH) /* DECDWL */
- return TERM_CMD_DECDWL;
- break;
- case '7':
- if (flags == 0) /* DECSC */
- return TERM_CMD_DECSC;
- break;
- case '8':
- if (flags == 0) /* DECRC */
- return TERM_CMD_DECRC;
- else if (flags == TERM_SEQ_FLAG_HASH) /* DECALN */
- return TERM_CMD_DECALN;
- break;
- case '9':
- if (flags == 0) /* DECFI */
- return TERM_CMD_DECFI;
- break;
- case '<':
- if (flags == 0) /* DECANM */
- return TERM_CMD_DECANM;
- break;
- case '=':
- if (flags == 0) /* DECKPAM */
- return TERM_CMD_DECKPAM;
- break;
- case '>':
- if (flags == 0) /* DECKPNM */
- return TERM_CMD_DECKPNM;
- break;
- case '@':
- if (flags == TERM_SEQ_FLAG_PERCENT) {
- /* Select default character set */
- return TERM_CMD_XTERM_SDCS;
- }
- break;
- case 'D':
- if (flags == 0) /* IND */
- return TERM_CMD_IND;
- break;
- case 'E':
- if (flags == 0) /* NEL */
- return TERM_CMD_NEL;
- break;
- case 'F':
- if (flags == 0) /* Cursor to lower-left corner of screen */
- return TERM_CMD_XTERM_CLLHP;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* S7C1T */
- return TERM_CMD_S7C1T;
- break;
- case 'G':
- if (flags == TERM_SEQ_FLAG_SPACE) { /* S8C1T */
- return TERM_CMD_S8C1T;
- } else if (flags == TERM_SEQ_FLAG_PERCENT) {
- /* Select UTF-8 character set */
- return TERM_CMD_XTERM_SUCS;
- }
- break;
- case 'H':
- if (flags == 0) /* HTS */
- return TERM_CMD_HTS;
- break;
- case 'L':
- if (flags == TERM_SEQ_FLAG_SPACE) {
- /* Set ANSI conformance level 1 */
- return TERM_CMD_XTERM_SACL1;
- }
- break;
- case 'M':
- if (flags == 0) { /* RI */
- return TERM_CMD_RI;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* Set ANSI conformance level 2 */
- return TERM_CMD_XTERM_SACL2;
- }
- break;
- case 'N':
- if (flags == 0) { /* SS2 */
- return TERM_CMD_SS2;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* Set ANSI conformance level 3 */
- return TERM_CMD_XTERM_SACL3;
- }
- break;
- case 'O':
- if (flags == 0) /* SS3 */
- return TERM_CMD_SS3;
- break;
- case 'P':
- if (flags == 0) /* DCS: this is already handled by the state-machine */
- return 0;
- break;
- case 'V':
- if (flags == 0) /* SPA */
- return TERM_CMD_SPA;
- break;
- case 'W':
- if (flags == 0) /* EPA */
- return TERM_CMD_EPA;
- break;
- case 'X':
- if (flags == 0) { /* SOS */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case 'Z':
- if (flags == 0) /* DECID */
- return TERM_CMD_DECID;
- break;
- case '[':
- if (flags == 0) { /* CSI */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case '\\':
- if (flags == 0) /* ST */
- return TERM_CMD_ST;
- break;
- case ']':
- if (flags == 0) { /* OSC */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case '^':
- if (flags == 0) { /* PM */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case '_':
- if (flags == 0) { /* APC */
- /* this is already handled by the state-machine */
- break;
- }
- break;
- case 'c':
- if (flags == 0) /* RIS */
- return TERM_CMD_RIS;
- break;
- case 'l':
- if (flags == 0) /* Memory lock */
- return TERM_CMD_XTERM_MLHP;
- break;
- case 'm':
- if (flags == 0) /* Memory unlock */
- return TERM_CMD_XTERM_MUHP;
- break;
- case 'n':
- if (flags == 0) /* LS2 */
- return TERM_CMD_LS2;
- break;
- case 'o':
- if (flags == 0) /* LS3 */
- return TERM_CMD_LS3;
- break;
- case '|':
- if (flags == 0) /* LS3R */
- return TERM_CMD_LS3R;
- break;
- case '}':
- if (flags == 0) /* LS2R */
- return TERM_CMD_LS2R;
- break;
- case '~':
- if (flags == 0) /* LS1R */
- return TERM_CMD_LS1R;
- break;
- }
-
- return TERM_CMD_NONE;
-}
-
-static unsigned int term_parse_host_csi(const term_seq *seq) {
- unsigned int flags;
-
- assert_return(seq, TERM_CMD_NONE);
-
- flags = seq->intermediates;
-
- switch (seq->terminator) {
- case 'A':
- if (flags == 0) /* CUU */
- return TERM_CMD_CUU;
- break;
- case 'a':
- if (flags == 0) /* HPR */
- return TERM_CMD_HPR;
- break;
- case 'B':
- if (flags == 0) /* CUD */
- return TERM_CMD_CUD;
- break;
- case 'b':
- if (flags == 0) /* REP */
- return TERM_CMD_REP;
- break;
- case 'C':
- if (flags == 0) /* CUF */
- return TERM_CMD_CUF;
- break;
- case 'c':
- if (flags == 0) /* DA1 */
- return TERM_CMD_DA1;
- else if (flags == TERM_SEQ_FLAG_GT) /* DA2 */
- return TERM_CMD_DA2;
- else if (flags == TERM_SEQ_FLAG_EQUAL) /* DA3 */
- return TERM_CMD_DA3;
- break;
- case 'D':
- if (flags == 0) /* CUB */
- return TERM_CMD_CUB;
- break;
- case 'd':
- if (flags == 0) /* VPA */
- return TERM_CMD_VPA;
- break;
- case 'E':
- if (flags == 0) /* CNL */
- return TERM_CMD_CNL;
- break;
- case 'e':
- if (flags == 0) /* VPR */
- return TERM_CMD_VPR;
- break;
- case 'F':
- if (flags == 0) /* CPL */
- return TERM_CMD_CPL;
- break;
- case 'f':
- if (flags == 0) /* HVP */
- return TERM_CMD_HVP;
- break;
- case 'G':
- if (flags == 0) /* CHA */
- return TERM_CMD_CHA;
- break;
- case 'g':
- if (flags == 0) /* TBC */
- return TERM_CMD_TBC;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECLFKC */
- return TERM_CMD_DECLFKC;
- break;
- case 'H':
- if (flags == 0) /* CUP */
- return TERM_CMD_CUP;
- break;
- case 'h':
- if (flags == 0) /* SM ANSI */
- return TERM_CMD_SM_ANSI;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* SM DEC */
- return TERM_CMD_SM_DEC;
- break;
- case 'I':
- if (flags == 0) /* CHT */
- return TERM_CMD_CHT;
- break;
- case 'i':
- if (flags == 0) /* MC ANSI */
- return TERM_CMD_MC_ANSI;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* MC DEC */
- return TERM_CMD_MC_DEC;
- break;
- case 'J':
- if (flags == 0) /* ED */
- return TERM_CMD_ED;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* DECSED */
- return TERM_CMD_DECSED;
- break;
- case 'K':
- if (flags == 0) /* EL */
- return TERM_CMD_EL;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* DECSEL */
- return TERM_CMD_DECSEL;
- break;
- case 'L':
- if (flags == 0) /* IL */
- return TERM_CMD_IL;
- break;
- case 'l':
- if (flags == 0) /* RM ANSI */
- return TERM_CMD_RM_ANSI;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* RM DEC */
- return TERM_CMD_RM_DEC;
- break;
- case 'M':
- if (flags == 0) /* DL */
- return TERM_CMD_DL;
- break;
- case 'm':
- if (flags == 0) /* SGR */
- return TERM_CMD_SGR;
- else if (flags == TERM_SEQ_FLAG_GT) /* XTERM SMR */
- return TERM_CMD_XTERM_SRV;
- break;
- case 'n':
- if (flags == 0) /* DSR ANSI */
- return TERM_CMD_DSR_ANSI;
- else if (flags == TERM_SEQ_FLAG_GT) /* XTERM RMR */
- return TERM_CMD_XTERM_RRV;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* DSR DEC */
- return TERM_CMD_DSR_DEC;
- break;
- case 'P':
- if (flags == 0) /* DCH */
- return TERM_CMD_DCH;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* PPA */
- return TERM_CMD_PPA;
- break;
- case 'p':
- if (flags == 0) /* DECSSL */
- return TERM_CMD_DECSSL;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* DECSSCLS */
- return TERM_CMD_DECSSCLS;
- else if (flags == TERM_SEQ_FLAG_BANG) /* DECSTR */
- return TERM_CMD_DECSTR;
- else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECSCL */
- return TERM_CMD_DECSCL;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECRQM-ANSI */
- return TERM_CMD_DECRQM_ANSI;
- else if (flags == (TERM_SEQ_FLAG_CASH | TERM_SEQ_FLAG_WHAT)) /* DECRQM-DEC */
- return TERM_CMD_DECRQM_DEC;
- else if (flags == TERM_SEQ_FLAG_PCLOSE) /* DECSDPT */
- return TERM_CMD_DECSDPT;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSPPCS */
- return TERM_CMD_DECSPPCS;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECSR */
- return TERM_CMD_DECSR;
- else if (flags == TERM_SEQ_FLAG_COMMA) /* DECLTOD */
- return TERM_CMD_DECLTOD;
- else if (flags == TERM_SEQ_FLAG_GT) /* XTERM SPM */
- return TERM_CMD_XTERM_SPM;
- break;
- case 'Q':
- if (flags == TERM_SEQ_FLAG_SPACE) /* PPR */
- return TERM_CMD_PPR;
- break;
- case 'q':
- if (flags == 0) /* DECLL */
- return TERM_CMD_DECLL;
- else if (flags == TERM_SEQ_FLAG_SPACE) /* DECSCUSR */
- return TERM_CMD_DECSCUSR;
- else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECSCA */
- return TERM_CMD_DECSCA;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECSDDT */
- return TERM_CMD_DECSDDT;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSRC */
- return TERM_CMD_DECSR;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECELF */
- return TERM_CMD_DECELF;
- else if (flags == TERM_SEQ_FLAG_COMMA) /* DECTID */
- return TERM_CMD_DECTID;
- break;
- case 'R':
- if (flags == TERM_SEQ_FLAG_SPACE) /* PPB */
- return TERM_CMD_PPB;
- break;
- case 'r':
- if (flags == 0) {
- /* DECSTBM */
- return TERM_CMD_DECSTBM;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* DECSKCV */
- return TERM_CMD_DECSKCV;
- } else if (flags == TERM_SEQ_FLAG_CASH) {
- /* DECCARA */
- return TERM_CMD_DECCARA;
- } else if (flags == TERM_SEQ_FLAG_MULT) {
- /* DECSCS */
- return TERM_CMD_DECSCS;
- } else if (flags == TERM_SEQ_FLAG_PLUS) {
- /* DECSMKR */
- return TERM_CMD_DECSMKR;
- } else if (flags == TERM_SEQ_FLAG_WHAT) {
- /*
- * There's a conflict between DECPCTERM and XTERM-RPM.
- * XTERM-RPM takes a single argument, DECPCTERM takes 2.
- * Split both up and forward the call to the closer
- * match.
- */
- if (seq->n_args <= 1) /* XTERM RPM */
- return TERM_CMD_XTERM_RPM;
- else if (seq->n_args >= 2) /* DECPCTERM */
- return TERM_CMD_DECPCTERM;
- }
- break;
- case 'S':
- if (flags == 0) /* SU */
- return TERM_CMD_SU;
- else if (flags == TERM_SEQ_FLAG_WHAT) /* XTERM SGFX */
- return TERM_CMD_XTERM_SGFX;
- break;
- case 's':
- if (flags == 0) {
- /*
- * There's a conflict between DECSLRM and SC-ANSI which
- * cannot be resolved without knowing the state of
- * DECLRMM. We leave that decision up to the caller.
- */
- return TERM_CMD_DECSLRM_OR_SC;
- } else if (flags == TERM_SEQ_FLAG_CASH) {
- /* DECSPRTT */
- return TERM_CMD_DECSPRTT;
- } else if (flags == TERM_SEQ_FLAG_MULT) {
- /* DECSFC */
- return TERM_CMD_DECSFC;
- } else if (flags == TERM_SEQ_FLAG_WHAT) {
- /* XTERM SPM */
- return TERM_CMD_XTERM_SPM;
- }
- break;
- case 'T':
- if (flags == 0) {
- /*
- * Awesome: There's a conflict between SD and XTERM IHMT
- * that we have to resolve by checking the parameter
- * count.. XTERM_IHMT needs exactly 5 arguments, SD
- * takes 0 or 1. We're conservative here and give both
- * a wider range to allow unused arguments (compat...).
- */
- if (seq->n_args >= 5) {
- /* XTERM IHMT */
- return TERM_CMD_XTERM_IHMT;
- } else if (seq->n_args < 5) {
- /* SD */
- return TERM_CMD_SD;
- }
- } else if (flags == TERM_SEQ_FLAG_GT) {
- /* XTERM RTM */
- return TERM_CMD_XTERM_RTM;
- }
- break;
- case 't':
- if (flags == 0) {
- if (seq->n_args > 0 && seq->args[0] < 24) {
- /* XTERM WM */
- return TERM_CMD_XTERM_WM;
- } else {
- /* DECSLPP */
- return TERM_CMD_DECSLPP;
- }
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* DECSWBV */
- return TERM_CMD_DECSWBV;
- } else if (flags == TERM_SEQ_FLAG_DQUOTE) {
- /* DECSRFR */
- return TERM_CMD_DECSRFR;
- } else if (flags == TERM_SEQ_FLAG_CASH) {
- /* DECRARA */
- return TERM_CMD_DECRARA;
- } else if (flags == TERM_SEQ_FLAG_GT) {
- /* XTERM STM */
- return TERM_CMD_XTERM_STM;
- }
- break;
- case 'U':
- if (flags == 0) /* NP */
- return TERM_CMD_NP;
- break;
- case 'u':
- if (flags == 0) {
- /* RC */
- return TERM_CMD_RC;
- } else if (flags == TERM_SEQ_FLAG_SPACE) {
- /* DECSMBV */
- return TERM_CMD_DECSMBV;
- } else if (flags == TERM_SEQ_FLAG_DQUOTE) {
- /* DECSTRL */
- return TERM_CMD_DECSTRL;
- } else if (flags == TERM_SEQ_FLAG_WHAT) {
- /* DECRQUPSS */
- return TERM_CMD_DECRQUPSS;
- } else if (seq->args[0] == 1 && flags == TERM_SEQ_FLAG_CASH) {
- /* DECRQTSR */
- return TERM_CMD_DECRQTSR;
- } else if (flags == TERM_SEQ_FLAG_MULT) {
- /* DECSCP */
- return TERM_CMD_DECSCP;
- } else if (flags == TERM_SEQ_FLAG_COMMA) {
- /* DECRQKT */
- return TERM_CMD_DECRQKT;
- }
- break;
- case 'V':
- if (flags == 0) /* PP */
- return TERM_CMD_PP;
- break;
- case 'v':
- if (flags == TERM_SEQ_FLAG_SPACE) /* DECSLCK */
- return TERM_CMD_DECSLCK;
- else if (flags == TERM_SEQ_FLAG_DQUOTE) /* DECRQDE */
- return TERM_CMD_DECRQDE;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECCRA */
- return TERM_CMD_DECCRA;
- else if (flags == TERM_SEQ_FLAG_COMMA) /* DECRPKT */
- return TERM_CMD_DECRPKT;
- break;
- case 'W':
- if (seq->args[0] == 5 && flags == TERM_SEQ_FLAG_WHAT) {
- /* DECST8C */
- return TERM_CMD_DECST8C;
- }
- break;
- case 'w':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECRQPSR */
- return TERM_CMD_DECRQPSR;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECEFR */
- return TERM_CMD_DECEFR;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECSPP */
- return TERM_CMD_DECSPP;
- break;
- case 'X':
- if (flags == 0) /* ECH */
- return TERM_CMD_ECH;
- break;
- case 'x':
- if (flags == 0) /* DECREQTPARM */
- return TERM_CMD_DECREQTPARM;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECFRA */
- return TERM_CMD_DECFRA;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSACE */
- return TERM_CMD_DECSACE;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECRQPKFM */
- return TERM_CMD_DECRQPKFM;
- break;
- case 'y':
- if (flags == 0) /* DECTST */
- return TERM_CMD_DECTST;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECRQCRA */
- return TERM_CMD_DECRQCRA;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECPKFMR */
- return TERM_CMD_DECPKFMR;
- break;
- case 'Z':
- if (flags == 0) /* CBT */
- return TERM_CMD_CBT;
- break;
- case 'z':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECERA */
- return TERM_CMD_DECERA;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECELR */
- return TERM_CMD_DECELR;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECINVM */
- return TERM_CMD_DECINVM;
- else if (flags == TERM_SEQ_FLAG_PLUS) /* DECPKA */
- return TERM_CMD_DECPKA;
- break;
- case '@':
- if (flags == 0) /* ICH */
- return TERM_CMD_ICH;
- break;
- case '`':
- if (flags == 0) /* HPA */
- return TERM_CMD_HPA;
- break;
- case '{':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECSERA */
- return TERM_CMD_DECSERA;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECSLE */
- return TERM_CMD_DECSLE;
- break;
- case '|':
- if (flags == TERM_SEQ_FLAG_CASH) /* DECSCPP */
- return TERM_CMD_DECSCPP;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECRQLP */
- return TERM_CMD_DECRQLP;
- else if (flags == TERM_SEQ_FLAG_MULT) /* DECSNLS */
- return TERM_CMD_DECSNLS;
- break;
- case '}':
- if (flags == TERM_SEQ_FLAG_SPACE) /* DECKBD */
- return TERM_CMD_DECKBD;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECSASD */
- return TERM_CMD_DECSASD;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECIC */
- return TERM_CMD_DECIC;
- break;
- case '~':
- if (flags == TERM_SEQ_FLAG_SPACE) /* DECTME */
- return TERM_CMD_DECTME;
- else if (flags == TERM_SEQ_FLAG_CASH) /* DECSSDT */
- return TERM_CMD_DECSSDT;
- else if (flags == TERM_SEQ_FLAG_SQUOTE) /* DECDC */
- return TERM_CMD_DECDC;
- break;
- }
-
- return TERM_CMD_NONE;
-}
-
-/*
- * State Machine
- * This parser controls the parser-state and returns any detected sequence to
- * the caller. The parser is based on this state-diagram from Paul Williams:
- * http://vt100.net/emu/
- * It was written from scratch and extended where needed.
- * This parser is fully compatible up to the vt500 series. We expect UCS-4 as
- * input. It's the callers responsibility to do any UTF-8 parsing.
- */
-
-enum parser_state {
- STATE_NONE, /* placeholder */
- STATE_GROUND, /* initial state and ground */
- STATE_ESC, /* ESC sequence was started */
- STATE_ESC_INT, /* intermediate escape characters */
- STATE_CSI_ENTRY, /* starting CSI sequence */
- STATE_CSI_PARAM, /* CSI parameters */
- STATE_CSI_INT, /* intermediate CSI characters */
- STATE_CSI_IGNORE, /* CSI error; ignore this CSI sequence */
- STATE_DCS_ENTRY, /* starting DCS sequence */
- STATE_DCS_PARAM, /* DCS parameters */
- STATE_DCS_INT, /* intermediate DCS characters */
- STATE_DCS_PASS, /* DCS data passthrough */
- STATE_DCS_IGNORE, /* DCS error; ignore this DCS sequence */
- STATE_OSC_STRING, /* parsing OSC sequence */
- STATE_ST_IGNORE, /* unimplemented seq; ignore until ST */
- STATE_NUM
-};
-
-enum parser_action {
- ACTION_NONE, /* placeholder */
- ACTION_CLEAR, /* clear parameters */
- ACTION_IGNORE, /* ignore the character entirely */
- ACTION_PRINT, /* print the character on the console */
- ACTION_EXECUTE, /* execute single control character (C0/C1) */
- ACTION_COLLECT, /* collect intermediate character */
- ACTION_PARAM, /* collect parameter character */
- ACTION_ESC_DISPATCH, /* dispatch escape sequence */
- ACTION_CSI_DISPATCH, /* dispatch csi sequence */
- ACTION_DCS_START, /* start of DCS data */
- ACTION_DCS_COLLECT, /* collect DCS data */
- ACTION_DCS_CONSUME, /* consume DCS terminator */
- ACTION_DCS_DISPATCH, /* dispatch dcs sequence */
- ACTION_OSC_START, /* start of OSC data */
- ACTION_OSC_COLLECT, /* collect OSC data */
- ACTION_OSC_CONSUME, /* consume OSC terminator */
- ACTION_OSC_DISPATCH, /* dispatch osc sequence */
- ACTION_NUM
-};
-
-int term_parser_new(term_parser **out, bool host) {
- _term_parser_free_ term_parser *parser = NULL;
-
- assert_return(out, -EINVAL);
-
- parser = new0(term_parser, 1);
- if (!parser)
- return -ENOMEM;
-
- parser->is_host = host;
- parser->st_alloc = 64;
- parser->seq.st = new0(char, parser->st_alloc + 1);
- if (!parser->seq.st)
- return -ENOMEM;
-
- *out = parser;
- parser = NULL;
- return 0;
-}
-
-term_parser *term_parser_free(term_parser *parser) {
- if (!parser)
- return NULL;
-
- free(parser->seq.st);
- free(parser);
- return NULL;
-}
-
-static inline void parser_clear(term_parser *parser) {
- unsigned int i;
-
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = 0;
- parser->seq.intermediates = 0;
- parser->seq.charset = TERM_CHARSET_NONE;
- parser->seq.n_args = 0;
- for (i = 0; i < TERM_PARSER_ARG_MAX; ++i)
- parser->seq.args[i] = -1;
-
- parser->seq.n_st = 0;
- parser->seq.st[0] = 0;
-}
-
-static int parser_ignore(term_parser *parser, uint32_t raw) {
- parser_clear(parser);
- parser->seq.type = TERM_SEQ_IGNORE;
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
-
- return parser->seq.type;
-}
-
-static int parser_print(term_parser *parser, uint32_t raw) {
- parser_clear(parser);
- parser->seq.type = TERM_SEQ_GRAPHIC;
- parser->seq.command = TERM_CMD_GRAPHIC;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
-
- return parser->seq.type;
-}
-
-static int parser_execute(term_parser *parser, uint32_t raw) {
- parser_clear(parser);
- parser->seq.type = TERM_SEQ_CONTROL;
- parser->seq.command = TERM_CMD_GRAPHIC;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
- if (!parser->is_host)
- parser->seq.command = term_parse_host_control(&parser->seq);
-
- return parser->seq.type;
-}
-
-static void parser_collect(term_parser *parser, uint32_t raw) {
- /*
- * Usually, characters from 0x30 to 0x3f are only allowed as leading
- * markers (or as part of the parameters), characters from 0x20 to 0x2f
- * are only allowed as trailing markers. However, our state-machine
- * already verifies those restrictions so we can handle them the same
- * way here. Note that we safely allow markers to be specified multiple
- * times.
- */
-
- if (raw >= 0x20 && raw <= 0x3f)
- parser->seq.intermediates |= 1 << (raw - 0x20);
-}
-
-static void parser_param(term_parser *parser, uint32_t raw) {
- int new;
-
- if (raw == ';') {
- if (parser->seq.n_args < TERM_PARSER_ARG_MAX)
- ++parser->seq.n_args;
-
- return;
- }
-
- if (parser->seq.n_args >= TERM_PARSER_ARG_MAX)
- return;
-
- if (raw >= '0' && raw <= '9') {
- new = parser->seq.args[parser->seq.n_args];
- if (new < 0)
- new = 0;
- new = new * 10 + raw - '0';
-
- /* VT510 tells us to clamp all values to [0, 9999], however, it
- * also allows commands with values up to 2^15-1. We simply use
- * 2^16 as maximum here to be compatible to all commands, but
- * avoid overflows in any calculations. */
- if (new > 0xffff)
- new = 0xffff;
-
- parser->seq.args[parser->seq.n_args] = new;
- }
-}
-
-static int parser_esc(term_parser *parser, uint32_t raw) {
- parser->seq.type = TERM_SEQ_ESCAPE;
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
- if (!parser->is_host)
- parser->seq.command = term_parse_host_escape(&parser->seq, &parser->seq.charset);
-
- return parser->seq.type;
-}
-
-static int parser_csi(term_parser *parser, uint32_t raw) {
- /* parser->seq is cleared during CSI-ENTER state, thus there's no need
- * to clear invalid fields here. */
-
- if (parser->seq.n_args < TERM_PARSER_ARG_MAX) {
- if (parser->seq.n_args > 0 ||
- parser->seq.args[parser->seq.n_args] >= 0)
- ++parser->seq.n_args;
- }
-
- parser->seq.type = TERM_SEQ_CSI;
- parser->seq.command = TERM_CMD_NONE;
- parser->seq.terminator = raw;
- parser->seq.charset = TERM_CHARSET_NONE;
- if (!parser->is_host)
- parser->seq.command = term_parse_host_csi(&parser->seq);
-
- return parser->seq.type;
-}
-
-/* perform state transition and dispatch related actions */
-static int parser_transition(term_parser *parser, uint32_t raw, unsigned int state, unsigned int action) {
- if (state != STATE_NONE)
- parser->state = state;
-
- switch (action) {
- case ACTION_NONE:
- return TERM_SEQ_NONE;
- case ACTION_CLEAR:
- parser_clear(parser);
- return TERM_SEQ_NONE;
- case ACTION_IGNORE:
- return parser_ignore(parser, raw);
- case ACTION_PRINT:
- return parser_print(parser, raw);
- case ACTION_EXECUTE:
- return parser_execute(parser, raw);
- case ACTION_COLLECT:
- parser_collect(parser, raw);
- return TERM_SEQ_NONE;
- case ACTION_PARAM:
- parser_param(parser, raw);
- return TERM_SEQ_NONE;
- case ACTION_ESC_DISPATCH:
- return parser_esc(parser, raw);
- case ACTION_CSI_DISPATCH:
- return parser_csi(parser, raw);
- case ACTION_DCS_START:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_DCS_COLLECT:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_DCS_CONSUME:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_DCS_DISPATCH:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_START:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_COLLECT:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_CONSUME:
- /* not implemented */
- return TERM_SEQ_NONE;
- case ACTION_OSC_DISPATCH:
- /* not implemented */
- return TERM_SEQ_NONE;
- default:
- assert_not_reached("invalid vte-parser action");
- return TERM_SEQ_NONE;
- }
-}
-
-static int parser_feed_to_state(term_parser *parser, uint32_t raw) {
- switch (parser->state) {
- case STATE_NONE:
- /*
- * During initialization, parser->state is cleared. Treat this
- * as STATE_GROUND. We will then never get to STATE_NONE again.
- */
- case STATE_GROUND:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- case 0x80 ... 0x9b: /* C1 \ { ST } */
- case 0x9d ... 0x9f:
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_PRINT);
- case STATE_ESC:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_ESC_INT, ACTION_COLLECT);
- case 0x30 ... 0x4f: /* ['0' - '~'] \ { 'P', 'X', '[', ']', '^', '_' } */
- case 0x51 ... 0x57:
- case 0x59 ... 0x5a:
- case 0x5c:
- case 0x60 ... 0x7e:
- return parser_transition(parser, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
- case 0x50: /* 'P' */
- return parser_transition(parser, raw, STATE_DCS_ENTRY, ACTION_CLEAR);
- case 0x5b: /* '[' */
- return parser_transition(parser, raw, STATE_CSI_ENTRY, ACTION_CLEAR);
- case 0x5d: /* ']' */
- return parser_transition(parser, raw, STATE_OSC_STRING, ACTION_CLEAR);
- case 0x58: /* 'X' */
- case 0x5e: /* '^' */
- case 0x5f: /* '_' */
- return parser_transition(parser, raw, STATE_ST_IGNORE, ACTION_NONE);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_ESC_INT, ACTION_COLLECT);
- case STATE_ESC_INT:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case 0x30 ... 0x7e: /* ['0' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_ESC_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case STATE_CSI_ENTRY:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_CSI_INT, ACTION_COLLECT);
- case 0x3a: /* ':' */
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_CSI_PARAM, ACTION_PARAM);
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_CSI_PARAM, ACTION_COLLECT);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case STATE_CSI_PARAM:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_CSI_INT, ACTION_COLLECT);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_NONE, ACTION_PARAM);
- case 0x3a: /* ':' */
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case STATE_CSI_INT:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case 0x30 ... 0x3f: /* ['0' - '?'] */
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_CSI_DISPATCH);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_CSI_IGNORE, ACTION_NONE);
- case STATE_CSI_IGNORE:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_EXECUTE);
- case 0x20 ... 0x3f: /* [' ' - '?'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_NONE);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- case STATE_DCS_ENTRY:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_DCS_INT, ACTION_COLLECT);
- case 0x3a: /* ':' */
- return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_DCS_PARAM, ACTION_PARAM);
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_DCS_PARAM, ACTION_COLLECT);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case STATE_DCS_PARAM:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_DCS_INT, ACTION_COLLECT);
- case 0x30 ... 0x39: /* ['0' - '9'] */
- case 0x3b: /* ';' */
- return parser_transition(parser, raw, STATE_NONE, ACTION_PARAM);
- case 0x3a: /* ':' */
- case 0x3c ... 0x3f: /* ['<' - '?'] */
- return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case STATE_DCS_INT:
- switch (raw) {
- case 0x00 ... 0x1f: /* C0 */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x2f: /* [' ' - '\'] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_COLLECT);
- case 0x30 ... 0x3f: /* ['0' - '?'] */
- return parser_transition(parser, raw, STATE_DCS_IGNORE, ACTION_NONE);
- case 0x40 ... 0x7e: /* ['@' - '~'] */
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_DCS_PASS, ACTION_DCS_CONSUME);
- case STATE_DCS_PASS:
- switch (raw) {
- case 0x00 ... 0x7e: /* ASCII \ { DEL } */
- return parser_transition(parser, raw, STATE_NONE, ACTION_DCS_COLLECT);
- case 0x7f: /* DEL */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_DCS_DISPATCH);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_DCS_COLLECT);
- case STATE_DCS_IGNORE:
- switch (raw) {
- case 0x00 ... 0x7f: /* ASCII */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_NONE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- case STATE_OSC_STRING:
- switch (raw) {
- case 0x00 ... 0x06: /* C0 \ { BEL } */
- case 0x08 ... 0x1f:
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x20 ... 0x7f: /* [' ' - DEL] */
- return parser_transition(parser, raw, STATE_NONE, ACTION_OSC_COLLECT);
- case 0x07: /* BEL */
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_OSC_DISPATCH);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_OSC_COLLECT);
- case STATE_ST_IGNORE:
- switch (raw) {
- case 0x00 ... 0x7f: /* ASCII */
- return parser_transition(parser, raw, STATE_NONE, ACTION_IGNORE);
- case 0x9c: /* ST */
- return parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- }
-
- return parser_transition(parser, raw, STATE_NONE, ACTION_NONE);
- }
-
- assert_not_reached("bad vte-parser state");
- return -EINVAL;
-}
-
-int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw) {
- int r;
-
- assert_return(parser, -EINVAL);
- assert_return(seq_out, -EINVAL);
-
- /*
- * Notes:
- * * DEC treats GR codes as GL. We don't do that as we require UTF-8
- * as charset and, thus, it doesn't make sense to treat GR special.
- * * During control sequences, unexpected C1 codes cancel the sequence
- * and immediately start a new one. C0 codes, however, may or may not
- * be ignored/executed depending on the sequence.
- */
-
- switch (raw) {
- case 0x18: /* CAN */
- r = parser_transition(parser, raw, STATE_GROUND, ACTION_IGNORE);
- break;
- case 0x1a: /* SUB */
- r = parser_transition(parser, raw, STATE_GROUND, ACTION_EXECUTE);
- break;
- case 0x80 ... 0x8f: /* C1 \ {DCS, SOS, CSI, ST, OSC, PM, APC} */
- case 0x91 ... 0x97:
- case 0x99 ... 0x9a:
- r = parser_transition(parser, raw, STATE_GROUND, ACTION_EXECUTE);
- break;
- case 0x1b: /* ESC */
- r = parser_transition(parser, raw, STATE_ESC, ACTION_CLEAR);
- break;
- case 0x98: /* SOS */
- case 0x9e: /* PM */
- case 0x9f: /* APC */
- r = parser_transition(parser, raw, STATE_ST_IGNORE, ACTION_NONE);
- break;
- case 0x90: /* DCS */
- r = parser_transition(parser, raw, STATE_DCS_ENTRY, ACTION_CLEAR);
- break;
- case 0x9d: /* OSC */
- r = parser_transition(parser, raw, STATE_OSC_STRING, ACTION_CLEAR);
- break;
- case 0x9b: /* CSI */
- r = parser_transition(parser, raw, STATE_CSI_ENTRY, ACTION_CLEAR);
- break;
- default:
- r = parser_feed_to_state(parser, raw);
- break;
- }
-
- if (r <= 0)
- *seq_out = NULL;
- else
- *seq_out = &parser->seq;
-
- return r;
-}
diff --git a/src/libsystemd-terminal/term-screen.c b/src/libsystemd-terminal/term-screen.c
deleted file mode 100644
index 0e38ff41c6..0000000000
--- a/src/libsystemd-terminal/term-screen.c
+++ /dev/null
@@ -1,4333 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Terminal Screens
- * The term_screen layer implements the terminal-side. It handles all commands
- * returned by the seq-parser and applies them to its own pages.
- *
- * While there are a lot of legacy control-sequences, we only support a small
- * subset. There is no reason to implement unused codes like horizontal
- * scrolling.
- * If you implement new commands, make sure to document them properly.
- *
- * Standards:
- * ECMA-48
- * ANSI X3.64
- * ISO/IEC 6429
- * References:
- * http://www.vt100.net/emu/ctrlseq_dec.html
- * http://www.vt100.net/docs/vt100-ug/chapter3.html
- * http://www.vt100.net/docs/vt510-rm/chapter4
- * http://www.vt100.net/docs/vt510-rm/contents
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- * ASCII
- * http://en.wikipedia.org/wiki/C0_and_C1_control_codes
- * https://en.wikipedia.org/wiki/ANSI_color
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <xkbcommon/xkbcommon-keysyms.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "util.h"
-#include "utf8.h"
-
-int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data) {
- _cleanup_(term_screen_unrefp) term_screen *screen = NULL;
- int r;
-
- assert_return(out, -EINVAL);
-
- screen = new0(term_screen, 1);
- if (!screen)
- return -ENOMEM;
-
- screen->ref = 1;
- screen->age = 1;
- screen->write_fn = write_fn;
- screen->write_fn_data = write_fn_data;
- screen->cmd_fn = cmd_fn;
- screen->cmd_fn_data = cmd_fn_data;
- screen->flags = TERM_FLAG_7BIT_MODE;
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
- screen->g0 = &term_unicode_lower;
- screen->g1 = &term_unicode_upper;
- screen->g2 = &term_unicode_lower;
- screen->g3 = &term_unicode_upper;
- screen->state.gl = &screen->g0;
- screen->state.gr = &screen->g1;
- screen->saved = screen->state;
- screen->saved_alt = screen->saved;
-
- r = term_page_new(&screen->page_main);
- if (r < 0)
- return r;
-
- r = term_page_new(&screen->page_alt);
- if (r < 0)
- return r;
-
- r = term_parser_new(&screen->parser, false);
- if (r < 0)
- return r;
-
- r = term_history_new(&screen->history_main);
- if (r < 0)
- return r;
-
- screen->page = screen->page_main;
- screen->history = screen->history_main;
-
- *out = screen;
- screen = NULL;
- return 0;
-}
-
-term_screen *term_screen_ref(term_screen *screen) {
- if (!screen)
- return NULL;
-
- assert_return(screen->ref > 0, NULL);
-
- ++screen->ref;
- return screen;
-}
-
-term_screen *term_screen_unref(term_screen *screen) {
- if (!screen)
- return NULL;
-
- assert_return(screen->ref > 0, NULL);
-
- if (--screen->ref)
- return NULL;
-
- free(screen->answerback);
- free(screen->tabs);
- term_history_free(screen->history_main);
- term_page_free(screen->page_alt);
- term_page_free(screen->page_main);
- term_parser_free(screen->parser);
- free(screen);
-
- return NULL;
-}
-
-/*
- * Write-Helpers
- * Unfortunately, 7bit/8bit compat mode requires us to send C1 controls encoded
- * as 7bit if asked by the application. This is really used in the wild, so we
- * cannot fall back to "always 7bit".
- * screen_write() is the underlying backend which forwards any writes to the
- * users's callback. It's the users responsibility to buffer these and write
- * them out once their call to term_screen_feed_*() returns.
- * The SEQ_WRITE() and SEQ_WRITE_KEY() macros allow constructing C0/C1 sequences
- * directly in the code-base without requiring any intermediate buffer during
- * runtime.
- */
-
-#define C0_CSI "\e["
-#define C1_CSI "\x9b"
-
-#define SEQ(_screen, _prefix_esc, _c0, _c1, _seq) \
- (((_screen)->flags & TERM_FLAG_7BIT_MODE) ? \
- ((_prefix_esc) ? ("\e" _c0 _seq) : (_c0 _seq)) : \
- ((_prefix_esc) ? ("\e" _c1 _seq) : (_c1 _seq)))
-
-#define SEQ_SIZE(_screen, _prefix_esc, _c0, _c1, _seq) \
- (((_screen)->flags & TERM_FLAG_7BIT_MODE) ? \
- ((_prefix_esc) ? sizeof("\e" _c0 _seq) : sizeof(_c0 _seq)) : \
- ((_prefix_esc) ? sizeof("\e" _c1 _seq) : sizeof(_c1 _seq)))
-
-#define SEQ_WRITE_KEY(_screen, _prefix_esc, _c0, _c1, _seq) \
- screen_write((_screen), \
- SEQ((_screen), (_prefix_esc), \
- _c0, _c1, _seq), \
- SEQ_SIZE((_screen), (_prefix_esc), \
- _c0, _c1, _seq) - 1)
-
-#define SEQ_WRITE(_screen, _c0, _c1, _seq) \
- SEQ_WRITE_KEY((_screen), false, _c0, _c1, _seq)
-
-static int screen_write(term_screen *screen, const void *buf, size_t len) {
- if (len < 1 || !screen->write_fn)
- return 0;
-
- return screen->write_fn(screen, screen->write_fn_data, buf, len);
-}
-
-/*
- * Command Forwarding
- * Some commands cannot be handled by the screen-layer directly. Those are
- * forwarded to the command-handler of the caller. This is rarely used and can
- * safely be set to NULL.
- */
-
-static int screen_forward(term_screen *screen, unsigned int cmd, const term_seq *seq) {
- if (!screen->cmd_fn)
- return 0;
-
- return screen->cmd_fn(screen, screen->cmd_fn_data, cmd, seq);
-}
-
-/*
- * Screen Helpers
- * These helpers implement common-operations like cursor-handler and more, which
- * are used by several command dispatchers.
- */
-
-static unsigned int screen_clamp_x(term_screen *screen, unsigned int x) {
- if (x >= screen->page->width)
- return (screen->page->width > 0) ? screen->page->width - 1 : 0;
-
- return x;
-}
-
-static unsigned int screen_clamp_y(term_screen *screen, unsigned int y) {
- if (y >= screen->page->height)
- return (screen->page->height > 0) ? screen->page->height - 1 : 0;
-
- return y;
-}
-
-static bool screen_tab_is_set(term_screen *screen, unsigned int pos) {
- if (pos >= screen->page->width)
- return false;
-
- return screen->tabs[pos / 8] & (1 << (pos % 8));
-}
-
-static inline void screen_age_cursor(term_screen *screen) {
- term_cell *cell;
-
- cell = term_page_get_cell(screen->page, screen->state.cursor_x, screen->state.cursor_y);
- if (cell)
- cell->age = screen->age;
-}
-
-static void screen_cursor_clear_wrap(term_screen *screen) {
- screen->flags &= ~TERM_FLAG_PENDING_WRAP;
-}
-
-static void screen_cursor_set(term_screen *screen, unsigned int x, unsigned int y) {
- x = screen_clamp_x(screen, x);
- y = screen_clamp_y(screen, y);
-
- if (x == screen->state.cursor_x && y == screen->state.cursor_y)
- return;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-
- screen->state.cursor_x = x;
- screen->state.cursor_y = y;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-}
-
-static void screen_cursor_set_rel(term_screen *screen, unsigned int x, unsigned int y) {
- if (screen->state.origin_mode) {
- x = screen_clamp_x(screen, x);
- y = screen_clamp_x(screen, y) + screen->page->scroll_idx;
-
- if (y >= screen->page->scroll_idx + screen->page->scroll_num) {
- y = screen->page->scroll_idx + screen->page->scroll_num;
- if (screen->page->scroll_num > 0)
- y -= 1;
- }
- }
-
- screen_cursor_set(screen, x, y);
-}
-
-static void screen_cursor_left(term_screen *screen, unsigned int num) {
- if (num > screen->state.cursor_x)
- num = screen->state.cursor_x;
-
- screen_cursor_set(screen, screen->state.cursor_x - num, screen->state.cursor_y);
-}
-
-static void screen_cursor_left_tab(term_screen *screen, unsigned int num) {
- unsigned int i;
-
- i = screen->state.cursor_x;
- while (i > 0 && num > 0) {
- if (screen_tab_is_set(screen, --i))
- --num;
- }
-
- screen_cursor_set(screen, i, screen->state.cursor_y);
-}
-
-static void screen_cursor_right(term_screen *screen, unsigned int num) {
- if (num > screen->page->width)
- num = screen->page->width;
-
- screen_cursor_set(screen, screen->state.cursor_x + num, screen->state.cursor_y);
-}
-
-static void screen_cursor_right_tab(term_screen *screen, unsigned int num) {
- unsigned int i;
-
- i = screen->state.cursor_x;
- while (i + 1 < screen->page->width && num > 0) {
- if (screen_tab_is_set(screen, ++i))
- --num;
- }
-
- screen_cursor_set(screen, i, screen->state.cursor_y);
-}
-
-static void screen_cursor_up(term_screen *screen, unsigned int num, bool scroll) {
- unsigned int max;
-
- if (screen->state.cursor_y < screen->page->scroll_idx) {
- if (num > screen->state.cursor_y)
- num = screen->state.cursor_y;
-
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
- } else {
- max = screen->state.cursor_y - screen->page->scroll_idx;
- if (num > max) {
- if (num < 1)
- return;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-
- if (scroll)
- term_page_scroll_down(screen->page, num - max, &screen->state.attr, screen->age, NULL);
-
- screen->state.cursor_y = screen->page->scroll_idx;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
- } else {
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
- }
- }
-}
-
-static void screen_cursor_down(term_screen *screen, unsigned int num, bool scroll) {
- unsigned int max;
-
- if (screen->state.cursor_y >= screen->page->scroll_idx + screen->page->scroll_num) {
- if (num > screen->page->height)
- num = screen->page->height;
-
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y - num);
- } else {
- max = screen->page->scroll_idx + screen->page->scroll_num - 1 - screen->state.cursor_y;
- if (num > max) {
- if (num < 1)
- return;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
-
- if (scroll)
- term_page_scroll_up(screen->page, num - max, &screen->state.attr, screen->age, screen->history);
-
- screen->state.cursor_y = screen->page->scroll_idx + screen->page->scroll_num - 1;
-
- if (!(screen->flags & TERM_FLAG_HIDE_CURSOR))
- screen_age_cursor(screen);
- } else {
- screen_cursor_set(screen, screen->state.cursor_x, screen->state.cursor_y + num);
- }
- }
-}
-
-static void screen_save_state(term_screen *screen, term_state *where) {
- *where = screen->state;
-}
-
-static void screen_restore_state(term_screen *screen, term_state *from) {
- screen_cursor_set(screen, from->cursor_x, from->cursor_y);
- screen->state = *from;
-}
-
-static void screen_reset_page(term_screen *screen, term_page *page) {
- term_page_set_scroll_region(page, 0, page->height);
- term_page_erase(page, 0, 0, page->width, page->height, &screen->state.attr, screen->age, false);
-}
-
-static void screen_change_alt(term_screen *screen, bool set) {
- if (set) {
- screen->page = screen->page_alt;
- screen->history = NULL;
- } else {
- screen->page = screen->page_main;
- screen->history = screen->history_main;
- }
-
- screen->page->age = screen->age;
-}
-
-static inline void set_reset(term_screen *screen, unsigned int flag, bool set) {
- if (set)
- screen->flags |= flag;
- else
- screen->flags &= ~flag;
-}
-
-static void screen_mode_change_ansi(term_screen *screen, unsigned mode, bool set) {
- switch (mode) {
- case 20:
- /*
- * LNM: line-feed/new-line mode
- * TODO
- */
- set_reset(screen, TERM_FLAG_NEWLINE_MODE, set);
-
- break;
- default:
- log_debug("terminal: failed to %s unknown ANSI mode %u", set ? "set" : "unset", mode);
- }
-}
-
-static void screen_mode_change_dec(term_screen *screen, unsigned int mode, bool set) {
- switch (mode) {
- case 1:
- /*
- * DECCKM: cursor-keys
- * TODO
- */
- set_reset(screen, TERM_FLAG_CURSOR_KEYS, set);
-
- break;
- case 6:
- /*
- * DECOM: origin-mode
- * TODO
- */
- screen->state.origin_mode = set;
-
- break;
- case 7:
- /*
- * DECAWN: auto-wrap mode
- * TODO
- */
- screen->state.auto_wrap = set;
-
- break;
- case 25:
- /*
- * DECTCEM: text-cursor-enable
- * TODO
- */
- set_reset(screen, TERM_FLAG_HIDE_CURSOR, !set);
- screen_age_cursor(screen);
-
- break;
- case 47:
- /*
- * XTERM-ASB: alternate-screen-buffer
- * This enables/disables the alternate screen-buffer.
- * It effectively saves the current page content and
- * allows you to restore it when changing to the
- * original screen-buffer again.
- */
- screen_change_alt(screen, set);
-
- break;
- case 1047:
- /*
- * XTERM-ASBPE: alternate-screen-buffer-post-erase
- * This is the same as XTERM-ASB but erases the
- * alternate screen-buffer before switching back to the
- * original buffer. Use it to discard any data on the
- * alternate screen buffer when done.
- */
- if (!set)
- screen_reset_page(screen, screen->page_alt);
-
- screen_change_alt(screen, set);
-
- break;
- case 1048:
- /*
- * XTERM-ASBCS: alternate-screen-buffer-cursor-state
- * This has the same effect as DECSC/DECRC, but uses a
- * separate state buffer. It is usually used in
- * combination with alternate screen buffers to provide
- * stacked state storage.
- */
- if (set)
- screen_save_state(screen, &screen->saved_alt);
- else
- screen_restore_state(screen, &screen->saved_alt);
-
- break;
- case 1049:
- /*
- * XTERM-ASBX: alternate-screen-buffer-extended
- * This combines XTERM-ASBPE and XTERM-ASBCS somewhat.
- * When enabling, state is saved, alternate screen
- * buffer is activated and cleared.
- * When disabled, alternate screen buffer is cleared,
- * then normal screen buffer is enabled and state is
- * restored.
- */
- if (set)
- screen_save_state(screen, &screen->saved_alt);
-
- screen_reset_page(screen, screen->page_alt);
- screen_change_alt(screen, set);
-
- if (!set)
- screen_restore_state(screen, &screen->saved_alt);
-
- break;
- default:
- log_debug("terminal: failed to %s unknown DEC mode %u", set ? "set" : "unset", mode);
- }
-}
-
-/* map a character according to current GL and GR maps */
-static uint32_t screen_map(term_screen *screen, uint32_t val) {
- uint32_t nval = -1U;
-
- /* 32 and 127 always map to identity. 160 and 255 map to identity iff a
- * 96 character set is loaded into GR. Values above 255 always map to
- * identity. */
- switch (val) {
- case 33 ... 126:
- if (screen->state.glt) {
- nval = (**screen->state.glt)[val - 32];
- screen->state.glt = NULL;
- } else {
- nval = (**screen->state.gl)[val - 32];
- }
- break;
- case 160 ... 255:
- if (screen->state.grt) {
- nval = (**screen->state.grt)[val - 160];
- screen->state.grt = NULL;
- } else {
- nval = (**screen->state.gr)[val - 160];
- }
- break;
- }
-
- return (nval == -1U) ? val : nval;
-}
-
-/*
- * Command Handlers
- * This is the unofficial documentation of all the TERM_CMD_* definitions. Each
- * handled command has a separate function with an extensive comment on the
- * semantics of the command.
- * Note that many semantics are unknown and need to be verified. This is mostly
- * about error-handling, though. Applications rarely rely on those features.
- */
-
-static int screen_DA1(term_screen *screen, const term_seq *seq);
-static int screen_LF(term_screen *screen, const term_seq *seq);
-
-static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) {
- term_char_t ch = TERM_CHAR_NULL;
-
- if (screen->state.cursor_x + 1 == screen->page->width
- && screen->flags & TERM_FLAG_PENDING_WRAP
- && screen->state.auto_wrap) {
- screen_cursor_down(screen, 1, true);
- screen_cursor_set(screen, 0, screen->state.cursor_y);
- }
-
- screen_cursor_clear_wrap(screen);
-
- ch = term_char_merge(ch, screen_map(screen, seq->terminator));
- term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false);
-
- if (screen->state.cursor_x + 1 == screen->page->width)
- screen->flags |= TERM_FLAG_PENDING_WRAP;
- else
- screen_cursor_right(screen, 1);
-
- return 0;
-}
-
-static int screen_BEL(term_screen *screen, const term_seq *seq) {
- /*
- * BEL - sound bell tone
- * This command should trigger an acoustic bell. Usually, this is
- * forwarded directly to the pcspkr. However, bells have become quite
- * uncommon and annoying, so we're not implementing them here. Instead,
- * it's one of the commands we forward to the caller.
- */
-
- return screen_forward(screen, TERM_CMD_BEL, seq);
-}
-
-static int screen_BS(term_screen *screen, const term_seq *seq) {
- /*
- * BS - backspace
- * Move cursor one cell to the left. If already at the left margin,
- * nothing happens.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_left(screen, 1);
- return 0;
-}
-
-static int screen_CBT(term_screen *screen, const term_seq *seq) {
- /*
- * CBT - cursor-backward-tabulation
- * Move the cursor @args[0] tabs backwards (to the left). The
- * current cursor cell, in case it's a tab, is not counted.
- * Furthermore, the cursor cannot be moved beyond position 0 and
- * it will stop there.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_left_tab(screen, num);
-
- return 0;
-}
-
-static int screen_CHA(term_screen *screen, const term_seq *seq) {
- /*
- * CHA - cursor-horizontal-absolute
- * Move the cursor to position @args[0] in the current line. The
- * cursor cannot be moved beyond the rightmost cell and will stop
- * there.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int pos = 1;
-
- if (seq->args[0] > 0)
- pos = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, pos - 1, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_CHT(term_screen *screen, const term_seq *seq) {
- /*
- * CHT - cursor-horizontal-forward-tabulation
- * Move the cursor @args[0] tabs forward (to the right). The
- * current cursor cell, in case it's a tab, is not counted.
- * Furthermore, the cursor cannot be moved beyond the rightmost cell
- * and will stop there.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right_tab(screen, num);
-
- return 0;
-}
-
-static int screen_CNL(term_screen *screen, const term_seq *seq) {
- /*
- * CNL - cursor-next-line
- * Move the cursor @args[0] lines down.
- *
- * TODO: Does this stop at the bottom or cause a scroll-up?
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, num, false);
-
- return 0;
-}
-
-static int screen_CPL(term_screen *screen, const term_seq *seq) {
- /*
- * CPL - cursor-preceding-line
- * Move the cursor @args[0] lines up.
- *
- * TODO: Does this stop at the top or cause a scroll-up?
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_up(screen, num, false);
-
- return 0;
-}
-
-static int screen_CR(term_screen *screen, const term_seq *seq) {
- /*
- * CR - carriage-return
- * Move the cursor to the left margin on the current line.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, 0, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_CUB(term_screen *screen, const term_seq *seq) {
- /*
- * CUB - cursor-backward
- * Move the cursor @args[0] positions to the left. The cursor stops
- * at the left-most position.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_left(screen, num);
-
- return 0;
-}
-
-static int screen_CUD(term_screen *screen, const term_seq *seq) {
- /*
- * CUD - cursor-down
- * Move the cursor @args[0] positions down. The cursor stops at the
- * bottom margin. If it was already moved further, it stops at the
- * bottom line.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, num, false);
-
- return 0;
-}
-
-static int screen_CUF(term_screen *screen, const term_seq *seq) {
- /*
- * CUF -cursor-forward
- * Move the cursor @args[0] positions to the right. The cursor stops
- * at the right-most position.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right(screen, num);
-
- return 0;
-}
-
-static int screen_CUP(term_screen *screen, const term_seq *seq) {
- /*
- * CUP - cursor-position
- * Moves the cursor to position @args[1] x @args[0]. If either is 0, it
- * is treated as 1. The positions are subject to the origin-mode and
- * clamped to the addressable with/height.
- *
- * Defaults:
- * args[0]: 1
- * args[1]: 1
- */
-
- unsigned int x = 1, y = 1;
-
- if (seq->args[0] > 0)
- y = seq->args[0];
- if (seq->args[1] > 0)
- x = seq->args[1];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set_rel(screen, x - 1, y - 1);
-
- return 0;
-}
-
-static int screen_CUU(term_screen *screen, const term_seq *seq) {
- /*
- * CUU - cursor-up
- * Move the cursor @args[0] positions up. The cursor stops at the
- * top margin. If it was already moved further, it stops at the
- * top line.
- *
- * Defaults:
- * args[0]: 1
- *
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_up(screen, num, false);
-
- return 0;
-}
-
-static int screen_DA1(term_screen *screen, const term_seq *seq) {
- /*
- * DA1 - primary-device-attributes
- * The primary DA asks for basic terminal features. We simply return
- * a hard-coded list of features we implement.
- * Note that the primary DA asks for supported features, not currently
- * enabled features.
- *
- * The terminal's answer is:
- * ^[ ? 64 ; ARGS c
- * The first argument, 64, is fixed and denotes a VT420, the last
- * DEC-term that extended this number.
- * All following arguments denote supported features. Note
- * that at most 15 features can be sent (max CSI args). It is safe to
- * send more, but clients might not be able to parse them. This is a
- * client's problem and we shouldn't care. There is no other way to
- * send those feature lists, so we have to extend them beyond 15 in
- * those cases.
- *
- * Known modes:
- * 1: 132 column mode
- * The 132 column mode is supported by the terminal.
- * 2: printer port
- * A priner-port is supported and can be addressed via
- * control-codes.
- * 3: ReGIS graphics
- * Support for ReGIS graphics is available. The ReGIS routines
- * provide the "remote graphics instruction set" and allow basic
- * vector-rendering.
- * 4: sixel
- * Support of Sixel graphics is available. This provides access
- * to the sixel bitmap routines.
- * 6: selective erase
- * The terminal supports DECSCA and related selective-erase
- * functions. This allows to protect specific cells from being
- * erased, if specified.
- * 7: soft character set (DRCS)
- * TODO: ?
- * 8: user-defined keys (UDKs)
- * TODO: ?
- * 9: national-replacement character sets (NRCS)
- * National-replacement character-sets are available.
- * 12: Yugoslavian (SCS)
- * TODO: ?
- * 15: technical character set
- * The DEC technical-character-set is available.
- * 18: windowing capability
- * TODO: ?
- * 21: horizontal scrolling
- * TODO: ?
- * 22: ANSII color
- * TODO: ?
- * 23: Greek
- * TODO: ?
- * 24: Turkish
- * TODO: ?
- * 29: ANSI text locator
- * TODO: ?
- * 42: ISO Latin-2 character set
- * TODO: ?
- * 44: PCTerm
- * TODO: ?
- * 45: soft keymap
- * TODO: ?
- * 46: ASCII emulation
- * TODO: ?
- */
-
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, "?64;1;6;9;15c");
-}
-
-static int screen_DA2(term_screen *screen, const term_seq *seq) {
- /*
- * DA2 - secondary-device-attributes
- * The secondary DA asks for the terminal-ID, firmware versions and
- * other non-primary attributes. All these values are
- * informational-only and should not be used by the host to detect
- * terminal features.
- *
- * The terminal's response is:
- * ^[ > 61 ; FIRMWARE ; KEYBOARD c
- * whereas 65 is fixed for VT525 terminals, the last terminal-line that
- * increased this number. FIRMWARE is the firmware
- * version encoded as major/minor (20 == 2.0) and KEYBOARD is 0 for STD
- * keyboard and 1 for PC keyboards.
- *
- * We replace the firmware-version with the systemd-version so clients
- * can decode it again.
- */
-
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, ">65;" PACKAGE_VERSION ";1c");
-}
-
-static int screen_DA3(term_screen *screen, const term_seq *seq) {
- /*
- * DA3 - tertiary-device-attributes
- * The tertiary DA is used to query the terminal-ID.
- *
- * The terminal's response is:
- * ^P ! | XX AA BB CC ^\
- * whereas all four parameters are hexadecimal-encoded pairs. XX
- * denotes the manufacturing site, AA BB CC is the terminal's ID.
- */
-
- /* we do not support tertiary DAs */
- return 0;
-}
-
-static int screen_DC1(term_screen *screen, const term_seq *seq) {
- /*
- * DC1 - device-control-1 or XON
- * This clears any previous XOFF and resumes terminal-transmission.
- */
-
- /* we do not support XON */
- return 0;
-}
-
-static int screen_DC3(term_screen *screen, const term_seq *seq) {
- /*
- * DC3 - device-control-3 or XOFF
- * Stops terminal transmission. No further characters are sent until
- * an XON is received.
- */
-
- /* we do not support XOFF */
- return 0;
-}
-
-static int screen_DCH(term_screen *screen, const term_seq *seq) {
- /*
- * DCH - delete-character
- * This deletes @argv[0] characters at the current cursor position. As
- * characters are deleted, the remaining characters between the cursor
- * and right margin move to the left. Character attributes move with the
- * characters. The terminal adds blank spaces with no visual character
- * attributes at the right margin. DCH has no effect outside the
- * scrolling margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- term_page_delete_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_DECALN(term_screen *screen, const term_seq *seq) {
- /*
- * DECALN - screen-alignment-pattern
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECANM(term_screen *screen, const term_seq *seq) {
- /*
- * DECANM - ansi-mode
- * Set the terminal into VT52 compatibility mode. Control sequences
- * overlap with regular sequences so we have to detect them early before
- * dispatching them.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECBI(term_screen *screen, const term_seq *seq) {
- /*
- * DECBI - back-index
- * This control function moves the cursor backward one column. If the
- * cursor is at the left margin, then all screen data within the margin
- * moves one column to the right. The column that shifted past the right
- * margin is lost.
- * DECBI adds a new column at the left margin with no visual attributes.
- * DECBI does not affect the margins. If the cursor is beyond the
- * left-margin at the left border, then the terminal ignores DECBI.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECCARA(term_screen *screen, const term_seq *seq) {
- /*
- * DECCARA - change-attributes-in-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECCRA(term_screen *screen, const term_seq *seq) {
- /*
- * DECCRA - copy-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDC(term_screen *screen, const term_seq *seq) {
- /*
- * DECDC - delete-column
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDHL_BH(term_screen *screen, const term_seq *seq) {
- /*
- * DECDHL_BH - double-width-double-height-line: bottom half
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDHL_TH(term_screen *screen, const term_seq *seq) {
- /*
- * DECDHL_TH - double-width-double-height-line: top half
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECDWL(term_screen *screen, const term_seq *seq) {
- /*
- * DECDWL - double-width-single-height-line
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECEFR(term_screen *screen, const term_seq *seq) {
- /*
- * DECEFR - enable-filter-rectangle
- * Defines the coordinates of a filter rectangle (top, left, bottom,
- * right as @args[0] to @args[3]) and activates it.
- * Anytime the locator is detected outside of the filter rectangle, an
- * outside rectangle event is generated and the rectangle is disabled.
- * Filter rectangles are always treated as "one-shot" events. Any
- * parameters that are omitted default to the current locator position.
- * If all parameters are omitted, any locator motion will be reported.
- * DECELR always cancels any prevous rectangle definition.
- *
- * The locator is usually associated with the mouse-cursor, but based
- * on cells instead of pixels. See DECELR how to initialize and enable
- * it. DECELR can also enable pixel-mode instead of cell-mode.
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECELF(term_screen *screen, const term_seq *seq) {
- /*
- * DECELF - enable-local-functions
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECELR(term_screen *screen, const term_seq *seq) {
- /*
- * DECELR - enable-locator-reporting
- * This changes the locator-reporting mode. @args[0] specifies the mode
- * to set, 0 disables locator-reporting, 1 enables it continuously, 2
- * enables it for a single report. @args[1] specifies the
- * precision-mode. 0 and 2 set the reporting to cell-precision, 1 sets
- * pixel-precision.
- *
- * Defaults:
- * args[0]: 0
- * args[1]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECERA(term_screen *screen, const term_seq *seq) {
- /*
- * DECERA - erase-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECFI(term_screen *screen, const term_seq *seq) {
- /*
- * DECFI - forward-index
- * This control function moves the cursor forward one column. If the
- * cursor is at the right margin, then all screen data within the
- * margins moves one column to the left. The column shifted past the
- * left margin is lost.
- * DECFI adds a new column at the right margin, with no visual
- * attributes. DECFI does not affect margins. If the cursor is beyond
- * the right margin at the border of the page when the terminal
- * receives DECFI, then the terminal ignores DECFI.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECFRA(term_screen *screen, const term_seq *seq) {
- /*
- * DECFRA - fill-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECIC(term_screen *screen, const term_seq *seq) {
- /*
- * DECIC - insert-column
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECID(term_screen *screen, const term_seq *seq) {
- /*
- * DECID - return-terminal-id
- * This is an obsolete form of TERM_CMD_DA1.
- */
-
- return screen_DA1(screen, seq);
-}
-
-static int screen_DECINVM(term_screen *screen, const term_seq *seq) {
- /*
- * DECINVM - invoke-macro
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECKBD(term_screen *screen, const term_seq *seq) {
- /*
- * DECKBD - keyboard-language-selection
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECKPAM(term_screen *screen, const term_seq *seq) {
- /*
- * DECKPAM - keypad-application-mode
- * Enables the keypad-application mode. If enabled, the keypad sends
- * special characters instead of the printed characters. This way,
- * applications can detect whether a numeric key was pressed on the
- * top-row or on the keypad.
- * Default is keypad-numeric-mode.
- */
-
- screen->flags |= TERM_FLAG_KEYPAD_MODE;
-
- return 0;
-}
-
-static int screen_DECKPNM(term_screen *screen, const term_seq *seq) {
- /*
- * DECKPNM - keypad-numeric-mode
- * This disables the keypad-application-mode (DECKPAM) and returns to
- * the keypad-numeric-mode. Keypresses on the keypad generate the same
- * sequences as corresponding keypresses on the main keyboard.
- * Default is keypad-numeric-mode.
- */
-
- screen->flags &= ~TERM_FLAG_KEYPAD_MODE;
-
- return 0;
-}
-
-static int screen_DECLFKC(term_screen *screen, const term_seq *seq) {
- /*
- * DECLFKC - local-function-key-control
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECLL(term_screen *screen, const term_seq *seq) {
- /*
- * DECLL - load-leds
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECLTOD(term_screen *screen, const term_seq *seq) {
- /*
- * DECLTOD - load-time-of-day
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECPCTERM(term_screen *screen, const term_seq *seq) {
- /*
- * DECPCTERM - pcterm-mode
- * This enters/exits the PCTerm mode. Default mode is VT-mode. It can
- * also select parameters for scancode/keycode mappings in SCO mode.
- *
- * Definitely not worth implementing. Lets kill PCTerm/SCO modes!
- */
-
- return 0;
-}
-
-static int screen_DECPKA(term_screen *screen, const term_seq *seq) {
- /*
- * DECPKA - program-key-action
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECPKFMR(term_screen *screen, const term_seq *seq) {
- /*
- * DECPKFMR - program-key-free-memory-report
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRARA(term_screen *screen, const term_seq *seq) {
- /*
- * DECRARA - reverse-attributes-in-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRC(term_screen *screen, const term_seq *seq) {
- /*
- * DECRC - restore-cursor
- * Restores the terminal to the state saved by the save cursor (DECSC)
- * function. This includes more than just the cursor-position.
- *
- * If nothing was saved by DECSC, then DECRC performs the following
- * actions:
- * * Moves the cursor to the home position (upper left of screen).
- * * Resets origin mode (DECOM).
- * * Turns all character attributes off (normal setting).
- * * Maps the ASCII character set into GL, and the DEC Supplemental
- * Graphic set into GR.
- *
- * The terminal maintains a separate DECSC buffer for the main display
- * and the status line. This feature lets you save a separate operating
- * state for the main display and the status line.
- */
-
- screen_restore_state(screen, &screen->saved);
-
- return 0;
-}
-
-static int screen_DECREQTPARM(term_screen *screen, const term_seq *seq) {
- /*
- * DECREQTPARM - request-terminal-parameters
- * The sequence DECREPTPARM is sent by the terminal controller to notify
- * the host of the status of selected terminal parameters. The status
- * sequence may be sent when requested by the host or at the terminal's
- * discretion. DECREPTPARM is sent upon receipt of a DECREQTPARM.
- *
- * If @args[0] is 0, this marks a request and the terminal is allowed
- * to send DECREPTPARM messages without request. If it is 1, the same
- * applies but the terminal should no longer send DECREPTPARM
- * unrequested.
- * 2 and 3 mark a report, but 3 is only used if the terminal answers as
- * an explicit request with @args[0] == 1.
- *
- * The other arguments are ignored in requests, but have the following
- * meaning in responses:
- * args[1]: 1=no-parity-set 4=parity-set-and-odd 5=parity-set-and-even
- * args[2]: 1=8bits-per-char 2=7bits-per-char
- * args[3]: transmission-speed
- * args[4]: receive-speed
- * args[5]: 1=bit-rate-multiplier-is-16
- * args[6]: This value communicates the four switch values in block 5
- * of SETUP B, which are only visible to the user when an STP
- * option is installed. These bits may be assigned for an STP
- * device. The four bits are a decimal-encoded binary number.
- * Value between 0-15.
- *
- * The transmission/receive speeds have mappings for number => bits/s
- * which are quite weird. Examples are: 96->3600, 112->9600, 120->19200
- *
- * Defaults:
- * args[0]: 0
- */
-
- if (seq->n_args < 1 || seq->args[0] == 0) {
- screen->flags &= ~TERM_FLAG_INHIBIT_TPARM;
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, "2;1;1;120;120;1;0x");
- } else if (seq->args[0] == 1) {
- screen->flags |= TERM_FLAG_INHIBIT_TPARM;
- return SEQ_WRITE(screen, C0_CSI, C1_CSI, "3;1;1;120;120;1;0x");
- } else {
- return 0;
- }
-}
-
-static int screen_DECRPKT(term_screen *screen, const term_seq *seq) {
- /*
- * DECRPKT - report-key-type
- * Response to DECRQKT, we can safely ignore it as we're the one sending
- * it to the host.
- */
-
- return 0;
-}
-
-static int screen_DECRQCRA(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQCRA - request-checksum-of-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQDE(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQDE - request-display-extent
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQKT(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQKT - request-key-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQLP(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQLP - request-locator-position
- * See DECELR for locator-information.
- *
- * TODO: document and implement
- */
-
- return 0;
-}
-
-static int screen_DECRQM_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQM_ANSI - request-mode-ansi
- * The host sends this control function to find out if a particular mode
- * is set or reset. The terminal responds with a report mode function.
- * @args[0] contains the mode to query.
- *
- * Response is DECRPM with the first argument set to the mode that was
- * queried, second argument is 0 if mode is invalid, 1 if mode is set,
- * 2 if mode is not set (reset), 3 if mode is permanently set and 4 if
- * mode is permanently not set (reset):
- * ANSI: ^[ MODE ; VALUE $ y
- * DEC: ^[ ? MODE ; VALUE $ y
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECRQM_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQM_DEC - request-mode-dec
- * Same as DECRQM_ANSI but for DEC modes.
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECRQPKFM(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQPKFM - request-program-key-free-memory
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQPSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQPSR - request-presentation-state-report
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQTSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQTSR - request-terminal-state-report
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECRQUPSS(term_screen *screen, const term_seq *seq) {
- /*
- * DECRQUPSS - request-user-preferred-supplemental-set
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSACE(term_screen *screen, const term_seq *seq) {
- /*
- * DECSACE - select-attribute-change-extent
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSASD(term_screen *screen, const term_seq *seq) {
- /*
- * DECSASD - select-active-status-display
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSC(term_screen *screen, const term_seq *seq) {
- /*
- * DECSC - save-cursor
- * Save cursor and terminal state so it can be restored later on.
- * Saves the following items in the terminal's memory:
- * * Cursor position
- * * Character attributes set by the SGR command
- * * Character sets (G0, G1, G2, or G3) currently in GL and GR
- * * Wrap flag (autowrap or no autowrap)
- * * State of origin mode (DECOM)
- * * Selective erase attribute
- * * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent
- */
-
- screen_save_state(screen, &screen->saved);
-
- return 0;
-}
-
-static int screen_DECSCA(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCA - select-character-protection-attribute
- * Defines the characters that come after it as erasable or not erasable
- * from the screen. The selective erase control functions (DECSED and
- * DECSEL) can only erase characters defined as erasable.
- *
- * @args[0] specifies the new mode. 0 and 2 mark any following character
- * as erasable, 1 marks it as not erasable.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- case 2:
- screen->state.attr.protect = 0;
- break;
- case 1:
- screen->state.attr.protect = 1;
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSCL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCL - select-conformance-level
- * Select the terminal's operating level. The factory default is
- * level 4 (VT Level 4 mode, 7-bit controls).
- * When you change the conformance level, the terminal performs a hard
- * reset (RIS).
- *
- * @args[0] defines the conformance-level, valid values are:
- * 61: Level 1 (VT100)
- * 62: Level 2 (VT200)
- * 63: Level 3 (VT300)
- * 64: Level 4 (VT400)
- * @args[1] defines the 8bit-mode, valid values are:
- * 0: 8-bit controls
- * 1: 7-bit controls
- * 2: 8-bit controls (same as 0)
- *
- * If @args[0] is 61, then @args[1] is ignored and 7bit controls are
- * enforced.
- *
- * Defaults:
- * args[0]: 64
- * args[1]: 0
- */
-
- unsigned int level = 64, bit = 0;
-
- if (seq->n_args > 0) {
- level = seq->args[0];
- if (seq->n_args > 1)
- bit = seq->args[1];
- }
-
- term_screen_hard_reset(screen);
-
- switch (level) {
- case 61:
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT100;
- screen->flags |= TERM_FLAG_7BIT_MODE;
- break;
- case 62 ... 69:
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
- if (bit == 1)
- screen->flags |= TERM_FLAG_7BIT_MODE;
- else
- screen->flags &= ~TERM_FLAG_7BIT_MODE;
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSCP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCP - select-communication-port
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSCPP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCPP - select-columns-per-page
- * Select columns per page. The number of rows is unaffected by this.
- * @args[0] selectes the number of columns (width), DEC only defines 80
- * and 132, but we allow any integer here. 0 is equivalent to 80.
- * Page content is *not* cleared and the cursor is left untouched.
- * However, if the page is reduced in width and the cursor would be
- * outside the visible region, it's set to the right border. Newly added
- * cells are cleared. No data is retained outside the visible region.
- *
- * Defaults:
- * args[0]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSCS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCS - select-communication-speed
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSCUSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSCUSR - set-cursor-style
- * This changes the style of the cursor. @args[0] can be one of:
- * 0, 1: blinking block
- * 2: steady block
- * 3: blinking underline
- * 4: steady underline
- * Changing this setting does _not_ affect the cursor visibility itself.
- * Use DECTCEM for that.
- *
- * Defaults:
- * args[0]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSDDT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSDDT - select-disconnect-delay-time
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSDPT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSDPT - select-digital-printed-data-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSED(term_screen *screen, const term_seq *seq) {
- /*
- * DECSED - selective-erase-in-display
- * This control function erases some or all of the erasable characters
- * in the display. DECSED can only erase characters defined as erasable
- * by the DECSCA control function. DECSED works inside or outside the
- * scrolling margins.
- *
- * @args[0] defines which regions are erased. If it is 0, all cells from
- * the cursor (inclusive) till the end of the display are erase. If it
- * is 1, all cells from the start of the display till the cursor
- * (inclusive) are erased. If it is 2, all cells are erased.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, true);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, 0,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, 0,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, true);
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSEL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSEL - selective-erase-in-line
- * This control function erases some or all of the erasable characters
- * in a single line of text. DECSEL erases only those characters defined
- * as erasable by the DECSCA control function. DECSEL works inside or
- * outside the scrolling margins.
- *
- * @args[0] defines the region to be erased. If it is 0, all cells from
- * the cursor (inclusive) till the end of the line are erase. If it is
- * 1, all cells from the start of the line till the cursor (inclusive)
- * are erased. If it is 2, the whole line of the cursor is erased.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, true);
- break;
- }
-
- return 0;
-}
-
-static int screen_DECSERA(term_screen *screen, const term_seq *seq) {
- /*
- * DECSERA - selective-erase-rectangular-area
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSFC(term_screen *screen, const term_seq *seq) {
- /*
- * DECSFC - select-flow-control
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSKCV(term_screen *screen, const term_seq *seq) {
- /*
- * DECSKCV - set-key-click-volume
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSLCK(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLCK - set-lock-key-style
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSLE(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLE - select-locator-events
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSLPP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLPP - set-lines-per-page
- * Set the number of lines used for the page. @args[0] specifies the
- * number of lines to be used. DEC only allows a limited number of
- * choices, however, we allow all integers. 0 is equivalent to 24.
- *
- * Defaults:
- * args[0]: 0
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DECSLRM_OR_SC(term_screen *screen, const term_seq *seq) {
- /*
- * DECSLRM_OR_SC - set-left-and-right-margins or save-cursor
- *
- * TODO: Detect save-cursor and run it. DECSLRM is not worth
- * implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSMBV(term_screen *screen, const term_seq *seq) {
- /*
- * DECSMBV - set-margin-bell-volume
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSMKR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSMKR - select-modifier-key-reporting
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSNLS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSNLS - set-lines-per-screen
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSPP(term_screen *screen, const term_seq *seq) {
- /*
- * DECSPP - set-port-parameter
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSPPCS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSPPCS - select-pro-printer-character-set
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSPRTT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSPRTT - select-printer-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSR - secure-reset
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSRFR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSRFR - select-refresh-rate
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSSCLS(term_screen *screen, const term_seq *seq) {
- /*
- * DECSSCLS - set-scroll-speed
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSSDT(term_screen *screen, const term_seq *seq) {
- /*
- * DECSSDT - select-status-display-line-type
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSSL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSSL - select-setup-language
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECST8C(term_screen *screen, const term_seq *seq) {
- /*
- * DECST8C - set-tab-at-every-8-columns
- * Clear the tab-ruler and reset it to a tab at every 8th column,
- * starting at 9 (though, setting a tab at 1 is fine as it has no
- * effect).
- */
-
- unsigned int i;
-
- for (i = 0; i < screen->page->width; i += 8)
- screen->tabs[i / 8] = 0x1;
-
- return 0;
-}
-
-static int screen_DECSTBM(term_screen *screen, const term_seq *seq) {
- /*
- * DECSTBM - set-top-and-bottom-margins
- * This control function sets the top and bottom margins for the current
- * page. You cannot perform scrolling outside the margins.
- *
- * @args[0] defines the top margin, @args[1] defines the bottom margin.
- * The bottom margin must be lower than the top-margin.
- *
- * This call resets the cursor position to 0/0 of the page.
- *
- * Defaults:
- * args[0]: 1
- * args[1]: last page-line
- */
-
- unsigned int top, bottom;
-
- top = 1;
- bottom = screen->page->height;
-
- if (seq->args[0] > 0)
- top = seq->args[0];
- if (seq->args[1] > 0)
- bottom = seq->args[1];
-
- if (top > screen->page->height)
- top = screen->page->height;
- if (bottom > screen->page->height)
- bottom = screen->page->height;
-
- if (top >= bottom || top > screen->page->height || bottom > screen->page->height) {
- top = 1;
- bottom = screen->page->height;
- }
-
- term_page_set_scroll_region(screen->page, top - 1, bottom - top + 1);
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, 0, 0);
-
- return 0;
-}
-
-static int screen_DECSTR(term_screen *screen, const term_seq *seq) {
- /*
- * DECSTR - soft-terminal-reset
- * Perform a soft reset to the default values.
- */
-
- term_screen_soft_reset(screen);
-
- return 0;
-}
-
-static int screen_DECSTRL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSTRL - set-transmit-rate-limit
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSWBV(term_screen *screen, const term_seq *seq) {
- /*
- * DECSWBV - set-warning-bell-volume
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECSWL(term_screen *screen, const term_seq *seq) {
- /*
- * DECSWL - single-width-single-height-line
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECTID(term_screen *screen, const term_seq *seq) {
- /*
- * DECTID - select-terminal-id
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECTME(term_screen *screen, const term_seq *seq) {
- /*
- * DECTME - terminal-mode-emulation
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DECTST(term_screen *screen, const term_seq *seq) {
- /*
- * DECTST - invoke-confidence-test
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_DL(term_screen *screen, const term_seq *seq) {
- /*
- * DL - delete-line
- * This control function deletes one or more lines in the scrolling
- * region, starting with the line that has the cursor. @args[0] defines
- * the number of lines to delete. 0 is treated the same as 1.
- * As lines are deleted, lines below the cursor and in the scrolling
- * region move up. The terminal adds blank lines with no visual
- * character attributes at the bottom of the scrolling region. If it is
- * greater than the number of lines remaining on the page, DL deletes
- * only the remaining lines. DL has no effect outside the scrolling
- * margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_delete_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_DSR_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * DSR_ANSI - device-status-report-ansi
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_DSR_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * DSR_DEC - device-status-report-dec
- *
- * TODO: implement
- */
-
- return 0;
-}
-
-static int screen_ECH(term_screen *screen, const term_seq *seq) {
- /*
- * ECH - erase-character
- * This control function erases one or more characters, from the cursor
- * position to the right. ECH clears character attributes from erased
- * character positions. ECH works inside or outside the scrolling
- * margins.
- * @args[0] defines the number of characters to erase. 0 is treated the
- * same as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->state.cursor_x + num, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
-
- return 0;
-}
-
-static int screen_ED(term_screen *screen, const term_seq *seq) {
- /*
- * ED - erase-in-display
- * This control function erases characters from part or all of the
- * display. When you erase complete lines, they become single-height,
- * single-width lines, with all visual character attributes cleared. ED
- * works inside or outside the scrolling margins.
- *
- * @args[0] defines the region to erase. 0 means from cursor (inclusive)
- * till the end of the screen. 1 means from the start of the screen till
- * the cursor (inclusive) and 2 means the whole screen.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, false);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, 0,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, 0,
- screen->page->width, screen->page->height,
- &screen->state.attr, screen->age, false);
- break;
- }
-
- return 0;
-}
-
-static int screen_EL(term_screen *screen, const term_seq *seq) {
- /*
- * EL - erase-in-line
- * This control function erases characters on the line that has the
- * cursor. EL clears all character attributes from erased character
- * positions. EL works inside or outside the scrolling margins.
- *
- * @args[0] defines the region to erase. 0 means from cursor (inclusive)
- * till the end of the line. 1 means from the start of the line till the
- * cursor (inclusive) and 2 means the whole line.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- term_page_erase(screen->page,
- screen->state.cursor_x, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- case 1:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->state.cursor_x, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- case 2:
- term_page_erase(screen->page,
- 0, screen->state.cursor_y,
- screen->page->width, screen->state.cursor_y,
- &screen->state.attr, screen->age, false);
- break;
- }
-
- return 0;
-}
-
-static int screen_ENQ(term_screen *screen, const term_seq *seq) {
- /*
- * ENQ - enquiry
- * Transmit the answerback-string. If none is set, do nothing.
- */
-
- if (screen->answerback)
- return screen_write(screen, screen->answerback, strlen(screen->answerback));
-
- return 0;
-}
-
-static int screen_EPA(term_screen *screen, const term_seq *seq) {
- /*
- * EPA - end-of-guarded-area
- *
- * TODO: What is this?
- */
-
- return 0;
-}
-
-static int screen_FF(term_screen *screen, const term_seq *seq) {
- /*
- * FF - form-feed
- * This causes the cursor to jump to the next line. It is treated the
- * same as LF.
- */
-
- return screen_LF(screen, seq);
-}
-
-static int screen_HPA(term_screen *screen, const term_seq *seq) {
- /*
- * HPA - horizontal-position-absolute
- * HPA causes the active position to be moved to the n-th horizontal
- * position of the active line. If an attempt is made to move the active
- * position past the last position on the line, then the active position
- * stops at the last position on the line.
- *
- * @args[0] defines the horizontal position. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set(screen, num - 1, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_HPR(term_screen *screen, const term_seq *seq) {
- /*
- * HPR - horizontal-position-relative
- * HPR causes the active position to be moved to the n-th following
- * horizontal position of the active line. If an attempt is made to move
- * the active position past the last position on the line, then the
- * active position stops at the last position on the line.
- *
- * @args[0] defines the horizontal position. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right(screen, num);
-
- return 0;
-}
-
-static int screen_HT(term_screen *screen, const term_seq *seq) {
- /*
- * HT - horizontal-tab
- * Moves the cursor to the next tab stop. If there are no more tab
- * stops, the cursor moves to the right margin. HT does not cause text
- * to auto wrap.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_right_tab(screen, 1);
-
- return 0;
-}
-
-static int screen_HTS(term_screen *screen, const term_seq *seq) {
- /*
- * HTS - horizontal-tab-set
- * HTS sets a horizontal tab stop at the column position indicated by
- * the value of the active column when the terminal receives an HTS.
- *
- * Executing an HTS does not effect the other horizontal tab stop
- * settings.
- */
-
- unsigned int pos;
-
- pos = screen->state.cursor_x;
- if (screen->page->width > 0)
- screen->tabs[pos / 8] |= 1U << (pos % 8);
-
- return 0;
-}
-
-static int screen_HVP(term_screen *screen, const term_seq *seq) {
- /*
- * HVP - horizontal-and-vertical-position
- * This control function works the same as the cursor position (CUP)
- * function. Origin mode (DECOM) selects line numbering and the ability
- * to move the cursor into margins.
- *
- * Defaults:
- * args[0]: 1
- * args[1]: 1
- */
-
- return screen_CUP(screen, seq);
-}
-
-static int screen_ICH(term_screen *screen, const term_seq *seq) {
- /*
- * ICH - insert-character
- * This control function inserts one or more space (SP) characters
- * starting at the cursor position. @args[0] is the number of characters
- * to insert. 0 is treated as 1.
- *
- * The ICH sequence inserts blank characters with the normal
- * character attribute. The cursor remains at the beginning of the blank
- * characters. Text between the cursor and right margin moves to the
- * right. Characters scrolled past the right margin are lost. ICH has no
- * effect outside the scrolling margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- term_page_insert_cells(screen->page, screen->state.cursor_x, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_IL(term_screen *screen, const term_seq *seq) {
- /*
- * IL - insert-line
- * This control function inserts one or more blank lines, starting at
- * the cursor. @args[0] is the number of lines to insert. 0 is treated
- * as 1.
- *
- * As lines are inserted, lines below the cursor and in the scrolling
- * region move down. Lines scrolled off the page are lost. IL has no
- * effect outside the page margins.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- term_page_insert_lines(screen->page, screen->state.cursor_y, num, &screen->state.attr, screen->age);
-
- return 0;
-}
-
-static int screen_IND(term_screen *screen, const term_seq *seq) {
- /*
- * IND - index
- * IND moves the cursor down one line in the same column. If the cursor
- * is at the bottom margin, then the screen performs a scroll-up.
- */
-
- screen_cursor_down(screen, 1, true);
-
- return 0;
-}
-
-static int screen_LF(term_screen *screen, const term_seq *seq) {
- /*
- * LF - line-feed
- * Causes a line feed or a new line operation, depending on the setting
- * of line feed/new line mode.
- */
-
- screen_cursor_down(screen, 1, true);
- if (screen->flags & TERM_FLAG_NEWLINE_MODE)
- screen_cursor_left(screen, screen->state.cursor_x);
-
- return 0;
-}
-
-static int screen_LS1R(term_screen *screen, const term_seq *seq) {
- /*
- * LS1R - locking-shift-1-right
- * Map G1 into GR.
- */
-
- screen->state.gr = &screen->g1;
-
- return 0;
-}
-
-static int screen_LS2(term_screen *screen, const term_seq *seq) {
- /*
- * LS2 - locking-shift-2
- * Map G2 into GL.
- */
-
- screen->state.gl = &screen->g2;
-
- return 0;
-}
-
-static int screen_LS2R(term_screen *screen, const term_seq *seq) {
- /*
- * LS2R - locking-shift-2-right
- * Map G2 into GR.
- */
-
- screen->state.gr = &screen->g2;
-
- return 0;
-}
-
-static int screen_LS3(term_screen *screen, const term_seq *seq) {
- /*
- * LS3 - locking-shift-3
- * Map G3 into GL.
- */
-
- screen->state.gl = &screen->g3;
-
- return 0;
-}
-
-static int screen_LS3R(term_screen *screen, const term_seq *seq) {
- /*
- * LS3R - locking-shift-3-right
- * Map G3 into GR.
- */
-
- screen->state.gr = &screen->g3;
-
- return 0;
-}
-
-static int screen_MC_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * MC_ANSI - media-copy-ansi
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_MC_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * MC_DEC - media-copy-dec
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_NEL(term_screen *screen, const term_seq *seq) {
- /*
- * NEL - next-line
- * Moves cursor to first position on next line. If cursor is at bottom
- * margin, then screen performs a scroll-up.
- */
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, 1, true);
- screen_cursor_set(screen, 0, screen->state.cursor_y);
-
- return 0;
-}
-
-static int screen_NP(term_screen *screen, const term_seq *seq) {
- /*
- * NP - next-page
- * This control function moves the cursor forward to the home position
- * on one of the following pages in page memory. If there is only one
- * page, then the terminal ignores NP.
- * If NP tries to move the cursor past the last page in memory, then the
- * cursor stops at the last page.
- *
- * @args[0] defines the number of pages to forward. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_NULL(term_screen *screen, const term_seq *seq) {
- /*
- * NULL - null
- * The NULL operation does nothing. ASCII NULL is always ignored.
- */
-
- return 0;
-}
-
-static int screen_PP(term_screen *screen, const term_seq *seq) {
- /*
- * PP - preceding-page
- * This control function moves the cursor backward to the home position
- * on one of the preceding pages in page memory. If there is only one
- * page, then the terminal ignores PP.
- * If PP tries to move the cursor back farther than the first page in
- * memory, then the cursor stops at the first page.
- *
- * @args[0] defines the number of pages to go backwards. 0 is treated
- * as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_PPA(term_screen *screen, const term_seq *seq) {
- /*
- * PPA - page-position-absolute
- * This control function can move the cursor to the corresponding row
- * and column on any page in page memory. You select the page by its
- * number. If there is only one page, then the terminal ignores PPA.
- *
- * @args[0] is the number of the page to move the cursor to. If it is
- * greater than the number of the last page in memory, then the cursor
- * stops at the last page. If it is less than the number of the first
- * page, then the cursor stops at the first page.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_PPB(term_screen *screen, const term_seq *seq) {
- /*
- * PPB - page-position-backward
- * This control function moves the cursor backward to the corresponding
- * row and column on one of the preceding pages in page memory. If there
- * is only one page, then the terminal ignores PPB.
- *
- * @args[0] indicates the number of pages to move the cursor backward.
- * If it tries to move the cursor back farther than the first page in
- * memory, then the cursor stops at the first page. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_PPR(term_screen *screen, const term_seq *seq) {
- /*
- * PPR - page-position-relative
- * This control function moves the cursor forward to the corresponding
- * row and column on one of the following pages in page memory. If there
- * is only one page, then the terminal ignores PPR.
- *
- * @args[0] indicates how many pages to move the cursor forward. If it
- * tries to move the cursor beyond the last page in memory, then the
- * cursor stops at the last page. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing. We only support a single page.
- */
-
- return 0;
-}
-
-static int screen_RC(term_screen *screen, const term_seq *seq) {
- /*
- * RC - restore-cursor
- */
-
- return screen_DECRC(screen, seq);
-}
-
-static int screen_REP(term_screen *screen, const term_seq *seq) {
- /*
- * REP - repeat
- * Repeat the preceding graphics-character the given number of times.
- * @args[0] specifies how often it shall be repeated. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_RI(term_screen *screen, const term_seq *seq) {
- /*
- * RI - reverse-index
- * Moves the cursor up one line in the same column. If the cursor is at
- * the top margin, the page scrolls down.
- */
-
- screen_cursor_up(screen, 1, true);
-
- return 0;
-}
-
-static int screen_RIS(term_screen *screen, const term_seq *seq) {
- /*
- * RIS - reset-to-initial-state
- * This control function causes a nonvolatile memory (NVR) recall to
- * occur. RIS replaces all set-up features with their saved settings.
- *
- * The terminal stores these saved settings in NVR memory. The saved
- * setting for a feature is the same as the factory-default setting,
- * unless you saved a new setting.
- */
-
- term_screen_hard_reset(screen);
-
- return 0;
-}
-
-static int screen_RM_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * RM_ANSI - reset-mode-ansi
- *
- * TODO: implement (see VT510rm manual)
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_ansi(screen, seq->args[i], false);
-
- return 0;
-}
-
-static int screen_RM_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * RM_DEC - reset-mode-dec
- * This is the same as RM_ANSI but for DEC modes.
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_dec(screen, seq->args[i], false);
-
- return 0;
-}
-
-static int screen_S7C1T(term_screen *screen, const term_seq *seq) {
- /*
- * S7C1T - set-7bit-c1-terminal
- * This causes the terminal to start sending C1 controls as 7bit
- * sequences instead of 8bit C1 controls.
- * This is ignored if the terminal is below level-2 emulation mode
- * (VT100 and below), the terminal already sends 7bit controls then.
- */
-
- if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
- screen->flags |= TERM_FLAG_7BIT_MODE;
-
- return 0;
-}
-
-static int screen_S8C1T(term_screen *screen, const term_seq *seq) {
- /*
- * S8C1T - set-8bit-c1-terminal
- * This causes the terminal to start sending C1 controls as 8bit C1
- * control instead of 7bit sequences.
- * This is ignored if the terminal is below level-2 emulation mode
- * (VT100 and below). The terminal always sends 7bit controls in those
- * modes.
- */
-
- if (screen->conformance_level > TERM_CONFORMANCE_LEVEL_VT100)
- screen->flags &= ~TERM_FLAG_7BIT_MODE;
-
- return 0;
-}
-
-static int screen_SCS(term_screen *screen, const term_seq *seq) {
- /*
- * SCS - select-character-set
- * Designate character sets to G-sets. The mapping from intermediates
- * and terminal characters in the escape sequence to G-sets and
- * character-sets is non-trivial and implemented separately. See there
- * for more information.
- * This call simply sets the selected G-set to the desired
- * character-set.
- */
-
- term_charset *cs = NULL;
-
- /* TODO: support more of them? */
- switch (seq->charset) {
- case TERM_CHARSET_ISO_LATIN1_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_LATIN2_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_LATIN5_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_GREEK_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_HEBREW_SUPPLEMENTAL:
- case TERM_CHARSET_ISO_LATIN_CYRILLIC:
- break;
-
- case TERM_CHARSET_DEC_SPECIAL_GRAPHIC:
- cs = &term_dec_special_graphics;
- break;
- case TERM_CHARSET_DEC_SUPPLEMENTAL:
- cs = &term_dec_supplemental_graphics;
- break;
- case TERM_CHARSET_DEC_TECHNICAL:
- case TERM_CHARSET_CYRILLIC_DEC:
- case TERM_CHARSET_DUTCH_NRCS:
- case TERM_CHARSET_FINNISH_NRCS:
- case TERM_CHARSET_FRENCH_NRCS:
- case TERM_CHARSET_FRENCH_CANADIAN_NRCS:
- case TERM_CHARSET_GERMAN_NRCS:
- case TERM_CHARSET_GREEK_DEC:
- case TERM_CHARSET_GREEK_NRCS:
- case TERM_CHARSET_HEBREW_DEC:
- case TERM_CHARSET_HEBREW_NRCS:
- case TERM_CHARSET_ITALIAN_NRCS:
- case TERM_CHARSET_NORWEGIAN_DANISH_NRCS:
- case TERM_CHARSET_PORTUGUESE_NRCS:
- case TERM_CHARSET_RUSSIAN_NRCS:
- case TERM_CHARSET_SCS_NRCS:
- case TERM_CHARSET_SPANISH_NRCS:
- case TERM_CHARSET_SWEDISH_NRCS:
- case TERM_CHARSET_SWISS_NRCS:
- case TERM_CHARSET_TURKISH_DEC:
- case TERM_CHARSET_TURKISH_NRCS:
- break;
-
- case TERM_CHARSET_USERPREF_SUPPLEMENTAL:
- break;
- }
-
- if (seq->intermediates & TERM_SEQ_FLAG_POPEN)
- screen->g0 = cs ? : &term_unicode_lower;
- else if (seq->intermediates & TERM_SEQ_FLAG_PCLOSE)
- screen->g1 = cs ? : &term_unicode_upper;
- else if (seq->intermediates & TERM_SEQ_FLAG_MULT)
- screen->g2 = cs ? : &term_unicode_lower;
- else if (seq->intermediates & TERM_SEQ_FLAG_PLUS)
- screen->g3 = cs ? : &term_unicode_upper;
- else if (seq->intermediates & TERM_SEQ_FLAG_MINUS)
- screen->g1 = cs ? : &term_unicode_upper;
- else if (seq->intermediates & TERM_SEQ_FLAG_DOT)
- screen->g2 = cs ? : &term_unicode_lower;
- else if (seq->intermediates & TERM_SEQ_FLAG_SLASH)
- screen->g3 = cs ? : &term_unicode_upper;
-
- return 0;
-}
-
-static int screen_SD(term_screen *screen, const term_seq *seq) {
- /*
- * SD - scroll-down
- * This control function moves the user window down a specified number
- * of lines in page memory.
- * @args[0] is the number of lines to move the
- * user window up in page memory. New lines appear at the top of the
- * display. Old lines disappear at the bottom of the display. You
- * cannot pan past the top margin of the current page. 0 is treated
- * as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_scroll_down(screen->page, num, &screen->state.attr, screen->age, NULL);
-
- return 0;
-}
-
-static int screen_SGR(term_screen *screen, const term_seq *seq) {
- /*
- * SGR - select-graphics-rendition
- */
-
- term_color *dst;
- unsigned int i, code;
- int v;
-
- if (seq->n_args < 1) {
- zero(screen->state.attr);
- return 0;
- }
-
- for (i = 0; i < seq->n_args; ++i) {
- v = seq->args[i];
- switch (v) {
- case 1:
- screen->state.attr.bold = 1;
- break;
- case 3:
- screen->state.attr.italic = 1;
- break;
- case 4:
- screen->state.attr.underline = 1;
- break;
- case 5:
- screen->state.attr.blink = 1;
- break;
- case 7:
- screen->state.attr.inverse = 1;
- break;
- case 8:
- screen->state.attr.hidden = 1;
- break;
- case 22:
- screen->state.attr.bold = 0;
- break;
- case 23:
- screen->state.attr.italic = 0;
- break;
- case 24:
- screen->state.attr.underline = 0;
- break;
- case 25:
- screen->state.attr.blink = 0;
- break;
- case 27:
- screen->state.attr.inverse = 0;
- break;
- case 28:
- screen->state.attr.hidden = 0;
- break;
- case 30 ... 37:
- screen->state.attr.fg.ccode = v - 30 + TERM_CCODE_BLACK;
- break;
- case 39:
- screen->state.attr.fg.ccode = 0;
- break;
- case 40 ... 47:
- screen->state.attr.bg.ccode = v - 40 + TERM_CCODE_BLACK;
- break;
- case 49:
- screen->state.attr.bg.ccode = 0;
- break;
- case 90 ... 97:
- screen->state.attr.fg.ccode = v - 90 + TERM_CCODE_LIGHT_BLACK;
- break;
- case 100 ... 107:
- screen->state.attr.bg.ccode = v - 100 + TERM_CCODE_LIGHT_BLACK;
- break;
- case 38:
- /* fallthrough */
- case 48:
-
- if (v == 38)
- dst = &screen->state.attr.fg;
- else
- dst = &screen->state.attr.bg;
-
- ++i;
- if (i >= seq->n_args)
- break;
-
- switch (seq->args[i]) {
- case 2:
- /* 24bit-color support */
-
- i += 3;
- if (i >= seq->n_args)
- break;
-
- dst->ccode = TERM_CCODE_RGB;
- dst->red = (seq->args[i - 2] >= 0) ? seq->args[i - 2] : 0;
- dst->green = (seq->args[i - 1] >= 0) ? seq->args[i - 1] : 0;
- dst->blue = (seq->args[i] >= 0) ? seq->args[i] : 0;
-
- break;
- case 5:
- /* 256-color support */
-
- ++i;
- if (i >= seq->n_args || seq->args[i] < 0)
- break;
-
- dst->ccode = TERM_CCODE_256;
- code = seq->args[i];
- dst->c256 = code < 256 ? code : 0;
-
- break;
- }
-
- break;
- case -1:
- /* fallthrough */
- case 0:
- zero(screen->state.attr);
- break;
- }
- }
-
- return 0;
-}
-
-static int screen_SI(term_screen *screen, const term_seq *seq) {
- /*
- * SI - shift-in
- * Map G0 into GL.
- */
-
- screen->state.gl = &screen->g0;
-
- return 0;
-}
-
-static int screen_SM_ANSI(term_screen *screen, const term_seq *seq) {
- /*
- * SM_ANSI - set-mode-ansi
- *
- * TODO: implement
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_ansi(screen, seq->args[i], true);
-
- return 0;
-}
-
-static int screen_SM_DEC(term_screen *screen, const term_seq *seq) {
- /*
- * SM_DEC - set-mode-dec
- * This is the same as SM_ANSI but for DEC modes.
- */
-
- unsigned int i;
-
- for (i = 0; i < seq->n_args; ++i)
- screen_mode_change_dec(screen, seq->args[i], true);
-
- return 0;
-}
-
-static int screen_SO(term_screen *screen, const term_seq *seq) {
- /*
- * SO - shift-out
- * Map G1 into GL.
- */
-
- screen->state.gl = &screen->g1;
-
- return 0;
-}
-
-static int screen_SPA(term_screen *screen, const term_seq *seq) {
- /*
- * SPA - start-of-protected-area
- *
- * TODO: What is this?
- */
-
- return 0;
-}
-
-static int screen_SS2(term_screen *screen, const term_seq *seq) {
- /*
- * SS2 - single-shift-2
- * Temporarily map G2 into GL for the next graphics character.
- */
-
- screen->state.glt = &screen->g2;
-
- return 0;
-}
-
-static int screen_SS3(term_screen *screen, const term_seq *seq) {
- /*
- * SS3 - single-shift-3
- * Temporarily map G3 into GL for the next graphics character
- */
-
- screen->state.glt = &screen->g3;
-
- return 0;
-}
-
-static int screen_ST(term_screen *screen, const term_seq *seq) {
- /*
- * ST - string-terminator
- * The string-terminator is usually part of control-sequences and
- * handled by the parser. In all other situations it is silently
- * ignored.
- */
-
- return 0;
-}
-
-static int screen_SU(term_screen *screen, const term_seq *seq) {
- /*
- * SU - scroll-up
- * This control function moves the user window up a specified number of
- * lines in page memory.
- * @args[0] is the number of lines to move the
- * user window down in page memory. New lines appear at the bottom of
- * the display. Old lines disappear at the top of the display. You
- * cannot pan past the bottom margin of the current page. 0 is treated
- * as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- term_page_scroll_up(screen->page, num, &screen->state.attr, screen->age, screen->history);
-
- return 0;
-}
-
-static int screen_SUB(term_screen *screen, const term_seq *seq) {
- /*
- * SUB - substitute
- * Cancel the current control-sequence and print a replacement
- * character. Our parser already handles this so all we have to do is
- * print the replacement character.
- */
-
- static const term_seq rep = {
- .type = TERM_SEQ_GRAPHIC,
- .command = TERM_CMD_GRAPHIC,
- .terminator = 0xfffd,
- };
-
- return screen_GRAPHIC(screen, &rep);
-}
-
-static int screen_TBC(term_screen *screen, const term_seq *seq) {
- /*
- * TBC - tab-clear
- * This clears tab-stops. If @args[0] is 0, the tab-stop at the current
- * cursor position is cleared. If it is 3, all tab stops are cleared.
- *
- * Defaults:
- * args[0]: 0
- */
-
- unsigned int mode = 0, pos;
-
- if (seq->args[0] > 0)
- mode = seq->args[0];
-
- switch (mode) {
- case 0:
- pos = screen->state.cursor_x;
- if (screen->page->width > 0)
- screen->tabs[pos / 8] &= ~(1U << (pos % 8));
- break;
- case 3:
- if (screen->page->width > 0)
- memzero(screen->tabs, (screen->page->width + 7) / 8);
- break;
- }
-
- return 0;
-}
-
-static int screen_VPA(term_screen *screen, const term_seq *seq) {
- /*
- * VPA - vertical-line-position-absolute
- * VPA causes the active position to be moved to the corresponding
- * horizontal position. @args[0] specifies the line to jump to. If an
- * attempt is made to move the active position below the last line, then
- * the active position stops on the last line. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int pos = 1;
-
- if (seq->args[0] > 0)
- pos = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_set_rel(screen, screen->state.cursor_x, pos - 1);
-
- return 0;
-}
-
-static int screen_VPR(term_screen *screen, const term_seq *seq) {
- /*
- * VPR - vertical-line-position-relative
- * VPR causes the active position to be moved to the corresponding
- * horizontal position. @args[0] specifies the number of lines to jump
- * down relative to the current cursor position. If an attempt is made
- * to move the active position below the last line, the active position
- * stops at the last line. 0 is treated as 1.
- *
- * Defaults:
- * args[0]: 1
- */
-
- unsigned int num = 1;
-
- if (seq->args[0] > 0)
- num = seq->args[0];
-
- screen_cursor_clear_wrap(screen);
- screen_cursor_down(screen, num, false);
-
- return 0;
-}
-
-static int screen_VT(term_screen *screen, const term_seq *seq) {
- /*
- * VT - vertical-tab
- * This causes a vertical jump by one line. Terminals treat it exactly
- * the same as LF.
- */
-
- return screen_LF(screen, seq);
-}
-
-static int screen_XTERM_CLLHP(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_CLLHP - xterm-cursor-lower-left-hp-bugfix
- * Move the cursor to the lower-left corner of the page. This is an HP
- * bugfix by xterm.
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_IHMT(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_IHMT - xterm-initiate-highlight-mouse-tracking
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_MLHP(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_MLHP - xterm-memory-lock-hp-bugfix
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_MUHP(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_MUHP - xterm-memory-unlock-hp-bugfix
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_RPM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_RPM - xterm-restore-private-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_RRV(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_RRV - xterm-reset-resource-value
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_RTM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_RTM - xterm-reset-title-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SACL1(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SACL1 - xterm-set-ansi-conformance-level-1
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SACL2(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SACL2 - xterm-set-ansi-conformance-level-2
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SACL3(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SACL3 - xterm-set-ansi-conformance-level-3
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SDCS(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SDCS - xterm-set-default-character-set
- * Select the default character set. We treat this the same as UTF-8 as
- * this is our default character set. As we always use UTF-8, this
- * becomes as no-op.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SGFX(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SGFX - xterm-sixel-graphics
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SPM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SPM - xterm-set-private-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SRV(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SRV - xterm-set-resource-value
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_STM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_STM - xterm-set-title-mode
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-static int screen_XTERM_SUCS(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_SUCS - xterm-select-utf8-character-set
- * Select UTF-8 as character set. This is our default on only character
- * set. Hence, this is a no-op.
- */
-
- return 0;
-}
-
-static int screen_XTERM_WM(term_screen *screen, const term_seq *seq) {
- /*
- * XTERM_WM - xterm-window-management
- *
- * Probably not worth implementing.
- */
-
- return 0;
-}
-
-/*
- * Feeding data
- * The screen_feed_*() handlers take data from the user and feed it into the
- * screen. Once the parser has detected a sequence, we parse the command-type
- * and forward it to the command-dispatchers.
- */
-
-static int screen_feed_cmd(term_screen *screen, const term_seq *seq) {
- switch (seq->command) {
- case TERM_CMD_GRAPHIC:
- return screen_GRAPHIC(screen, seq);
- case TERM_CMD_BEL:
- return screen_BEL(screen, seq);
- case TERM_CMD_BS:
- return screen_BS(screen, seq);
- case TERM_CMD_CBT:
- return screen_CBT(screen, seq);
- case TERM_CMD_CHA:
- return screen_CHA(screen, seq);
- case TERM_CMD_CHT:
- return screen_CHT(screen, seq);
- case TERM_CMD_CNL:
- return screen_CNL(screen, seq);
- case TERM_CMD_CPL:
- return screen_CPL(screen, seq);
- case TERM_CMD_CR:
- return screen_CR(screen, seq);
- case TERM_CMD_CUB:
- return screen_CUB(screen, seq);
- case TERM_CMD_CUD:
- return screen_CUD(screen, seq);
- case TERM_CMD_CUF:
- return screen_CUF(screen, seq);
- case TERM_CMD_CUP:
- return screen_CUP(screen, seq);
- case TERM_CMD_CUU:
- return screen_CUU(screen, seq);
- case TERM_CMD_DA1:
- return screen_DA1(screen, seq);
- case TERM_CMD_DA2:
- return screen_DA2(screen, seq);
- case TERM_CMD_DA3:
- return screen_DA3(screen, seq);
- case TERM_CMD_DC1:
- return screen_DC1(screen, seq);
- case TERM_CMD_DC3:
- return screen_DC3(screen, seq);
- case TERM_CMD_DCH:
- return screen_DCH(screen, seq);
- case TERM_CMD_DECALN:
- return screen_DECALN(screen, seq);
- case TERM_CMD_DECANM:
- return screen_DECANM(screen, seq);
- case TERM_CMD_DECBI:
- return screen_DECBI(screen, seq);
- case TERM_CMD_DECCARA:
- return screen_DECCARA(screen, seq);
- case TERM_CMD_DECCRA:
- return screen_DECCRA(screen, seq);
- case TERM_CMD_DECDC:
- return screen_DECDC(screen, seq);
- case TERM_CMD_DECDHL_BH:
- return screen_DECDHL_BH(screen, seq);
- case TERM_CMD_DECDHL_TH:
- return screen_DECDHL_TH(screen, seq);
- case TERM_CMD_DECDWL:
- return screen_DECDWL(screen, seq);
- case TERM_CMD_DECEFR:
- return screen_DECEFR(screen, seq);
- case TERM_CMD_DECELF:
- return screen_DECELF(screen, seq);
- case TERM_CMD_DECELR:
- return screen_DECELR(screen, seq);
- case TERM_CMD_DECERA:
- return screen_DECERA(screen, seq);
- case TERM_CMD_DECFI:
- return screen_DECFI(screen, seq);
- case TERM_CMD_DECFRA:
- return screen_DECFRA(screen, seq);
- case TERM_CMD_DECIC:
- return screen_DECIC(screen, seq);
- case TERM_CMD_DECID:
- return screen_DECID(screen, seq);
- case TERM_CMD_DECINVM:
- return screen_DECINVM(screen, seq);
- case TERM_CMD_DECKBD:
- return screen_DECKBD(screen, seq);
- case TERM_CMD_DECKPAM:
- return screen_DECKPAM(screen, seq);
- case TERM_CMD_DECKPNM:
- return screen_DECKPNM(screen, seq);
- case TERM_CMD_DECLFKC:
- return screen_DECLFKC(screen, seq);
- case TERM_CMD_DECLL:
- return screen_DECLL(screen, seq);
- case TERM_CMD_DECLTOD:
- return screen_DECLTOD(screen, seq);
- case TERM_CMD_DECPCTERM:
- return screen_DECPCTERM(screen, seq);
- case TERM_CMD_DECPKA:
- return screen_DECPKA(screen, seq);
- case TERM_CMD_DECPKFMR:
- return screen_DECPKFMR(screen, seq);
- case TERM_CMD_DECRARA:
- return screen_DECRARA(screen, seq);
- case TERM_CMD_DECRC:
- return screen_DECRC(screen, seq);
- case TERM_CMD_DECREQTPARM:
- return screen_DECREQTPARM(screen, seq);
- case TERM_CMD_DECRPKT:
- return screen_DECRPKT(screen, seq);
- case TERM_CMD_DECRQCRA:
- return screen_DECRQCRA(screen, seq);
- case TERM_CMD_DECRQDE:
- return screen_DECRQDE(screen, seq);
- case TERM_CMD_DECRQKT:
- return screen_DECRQKT(screen, seq);
- case TERM_CMD_DECRQLP:
- return screen_DECRQLP(screen, seq);
- case TERM_CMD_DECRQM_ANSI:
- return screen_DECRQM_ANSI(screen, seq);
- case TERM_CMD_DECRQM_DEC:
- return screen_DECRQM_DEC(screen, seq);
- case TERM_CMD_DECRQPKFM:
- return screen_DECRQPKFM(screen, seq);
- case TERM_CMD_DECRQPSR:
- return screen_DECRQPSR(screen, seq);
- case TERM_CMD_DECRQTSR:
- return screen_DECRQTSR(screen, seq);
- case TERM_CMD_DECRQUPSS:
- return screen_DECRQUPSS(screen, seq);
- case TERM_CMD_DECSACE:
- return screen_DECSACE(screen, seq);
- case TERM_CMD_DECSASD:
- return screen_DECSASD(screen, seq);
- case TERM_CMD_DECSC:
- return screen_DECSC(screen, seq);
- case TERM_CMD_DECSCA:
- return screen_DECSCA(screen, seq);
- case TERM_CMD_DECSCL:
- return screen_DECSCL(screen, seq);
- case TERM_CMD_DECSCP:
- return screen_DECSCP(screen, seq);
- case TERM_CMD_DECSCPP:
- return screen_DECSCPP(screen, seq);
- case TERM_CMD_DECSCS:
- return screen_DECSCS(screen, seq);
- case TERM_CMD_DECSCUSR:
- return screen_DECSCUSR(screen, seq);
- case TERM_CMD_DECSDDT:
- return screen_DECSDDT(screen, seq);
- case TERM_CMD_DECSDPT:
- return screen_DECSDPT(screen, seq);
- case TERM_CMD_DECSED:
- return screen_DECSED(screen, seq);
- case TERM_CMD_DECSEL:
- return screen_DECSEL(screen, seq);
- case TERM_CMD_DECSERA:
- return screen_DECSERA(screen, seq);
- case TERM_CMD_DECSFC:
- return screen_DECSFC(screen, seq);
- case TERM_CMD_DECSKCV:
- return screen_DECSKCV(screen, seq);
- case TERM_CMD_DECSLCK:
- return screen_DECSLCK(screen, seq);
- case TERM_CMD_DECSLE:
- return screen_DECSLE(screen, seq);
- case TERM_CMD_DECSLPP:
- return screen_DECSLPP(screen, seq);
- case TERM_CMD_DECSLRM_OR_SC:
- return screen_DECSLRM_OR_SC(screen, seq);
- case TERM_CMD_DECSMBV:
- return screen_DECSMBV(screen, seq);
- case TERM_CMD_DECSMKR:
- return screen_DECSMKR(screen, seq);
- case TERM_CMD_DECSNLS:
- return screen_DECSNLS(screen, seq);
- case TERM_CMD_DECSPP:
- return screen_DECSPP(screen, seq);
- case TERM_CMD_DECSPPCS:
- return screen_DECSPPCS(screen, seq);
- case TERM_CMD_DECSPRTT:
- return screen_DECSPRTT(screen, seq);
- case TERM_CMD_DECSR:
- return screen_DECSR(screen, seq);
- case TERM_CMD_DECSRFR:
- return screen_DECSRFR(screen, seq);
- case TERM_CMD_DECSSCLS:
- return screen_DECSSCLS(screen, seq);
- case TERM_CMD_DECSSDT:
- return screen_DECSSDT(screen, seq);
- case TERM_CMD_DECSSL:
- return screen_DECSSL(screen, seq);
- case TERM_CMD_DECST8C:
- return screen_DECST8C(screen, seq);
- case TERM_CMD_DECSTBM:
- return screen_DECSTBM(screen, seq);
- case TERM_CMD_DECSTR:
- return screen_DECSTR(screen, seq);
- case TERM_CMD_DECSTRL:
- return screen_DECSTRL(screen, seq);
- case TERM_CMD_DECSWBV:
- return screen_DECSWBV(screen, seq);
- case TERM_CMD_DECSWL:
- return screen_DECSWL(screen, seq);
- case TERM_CMD_DECTID:
- return screen_DECTID(screen, seq);
- case TERM_CMD_DECTME:
- return screen_DECTME(screen, seq);
- case TERM_CMD_DECTST:
- return screen_DECTST(screen, seq);
- case TERM_CMD_DL:
- return screen_DL(screen, seq);
- case TERM_CMD_DSR_ANSI:
- return screen_DSR_ANSI(screen, seq);
- case TERM_CMD_DSR_DEC:
- return screen_DSR_DEC(screen, seq);
- case TERM_CMD_ECH:
- return screen_ECH(screen, seq);
- case TERM_CMD_ED:
- return screen_ED(screen, seq);
- case TERM_CMD_EL:
- return screen_EL(screen, seq);
- case TERM_CMD_ENQ:
- return screen_ENQ(screen, seq);
- case TERM_CMD_EPA:
- return screen_EPA(screen, seq);
- case TERM_CMD_FF:
- return screen_FF(screen, seq);
- case TERM_CMD_HPA:
- return screen_HPA(screen, seq);
- case TERM_CMD_HPR:
- return screen_HPR(screen, seq);
- case TERM_CMD_HT:
- return screen_HT(screen, seq);
- case TERM_CMD_HTS:
- return screen_HTS(screen, seq);
- case TERM_CMD_HVP:
- return screen_HVP(screen, seq);
- case TERM_CMD_ICH:
- return screen_ICH(screen, seq);
- case TERM_CMD_IL:
- return screen_IL(screen, seq);
- case TERM_CMD_IND:
- return screen_IND(screen, seq);
- case TERM_CMD_LF:
- return screen_LF(screen, seq);
- case TERM_CMD_LS1R:
- return screen_LS1R(screen, seq);
- case TERM_CMD_LS2:
- return screen_LS2(screen, seq);
- case TERM_CMD_LS2R:
- return screen_LS2R(screen, seq);
- case TERM_CMD_LS3:
- return screen_LS3(screen, seq);
- case TERM_CMD_LS3R:
- return screen_LS3R(screen, seq);
- case TERM_CMD_MC_ANSI:
- return screen_MC_ANSI(screen, seq);
- case TERM_CMD_MC_DEC:
- return screen_MC_DEC(screen, seq);
- case TERM_CMD_NEL:
- return screen_NEL(screen, seq);
- case TERM_CMD_NP:
- return screen_NP(screen, seq);
- case TERM_CMD_NULL:
- return screen_NULL(screen, seq);
- case TERM_CMD_PP:
- return screen_PP(screen, seq);
- case TERM_CMD_PPA:
- return screen_PPA(screen, seq);
- case TERM_CMD_PPB:
- return screen_PPB(screen, seq);
- case TERM_CMD_PPR:
- return screen_PPR(screen, seq);
- case TERM_CMD_RC:
- return screen_RC(screen, seq);
- case TERM_CMD_REP:
- return screen_REP(screen, seq);
- case TERM_CMD_RI:
- return screen_RI(screen, seq);
- case TERM_CMD_RIS:
- return screen_RIS(screen, seq);
- case TERM_CMD_RM_ANSI:
- return screen_RM_ANSI(screen, seq);
- case TERM_CMD_RM_DEC:
- return screen_RM_DEC(screen, seq);
- case TERM_CMD_S7C1T:
- return screen_S7C1T(screen, seq);
- case TERM_CMD_S8C1T:
- return screen_S8C1T(screen, seq);
- case TERM_CMD_SCS:
- return screen_SCS(screen, seq);
- case TERM_CMD_SD:
- return screen_SD(screen, seq);
- case TERM_CMD_SGR:
- return screen_SGR(screen, seq);
- case TERM_CMD_SI:
- return screen_SI(screen, seq);
- case TERM_CMD_SM_ANSI:
- return screen_SM_ANSI(screen, seq);
- case TERM_CMD_SM_DEC:
- return screen_SM_DEC(screen, seq);
- case TERM_CMD_SO:
- return screen_SO(screen, seq);
- case TERM_CMD_SPA:
- return screen_SPA(screen, seq);
- case TERM_CMD_SS2:
- return screen_SS2(screen, seq);
- case TERM_CMD_SS3:
- return screen_SS3(screen, seq);
- case TERM_CMD_ST:
- return screen_ST(screen, seq);
- case TERM_CMD_SU:
- return screen_SU(screen, seq);
- case TERM_CMD_SUB:
- return screen_SUB(screen, seq);
- case TERM_CMD_TBC:
- return screen_TBC(screen, seq);
- case TERM_CMD_VPA:
- return screen_VPA(screen, seq);
- case TERM_CMD_VPR:
- return screen_VPR(screen, seq);
- case TERM_CMD_VT:
- return screen_VT(screen, seq);
- case TERM_CMD_XTERM_CLLHP:
- return screen_XTERM_CLLHP(screen, seq);
- case TERM_CMD_XTERM_IHMT:
- return screen_XTERM_IHMT(screen, seq);
- case TERM_CMD_XTERM_MLHP:
- return screen_XTERM_MLHP(screen, seq);
- case TERM_CMD_XTERM_MUHP:
- return screen_XTERM_MUHP(screen, seq);
- case TERM_CMD_XTERM_RPM:
- return screen_XTERM_RPM(screen, seq);
- case TERM_CMD_XTERM_RRV:
- return screen_XTERM_RRV(screen, seq);
- case TERM_CMD_XTERM_RTM:
- return screen_XTERM_RTM(screen, seq);
- case TERM_CMD_XTERM_SACL1:
- return screen_XTERM_SACL1(screen, seq);
- case TERM_CMD_XTERM_SACL2:
- return screen_XTERM_SACL2(screen, seq);
- case TERM_CMD_XTERM_SACL3:
- return screen_XTERM_SACL3(screen, seq);
- case TERM_CMD_XTERM_SDCS:
- return screen_XTERM_SDCS(screen, seq);
- case TERM_CMD_XTERM_SGFX:
- return screen_XTERM_SGFX(screen, seq);
- case TERM_CMD_XTERM_SPM:
- return screen_XTERM_SPM(screen, seq);
- case TERM_CMD_XTERM_SRV:
- return screen_XTERM_SRV(screen, seq);
- case TERM_CMD_XTERM_STM:
- return screen_XTERM_STM(screen, seq);
- case TERM_CMD_XTERM_SUCS:
- return screen_XTERM_SUCS(screen, seq);
- case TERM_CMD_XTERM_WM:
- return screen_XTERM_WM(screen, seq);
- }
-
- return 0;
-}
-
-unsigned int term_screen_get_width(term_screen *screen) {
- assert_return(screen, -EINVAL);
-
- return screen->page->width;
-}
-
-unsigned int term_screen_get_height(term_screen *screen) {
- assert_return(screen, -EINVAL);
-
- return screen->page->height;
-}
-
-uint64_t term_screen_get_age(term_screen *screen) {
- assert_return(screen, 0);
-
- return screen->age;
-}
-
-int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size) {
- uint32_t *ucs4_str;
- size_t i, j, ucs4_len;
- const term_seq *seq;
- int r;
-
- assert_return(screen, -EINVAL);
-
- ++screen->age;
-
- /* Feed bytes into utf8 decoder and handle parsed ucs4 chars. We always
- * treat data as UTF-8, but the parser makes sure to fall back to raw
- * 8bit mode if the stream is not valid UTF-8. This should be more than
- * enough to support old 7bit/8bit modes. */
- for (i = 0; i < size; ++i) {
- ucs4_len = term_utf8_decode(&screen->utf8, &ucs4_str, in[i]);
- for (j = 0; j < ucs4_len; ++j) {
- r = term_parser_feed(screen->parser, &seq, ucs4_str[j]);
- if (r < 0) {
- return r;
- } else if (r != TERM_SEQ_NONE) {
- r = screen_feed_cmd(screen, seq);
- if (r < 0)
- return r;
- }
- }
- }
-
- return 0;
-}
-
-static char *screen_map_key(term_screen *screen,
- char *p,
- const uint32_t *keysyms,
- size_t n_syms,
- uint32_t ascii,
- const uint32_t *ucs4,
- unsigned int mods) {
- char ch, ch2, ch_mods;
- uint32_t v;
- size_t i;
-
- /* TODO: All these key-mappings need to be verified. Public information
- * on those mappings is pretty scarce and every emulator seems to do it
- * slightly differently.
- * A lot of mappings are also missing. */
-
- if (n_syms < 1)
- return p;
-
- if (n_syms == 1)
- v = keysyms[0];
- else
- v = XKB_KEY_NoSymbol;
-
- /* In some mappings, the modifiers are encoded as CSI parameters. The
- * encoding is rather arbitrary, but seems to work. */
- ch_mods = 0;
- switch (mods & (TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT | TERM_KBDMOD_CTRL)) {
- case TERM_KBDMOD_SHIFT:
- ch_mods = '2';
- break;
- case TERM_KBDMOD_ALT:
- ch_mods = '3';
- break;
- case TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
- ch_mods = '4';
- break;
- case TERM_KBDMOD_CTRL:
- ch_mods = '5';
- break;
- case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT:
- ch_mods = '6';
- break;
- case TERM_KBDMOD_CTRL | TERM_KBDMOD_ALT:
- ch_mods = '7';
- break;
- case TERM_KBDMOD_CTRL | TERM_KBDMOD_SHIFT | TERM_KBDMOD_ALT:
- ch_mods = '8';
- break;
- }
-
- /* A user might actually use multiple layouts for keyboard
- * input. @keysyms[0] contains the actual keysym that the user
- * used. But if this keysym is not in the ascii range, the
- * input handler does check all other layouts that the user
- * specified whether one of them maps the key to some ASCII
- * keysym and provides this via @ascii. We always use the real
- * keysym except when handling CTRL+<XY> shortcuts we use the
- * ascii keysym. This is for compatibility to xterm et. al. so
- * ctrl+c always works regardless of the currently active
- * keyboard layout. But if no ascii-sym is found, we still use
- * the real keysym. */
- if (ascii == XKB_KEY_NoSymbol)
- ascii = v;
-
- /* map CTRL+<ascii> */
- if (mods & TERM_KBDMOD_CTRL) {
- switch (ascii) {
- case 0x60 ... 0x7e:
- /* Right hand side is mapped to the left and then
- * treated equally. Fall through to left-hand side.. */
- ascii -= 0x20;
- case 0x20 ... 0x5f:
- /* Printable ASCII is mapped 1-1 in XKB and in
- * combination with CTRL bit 7 is flipped. This
- * is equivalent to the caret-notation. */
- *p++ = ascii ^ 0x40;
- return p;
- }
- }
-
- /* map cursor keys */
- ch = 0;
- switch (v) {
- case XKB_KEY_Up:
- ch = 'A';
- break;
- case XKB_KEY_Down:
- ch = 'B';
- break;
- case XKB_KEY_Right:
- ch = 'C';
- break;
- case XKB_KEY_Left:
- ch = 'D';
- break;
- case XKB_KEY_Home:
- ch = 'H';
- break;
- case XKB_KEY_End:
- ch = 'F';
- break;
- }
- if (ch) {
- *p++ = 0x1b;
- if (screen->flags & TERM_FLAG_CURSOR_KEYS)
- *p++ = 'O';
- else
- *p++ = '[';
- if (ch_mods) {
- *p++ = '1';
- *p++ = ';';
- *p++ = ch_mods;
- }
- *p++ = ch;
- return p;
- }
-
- /* map action keys */
- ch = 0;
- switch (v) {
- case XKB_KEY_Find:
- ch = '1';
- break;
- case XKB_KEY_Insert:
- ch = '2';
- break;
- case XKB_KEY_Delete:
- ch = '3';
- break;
- case XKB_KEY_Select:
- ch = '4';
- break;
- case XKB_KEY_Page_Up:
- ch = '5';
- break;
- case XKB_KEY_Page_Down:
- ch = '6';
- break;
- }
- if (ch) {
- *p++ = 0x1b;
- *p++ = '[';
- *p++ = ch;
- if (ch_mods) {
- *p++ = ';';
- *p++ = ch_mods;
- }
- *p++ = '~';
- return p;
- }
-
- /* map lower function keys */
- ch = 0;
- switch (v) {
- case XKB_KEY_F1:
- ch = 'P';
- break;
- case XKB_KEY_F2:
- ch = 'Q';
- break;
- case XKB_KEY_F3:
- ch = 'R';
- break;
- case XKB_KEY_F4:
- ch = 'S';
- break;
- }
- if (ch) {
- if (ch_mods) {
- *p++ = 0x1b;
- *p++ = '[';
- *p++ = '1';
- *p++ = ';';
- *p++ = ch_mods;
- *p++ = ch;
- } else {
- *p++ = 0x1b;
- *p++ = 'O';
- *p++ = ch;
- }
-
- return p;
- }
-
- /* map upper function keys */
- ch = 0;
- ch2 = 0;
- switch (v) {
- case XKB_KEY_F5:
- ch = '1';
- ch2 = '5';
- break;
- case XKB_KEY_F6:
- ch = '1';
- ch2 = '7';
- break;
- case XKB_KEY_F7:
- ch = '1';
- ch2 = '8';
- break;
- case XKB_KEY_F8:
- ch = '1';
- ch2 = '9';
- break;
- case XKB_KEY_F9:
- ch = '2';
- ch2 = '0';
- break;
- case XKB_KEY_F10:
- ch = '2';
- ch2 = '1';
- break;
- case XKB_KEY_F11:
- ch = '2';
- ch2 = '2';
- break;
- case XKB_KEY_F12:
- ch = '2';
- ch2 = '3';
- break;
- }
- if (ch) {
- *p++ = 0x1b;
- *p++ = '[';
- *p++ = ch;
- if (ch2)
- *p++ = ch2;
- if (ch_mods) {
- *p++ = ';';
- *p++ = ch_mods;
- }
- *p++ = '~';
- return p;
- }
-
- /* map special keys */
- switch (v) {
- case 0xff08: /* XKB_KEY_BackSpace */
- case 0xff09: /* XKB_KEY_Tab */
- case 0xff0a: /* XKB_KEY_Linefeed */
- case 0xff0b: /* XKB_KEY_Clear */
- case 0xff15: /* XKB_KEY_Sys_Req */
- case 0xff1b: /* XKB_KEY_Escape */
- case 0xffff: /* XKB_KEY_Delete */
- *p++ = v - 0xff00;
- return p;
- case 0xff13: /* XKB_KEY_Pause */
- /* TODO: What should we do with this key?
- * Sending XOFF is awful as there is no simple
- * way on modern keyboards to send XON again.
- * If someone wants this, we can re-eanble
- * optionally. */
- return p;
- case 0xff14: /* XKB_KEY_Scroll_Lock */
- /* TODO: What should we do on scroll-lock?
- * Sending 0x14 is what the specs say but it is
- * not used today the way most users would
- * expect so we disable it. If someone wants
- * this, we can re-enable it (optionally). */
- return p;
- case XKB_KEY_Return:
- *p++ = 0x0d;
- if (screen->flags & TERM_FLAG_NEWLINE_MODE)
- *p++ = 0x0a;
- return p;
- case XKB_KEY_ISO_Left_Tab:
- *p++ = 0x09;
- return p;
- }
-
- /* map unicode keys */
- for (i = 0; i < n_syms; ++i)
- p += utf8_encode_unichar(p, ucs4[i]);
-
- return p;
-}
-
-int term_screen_feed_keyboard(term_screen *screen,
- const uint32_t *keysyms,
- size_t n_syms,
- uint32_t ascii,
- const uint32_t *ucs4,
- unsigned int mods) {
- _cleanup_free_ char *dyn = NULL;
- static const size_t padding = 1;
- char buf[128], *start, *p;
-
- assert_return(screen, -EINVAL);
-
- /* allocate buffer if too small */
- start = buf;
- if (4 * n_syms + padding > sizeof(buf)) {
- dyn = malloc(4 * n_syms + padding);
- if (!dyn)
- return -ENOMEM;
-
- start = dyn;
- }
-
- /* reserve prefix space */
- start += padding;
- p = start;
-
- p = screen_map_key(screen, p, keysyms, n_syms, ascii, ucs4, mods);
- if (!p || p - start < 1)
- return 0;
-
- /* The ALT modifier causes ESC to be prepended to any key-stroke. We
- * already accounted for that buffer space above, so simply prepend it
- * here.
- * TODO: is altSendsEscape a suitable default? What are the semantics
- * exactly? Is it used in C0/C1 conversion? Is it prepended if there
- * already is an escape character? */
- if (mods & TERM_KBDMOD_ALT && *start != 0x1b)
- *--start = 0x1b;
-
- /* turn C0 into C1 */
- if (!(screen->flags & TERM_FLAG_7BIT_MODE) && p - start >= 2)
- if (start[0] == 0x1b && start[1] >= 0x40 && start[1] <= 0x5f)
- *++start ^= 0x40;
-
- return screen_write(screen, start, p - start);
-}
-
-int term_screen_resize(term_screen *screen, unsigned int x, unsigned int y) {
- unsigned int i;
- uint8_t *t;
- int r;
-
- assert_return(screen, -EINVAL);
-
- r = term_page_reserve(screen->page_main, x, y, &screen->state.attr, screen->age);
- if (r < 0)
- return r;
-
- r = term_page_reserve(screen->page_alt, x, y, &screen->state.attr, screen->age);
- if (r < 0)
- return r;
-
- if (x > screen->n_tabs) {
- t = realloc(screen->tabs, (x + 7) / 8);
- if (!t)
- return -ENOMEM;
-
- screen->tabs = t;
- screen->n_tabs = x;
- }
-
- for (i = (screen->page->width + 7) / 8 * 8; i < x; i += 8)
- screen->tabs[i / 8] = 0x1;
-
- term_page_resize(screen->page_main, x, y, &screen->state.attr, screen->age, screen->history);
- term_page_resize(screen->page_alt, x, y, &screen->state.attr, screen->age, NULL);
-
- screen->state.cursor_x = screen_clamp_x(screen, screen->state.cursor_x);
- screen->state.cursor_y = screen_clamp_x(screen, screen->state.cursor_y);
- screen_cursor_clear_wrap(screen);
-
- return 0;
-}
-
-void term_screen_soft_reset(term_screen *screen) {
- unsigned int i;
-
- assert(screen);
-
- screen->g0 = &term_unicode_lower;
- screen->g1 = &term_unicode_upper;
- screen->g2 = &term_unicode_lower;
- screen->g3 = &term_unicode_upper;
- screen->state.attr = screen->default_attr;
- screen->state.gl = &screen->g0;
- screen->state.gr = &screen->g1;
- screen->state.glt = NULL;
- screen->state.grt = NULL;
- screen->state.auto_wrap = 0;
- screen->state.origin_mode = 0;
-
- screen->saved = screen->state;
- screen->saved.cursor_x = 0;
- screen->saved.cursor_y = 0;
- screen->saved_alt = screen->saved;
-
- screen->page = screen->page_main;
- screen->history = screen->history_main;
- screen->flags = TERM_FLAG_7BIT_MODE;
- screen->conformance_level = TERM_CONFORMANCE_LEVEL_VT400;
-
- for (i = 0; i < screen->page->width; i += 8)
- screen->tabs[i / 8] = 0x1;
-
- term_page_set_scroll_region(screen->page_main, 0, screen->page->height);
- term_page_set_scroll_region(screen->page_alt, 0, screen->page->height);
-}
-
-void term_screen_hard_reset(term_screen *screen) {
- assert(screen);
-
- term_screen_soft_reset(screen);
- zero(screen->utf8);
- screen->state.cursor_x = 0;
- screen->state.cursor_y = 0;
- term_page_erase(screen->page_main, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
- term_page_erase(screen->page_alt, 0, 0, screen->page->width, screen->page->height, &screen->state.attr, screen->age, false);
-}
-
-int term_screen_set_answerback(term_screen *screen, const char *answerback) {
- char *t = NULL;
-
- assert_return(screen, -EINVAL);
-
- if (answerback) {
- t = strdup(answerback);
- if (!t)
- return -ENOMEM;
- }
-
- free(screen->answerback);
- screen->answerback = t;
-
- return 0;
-}
-
-int term_screen_draw(term_screen *screen,
- int (*draw_fn) (term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width),
- void *userdata,
- uint64_t *fb_age) {
- uint64_t cell_age, line_age, age = 0;
- term_charbuf_t ch_buf;
- const uint32_t *ch_str;
- unsigned int i, j, cw;
- term_page *page;
- term_line *line;
- term_cell *cell;
- size_t ch_n;
- int r;
-
- assert(screen);
- assert(draw_fn);
-
- if (fb_age)
- age = *fb_age;
-
- page = screen->page;
-
- for (j = 0; j < page->height; ++j) {
- line = page->lines[j];
- line_age = MAX(line->age, page->age);
-
- for (i = 0; i < page->width; ++i) {
- term_attr attr;
-
- cell = &line->cells[i];
- cell_age = MAX(cell->age, line_age);
-
- if (age != 0 && cell_age <= age)
- continue;
-
- ch_str = term_char_resolve(cell->ch, &ch_n, &ch_buf);
-
- /* Character-width of 0 is used for cleared cells.
- * Always treat this as single-cell character, so
- * renderers can assume ch_width is set properpy. */
- cw = MAX(cell->cwidth, 1U);
-
- attr = cell->attr;
- if (i == screen->state.cursor_x && j == screen->state.cursor_y &&
- !(screen->flags & TERM_FLAG_HIDE_CURSOR))
- attr.inverse ^= 1;
-
- r = draw_fn(screen,
- userdata,
- i,
- j,
- &attr,
- ch_str,
- ch_n,
- cw);
- if (r != 0)
- return r;
- }
- }
-
- if (fb_age)
- *fb_age = screen->age;
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/term-wcwidth.c b/src/libsystemd-terminal/term-wcwidth.c
deleted file mode 100644
index 833a099bd7..0000000000
--- a/src/libsystemd-terminal/term-wcwidth.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * (Minimal changes made by David Herrmann, to make clean for inclusion in
- * systemd. Original header follows.)
- *
- * This is an implementation of wcwidth() and wcswidth() (defined in
- * IEEE Std 1002.1-2001) for Unicode.
- *
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
- *
- * In fixed-width output devices, Latin characters all occupy a single
- * "cell" position of equal width, whereas ideographic CJK characters
- * occupy two such cells. Interoperability between terminal-line
- * applications and (teletype-style) character terminals using the
- * UTF-8 encoding requires agreement on which character should advance
- * the cursor by how many cell positions. No established formal
- * standards exist at present on which Unicode character shall occupy
- * how many cell positions on character terminals. These routines are
- * a first attempt of defining such behavior based on simple rules
- * applied to data provided by the Unicode Consortium.
- *
- * For some graphical characters, the Unicode standard explicitly
- * defines a character-cell width via the definition of the East Asian
- * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
- * In all these cases, there is no ambiguity about which width a
- * terminal shall use. For characters in the East Asian Ambiguous (A)
- * class, the width choice depends purely on a preference of backward
- * compatibility with either historic CJK or Western practice.
- * Choosing single-width for these characters is easy to justify as
- * the appropriate long-term solution, as the CJK practice of
- * displaying these characters as double-width comes from historic
- * implementation simplicity (8-bit encoded characters were displayed
- * single-width and 16-bit ones double-width, even for Greek,
- * Cyrillic, etc.) and not any typographic considerations.
- *
- * Much less clear is the choice of width for the Not East Asian
- * (Neutral) class. Existing practice does not dictate a width for any
- * of these characters. It would nevertheless make sense
- * typographically to allocate two character cells to characters such
- * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
- * represented adequately with a single-width glyph. The following
- * routines at present merely assign a single-cell width to all
- * neutral characters, in the interest of simplicity. This is not
- * entirely satisfactory and should be reconsidered before
- * establishing a formal standard in this area. At the moment, the
- * decision which Not East Asian (Neutral) characters should be
- * represented by double-width glyphs cannot yet be answered by
- * applying a simple rule from the Unicode database content. Setting
- * up a proper standard for the behavior of UTF-8 character terminals
- * will require a careful analysis not only of each Unicode character,
- * but also of each presentation form, something the author of these
- * routines has avoided to do so far.
- *
- * http://www.unicode.org/unicode/reports/tr11/
- *
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- *
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-
-#include "term-internal.h"
-
-struct interval {
- wchar_t first;
- wchar_t last;
-};
-
-/* auxiliary function for binary search in interval table */
-static int bisearch(wchar_t ucs, const struct interval *table, int max) {
- int min = 0;
- int mid;
-
- if (ucs < table[0].first || ucs > table[max].last)
- return 0;
- while (max >= min) {
- mid = (min + max) / 2;
- if (ucs > table[mid].last)
- min = mid + 1;
- else if (ucs < table[mid].first)
- max = mid - 1;
- else
- return 1;
- }
-
- return 0;
-}
-
-
-/* The following two functions define the column width of an ISO 10646
- * character as follows:
- *
- * - The null character (U+0000) has a column width of 0.
- *
- * - Other C0/C1 control characters and DEL will lead to a return
- * value of -1.
- *
- * - Non-spacing and enclosing combining characters (general
- * category code Mn or Me in the Unicode database) have a
- * column width of 0.
- *
- * - SOFT HYPHEN (U+00AD) has a column width of 1.
- *
- * - Other format characters (general category code Cf in the Unicode
- * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
- *
- * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
- * have a column width of 0.
- *
- * - Spacing characters in the East Asian Wide (W) or East Asian
- * Full-width (F) category as defined in Unicode Technical
- * Report #11 have a column width of 2.
- *
- * - All remaining characters (including all printable
- * ISO 8859-1 and WGL4 characters, Unicode control characters,
- * etc.) have a column width of 1.
- *
- * This implementation assumes that wchar_t characters are encoded
- * in ISO 10646.
- */
-
-int mk_wcwidth(wchar_t ucs)
-{
- /* sorted list of non-overlapping intervals of non-spacing characters */
- /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
- static const struct interval combining[] = {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
- { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
- { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
- { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
- { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
- { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
- { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
- { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
- { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
- { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
- { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
- { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
- { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
- { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
- { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
- { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
- { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
- { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
- { 0xE0100, 0xE01EF }
- };
-
- /* test for 8-bit control characters */
- if (ucs == 0)
- return 0;
- if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
- return -1;
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, combining,
- sizeof(combining) / sizeof(struct interval) - 1))
- return 0;
-
- /* if we arrive here, ucs is not a combining or C0/C1 control character */
-
- return 1 +
- (ucs >= 0x1100 &&
- (ucs <= 0x115f || /* Hangul Jamo init. consonants */
- ucs == 0x2329 || ucs == 0x232a ||
- (ucs >= 0x2e80 && ucs <= 0xa4cf &&
- ucs != 0x303f) || /* CJK ... Yi */
- (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
- (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
- (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
- (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
- (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
- (ucs >= 0xffe0 && ucs <= 0xffe6) ||
- (ucs >= 0x20000 && ucs <= 0x2fffd) ||
- (ucs >= 0x30000 && ucs <= 0x3fffd)));
-}
-
-
-int mk_wcswidth(const wchar_t *pwcs, size_t n)
-{
- int w, width = 0;
-
- for (;*pwcs && n-- > 0; pwcs++)
- if ((w = mk_wcwidth(*pwcs)) < 0)
- return -1;
- else
- width += w;
-
- return width;
-}
-
-
-/*
- * The following functions are the same as mk_wcwidth() and
- * mk_wcswidth(), except that spacing characters in the East Asian
- * Ambiguous (A) category as defined in Unicode Technical Report #11
- * have a column width of 2. This variant might be useful for users of
- * CJK legacy encodings who want to migrate to UCS without changing
- * the traditional terminal character-width behaviour. It is not
- * otherwise recommended for general use.
- */
-int mk_wcwidth_cjk(wchar_t ucs)
-{
- /* sorted list of non-overlapping intervals of East Asian Ambiguous
- * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
- static const struct interval ambiguous[] = {
- { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
- { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
- { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
- { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
- { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
- { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
- { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
- { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
- { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
- { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
- { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
- { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
- { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
- { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
- { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
- { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
- { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
- { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
- { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
- { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
- { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
- { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
- { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
- { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
- { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
- { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
- { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
- { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
- { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
- { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
- { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
- { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
- { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
- { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
- { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
- { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
- { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
- { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
- { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
- { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
- { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
- { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
- { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
- { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
- { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
- { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
- { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
- { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
- { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
- { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
- { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
- { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
- };
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, ambiguous,
- sizeof(ambiguous) / sizeof(struct interval) - 1))
- return 2;
-
- return mk_wcwidth(ucs);
-}
-
-
-int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
-{
- int w, width = 0;
-
- for (;*pwcs && n-- > 0; pwcs++)
- if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
- return -1;
- else
- width += w;
-
- return width;
-}
diff --git a/src/libsystemd-terminal/term.h b/src/libsystemd-terminal/term.h
deleted file mode 100644
index 1a78a81184..0000000000
--- a/src/libsystemd-terminal/term.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "util.h"
-
-typedef struct term_color term_color;
-typedef struct term_attr term_attr;
-
-typedef struct term_utf8 term_utf8;
-typedef struct term_seq term_seq;
-typedef struct term_parser term_parser;
-
-typedef struct term_screen term_screen;
-
-/*
- * Ageing
- */
-
-typedef uint64_t term_age_t;
-
-#define TERM_AGE_NULL 0
-
-/*
- * Attributes
- */
-
-enum {
- /* special color-codes */
- TERM_CCODE_DEFAULT, /* default foreground/background color */
- TERM_CCODE_256, /* 256color code */
- TERM_CCODE_RGB, /* color is specified as RGB */
-
- /* dark color-codes */
- TERM_CCODE_BLACK,
- TERM_CCODE_RED,
- TERM_CCODE_GREEN,
- TERM_CCODE_YELLOW,
- TERM_CCODE_BLUE,
- TERM_CCODE_MAGENTA,
- TERM_CCODE_CYAN,
- TERM_CCODE_WHITE, /* technically: light grey */
-
- /* light color-codes */
- TERM_CCODE_LIGHT_BLACK = TERM_CCODE_BLACK + 8, /* technically: dark grey */
- TERM_CCODE_LIGHT_RED = TERM_CCODE_RED + 8,
- TERM_CCODE_LIGHT_GREEN = TERM_CCODE_GREEN + 8,
- TERM_CCODE_LIGHT_YELLOW = TERM_CCODE_YELLOW + 8,
- TERM_CCODE_LIGHT_BLUE = TERM_CCODE_BLUE + 8,
- TERM_CCODE_LIGHT_MAGENTA = TERM_CCODE_MAGENTA + 8,
- TERM_CCODE_LIGHT_CYAN = TERM_CCODE_CYAN + 8,
- TERM_CCODE_LIGHT_WHITE = TERM_CCODE_WHITE + 8,
-
- TERM_CCODE_CNT,
-};
-
-struct term_color {
- uint8_t ccode;
- uint8_t c256;
- uint8_t red;
- uint8_t green;
- uint8_t blue;
-};
-
-struct term_attr {
- term_color fg; /* foreground color */
- term_color bg; /* background color */
-
- unsigned int bold : 1; /* bold font */
- unsigned int italic : 1; /* italic font */
- unsigned int underline : 1; /* underline text */
- unsigned int inverse : 1; /* inverse fg/bg */
- unsigned int protect : 1; /* protect from erase */
- unsigned int blink : 1; /* blink text */
- unsigned int hidden : 1; /* hidden */
-};
-
-void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette);
-
-/*
- * UTF-8
- */
-
-struct term_utf8 {
- uint32_t chars[5];
- uint32_t ucs4;
-
- unsigned int i_bytes : 3;
- unsigned int n_bytes : 3;
- unsigned int valid : 1;
-};
-
-size_t term_utf8_decode(term_utf8 *p, uint32_t **out_buf, char c);
-
-/*
- * Parsers
- */
-
-int term_parser_new(term_parser **out, bool host);
-term_parser *term_parser_free(term_parser *parser);
-int term_parser_feed(term_parser *parser, const term_seq **seq_out, uint32_t raw);
-
-#define _term_parser_free_ _cleanup_(term_parser_freep)
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_parser*, term_parser_free);
-
-/*
- * Screens
- */
-
-enum {
- TERM_KBDMOD_IDX_SHIFT,
- TERM_KBDMOD_IDX_CTRL,
- TERM_KBDMOD_IDX_ALT,
- TERM_KBDMOD_IDX_LINUX,
- TERM_KBDMOD_IDX_CAPS,
- TERM_KBDMOD_CNT,
-
- TERM_KBDMOD_SHIFT = 1 << TERM_KBDMOD_IDX_SHIFT,
- TERM_KBDMOD_CTRL = 1 << TERM_KBDMOD_IDX_CTRL,
- TERM_KBDMOD_ALT = 1 << TERM_KBDMOD_IDX_ALT,
- TERM_KBDMOD_LINUX = 1 << TERM_KBDMOD_IDX_LINUX,
- TERM_KBDMOD_CAPS = 1 << TERM_KBDMOD_IDX_CAPS,
-};
-
-typedef int (*term_screen_write_fn) (term_screen *screen, void *userdata, const void *buf, size_t size);
-typedef int (*term_screen_cmd_fn) (term_screen *screen, void *userdata, unsigned int cmd, const term_seq *seq);
-
-int term_screen_new(term_screen **out, term_screen_write_fn write_fn, void *write_fn_data, term_screen_cmd_fn cmd_fn, void *cmd_fn_data);
-term_screen *term_screen_ref(term_screen *screen);
-term_screen *term_screen_unref(term_screen *screen);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(term_screen*, term_screen_unref);
-
-unsigned int term_screen_get_width(term_screen *screen);
-unsigned int term_screen_get_height(term_screen *screen);
-uint64_t term_screen_get_age(term_screen *screen);
-
-int term_screen_feed_text(term_screen *screen, const uint8_t *in, size_t size);
-int term_screen_feed_keyboard(term_screen *screen,
- const uint32_t *keysyms,
- size_t n_syms,
- uint32_t ascii,
- const uint32_t *ucs4,
- unsigned int mods);
-int term_screen_resize(term_screen *screen, unsigned int width, unsigned int height);
-void term_screen_soft_reset(term_screen *screen);
-void term_screen_hard_reset(term_screen *screen);
-
-int term_screen_set_answerback(term_screen *screen, const char *answerback);
-
-int term_screen_draw(term_screen *screen,
- int (*draw_fn) (term_screen *screen,
- void *userdata,
- unsigned int x,
- unsigned int y,
- const term_attr *attr,
- const uint32_t *ch,
- size_t n_ch,
- unsigned int ch_width),
- void *userdata,
- uint64_t *fb_age);
diff --git a/src/libsystemd-terminal/test-term-page.c b/src/libsystemd-terminal/test-term-page.c
deleted file mode 100644
index d59139b62d..0000000000
--- a/src/libsystemd-terminal/test-term-page.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Terminal Page/Line/Cell/Char Tests
- * This tests internals of terminal page, line, cell and char handling. It
- * relies on some implementation details, so it might need to be updated if
- * those internals are changed. They should be fairly obvious, though.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "macro.h"
-#include "term-internal.h"
-
-#define MY_ASSERT_VALS __FILE__, __LINE__, __PRETTY_FUNCTION__
-#define MY_ASSERT_FORW _FILE, _LINE, _FUNC
-#define MY_ASSERT_ARGS const char *_FILE, int _LINE, const char *_FUNC
-#define MY_ASSERT(expr) \
- do { \
- if (_unlikely_(!(expr))) \
- log_assert_failed(#expr, _FILE, _LINE, _FUNC); \
- } while (false) \
-
-/*
- * Character Tests
- *
- * These tests rely on some implementation details of term_char_t, including
- * the way we pack characters and the internal layout of "term_char_t". These
- * tests have to be updated once we change the implementation.
- */
-
-#define PACK(v1, v2, v3) \
- TERM_CHAR_INIT( \
- (((((uint64_t)v1) & 0x1fffffULL) << 43) | \
- ((((uint64_t)v2) & 0x1fffffULL) << 22) | \
- ((((uint64_t)v3) & 0x1fffffULL) << 1) | \
- 0x1) \
- )
-#define PACK1(v1) PACK2((v1), 0x110000)
-#define PACK2(v1, v2) PACK3((v1), (v2), 0x110000)
-#define PACK3(v1, v2, v3) PACK((v1), (v2), (v3))
-
-static void test_term_char_misc(void) {
- term_char_t c, t;
-
- /* test TERM_CHAR_NULL handling */
-
- c = TERM_CHAR_NULL; /* c is NULL */
- assert_se(term_char_same(c, TERM_CHAR_NULL));
- assert_se(term_char_equal(c, TERM_CHAR_NULL));
- assert_se(term_char_is_null(c));
- assert_se(term_char_is_null(TERM_CHAR_NULL));
- assert_se(!term_char_is_allocated(c));
-
- /* test single char handling */
-
- t = term_char_dup_append(c, 'A'); /* t is >A< now */
- assert_se(!term_char_same(c, t));
- assert_se(!term_char_equal(c, t));
- assert_se(!term_char_is_allocated(t));
- assert_se(!term_char_is_null(t));
-
- /* test basic combined char handling */
-
- t = term_char_dup_append(t, '~');
- t = term_char_dup_append(t, '^'); /* t is >A~^< now */
- assert_se(!term_char_same(c, t));
- assert_se(!term_char_is_allocated(t));
- assert_se(!term_char_is_null(t));
-
- c = term_char_dup_append(c, 'A');
- c = term_char_dup_append(c, '~');
- c = term_char_dup_append(c, '^'); /* c is >A~^< now */
- assert_se(term_char_same(c, t));
- assert_se(term_char_equal(c, t));
-
- /* test more than 2 comb-chars so the chars are allocated */
-
- t = term_char_dup_append(t, '`'); /* t is >A~^`< now */
- c = term_char_dup_append(c, '`'); /* c is >A~^`< now */
- assert_se(!term_char_same(c, t));
- assert_se(term_char_equal(c, t));
-
- /* test dup_append() on allocated chars */
-
- term_char_free(t);
- t = term_char_dup_append(c, '"'); /* t is >A~^`"< now */
- assert_se(!term_char_same(c, t));
- assert_se(!term_char_equal(c, t));
- c = term_char_merge(c, '"'); /* c is >A~^`"< now */
- assert_se(!term_char_same(c, t));
- assert_se(term_char_equal(c, t));
-
- term_char_free(t);
- term_char_free(c);
-}
-
-static void test_term_char_packing(void) {
- uint32_t seqs[][1024] = {
- { -1 },
- { 0, -1 },
- { 'A', '~', -1 },
- { 'A', '~', 0, -1 },
- { 'A', '~', 'a', -1 },
- };
- term_char_t res[] = {
- TERM_CHAR_NULL,
- PACK1(0),
- PACK2('A', '~'),
- PACK3('A', '~', 0),
- PACK3('A', '~', 'a'),
- };
- uint32_t next;
- unsigned int i, j;
- term_char_t c = TERM_CHAR_NULL;
-
- /*
- * This creates term_char_t objects based on the data in @seqs and
- * compares the result to @res. Only basic packed types are tested, no
- * allocations are done.
- */
-
- for (i = 0; i < ELEMENTSOF(seqs); ++i) {
- for (j = 0; j < ELEMENTSOF(seqs[i]); ++j) {
- next = seqs[i][j];
- if (next == (uint32_t)-1)
- break;
-
- c = term_char_merge(c, next);
- }
-
- assert_se(!memcmp(&c, &res[i], sizeof(c)));
- c = term_char_free(c);
- }
-}
-
-static void test_term_char_allocating(void) {
- uint32_t seqs[][1024] = {
- { 0, -1 },
- { 'A', '~', -1 },
- { 'A', '~', 0, -1 },
- { 'A', '~', 'a', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', 0, '^', -1 },
- /* exceeding implementation-defined soft-limit of 64 */
- { 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', -1 },
- };
- term_char_t res[] = {
- PACK1(0),
- PACK2('A', '~'),
- PACK3('A', '~', 0),
- PACK3('A', '~', 'a'),
- TERM_CHAR_NULL, /* allocated */
- TERM_CHAR_NULL, /* allocated */
- TERM_CHAR_NULL, /* allocated */
- };
- uint32_t str[][1024] = {
- { 0, -1 },
- { 'A', '~', -1 },
- { 'A', '~', 0, -1 },
- { 'A', '~', 'a', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', -1 },
- { 'A', '~', 'a', 'b', 'c', 'd', 0, '^', -1 },
- { 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
- 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', -1 },
- };
- size_t n;
- uint32_t next;
- unsigned int i, j;
- const uint32_t *t;
-
- /*
- * This builds term_char_t objects based on the data in @seqs. It
- * compares the result to @res for packed chars, otherwise it requires
- * them to be allocated.
- * After that, we resolve the UCS-4 string and compare it to the
- * expected strings in @str.
- */
-
- for (i = 0; i < ELEMENTSOF(seqs); ++i) {
- _term_char_free_ term_char_t c = TERM_CHAR_NULL;
-
- for (j = 0; j < ELEMENTSOF(seqs[i]); ++j) {
- next = seqs[i][j];
- if (next == (uint32_t)-1)
- break;
-
- c = term_char_merge(c, next);
- }
-
- /* we use TERM_CHAR_NULL as marker for allocated chars here */
- if (term_char_is_null(res[i]))
- assert_se(term_char_is_allocated(c));
- else
- assert_se(!memcmp(&c, &res[i], sizeof(c)));
-
- t = term_char_resolve(c, &n, NULL);
- for (j = 0; j < ELEMENTSOF(str[i]); ++j) {
- next = str[i][j];
- if (next == (uint32_t)-1)
- break;
-
- assert_se(t[j] == next);
- }
-
- assert_se(n == j);
- }
-}
-
-/*
- * Line Tests
- *
- * The following tests work on term_line objects and verify their behavior when
- * we modify them. To verify and set line layouts, we have two simple helpers
- * to avoid harcoding the cell-verification all the time:
- * line_set(): Set a line to a given layout
- * line_assert(): Verify that a line has a given layout
- *
- * These functions take the line-layout encoded as a string and verify it
- * against, or set it on, a term_line object. The format used to describe a
- * line looks like this:
- * example: "| | A | | | | | | 10 *AB* |"
- *
- * The string describes the contents of all cells of a line, separated by
- * pipe-symbols ('|'). Whitespace are ignored, the leading pipe-symbol is
- * optional.
- * The description of each cell can contain an arbitrary amount of characters
- * in the range 'A'-'Z', 'a'-'z'. All those are combined and used as term_char_t
- * on this cell. Any numbers in the description are combined and are used as
- * cell-age.
- * The occurrence of a '*'-symbol marks the cell as bold, '/' marks it as italic.
- * You can use those characters multiple times, but only the first one has an
- * effect.
- * For further symbols, see parse_attr().
- *
- * Therefore, the following descriptions are equivalent:
- * 1) "| | /A* | | | | | | 10 *AB* |"
- * 2) "| | /A** | | | | | | 10 *AB* |"
- * 3) "| | A* // | | | | | | 10 *AB* |"
- * 4) "| | A* // | | | | | | 1 *AB* 0 |"
- * 5) "| | A* // | | | | | | A1B0* |"
- *
- * The parser isn't very strict about placement of alpha/numerical characters,
- * but simply appends all found chars. Don't make use of that feature! It's
- * just a stupid parser to simplify these tests. Make them readable!
- */
-
-static void parse_attr(char c, term_char_t *ch, term_attr *attr, term_age_t *age) {
- switch (c) {
- case ' ':
- /* ignore */
- break;
- case '0' ... '9':
- /* increase age */
- *age = *age * 10;
- *age = *age + c - '0';
- break;
- case 'A' ... 'Z':
- case 'a' ... 'z':
- /* add to character */
- *ch = term_char_merge(*ch, c);
- break;
- case '*':
- attr->bold = true;
- break;
- case '/':
- attr->italic = true;
- break;
- default:
- assert_se(0);
- break;
- }
-}
-
-static void cell_assert(MY_ASSERT_ARGS, term_cell *c, term_char_t ch, const term_attr *attr, term_age_t age) {
- MY_ASSERT(term_char_equal(c->ch, ch));
- MY_ASSERT(!memcmp(&c->attr, attr, sizeof(*attr)));
- MY_ASSERT(c->age == age);
-}
-#define CELL_ASSERT(_cell, _ch, _attr, _age) cell_assert(MY_ASSERT_VALS, (_cell), (_ch), (_attr), (_age))
-
-static void line_assert(MY_ASSERT_ARGS, term_line *l, const char *str, unsigned int fill) {
- unsigned int cell_i;
- term_char_t ch = TERM_CHAR_NULL;
- term_attr attr = { };
- term_age_t age = TERM_AGE_NULL;
- char c;
-
- assert_se(l->fill == fill);
-
- /* skip leading whitespace */
- while (*str == ' ')
- ++str;
-
- /* skip leading '|' */
- if (*str == '|')
- ++str;
-
- cell_i = 0;
- while ((c = *str++)) {
- switch (c) {
- case '|':
- /* end of cell-description; compare it */
- assert_se(cell_i < l->n_cells);
- cell_assert(MY_ASSERT_FORW,
- &l->cells[cell_i],
- ch,
- &attr,
- age);
-
- ++cell_i;
- ch = term_char_free(ch);
- zero(attr);
- age = TERM_AGE_NULL;
- break;
- default:
- parse_attr(c, &ch, &attr, &age);
- break;
- }
- }
-
- assert_se(cell_i == l->n_cells);
-}
-#define LINE_ASSERT(_line, _str, _fill) line_assert(MY_ASSERT_VALS, (_line), (_str), (_fill))
-
-static void line_set(term_line *l, unsigned int pos, const char *str, bool insert_mode) {
- term_char_t ch = TERM_CHAR_NULL;
- term_attr attr = { };
- term_age_t age = TERM_AGE_NULL;
- char c;
-
- while ((c = *str++))
- parse_attr(c, &ch, &attr, &age);
-
- term_line_write(l, pos, ch, 1, &attr, age, insert_mode);
-}
-
-static void line_resize(term_line *l, unsigned int width, const term_attr *attr, term_age_t age) {
- assert_se(term_line_reserve(l, width, attr, age, width) >= 0);
- term_line_set_width(l, width);
-}
-
-static void test_term_line_misc(void) {
- term_line *l;
-
- assert_se(term_line_new(&l) >= 0);
- assert_se(!term_line_free(l));
-
- assert_se(term_line_new(NULL) < 0);
- assert_se(!term_line_free(NULL));
-
- assert_se(term_line_new(&l) >= 0);
- assert_se(l->n_cells == 0);
- assert_se(l->fill == 0);
- assert_se(term_line_reserve(l, 16, NULL, 0, 0) >= 0);
- assert_se(l->n_cells == 16);
- assert_se(l->fill == 0);
- assert_se(term_line_reserve(l, 512, NULL, 0, 0) >= 0);
- assert_se(l->n_cells == 512);
- assert_se(l->fill == 0);
- assert_se(term_line_reserve(l, 16, NULL, 0, 0) >= 0);
- assert_se(l->n_cells == 512);
- assert_se(l->fill == 0);
- assert_se(!term_line_free(l));
-}
-
-static void test_term_line_ops(void) {
- term_line *l;
- term_attr attr_regular = { };
- term_attr attr_bold = { .bold = true };
- term_attr attr_italic = { .italic = true };
-
- assert_se(term_line_new(&l) >= 0);
- line_resize(l, 8, NULL, 0);
- assert_se(l->n_cells == 8);
-
- LINE_ASSERT(l, "| | | | | | | | |", 0);
-
- term_line_write(l, 4, TERM_CHAR_NULL, 0, NULL, TERM_AGE_NULL, 0);
- LINE_ASSERT(l, "| | | | | | | | |", 5);
-
- term_line_write(l, 1, PACK1('A'), 1, NULL, TERM_AGE_NULL, 0);
- LINE_ASSERT(l, "| |A| | | | | | |", 5);
-
- term_line_write(l, 8, PACK2('A', 'B'), 1, NULL, TERM_AGE_NULL, 0);
- LINE_ASSERT(l, "| |A| | | | | | |", 5);
-
- term_line_write(l, 7, PACK2('A', 'B'), 1, &attr_regular, 10, 0);
- LINE_ASSERT(l, "| |A| | | | | | 10 AB |", 8);
-
- term_line_write(l, 7, PACK2('A', 'B'), 1, &attr_bold, 10, 0);
- LINE_ASSERT(l, "| |A| | | | | | 10 *AB* |", 8);
-
- term_line_reset(l, NULL, TERM_AGE_NULL);
-
- LINE_ASSERT(l, "| | | | | | | | |", 0);
- line_set(l, 2, "*wxyz* 8", 0);
- line_set(l, 3, "/wxyz/ 8", 0);
- LINE_ASSERT(l, "| | | *wxyz* 8 | /wxyz/ 8 | | | | |", 4);
- line_set(l, 2, "*abc* 9", true);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 9 | 9 |", 5);
- line_set(l, 7, "*abc* 10", true);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 9 | *abc* 10 |", 8);
-
- term_line_erase(l, 6, 1, NULL, 11, 0);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 11 | *abc* 10 |", 8);
- term_line_erase(l, 6, 2, &attr_italic, 12, 0);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 12 // | 12 // |", 6);
- term_line_erase(l, 7, 2, &attr_regular, 13, 0);
- LINE_ASSERT(l, "| | | *abc* 9 | *wxyz* 9 | /wxyz/ 9 | 9 | 12 // | 13 |", 6);
- term_line_delete(l, 1, 3, &attr_bold, 14);
- LINE_ASSERT(l, "| | /wxyz/ 14 | 14 | 14 // | 14 | 14 ** | 14 ** | 14 ** |", 3);
- term_line_insert(l, 2, 2, &attr_regular, 15);
- LINE_ASSERT(l, "| | /wxyz/ 14 | 15 | 15 | 15 | 15 // | 15 | 15 ** |", 5);
-
- assert_se(!term_line_free(l));
-}
-
-int main(int argc, char *argv[]) {
- test_term_char_misc();
- test_term_char_packing();
- test_term_char_allocating();
-
- test_term_line_misc();
- test_term_line_ops();
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/test-term-parser.c b/src/libsystemd-terminal/test-term-parser.c
deleted file mode 100644
index e40b267b1c..0000000000
--- a/src/libsystemd-terminal/test-term-parser.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Terminal Parser Tests
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "macro.h"
-#include "term-internal.h"
-#include "utf8.h"
-
-static void test_term_utf8_invalid(void) {
- term_utf8 p = { };
- uint32_t *res;
- size_t len;
-
- len = term_utf8_decode(NULL, NULL, 0);
- assert_se(!len);
-
- len = term_utf8_decode(&p, NULL, 0);
- assert_se(len == 1);
-
- res = NULL;
- len = term_utf8_decode(NULL, &res, 0);
- assert_se(!len);
- assert_se(res != NULL);
- assert_se(!*res);
-
- len = term_utf8_decode(&p, &res, 0);
- assert_se(len == 1);
- assert_se(res != NULL);
- assert_se(!*res);
-
- len = term_utf8_decode(&p, &res, 0xCf);
- assert_se(len == 0);
- assert_se(res != NULL);
- assert_se(!*res);
-
- len = term_utf8_decode(&p, &res, 0);
- assert_se(len == 2);
- assert_se(res != NULL);
- assert_se(res[0] == 0xCf && res[1] == 0);
-}
-
-static void test_term_utf8_range(void) {
- term_utf8 p = { };
- uint32_t *res;
- char u8[4];
- uint32_t i, j;
- size_t ulen, len;
-
- /* Convert all ucs-4 chars to utf-8 and back */
-
- for (i = 0; i < 0x10FFFF; ++i) {
- ulen = utf8_encode_unichar(u8, i);
- if (!ulen)
- continue;
-
- for (j = 0; j < ulen; ++j) {
- len = term_utf8_decode(&p, &res, u8[j]);
- if (len < 1) {
- assert_se(j + 1 != ulen);
- continue;
- }
-
- assert_se(j + 1 == ulen);
- assert_se(len == 1 && *res == i);
- assert_se(i <= 127 || ulen >= 2);
- }
- }
-}
-
-static void test_term_utf8_mix(void) {
- static const char source[] = {
- 0x00, /* normal 0 */
- 0xC0, 0x80, /* overlong 0 */
- 0xC0, 0x81, /* overlong 1 */
- 0xE0, 0x80, 0x81, /* overlong 1 */
- 0xF0, 0x80, 0x80, 0x81, /* overlong 1 */
- 0xC0, 0x00, /* invalid continuation */
- 0xC0, 0xC0, 0x81, /* invalid continuation with a following overlong 1 */
- 0xF8, 0x80, 0x80, 0x80, 0x81, /* overlong 1 with 5 bytes */
- 0xE0, 0x80, 0xC0, 0x81, /* invalid 3-byte followed by valid 2-byte */
- 0xF0, 0x80, 0x80, 0xC0, 0x81, /* invalid 4-byte followed by valid 2-byte */
- };
- static const uint32_t result[] = {
- 0x0000,
- 0x0000,
- 0x0001,
- 0x0001,
- 0x0001,
- 0x00C0, 0x0000,
- 0x00C0, 0x0001,
- 0x00F8, 0x0080, 0x0080, 0x0080, 0x0081,
- 0x00E0, 0x0080, 0x0001,
- 0x00F0, 0x0080, 0x0080, 0x0001,
- };
- term_utf8 p = { };
- uint32_t *res;
- unsigned int i, j;
- size_t len;
-
- for (i = 0, j = 0; i < sizeof(source); ++i) {
- len = term_utf8_decode(&p, &res, source[i]);
- if (len < 1)
- continue;
-
- assert_se(j + len <= ELEMENTSOF(result));
- assert_se(!memcmp(res, &result[j], sizeof(uint32_t) * len));
- j += len;
- }
-
- assert_se(j == ELEMENTSOF(result));
-}
-
-int main(int argc, char *argv[]) {
- test_term_utf8_invalid();
- test_term_utf8_range();
- test_term_utf8_mix();
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/test-unifont.c b/src/libsystemd-terminal/test-unifont.c
deleted file mode 100644
index 2366d38574..0000000000
--- a/src/libsystemd-terminal/test-unifont.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Test Unifont Helper
- * This tries opening the binary unifont glyph-array and renders some glyphs.
- * The glyphs are then compared to hard-coded glyphs.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "macro.h"
-#include "unifont-def.h"
-#include "unifont.h"
-
-static void render(char *w, const unifont_glyph *g) {
- unsigned int i, j;
- const uint8_t *d = g->data;
-
- for (j = 0; j < 16; ++j) {
- for (i = 0; i < 8 * g->cwidth; ++i) {
- if (d[i / 8] & (1 << (7 - i % 8)))
- *w++ = '#';
- else
- *w++ = ' ';
- }
- *w++ = '\n';
- d += g->stride;
- }
-
- *w++ = 0;
-}
-
-static void test_unifont(void) {
- char buf[4096];
- unifont_glyph g;
- unifont *u;
-
- assert_se(unifont_new(&u) >= 0);
-
- /* lookup invalid font */
- assert_se(unifont_lookup(u, &g, 0xffffffffU) < 0);
-
- /* lookup and render 'A' */
- assert_se(unifont_lookup(u, &g, 'A') >= 0);
- assert_se(g.width == 8);
- assert_se(g.height == 16);
- assert_se(g.stride >= 1);
- assert_se(g.cwidth == 1);
- assert_se(g.data != NULL);
- render(buf, &g);
- assert_se(!strcmp(buf,
- " \n"
- " \n"
- " \n"
- " \n"
- " ## \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " ###### \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " \n"
- " \n"
- ));
-
- /* lookup and render '什' */
- assert_se(unifont_lookup(u, &g, 0x4ec0) >= 0);
- assert_se(g.width == 16);
- assert_se(g.height == 16);
- assert_se(g.stride >= 2);
- assert_se(g.cwidth == 2);
- assert_se(g.data != NULL);
- render(buf, &g);
- assert_se(!strcmp(buf,
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " ## # \n"
- " ## ########## \n"
- " # # # \n"
- "# # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- " # # \n"
- ));
-
- unifont_unref(u);
-}
-
-int main(int argc, char **argv) {
- if (access(UNIFONT_PATH, F_OK))
- return 77;
-
- test_unifont();
-
- return 0;
-}
diff --git a/src/libsystemd-terminal/unifont-def.h b/src/libsystemd-terminal/unifont-def.h
deleted file mode 100644
index 3847a2cf6c..0000000000
--- a/src/libsystemd-terminal/unifont-def.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "sparse-endian.h"
-#include "util.h"
-
-typedef struct unifont_header unifont_header;
-typedef struct unifont_glyph_header unifont_glyph_header;
-
-/*
- * Unifont: On-disk data
- * Conventional font-formats have the problem that you have to pre-render each
- * glyph before you can use it. If you just need one glyph, you have to parse
- * the font-file until you found that glyph.
- * GNU-Unifont is a bitmap font with very good Unicode coverage. All glyphs are
- * (n*8)x16 bitmaps. Our on-disk data stores all those glyphs pre-compiled with
- * fixed offsets. Therefore, the font-file can be mmap()ed and all glyphs can
- * be accessed in O(1) (because all glyphs have the same size and thus their
- * offsets can be easily computed). This guarantees, that the kernel only loads
- * the pages that are really accessed. Thus, we have a far lower overhead than
- * traditional font-formats like BDF. Furthermore, the backing file is read-only
- * and can be shared in memory between multiple users.
- *
- * The binary-format starts with a fixed header:
- *
- * | 2bytes | 2bytes | 2bytes | 2bytes |
- *
- * +-----------------------------------+
- * | SIGNATURE | 8 bytes
- * +-----------------+-----------------+
- * | COMPAT FLAGS | INCOMPAT FLAGS | 8 bytes
- * +-----------------+--------+--------+
- * | HEADER SIZE |GH-SIZE |G-STRIDE| 8 bytes
- * +-----------------+--------+--------+
- * | GLYPH BODY SIZE | 8 bytes
- * +-----------------------------------+
- *
- * * The 8 bytes signature must be set to the ASCII string "DVDHRMUF".
- * * The 4 bytes compatible-flags field contains flags for new features that
- * might be added in the future and which are compatible to older parsers.
- * * The 4 bytes incompatible-flags field contains flags for new features that
- * might be added in the future and which are incompatible to old parses.
- * Thus, if you encounter an unknown bit set, you must abort!
- * * The 4 bytes header-size field contains the size of the header in bytes. It
- * must be at least 32 (the size of this fixed header). If new features are
- * added, it might be increased. It can also be used to add padding to the
- * end of the header.
- * * The 2 bytes glyph-header-size field specifies the size of each glyph
- * header in bytes (see below).
- * * The 2 bytes glyph-stride field specifies the stride of each line of glyph
- * data in "bytes per line".
- * * The 8 byte glyph-body-size field defines the size of each glyph body in
- * bytes.
- *
- * After the header, the file can contain padding bytes, depending on the
- * header-size field. Everything beyond the header+padding is treated as a big
- * array of glyphs. Each glyph looks like this:
- *
- * | 1 byte |
- *
- * +-----------------------------------+
- * | WIDTH | 1 byte
- * +-----------------------------------+
- * ~ PADDING ~
- * +-----------------------------------+
- * ~ ~
- * ~ ~
- * ~ DATA ~
- * ~ ~
- * ~ ~
- * +-----------------------------------+
- *
- * * The first byte specifies the width of the glyph. If it is 0, the glyph
- * must be treated as non-existent.
- * All glyphs are "8*n" pixels wide and "16" pixels high. The width-field
- * specifies the width multiplier "n".
- * * After the width field padding might be added. This depends on the global
- * glyph-header-size field. It defines the total size of each glyph-header.
- * After the glyph-header+padding, the data-field starts.
- * * The data-field contains a byte-array of bitmap data. The array is always
- * as big as specified in the global glyph-body-size header field. This might
- * include padding.
- * The array contains all 16 lines of bitmap information for that glyph. The
- * stride is given in the global glyph-stride header field. This can be used
- * to add padding after each line.
- * Each line is encoded as 1 bit per pixel bitmap. That is, each byte encodes
- * data for 8 pixels (left most pixel is encoded in the LSB, right most pixel
- * in the MSB). The width field defines the number of bytes valid per line.
- * For width==1, you need 1 byte to encode the 8 pixels. The stride defines
- * where the encoding of the next line starts.
- * Any data beyond the 16th line is padding and must be ignored.
- */
-
-/* path to binary file */
-#define UNIFONT_PATH "/usr/share/systemd/unifont-glyph-array.bin"
-
-/* header-size of version 1 */
-#define UNIFONT_HEADER_SIZE_MIN 32
-
-struct unifont_header {
- /* fields available in version 1 */
- uint8_t signature[8];
- le32_t compatible_flags;
- le32_t incompatible_flags;
- le32_t header_size;
- le16_t glyph_header_size;
- le16_t glyph_stride;
- le64_t glyph_body_size;
-} _packed_;
-
-struct unifont_glyph_header {
- /* fields available in version 1 */
- uint8_t width;
-} _packed_;
diff --git a/src/libsystemd-terminal/unifont.c b/src/libsystemd-terminal/unifont.c
deleted file mode 100644
index 0da81e8ff2..0000000000
--- a/src/libsystemd-terminal/unifont.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/*
- * Unifont
- * This implements the unifont glyph-array parser and provides it via a simple
- * API to the caller. No heavy transformations are performed so glyph-lookups
- * stay as fast as possible.
- */
-
-#include <endian.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include "macro.h"
-#include "unifont-def.h"
-#include "unifont.h"
-#include "util.h"
-
-struct unifont {
- unsigned long ref;
-
- int fd;
- const uint8_t *map;
- size_t size;
-
- unifont_header header;
- const void *glyphs; /* unaligned! */
- size_t n_glyphs;
- size_t glyphsize;
-};
-
-static int unifont_fetch_header(unifont *u) {
- unifont_header h = { };
- uint64_t glyphsize;
-
- if (u->size < UNIFONT_HEADER_SIZE_MIN)
- return -EBFONT;
-
- assert_cc(sizeof(h) >= UNIFONT_HEADER_SIZE_MIN);
- memcpy(&h, u->map, UNIFONT_HEADER_SIZE_MIN);
-
- h.compatible_flags = le32toh(h.compatible_flags);
- h.incompatible_flags = le32toh(h.incompatible_flags);
- h.header_size = le32toh(h.header_size);
- h.glyph_header_size = le16toh(h.glyph_header_size);
- h.glyph_stride = le16toh(h.glyph_stride);
- h.glyph_body_size = le64toh(h.glyph_body_size);
-
- if (memcmp(h.signature, "DVDHRMUF", 8))
- return -EBFONT;
- if (h.incompatible_flags != 0)
- return -EBFONT;
- if (h.header_size < UNIFONT_HEADER_SIZE_MIN || h.header_size > u->size)
- return -EBFONT;
- if (h.glyph_header_size + h.glyph_body_size < h.glyph_header_size)
- return -EBFONT;
- if (h.glyph_stride * 16ULL > h.glyph_body_size)
- return -EBFONT;
-
- glyphsize = h.glyph_header_size + h.glyph_body_size;
-
- if (glyphsize == 0 || glyphsize > u->size - h.header_size) {
- u->n_glyphs = 0;
- } else {
- u->glyphs = u->map + h.header_size;
- u->n_glyphs = (u->size - h.header_size) / glyphsize;
- u->glyphsize = glyphsize;
- }
-
- memcpy(&u->header, &h, sizeof(h));
- return 0;
-}
-
-static int unifont_fetch_glyph(unifont *u, unifont_glyph_header *out_header, const void **out_body, uint32_t ucs4) {
- unifont_glyph_header glyph_header = { };
- const void *glyph_body = NULL;
- const uint8_t *p;
-
- if (ucs4 >= u->n_glyphs)
- return -ENOENT;
-
- p = u->glyphs;
-
- /* copy glyph-header data */
- p += ucs4 * u->glyphsize;
- memcpy(&glyph_header, p, MIN(sizeof(glyph_header), u->header.glyph_header_size));
-
- /* copy glyph-body pointer */
- p += u->header.glyph_header_size;
- glyph_body = p;
-
- if (glyph_header.width < 1)
- return -ENOENT;
- if (glyph_header.width > u->header.glyph_stride)
- return -EBFONT;
-
- memcpy(out_header, &glyph_header, sizeof(glyph_header));
- *out_body = glyph_body;
- return 0;
-}
-
-int unifont_new(unifont **out) {
- _cleanup_(unifont_unrefp) unifont *u = NULL;
- struct stat st;
- int r;
-
- assert_return(out, -EINVAL);
-
- u = new0(unifont, 1);
- if (!u)
- return -ENOMEM;
-
- u->ref = 1;
- u->fd = -1;
- u->map = MAP_FAILED;
-
- u->fd = open(UNIFONT_PATH, O_RDONLY | O_CLOEXEC | O_NOCTTY);
- if (u->fd < 0)
- return -errno;
-
- r = fstat(u->fd, &st);
- if (r < 0)
- return -errno;
-
- u->size = st.st_size;
- u->map = mmap(NULL, u->size, PROT_READ, MAP_PRIVATE, u->fd, 0);
- if (u->map == MAP_FAILED)
- return -errno;
-
- r = unifont_fetch_header(u);
- if (r < 0)
- return r;
-
- *out = u;
- u = NULL;
- return 0;
-}
-
-unifont *unifont_ref(unifont *u) {
- if (!u || !u->ref)
- return NULL;
-
- ++u->ref;
-
- return u;
-}
-
-unifont *unifont_unref(unifont *u) {
- if (!u || !u->ref || --u->ref)
- return NULL;
-
- if (u->map != MAP_FAILED)
- munmap((void*)u->map, u->size);
- u->fd = safe_close(u->fd);
- free(u);
-
- return NULL;
-}
-
-unsigned int unifont_get_width(unifont *u) {
- assert(u);
-
- return 8U;
-}
-
-unsigned int unifont_get_height(unifont *u) {
- assert(u);
-
- return 16U;
-}
-
-unsigned int unifont_get_stride(unifont *u) {
- assert(u);
-
- return u->header.glyph_stride;
-}
-
-int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4) {
- unifont_glyph_header h = { };
- const void *b = NULL;
- unifont_glyph g = { };
- int r;
-
- assert_return(u, -EINVAL);
-
- r = unifont_fetch_glyph(u, &h, &b, ucs4);
- if (r < 0)
- return r;
-
- g.width = h.width * 8U;
- g.height = 16U;
- g.stride = u->header.glyph_stride;
- g.cwidth = h.width;
- g.data = b;
-
- if (out)
- memcpy(out, &g, sizeof(g));
- return 0;
-}
-
-void unifont_fallback(unifont_glyph *out) {
- static const uint8_t fallback_data[] = {
- /* unifont 0xfffd '�' (unicode replacement character) */
- 0x00, 0x00, 0x00, 0x7e,
- 0x66, 0x5a, 0x5a, 0x7a,
- 0x76, 0x76, 0x7e, 0x76,
- 0x76, 0x7e, 0x00, 0x00,
- };
-
- assert(out);
-
- out->width = 8;
- out->height = 16;
- out->stride = 1;
- out->cwidth = 1;
- out->data = fallback_data;
-}
diff --git a/src/libsystemd-terminal/unifont.h b/src/libsystemd-terminal/unifont.h
deleted file mode 100644
index 74ee5ecb3c..0000000000
--- a/src/libsystemd-terminal/unifont.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <stdint.h>
-
-typedef struct unifont unifont;
-typedef struct unifont_glyph unifont_glyph;
-
-/*
- * Unifont
- * The unifont API provides a glyph-lookup for bitmap fonts which can be used
- * as fallback if no system-font is available or if you don't want to deal with
- * full font renderers.
- */
-
-struct unifont_glyph {
- unsigned int width;
- unsigned int height;
- unsigned int stride;
- unsigned int cwidth;
- const void *data; /* unaligned! */
-};
-
-int unifont_new(unifont **out);
-unifont *unifont_ref(unifont *u);
-unifont *unifont_unref(unifont *u);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(unifont*, unifont_unref);
-
-unsigned int unifont_get_width(unifont *u);
-unsigned int unifont_get_height(unifont *u);
-unsigned int unifont_get_stride(unifont *u);
-int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4);
-void unifont_fallback(unifont_glyph *out);
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index 0dbfbddcf6..f2092795f4 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -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"
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index a38c5c50fc..c53666ddd0 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -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;
}
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index e3fac01f92..6ac5ebc3da 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -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;
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 983e2f62cd..18685be8ff 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -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 {
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 2eaa7de306..c25293e5e9 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -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;
diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c
index c452477566..b149ea16da 100644
--- a/src/libsystemd/sd-bus/bus-slot.c
+++ b/src/libsystemd/sd-bus/bus-slot.c
@@ -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);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 322d57ddbb..735a775cb4 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -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);
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index a866a56179..59deaea89f 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -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);
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index 52952603e4..359984c7f3 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -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
index 0000000000..369c2f331c
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-proxy.c
@@ -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;
+}
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index b274f71093..7cea5a0746 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -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;
diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h
index 6f51ebe73d..4026e2c341 100644
--- a/src/libsystemd/sd-netlink/netlink-internal.h
+++ b/src/libsystemd/sd-netlink/netlink-internal.h
@@ -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 {
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 13573dcea8..b0ed2f2882 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -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
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 74ac2ab344..1e747abb24 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -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 },
/*
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index a210163241..758ffad1b7 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -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,
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 82654ee8c7..e6371ff04d 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -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),
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index fb5d076311..495ec50be0 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -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)
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 6a450b02a0..45f4c09d3d 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -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);
}
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index 4bf739a44d..b8565ebf51 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -70,7 +70,7 @@ struct Session {
Manager *manager;
const char *id;
- unsigned int pos;
+ unsigned int position;
SessionType type;
SessionClass class;
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index 0f72d70b10..36c0e8626d 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -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(
diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf
index 0ad78802dd..d8deb7bc8b 100644
--- a/src/login/org.freedesktop.login1.conf
+++ b/src/login/org.freedesktop.login1.conf
@@ -90,6 +90,42 @@
<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"/>
<allow send_destination="org.freedesktop.login1"
@@ -130,6 +166,14 @@
<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"/>
<allow send_destination="org.freedesktop.login1"
@@ -146,6 +190,10 @@
<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"/>
<allow send_destination="org.freedesktop.login1"
@@ -162,14 +210,30 @@
<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"/>
<allow send_destination="org.freedesktop.login1"
@@ -188,6 +252,14 @@
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>
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 7813a0bcc7..dc42ffdc52 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -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);
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 0e971a6789..3637815fc9 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -31,12 +31,13 @@
#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
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index 93aaf6a377..d58f01507b 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -113,6 +113,22 @@
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"/>
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index d446bfa8b3..4aa301b112 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -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;
}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5607cf470e..f20f68b482 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -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)
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 66ed2e013c..7e46293a06 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -29,6 +29,7 @@ NetDev.MTUBytes, config_parse_iec_size, 0,
NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id)
MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
@@ -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)
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c
index c2c564935c..e17de793ce 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/networkd-netdev-macvlan.c
@@ -35,14 +35,20 @@ DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
- MacVlan *m = MACVLAN(netdev);
+ MacVlan *m;
int r;
assert(netdev);
- assert(m);
assert(link);
assert(netdev->ifname);
+ if (netdev->kind == NETDEV_KIND_MACVLAN)
+ m = MACVLAN(netdev);
+ else
+ m = MACVTAP(netdev);
+
+ assert(m);
+
if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) {
r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode);
if (r < 0)
@@ -53,14 +59,28 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net
}
static void macvlan_init(NetDev *n) {
- MacVlan *m = MACVLAN(n);
+ MacVlan *m;
assert(n);
+
+ if (n->kind == NETDEV_KIND_MACVLAN)
+ m = MACVLAN(n);
+ else
+ m = MACVTAP(n);
+
assert(m);
m->mode = _NETDEV_MACVLAN_MODE_INVALID;
}
+const NetDevVTable macvtap_vtable = {
+ .object_size = sizeof(MacVlan),
+ .init = macvlan_init,
+ .sections = "Match\0NetDev\0MACVTAP\0",
+ .fill_message_create = netdev_macvlan_fill_message_create,
+ .create_type = NETDEV_CREATE_STACKED,
+};
+
const NetDevVTable macvlan_vtable = {
.object_size = sizeof(MacVlan),
.init = macvlan_init,
diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/networkd-netdev-macvlan.h
index d61efc16d4..c491bfa312 100644
--- a/src/network/networkd-netdev-macvlan.h
+++ b/src/network/networkd-netdev-macvlan.h
@@ -41,6 +41,7 @@ struct MacVlan {
};
extern const NetDevVTable macvlan_vtable;
+extern const NetDevVTable macvtap_vtable;
const char *macvlan_mode_to_string(MacVlanMode d) _const_;
MacVlanMode macvlan_mode_from_string(const char *d) _pure_;
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index 5533fb5c7b..7fd9ef584b 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -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 = {
diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h
index 88f57ac105..1fd2b94ae1 100644
--- a/src/network/networkd-netdev-tunnel.h
+++ b/src/network/networkd-netdev-tunnel.h
@@ -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);
diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c
index 378312f091..ba84e802fc 100644
--- a/src/network/networkd-netdev-tuntap.c
+++ b/src/network/networkd-netdev-tuntap.c
@@ -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;
diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/networkd-netdev-tuntap.h
index b804875bbb..29f8bb0ea5 100644
--- a/src/network/networkd-netdev-tuntap.h
+++ b/src/network/networkd-netdev-tuntap.h
@@ -33,6 +33,7 @@ struct TunTap {
bool one_queue;
bool multi_queue;
bool packet_info;
+ bool vnet_hdr;
};
extern const NetDevVTable tun_vtable;
diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h
index fe5254e91f..e7d1306f13 100644
--- a/src/network/networkd-netdev-vxlan.h
+++ b/src/network/networkd-netdev-vxlan.h
@@ -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);
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index 6949b403c8..cd31387b41 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -34,6 +34,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BOND] = &bond_vtable,
[NETDEV_KIND_VLAN] = &vlan_vtable,
[NETDEV_KIND_MACVLAN] = &macvlan_vtable,
+ [NETDEV_KIND_MACVTAP] = &macvtap_vtable,
[NETDEV_KIND_IPVLAN] = &ipvlan_vtable,
[NETDEV_KIND_VXLAN] = &vxlan_vtable,
[NETDEV_KIND_IPIP] = &ipip_vtable,
@@ -56,6 +57,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BOND] = "bond",
[NETDEV_KIND_VLAN] = "vlan",
[NETDEV_KIND_MACVLAN] = "macvlan",
+ [NETDEV_KIND_MACVTAP] = "macvtap",
[NETDEV_KIND_IPVLAN] = "ipvlan",
[NETDEV_KIND_VXLAN] = "vxlan",
[NETDEV_KIND_IPIP] = "ipip",
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index a004f2fe5f..19fb5bb185 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -40,6 +40,7 @@ typedef enum NetDevKind {
NETDEV_KIND_BOND,
NETDEV_KIND_VLAN,
NETDEV_KIND_MACVLAN,
+ NETDEV_KIND_MACVTAP,
NETDEV_KIND_IPVLAN,
NETDEV_KIND_VXLAN,
NETDEV_KIND_IPIP,
@@ -161,6 +162,7 @@ DEFINE_CAST(BRIDGE, Bridge);
DEFINE_CAST(BOND, Bond);
DEFINE_CAST(VLAN, VLan);
DEFINE_CAST(MACVLAN, MacVlan);
+DEFINE_CAST(MACVTAP, MacVlan);
DEFINE_CAST(IPVLAN, IPVlan);
DEFINE_CAST(VXLAN, VxLan);
DEFINE_CAST(IPIP, Tunnel);
diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c
index b5f8f5cfb2..5717a15327 100644
--- a/src/network/networkd-network-bus.c
+++ b/src/network/networkd-network-bus.c
@@ -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[] = {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 787fc2ff5b..8735b39581 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -31,6 +31,7 @@ Network.Bridge, config_parse_netdev, 0
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
Network.VLAN, config_parse_netdev, 0, 0
Network.MACVLAN, config_parse_netdev, 0, 0
+Network.MACVTAP, config_parse_netdev, 0, 0
Network.IPVLAN, config_parse_netdev, 0, 0
Network.VXLAN, config_parse_netdev, 0, 0
Network.Tunnel, config_parse_tunnel, 0, 0
@@ -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 */
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index a8e9ef909c..e3593fc0ea 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -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;
+}
diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c
index 32c31fdf3d..d958b48771 100644
--- a/src/network/networkd-wait-online.c
+++ b/src/network/networkd-wait-online.c
@@ -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) {
diff --git a/src/network/networkd.h b/src/network/networkd.h
index f98c640822..a285a4b08f 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -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);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 198de3097d..65b9a5071b 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -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");
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index f712033e6c..cdec83d074 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -28,9 +28,12 @@
#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;
+}
diff --git a/src/nss-mymachines/nss-mymachines.sym b/src/nss-mymachines/nss-mymachines.sym
index f80b51c1aa..0728ac3ba7 100644
--- a/src/nss-mymachines/nss-mymachines.sym
+++ b/src/nss-mymachines/nss-mymachines.sym
@@ -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
index 4124b7affd..0000000000
--- a/src/python-systemd/.gitignore
+++ /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
index d0b0e8e008..0000000000
--- a/src/python-systemd/Makefile
+++ /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
index 0d56b992f4..0000000000
--- a/src/python-systemd/__init__.py
+++ /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
index 7c5f1b2bb6..0000000000
--- a/src/python-systemd/_daemon.c
+++ /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
index 456e4a2796..0000000000
--- a/src/python-systemd/_journal.c
+++ /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
index 3a561269a7..0000000000
--- a/src/python-systemd/_reader.c
+++ /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
index 82011ca606..0000000000
--- a/src/python-systemd/daemon.py
+++ /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
index b06a965e6a..0000000000
--- a/src/python-systemd/docs/.gitignore
+++ /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
index 1919170bb1..0000000000
--- a/src/python-systemd/docs/conf.py
+++ /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
index 0ad11edaf3..0000000000
--- a/src/python-systemd/docs/daemon.rst
+++ /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
index 7c097d64a2..0000000000
--- a/src/python-systemd/docs/default.css
+++ /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
index 89c37f3470..0000000000
--- a/src/python-systemd/docs/id128.rst
+++ /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
index e78d966274..0000000000
--- a/src/python-systemd/docs/index.rst
+++ /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
index ea74cf85c4..0000000000
--- a/src/python-systemd/docs/journal.rst
+++ /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
index 930a6a7afe..0000000000
--- a/src/python-systemd/docs/layout.html
+++ /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
index 6b4de64c55..0000000000
--- a/src/python-systemd/docs/login.rst
+++ /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
index 5ec7309a54..0000000000
--- a/src/python-systemd/id128.c
+++ /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
index dd1f229973..0000000000
--- a/src/python-systemd/journal.py
+++ /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
index e844f5fc69..0000000000
--- a/src/python-systemd/login.c
+++ /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
index 722c4f5b5f..0000000000
--- a/src/python-systemd/pyutil.c
+++ /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
index 1477e7bf9c..0000000000
--- a/src/python-systemd/pyutil.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 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
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index f9448e3bc5..0edba415b6 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -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);
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index a3e740896f..63b4b36e88 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -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);
+}
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index 86951d233a..950af36ee3 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -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.
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index bb74b1828e..39951a362c 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -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);
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index c5867386c6..58559c85df 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -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,
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 4d71f5e3d4..0efe740d1a 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -188,6 +188,46 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
return 1;
}
+int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
+ unsigned j;
+ int r;
+
+ assert(a);
+ assert(k);
+
+ for (j = 0; j < a->n_keys; j++) {
+ r = dns_resource_key_equal(a->keys[j], k);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
+ unsigned j;
+ int r;
+
+ assert(a);
+ assert(b);
+
+ /* Checks if all keys in a are also contained b, and vice versa */
+
+ for (j = 0; j < a->n_keys; j++) {
+ r = dns_question_contains(b, a->keys[j]);
+ if (r <= 0)
+ return r;
+ }
+
+ for (j = 0; j < b->n_keys; j++) {
+ r = dns_question_contains(a, b->keys[j]);
+ if (r <= 0)
+ return r;
+ }
+
+ return 1;
+}
+
int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) {
_cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
bool same = true;
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 4ba2fe9f0e..fc98677798 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -43,6 +43,8 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr);
int dns_question_is_valid(DnsQuestion *q);
int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other);
+int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
+int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret);
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index c1818eef9c..ad7ca26cfe 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -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;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 26796c842b..0f40f3ceef 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -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 {
@@ -109,10 +110,19 @@ struct DnsResourceRecord {
} 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;
};
};
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index c25ac2216d..4bc4157028 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -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))
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index cfbde1343f..29479ad550 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -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);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 9a62a63258..92e48ae442 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -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;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 70ff35b08f..06059e8829 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -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;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 214938986d..8a93b265c6 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -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);
}
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index f6d539d315..a8f4267bc8 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -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;
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index ff8dc3a5bc..d66b3a88fc 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -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
index 0000000000..8afaf8db6e
--- /dev/null
+++ b/src/resolve/resolved-llmnr.c
@@ -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
index 0000000000..d489d481e8
--- /dev/null
+++ b/src/resolve/resolved-llmnr.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#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);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index dee5e61922..5be01d3cb8 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -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] = {
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 0f4ffad141..53b5acb33c 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -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);
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c
index 5a90c778fb..904dec6bfc 100644
--- a/src/rfkill/rfkill.c
+++ b/src/rfkill/rfkill.c
@@ -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;
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 20a44ce4e1..8a0dec1540 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -114,6 +114,68 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
return r;
}
+/* @label_terminal: terminal character of a label, updated to point to the terminal character of
+ * the previous label (always skipping one dot) or to NULL if there are no more
+ * labels. */
+int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
+ const char *terminal;
+ int r;
+
+ assert(name);
+ assert(label_terminal);
+ assert(dest);
+
+ /* no more labels */
+ if (!*label_terminal) {
+ if (sz >= 1)
+ *dest = 0;
+
+ return 0;
+ }
+
+ assert(**label_terminal == '.' || **label_terminal == 0);
+
+ /* skip current terminal character */
+ terminal = *label_terminal - 1;
+
+ /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
+ for (;;) {
+ if (terminal < name) {
+ /* reached the first label, so indicate that there are no more */
+ terminal = NULL;
+ break;
+ }
+
+ /* find the start of the last label */
+ if (*terminal == '.') {
+ const char *y;
+ unsigned slashes = 0;
+
+ for (y = terminal - 1; y >= name && *y == '\\'; y--)
+ slashes ++;
+
+ if (slashes % 2 == 0) {
+ /* the '.' was not escaped */
+ name = terminal + 1;
+ break;
+ } else {
+ terminal = y;
+ continue;
+ }
+ }
+
+ terminal --;
+ }
+
+ r = dns_label_unescape(&name, dest, sz);
+ if (r < 0)
+ return r;
+
+ *label_terminal = terminal;
+
+ return r;
+}
+
int dns_label_escape(const char *p, size_t l, char **ret) {
_cleanup_free_ char *s = NULL;
char *q;
@@ -338,20 +400,23 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
}
int dns_name_compare_func(const void *a, const void *b) {
- const char *x = a, *y = b;
+ const char *x, *y;
int r, q, k, w;
assert(a);
assert(b);
+ x = (const char *) a + strlen(a);
+ y = (const char *) b + strlen(b);
+
for (;;) {
char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
- if (*x == 0 && *y == 0)
+ if (x == NULL && y == NULL)
return 0;
- r = dns_label_unescape(&x, la, sizeof(la));
- q = dns_label_unescape(&y, lb, sizeof(lb));
+ r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
+ q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
if (r < 0 || q < 0)
return r - q;
@@ -464,6 +529,28 @@ int dns_name_endswith(const char *name, const char *suffix) {
}
}
+int dns_name_between(const char *a, const char *b, const char *c) {
+ int n;
+
+ /* Determine if b is strictly greater than a and strictly smaller than c.
+ We consider the order of names to be circular, so that if a is
+ strictly greater than c, we consider b to be between them if it is
+ either greater than a or smaller than c. This is how the canonical
+ DNS name order used in NSEC records work. */
+
+ n = dns_name_compare_func(a, c);
+ if (n == 0)
+ return -EINVAL;
+ else if (n < 0)
+ /* a<---b--->c */
+ return dns_name_compare_func(a, b) < 0 &&
+ dns_name_compare_func(b, c) < 0;
+ else
+ /* <--b--c a--b--> */
+ return dns_name_compare_func(b, c) < 0 ||
+ dns_name_compare_func(a, b) < 0;
+}
+
int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
const uint8_t *p;
int r;
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 00caf5d700..bd50ad3e6d 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -29,6 +29,7 @@
#define DNS_NAME_MAX 255
int dns_label_unescape(const char **name, char *dest, size_t sz);
+int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
int dns_label_escape(const char *p, size_t l, char **ret);
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
@@ -49,6 +50,7 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
int dns_name_compare_func(const void *a, const void *b);
extern const struct hash_ops dns_name_hash_ops;
+int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
int dns_name_endswith(const char *name, const char *suffix);
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
index 0d6ecf52cf..347cd30b09 100644
--- a/src/shared/efivars.c
+++ b/src/shared/efivars.c
@@ -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;
diff --git a/src/shared/install.c b/src/shared/install.c
index c37cf1948a..3d2b5ae77f 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -2190,6 +2190,7 @@ int unit_file_get_list(
_cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
struct dirent *de;
_cleanup_free_ char *path = NULL;
+ bool also = false;
errno = 0;
de = readdir(d);
@@ -2243,7 +2244,7 @@ int unit_file_get_list(
if (!path)
return -ENOMEM;
- r = unit_file_can_install(&paths, root_dir, path, true, NULL);
+ r = unit_file_can_install(&paths, root_dir, path, true, &also);
if (r == -EINVAL || /* Invalid setting? */
r == -EBADMSG || /* Invalid format? */
r == -ENOENT /* Included file not found? */)
@@ -2253,7 +2254,7 @@ int unit_file_get_list(
else if (r > 0)
f->state = UNIT_FILE_DISABLED;
else
- f->state = UNIT_FILE_STATIC;
+ f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC;
found:
r = hashmap_put(h, basename(f->path), f);
diff --git a/src/shared/nss-util.h b/src/shared/nss-util.h
index 230a986040..3657aa5d9c 100644
--- a/src/shared/nss-util.h
+++ b/src/shared/nss-util.h
@@ -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_
diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c
index 55f4e48601..1de0b94fd5 100644
--- a/src/shared/sysctl-util.c
+++ b/src/shared/sysctl-util.c
@@ -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) {
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index eee6bc8982..2b2310152d 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -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",
diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h
index a03221502a..bb4b1eb748 100644
--- a/src/systemd/sd-bus-vtable.h
+++ b/src/systemd/sd-bus-vtable.h
@@ -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) \
{ \
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index f34893171f..5439a1903b 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -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);
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index 4296b91d8a..5afa50a9d0 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -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
index 0000000000..96deeded7e
--- /dev/null
+++ b/src/test/test-bitmap.c
@@ -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;
+}
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index 838ffcba3d..e4771c9dd7 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -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");
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index b1385b8b87..b73c958ec5 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -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) {
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 527cdd3b54..0042722c99 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -50,6 +50,46 @@ static void test_dns_label_unescape(void) {
test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
}
+static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
+ char buffer[buffer_sz];
+ const char *label;
+ int r;
+
+ label = what + strlen(what);
+
+ r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
+ assert_se(r == ret1);
+ if (r >= 0)
+ assert_se(streq(buffer, expect1));
+
+ r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
+ assert_se(r == ret2);
+ if (r >= 0)
+ assert_se(streq(buffer, expect2));
+}
+
+static void test_dns_label_unescape_suffix(void) {
+ test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0);
+ test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC);
+ test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0);
+ test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0);
+ test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5);
+ test_dns_label_unescape_suffix_one("hallo.foobar\n", "foobar", "foobar", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo ", "", 20, 7, 0);
+ test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0);
+ test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0);
+ test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL);
+ test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6);
+ test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0);
+ test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3);
+ test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL);
+ test_dns_label_unescape_suffix_one("foo...bar", "bar", "", 20, 3, -EINVAL);
+ test_dns_label_unescape_suffix_one("foo\\.bar", "foo.bar", "", 20, 7, 0);
+ test_dns_label_unescape_suffix_one("foo\\\\.bar", "bar", "foo\\", 20, 3, 4);
+ test_dns_label_unescape_suffix_one("foo\\\\\\.bar", "foo\\.bar", "", 20, 8, 0);
+}
+
static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) {
_cleanup_free_ char *t = NULL;
int r;
@@ -120,6 +160,38 @@ static void test_dns_name_equal(void) {
test_dns_name_equal_one("..", "..", -EINVAL);
}
+static void test_dns_name_between_one(const char *a, const char *b, const char *c, int ret) {
+ int r;
+
+ r = dns_name_between(a, b, c);
+ assert_se(r == ret);
+
+ r = dns_name_between(c, b, a);
+ if (ret >= 0)
+ assert_se(r == 0);
+ else
+ assert_se(r == ret);
+}
+
+static void test_dns_name_between(void) {
+ /* see https://tools.ietf.org/html/rfc4034#section-6.1
+ Note that we use "\033.z.example" in stead of "\001.z.example" as we
+ consider the latter invalid */
+ test_dns_name_between_one("example", "a.example", "yljkjljk.a.example", true);
+ test_dns_name_between_one("a.example", "yljkjljk.a.example", "Z.a.example", true);
+ test_dns_name_between_one("yljkjljk.a.example", "Z.a.example", "zABC.a.EXAMPLE", true);
+ test_dns_name_between_one("Z.a.example", "zABC.a.EXAMPLE", "z.example", true);
+ test_dns_name_between_one("zABC.a.EXAMPLE", "z.example", "\\033.z.example", true);
+ test_dns_name_between_one("z.example", "\\033.z.example", "*.z.example", true);
+ test_dns_name_between_one("\\033.z.example", "*.z.example", "\\200.z.example", true);
+ test_dns_name_between_one("*.z.example", "\\200.z.example", "example", true);
+ test_dns_name_between_one("\\200.z.example", "example", "a.example", true);
+
+ test_dns_name_between_one("example", "a.example", "example", -EINVAL);
+ test_dns_name_between_one("example", "example", "yljkjljk.a.example", false);
+ test_dns_name_between_one("example", "yljkjljk.a.example", "yljkjljk.a.example", false);
+}
+
static void test_dns_name_endswith_one(const char *a, const char *b, int ret) {
assert_se(dns_name_endswith(a, b) == ret);
}
@@ -175,15 +247,19 @@ static void test_dns_name_reverse_one(const char *address, const char *name) {
static void test_dns_name_reverse(void) {
test_dns_name_reverse_one("47.11.8.15", "15.8.11.47.in-addr.arpa");
test_dns_name_reverse_one("fe80::47", "7.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa");
+ test_dns_name_reverse_one("127.0.0.1", "1.0.0.127.in-addr.arpa");
+ test_dns_name_reverse_one("::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa");
}
int main(int argc, char *argv[]) {
test_dns_label_unescape();
+ test_dns_label_unescape_suffix();
test_dns_label_escape();
test_dns_name_normalize();
test_dns_name_equal();
test_dns_name_endswith();
+ test_dns_name_between();
test_dns_name_root();
test_dns_name_single_label();
test_dns_name_reverse();
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index 4c31b776bd..be3a87958f 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -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");
diff --git a/src/test/test-util.c b/src/test/test-util.c
index ad9ea3bcce..f43433baa1 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -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();
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 42f757c4b7..271984b5a8 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -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;
}
diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
index 7ba0b7fc8f..c6a2c56e77 100644
--- a/src/udev/ata_id/ata_id.c
+++ b/src/udev/ata_id/ata_id.c
@@ -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 ||
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index b656204c46..72109d93d2 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -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;
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index fabc653800..4f625251d6 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -52,7 +52,7 @@ void udev_builtin_init(struct udev *udev) {
return;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i]->init)
+ if (builtins[i] && builtins[i]->init)
builtins[i]->init(udev);
initialized = true;
@@ -65,7 +65,7 @@ void udev_builtin_exit(struct udev *udev) {
return;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i]->exit)
+ if (builtins[i] && builtins[i]->exit)
builtins[i]->exit(udev);
initialized = false;
@@ -75,7 +75,7 @@ bool udev_builtin_validate(struct udev *udev) {
unsigned int i;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i]->validate && builtins[i]->validate(udev))
+ if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev))
return true;
return false;
}
@@ -84,14 +84,21 @@ void udev_builtin_list(struct udev *udev) {
unsigned int i;
for (i = 0; i < ELEMENTSOF(builtins); i++)
- fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
+ if (builtins[i])
+ fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
}
const char *udev_builtin_name(enum udev_builtin_cmd cmd) {
+ if (!builtins[cmd])
+ return NULL;
+
return builtins[cmd]->name;
}
bool udev_builtin_run_once(enum udev_builtin_cmd cmd) {
+ if (!builtins[cmd])
+ return false;
+
return builtins[cmd]->run_once;
}
@@ -105,7 +112,7 @@ enum udev_builtin_cmd udev_builtin_lookup(const char *command) {
if (pos)
pos[0] = '\0';
for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (streq(builtins[i]->name, name))
+ if (builtins[i] && streq(builtins[i]->name, name))
return i;
return UDEV_BUILTIN_MAX;
}
@@ -115,6 +122,9 @@ int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const c
int argc;
char *argv[128];
+ if (!builtins[cmd])
+ return -EOPNOTSUPP;
+
/* we need '0' here to reset the internal state */
optind = 0;
strscpy(arg, sizeof(arg), command);
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index e27fb1fd9e..d0b8bad48e 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -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,26 +1378,29 @@ 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;
}
static void help(void) {
@@ -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);
diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c
index 1c31769fde..e80a7771de 100644
--- a/src/user-sessions/user-sessions.c
+++ b/src/user-sessions/user-sessions.c
@@ -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;
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index f7728dcfff..7bdc158ad7 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -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;