1/*-
2 * Copyright (c) 2017 Netflix, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/param.h>
27#include <ctype.h>
28#include <devinfo.h>
29#include <err.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <getopt.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sysexits.h>
38#include <unistd.h>
39#include <sys/linker.h>
40#include <sys/module.h>
41#include <sys/stat.h>
42#include <sys/sysctl.h>
43
44/* options descriptor */
45static struct option longopts[] = {
46	{ "all",		no_argument,		NULL,	'a' },
47	{ "dump",		no_argument,		NULL,	'd' },
48	{ "hints",		required_argument,	NULL,	'h' },
49	{ "nomatch",		required_argument,	NULL,	'p' },
50	{ "quiet",		no_argument,		NULL,	'q' },
51	{ "unbound",		no_argument,		NULL,	'u' },
52	{ "verbose",		no_argument,		NULL,	'v' },
53	{ NULL,			0,			NULL,	0 }
54};
55
56#define	DEVMATCH_MAX_HITS 256
57
58static bool all_flag;
59static bool  dump_flag;
60static char *linker_hints;
61static char *nomatch_str;
62static bool quiet_flag;
63static bool unbound_flag;
64static bool verbose_flag;
65
66static void *hints;
67static void *hints_end;
68static struct devinfo_dev *root;
69
70static void *
71read_hints(const char *fn, size_t *len)
72{
73	void *h;
74	int fd;
75	struct stat sb;
76
77	fd = open(fn, O_RDONLY);
78	if (fd < 0) {
79		if (errno == ENOENT)
80			return NULL;
81		err(1, "Can't open %s for reading", fn);
82	}
83	if (fstat(fd, &sb) != 0)
84		err(1, "Can't fstat %s\n", fn);
85	h = malloc(sb.st_size);
86	if (h == NULL)
87		err(1, "not enough space to read hints file of %ju bytes", (uintmax_t)sb.st_size);
88	if (read(fd, h, sb.st_size) != sb.st_size)
89		err(1, "Can't read in %ju bytes from %s", (uintmax_t)sb.st_size, fn);
90	close(fd);
91	*len = sb.st_size;
92	return h;
93}
94
95static void
96read_linker_hints(void)
97{
98	char fn[MAXPATHLEN];
99	char *modpath, *p, *q;
100	size_t buflen, len;
101
102	if (linker_hints == NULL) {
103		if (sysctlbyname("kern.module_path", NULL, &buflen, NULL, 0) < 0)
104			errx(1, "Can't find kernel module path.");
105		modpath = malloc(buflen);
106		if (modpath == NULL)
107			err(1, "Can't get memory for modpath.");
108		if (sysctlbyname("kern.module_path", modpath, &buflen, NULL, 0) < 0)
109			errx(1, "Can't find kernel module path.");
110		p = modpath;
111		while ((q = strsep(&p, ";")) != NULL) {
112			snprintf(fn, sizeof(fn), "%s/linker.hints", q);
113			hints = read_hints(fn, &len);
114			if (hints == NULL)
115				continue;
116			break;
117		}
118		if (q == NULL) {
119			if (quiet_flag)
120				exit(EX_UNAVAILABLE);
121			else
122				errx(EX_UNAVAILABLE, "Can't read linker hints file.");
123		}
124	} else {
125		hints = read_hints(linker_hints, &len);
126		if (hints == NULL)
127			err(1, "Can't open %s for reading", fn);
128	}
129
130	if (*(int *)(intptr_t)hints != LINKER_HINTS_VERSION) {
131		warnx("Linker hints version %d doesn't match expected %d.",
132		    *(int *)(intptr_t)hints, LINKER_HINTS_VERSION);
133		free(hints);
134		hints = NULL;
135	}
136	if (hints != NULL)
137		hints_end = (void *)((intptr_t)hints + (intptr_t)len);
138}
139
140static int
141getint(void **ptr)
142{
143	int *p = *ptr;
144	int rv;
145
146	p = (int *)roundup2((intptr_t)p, sizeof(int));
147	rv = *p++;
148	*ptr = p;
149	return rv;
150}
151
152static void
153getstr(void **ptr, char *val)
154{
155	int *p = *ptr;
156	char *c = (char *)p;
157	int len = *(uint8_t *)c;
158
159	memcpy(val, c + 1, len);
160	val[len] = 0;
161	c += len + 1;
162	*ptr = (void *)c;
163}
164
165static int
166pnpval_as_int(const char *val, const char *pnpinfo)
167{
168	int rv;
169	char key[256];
170	char *cp;
171
172	if (pnpinfo == NULL)
173		return -1;
174
175	cp = strchr(val, ';');
176	key[0] = ' ';
177	if (cp == NULL)
178		strlcpy(key + 1, val, sizeof(key) - 1);
179	else {
180		memcpy(key + 1, val, cp - val);
181		key[cp - val + 1] = '\0';
182	}
183	strlcat(key, "=", sizeof(key));
184	if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0)
185		rv = strtol(pnpinfo + strlen(key + 1), NULL, 0);
186	else {
187		cp = strstr(pnpinfo, key);
188		if (cp == NULL)
189			rv = -1;
190		else
191			rv = strtol(cp + strlen(key), NULL, 0);
192	}
193	return rv;
194}
195
196static void
197quoted_strcpy(char *dst, const char *src)
198{
199	char q = ' ';
200
201	if (*src == '\'' || *src == '"')
202		q = *src++;
203	while (*src && *src != q)
204		*dst++ = *src++; // XXX backtick quoting
205	*dst++ = '\0';
206	// XXX overflow
207}
208
209static char *
210pnpval_as_str(const char *val, const char *pnpinfo)
211{
212	static char retval[256];
213	char key[256];
214	char *cp;
215
216	if (pnpinfo == NULL) {
217		*retval = '\0';
218		return retval;
219	}
220
221	cp = strchr(val, ';');
222	key[0] = ' ';
223	if (cp == NULL)
224		strlcpy(key + 1, val, sizeof(key) - 1);
225	else {
226		memcpy(key + 1, val, cp - val);
227		key[cp - val + 1] = '\0';
228	}
229	strlcat(key, "=", sizeof(key));
230	if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0)
231		quoted_strcpy(retval, pnpinfo + strlen(key + 1));
232	else {
233		cp = strstr(pnpinfo, key);
234		if (cp == NULL)
235			strcpy(retval, "MISSING");
236		else
237			quoted_strcpy(retval, cp + strlen(key));
238	}
239	return retval;
240}
241
242static void
243search_hints(const char *bus, const char *dev, const char *pnpinfo)
244{
245	char val1[256], val2[256];
246	int ival, len, ents, i, notme, mask, bit, v, found;
247	void *ptr, *walker;
248	char *lastmod = NULL, *cp, *s;
249
250	walker = hints;
251	getint(&walker);
252	found = 0;
253	if (verbose_flag)
254		printf("Searching bus %s dev %s for pnpinfo %s\n",
255		    bus, dev, pnpinfo);
256	while (walker < hints_end) {
257		len = getint(&walker);
258		ival = getint(&walker);
259		ptr = walker;
260		switch (ival) {
261		case MDT_VERSION:
262			getstr(&ptr, val1);
263			ival = getint(&ptr);
264			getstr(&ptr, val2);
265			if (dump_flag || verbose_flag)
266				printf("Version: if %s.%d kmod %s\n", val1, ival, val2);
267			break;
268		case MDT_MODULE:
269			getstr(&ptr, val1);
270			getstr(&ptr, val2);
271			if (lastmod)
272				free(lastmod);
273			lastmod = strdup(val2);
274			if (dump_flag || verbose_flag)
275				printf("Module %s in %s\n", val1, val2);
276			break;
277		case MDT_PNP_INFO:
278			if (!dump_flag && !unbound_flag && lastmod && strcmp(lastmod, "kernel") == 0)
279				break;
280			getstr(&ptr, val1);
281			getstr(&ptr, val2);
282			ents = getint(&ptr);
283			if (dump_flag || verbose_flag)
284				printf("PNP info for bus %s format %s %d entries (%s)\n",
285				    val1, val2, ents, lastmod);
286			if (strcmp(val1, "usb") == 0) {
287				if (verbose_flag)
288					printf("Treating usb as uhub -- bug in source table still?\n");
289				strcpy(val1, "uhub");
290			}
291			if (bus && strcmp(val1, bus) != 0) {
292				if (verbose_flag)
293					printf("Skipped because table for bus %s, looking for %s\n",
294					    val1, bus);
295				break;
296			}
297			for (i = 0; i < ents; i++) {
298				if (verbose_flag)
299					printf("---------- Entry %d ----------\n", i);
300				if (dump_flag)
301					printf("   ");
302				cp = val2;
303				notme = 0;
304				mask = -1;
305				bit = -1;
306				do {
307					switch (*cp) {
308						/* All integer fields */
309					case 'I':
310					case 'J':
311					case 'G':
312					case 'L':
313					case 'M':
314						ival = getint(&ptr);
315						if (dump_flag) {
316							printf("%#x:", ival);
317							break;
318						}
319						if (bit >= 0 && ((1 << bit) & mask) == 0)
320							break;
321						if (cp[2] == '#') {
322							if (verbose_flag) {
323								printf("Ignoring %s (%c) table=%#x tomatch=%#x\n",
324								    cp + 2, *cp, v, ival);
325							}
326							break;
327						}
328						v = pnpval_as_int(cp + 2, pnpinfo);
329						if (verbose_flag)
330							printf("Matching %s (%c) table=%#x tomatch=%#x\n",
331							    cp + 2, *cp, v, ival);
332						switch (*cp) {
333						case 'J':
334							if (ival == -1)
335								break;
336							/*FALLTHROUGH*/
337						case 'I':
338							if (v != ival)
339								notme++;
340							break;
341						case 'G':
342							if (v < ival)
343								notme++;
344							break;
345						case 'L':
346							if (v > ival)
347								notme++;
348							break;
349						case 'M':
350							mask = ival;
351							break;
352						}
353						break;
354						/* String fields */
355					case 'D':
356					case 'Z':
357						getstr(&ptr, val1);
358						if (dump_flag) {
359							printf("'%s':", val1);
360							break;
361						}
362						if (*cp == 'D')
363							break;
364						if (bit >= 0 && ((1 << bit) & mask) == 0)
365							break;
366						if (cp[2] == '#') {
367							if (verbose_flag) {
368								printf("Ignoring %s (%c) table=%#x tomatch=%#x\n",
369								    cp + 2, *cp, v, ival);
370							}
371							break;
372						}
373						s = pnpval_as_str(cp + 2, pnpinfo);
374						if (verbose_flag)
375							printf("Matching %s (%c) table=%s tomatch=%s\n",
376							    cp + 2, *cp, s, val1);
377						if (strcmp(s, val1) != 0)
378							notme++;
379						break;
380						/* Key override fields, required to be last in the string */
381					case 'T':
382						/*
383						 * This is imperfect and only does one key and will be redone
384						 * to be more general for multiple keys. Currently, nothing
385						 * does that.
386						 */
387						if (dump_flag)				/* No per-row data stored */
388							break;
389						if (cp[strlen(cp) - 1] == ';')		/* Skip required ; at end */
390							cp[strlen(cp) - 1] = '\0';	/* in case it's not there */
391						if ((s = strstr(pnpinfo, cp + 2)) == NULL)
392							notme++;
393						else if (s > pnpinfo && s[-1] != ' ')
394							notme++;
395						break;
396					default:
397						fprintf(stderr, "Unknown field type %c\n:", *cp);
398						break;
399					}
400					bit++;
401					cp = strchr(cp, ';');
402					if (cp)
403						cp++;
404				} while (cp && *cp);
405				if (dump_flag)
406					printf("\n");
407				else if (!notme) {
408					if (!unbound_flag) {
409						if (all_flag)
410							printf("%s: %s\n", *dev ? dev : "unattached", lastmod);
411						else
412							printf("%s\n", lastmod);
413						if (verbose_flag)
414							printf("Matches --- %s ---\n", lastmod);
415					}
416					found++;
417				}
418			}
419			break;
420		default:
421			if (dump_flag)
422				printf("Unknown Type %d len %d\n", ival, len);
423			break;
424		}
425		walker = (void *)(len - sizeof(int) + (intptr_t)walker);
426	}
427	if (unbound_flag && found == 0 && *pnpinfo) {
428		if (verbose_flag)
429			printf("------------------------- ");
430		printf("%s on %s pnpinfo %s", *dev ? dev : "unattached", bus, pnpinfo);
431		if (verbose_flag)
432			printf(" -------------------------");
433		printf("\n");
434	}
435	free(lastmod);
436}
437
438static int
439find_unmatched(struct devinfo_dev *dev, void *arg)
440{
441	struct devinfo_dev *parent;
442	char *bus, *p;
443
444	do {
445		if (!all_flag && dev->dd_name[0] != '\0')
446			break;
447		if (!(dev->dd_flags & DF_ENABLED))
448			break;
449		if (!all_flag && dev->dd_flags & DF_ATTACHED_ONCE)
450			break;
451		parent = devinfo_handle_to_device(dev->dd_parent);
452		bus = strdup(parent->dd_name);
453		p = bus + strlen(bus) - 1;
454		while (p >= bus && isdigit(*p))
455			p--;
456		*++p = '\0';
457		if (verbose_flag)
458			printf("Searching %s %s bus at %s for pnpinfo %s\n",
459			    dev->dd_name, bus, dev->dd_location, dev->dd_pnpinfo);
460		search_hints(bus, dev->dd_name, dev->dd_pnpinfo);
461		free(bus);
462	} while (0);
463
464	return (devinfo_foreach_device_child(dev, find_unmatched, arg));
465}
466
467struct exact_info
468{
469	const char *bus;
470	const char *loc;
471	struct devinfo_dev *dev;
472};
473
474/*
475 * Look for the exact location specified by the nomatch event.  The
476 * loc and pnpinfo run together to get the string we're looking for,
477 * so we have to synthesize the same thing that subr_bus.c is
478 * generating in devnomatch/devaddq to do the string comparison.
479 */
480static int
481find_exact_dev(struct devinfo_dev *dev, void *arg)
482{
483	struct devinfo_dev *parent;
484	char *loc;
485	struct exact_info *info;
486
487	info = arg;
488	do {
489		if (info->dev != NULL)
490			break;
491		if (!(dev->dd_flags & DF_ENABLED))
492			break;
493		parent = devinfo_handle_to_device(dev->dd_parent);
494		if (strcmp(info->bus, parent->dd_name) != 0)
495			break;
496		asprintf(&loc, "%s %s", parent->dd_pnpinfo,
497		    parent->dd_location);
498		if (strcmp(loc, info->loc) == 0)
499			info->dev = dev;
500		free(loc);
501	} while (0);
502
503	return (devinfo_foreach_device_child(dev, find_exact_dev, arg));
504}
505
506static void
507find_nomatch(char *nomatch)
508{
509	char *bus, *pnpinfo, *tmp, *busnameunit;
510	struct exact_info info;
511
512	/*
513	 * Find our bus name. It will include the unit number. We have to search
514	 * backwards to avoid false positive for any PNP string that has ' on '
515	 * in them, which would come earlier in the string. Like if there were
516	 * an 'Old Bard' ethernet card made by 'Stratford on Avon Hardware' or
517	 * something silly like that.
518	 */
519	tmp = nomatch + strlen(nomatch) - 4;
520	while (tmp > nomatch && strncmp(tmp, " on ", 4) != 0)
521		tmp--;
522	if (tmp == nomatch)
523		errx(1, "No bus found in nomatch string: '%s'", nomatch);
524	bus = tmp + 4;
525	*tmp = '\0';
526	busnameunit = strdup(bus);
527	if (busnameunit == NULL)
528		errx(1, "Can't allocate memory for strings");
529	tmp = bus + strlen(bus) - 1;
530	while (tmp > bus && isdigit(*tmp))
531		tmp--;
532	*++tmp = '\0';
533
534	/*
535	 * Note: the NOMATCH events place both the bus location as well as the
536	 * pnp info after the 'at' and we don't know where one stops and the
537	 * other begins, so we pass the whole thing to our search routine.
538	 */
539	if (*nomatch == '?')
540		nomatch++;
541	if (strncmp(nomatch, " at ", 4) != 0)
542		errx(1, "Malformed NOMATCH string: '%s'", nomatch);
543	pnpinfo = nomatch + 4;
544
545	/*
546	 * See if we can find the devinfo_dev for this device. If we
547	 * can, and it's been attached before, we should filter it out
548	 * so that a kldunload foo doesn't cause an immediate reload.
549	 */
550	info.loc = pnpinfo;
551	info.bus = busnameunit;
552	info.dev = NULL;
553	devinfo_foreach_device_child(root, find_exact_dev, (void *)&info);
554	if (info.dev != NULL && info.dev->dd_flags & DF_ATTACHED_ONCE)
555		exit(0);
556	search_hints(bus, "", pnpinfo);
557
558	exit(0);
559}
560
561static void
562usage(void)
563{
564
565	errx(1, "devmatch [-adv] [-p nomatch] [-h linker-hints]");
566}
567
568int
569main(int argc, char **argv)
570{
571	int ch;
572
573	while ((ch = getopt_long(argc, argv, "adh:p:quv",
574		    longopts, NULL)) != -1) {
575		switch (ch) {
576		case 'a':
577			all_flag = true;
578			break;
579		case 'd':
580			dump_flag = true;
581			break;
582		case 'h':
583			linker_hints = optarg;
584			break;
585		case 'p':
586			nomatch_str = optarg;
587			break;
588		case 'q':
589			quiet_flag = true;
590			break;
591		case 'u':
592			unbound_flag = true;
593			break;
594		case 'v':
595			verbose_flag = true;
596			break;
597		default:
598			usage();
599		}
600	}
601	argc -= optind;
602	argv += optind;
603
604	if (argc >= 1)
605		usage();
606
607	read_linker_hints();
608	if (dump_flag) {
609		search_hints(NULL, NULL, NULL);
610		exit(0);
611	}
612
613	if (devinfo_init())
614		err(1, "devinfo_init");
615	if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL)
616		errx(1, "can't find root device");
617	if (nomatch_str != NULL)
618		find_nomatch(nomatch_str);
619	else
620		devinfo_foreach_device_child(root, find_unmatched, (void *)0);
621	devinfo_free();
622}
623