150465Smarcel/*-
250465Smarcel * Copyright (c) 1999 Marcel Moolenaar
350465Smarcel * All rights reserved.
450465Smarcel *
550465Smarcel * Redistribution and use in source and binary forms, with or without
650465Smarcel * modification, are permitted provided that the following conditions
750465Smarcel * are met:
850465Smarcel * 1. Redistributions of source code must retain the above copyright
950465Smarcel *    notice, this list of conditions and the following disclaimer
1050465Smarcel *    in this position and unchanged.
1150465Smarcel * 2. Redistributions in binary form must reproduce the above copyright
1250465Smarcel *    notice, this list of conditions and the following disclaimer in the
1350465Smarcel *    documentation and/or other materials provided with the distribution.
1450465Smarcel * 3. The name of the author may not be used to endorse or promote products
1565067Smarcel *    derived from this software without specific prior written permission.
1650465Smarcel *
1750465Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1850465Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1950465Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2050465Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2150465Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2250465Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2350465Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2450465Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2550465Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2650465Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2750465Smarcel */
2850465Smarcel
29116173Sobrien#include <sys/cdefs.h>
30116173Sobrien__FBSDID("$FreeBSD$");
31116173Sobrien
32235063Snetchild#include "opt_compat.h"
33235063Snetchild#include "opt_kdtrace.h"
34235063Snetchild
3550465Smarcel#include <sys/param.h>
3650465Smarcel#include <sys/kernel.h>
37235063Snetchild#include <sys/sdt.h>
3850465Smarcel#include <sys/systm.h>
3950465Smarcel#include <sys/sysctl.h>
4050465Smarcel#include <sys/proc.h>
4150465Smarcel#include <sys/malloc.h>
42191896Sjamie#include <sys/mount.h>
4350465Smarcel#include <sys/jail.h>
4487275Srwatson#include <sys/lock.h>
4587275Srwatson#include <sys/mutex.h>
46191896Sjamie#include <sys/sx.h>
4750465Smarcel
48140214Sobrien#ifdef COMPAT_LINUX32
49140214Sobrien#include <machine/../linux32/linux.h>
50140214Sobrien#else
5164907Smarcel#include <machine/../linux/linux.h>
52133816Stjr#endif
53235063Snetchild#include <compat/linux/linux_dtrace.h>
5464907Smarcel#include <compat/linux/linux_mib.h>
55246085Sjhb#include <compat/linux/linux_misc.h>
5650465Smarcel
57235063Snetchild/* DTrace init */
58235063SnetchildLIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE);
59235063Snetchild
60235063Snetchild/**
61235063Snetchild * DTrace probes in this module.
62235063Snetchild */
63235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osname, entry);
64235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, sysctl_string_error, "int");
65235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, return, "int");
66235063Snetchild
67235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osrelease, entry);
68235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, sysctl_string_error, "int");
69235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, return, "int");
70235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_oss_version, entry);
71235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, sysctl_string_error,
72235063Snetchild    "int");
73235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, return, "int");
74235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_map_osrel, entry, "char *", "int *");
75235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_map_osrel, return, "int");
76235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_get_prison, entry, "struct prison *",
77235063Snetchild    "struct prison **");
78235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_get_prison, return, "struct linux_prison *");
79235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_alloc_prison, entry, "struct prison *",
80235063Snetchild    "struct linux_prison **");
81235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_alloc_prison, return, "int");
82235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_create, entry, "void *", "void *");
83235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, vfs_copyopt_error, "int");
84235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, return, "int");
85235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_check, entry, "void *", "void *");
86235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_copyopt_error, "int");
87235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_getopt_error, "int");
88235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, return, "int");
89235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_set, entry, "void *", "void *");
90235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_copyopt_error, "int");
91235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_getopt_error, "int");
92235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, return, "int");
93235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_get, entry, "void *", "void *");
94235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopt_error, "int");
95235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopts_error, "int");
96235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, return, "int");
97235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_destructor, entry, "void *");
98235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_prison_destructor, return);
99235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, entry);
100235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, return);
101235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, entry);
102235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, return);
103235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_get_osname, entry, "struct thread *",
104235063Snetchild    "char *");
105235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_get_osname, return);
106235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_set_osname, entry, "struct thread *",
107235063Snetchild    "char *");
108235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_set_osname, return, "int");
109235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_get_osrelease, entry, "struct thread *",
110235063Snetchild    "char *");
111235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_get_osrelease, return);
112235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_kernver, entry, "struct thread *");
113235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_kernver, return, "int");
114235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_set_osrelease, entry, "struct thread *",
115235063Snetchild    "char *");
116235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_set_osrelease, return, "int");
117235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, entry, "struct thread *");
118235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, return, "int");
119235063Snetchild
120235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_set_oss_version, entry, "struct thread *",
121235063Snetchild    "int");
122235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_set_oss_version, return, "int");
123235063Snetchild
12450465Smarcelstruct linux_prison {
12550465Smarcel	char	pr_osname[LINUX_MAX_UTSNAME];
12650465Smarcel	char	pr_osrelease[LINUX_MAX_UTSNAME];
12750465Smarcel	int	pr_oss_version;
128191972Sdchagin	int	pr_osrel;
12950465Smarcel};
13050465Smarcel
131192895Sjamiestatic struct linux_prison lprison0 = {
132192895Sjamie	.pr_osname =		"Linux",
133192895Sjamie	.pr_osrelease =		"2.6.16",
134192895Sjamie	.pr_oss_version =	0x030600,
135192895Sjamie	.pr_osrel =		2006016
136192895Sjamie};
137192895Sjamie
138191896Sjamiestatic unsigned linux_osd_jail_slot;
139191896Sjamie
140227309Sedstatic SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0,
14150465Smarcel	    "Linux mode");
14250465Smarcel
143219668Snetchildstatic int	linux_set_osname(struct thread *td, char *osname);
144219668Snetchildstatic int	linux_set_osrelease(struct thread *td, char *osrelease);
145219668Snetchildstatic int	linux_set_oss_version(struct thread *td, int oss_version);
146219668Snetchild
14750465Smarcelstatic int
14862573Sphklinux_sysctl_osname(SYSCTL_HANDLER_ARGS)
14950465Smarcel{
15050465Smarcel	char osname[LINUX_MAX_UTSNAME];
15150465Smarcel	int error;
15250465Smarcel
153235063Snetchild	LIN_SDT_PROBE0(mib, linux_sysctl_osname, entry);
154235063Snetchild
155112206Sjhb	linux_get_osname(req->td, osname);
15650465Smarcel	error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req);
157235063Snetchild	if (error != 0 || req->newptr == NULL) {
158235063Snetchild		LIN_SDT_PROBE1(mib, linux_sysctl_osname, sysctl_string_error,
159235063Snetchild		    error);
160235063Snetchild		LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error);
16150465Smarcel		return (error);
162235063Snetchild	}
163112206Sjhb	error = linux_set_osname(req->td, osname);
164235063Snetchild
165235063Snetchild	LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error);
16650465Smarcel	return (error);
16750465Smarcel}
16850465Smarcel
16950465SmarcelSYSCTL_PROC(_compat_linux, OID_AUTO, osname,
170191792Sjamie	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
17150465Smarcel	    0, 0, linux_sysctl_osname, "A",
17250465Smarcel	    "Linux kernel OS name");
17350465Smarcel
17450465Smarcelstatic int
17562573Sphklinux_sysctl_osrelease(SYSCTL_HANDLER_ARGS)
17650465Smarcel{
17750465Smarcel	char osrelease[LINUX_MAX_UTSNAME];
17850465Smarcel	int error;
17950465Smarcel
180235063Snetchild	LIN_SDT_PROBE0(mib, linux_sysctl_osrelease, entry);
181235063Snetchild
182112206Sjhb	linux_get_osrelease(req->td, osrelease);
18350465Smarcel	error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req);
184235063Snetchild	if (error != 0 || req->newptr == NULL) {
185235063Snetchild		LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, sysctl_string_error,
186235063Snetchild		    error);
187235063Snetchild		LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error);
18850465Smarcel		return (error);
189235063Snetchild	}
190112206Sjhb	error = linux_set_osrelease(req->td, osrelease);
191235063Snetchild
192235063Snetchild	LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error);
19350465Smarcel	return (error);
19450465Smarcel}
19550465Smarcel
19650465SmarcelSYSCTL_PROC(_compat_linux, OID_AUTO, osrelease,
197191792Sjamie	    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
19850465Smarcel	    0, 0, linux_sysctl_osrelease, "A",
19950465Smarcel	    "Linux kernel OS release");
20050465Smarcel
20150465Smarcelstatic int
20262573Sphklinux_sysctl_oss_version(SYSCTL_HANDLER_ARGS)
20350465Smarcel{
20450465Smarcel	int oss_version;
20550465Smarcel	int error;
20650465Smarcel
207235063Snetchild	LIN_SDT_PROBE0(mib, linux_sysctl_oss_version, entry);
208235063Snetchild
209112206Sjhb	oss_version = linux_get_oss_version(req->td);
21050465Smarcel	error = sysctl_handle_int(oidp, &oss_version, 0, req);
211235063Snetchild	if (error != 0 || req->newptr == NULL) {
212235063Snetchild		LIN_SDT_PROBE1(mib, linux_sysctl_oss_version,
213235063Snetchild		    sysctl_string_error, error);
214235063Snetchild		LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error);
21550465Smarcel		return (error);
216235063Snetchild	}
217112206Sjhb	error = linux_set_oss_version(req->td, oss_version);
218235063Snetchild
219235063Snetchild	LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error);
22050465Smarcel	return (error);
22150465Smarcel}
22250465Smarcel
22350465SmarcelSYSCTL_PROC(_compat_linux, OID_AUTO, oss_version,
224191792Sjamie	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
22550465Smarcel	    0, 0, linux_sysctl_oss_version, "I",
22650465Smarcel	    "Linux OSS version");
22750465Smarcel
22887275Srwatson/*
229191972Sdchagin * Map the osrelease into integer
230191972Sdchagin */
231191972Sdchaginstatic int
232191972Sdchaginlinux_map_osrel(char *osrelease, int *osrel)
233191972Sdchagin{
234191972Sdchagin	char *sep, *eosrelease;
235191972Sdchagin	int len, v0, v1, v2, v;
236191972Sdchagin
237235063Snetchild	LIN_SDT_PROBE2(mib, linux_map_osrel, entry, osrelease, osrel);
238235063Snetchild
239191972Sdchagin	len = strlen(osrelease);
240191972Sdchagin	eosrelease = osrelease + len;
241191972Sdchagin	v0 = strtol(osrelease, &sep, 10);
242235063Snetchild	if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') {
243235063Snetchild		LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL);
244191972Sdchagin		return (EINVAL);
245235063Snetchild	}
246191972Sdchagin	osrelease = sep + 1;
247191972Sdchagin	v1 = strtol(osrelease, &sep, 10);
248235063Snetchild	if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') {
249235063Snetchild		LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL);
250191972Sdchagin		return (EINVAL);
251235063Snetchild	}
252191972Sdchagin	osrelease = sep + 1;
253191972Sdchagin	v2 = strtol(osrelease, &sep, 10);
254235063Snetchild	if (osrelease == sep || sep != eosrelease) {
255235063Snetchild		LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL);
256191972Sdchagin		return (EINVAL);
257235063Snetchild	}
258191972Sdchagin
259191972Sdchagin	v = v0 * 1000000 + v1 * 1000 + v2;
260235063Snetchild	if (v < 1000000) {
261235063Snetchild		LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL);
262191972Sdchagin		return (EINVAL);
263235063Snetchild	}
264191972Sdchagin
265191972Sdchagin	*osrel = v;
266235063Snetchild
267235063Snetchild	LIN_SDT_PROBE1(mib, linux_map_osrel, return, 0);
268191972Sdchagin	return (0);
269191972Sdchagin}
270191972Sdchagin
271191972Sdchagin/*
272192895Sjamie * Find a prison with Linux info.
273192895Sjamie * Return the Linux info and the (locked) prison.
27487275Srwatson */
275191896Sjamiestatic struct linux_prison *
276192895Sjamielinux_find_prison(struct prison *spr, struct prison **prp)
27750465Smarcel{
278191896Sjamie	struct prison *pr;
279191896Sjamie	struct linux_prison *lpr;
28050465Smarcel
281235063Snetchild	LIN_SDT_PROBE2(mib, linux_get_prison, entry, spr, prp);
282235063Snetchild
283192895Sjamie	if (!linux_osd_jail_slot)
284192895Sjamie		/* In case osd_register failed. */
285192895Sjamie		spr = &prison0;
286192895Sjamie	for (pr = spr;; pr = pr->pr_parent) {
287192895Sjamie		mtx_lock(&pr->pr_mtx);
288192895Sjamie		lpr = (pr == &prison0)
289192895Sjamie		    ? &lprison0
290192895Sjamie		    : osd_jail_get(pr, linux_osd_jail_slot);
291192895Sjamie		if (lpr != NULL)
292192895Sjamie			break;
293191896Sjamie		mtx_unlock(&pr->pr_mtx);
294192895Sjamie	}
295192895Sjamie	*prp = pr;
296235063Snetchild
297235063Snetchild	LIN_SDT_PROBE1(mib, linux_get_prison, return, lpr);
298191896Sjamie	return (lpr);
299191896Sjamie}
300191896Sjamie
301191896Sjamie/*
302192895Sjamie * Ensure a prison has its own Linux info.  If lprp is non-null, point it to
303192895Sjamie * the Linux info and lock the prison.
304191896Sjamie */
305191896Sjamiestatic int
306191896Sjamielinux_alloc_prison(struct prison *pr, struct linux_prison **lprp)
307191896Sjamie{
308192895Sjamie	struct prison *ppr;
309191896Sjamie	struct linux_prison *lpr, *nlpr;
310191896Sjamie	int error;
311191896Sjamie
312235063Snetchild	LIN_SDT_PROBE2(mib, linux_alloc_prison, entry, pr, lprp);
313235063Snetchild
314191896Sjamie	/* If this prison already has Linux info, return that. */
315191896Sjamie	error = 0;
316192895Sjamie	lpr = linux_find_prison(pr, &ppr);
317192895Sjamie	if (ppr == pr)
318191896Sjamie		goto done;
319191896Sjamie	/*
320191896Sjamie	 * Allocate a new info record.  Then check again, in case something
321191896Sjamie	 * changed during the allocation.
322191896Sjamie	 */
323192895Sjamie	mtx_unlock(&ppr->pr_mtx);
324191896Sjamie	nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK);
325192895Sjamie	lpr = linux_find_prison(pr, &ppr);
326192895Sjamie	if (ppr == pr) {
327191896Sjamie		free(nlpr, M_PRISON);
328191896Sjamie		goto done;
329191896Sjamie	}
330192895Sjamie	/* Inherit the initial values from the ancestor. */
331192895Sjamie	mtx_lock(&pr->pr_mtx);
332191896Sjamie	error = osd_jail_set(pr, linux_osd_jail_slot, nlpr);
333192895Sjamie	if (error == 0) {
334192895Sjamie		bcopy(lpr, nlpr, sizeof(*lpr));
335192895Sjamie		lpr = nlpr;
336192895Sjamie	} else {
337191896Sjamie		free(nlpr, M_PRISON);
338192895Sjamie		lpr = NULL;
339191896Sjamie	}
340192895Sjamie	mtx_unlock(&ppr->pr_mtx);
341192895Sjamie done:
342191896Sjamie	if (lprp != NULL)
343191896Sjamie		*lprp = lpr;
344192895Sjamie	else
345192895Sjamie		mtx_unlock(&pr->pr_mtx);
346235063Snetchild
347235063Snetchild	LIN_SDT_PROBE1(mib, linux_alloc_prison, return, error);
348191896Sjamie	return (error);
349191896Sjamie}
350191896Sjamie
351191896Sjamie/*
352191896Sjamie * Jail OSD methods for Linux prison data.
353191896Sjamie */
354191896Sjamiestatic int
355191896Sjamielinux_prison_create(void *obj, void *data)
356191896Sjamie{
357191896Sjamie	struct prison *pr = obj;
358191896Sjamie	struct vfsoptlist *opts = data;
359235063Snetchild	int jsys, error;
360191896Sjamie
361235063Snetchild	LIN_SDT_PROBE2(mib, linux_prison_create, entry, obj, data);
362235063Snetchild
363235063Snetchild	error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
364235063Snetchild	if (error != 0) {
365235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_create, vfs_copyopt_error,
366235063Snetchild		    error);
367235063Snetchild	} else if (jsys == JAIL_SYS_INHERIT) {
368235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_create, return, 0);
369191896Sjamie		return (0);
370235063Snetchild	}
371191896Sjamie	/*
372191896Sjamie	 * Inherit a prison's initial values from its parent
373195870Sjamie	 * (different from JAIL_SYS_INHERIT which also inherits changes).
374191896Sjamie	 */
375235063Snetchild	error = linux_alloc_prison(pr, NULL);
376235063Snetchild
377235063Snetchild	LIN_SDT_PROBE1(mib, linux_prison_create, return, error);
378235063Snetchild	return (error);
379191896Sjamie}
380191896Sjamie
381191896Sjamiestatic int
382191896Sjamielinux_prison_check(void *obj __unused, void *data)
383191896Sjamie{
384191896Sjamie	struct vfsoptlist *opts = data;
385191896Sjamie	char *osname, *osrelease;
386195870Sjamie	int error, jsys, len, osrel, oss_version;
387191896Sjamie
388235063Snetchild	LIN_SDT_PROBE2(mib, linux_prison_check, entry, obj, data);
389235063Snetchild
390191896Sjamie	/* Check that the parameters are correct. */
391195870Sjamie	error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
392235063Snetchild	if (error != 0) {
393235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error,
394235063Snetchild		    error);
395235063Snetchild	}
396195870Sjamie	if (error != ENOENT) {
397235063Snetchild		if (error != 0) {
398235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, error);
399195870Sjamie			return (error);
400235063Snetchild		}
401235063Snetchild		if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT) {
402235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL);
403195870Sjamie			return (EINVAL);
404235063Snetchild		}
405195870Sjamie	}
406191896Sjamie	error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len);
407235063Snetchild	if (error != 0) {
408235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error,
409235063Snetchild		    error);
410235063Snetchild	}
411191896Sjamie	if (error != ENOENT) {
412235063Snetchild		if (error != 0) {
413235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, error);
414191896Sjamie			return (error);
415235063Snetchild		}
416235063Snetchild		if (len == 0 || osname[len - 1] != '\0') {
417235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL);
418191896Sjamie			return (EINVAL);
419235063Snetchild		}
420191896Sjamie		if (len > LINUX_MAX_UTSNAME) {
421191896Sjamie			vfs_opterror(opts, "linux.osname too long");
422235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return,
423235063Snetchild			    ENAMETOOLONG);
424191896Sjamie			return (ENAMETOOLONG);
425191896Sjamie		}
426191896Sjamie	}
427191896Sjamie	error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len);
428235063Snetchild	if (error != 0) {
429235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error,
430235063Snetchild		    error);
431235063Snetchild	}
432191896Sjamie	if (error != ENOENT) {
433235063Snetchild		if (error != 0) {
434235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, error);
435191896Sjamie			return (error);
436235063Snetchild		}
437235063Snetchild		if (len == 0 || osrelease[len - 1] != '\0') {
438235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL);
439191896Sjamie			return (EINVAL);
440235063Snetchild		}
441191896Sjamie		if (len > LINUX_MAX_UTSNAME) {
442191896Sjamie			vfs_opterror(opts, "linux.osrelease too long");
443235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return,
444235063Snetchild			    ENAMETOOLONG);
445191896Sjamie			return (ENAMETOOLONG);
446191896Sjamie		}
447192895Sjamie		error = linux_map_osrel(osrelease, &osrel);
448192895Sjamie		if (error != 0) {
449192895Sjamie			vfs_opterror(opts, "linux.osrelease format error");
450235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_check, return, error);
451192895Sjamie			return (error);
452192895Sjamie		}
453191896Sjamie	}
454191896Sjamie	error = vfs_copyopt(opts, "linux.oss_version", &oss_version,
455191896Sjamie	    sizeof(oss_version));
456235063Snetchild	if (error != 0)
457235063Snetchild	    LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, error);
458235063Snetchild
459235063Snetchild	if (error == ENOENT)
460235063Snetchild		error = 0;
461235063Snetchild	LIN_SDT_PROBE1(mib, linux_prison_check, return, error);
462235063Snetchild	return (error);
463191896Sjamie}
464191896Sjamie
465191896Sjamiestatic int
466191896Sjamielinux_prison_set(void *obj, void *data)
467191896Sjamie{
468191896Sjamie	struct linux_prison *lpr;
469191896Sjamie	struct prison *pr = obj;
470191896Sjamie	struct vfsoptlist *opts = data;
471191896Sjamie	char *osname, *osrelease;
472195870Sjamie	int error, gotversion, jsys, len, oss_version;
473191896Sjamie
474235063Snetchild	LIN_SDT_PROBE2(mib, linux_prison_set, entry, obj, data);
475235063Snetchild
476191896Sjamie	/* Set the parameters, which should be correct. */
477195870Sjamie	error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
478235063Snetchild	if (error != 0)
479235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error);
480195870Sjamie	if (error == ENOENT)
481195870Sjamie		jsys = -1;
482191896Sjamie	error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len);
483235063Snetchild	if (error != 0)
484235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error);
485191896Sjamie	if (error == ENOENT)
486191896Sjamie		osname = NULL;
487191896Sjamie	else
488195870Sjamie		jsys = JAIL_SYS_NEW;
489191896Sjamie	error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len);
490235063Snetchild	if (error != 0)
491235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error);
492191896Sjamie	if (error == ENOENT)
493191896Sjamie		osrelease = NULL;
494191896Sjamie	else
495195870Sjamie		jsys = JAIL_SYS_NEW;
496191896Sjamie	error = vfs_copyopt(opts, "linux.oss_version", &oss_version,
497191896Sjamie	    sizeof(oss_version));
498235063Snetchild	if (error != 0)
499235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error);
500195870Sjamie	if (error == ENOENT)
501195870Sjamie		gotversion = 0;
502195870Sjamie	else {
503195870Sjamie		gotversion = 1;
504195870Sjamie		jsys = JAIL_SYS_NEW;
505195870Sjamie	}
506195870Sjamie	switch (jsys) {
507195870Sjamie	case JAIL_SYS_INHERIT:
508195870Sjamie		/* "linux=inherit": inherit the parent's Linux info. */
509191896Sjamie		mtx_lock(&pr->pr_mtx);
510191896Sjamie		osd_jail_del(pr, linux_osd_jail_slot);
511191896Sjamie		mtx_unlock(&pr->pr_mtx);
512195870Sjamie		break;
513195870Sjamie	case JAIL_SYS_NEW:
514112206Sjhb		/*
515195870Sjamie		 * "linux=new" or "linux.*":
516191896Sjamie		 * the prison gets its own Linux info.
517112206Sjhb		 */
518191896Sjamie		error = linux_alloc_prison(pr, &lpr);
519191896Sjamie		if (error) {
520191896Sjamie			mtx_unlock(&pr->pr_mtx);
521235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_set, return, error);
522191896Sjamie			return (error);
523191896Sjamie		}
524191896Sjamie		if (osrelease) {
525191972Sdchagin			error = linux_map_osrel(osrelease, &lpr->pr_osrel);
526191972Sdchagin			if (error) {
527191972Sdchagin				mtx_unlock(&pr->pr_mtx);
528235063Snetchild				LIN_SDT_PROBE1(mib, linux_prison_set, return,
529235063Snetchild				    error);
530191972Sdchagin				return (error);
531191972Sdchagin			}
532191896Sjamie			strlcpy(lpr->pr_osrelease, osrelease,
533191896Sjamie			    LINUX_MAX_UTSNAME);
534191896Sjamie		}
535191972Sdchagin		if (osname)
536191972Sdchagin			strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME);
537191896Sjamie		if (gotversion)
538191896Sjamie			lpr->pr_oss_version = oss_version;
53987275Srwatson		mtx_unlock(&pr->pr_mtx);
54050465Smarcel	}
541235063Snetchild
542235063Snetchild	LIN_SDT_PROBE1(mib, linux_prison_set, return, 0);
543191896Sjamie	return (0);
54450465Smarcel}
54550465Smarcel
546195870SjamieSYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters");
547191896SjamieSYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME,
548191896Sjamie    "Jail Linux kernel OS name");
549191896SjamieSYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME,
550191896Sjamie    "Jail Linux kernel OS release");
551191896SjamieSYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW,
552191896Sjamie    "I", "Jail Linux OSS version");
553191896Sjamie
554191896Sjamiestatic int
555191896Sjamielinux_prison_get(void *obj, void *data)
556191896Sjamie{
557191896Sjamie	struct linux_prison *lpr;
558192895Sjamie	struct prison *ppr;
559191896Sjamie	struct prison *pr = obj;
560191896Sjamie	struct vfsoptlist *opts = data;
561191896Sjamie	int error, i;
562191896Sjamie
563192895Sjamie	static int version0;
564192895Sjamie
565235063Snetchild	LIN_SDT_PROBE2(mib, linux_prison_get, entry, obj, data);
566235063Snetchild
567192895Sjamie	/* See if this prison is the one with the Linux info. */
568192895Sjamie	lpr = linux_find_prison(pr, &ppr);
569195870Sjamie	i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
570191896Sjamie	error = vfs_setopt(opts, "linux", &i, sizeof(i));
571235063Snetchild	if (error != 0) {
572235063Snetchild		LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, error);
573235063Snetchild		if (error != ENOENT)
574235063Snetchild			goto done;
575235063Snetchild	}
576192895Sjamie	if (i) {
577195870Sjamie		error = vfs_setopts(opts, "linux.osname", lpr->pr_osname);
578235063Snetchild		if (error != 0) {
579235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error,
580235063Snetchild			    error);
581235063Snetchild			if (error != ENOENT)
582235063Snetchild				goto done;
583235063Snetchild		}
584195870Sjamie		error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease);
585235063Snetchild		if (error != 0) {
586235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error,
587235063Snetchild			    error);
588235063Snetchild			if (error != ENOENT)
589235063Snetchild				goto done;
590235063Snetchild		}
591195870Sjamie		error = vfs_setopt(opts, "linux.oss_version",
592195870Sjamie		    &lpr->pr_oss_version, sizeof(lpr->pr_oss_version));
593235063Snetchild		if (error != 0) {
594235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error,
595235063Snetchild			    error);
596235063Snetchild			if(error != ENOENT)
597235063Snetchild				goto done;
598235063Snetchild		}
599195870Sjamie	} else {
600192895Sjamie		/*
601192895Sjamie		 * If this prison is inheriting its Linux info, report
602192895Sjamie		 * empty/zero parameters.
603192895Sjamie		 */
604192895Sjamie		error = vfs_setopts(opts, "linux.osname", "");
605235063Snetchild		if (error != 0) {
606235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error,
607235063Snetchild			    error);
608235063Snetchild			if(error != ENOENT)
609235063Snetchild				goto done;
610235063Snetchild		}
611192895Sjamie		error = vfs_setopts(opts, "linux.osrelease", "");
612235063Snetchild		if (error != 0) {
613235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error,
614235063Snetchild			    error);
615235063Snetchild			if(error != ENOENT)
616235063Snetchild				goto done;
617235063Snetchild		}
618192895Sjamie		error = vfs_setopt(opts, "linux.oss_version", &version0,
619192895Sjamie		    sizeof(lpr->pr_oss_version));
620235063Snetchild		if (error != 0) {
621235063Snetchild			LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error,
622235063Snetchild			    error);
623235063Snetchild			if(error != ENOENT)
624235063Snetchild				goto done;
625235063Snetchild		}
626191896Sjamie	}
627191896Sjamie	error = 0;
628191896Sjamie
629191896Sjamie done:
630192895Sjamie	mtx_unlock(&ppr->pr_mtx);
631235063Snetchild
632235063Snetchild	LIN_SDT_PROBE1(mib, linux_prison_get, return, error);
633191896Sjamie	return (error);
634191896Sjamie}
635191896Sjamie
636191896Sjamiestatic void
637191896Sjamielinux_prison_destructor(void *data)
638191896Sjamie{
639191896Sjamie
640235063Snetchild	LIN_SDT_PROBE1(mib, linux_prison_destructor, entry, data);
641191896Sjamie	free(data, M_PRISON);
642235063Snetchild	LIN_SDT_PROBE0(mib, linux_prison_destructor, return);
643191896Sjamie}
644191896Sjamie
64587275Srwatsonvoid
646191896Sjamielinux_osd_jail_register(void)
64750465Smarcel{
648191896Sjamie	struct prison *pr;
649191896Sjamie	osd_method_t methods[PR_MAXMETHOD] = {
650191896Sjamie	    [PR_METHOD_CREATE] =	linux_prison_create,
651191896Sjamie	    [PR_METHOD_GET] =		linux_prison_get,
652191896Sjamie	    [PR_METHOD_SET] =		linux_prison_set,
653191896Sjamie	    [PR_METHOD_CHECK] =		linux_prison_check
654191896Sjamie	};
65550465Smarcel
656235063Snetchild	LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry);
657235063Snetchild
658191896Sjamie	linux_osd_jail_slot =
659191896Sjamie	    osd_jail_register(linux_prison_destructor, methods);
660191896Sjamie	if (linux_osd_jail_slot > 0) {
661191896Sjamie		/* Copy the system linux info to any current prisons. */
662191896Sjamie		sx_xlock(&allprison_lock);
663192895Sjamie		TAILQ_FOREACH(pr, &allprison, pr_list)
664191896Sjamie			(void)linux_alloc_prison(pr, NULL);
665191896Sjamie		sx_xunlock(&allprison_lock);
66687275Srwatson	}
667235063Snetchild
668235063Snetchild	LIN_SDT_PROBE0(mib, linux_osd_jail_register, return);
669191896Sjamie}
67087275Srwatson
671191896Sjamievoid
672191896Sjamielinux_osd_jail_deregister(void)
673191896Sjamie{
674191896Sjamie
675235063Snetchild	LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry);
676235063Snetchild
677191896Sjamie	if (linux_osd_jail_slot)
678191896Sjamie		osd_jail_deregister(linux_osd_jail_slot);
679235063Snetchild
680235063Snetchild	LIN_SDT_PROBE0(mib, linux_osd_jail_register, return);
68150465Smarcel}
68250465Smarcel
683191896Sjamievoid
684191896Sjamielinux_get_osname(struct thread *td, char *dst)
685191896Sjamie{
686191896Sjamie	struct prison *pr;
687191896Sjamie	struct linux_prison *lpr;
688191896Sjamie
689235063Snetchild	LIN_SDT_PROBE2(mib, linux_get_osname, entry, td, dst);
690235063Snetchild
691192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
692192895Sjamie	bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME);
693192895Sjamie	mtx_unlock(&pr->pr_mtx);
694235063Snetchild
695235063Snetchild	LIN_SDT_PROBE0(mib, linux_get_osname, return);
696191896Sjamie}
697191896Sjamie
698219668Snetchildstatic int
699112206Sjhblinux_set_osname(struct thread *td, char *osname)
70050465Smarcel{
701112206Sjhb	struct prison *pr;
702112206Sjhb	struct linux_prison *lpr;
70350465Smarcel
704235063Snetchild	LIN_SDT_PROBE2(mib, linux_set_osname, entry, td, osname);
705235063Snetchild
706192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
707192895Sjamie	strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME);
708192895Sjamie	mtx_unlock(&pr->pr_mtx);
709235063Snetchild
710235063Snetchild	LIN_SDT_PROBE1(mib, linux_set_osname, return, 0);
71150465Smarcel	return (0);
71250465Smarcel}
71350465Smarcel
71487275Srwatsonvoid
715112206Sjhblinux_get_osrelease(struct thread *td, char *dst)
71650465Smarcel{
717191896Sjamie	struct prison *pr;
71887275Srwatson	struct linux_prison *lpr;
71950465Smarcel
720235063Snetchild	LIN_SDT_PROBE2(mib, linux_get_osrelease, entry, td, dst);
721235063Snetchild
722192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
723192895Sjamie	bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME);
724192895Sjamie	mtx_unlock(&pr->pr_mtx);
725235063Snetchild
726235063Snetchild	LIN_SDT_PROBE0(mib, linux_get_osrelease, return);
72750465Smarcel}
72850465Smarcel
72950465Smarcelint
730191972Sdchaginlinux_kernver(struct thread *td)
731165687Snetchild{
732165687Snetchild	struct prison *pr;
733165687Snetchild	struct linux_prison *lpr;
734191972Sdchagin	int osrel;
735165687Snetchild
736235063Snetchild	LIN_SDT_PROBE1(mib, linux_kernver, entry, td);
737235063Snetchild
738192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
739192895Sjamie	osrel = lpr->pr_osrel;
740192895Sjamie	mtx_unlock(&pr->pr_mtx);
741235063Snetchild
742235063Snetchild	LIN_SDT_PROBE1(mib, linux_kernver, return, osrel);
743191972Sdchagin	return (osrel);
744165687Snetchild}
745165687Snetchild
746219668Snetchildstatic int
747112206Sjhblinux_set_osrelease(struct thread *td, char *osrelease)
74850465Smarcel{
749112206Sjhb	struct prison *pr;
750112206Sjhb	struct linux_prison *lpr;
751191972Sdchagin	int error;
75250465Smarcel
753235063Snetchild	LIN_SDT_PROBE2(mib, linux_set_osrelease, entry, td, osrelease);
754235063Snetchild
755192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
756192895Sjamie	error = linux_map_osrel(osrelease, &lpr->pr_osrel);
757192895Sjamie	if (error == 0)
758191896Sjamie		strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME);
759192895Sjamie	mtx_unlock(&pr->pr_mtx);
760235063Snetchild
761235063Snetchild	LIN_SDT_PROBE1(mib, linux_set_osrelease, return, error);
762192895Sjamie	return (error);
76350465Smarcel}
76450465Smarcel
76550465Smarcelint
766112206Sjhblinux_get_oss_version(struct thread *td)
76750465Smarcel{
768191896Sjamie	struct prison *pr;
769191896Sjamie	struct linux_prison *lpr;
77087275Srwatson	int version;
77150465Smarcel
772235063Snetchild	LIN_SDT_PROBE1(mib, linux_get_oss_version, entry, td);
773235063Snetchild
774192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
775192895Sjamie	version = lpr->pr_oss_version;
776192895Sjamie	mtx_unlock(&pr->pr_mtx);
777235063Snetchild
778235063Snetchild	LIN_SDT_PROBE1(mib, linux_get_oss_version, return, version);
77987275Srwatson	return (version);
78050465Smarcel}
78150465Smarcel
782219668Snetchildstatic int
783112206Sjhblinux_set_oss_version(struct thread *td, int oss_version)
78450465Smarcel{
785112206Sjhb	struct prison *pr;
786112206Sjhb	struct linux_prison *lpr;
78750465Smarcel
788235063Snetchild	LIN_SDT_PROBE2(mib, linux_set_oss_version, entry, td, oss_version);
789235063Snetchild
790192895Sjamie	lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
791192895Sjamie	lpr->pr_oss_version = oss_version;
792192895Sjamie	mtx_unlock(&pr->pr_mtx);
793235063Snetchild
794235063Snetchild	LIN_SDT_PROBE1(mib, linux_set_oss_version, return, 0);
79550465Smarcel	return (0);
79650465Smarcel}
79772543Sjlemon
798191877Sdchagin#if defined(DEBUG) || defined(KTR)
799235063Snetchild/* XXX: can be removed when every ldebug(...) and KTR stuff are removed. */
80072543Sjlemon
80178264Speteru_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))];
80272543Sjlemon
80378258Speterstatic int
80472543Sjlemonlinux_debug(int syscall, int toggle, int global)
80572543Sjlemon{
80672543Sjlemon
80772543Sjlemon	if (global) {
80872543Sjlemon		char c = toggle ? 0 : 0xff;
80972543Sjlemon
81072543Sjlemon		memset(linux_debug_map, c, sizeof(linux_debug_map));
81172543Sjlemon		return (0);
81272543Sjlemon	}
81372543Sjlemon	if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL)
81472543Sjlemon		return (EINVAL);
81572543Sjlemon	if (toggle)
81672543Sjlemon		clrbit(linux_debug_map, syscall);
81772543Sjlemon	else
81872543Sjlemon		setbit(linux_debug_map, syscall);
81972543Sjlemon	return (0);
82072543Sjlemon}
82172543Sjlemon
82272543Sjlemon/*
82396398Sdd * Usage: sysctl linux.debug=<syscall_nr>.<0/1>
82472543Sjlemon *
82596398Sdd *    E.g.: sysctl linux.debug=21.0
82672543Sjlemon *
82772543Sjlemon * As a special case, syscall "all" will apply to all syscalls globally.
82872543Sjlemon */
82972543Sjlemon#define LINUX_MAX_DEBUGSTR	16
83072543Sjlemonstatic int
83172543Sjlemonlinux_sysctl_debug(SYSCTL_HANDLER_ARGS)
83272543Sjlemon{
83372543Sjlemon	char value[LINUX_MAX_DEBUGSTR], *p;
83472543Sjlemon	int error, sysc, toggle;
83572543Sjlemon	int global = 0;
83672543Sjlemon
83772543Sjlemon	value[0] = '\0';
83872543Sjlemon	error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req);
83972543Sjlemon	if (error || req->newptr == NULL)
84072543Sjlemon		return (error);
84172543Sjlemon	for (p = value; *p != '\0' && *p != '.'; p++);
84272543Sjlemon	if (*p == '\0')
84372543Sjlemon		return (EINVAL);
84472543Sjlemon	*p++ = '\0';
84572543Sjlemon	sysc = strtol(value, NULL, 0);
84672543Sjlemon	toggle = strtol(p, NULL, 0);
84772543Sjlemon	if (strcmp(value, "all") == 0)
84872543Sjlemon		global = 1;
84972543Sjlemon	error = linux_debug(sysc, toggle, global);
85072543Sjlemon	return (error);
85172543Sjlemon}
85272543Sjlemon
85372543SjlemonSYSCTL_PROC(_compat_linux, OID_AUTO, debug,
85472543Sjlemon            CTLTYPE_STRING | CTLFLAG_RW,
85572543Sjlemon            0, 0, linux_sysctl_debug, "A",
85672543Sjlemon            "Linux debugging control");
85772543Sjlemon
858191877Sdchagin#endif /* DEBUG || KTR */
859