devctl.c revision 367457
1/*-
2 * Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
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/cdefs.h>
27__FBSDID("$FreeBSD: stable/11/usr.sbin/devctl/devctl.c 367457 2020-11-07 18:10:59Z dim $");
28
29#include <sys/linker_set.h>
30#include <devctl.h>
31#include <err.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <strings.h>
37#include <unistd.h>
38
39struct devctl_command {
40	const char *name;
41	int (*handler)(int ac, char **av);
42};
43
44#define	DEVCTL_DATASET(name)	devctl_ ## name ## _table
45
46#define	DEVCTL_COMMAND(set, name, function)				\
47	static struct devctl_command function ## _devctl_command =	\
48	{ #name, function };						\
49	DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command)
50
51#define	DEVCTL_TABLE(set, name)						\
52	SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command);	\
53									\
54	static int							\
55	devctl_ ## name ## _table_handler(int ac, char **av)		\
56	{								\
57		return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \
58		    SET_LIMIT(DEVCTL_DATASET(name)), ac, av));		\
59	}								\
60	DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler)
61
62static int	devctl_table_handler(struct devctl_command **start,
63    struct devctl_command **end, int ac, char **av);
64
65SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command);
66
67DEVCTL_TABLE(top, clear);
68DEVCTL_TABLE(top, set);
69
70static void
71usage(void)
72{
73	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
74	    "usage: devctl attach device",
75	    "       devctl detach [-f] device",
76	    "       devctl disable [-f] device",
77	    "       devctl enable device",
78	    "       devctl suspend device",
79	    "       devctl resume device",
80	    "       devctl set driver [-f] device driver",
81	    "       devctl clear driver [-f] device",
82	    "       devctl rescan device",
83	    "       devctl delete [-f] device",
84	    "       devctl reset [-d] device"
85	    );
86	exit(1);
87}
88
89static int
90devctl_table_handler(struct devctl_command **start,
91    struct devctl_command **end, int ac, char **av)
92{
93	struct devctl_command **cmd;
94
95	if (ac < 2) {
96		warnx("The %s command requires a sub-command.", av[0]);
97		return (EINVAL);
98	}
99	for (cmd = start; cmd < end; cmd++) {
100		if (strcmp((*cmd)->name, av[1]) == 0)
101			return ((*cmd)->handler(ac - 1, av + 1));
102	}
103
104	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
105	return (ENOENT);
106}
107
108static int
109help(int ac __unused, char **av __unused)
110{
111
112	usage();
113	return (0);
114}
115DEVCTL_COMMAND(top, help, help);
116
117static int
118attach(int ac, char **av)
119{
120
121	if (ac != 2)
122		usage();
123	if (devctl_attach(av[1]) < 0)
124		err(1, "Failed to attach %s", av[1]);
125	return (0);
126}
127DEVCTL_COMMAND(top, attach, attach);
128
129static void
130detach_usage(void)
131{
132
133	fprintf(stderr, "usage: devctl detach [-f] device\n");
134	exit(1);
135}
136
137static int
138detach(int ac, char **av)
139{
140	bool force;
141	int ch;
142
143	force = false;
144	while ((ch = getopt(ac, av, "f")) != -1)
145		switch (ch) {
146		case 'f':
147			force = true;
148			break;
149		default:
150			detach_usage();
151		}
152	ac -= optind;
153	av += optind;
154
155	if (ac != 1)
156		detach_usage();
157	if (devctl_detach(av[0], force) < 0)
158		err(1, "Failed to detach %s", av[0]);
159	return (0);
160}
161DEVCTL_COMMAND(top, detach, detach);
162
163static void
164disable_usage(void)
165{
166
167	fprintf(stderr, "usage: devctl disable [-f] device\n");
168	exit(1);
169}
170
171static int
172disable(int ac, char **av)
173{
174	bool force;
175	int ch;
176
177	force = false;
178	while ((ch = getopt(ac, av, "f")) != -1)
179		switch (ch) {
180		case 'f':
181			force = true;
182			break;
183		default:
184			disable_usage();
185		}
186	ac -= optind;
187	av += optind;
188
189	if (ac != 1)
190		disable_usage();
191	if (devctl_disable(av[0], force) < 0)
192		err(1, "Failed to disable %s", av[0]);
193	return (0);
194}
195DEVCTL_COMMAND(top, disable, disable);
196
197static int
198enable(int ac, char **av)
199{
200
201	if (ac != 2)
202		usage();
203	if (devctl_enable(av[1]) < 0)
204		err(1, "Failed to enable %s", av[1]);
205	return (0);
206}
207DEVCTL_COMMAND(top, enable, enable);
208
209static int
210suspend(int ac, char **av)
211{
212
213	if (ac != 2)
214		usage();
215	if (devctl_suspend(av[1]) < 0)
216		err(1, "Failed to suspend %s", av[1]);
217	return (0);
218}
219DEVCTL_COMMAND(top, suspend, suspend);
220
221static int
222resume(int ac, char **av)
223{
224
225	if (ac != 2)
226		usage();
227	if (devctl_resume(av[1]) < 0)
228		err(1, "Failed to resume %s", av[1]);
229	return (0);
230}
231DEVCTL_COMMAND(top, resume, resume);
232
233static void
234set_driver_usage(void)
235{
236
237	fprintf(stderr, "usage: devctl set driver [-f] device driver\n");
238	exit(1);
239}
240
241static int
242set_driver(int ac, char **av)
243{
244	bool force;
245	int ch;
246
247	force = false;
248	while ((ch = getopt(ac, av, "f")) != -1)
249		switch (ch) {
250		case 'f':
251			force = true;
252			break;
253		default:
254			set_driver_usage();
255		}
256	ac -= optind;
257	av += optind;
258
259	if (ac != 2)
260		set_driver_usage();
261	if (devctl_set_driver(av[0], av[1], force) < 0)
262		err(1, "Failed to set %s driver to %s", av[0], av[1]);
263	return (0);
264}
265DEVCTL_COMMAND(set, driver, set_driver);
266
267static void
268clear_driver_usage(void)
269{
270
271	fprintf(stderr, "usage: devctl clear driver [-f] device\n");
272	exit(1);
273}
274
275static int
276clear_driver(int ac, char **av)
277{
278	bool force;
279	int ch;
280
281	force = false;
282	while ((ch = getopt(ac, av, "f")) != -1)
283		switch (ch) {
284		case 'f':
285			force = true;
286			break;
287		default:
288			clear_driver_usage();
289		}
290	ac -= optind;
291	av += optind;
292
293	if (ac != 1)
294		clear_driver_usage();
295	if (devctl_clear_driver(av[0], force) < 0)
296		err(1, "Failed to clear %s driver", av[0]);
297	return (0);
298}
299DEVCTL_COMMAND(clear, driver, clear_driver);
300
301static int
302rescan(int ac, char **av)
303{
304
305	if (ac != 2)
306		usage();
307	if (devctl_rescan(av[1]) < 0)
308		err(1, "Failed to rescan %s", av[1]);
309	return (0);
310}
311DEVCTL_COMMAND(top, rescan, rescan);
312
313static void
314delete_usage(void)
315{
316
317	fprintf(stderr, "usage: devctl delete [-f] device\n");
318	exit(1);
319}
320
321static int
322delete(int ac, char **av)
323{
324	bool force;
325	int ch;
326
327	force = false;
328	while ((ch = getopt(ac, av, "f")) != -1)
329		switch (ch) {
330		case 'f':
331			force = true;
332			break;
333		default:
334			delete_usage();
335		}
336	ac -= optind;
337	av += optind;
338
339	if (ac != 1)
340		delete_usage();
341	if (devctl_delete(av[0], force) < 0)
342		err(1, "Failed to delete %s", av[0]);
343	return (0);
344}
345DEVCTL_COMMAND(top, delete, delete);
346
347static void
348reset_usage(void)
349{
350
351	fprintf(stderr, "usage: devctl reset [-d] device\n");
352	exit(1);
353}
354
355static int
356reset(int ac, char **av)
357{
358	bool detach_drv;
359	int ch;
360
361	detach_drv = false;
362	while ((ch = getopt(ac, av, "d")) != -1)
363		switch (ch) {
364		case 'd':
365			detach_drv = true;
366			break;
367		default:
368			reset_usage();
369		}
370	ac -= optind;
371	av += optind;
372
373	if (ac != 1)
374		reset_usage();
375	if (devctl_reset(av[0], detach_drv) < 0)
376		err(1, "Failed to reset %s", av[0]);
377	return (0);
378}
379DEVCTL_COMMAND(top, reset, reset);
380
381int
382main(int ac, char *av[])
383{
384	struct devctl_command **cmd;
385
386	if (ac == 1)
387		usage();
388	ac--;
389	av++;
390
391	SET_FOREACH(cmd, DEVCTL_DATASET(top)) {
392		if (strcmp((*cmd)->name, av[0]) == 0) {
393			if ((*cmd)->handler(ac, av) != 0)
394				return (1);
395			else
396				return (0);
397		}
398	}
399	warnx("Unknown command %s.", av[0]);
400	return (1);
401}
402