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 
36 static bool verbose = false;
37 
38 LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
39 static void
log_handler(struct libinput *this_is_null, enum libinput_log_priority priority, const char *format, va_list args)40 log_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 
77 static void
usage(void)78 usage(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 
88 static void
simple_printf(void *userdata, const char *val)89 simple_printf(void *userdata, const char *val)
90 {
91 	printf("%s\n", val);
92 }
93 
94 int
main(int argc, char **argv)95 main(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);
222 out:
223 	udev_unref(udev);
224 
225 	quirks_context_unref(quirks);
226 
227 	return rc;
228 }
229