1/*
2 * Copyright © 2018 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include "config.h"
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <getopt.h>
30#include <sys/stat.h>
31
32#include "quirks.h"
33#include "shared.h"
34#include "builddir.h"
35
36static bool verbose = false;
37
38LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
39static void
40log_handler(struct libinput *this_is_null,
41	    enum libinput_log_priority priority,
42	    const char *format,
43	    va_list args)
44{
45	FILE *out = stdout;
46	enum quirks_log_priorities p = (enum quirks_log_priorities)priority;
47	char buf[256] = {0};
48	const char *prefix = NULL;
49
50	switch (p) {
51	case QLOG_NOISE:
52	case QLOG_DEBUG:
53		if (!verbose)
54			return;
55		prefix = "quirks debug";
56		break;
57	case QLOG_INFO:
58		prefix = "quirks info";
59		break;
60	case QLOG_ERROR:
61		out = stderr;
62		prefix = "quirks error";
63		break;
64	case QLOG_PARSER_ERROR:
65		out = stderr;
66		prefix = "quirks parser error";
67		break;
68	}
69
70	snprintf(buf, sizeof(buf), "%s: %s", prefix, format);
71#pragma GCC diagnostic push
72#pragma GCC diagnostic ignored "-Wformat-nonliteral"
73	vfprintf(out, buf, args);
74#pragma GCC diagnostic pop
75}
76
77static void
78usage(void)
79{
80	printf("Usage:\n"
81	       "  libinput quirks list [--data-dir /path/to/quirks/dir] /dev/input/event0\n"
82	       "	Print the quirks for the given device\n"
83	       "\n"
84	       "  libinput quirks validate [--data-dir /path/to/quirks/dir]\n"
85	       "	Validate the database\n");
86}
87
88static void
89simple_printf(void *userdata, const char *val)
90{
91	printf("%s\n", val);
92}
93
94int
95main(int argc, char **argv)
96{
97	struct udev *udev = NULL;
98	struct udev_device *device = NULL;
99	const char *path;
100	const char *data_path = NULL,
101	           *override_file = NULL;
102	int rc = 1;
103	struct quirks_context *quirks;
104	bool validate = false;
105
106	while (1) {
107		int c;
108		int option_index = 0;
109		enum {
110			OPT_VERBOSE,
111			OPT_DATADIR,
112		};
113		static struct option opts[] = {
114			{ "help",     no_argument,       0, 'h' },
115			{ "verbose",  no_argument,       0, OPT_VERBOSE },
116			{ "data-dir", required_argument, 0, OPT_DATADIR },
117			{ 0, 0, 0, 0}
118		};
119
120		c = getopt_long(argc, argv, "h", opts, &option_index);
121		if (c == -1)
122			break;
123
124		switch(c) {
125		case '?':
126			exit(1);
127			break;
128		case 'h':
129			usage();
130			exit(0);
131			break;
132		case OPT_VERBOSE:
133			verbose = true;
134			break;
135		case OPT_DATADIR:
136			data_path = optarg;
137			break;
138		default:
139			usage();
140			return 1;
141		}
142	}
143
144	if (optind >= argc) {
145		usage();
146		return 1;
147	}
148
149	if (streq(argv[optind], "list")) {
150		optind++;
151		if (optind >= argc) {
152			usage();
153			return 1;
154		}
155	} else if (streq(argv[optind], "validate")) {
156		optind++;
157		if (optind < argc) {
158			usage();
159			return 1;
160		}
161		validate = true;
162	} else {
163		fprintf(stderr, "Unnkown action '%s'\n", argv[optind]);
164		return 1;
165	}
166
167	/* Overriding the data dir means no custom override file */
168	if (!data_path) {
169		char *builddir = builddir_lookup();
170		if (builddir) {
171			data_path = LIBINPUT_QUIRKS_SRCDIR;
172			free(builddir);
173		} else {
174			data_path = LIBINPUT_QUIRKS_DIR;
175			override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
176		}
177	}
178
179	quirks = quirks_init_subsystem(data_path,
180				      override_file,
181				      log_handler,
182				      NULL,
183				      QLOG_CUSTOM_LOG_PRIORITIES);
184	if (!quirks) {
185		fprintf(stderr,
186			"Failed to initialize the device quirks. "
187			"Please see the above errors "
188			"and/or re-run with --verbose for more details\n");
189		return 1;
190	}
191
192	if (validate) {
193		rc = 0;
194		goto out;
195	}
196
197	udev = udev_new();
198	if (!udev)
199		goto out;
200
201	path = argv[optind];
202	if (strneq(path, "/sys/", 5)) {
203		device = udev_device_new_from_syspath(udev, path);
204	} else {
205		struct stat st;
206		if (stat(path, &st) < 0) {
207			fprintf(stderr, "Error: %s: %m\n", path);
208			goto out;
209		}
210
211		device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
212	}
213	if (device) {
214		tools_list_device_quirks(quirks, device, simple_printf, NULL);
215		rc = 0;
216	} else {
217		usage();
218		rc = 1;
219	}
220
221	udev_device_unref(device);
222out:
223	udev_unref(udev);
224
225	quirks_context_unref(quirks);
226
227	return rc;
228}
229