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 return log_error_errno(r, "Cannot retrieve logind session: %m");
143 r = sd_session_get_seat(e->session, &e->seat);
145 return log_error_errno(r, "Cannot retrieve seat of logind session: %m");
147 e->managed = is_managed(e->session);
149 r = sd_event_default(&e->event);
153 r = sd_bus_open_system(&e->bus);
157 r = sd_bus_attach_event(e->bus, e->event, SD_EVENT_PRIORITY_NORMAL);
161 r = sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
165 r = sd_event_add_signal(e->event, NULL, SIGTERM, NULL, NULL);
169 r = sd_event_add_signal(e->event, NULL, SIGINT, NULL, NULL);
173 r = sysview_context_new(&e->sysview,
174 SYSVIEW_CONTEXT_SCAN_LOGIND |
175 SYSVIEW_CONTEXT_SCAN_EVDEV,
182 r = idev_context_new(&e->idev, e->event, e->bus);
191 static void kdata_print(idev_data *data) {
192 idev_data_keyboard *k = &data->keyboard;
197 /* Key-press state: UP/DOWN/REPEAT */
198 printf(" %-6s", k->value == 0 ? "UP" :
199 k->value == 1 ? "DOWN" :
203 printf(" | %-6s", data->resync ? "RESYNC" : "");
205 /* Keycode that triggered the event */
206 printf(" | %5u", (unsigned)k->keycode);
208 /* Well-known name of the keycode */
209 printf(" | %-20s", libevdev_event_code_get_name(EV_KEY, k->keycode) ? : "<unknown>");
211 /* Well-known modifiers */
212 printf(" | %-5s", (k->mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
213 printf(" %-4s", (k->mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
214 printf(" %-3s", (k->mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
215 printf(" %-5s", (k->mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
216 printf(" %-4s", (k->mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
218 /* Consumed modifiers */
219 printf(" | %-5s", (k->consumed_mods & IDEV_KBDMOD_SHIFT) ? "SHIFT" : "");
220 printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CTRL) ? "CTRL" : "");
221 printf(" %-3s", (k->consumed_mods & IDEV_KBDMOD_ALT) ? "ALT" : "");
222 printf(" %-5s", (k->consumed_mods & IDEV_KBDMOD_LINUX) ? "LINUX" : "");
223 printf(" %-4s", (k->consumed_mods & IDEV_KBDMOD_CAPS) ? "CAPS" : "");
225 /* Resolved symbols */
227 for (i = 0; i < k->n_syms; ++i) {
229 xkb_keysym_get_name(k->keysyms[i], buf, sizeof(buf));
231 if (is_locale_utf8()) {
232 c = k->codepoints[i];
233 if (c < 0x110000 && c > 0x20 && (c < 0x7f || c > 0x9f)) {
234 /* "%4lc" doesn't work well, so hard-code it */
235 cwidth = mk_wcwidth(c);
239 printf(" '%lc':", (wchar_t)c);
245 printf(" XKB_KEY_%-30s", buf);
251 static bool kdata_is_exit(idev_data *data) {
252 idev_data_keyboard *k = &data->keyboard;
259 return k->codepoints[0] == 'q';
262 static int evcat_idev_fn(idev_session *session, void *userdata, idev_event *ev) {
266 case IDEV_EVENT_DEVICE_ADD:
267 idev_device_enable(ev->device_add.device);
269 case IDEV_EVENT_DEVICE_REMOVE:
270 idev_device_disable(ev->device_remove.device);
272 case IDEV_EVENT_DEVICE_DATA:
273 switch (ev->device_data.data.type) {
274 case IDEV_DATA_KEYBOARD:
275 if (kdata_is_exit(&ev->device_data.data))
276 sd_event_exit(e->event, 0);
278 kdata_print(&ev->device_data.data);
289 static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
290 unsigned int flags, type;
297 case SYSVIEW_EVENT_SESSION_FILTER:
298 if (streq_ptr(e->session, ev->session_filter.id))
302 case SYSVIEW_EVENT_SESSION_ADD:
303 assert(!e->idev_session);
305 name = sysview_session_get_name(ev->session_add.session);
309 flags |= IDEV_SESSION_MANAGED;
311 r = idev_session_new(&e->idev_session,
318 return log_error_errno(r, "Cannot create idev session: %m");
321 r = sysview_session_take_control(ev->session_add.session);
323 return log_error_errno(r, "Cannot request session control: %m");
326 idev_session_enable(e->idev_session);
329 case SYSVIEW_EVENT_SESSION_REMOVE:
330 idev_session_disable(e->idev_session);
331 e->idev_session = idev_session_free(e->idev_session);
332 if (sd_event_get_exit_code(e->event, &r) == -ENODATA)
333 sd_event_exit(e->event, 0);
335 case SYSVIEW_EVENT_SESSION_ATTACH:
336 d = ev->session_attach.device;
337 type = sysview_device_get_type(d);
338 if (type == SYSVIEW_DEVICE_EVDEV) {
339 r = idev_session_add_evdev(e->idev_session, sysview_device_get_ud(d));
341 return log_error_errno(r, "Cannot add evdev device to idev: %m");
345 case SYSVIEW_EVENT_SESSION_DETACH:
346 d = ev->session_detach.device;
347 type = sysview_device_get_type(d);
348 if (type == SYSVIEW_DEVICE_EVDEV) {
349 r = idev_session_remove_evdev(e->idev_session, sysview_device_get_ud(d));
351 return log_error_errno(r, "Cannot remove evdev device from idev: %m");
355 case SYSVIEW_EVENT_SESSION_CONTROL:
356 r = ev->session_control.error;
358 return log_error_errno(r, "Cannot acquire session control: %m");
360 r = ioctl(1, KDSKBMODE, K_UNICODE);
362 return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m");
364 r = ioctl(1, KDSETMODE, KD_TEXT);
366 return log_error_errno(errno, "Cannot set KD_TEXT on stdout: %m");
376 static int evcat_run(Evcat *e) {
377 struct termios in_attr, saved_attr;
382 if (!e->managed && geteuid() > 0)
383 log_warning("You run in unmanaged mode without being root. This is likely to produce no output..");
385 printf("evcat - Read and catenate events from selected input devices\n"
386 " Running on seat '%s' in user-session '%s'\n"
387 " Exit by pressing ^C or 'q'\n\n",
388 e->seat ? : "seat0", e->session ? : "<none>");
390 r = sysview_context_start(e->sysview, evcat_sysview_fn, e);
394 r = tcgetattr(0, &in_attr);
400 saved_attr = in_attr;
401 in_attr.c_lflag &= ~ECHO;
403 r = tcsetattr(0, TCSANOW, &in_attr);
409 r = sd_event_loop(e->event);
410 tcsetattr(0, TCSANOW, &saved_attr);
411 printf("exiting..\n");
414 sysview_context_stop(e->sysview);
418 static int help(void) {
419 printf("%s [OPTIONS...]\n\n"
420 "Read and catenate events from selected input devices.\n\n"
421 " -h --help Show this help\n"
422 " --version Show package version\n"
423 , program_invocation_short_name);
428 static int parse_argv(int argc, char *argv[]) {
432 static const struct option options[] = {
433 { "help", no_argument, NULL, 'h' },
434 { "version", no_argument, NULL, ARG_VERSION },
442 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
449 puts(PACKAGE_STRING);
450 puts(SYSTEMD_FEATURES);
457 assert_not_reached("Unhandled option");
461 log_error("Too many arguments");
468 int main(int argc, char *argv[]) {
469 _cleanup_(evcat_freep) Evcat *e = NULL;
472 log_set_target(LOG_TARGET_AUTO);
473 log_parse_environment();
476 setlocale(LC_ALL, "");
477 if (!is_locale_utf8())
478 log_warning("Locale is not set to UTF-8. Codepoints will not be printed!");
480 r = parse_argv(argc, argv);
491 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;