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