xref: /netbsd-src/sbin/drvctl/drvctl.c (revision 33b82b03392e2106db8728db8eea7f5301157325)
1*33b82b03Srillig /* $NetBSD: drvctl.c,v 1.23 2024/11/03 10:43:26 rillig Exp $ */
20c34f6cdSdrochner 
30c34f6cdSdrochner /*
40c34f6cdSdrochner  * Copyright (c) 2004
50c34f6cdSdrochner  * 	Matthias Drochner.  All rights reserved.
60c34f6cdSdrochner  *
70c34f6cdSdrochner  * Redistribution and use in source and binary forms, with or without
80c34f6cdSdrochner  * modification, are permitted provided that the following conditions
90c34f6cdSdrochner  * are met:
100c34f6cdSdrochner  * 1. Redistributions of source code must retain the above copyright
110c34f6cdSdrochner  *    notice, this list of conditions, and the following disclaimer.
120c34f6cdSdrochner  * 2. Redistributions in binary form must reproduce the above copyright
130c34f6cdSdrochner  *    notice, this list of conditions and the following disclaimer in the
140c34f6cdSdrochner  *    documentation and/or other materials provided with the distribution.
150c34f6cdSdrochner  *
160c34f6cdSdrochner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170c34f6cdSdrochner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180c34f6cdSdrochner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190c34f6cdSdrochner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200c34f6cdSdrochner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210c34f6cdSdrochner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220c34f6cdSdrochner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230c34f6cdSdrochner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240c34f6cdSdrochner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250c34f6cdSdrochner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260c34f6cdSdrochner  * SUCH DAMAGE.
270c34f6cdSdrochner  */
280c34f6cdSdrochner 
293b8212e8Sjmcneill #include <inttypes.h>
300f6df043Sdyoung #include <stdbool.h>
310c34f6cdSdrochner #include <stdio.h>
320c34f6cdSdrochner #include <stdlib.h>
330c34f6cdSdrochner #include <unistd.h>
340c34f6cdSdrochner #include <err.h>
350c34f6cdSdrochner #include <fcntl.h>
360c34f6cdSdrochner #include <string.h>
370c34f6cdSdrochner #include <sys/ioctl.h>
380c34f6cdSdrochner #include <sys/drvctlio.h>
390c34f6cdSdrochner 
40ef731d9eSthorpej 
41ef731d9eSthorpej #define	OPEN_MODE(mode)							\
42ef731d9eSthorpej 	(((mode) == 'd' || (mode) == 'r') ? O_RDWR			\
43ef731d9eSthorpej 					  : O_RDONLY)
440c34f6cdSdrochner 
45baa8e84bSjoerg __dead static void usage(void);
46440a8860Spgoyette static void extract_property(prop_dictionary_t, const char *, bool);
47440a8860Spgoyette static void display_object(prop_object_t, bool);
48f398b121Sjmcneill static void list_children(int, char *, bool, bool, int);
490c34f6cdSdrochner 
500c34f6cdSdrochner static void
517b1148bcSxtraeme usage(void)
520c34f6cdSdrochner {
530cc54e49Schristos 	const char *p = getprogname();
540c34f6cdSdrochner 
55702f9dcaSwiz 	fprintf(stderr, "Usage: %s -r [-a attribute] busdevice [locator ...]\n"
56ef731d9eSthorpej 	    "       %s -d device\n"
57f398b121Sjmcneill 	    "       %s [-nt] -l [device]\n"
581aa8a761Spgoyette 	    "       %s [-n] -p device [property]\n"
59ae1c071dSdyoung 	    "       %s -Q device\n"
60ae1c071dSdyoung 	    "       %s -R device\n"
610cc54e49Schristos 	    "       %s -S device\n", p, p, p, p, p, p, p);
620cc54e49Schristos 	exit(EXIT_FAILURE);
630c34f6cdSdrochner }
640c34f6cdSdrochner 
650c34f6cdSdrochner int
660c34f6cdSdrochner main(int argc, char **argv)
670c34f6cdSdrochner {
68f398b121Sjmcneill 	bool nflag = false, tflag = false;
690c34f6cdSdrochner 	int c, mode;
700c34f6cdSdrochner 	char *attr = 0;
710c34f6cdSdrochner 	int fd, res;
72ae1c071dSdyoung 	struct devpmargs paa = { .devname = "", .flags = 0 };
730c34f6cdSdrochner 	struct devdetachargs daa;
740c34f6cdSdrochner 	struct devrescanargs raa;
750c34f6cdSdrochner 	int *locs, i;
760cc54e49Schristos 	prop_dictionary_t command_dict, args_dict, results_dict, data_dict;
77ae1c071dSdyoung 	char *xml;
78aae463daSthorpej 	int drvctl_error;
790c34f6cdSdrochner 
802cd106eeSlukem 	mode = 0;
810cc54e49Schristos 	while ((c = getopt(argc, argv, "QRSa:dlnprt")) != -1) {
820c34f6cdSdrochner 		switch (c) {
83ae1c071dSdyoung 		case 'Q':
84ae1c071dSdyoung 		case 'R':
85ae1c071dSdyoung 		case 'S':
860c34f6cdSdrochner 		case 'd':
87ae1c071dSdyoung 		case 'l':
88ef731d9eSthorpej 		case 'p':
89ae1c071dSdyoung 		case 'r':
900c34f6cdSdrochner 			mode = c;
910c34f6cdSdrochner 			break;
92fbdc9192Sjakllsch 		case 'a':
93fbdc9192Sjakllsch 			attr = optarg;
94fbdc9192Sjakllsch 			break;
95fbdc9192Sjakllsch 		case 'n':
96fbdc9192Sjakllsch 			nflag = true;
97fbdc9192Sjakllsch 			break;
98f398b121Sjmcneill 		case 't':
99f398b121Sjmcneill 			tflag = nflag = true;
100f398b121Sjmcneill 			break;
1010c34f6cdSdrochner 		case '?':
1020c34f6cdSdrochner 		default:
1030c34f6cdSdrochner 			usage();
1040c34f6cdSdrochner 		}
1050c34f6cdSdrochner 	}
1060c34f6cdSdrochner 
1070c34f6cdSdrochner 	argc -= optind;
1080c34f6cdSdrochner 	argv += optind;
1090c34f6cdSdrochner 
1109d6b98aeSjoerg 	if ((argc < 1 && mode != 'l') || mode == 0)
1110c34f6cdSdrochner 		usage();
1120c34f6cdSdrochner 
113ef731d9eSthorpej 	fd = open(DRVCTLDEV, OPEN_MODE(mode), 0);
1140cc54e49Schristos 	if (fd == -1)
1150cc54e49Schristos 		err(EXIT_FAILURE, "open %s", DRVCTLDEV);
1160c34f6cdSdrochner 
117ae1c071dSdyoung 	switch (mode) {
118ae1c071dSdyoung 	case 'Q':
119ae1c071dSdyoung 		paa.flags = DEVPM_F_SUBTREE;
120ae1c071dSdyoung 		/*FALLTHROUGH*/
121ae1c071dSdyoung 	case 'R':
122ae1c071dSdyoung 		strlcpy(paa.devname, argv[0], sizeof(paa.devname));
123ae1c071dSdyoung 
124ae1c071dSdyoung 		if (ioctl(fd, DRVRESUMEDEV, &paa) == -1)
1250cc54e49Schristos 			err(EXIT_FAILURE, "DRVRESUMEDEV");
126ae1c071dSdyoung 		break;
127ae1c071dSdyoung 	case 'S':
128ae1c071dSdyoung 		strlcpy(paa.devname, argv[0], sizeof(paa.devname));
129ae1c071dSdyoung 
130ae1c071dSdyoung 		if (ioctl(fd, DRVSUSPENDDEV, &paa) == -1)
1310cc54e49Schristos 			err(EXIT_FAILURE, "DRVSUSPENDDEV");
132ae1c071dSdyoung 		break;
133ae1c071dSdyoung 	case 'd':
1340c34f6cdSdrochner 		strlcpy(daa.devname, argv[0], sizeof(daa.devname));
1350c34f6cdSdrochner 
136ae1c071dSdyoung 		if (ioctl(fd, DRVDETACHDEV, &daa) == -1)
1370cc54e49Schristos 			err(EXIT_FAILURE, "DRVDETACHDEV");
138ae1c071dSdyoung 		break;
139ae1c071dSdyoung 	case 'l':
140f398b121Sjmcneill 		list_children(fd, argc ? argv[0] : NULL, nflag, tflag, 0);
141ae1c071dSdyoung 		break;
142ae1c071dSdyoung 	case 'r':
1430c34f6cdSdrochner 		memset(&raa, 0, sizeof(raa));
1440c34f6cdSdrochner 		strlcpy(raa.busname, argv[0], sizeof(raa.busname));
1450c34f6cdSdrochner 		if (attr)
1460c34f6cdSdrochner 			strlcpy(raa.ifattr, attr, sizeof(raa.ifattr));
1470c34f6cdSdrochner 		if (argc > 1) {
1480c34f6cdSdrochner 			locs = malloc((argc - 1) * sizeof(int));
1490c34f6cdSdrochner 			if (!locs)
1500cc54e49Schristos 				err(EXIT_FAILURE, "malloc int[%d]", argc - 1);
1510c34f6cdSdrochner 			for (i = 0; i < argc - 1; i++)
1520c34f6cdSdrochner 				locs[i] = atoi(argv[i + 1]);
1530c34f6cdSdrochner 			raa.numlocators = argc - 1;
1540c34f6cdSdrochner 			raa.locators = locs;
1550c34f6cdSdrochner 		}
1560c34f6cdSdrochner 
157ae1c071dSdyoung 		if (ioctl(fd, DRVRESCANBUS, &raa) == -1)
1580cc54e49Schristos 			err(EXIT_FAILURE, "DRVRESCANBUS");
159ae1c071dSdyoung 		break;
160ae1c071dSdyoung 	case 'p':
161ef731d9eSthorpej 		command_dict = prop_dictionary_create();
162ef731d9eSthorpej 		args_dict = prop_dictionary_create();
163ef731d9eSthorpej 
164aae463daSthorpej 		prop_dictionary_set_string_nocopy(command_dict,
165aae463daSthorpej 		    "drvctl-command", "get-properties");
166aae463daSthorpej 		prop_dictionary_set_string(args_dict, "device-name",
167aae463daSthorpej 		    argv[0]);
168aae463daSthorpej 		prop_dictionary_set_and_rel(command_dict, "drvctl-arguments",
169ef731d9eSthorpej 		    args_dict);
170ef731d9eSthorpej 
171ef731d9eSthorpej 		res = prop_dictionary_sendrecv_ioctl(command_dict, fd,
1720cc54e49Schristos 		    DRVCTLCOMMAND, &results_dict);
173ef731d9eSthorpej 		prop_object_release(command_dict);
174ef731d9eSthorpej 		if (res)
1750cc54e49Schristos 			errc(EXIT_FAILURE, res, "DRVCTLCOMMAND");
176ef731d9eSthorpej 
177aae463daSthorpej 		if (prop_dictionary_get_int(results_dict, "drvctl-error",
178aae463daSthorpej 					    &drvctl_error) &&
179aae463daSthorpej 		    drvctl_error != 0) {
180aae463daSthorpej 			errc(EXIT_FAILURE, drvctl_error, "get-properties");
181ef731d9eSthorpej 		}
182ef731d9eSthorpej 
183ef731d9eSthorpej 		data_dict = prop_dictionary_get(results_dict,
184ef731d9eSthorpej 		    "drvctl-result-data");
185ef731d9eSthorpej 		if (data_dict == NULL) {
1860cc54e49Schristos 			errx(EXIT_FAILURE,
1870cc54e49Schristos 			    "get-properties: failed to return result data");
188ef731d9eSthorpej 		}
189ef731d9eSthorpej 
1903b8212e8Sjmcneill 		if (argc == 1) {
191ef731d9eSthorpej 			xml = prop_dictionary_externalize(data_dict);
19291e31ba9Sdyoung 			if (!nflag) {
19391e31ba9Sdyoung 				printf("Properties for device `%s':\n",
19491e31ba9Sdyoung 				    argv[0]);
19591e31ba9Sdyoung 			}
19691e31ba9Sdyoung 			printf("%s", xml);
197ef731d9eSthorpej 			free(xml);
1983b8212e8Sjmcneill 		} else {
1993b8212e8Sjmcneill 			for (i = 1; i < argc; i++)
200440a8860Spgoyette 				extract_property(data_dict, argv[i], nflag);
2013b8212e8Sjmcneill 		}
2023b8212e8Sjmcneill 
2033b8212e8Sjmcneill 		prop_object_release(results_dict);
204ae1c071dSdyoung 		break;
205ae1c071dSdyoung 	default:
2060cc54e49Schristos 		errx(EXIT_FAILURE, "unknown command `%c'", mode);
207ae1c071dSdyoung 	}
2080c34f6cdSdrochner 
2090cc54e49Schristos 	return EXIT_SUCCESS;
2100c34f6cdSdrochner }
2113b8212e8Sjmcneill 
2123b8212e8Sjmcneill static void
213440a8860Spgoyette extract_property(prop_dictionary_t dict, const char *prop, bool nflag)
2143b8212e8Sjmcneill {
215440a8860Spgoyette 	char *s, *p, *cur, *ep = NULL;
2163b8212e8Sjmcneill 	prop_object_t obj;
2170a7f816bSmlelstv 	unsigned long ind;
2183b8212e8Sjmcneill 
2190a7f816bSmlelstv 	obj = dict;
2200a7f816bSmlelstv 	cur = NULL;
2213b8212e8Sjmcneill 	s = strdup(prop);
2223b8212e8Sjmcneill 	p = strtok_r(s, "/", &ep);
2233b8212e8Sjmcneill 	while (p) {
2243b8212e8Sjmcneill 		cur = p;
2253b8212e8Sjmcneill 		p = strtok_r(NULL, "/", &ep);
2260a7f816bSmlelstv 
2270a7f816bSmlelstv 		switch (prop_object_type(obj)) {
2280a7f816bSmlelstv 		case PROP_TYPE_DICTIONARY:
2290a7f816bSmlelstv 			obj = prop_dictionary_get(obj, cur);
2300a7f816bSmlelstv 			if (obj == NULL)
231657333c9Smlelstv 				p = NULL;
2320a7f816bSmlelstv 			break;
2330a7f816bSmlelstv 		case PROP_TYPE_ARRAY:
2340a7f816bSmlelstv 			ind = strtoul(cur, NULL, 0);
2350a7f816bSmlelstv 			obj = prop_array_get(obj, ind);
2360a7f816bSmlelstv 			if (obj == NULL)
237657333c9Smlelstv 				p = NULL;
2380a7f816bSmlelstv 			break;
2390a7f816bSmlelstv 		default:
240657333c9Smlelstv 			fprintf(stderr, "Select neither dict nor array with"
241657333c9Smlelstv 			    " `%s'\n", cur);
242657333c9Smlelstv 			obj = NULL;
243657333c9Smlelstv 			p = NULL;
244657333c9Smlelstv 			break;
2450a7f816bSmlelstv 		}
2460a7f816bSmlelstv 	}
2470a7f816bSmlelstv 
2480a7f816bSmlelstv 	if (obj != NULL && cur != NULL)
249440a8860Spgoyette 		display_object(obj, nflag);
250440a8860Spgoyette 
251440a8860Spgoyette 	free(s);
252440a8860Spgoyette }
253440a8860Spgoyette 
254440a8860Spgoyette static void
255440a8860Spgoyette display_object(prop_object_t obj, bool nflag)
256440a8860Spgoyette {
257440a8860Spgoyette 	char *xml;
258440a8860Spgoyette 	prop_object_t next_obj;
259440a8860Spgoyette 	prop_object_iterator_t iter;
260440a8860Spgoyette 
2613b8212e8Sjmcneill 	if (obj == NULL)
2623b8212e8Sjmcneill 		exit(EXIT_FAILURE);
2633b8212e8Sjmcneill 	switch (prop_object_type(obj)) {
2643b8212e8Sjmcneill 	case PROP_TYPE_BOOL:
265440a8860Spgoyette 		printf("%s\n", prop_bool_true(obj) ? "true" : "false");
2663b8212e8Sjmcneill 		break;
2673b8212e8Sjmcneill 	case PROP_TYPE_NUMBER:
268aae463daSthorpej 		printf("%" PRId64 "\n", prop_number_signed_value(obj));
2693b8212e8Sjmcneill 		break;
2703b8212e8Sjmcneill 	case PROP_TYPE_STRING:
271aae463daSthorpej 		printf("%s\n", prop_string_value(obj));
2723b8212e8Sjmcneill 		break;
2733b8212e8Sjmcneill 	case PROP_TYPE_DICTIONARY:
2743b8212e8Sjmcneill 		xml = prop_dictionary_externalize(obj);
2753b8212e8Sjmcneill 		printf("%s", xml);
2763b8212e8Sjmcneill 		free(xml);
2773b8212e8Sjmcneill 		break;
278440a8860Spgoyette 	case PROP_TYPE_ARRAY:
279440a8860Spgoyette 		iter = prop_array_iterator(obj);
280440a8860Spgoyette 		if (!nflag)
281440a8860Spgoyette 			printf("Array:\n");
282440a8860Spgoyette 		while ((next_obj = prop_object_iterator_next(iter)) != NULL)
283440a8860Spgoyette 			display_object(next_obj, nflag);
284440a8860Spgoyette 		break;
2853b8212e8Sjmcneill 	default:
2860cc54e49Schristos 		errx(EXIT_FAILURE, "Unhandled type %d", prop_object_type(obj));
2873b8212e8Sjmcneill 	}
2883b8212e8Sjmcneill }
289f398b121Sjmcneill 
290f398b121Sjmcneill static void
291f398b121Sjmcneill list_children(int fd, char *dvname, bool nflag, bool tflag, int depth)
292f398b121Sjmcneill {
2930cc54e49Schristos 	struct devlistargs laa = {
2940cc54e49Schristos 	    .l_devname = "", .l_childname = NULL, .l_children = 0
2950cc54e49Schristos 	};
296f398b121Sjmcneill 	size_t children;
297f398b121Sjmcneill 	int i, n;
298f398b121Sjmcneill 
299f398b121Sjmcneill 	if (dvname == NULL) {
300f398b121Sjmcneill 		if (depth > 0)
301f398b121Sjmcneill 			return;
302f398b121Sjmcneill 		*laa.l_devname = '\0';
303f398b121Sjmcneill 	} else {
304f398b121Sjmcneill 		strlcpy(laa.l_devname, dvname, sizeof(laa.l_devname));
305f398b121Sjmcneill 	}
306f398b121Sjmcneill 
307f398b121Sjmcneill 	if (ioctl(fd, DRVLISTDEV, &laa) == -1)
3080cc54e49Schristos 		err(EXIT_FAILURE, "DRVLISTDEV");
309f398b121Sjmcneill 
310f398b121Sjmcneill 	children = laa.l_children;
311f398b121Sjmcneill 
312f398b121Sjmcneill 	laa.l_childname = malloc(children * sizeof(laa.l_childname[0]));
313f398b121Sjmcneill 	if (laa.l_childname == NULL)
3140cc54e49Schristos 		err(EXIT_FAILURE, "DRVLISTDEV");
315f398b121Sjmcneill 	if (ioctl(fd, DRVLISTDEV, &laa) == -1)
3160cc54e49Schristos 		err(EXIT_FAILURE, "DRVLISTDEV");
317f398b121Sjmcneill 	if (laa.l_children > children)
3180cc54e49Schristos 		err(EXIT_FAILURE, "DRVLISTDEV: number of children grew");
319f398b121Sjmcneill 
320f398b121Sjmcneill 	for (i = 0; i < (int)laa.l_children; i++) {
321f398b121Sjmcneill 		for (n = 0; n < depth; n++)
322f398b121Sjmcneill 			printf("  ");
323f398b121Sjmcneill 		if (!nflag) {
324f398b121Sjmcneill 			printf("%s ",
325f398b121Sjmcneill 			    (dvname == NULL) ? "root" : laa.l_devname);
326f398b121Sjmcneill 		}
327f398b121Sjmcneill 		printf("%s\n", laa.l_childname[i]);
328f398b121Sjmcneill 		if (tflag) {
329f398b121Sjmcneill 			list_children(fd, laa.l_childname[i], nflag,
330f398b121Sjmcneill 			    tflag, depth + 1);
331f398b121Sjmcneill 		}
332f398b121Sjmcneill 	}
333f398b121Sjmcneill 
334f398b121Sjmcneill 	free(laa.l_childname);
335f398b121Sjmcneill }
336