xref: /freebsd/sys/compat/linux/linux_mib.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
1 /*-
2  * Copyright (c) 1999 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/sysctl.h>
35 #include <sys/proc.h>
36 #include <sys/malloc.h>
37 #include <sys/jail.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.h>
40 
41 #include <machine/../linux/linux.h>
42 #include <compat/linux/linux_mib.h>
43 
44 struct linux_prison {
45 	char	pr_osname[LINUX_MAX_UTSNAME];
46 	char	pr_osrelease[LINUX_MAX_UTSNAME];
47 	int	pr_oss_version;
48 };
49 
50 SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0,
51 	    "Linux mode");
52 
53 static char	linux_osname[LINUX_MAX_UTSNAME] = "Linux";
54 
55 static int
56 linux_sysctl_osname(SYSCTL_HANDLER_ARGS)
57 {
58 	char osname[LINUX_MAX_UTSNAME];
59 	int error;
60 
61 	linux_get_osname(req->td->td_proc, osname);
62 	error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req);
63 	if (error || req->newptr == NULL)
64 		return (error);
65 	error = linux_set_osname(req->td->td_proc, osname);
66 	return (error);
67 }
68 
69 SYSCTL_PROC(_compat_linux, OID_AUTO, osname,
70 	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON,
71 	    0, 0, linux_sysctl_osname, "A",
72 	    "Linux kernel OS name");
73 
74 static char	linux_osrelease[LINUX_MAX_UTSNAME] = "2.4.2";
75 
76 static int
77 linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS)
78 {
79 	char osrelease[LINUX_MAX_UTSNAME];
80 	int error;
81 
82 	linux_get_osrelease(req->td->td_proc, osrelease);
83 	error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req);
84 	if (error || req->newptr == NULL)
85 		return (error);
86 	error = linux_set_osrelease(req->td->td_proc, osrelease);
87 	return (error);
88 }
89 
90 SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease,
91 	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON,
92 	    0, 0, linux_sysctl_osrelease, "A",
93 	    "Linux kernel OS release");
94 
95 static int	linux_oss_version = 0x030600;
96 
97 static int
98 linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS)
99 {
100 	int oss_version;
101 	int error;
102 
103 	oss_version = linux_get_oss_version(req->td->td_proc);
104 	error = sysctl_handle_int(oidp, &oss_version, 0, req);
105 	if (error || req->newptr == NULL)
106 		return (error);
107 	error = linux_set_oss_version(req->td->td_proc, oss_version);
108 	return (error);
109 }
110 
111 SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version,
112 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON,
113 	    0, 0, linux_sysctl_oss_version, "I",
114 	    "Linux OSS version");
115 
116 /*
117  * Returns holding the prison mutex if return non-NULL.
118  */
119 static struct linux_prison *
120 linux_get_prison(struct proc *p)
121 {
122 	register struct prison *pr;
123 	register struct linux_prison *lpr;
124 
125 	if (!jailed(p->p_ucred))
126 		return (NULL);
127 
128 	pr = p->p_ucred->cr_prison;
129 
130 	/*
131 	 * Rather than hold the prison mutex during allocation, check to
132 	 * see if we need to allocate while holding the mutex, release it,
133 	 * allocate, then once we've allocated the memory, check again to
134 	 * see if it's still needed, and set if appropriate.  If it's not,
135 	 * we release the mutex again to FREE(), and grab it again so as
136 	 * to release holding the lock.
137 	 */
138 	mtx_lock(&pr->pr_mtx);
139 	if (pr->pr_linux == NULL) {
140 		mtx_unlock(&pr->pr_mtx);
141 		MALLOC(lpr, struct linux_prison *, sizeof *lpr,
142 		    M_PRISON, M_WAITOK|M_ZERO);
143 		mtx_lock(&pr->pr_mtx);
144 		if (pr->pr_linux == NULL) {
145 			pr->pr_linux = lpr;
146 		} else {
147 			mtx_unlock(&pr->pr_mtx);
148 			FREE(lpr, M_PRISON);
149 			mtx_lock(&pr->pr_mtx);
150 		}
151 	}
152 
153 	return (pr->pr_linux);
154 }
155 
156 void
157 linux_get_osname(p, dst)
158 	struct proc *p;
159 	char *dst;
160 {
161 	register struct prison *pr;
162 	register struct linux_prison *lpr;
163 
164 	if (p->p_ucred->cr_prison == NULL) {
165 		bcopy(linux_osname, dst, LINUX_MAX_UTSNAME);
166 		return;
167 	}
168 
169 	pr = p->p_ucred->cr_prison;
170 
171 	mtx_lock(&pr->pr_mtx);
172 	if (pr->pr_linux != NULL) {
173 		lpr = (struct linux_prison *)pr->pr_linux;
174 		if (lpr->pr_osname[0]) {
175 			bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME);
176 			mtx_unlock(&pr->pr_mtx);
177 			return;
178 		}
179 	}
180 	mtx_unlock(&pr->pr_mtx);
181 	bcopy(linux_osname, dst, LINUX_MAX_UTSNAME);
182 }
183 
184 int
185 linux_set_osname(p, osname)
186 	struct proc *p;
187 	char *osname;
188 {
189 	register struct linux_prison *lpr;
190 
191 	lpr = linux_get_prison(p);
192 	if (lpr != NULL) {
193 		strcpy(lpr->pr_osname, osname);
194 		mtx_unlock(&p->p_ucred->cr_prison->pr_mtx);
195 	} else {
196 		strcpy(linux_osname, osname);
197 	}
198 
199 	return (0);
200 }
201 
202 void
203 linux_get_osrelease(p, dst)
204 	struct proc *p;
205 	char *dst;
206 {
207 	register struct prison *pr;
208 	struct linux_prison *lpr;
209 
210 	if (p->p_ucred->cr_prison == NULL) {
211 		bcopy(linux_osrelease, dst, LINUX_MAX_UTSNAME);
212 		return;
213 	}
214 
215 	pr = p->p_ucred->cr_prison;
216 
217 	mtx_lock(&pr->pr_mtx);
218 	if (pr->pr_linux != NULL) {
219 		lpr = (struct linux_prison *) pr->pr_linux;
220 		if (lpr->pr_osrelease[0]) {
221 			bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME);
222 			mtx_unlock(&pr->pr_mtx);
223 			return;
224 		}
225 	}
226 	mtx_unlock(&pr->pr_mtx);
227 	bcopy(linux_osrelease, dst, LINUX_MAX_UTSNAME);
228 }
229 
230 int
231 linux_set_osrelease(p, osrelease)
232 	struct proc *p;
233 	char *osrelease;
234 {
235 	register struct linux_prison *lpr;
236 
237 	lpr = linux_get_prison(p);
238 	if (lpr != NULL) {
239 		strcpy(lpr->pr_osrelease, osrelease);
240 		mtx_unlock(&p->p_ucred->cr_prison->pr_mtx);
241 	} else {
242 		strcpy(linux_osrelease, osrelease);
243 	}
244 
245 	return (0);
246 }
247 
248 int
249 linux_get_oss_version(p)
250 	struct proc *p;
251 {
252 	register struct prison *pr;
253 	register struct linux_prison *lpr;
254 	int version;
255 
256 	if (p->p_ucred->cr_prison == NULL)
257 		return (linux_oss_version);
258 
259 	pr = p->p_ucred->cr_prison;
260 
261 	mtx_lock(&pr->pr_mtx);
262 	if (pr->pr_linux != NULL) {
263 		lpr = (struct linux_prison *) pr->pr_linux;
264 		if (lpr->pr_oss_version) {
265 			version = lpr->pr_oss_version;
266 		} else {
267 			version = linux_oss_version;
268 		}
269 	} else {
270 		version = linux_oss_version;
271 	}
272 	mtx_unlock(&pr->pr_mtx);
273 
274 	return (version);
275 }
276 
277 int
278 linux_set_oss_version(p, oss_version)
279 	struct proc *p;
280 	int oss_version;
281 {
282 	register struct linux_prison *lpr;
283 
284 	lpr = linux_get_prison(p);
285 	if (lpr != NULL) {
286 		lpr->pr_oss_version = oss_version;
287 		mtx_unlock(&p->p_ucred->cr_prison->pr_mtx);
288 	} else {
289 		linux_oss_version = oss_version;
290 	}
291 
292 	return (0);
293 }
294 
295 #ifdef DEBUG
296 
297 u_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))];
298 
299 static int
300 linux_debug(int syscall, int toggle, int global)
301 {
302 
303 	if (global) {
304 		char c = toggle ? 0 : 0xff;
305 
306 		memset(linux_debug_map, c, sizeof(linux_debug_map));
307 		return (0);
308 	}
309 	if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL)
310 		return (EINVAL);
311 	if (toggle)
312 		clrbit(linux_debug_map, syscall);
313 	else
314 		setbit(linux_debug_map, syscall);
315 	return (0);
316 }
317 
318 /*
319  * Usage: sysctl linux.debug=<syscall_nr>.<0/1>
320  *
321  *    E.g.: sysctl linux.debug=21.0
322  *
323  * As a special case, syscall "all" will apply to all syscalls globally.
324  */
325 #define LINUX_MAX_DEBUGSTR	16
326 static int
327 linux_sysctl_debug(SYSCTL_HANDLER_ARGS)
328 {
329 	char value[LINUX_MAX_DEBUGSTR], *p;
330 	int error, sysc, toggle;
331 	int global = 0;
332 
333 	value[0] = '\0';
334 	error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req);
335 	if (error || req->newptr == NULL)
336 		return (error);
337 	for (p = value; *p != '\0' && *p != '.'; p++);
338 	if (*p == '\0')
339 		return (EINVAL);
340 	*p++ = '\0';
341 	sysc = strtol(value, NULL, 0);
342 	toggle = strtol(p, NULL, 0);
343 	if (strcmp(value, "all") == 0)
344 		global = 1;
345 	error = linux_debug(sysc, toggle, global);
346 	return (error);
347 }
348 
349 SYSCTL_PROC(_compat_linux, OID_AUTO, debug,
350             CTLTYPE_STRING | CTLFLAG_RW,
351             0, 0, linux_sysctl_debug, "A",
352             "Linux debugging control");
353 
354 #endif /* DEBUG */
355