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