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