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
31 #include <libevdev/libevdev.h>
36 #include <sys/ioctl.h>
38 #include <systemd/sd-bus.h>
39 #include <systemd/sd-event.h>
40 #include <systemd/sd-login.h>
43 #include <xkbcommon/xkbcommon.h>
45 #include "event-util.h"
49 #include "term-internal.h"
52 typedef struct Evcat Evcat;
59 sysview_context *sysview;
61 idev_session *idev_session;
66 static Evcat *evcat_free(Evcat *e) {
70 e->idev_session = idev_session_free(e->idev_session);
71 e->idev = idev_context_unref(e->idev);
72 e->sysview = sysview_context_free(e->sysview);
73 e->bus = sd_bus_unref(e->bus);
74 e->event = sd_event_unref(e->event);
79 tcflush(0, TCIOFLUSH);
84 DEFINE_TRIVIAL_CLEANUP_FUNC(Evcat*, evcat_free);
86 static bool is_managed(const char *session) {
92 /* Using logind's Controller API is highly fragile if there is already
93 * a session controller running. If it is registered as controller
94 * itself, TakeControl will simply fail. But if its a legacy controller
95 * that does not use logind's controller API, we must never register
96 * our own controller. Otherwise, we really mess up the VT. Therefore,
97 * only run in managed mode if there's no-one else. */
108 r = sd_session_get_vt(session, &vtnr);
109 if (r < 0 || vtnr < 1 || vtnr > 63)
113 r = ioctl(1, KDGETMODE, &mode);
114 if (r < 0 || mode != KD_TEXT)
118 if (r < 0 || minor(st.st_rdev) != vtnr)
124 static int evcat_new(Evcat **out) {
125 _cleanup_(evcat_freep) Evcat *e = NULL;
134 r = sd_pid_get_session(getpid(), &e->session);
136 return log_error_errno(r, "Cannot retrieve logind session: %m");
138 r = sd_session_get_seat(e->session, &e->seat);
140 return log_error_errno(r, "Cannot retrieve seat of logind session: %m");
142 e->managed = is_managed(e->session);
144 r = sd_event_default(&e->event);
148 r = sd_bus_open_system(&e->bus);
152 r = sd_bus_attach_event(e->bus, e->event, SD_EVENT_PRIORITY_NORMAL);
156 r = sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
160 r = sd_event_add_signal(e->event, NULL, SIGTERM, NULL, NULL);
164 r = sd_event_add_signal(e->event, NULL, SIGINT, NULL, NULL);
168 r = sysview_context_new(&e->sysview,
169 SYSVIEW_CONTEXT_SCAN_LOGIND |
170 SYSVIEW_CONTEXT_SCAN_EVDEV,
177 r = idev_context_new(&e->idev, e->event, e->bus);
186 static void kdata_print(idev_data *data) {
187 idev_data_keyboard *k = &data->keyboard;
192 /* Key-press state: UP/DOWN/REPEAT */
193 printf(" %-6s", k->value == 0 ? "UP" :
194 k->value == 1 ? "DOWN" :
198 printf(" | %-6s", data->resync ? "RESYNC" : "");
200 /* Keycode that triggered the event */
201 printf(" | %5u", (unsigned)k->keycode);
203 /* Well-known name of the keycode */
204 printf(" | %-20s", libevdev_event_code_get_name(EV_KEY, k->keycode) ? : "<unknown>");
206 /* Well-known modifiers */
207 printf(" | %-5s", (k->mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
208 printf(" %-4s", (k->mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
209 printf(" %-3s", (k->mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
210 printf(" %-5s", (k->mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
211 printf(" %-4s", (k->mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
213 /* Consumed modifiers */
214 printf(" | %-5s", (k->consumed_mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
215 printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
216 printf(" %-3s", (k->consumed_mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
217 printf(" %-5s", (k->consumed_mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
218 printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
220 /* Resolved symbols */
222 for (i = 0; i < k->n_syms; ++i) {
224 xkb_keysym_get_name(k->keysyms[i], buf, sizeof(buf));
226 if (is_locale_utf8()) {
227 c = k->codepoints[i];
228 if (c < 0x110000 && c > 0x20 && (c < 0x7f || c > 0x9f)) {
229 /* "%4lc" doesn't work well, so hard-code it */
230 cwidth = mk_wcwidth(c);
234 printf(" '%lc':", (wchar_t)c);
240 printf(" XKB_KEY_%-30s", buf);
246 static bool kdata_is_exit(idev_data *data) {
247 idev_data_keyboard *k = &data->keyboard;
254 return k->codepoints[0] == 'q';
257 static int evcat_idev_fn(idev_session *session, void *userdata, idev_event *ev) {
261 case IDEV_EVENT_DEVICE_ADD:
262 idev_device_enable(ev->device_add.device);
264 case IDEV_EVENT_DEVICE_REMOVE:
265 idev_device_disable(ev->device_remove.device);
267 case IDEV_EVENT_DEVICE_DATA:
268 switch (ev->device_data.data.type) {
269 case IDEV_DATA_KEYBOARD:
270 if (kdata_is_exit(&ev->device_data.data))
271 sd_event_exit(e->event, 0);
273 kdata_print(&ev->device_data.data);
284 static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
285 unsigned int flags, type;
292 case SYSVIEW_EVENT_SESSION_FILTER:
293 if (streq_ptr(e->session, ev->session_filter.id))
297 case SYSVIEW_EVENT_SESSION_ADD:
298 assert(!e->idev_session);
300 name = sysview_session_get_name(ev->session_add.session);
304 flags |= IDEV_SESSION_MANAGED;
306 r = idev_session_new(&e->idev_session,
313 return log_error_errno(r, "Cannot create idev session: %m");
316 r = sysview_session_take_control(ev->session_add.session);
318 return log_error_errno(r, "Cannot request session control: %m");
321 idev_session_enable(e->idev_session);
324 case SYSVIEW_EVENT_SESSION_REMOVE:
325 idev_session_disable(e->idev_session);
326 e->idev_session = idev_session_free(e->idev_session);
327 if (sd_event_get_exit_code(e->event, &r) == -ENODATA)
328 sd_event_exit(e->event, 0);
330 case SYSVIEW_EVENT_SESSION_ATTACH:
331 d = ev->session_attach.device;
332 type = sysview_device_get_type(d);
333 if (type == SYSVIEW_DEVICE_EVDEV) {
334 r = idev_session_add_evdev(e->idev_session, sysview_device_get_ud(d));
336 return log_error_errno(r, "Cannot add evdev device to idev: %m");
340 case SYSVIEW_EVENT_SESSION_DETACH:
341 d = ev->session_detach.device;
342 type = sysview_device_get_type(d);
343 if (type == SYSVIEW_DEVICE_EVDEV) {
344 r = idev_session_remove_evdev(e->idev_session, sysview_device_get_ud(d));
346 return log_error_errno(r, "Cannot remove evdev device from idev: %m");
350 case SYSVIEW_EVENT_SESSION_CONTROL:
351 r = ev->session_control.error;
353 return log_error_errno(r, "Cannot acquire session control: %m");
355 r = ioctl(1, KDSKBMODE, K_UNICODE);
357 return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m");
359 r = ioctl(1, KDSETMODE, KD_TEXT);
361 return log_error_errno(errno, "Cannot set KD_TEXT on stdout: %m");
371 static int evcat_run(Evcat *e) {
372 struct termios in_attr, saved_attr;
377 if (!e->managed && geteuid() > 0)
378 log_warning("You run in unmanaged mode without being root. This is likely to produce no output..");
380 printf("evcat - Read and catenate events from selected input devices\n"
381 " Running on seat '%s' in user-session '%s'\n"
382 " Exit by pressing ^C or 'q'\n\n",
383 e->seat ? : "seat0", e->session ? : "<none>");
385 r = sysview_context_start(e->sysview, evcat_sysview_fn, e);
389 r = tcgetattr(0, &in_attr);
395 saved_attr = in_attr;
396 in_attr.c_lflag &= ~ECHO;
398 r = tcsetattr(0, TCSANOW, &in_attr);
404 r = sd_event_loop(e->event);
405 tcsetattr(0, TCSANOW, &saved_attr);
406 printf("exiting..\n");
409 sysview_context_stop(e->sysview);
413 static int help(void) {
414 printf("%s [OPTIONS...]\n\n"
415 "Read and catenate events from selected input devices.\n\n"
416 " -h --help Show this help\n"
417 " --version Show package version\n"
418 , program_invocation_short_name);
423 static int parse_argv(int argc, char *argv[]) {
427 static const struct option options[] = {
428 { "help", no_argument, NULL, 'h' },
429 { "version", no_argument, NULL, ARG_VERSION },
437 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
444 puts(PACKAGE_STRING);
445 puts(SYSTEMD_FEATURES);
452 assert_not_reached("Unhandled option");
456 log_error("Too many arguments");
463 int main(int argc, char *argv[]) {
464 _cleanup_(evcat_freep) Evcat *e = NULL;
467 log_set_target(LOG_TARGET_AUTO);
468 log_parse_environment();
471 setlocale(LC_ALL, "");
472 if (!is_locale_utf8())
473 log_warning("Locale is not set to UTF-8. Codepoints will not be printed!");
475 r = parse_argv(argc, argv);
486 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;