xref: /minix3/minix/usr.bin/trace/ioctl.c (revision b58e161ccb9bcc2008a44da1b8ee01a0a6f2990e)
1521fa314SDavid van Moolenbroek 
2521fa314SDavid van Moolenbroek #include "inc.h"
3521fa314SDavid van Moolenbroek 
4521fa314SDavid van Moolenbroek #include <sys/ioctl.h>
5521fa314SDavid van Moolenbroek 
6521fa314SDavid van Moolenbroek static char ioctlbuf[IOCPARM_MASK];
7521fa314SDavid van Moolenbroek 
8521fa314SDavid van Moolenbroek static const struct {
9521fa314SDavid van Moolenbroek 	const char *(*name)(unsigned long);
10521fa314SDavid van Moolenbroek 	int (*arg)(struct trace_proc *, unsigned long, void *, int);
11521fa314SDavid van Moolenbroek 	int is_svrctl;
12521fa314SDavid van Moolenbroek } ioctl_table[] = {
13521fa314SDavid van Moolenbroek 	{ block_ioctl_name,	block_ioctl_arg,	FALSE	},
14521fa314SDavid van Moolenbroek 	{ char_ioctl_name,	char_ioctl_arg,		FALSE	},
15521fa314SDavid van Moolenbroek 	{ net_ioctl_name,	net_ioctl_arg,		FALSE	},
16521fa314SDavid van Moolenbroek 	{ svrctl_name,		svrctl_arg,		TRUE	},
17521fa314SDavid van Moolenbroek };
18521fa314SDavid van Moolenbroek 
19521fa314SDavid van Moolenbroek /*
20521fa314SDavid van Moolenbroek  * Print an IOCTL request code, and save certain values in the corresponding
21521fa314SDavid van Moolenbroek  * process structure in order to be able to print the IOCTL argument.
22521fa314SDavid van Moolenbroek  */
23521fa314SDavid van Moolenbroek void
put_ioctl_req(struct trace_proc * proc,const char * name,unsigned long req,int is_svrctl)24521fa314SDavid van Moolenbroek put_ioctl_req(struct trace_proc * proc, const char * name, unsigned long req,
25521fa314SDavid van Moolenbroek 	int is_svrctl)
26521fa314SDavid van Moolenbroek {
27521fa314SDavid van Moolenbroek 	const char *text;
28521fa314SDavid van Moolenbroek 	size_t size;
29*b58e161cSDavid van Moolenbroek 	unsigned int i, group, cmd;
30*b58e161cSDavid van Moolenbroek 	int r, w, big;
31521fa314SDavid van Moolenbroek 
32521fa314SDavid van Moolenbroek 	proc->ioctl_index = -1;
33521fa314SDavid van Moolenbroek 
34521fa314SDavid van Moolenbroek 	if (valuesonly > 1) {
35521fa314SDavid van Moolenbroek 		put_value(proc, name, "0x%lx", req);
36521fa314SDavid van Moolenbroek 
37521fa314SDavid van Moolenbroek 		return;
38521fa314SDavid van Moolenbroek 	}
39521fa314SDavid van Moolenbroek 
40521fa314SDavid van Moolenbroek 	/*
41521fa314SDavid van Moolenbroek 	 * Lookups are bruteforce across the IOCTL submodules; they're all
42521fa314SDavid van Moolenbroek 	 * checked.  We could use the group letter but that would create more
43521fa314SDavid van Moolenbroek 	 * issues than it solves.  Our hope is that at least the compiler is
44521fa314SDavid van Moolenbroek 	 * smart about looking up particular codes in each switch statement,
45521fa314SDavid van Moolenbroek 	 * although in the worst case, it's a full O(n) lookup.
46521fa314SDavid van Moolenbroek 	 */
478b18d03dSDavid van Moolenbroek 	for (i = 0; i < COUNT(ioctl_table); i++) {
48521fa314SDavid van Moolenbroek 		/* IOCTLs and SVRCTLs are considered different name spaces. */
49521fa314SDavid van Moolenbroek 		if (ioctl_table[i].is_svrctl != is_svrctl)
50521fa314SDavid van Moolenbroek 			continue;
51521fa314SDavid van Moolenbroek 
52521fa314SDavid van Moolenbroek 		if ((text = ioctl_table[i].name(req)) != NULL) {
53521fa314SDavid van Moolenbroek 			proc->ioctl_index = i;
54521fa314SDavid van Moolenbroek 
558b18d03dSDavid van Moolenbroek 			if (valuesonly)
568b18d03dSDavid van Moolenbroek 				break;
578b18d03dSDavid van Moolenbroek 
588b18d03dSDavid van Moolenbroek 			put_field(proc, name, text);
598b18d03dSDavid van Moolenbroek 
60521fa314SDavid van Moolenbroek 			return;
61521fa314SDavid van Moolenbroek 		}
62521fa314SDavid van Moolenbroek 	}
63521fa314SDavid van Moolenbroek 
64521fa314SDavid van Moolenbroek 	r = _MINIX_IOCTL_IOR(req);
65521fa314SDavid van Moolenbroek 	w = _MINIX_IOCTL_IOW(req);
66521fa314SDavid van Moolenbroek 	big = _MINIX_IOCTL_BIG(req);
67521fa314SDavid van Moolenbroek 	size = (size_t)(big ? _MINIX_IOCTL_SIZE_BIG(req) : IOCPARM_LEN(req));
68521fa314SDavid van Moolenbroek 	group = big ? 0 : IOCGROUP(req);
69521fa314SDavid van Moolenbroek 	cmd = req & 0xff; /* shockingly there is no macro for this.. */
70521fa314SDavid van Moolenbroek 
71521fa314SDavid van Moolenbroek 	/*
72521fa314SDavid van Moolenbroek 	 * Not sure why an entire bit is wasted on IOC_VOID (legacy reasons?),
73521fa314SDavid van Moolenbroek 	 * but since the redundancy is there, we might as well check whether
74521fa314SDavid van Moolenbroek 	 * this is a valid IOCTL request.  Also, we expect the group to be a
75521fa314SDavid van Moolenbroek 	 * printable character.  If either check fails, print just a number.
76521fa314SDavid van Moolenbroek 	 */
77521fa314SDavid van Moolenbroek 	if (((req & IOC_VOID) && (r || w || big || size > 0)) ||
78521fa314SDavid van Moolenbroek 	    (!(req & IOC_VOID) && ((!r && !w) || size == 0)) ||
79521fa314SDavid van Moolenbroek 	    (!big && (group < 32 || group > 127))) {
80521fa314SDavid van Moolenbroek 		put_value(proc, name, "0x%lx", req);
81521fa314SDavid van Moolenbroek 
82521fa314SDavid van Moolenbroek 		return;
83521fa314SDavid van Moolenbroek 	}
84521fa314SDavid van Moolenbroek 
85521fa314SDavid van Moolenbroek 	if (big) {
86521fa314SDavid van Moolenbroek 		/* For big IOCTLs, "R" becomes before "W" (old MINIX style). */
87521fa314SDavid van Moolenbroek 		put_value(proc, name, "_IO%s%s_BIG(%u,%zu)",
88521fa314SDavid van Moolenbroek 		    r ? "R" : "", w ? "W" : "", cmd, size);
89521fa314SDavid van Moolenbroek 	} else if (IOCGROUP(req) >= 32 && IOCGROUP(req) < 127) {
90521fa314SDavid van Moolenbroek 		/* For normal IOCTLs, "W" comes before "R" (NetBSD style). */
91521fa314SDavid van Moolenbroek 		put_value(proc, name, "_IO%s%s('%c',%u,%zu)",
92521fa314SDavid van Moolenbroek 		    w ? "W" : "", r ? "R" : "", group, cmd, size);
93521fa314SDavid van Moolenbroek 	}
94521fa314SDavid van Moolenbroek }
95521fa314SDavid van Moolenbroek 
96521fa314SDavid van Moolenbroek /*
97521fa314SDavid van Moolenbroek  * Print the supplied (out) part of an IOCTL argument, as applicable.  For
98521fa314SDavid van Moolenbroek  * efficiency reasons, this function assumes that put_ioctl_req() has been
99521fa314SDavid van Moolenbroek  * called for the corresponding IOCTL already, so that the necessary fields in
100521fa314SDavid van Moolenbroek  * the given proc structure are set as expected.
101521fa314SDavid van Moolenbroek  */
102521fa314SDavid van Moolenbroek int
put_ioctl_arg_out(struct trace_proc * proc,const char * name,unsigned long req,vir_bytes addr,int is_svrctl)103521fa314SDavid van Moolenbroek put_ioctl_arg_out(struct trace_proc * proc, const char * name,
104521fa314SDavid van Moolenbroek 	unsigned long req, vir_bytes addr, int is_svrctl)
105521fa314SDavid van Moolenbroek {
106521fa314SDavid van Moolenbroek 	size_t size;
107521fa314SDavid van Moolenbroek 	int dir, all;
108521fa314SDavid van Moolenbroek 
109521fa314SDavid van Moolenbroek 	dir = (_MINIX_IOCTL_IOW(req) ? IF_OUT : 0) |
110521fa314SDavid van Moolenbroek 	    (_MINIX_IOCTL_IOR(req) ? IF_IN : 0);
111521fa314SDavid van Moolenbroek 
1128b18d03dSDavid van Moolenbroek 	if (dir == 0) {
113521fa314SDavid van Moolenbroek 		proc->ioctl_index = -1; /* no argument to print at all */
114521fa314SDavid van Moolenbroek 
1158b18d03dSDavid van Moolenbroek 		return CT_DONE;
1168b18d03dSDavid van Moolenbroek 	}
1178b18d03dSDavid van Moolenbroek 
118521fa314SDavid van Moolenbroek 	/* No support for printing big-IOCTL contents just yet. */
119521fa314SDavid van Moolenbroek 	if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
120521fa314SDavid van Moolenbroek 	    proc->ioctl_index == -1) {
121521fa314SDavid van Moolenbroek 		put_ptr(proc, name, addr);
122521fa314SDavid van Moolenbroek 
123521fa314SDavid van Moolenbroek 		return CT_DONE;
124521fa314SDavid van Moolenbroek 	}
125521fa314SDavid van Moolenbroek 
126521fa314SDavid van Moolenbroek 	assert(proc->ioctl_index >= 0);
127*b58e161cSDavid van Moolenbroek 	assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table));
128521fa314SDavid van Moolenbroek 	assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl);
129521fa314SDavid van Moolenbroek 
130521fa314SDavid van Moolenbroek 	proc->ioctl_flags =
131521fa314SDavid van Moolenbroek 	    ioctl_table[proc->ioctl_index].arg(proc, req, NULL, dir);
132521fa314SDavid van Moolenbroek 
133521fa314SDavid van Moolenbroek 	if (proc->ioctl_flags == 0) { /* no argument printing for this IOCTL */
134521fa314SDavid van Moolenbroek 		put_ptr(proc, name, addr);
135521fa314SDavid van Moolenbroek 
136521fa314SDavid van Moolenbroek 		proc->ioctl_index = -1; /* forget about the IOCTL handler */
137521fa314SDavid van Moolenbroek 
138521fa314SDavid van Moolenbroek 		return CT_DONE;
139521fa314SDavid van Moolenbroek 	}
140521fa314SDavid van Moolenbroek 
141521fa314SDavid van Moolenbroek 	/*
142521fa314SDavid van Moolenbroek 	 * If this triggers, the IOCTL handler returns a direction that is not
143521fa314SDavid van Moolenbroek 	 * part of the actual IOCTL, and the handler should be fixed.
144521fa314SDavid van Moolenbroek 	 */
145521fa314SDavid van Moolenbroek 	if (proc->ioctl_flags & ~dir) {
146521fa314SDavid van Moolenbroek 		output_flush(); /* show the IOCTL name for debugging */
147521fa314SDavid van Moolenbroek 
148521fa314SDavid van Moolenbroek 		assert(0);
149521fa314SDavid van Moolenbroek 	}
150521fa314SDavid van Moolenbroek 
151521fa314SDavid van Moolenbroek 	if (!(proc->ioctl_flags & IF_OUT))
152521fa314SDavid van Moolenbroek 		return CT_NOTDONE;
153521fa314SDavid van Moolenbroek 
154521fa314SDavid van Moolenbroek 	size = IOCPARM_LEN(req);
155521fa314SDavid van Moolenbroek 
156521fa314SDavid van Moolenbroek 	if (size > sizeof(ioctlbuf) ||
157521fa314SDavid van Moolenbroek 	    mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) {
158521fa314SDavid van Moolenbroek 		put_ptr(proc, name, addr);
159521fa314SDavid van Moolenbroek 
160521fa314SDavid van Moolenbroek 		/* There's no harm in trying the _in side later anyhow.. */
161521fa314SDavid van Moolenbroek 		return CT_DONE;
162521fa314SDavid van Moolenbroek 	}
163521fa314SDavid van Moolenbroek 
164521fa314SDavid van Moolenbroek 	put_open(proc, name, 0, "{", ", ");
165521fa314SDavid van Moolenbroek 
166521fa314SDavid van Moolenbroek 	all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_OUT);
167521fa314SDavid van Moolenbroek 
168521fa314SDavid van Moolenbroek 	if (!all)
169521fa314SDavid van Moolenbroek 		put_field(proc, NULL, "..");
170521fa314SDavid van Moolenbroek 
171521fa314SDavid van Moolenbroek 	put_close(proc, "}");
172521fa314SDavid van Moolenbroek 
173521fa314SDavid van Moolenbroek 	return CT_DONE;
174521fa314SDavid van Moolenbroek }
175521fa314SDavid van Moolenbroek 
176521fa314SDavid van Moolenbroek /*
177521fa314SDavid van Moolenbroek  * Print the returned (in) part of an IOCTL argument, as applicable.  This
178521fa314SDavid van Moolenbroek  * function assumes that it is preceded by a call to put_ioctl_arg_out for this
179521fa314SDavid van Moolenbroek  * process.
180521fa314SDavid van Moolenbroek  */
181521fa314SDavid van Moolenbroek void
put_ioctl_arg_in(struct trace_proc * proc,const char * name,int failed,unsigned long req,vir_bytes addr,int is_svrctl)182521fa314SDavid van Moolenbroek put_ioctl_arg_in(struct trace_proc * proc, const char * name, int failed,
183521fa314SDavid van Moolenbroek 	unsigned long req, vir_bytes addr, int is_svrctl)
184521fa314SDavid van Moolenbroek {
185521fa314SDavid van Moolenbroek 	size_t size;
186521fa314SDavid van Moolenbroek 	int all;
187521fa314SDavid van Moolenbroek 
188521fa314SDavid van Moolenbroek 	if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
189521fa314SDavid van Moolenbroek 	    proc->ioctl_index == -1) {
190521fa314SDavid van Moolenbroek 		put_result(proc);
191521fa314SDavid van Moolenbroek 
192521fa314SDavid van Moolenbroek 		return;
193521fa314SDavid van Moolenbroek 	}
194521fa314SDavid van Moolenbroek 
195521fa314SDavid van Moolenbroek 	assert(proc->ioctl_index >= 0);
196*b58e161cSDavid van Moolenbroek 	assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table));
197521fa314SDavid van Moolenbroek 	assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl);
198521fa314SDavid van Moolenbroek 	assert(proc->ioctl_flags != 0);
199521fa314SDavid van Moolenbroek 
200521fa314SDavid van Moolenbroek 	if (proc->ioctl_flags & IF_OUT)
201521fa314SDavid van Moolenbroek 		put_result(proc);
202521fa314SDavid van Moolenbroek 	if (!(proc->ioctl_flags & IF_IN))
203521fa314SDavid van Moolenbroek 		return;
204521fa314SDavid van Moolenbroek 
205521fa314SDavid van Moolenbroek 	size = IOCPARM_LEN(req);
206521fa314SDavid van Moolenbroek 
207521fa314SDavid van Moolenbroek 	if (failed || size > sizeof(ioctlbuf) ||
208521fa314SDavid van Moolenbroek 	    mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) {
209521fa314SDavid van Moolenbroek 		if (!(proc->ioctl_flags & IF_OUT)) {
210521fa314SDavid van Moolenbroek 			put_ptr(proc, name, addr);
211521fa314SDavid van Moolenbroek 			put_equals(proc);
212521fa314SDavid van Moolenbroek 			put_result(proc);
213521fa314SDavid van Moolenbroek 		} else if (!failed)
214521fa314SDavid van Moolenbroek 			put_field(proc, NULL, "{..}");
215521fa314SDavid van Moolenbroek 
216521fa314SDavid van Moolenbroek 		return;
217521fa314SDavid van Moolenbroek 	}
218521fa314SDavid van Moolenbroek 
219521fa314SDavid van Moolenbroek 	put_open(proc, name, 0, "{", ", ");
220521fa314SDavid van Moolenbroek 
221521fa314SDavid van Moolenbroek 	all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_IN);
222521fa314SDavid van Moolenbroek 
223521fa314SDavid van Moolenbroek 	if (!all)
224521fa314SDavid van Moolenbroek 		put_field(proc, NULL, "..");
225521fa314SDavid van Moolenbroek 
226521fa314SDavid van Moolenbroek 	put_close(proc, "}");
227521fa314SDavid van Moolenbroek 
228521fa314SDavid van Moolenbroek 	if (!(proc->ioctl_flags & IF_OUT)) {
229521fa314SDavid van Moolenbroek 		put_equals(proc);
230521fa314SDavid van Moolenbroek 		put_result(proc);
231521fa314SDavid van Moolenbroek 	}
232521fa314SDavid van Moolenbroek }
233