xref: /minix/minix/usr.bin/trace/ioctl.c (revision e1cdaee1)
1 
2 #include "inc.h"
3 
4 #include <sys/ioctl.h>
5 
6 static char ioctlbuf[IOCPARM_MASK];
7 
8 static const struct {
9 	const char *(*name)(unsigned long);
10 	int (*arg)(struct trace_proc *, unsigned long, void *, int);
11 	int is_svrctl;
12 } ioctl_table[] = {
13 	{ block_ioctl_name,	block_ioctl_arg,	FALSE	},
14 	{ char_ioctl_name,	char_ioctl_arg,		FALSE	},
15 	{ net_ioctl_name,	net_ioctl_arg,		FALSE	},
16 	{ svrctl_name,		svrctl_arg,		TRUE	},
17 };
18 
19 /*
20  * Print an IOCTL request code, and save certain values in the corresponding
21  * process structure in order to be able to print the IOCTL argument.
22  */
23 void
24 put_ioctl_req(struct trace_proc * proc, const char * name, unsigned long req,
25 	int is_svrctl)
26 {
27 	const char *text;
28 	size_t size;
29 	unsigned int i, group, cmd;
30 	int r, w, big;
31 
32 	proc->ioctl_index = -1;
33 
34 	if (valuesonly > 1) {
35 		put_value(proc, name, "0x%lx", req);
36 
37 		return;
38 	}
39 
40 	/*
41 	 * Lookups are bruteforce across the IOCTL submodules; they're all
42 	 * checked.  We could use the group letter but that would create more
43 	 * issues than it solves.  Our hope is that at least the compiler is
44 	 * smart about looking up particular codes in each switch statement,
45 	 * although in the worst case, it's a full O(n) lookup.
46 	 */
47 	for (i = 0; i < COUNT(ioctl_table); i++) {
48 		/* IOCTLs and SVRCTLs are considered different name spaces. */
49 		if (ioctl_table[i].is_svrctl != is_svrctl)
50 			continue;
51 
52 		if ((text = ioctl_table[i].name(req)) != NULL) {
53 			proc->ioctl_index = i;
54 
55 			if (valuesonly)
56 				break;
57 
58 			put_field(proc, name, text);
59 
60 			return;
61 		}
62 	}
63 
64 	r = _MINIX_IOCTL_IOR(req);
65 	w = _MINIX_IOCTL_IOW(req);
66 	big = _MINIX_IOCTL_BIG(req);
67 	size = (size_t)(big ? _MINIX_IOCTL_SIZE_BIG(req) : IOCPARM_LEN(req));
68 	group = big ? 0 : IOCGROUP(req);
69 	cmd = req & 0xff; /* shockingly there is no macro for this.. */
70 
71 	/*
72 	 * Not sure why an entire bit is wasted on IOC_VOID (legacy reasons?),
73 	 * but since the redundancy is there, we might as well check whether
74 	 * this is a valid IOCTL request.  Also, we expect the group to be a
75 	 * printable character.  If either check fails, print just a number.
76 	 */
77 	if (((req & IOC_VOID) && (r || w || big || size > 0)) ||
78 	    (!(req & IOC_VOID) && ((!r && !w) || size == 0)) ||
79 	    (!big && (group < 32 || group > 127))) {
80 		put_value(proc, name, "0x%lx", req);
81 
82 		return;
83 	}
84 
85 	if (big) {
86 		/* For big IOCTLs, "R" becomes before "W" (old MINIX style). */
87 		put_value(proc, name, "_IO%s%s_BIG(%u,%zu)",
88 		    r ? "R" : "", w ? "W" : "", cmd, size);
89 	} else if (IOCGROUP(req) >= 32 && IOCGROUP(req) < 127) {
90 		/* For normal IOCTLs, "W" comes before "R" (NetBSD style). */
91 		put_value(proc, name, "_IO%s%s('%c',%u,%zu)",
92 		    w ? "W" : "", r ? "R" : "", group, cmd, size);
93 	}
94 }
95 
96 /*
97  * Print the supplied (out) part of an IOCTL argument, as applicable.  For
98  * efficiency reasons, this function assumes that put_ioctl_req() has been
99  * called for the corresponding IOCTL already, so that the necessary fields in
100  * the given proc structure are set as expected.
101  */
102 int
103 put_ioctl_arg_out(struct trace_proc * proc, const char * name,
104 	unsigned long req, vir_bytes addr, int is_svrctl)
105 {
106 	size_t size;
107 	int dir, all;
108 
109 	dir = (_MINIX_IOCTL_IOW(req) ? IF_OUT : 0) |
110 	    (_MINIX_IOCTL_IOR(req) ? IF_IN : 0);
111 
112 	if (dir == 0) {
113 		proc->ioctl_index = -1; /* no argument to print at all */
114 
115 		return CT_DONE;
116 	}
117 
118 	/* No support for printing big-IOCTL contents just yet. */
119 	if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
120 	    proc->ioctl_index == -1) {
121 		put_ptr(proc, name, addr);
122 
123 		return CT_DONE;
124 	}
125 
126 	assert(proc->ioctl_index >= 0);
127 	assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table));
128 	assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl);
129 
130 	proc->ioctl_flags =
131 	    ioctl_table[proc->ioctl_index].arg(proc, req, NULL, dir);
132 
133 	if (proc->ioctl_flags == 0) { /* no argument printing for this IOCTL */
134 		put_ptr(proc, name, addr);
135 
136 		proc->ioctl_index = -1; /* forget about the IOCTL handler */
137 
138 		return CT_DONE;
139 	}
140 
141 	/*
142 	 * If this triggers, the IOCTL handler returns a direction that is not
143 	 * part of the actual IOCTL, and the handler should be fixed.
144 	 */
145 	if (proc->ioctl_flags & ~dir) {
146 		output_flush(); /* show the IOCTL name for debugging */
147 
148 		assert(0);
149 	}
150 
151 	if (!(proc->ioctl_flags & IF_OUT))
152 		return CT_NOTDONE;
153 
154 	size = IOCPARM_LEN(req);
155 
156 	if (size > sizeof(ioctlbuf) ||
157 	    mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) {
158 		put_ptr(proc, name, addr);
159 
160 		/* There's no harm in trying the _in side later anyhow.. */
161 		return CT_DONE;
162 	}
163 
164 	put_open(proc, name, 0, "{", ", ");
165 
166 	all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_OUT);
167 
168 	if (!all)
169 		put_field(proc, NULL, "..");
170 
171 	put_close(proc, "}");
172 
173 	return CT_DONE;
174 }
175 
176 /*
177  * Print the returned (in) part of an IOCTL argument, as applicable.  This
178  * function assumes that it is preceded by a call to put_ioctl_arg_out for this
179  * process.
180  */
181 void
182 put_ioctl_arg_in(struct trace_proc * proc, const char * name, int failed,
183 	unsigned long req, vir_bytes addr, int is_svrctl)
184 {
185 	size_t size;
186 	int all;
187 
188 	if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
189 	    proc->ioctl_index == -1) {
190 		put_result(proc);
191 
192 		return;
193 	}
194 
195 	assert(proc->ioctl_index >= 0);
196 	assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table));
197 	assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl);
198 	assert(proc->ioctl_flags != 0);
199 
200 	if (proc->ioctl_flags & IF_OUT)
201 		put_result(proc);
202 	if (!(proc->ioctl_flags & IF_IN))
203 		return;
204 
205 	size = IOCPARM_LEN(req);
206 
207 	if (failed || size > sizeof(ioctlbuf) ||
208 	    mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) {
209 		if (!(proc->ioctl_flags & IF_OUT)) {
210 			put_ptr(proc, name, addr);
211 			put_equals(proc);
212 			put_result(proc);
213 		} else if (!failed)
214 			put_field(proc, NULL, "{..}");
215 
216 		return;
217 	}
218 
219 	put_open(proc, name, 0, "{", ", ");
220 
221 	all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_IN);
222 
223 	if (!all)
224 		put_field(proc, NULL, "..");
225 
226 	put_close(proc, "}");
227 
228 	if (!(proc->ioctl_flags & IF_OUT)) {
229 		put_equals(proc);
230 		put_result(proc);
231 	}
232 }
233