1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 * The evcat tool catenates input events of all requested devices and prints
25 * them to standard-output. It's only meant for debugging of input-related
32 #include <libevdev/libevdev.h>
40 #include <sys/ioctl.h>
42 #include <systemd/sd-bus.h>
43 #include <systemd/sd-event.h>
44 #include <systemd/sd-login.h>
47 #include <xkbcommon/xkbcommon.h>
50 #include "event-util.h"
54 #include "term-internal.h"
57 typedef struct Evcat Evcat;
64 sysview_context *sysview;
66 idev_session *idev_session;
71 static Evcat *evcat_free(Evcat *e) {
75 e->idev_session = idev_session_free(e->idev_session);
76 e->idev = idev_context_unref(e->idev);
77 e->sysview = sysview_context_free(e->sysview);
78 e->bus = sd_bus_unref(e->bus);
79 e->event = sd_event_unref(e->event);
84 tcflush(0, TCIOFLUSH);
89 DEFINE_TRIVIAL_CLEANUP_FUNC(Evcat*, evcat_free);
91 static bool is_managed(const char *session) {
97 /* Using logind's Controller API is highly fragile if there is already
98 * a session controller running. If it is registered as controller
99 * itself, TakeControl will simply fail. But if its a legacy controller
100 * that does not use logind's controller API, we must never register
101 * our own controller. Otherwise, we really mess up the VT. Therefore,
102 * only run in managed mode if there's no-one else. */
113 r = sd_session_get_vt(session, &vtnr);
114 if (r < 0 || vtnr < 1 || vtnr > 63)
118 r = ioctl(1, KDGETMODE, &mode);
119 if (r < 0 || mode != KD_TEXT)
123 if (r < 0 || minor(st.st_rdev) != vtnr)
129 static int evcat_new(Evcat **out) {
130 _cleanup_(evcat_freep) Evcat *e = NULL;
139 r = sd_pid_get_session(getpid(), &e->session);
141 log_error("Cannot retrieve logind session: %s", strerror(-r));
145 r = sd_session_get_seat(e->session, &e->seat);
147 log_error("Cannot retrieve seat of logind session: %s", strerror(-r));
151 e->managed = is_managed(e->session);
153 r = sd_event_default(&e->event);
157 r = sd_bus_open_system(&e->bus);
161 r = sd_bus_attach_event(e->bus, e->event, SD_EVENT_PRIORITY_NORMAL);
165 r = sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
169 r = sd_event_add_signal(e->event, NULL, SIGTERM, NULL, NULL);
173 r = sd_event_add_signal(e->event, NULL, SIGINT, NULL, NULL);
177 r = sysview_context_new(&e->sysview,
178 SYSVIEW_CONTEXT_SCAN_LOGIND |
179 SYSVIEW_CONTEXT_SCAN_EVDEV,
186 r = idev_context_new(&e->idev, e->event, e->bus);
195 static void kdata_print(idev_data *data) {
196 idev_data_keyboard *k = &data->keyboard;
201 /* Key-press state: UP/DOWN/REPEAT */
202 printf(" %-6s", k->value == 0 ? "UP" :
203 k->value == 1 ? "DOWN" :
207 printf(" | %-6s", data->resync ? "RESYNC" : "");
209 /* Keycode that triggered the event */
210 printf(" | %5u", (unsigned)k->keycode);
212 /* Well-known name of the keycode */
213 printf(" | %-20s", libevdev_event_code_get_name(EV_KEY, k->keycode) ? : "<unknown>");
215 /* Well-known modifiers */
216 printf(" | %-5s", (k->mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
217 printf(" %-4s", (k->mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
218 printf(" %-3s", (k->mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
219 printf(" %-5s", (k->mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
220 printf(" %-4s", (k->mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
222 /* Resolved symbols */
224 for (i = 0; i < k->n_syms; ++i) {
226 xkb_keysym_get_name(k->keysyms[i], buf, sizeof(buf));
228 if (is_locale_utf8()) {
229 c = k->codepoints[i];
230 if (c < 0x110000 && c > 0x20 && (c < 0x7f || c > 0x9f)) {
231 /* "%4lc" doesn't work well, so hard-code it */
232 cwidth = mk_wcwidth(c);
236 printf(" '%lc':", (wchar_t)c);
242 printf(" XKB_KEY_%-30s", buf);
248 static bool kdata_is_exit(idev_data *data) {
249 idev_data_keyboard *k = &data->keyboard;
256 return k->codepoints[0] == 'q';
259 static int evcat_idev_fn(idev_session *session, void *userdata, idev_event *ev) {
263 case IDEV_EVENT_DEVICE_ADD:
264 idev_device_enable(ev->device_add.device);
266 case IDEV_EVENT_DEVICE_REMOVE:
267 idev_device_disable(ev->device_remove.device);
269 case IDEV_EVENT_DEVICE_DATA:
270 switch (ev->device_data.data.type) {
271 case IDEV_DATA_KEYBOARD:
272 if (kdata_is_exit(&ev->device_data.data))
273 sd_event_exit(e->event, 0);
275 kdata_print(&ev->device_data.data);
286 static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
287 unsigned int flags, type;
294 case SYSVIEW_EVENT_SESSION_FILTER:
295 if (streq_ptr(e->session, ev->session_filter.id))
299 case SYSVIEW_EVENT_SESSION_ADD:
300 assert(!e->idev_session);
302 name = sysview_session_get_name(ev->session_add.session);
306 flags |= IDEV_SESSION_MANAGED;
308 r = idev_session_new(&e->idev_session,
315 log_error("Cannot create idev session: %s", strerror(-r));
320 r = sysview_session_take_control(ev->session_add.session);
322 log_error("Cannot request session control: %s", strerror(-r));
327 idev_session_enable(e->idev_session);
330 case SYSVIEW_EVENT_SESSION_REMOVE:
331 idev_session_disable(e->idev_session);
332 e->idev_session = idev_session_free(e->idev_session);
333 if (sd_event_get_exit_code(e->event, &r) == -ENODATA)
334 sd_event_exit(e->event, 0);
336 case SYSVIEW_EVENT_SESSION_ATTACH:
337 d = ev->session_attach.device;
338 type = sysview_device_get_type(d);
339 if (type == SYSVIEW_DEVICE_EVDEV) {
340 r = idev_session_add_evdev(e->idev_session, sysview_device_get_ud(d));
342 log_error("Cannot add evdev device to idev: %s", strerror(-r));
348 case SYSVIEW_EVENT_SESSION_DETACH:
349 d = ev->session_detach.device;
350 type = sysview_device_get_type(d);
351 if (type == SYSVIEW_DEVICE_EVDEV) {
352 r = idev_session_remove_evdev(e->idev_session, sysview_device_get_ud(d));
354 log_error("Cannot remove evdev device from idev: %s", strerror(-r));
360 case SYSVIEW_EVENT_SESSION_CONTROL:
361 r = ev->session_control.error;
363 log_error("Cannot acquire session control: %s", strerror(-r));
367 r = ioctl(1, KDSKBMODE, K_UNICODE);
369 log_error("Cannot set K_UNICODE on stdout: %m");
373 r = ioctl(1, KDSETMODE, KD_TEXT);
375 log_error("Cannot set KD_TEXT on stdout: %m");
387 static int evcat_run(Evcat *e) {
388 struct termios in_attr, saved_attr;
393 if (!e->managed && geteuid() > 0)
394 log_warning("You run in unmanaged mode without being root. This is likely to produce no output..");
396 printf("evcat - Read and catenate events from selected input devices\n"
397 " Running on seat '%s' in user-session '%s'\n"
398 " Exit by pressing ^C or 'q'\n\n",
399 e->seat ? : "seat0", e->session ? : "<none>");
401 r = sysview_context_start(e->sysview, evcat_sysview_fn, e);
405 r = tcgetattr(0, &in_attr);
411 saved_attr = in_attr;
412 in_attr.c_lflag &= ~ECHO;
414 r = tcsetattr(0, TCSANOW, &in_attr);
420 r = sd_event_loop(e->event);
421 tcsetattr(0, TCSANOW, &saved_attr);
422 printf("exiting..\n");
425 sysview_context_stop(e->sysview);
429 static int help(void) {
430 printf("%s [OPTIONS...]\n\n"
431 "Read and catenate events from selected input devices.\n\n"
432 " -h --help Show this help\n"
433 " --version Show package version\n"
434 , program_invocation_short_name);
439 static int parse_argv(int argc, char *argv[]) {
443 static const struct option options[] = {
444 { "help", no_argument, NULL, 'h' },
445 { "version", no_argument, NULL, ARG_VERSION },
453 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
460 puts(PACKAGE_STRING);
461 puts(SYSTEMD_FEATURES);
468 assert_not_reached("Unhandled option");
472 log_error("Too many arguments");
479 int main(int argc, char *argv[]) {
480 _cleanup_(evcat_freep) Evcat *e = NULL;
483 log_set_target(LOG_TARGET_AUTO);
484 log_parse_environment();
487 setlocale(LC_ALL, "");
488 if (!is_locale_utf8())
489 log_warning("Locale is not set to UTF-8. Codepoints will not be printed!");
491 r = parse_argv(argc, argv);
502 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;