linux_mib.c revision 116173
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
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/compat/linux/linux_mib.c 116173 2003-06-10 21:29:12Z obrien $");
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/sysctl.h>
36#include <sys/proc.h>
37#include <sys/malloc.h>
38#include <sys/jail.h>
39#include <sys/lock.h>
40#include <sys/mutex.h>
41
42#include <machine/../linux/linux.h>
43#include <compat/linux/linux_mib.h>
44
45struct linux_prison {
46	char	pr_osname[LINUX_MAX_UTSNAME];
47	char	pr_osrelease[LINUX_MAX_UTSNAME];
48	int	pr_oss_version;
49};
50
51SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0,
52	    "Linux mode");
53
54static struct mtx osname_lock;
55MTX_SYSINIT(linux_osname, &osname_lock, "linux osname", MTX_DEF);
56
57static char	linux_osname[LINUX_MAX_UTSNAME] = "Linux";
58
59static int
60linux_sysctl_osname(SYSCTL_HANDLER_ARGS)
61{
62	char osname[LINUX_MAX_UTSNAME];
63	int error;
64
65	linux_get_osname(req->td, osname);
66	error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req);
67	if (error || req->newptr == NULL)
68		return (error);
69	error = linux_set_osname(req->td, osname);
70	return (error);
71}
72
73SYSCTL_PROC(_compat_linux, OID_AUTO, osname,
74	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON,
75	    0, 0, linux_sysctl_osname, "A",
76	    "Linux kernel OS name");
77
78static char	linux_osrelease[LINUX_MAX_UTSNAME] = "2.4.2";
79
80static int
81linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS)
82{
83	char osrelease[LINUX_MAX_UTSNAME];
84	int error;
85
86	linux_get_osrelease(req->td, osrelease);
87	error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req);
88	if (error || req->newptr == NULL)
89		return (error);
90	error = linux_set_osrelease(req->td, osrelease);
91	return (error);
92}
93
94SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease,
95	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON,
96	    0, 0, linux_sysctl_osrelease, "A",
97	    "Linux kernel OS release");
98
99static int	linux_oss_version = 0x030600;
100
101static int
102linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS)
103{
104	int oss_version;
105	int error;
106
107	oss_version = linux_get_oss_version(req->td);
108	error = sysctl_handle_int(oidp, &oss_version, 0, req);
109	if (error || req->newptr == NULL)
110		return (error);
111	error = linux_set_oss_version(req->td, oss_version);
112	return (error);
113}
114
115SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version,
116	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON,
117	    0, 0, linux_sysctl_oss_version, "I",
118	    "Linux OSS version");
119
120/*
121 * Returns holding the prison mutex if return non-NULL.
122 */
123static struct prison *
124linux_get_prison(struct thread *td)
125{
126	register struct prison *pr;
127	register struct linux_prison *lpr;
128
129	KASSERT(td == curthread, ("linux_get_prison() called on !curthread"));
130	if (!jailed(td->td_ucred))
131		return (NULL);
132	pr = td->td_ucred->cr_prison;
133	mtx_lock(&pr->pr_mtx);
134	if (pr->pr_linux == NULL) {
135		/*
136		 * If we don't have a linux prison structure yet, allocate
137		 * one.  We have to handle the race where another thread
138		 * could be adding a linux prison to this process already.
139		 */
140		mtx_unlock(&pr->pr_mtx);
141		lpr = malloc(sizeof(struct linux_prison), M_PRISON,
142		    M_WAITOK | M_ZERO);
143		mtx_lock(&pr->pr_mtx);
144		if (pr->pr_linux == NULL)
145			pr->pr_linux = lpr;
146		else
147			free(lpr, M_PRISON);
148	}
149	return (pr);
150}
151
152void
153linux_mib_destroy(void)
154{
155
156	mtx_destroy(&osname_lock);
157}
158
159void
160linux_get_osname(struct thread *td, char *dst)
161{
162	register struct prison *pr;
163	register struct linux_prison *lpr;
164
165	pr = td->td_ucred->cr_prison;
166	if (pr != NULL) {
167		mtx_lock(&pr->pr_mtx);
168		if (pr->pr_linux != NULL) {
169			lpr = (struct linux_prison *)pr->pr_linux;
170			if (lpr->pr_osname[0]) {
171				bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME);
172				mtx_unlock(&pr->pr_mtx);
173				return;
174			}
175		}
176		mtx_unlock(&pr->pr_mtx);
177	}
178
179	mtx_lock(&osname_lock);
180	bcopy(linux_osname, dst, LINUX_MAX_UTSNAME);
181	mtx_unlock(&osname_lock);
182}
183
184int
185linux_set_osname(struct thread *td, char *osname)
186{
187	struct prison *pr;
188	struct linux_prison *lpr;
189
190	pr = linux_get_prison(td);
191	if (pr != NULL) {
192		lpr = (struct linux_prison *)pr->pr_linux;
193		strcpy(lpr->pr_osname, osname);
194		mtx_unlock(&pr->pr_mtx);
195	} else {
196		mtx_lock(&osname_lock);
197		strcpy(linux_osname, osname);
198		mtx_unlock(&osname_lock);
199	}
200
201	return (0);
202}
203
204void
205linux_get_osrelease(struct thread *td, char *dst)
206{
207	register struct prison *pr;
208	struct linux_prison *lpr;
209
210	pr = td->td_ucred->cr_prison;
211	if (pr != NULL) {
212		mtx_lock(&pr->pr_mtx);
213		if (pr->pr_linux != NULL) {
214			lpr = (struct linux_prison *)pr->pr_linux;
215			if (lpr->pr_osrelease[0]) {
216				bcopy(lpr->pr_osrelease, dst,
217				    LINUX_MAX_UTSNAME);
218				mtx_unlock(&pr->pr_mtx);
219				return;
220			}
221		}
222		mtx_unlock(&pr->pr_mtx);
223	}
224
225	mtx_lock(&osname_lock);
226	bcopy(linux_osrelease, dst, LINUX_MAX_UTSNAME);
227	mtx_unlock(&osname_lock);
228}
229
230int
231linux_set_osrelease(struct thread *td, char *osrelease)
232{
233	struct prison *pr;
234	struct linux_prison *lpr;
235
236	pr = linux_get_prison(td);
237	if (pr != NULL) {
238		lpr = (struct linux_prison *)pr->pr_linux;
239		strcpy(lpr->pr_osrelease, osrelease);
240		mtx_unlock(&pr->pr_mtx);
241	} else {
242		mtx_lock(&osname_lock);
243		strcpy(linux_osrelease, osrelease);
244		mtx_unlock(&osname_lock);
245	}
246
247	return (0);
248}
249
250int
251linux_get_oss_version(struct thread *td)
252{
253	register struct prison *pr;
254	register struct linux_prison *lpr;
255	int version;
256
257	pr = td->td_ucred->cr_prison;
258	if (pr != NULL) {
259		mtx_lock(&pr->pr_mtx);
260		if (pr->pr_linux != NULL) {
261			lpr = (struct linux_prison *)pr->pr_linux;
262			if (lpr->pr_oss_version) {
263				version = lpr->pr_oss_version;
264				mtx_unlock(&pr->pr_mtx);
265				return (version);
266			}
267		}
268		mtx_unlock(&pr->pr_mtx);
269	}
270
271	mtx_lock(&osname_lock);
272	version = linux_oss_version;
273	mtx_unlock(&osname_lock);
274	return (version);
275}
276
277int
278linux_set_oss_version(struct thread *td, int oss_version)
279{
280	struct prison *pr;
281	struct linux_prison *lpr;
282
283	pr = linux_get_prison(td);
284	if (pr != NULL) {
285		lpr = (struct linux_prison *)pr->pr_linux;
286		lpr->pr_oss_version = oss_version;
287		mtx_unlock(&pr->pr_mtx);
288	} else {
289		mtx_lock(&osname_lock);
290		linux_oss_version = oss_version;
291		mtx_unlock(&osname_lock);
292	}
293
294	return (0);
295}
296
297#ifdef DEBUG
298
299u_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))];
300
301static int
302linux_debug(int syscall, int toggle, int global)
303{
304
305	if (global) {
306		char c = toggle ? 0 : 0xff;
307
308		memset(linux_debug_map, c, sizeof(linux_debug_map));
309		return (0);
310	}
311	if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL)
312		return (EINVAL);
313	if (toggle)
314		clrbit(linux_debug_map, syscall);
315	else
316		setbit(linux_debug_map, syscall);
317	return (0);
318}
319
320/*
321 * Usage: sysctl linux.debug=<syscall_nr>.<0/1>
322 *
323 *    E.g.: sysctl linux.debug=21.0
324 *
325 * As a special case, syscall "all" will apply to all syscalls globally.
326 */
327#define LINUX_MAX_DEBUGSTR	16
328static int
329linux_sysctl_debug(SYSCTL_HANDLER_ARGS)
330{
331	char value[LINUX_MAX_DEBUGSTR], *p;
332	int error, sysc, toggle;
333	int global = 0;
334
335	value[0] = '\0';
336	error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req);
337	if (error || req->newptr == NULL)
338		return (error);
339	for (p = value; *p != '\0' && *p != '.'; p++);
340	if (*p == '\0')
341		return (EINVAL);
342	*p++ = '\0';
343	sysc = strtol(value, NULL, 0);
344	toggle = strtol(p, NULL, 0);
345	if (strcmp(value, "all") == 0)
346		global = 1;
347	error = linux_debug(sysc, toggle, global);
348	return (error);
349}
350
351SYSCTL_PROC(_compat_linux, OID_AUTO, debug,
352            CTLTYPE_STRING | CTLFLAG_RW,
353            0, 0, linux_sysctl_debug, "A",
354            "Linux debugging control");
355
356#endif /* DEBUG */
357