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$"); 31 32 #include "opt_compat.h" 33 34 #include <sys/param.h> 35 #include <sys/kernel.h> 36 #include <sys/sdt.h> 37 #include <sys/systm.h> 38 #include <sys/sysctl.h> 39 #include <sys/proc.h> 40 #include <sys/malloc.h> 41 #include <sys/mount.h> 42 #include <sys/jail.h> 43 #include <sys/lock.h> 44 #include <sys/mutex.h> 45 #include <sys/sx.h> 46 47 #ifdef COMPAT_LINUX32 48 #include <machine/../linux32/linux.h> 49 #else 50 #include <machine/../linux/linux.h> 51 #endif 52 #include <compat/linux/linux_dtrace.h> 53 #include <compat/linux/linux_mib.h> 54 #include <compat/linux/linux_misc.h> 55 56 /* DTrace init */ 57 LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); 58 59 /** 60 * DTrace probes in this module. 61 */ 62 LIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osname, entry); 63 LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, sysctl_string_error, "int"); 64 LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, return, "int"); 65 66 LIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osrelease, entry); 67 LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, sysctl_string_error, "int"); 68 LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, return, "int"); 69 LIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_oss_version, entry); 70 LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, sysctl_string_error, 71 "int"); 72 LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, return, "int"); 73 LIN_SDT_PROBE_DEFINE2(mib, linux_map_osrel, entry, "char *", "int *"); 74 LIN_SDT_PROBE_DEFINE1(mib, linux_map_osrel, return, "int"); 75 LIN_SDT_PROBE_DEFINE2(mib, linux_get_prison, entry, "struct prison *", 76 "struct prison **"); 77 LIN_SDT_PROBE_DEFINE1(mib, linux_get_prison, return, "struct linux_prison *"); 78 LIN_SDT_PROBE_DEFINE2(mib, linux_alloc_prison, entry, "struct prison *", 79 "struct linux_prison **"); 80 LIN_SDT_PROBE_DEFINE1(mib, linux_alloc_prison, return, "int"); 81 LIN_SDT_PROBE_DEFINE2(mib, linux_prison_create, entry, "void *", "void *"); 82 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, vfs_copyopt_error, "int"); 83 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, return, "int"); 84 LIN_SDT_PROBE_DEFINE2(mib, linux_prison_check, entry, "void *", "void *"); 85 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_copyopt_error, "int"); 86 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_getopt_error, "int"); 87 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, return, "int"); 88 LIN_SDT_PROBE_DEFINE2(mib, linux_prison_set, entry, "void *", "void *"); 89 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_copyopt_error, "int"); 90 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_getopt_error, "int"); 91 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, return, "int"); 92 LIN_SDT_PROBE_DEFINE2(mib, linux_prison_get, entry, "void *", "void *"); 93 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopt_error, "int"); 94 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopts_error, "int"); 95 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, return, "int"); 96 LIN_SDT_PROBE_DEFINE1(mib, linux_prison_destructor, entry, "void *"); 97 LIN_SDT_PROBE_DEFINE0(mib, linux_prison_destructor, return); 98 LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, entry); 99 LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, return); 100 LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, entry); 101 LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, return); 102 LIN_SDT_PROBE_DEFINE2(mib, linux_get_osname, entry, "struct thread *", 103 "char *"); 104 LIN_SDT_PROBE_DEFINE0(mib, linux_get_osname, return); 105 LIN_SDT_PROBE_DEFINE2(mib, linux_set_osname, entry, "struct thread *", 106 "char *"); 107 LIN_SDT_PROBE_DEFINE1(mib, linux_set_osname, return, "int"); 108 LIN_SDT_PROBE_DEFINE2(mib, linux_get_osrelease, entry, "struct thread *", 109 "char *"); 110 LIN_SDT_PROBE_DEFINE0(mib, linux_get_osrelease, return); 111 LIN_SDT_PROBE_DEFINE1(mib, linux_kernver, entry, "struct thread *"); 112 LIN_SDT_PROBE_DEFINE1(mib, linux_kernver, return, "int"); 113 LIN_SDT_PROBE_DEFINE2(mib, linux_set_osrelease, entry, "struct thread *", 114 "char *"); 115 LIN_SDT_PROBE_DEFINE1(mib, linux_set_osrelease, return, "int"); 116 LIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, entry, "struct thread *"); 117 LIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, return, "int"); 118 119 LIN_SDT_PROBE_DEFINE2(mib, linux_set_oss_version, entry, "struct thread *", 120 "int"); 121 LIN_SDT_PROBE_DEFINE1(mib, linux_set_oss_version, return, "int"); 122 123 struct linux_prison { 124 char pr_osname[LINUX_MAX_UTSNAME]; 125 char pr_osrelease[LINUX_MAX_UTSNAME]; 126 int pr_oss_version; 127 int pr_osrel; 128 }; 129 130 static struct linux_prison lprison0 = { 131 .pr_osname = "Linux", 132 .pr_osrelease = "2.6.18", 133 .pr_oss_version = 0x030600, 134 .pr_osrel = 2006018 135 }; 136 137 static unsigned linux_osd_jail_slot; 138 139 static SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, 140 "Linux mode"); 141 142 static int linux_set_osname(struct thread *td, char *osname); 143 static int linux_set_osrelease(struct thread *td, char *osrelease); 144 static int linux_set_oss_version(struct thread *td, int oss_version); 145 146 static int 147 linux_sysctl_osname(SYSCTL_HANDLER_ARGS) 148 { 149 char osname[LINUX_MAX_UTSNAME]; 150 int error; 151 152 LIN_SDT_PROBE0(mib, linux_sysctl_osname, entry); 153 154 linux_get_osname(req->td, osname); 155 error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req); 156 if (error != 0 || req->newptr == NULL) { 157 LIN_SDT_PROBE1(mib, linux_sysctl_osname, sysctl_string_error, 158 error); 159 LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error); 160 return (error); 161 } 162 error = linux_set_osname(req->td, osname); 163 164 LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error); 165 return (error); 166 } 167 168 SYSCTL_PROC(_compat_linux, OID_AUTO, osname, 169 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 170 0, 0, linux_sysctl_osname, "A", 171 "Linux kernel OS name"); 172 173 static int 174 linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS) 175 { 176 char osrelease[LINUX_MAX_UTSNAME]; 177 int error; 178 179 LIN_SDT_PROBE0(mib, linux_sysctl_osrelease, entry); 180 181 linux_get_osrelease(req->td, osrelease); 182 error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req); 183 if (error != 0 || req->newptr == NULL) { 184 LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, sysctl_string_error, 185 error); 186 LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error); 187 return (error); 188 } 189 error = linux_set_osrelease(req->td, osrelease); 190 191 LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error); 192 return (error); 193 } 194 195 SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease, 196 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 197 0, 0, linux_sysctl_osrelease, "A", 198 "Linux kernel OS release"); 199 200 static int 201 linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS) 202 { 203 int oss_version; 204 int error; 205 206 LIN_SDT_PROBE0(mib, linux_sysctl_oss_version, entry); 207 208 oss_version = linux_get_oss_version(req->td); 209 error = sysctl_handle_int(oidp, &oss_version, 0, req); 210 if (error != 0 || req->newptr == NULL) { 211 LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, 212 sysctl_string_error, error); 213 LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error); 214 return (error); 215 } 216 error = linux_set_oss_version(req->td, oss_version); 217 218 LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error); 219 return (error); 220 } 221 222 SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version, 223 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 224 0, 0, linux_sysctl_oss_version, "I", 225 "Linux OSS version"); 226 227 /* 228 * Map the osrelease into integer 229 */ 230 static int 231 linux_map_osrel(char *osrelease, int *osrel) 232 { 233 char *sep, *eosrelease; 234 int len, v0, v1, v2, v; 235 236 LIN_SDT_PROBE2(mib, linux_map_osrel, entry, osrelease, osrel); 237 238 len = strlen(osrelease); 239 eosrelease = osrelease + len; 240 v0 = strtol(osrelease, &sep, 10); 241 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') { 242 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 243 return (EINVAL); 244 } 245 osrelease = sep + 1; 246 v1 = strtol(osrelease, &sep, 10); 247 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') { 248 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 249 return (EINVAL); 250 } 251 osrelease = sep + 1; 252 v2 = strtol(osrelease, &sep, 10); 253 if (osrelease == sep || sep != eosrelease) { 254 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 255 return (EINVAL); 256 } 257 258 v = v0 * 1000000 + v1 * 1000 + v2; 259 if (v < 1000000) { 260 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 261 return (EINVAL); 262 } 263 264 *osrel = v; 265 266 LIN_SDT_PROBE1(mib, linux_map_osrel, return, 0); 267 return (0); 268 } 269 270 /* 271 * Find a prison with Linux info. 272 * Return the Linux info and the (locked) prison. 273 */ 274 static struct linux_prison * 275 linux_find_prison(struct prison *spr, struct prison **prp) 276 { 277 struct prison *pr; 278 struct linux_prison *lpr; 279 280 LIN_SDT_PROBE2(mib, linux_get_prison, entry, spr, prp); 281 282 if (!linux_osd_jail_slot) 283 /* In case osd_register failed. */ 284 spr = &prison0; 285 for (pr = spr;; pr = pr->pr_parent) { 286 mtx_lock(&pr->pr_mtx); 287 lpr = (pr == &prison0) 288 ? &lprison0 289 : osd_jail_get(pr, linux_osd_jail_slot); 290 if (lpr != NULL) 291 break; 292 mtx_unlock(&pr->pr_mtx); 293 } 294 *prp = pr; 295 296 LIN_SDT_PROBE1(mib, linux_get_prison, return, lpr); 297 return (lpr); 298 } 299 300 /* 301 * Ensure a prison has its own Linux info. If lprp is non-null, point it to 302 * the Linux info and lock the prison. 303 */ 304 static int 305 linux_alloc_prison(struct prison *pr, struct linux_prison **lprp) 306 { 307 struct prison *ppr; 308 struct linux_prison *lpr, *nlpr; 309 int error; 310 311 LIN_SDT_PROBE2(mib, linux_alloc_prison, entry, pr, lprp); 312 313 /* If this prison already has Linux info, return that. */ 314 error = 0; 315 lpr = linux_find_prison(pr, &ppr); 316 if (ppr == pr) 317 goto done; 318 /* 319 * Allocate a new info record. Then check again, in case something 320 * changed during the allocation. 321 */ 322 mtx_unlock(&ppr->pr_mtx); 323 nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK); 324 lpr = linux_find_prison(pr, &ppr); 325 if (ppr == pr) { 326 free(nlpr, M_PRISON); 327 goto done; 328 } 329 /* Inherit the initial values from the ancestor. */ 330 mtx_lock(&pr->pr_mtx); 331 error = osd_jail_set(pr, linux_osd_jail_slot, nlpr); 332 if (error == 0) { 333 bcopy(lpr, nlpr, sizeof(*lpr)); 334 lpr = nlpr; 335 } else { 336 free(nlpr, M_PRISON); 337 lpr = NULL; 338 } 339 mtx_unlock(&ppr->pr_mtx); 340 done: 341 if (lprp != NULL) 342 *lprp = lpr; 343 else 344 mtx_unlock(&pr->pr_mtx); 345 346 LIN_SDT_PROBE1(mib, linux_alloc_prison, return, error); 347 return (error); 348 } 349 350 /* 351 * Jail OSD methods for Linux prison data. 352 */ 353 static int 354 linux_prison_create(void *obj, void *data) 355 { 356 struct prison *pr = obj; 357 struct vfsoptlist *opts = data; 358 int jsys, error; 359 360 LIN_SDT_PROBE2(mib, linux_prison_create, entry, obj, data); 361 362 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 363 if (error != 0) { 364 LIN_SDT_PROBE1(mib, linux_prison_create, vfs_copyopt_error, 365 error); 366 } else if (jsys == JAIL_SYS_INHERIT) { 367 LIN_SDT_PROBE1(mib, linux_prison_create, return, 0); 368 return (0); 369 } 370 /* 371 * Inherit a prison's initial values from its parent 372 * (different from JAIL_SYS_INHERIT which also inherits changes). 373 */ 374 error = linux_alloc_prison(pr, NULL); 375 376 LIN_SDT_PROBE1(mib, linux_prison_create, return, error); 377 return (error); 378 } 379 380 static int 381 linux_prison_check(void *obj __unused, void *data) 382 { 383 struct vfsoptlist *opts = data; 384 char *osname, *osrelease; 385 int error, jsys, len, osrel, oss_version; 386 387 LIN_SDT_PROBE2(mib, linux_prison_check, entry, obj, data); 388 389 /* Check that the parameters are correct. */ 390 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 391 if (error != 0) { 392 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, 393 error); 394 } 395 if (error != ENOENT) { 396 if (error != 0) { 397 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 398 return (error); 399 } 400 if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT) { 401 LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 402 return (EINVAL); 403 } 404 } 405 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 406 if (error != 0) { 407 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error, 408 error); 409 } 410 if (error != ENOENT) { 411 if (error != 0) { 412 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 413 return (error); 414 } 415 if (len == 0 || osname[len - 1] != '\0') { 416 LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 417 return (EINVAL); 418 } 419 if (len > LINUX_MAX_UTSNAME) { 420 vfs_opterror(opts, "linux.osname too long"); 421 LIN_SDT_PROBE1(mib, linux_prison_check, return, 422 ENAMETOOLONG); 423 return (ENAMETOOLONG); 424 } 425 } 426 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 427 if (error != 0) { 428 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error, 429 error); 430 } 431 if (error != ENOENT) { 432 if (error != 0) { 433 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 434 return (error); 435 } 436 if (len == 0 || osrelease[len - 1] != '\0') { 437 LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 438 return (EINVAL); 439 } 440 if (len > LINUX_MAX_UTSNAME) { 441 vfs_opterror(opts, "linux.osrelease too long"); 442 LIN_SDT_PROBE1(mib, linux_prison_check, return, 443 ENAMETOOLONG); 444 return (ENAMETOOLONG); 445 } 446 error = linux_map_osrel(osrelease, &osrel); 447 if (error != 0) { 448 vfs_opterror(opts, "linux.osrelease format error"); 449 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 450 return (error); 451 } 452 } 453 error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 454 sizeof(oss_version)); 455 if (error != 0) 456 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, error); 457 458 if (error == ENOENT) 459 error = 0; 460 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 461 return (error); 462 } 463 464 static int 465 linux_prison_set(void *obj, void *data) 466 { 467 struct linux_prison *lpr; 468 struct prison *pr = obj; 469 struct vfsoptlist *opts = data; 470 char *osname, *osrelease; 471 int error, gotversion, jsys, len, oss_version; 472 473 LIN_SDT_PROBE2(mib, linux_prison_set, entry, obj, data); 474 475 /* Set the parameters, which should be correct. */ 476 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 477 if (error != 0) 478 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error); 479 if (error == ENOENT) 480 jsys = -1; 481 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 482 if (error != 0) 483 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error); 484 if (error == ENOENT) 485 osname = NULL; 486 else 487 jsys = JAIL_SYS_NEW; 488 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 489 if (error != 0) 490 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error); 491 if (error == ENOENT) 492 osrelease = NULL; 493 else 494 jsys = JAIL_SYS_NEW; 495 error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 496 sizeof(oss_version)); 497 if (error != 0) 498 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error); 499 if (error == ENOENT) 500 gotversion = 0; 501 else { 502 gotversion = 1; 503 jsys = JAIL_SYS_NEW; 504 } 505 switch (jsys) { 506 case JAIL_SYS_INHERIT: 507 /* "linux=inherit": inherit the parent's Linux info. */ 508 mtx_lock(&pr->pr_mtx); 509 osd_jail_del(pr, linux_osd_jail_slot); 510 mtx_unlock(&pr->pr_mtx); 511 break; 512 case JAIL_SYS_NEW: 513 /* 514 * "linux=new" or "linux.*": 515 * the prison gets its own Linux info. 516 */ 517 error = linux_alloc_prison(pr, &lpr); 518 if (error) { 519 mtx_unlock(&pr->pr_mtx); 520 LIN_SDT_PROBE1(mib, linux_prison_set, return, error); 521 return (error); 522 } 523 if (osrelease) { 524 error = linux_map_osrel(osrelease, &lpr->pr_osrel); 525 if (error) { 526 mtx_unlock(&pr->pr_mtx); 527 LIN_SDT_PROBE1(mib, linux_prison_set, return, 528 error); 529 return (error); 530 } 531 strlcpy(lpr->pr_osrelease, osrelease, 532 LINUX_MAX_UTSNAME); 533 } 534 if (osname) 535 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 536 if (gotversion) 537 lpr->pr_oss_version = oss_version; 538 mtx_unlock(&pr->pr_mtx); 539 } 540 541 LIN_SDT_PROBE1(mib, linux_prison_set, return, 0); 542 return (0); 543 } 544 545 SYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters"); 546 SYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME, 547 "Jail Linux kernel OS name"); 548 SYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME, 549 "Jail Linux kernel OS release"); 550 SYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW, 551 "I", "Jail Linux OSS version"); 552 553 static int 554 linux_prison_get(void *obj, void *data) 555 { 556 struct linux_prison *lpr; 557 struct prison *ppr; 558 struct prison *pr = obj; 559 struct vfsoptlist *opts = data; 560 int error, i; 561 562 static int version0; 563 564 LIN_SDT_PROBE2(mib, linux_prison_get, entry, obj, data); 565 566 /* See if this prison is the one with the Linux info. */ 567 lpr = linux_find_prison(pr, &ppr); 568 i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; 569 error = vfs_setopt(opts, "linux", &i, sizeof(i)); 570 if (error != 0) { 571 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, error); 572 if (error != ENOENT) 573 goto done; 574 } 575 if (i) { 576 error = vfs_setopts(opts, "linux.osname", lpr->pr_osname); 577 if (error != 0) { 578 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 579 error); 580 if (error != ENOENT) 581 goto done; 582 } 583 error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease); 584 if (error != 0) { 585 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 586 error); 587 if (error != ENOENT) 588 goto done; 589 } 590 error = vfs_setopt(opts, "linux.oss_version", 591 &lpr->pr_oss_version, sizeof(lpr->pr_oss_version)); 592 if (error != 0) { 593 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, 594 error); 595 if(error != ENOENT) 596 goto done; 597 } 598 } else { 599 /* 600 * If this prison is inheriting its Linux info, report 601 * empty/zero parameters. 602 */ 603 error = vfs_setopts(opts, "linux.osname", ""); 604 if (error != 0) { 605 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 606 error); 607 if(error != ENOENT) 608 goto done; 609 } 610 error = vfs_setopts(opts, "linux.osrelease", ""); 611 if (error != 0) { 612 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 613 error); 614 if(error != ENOENT) 615 goto done; 616 } 617 error = vfs_setopt(opts, "linux.oss_version", &version0, 618 sizeof(lpr->pr_oss_version)); 619 if (error != 0) { 620 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, 621 error); 622 if(error != ENOENT) 623 goto done; 624 } 625 } 626 error = 0; 627 628 done: 629 mtx_unlock(&ppr->pr_mtx); 630 631 LIN_SDT_PROBE1(mib, linux_prison_get, return, error); 632 return (error); 633 } 634 635 static void 636 linux_prison_destructor(void *data) 637 { 638 639 LIN_SDT_PROBE1(mib, linux_prison_destructor, entry, data); 640 free(data, M_PRISON); 641 LIN_SDT_PROBE0(mib, linux_prison_destructor, return); 642 } 643 644 void 645 linux_osd_jail_register(void) 646 { 647 struct prison *pr; 648 osd_method_t methods[PR_MAXMETHOD] = { 649 [PR_METHOD_CREATE] = linux_prison_create, 650 [PR_METHOD_GET] = linux_prison_get, 651 [PR_METHOD_SET] = linux_prison_set, 652 [PR_METHOD_CHECK] = linux_prison_check 653 }; 654 655 LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry); 656 657 linux_osd_jail_slot = 658 osd_jail_register(linux_prison_destructor, methods); 659 if (linux_osd_jail_slot > 0) { 660 /* Copy the system linux info to any current prisons. */ 661 sx_xlock(&allprison_lock); 662 TAILQ_FOREACH(pr, &allprison, pr_list) 663 (void)linux_alloc_prison(pr, NULL); 664 sx_xunlock(&allprison_lock); 665 } 666 667 LIN_SDT_PROBE0(mib, linux_osd_jail_register, return); 668 } 669 670 void 671 linux_osd_jail_deregister(void) 672 { 673 674 LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry); 675 676 if (linux_osd_jail_slot) 677 osd_jail_deregister(linux_osd_jail_slot); 678 679 LIN_SDT_PROBE0(mib, linux_osd_jail_register, return); 680 } 681 682 void 683 linux_get_osname(struct thread *td, char *dst) 684 { 685 struct prison *pr; 686 struct linux_prison *lpr; 687 688 LIN_SDT_PROBE2(mib, linux_get_osname, entry, td, dst); 689 690 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 691 bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME); 692 mtx_unlock(&pr->pr_mtx); 693 694 LIN_SDT_PROBE0(mib, linux_get_osname, return); 695 } 696 697 static int 698 linux_set_osname(struct thread *td, char *osname) 699 { 700 struct prison *pr; 701 struct linux_prison *lpr; 702 703 LIN_SDT_PROBE2(mib, linux_set_osname, entry, td, osname); 704 705 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 706 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 707 mtx_unlock(&pr->pr_mtx); 708 709 LIN_SDT_PROBE1(mib, linux_set_osname, return, 0); 710 return (0); 711 } 712 713 void 714 linux_get_osrelease(struct thread *td, char *dst) 715 { 716 struct prison *pr; 717 struct linux_prison *lpr; 718 719 LIN_SDT_PROBE2(mib, linux_get_osrelease, entry, td, dst); 720 721 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 722 bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME); 723 mtx_unlock(&pr->pr_mtx); 724 725 LIN_SDT_PROBE0(mib, linux_get_osrelease, return); 726 } 727 728 int 729 linux_kernver(struct thread *td) 730 { 731 struct prison *pr; 732 struct linux_prison *lpr; 733 int osrel; 734 735 LIN_SDT_PROBE1(mib, linux_kernver, entry, td); 736 737 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 738 osrel = lpr->pr_osrel; 739 mtx_unlock(&pr->pr_mtx); 740 741 LIN_SDT_PROBE1(mib, linux_kernver, return, osrel); 742 return (osrel); 743 } 744 745 static int 746 linux_set_osrelease(struct thread *td, char *osrelease) 747 { 748 struct prison *pr; 749 struct linux_prison *lpr; 750 int error; 751 752 LIN_SDT_PROBE2(mib, linux_set_osrelease, entry, td, osrelease); 753 754 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 755 error = linux_map_osrel(osrelease, &lpr->pr_osrel); 756 if (error == 0) 757 strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME); 758 mtx_unlock(&pr->pr_mtx); 759 760 LIN_SDT_PROBE1(mib, linux_set_osrelease, return, error); 761 return (error); 762 } 763 764 int 765 linux_get_oss_version(struct thread *td) 766 { 767 struct prison *pr; 768 struct linux_prison *lpr; 769 int version; 770 771 LIN_SDT_PROBE1(mib, linux_get_oss_version, entry, td); 772 773 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 774 version = lpr->pr_oss_version; 775 mtx_unlock(&pr->pr_mtx); 776 777 LIN_SDT_PROBE1(mib, linux_get_oss_version, return, version); 778 return (version); 779 } 780 781 static int 782 linux_set_oss_version(struct thread *td, int oss_version) 783 { 784 struct prison *pr; 785 struct linux_prison *lpr; 786 787 LIN_SDT_PROBE2(mib, linux_set_oss_version, entry, td, oss_version); 788 789 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 790 lpr->pr_oss_version = oss_version; 791 mtx_unlock(&pr->pr_mtx); 792 793 LIN_SDT_PROBE1(mib, linux_set_oss_version, return, 0); 794 return (0); 795 } 796 797 #if defined(DEBUG) || defined(KTR) 798 /* XXX: can be removed when every ldebug(...) and KTR stuff are removed. */ 799 800 u_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))]; 801 802 static int 803 linux_debug(int syscall, int toggle, int global) 804 { 805 806 if (global) { 807 char c = toggle ? 0 : 0xff; 808 809 memset(linux_debug_map, c, sizeof(linux_debug_map)); 810 return (0); 811 } 812 if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL) 813 return (EINVAL); 814 if (toggle) 815 clrbit(linux_debug_map, syscall); 816 else 817 setbit(linux_debug_map, syscall); 818 return (0); 819 } 820 821 /* 822 * Usage: sysctl linux.debug=<syscall_nr>.<0/1> 823 * 824 * E.g.: sysctl linux.debug=21.0 825 * 826 * As a special case, syscall "all" will apply to all syscalls globally. 827 */ 828 #define LINUX_MAX_DEBUGSTR 16 829 static int 830 linux_sysctl_debug(SYSCTL_HANDLER_ARGS) 831 { 832 char value[LINUX_MAX_DEBUGSTR], *p; 833 int error, sysc, toggle; 834 int global = 0; 835 836 value[0] = '\0'; 837 error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req); 838 if (error || req->newptr == NULL) 839 return (error); 840 for (p = value; *p != '\0' && *p != '.'; p++); 841 if (*p == '\0') 842 return (EINVAL); 843 *p++ = '\0'; 844 sysc = strtol(value, NULL, 0); 845 toggle = strtol(p, NULL, 0); 846 if (strcmp(value, "all") == 0) 847 global = 1; 848 error = linux_debug(sysc, toggle, global); 849 return (error); 850 } 851 852 SYSCTL_PROC(_compat_linux, OID_AUTO, debug, 853 CTLTYPE_STRING | CTLFLAG_RW, 854 0, 0, linux_sysctl_debug, "A", 855 "Linux debugging control"); 856 857 #endif /* DEBUG || KTR */ 858