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