1 /* MIB service - tree.c - tree access and management */ 2 3 #include "mib.h" 4 5 /* 6 * Does the given identifier fall within the range of static identifiers in the 7 * given parent? This check can be used to enumerate all static array entries 8 * in the given parent, starting from zero. The check does not guarantee that 9 * the entry is actually for a valid node, nor does it guarantee that there is 10 * not a dynamic node with this identifier. 11 */ 12 #define IS_STATIC_ID(parent, id) ((unsigned int)(id) < (parent)->node_size) 13 14 /* 15 * Scratch buffer, used for various cases of temporary data storage. It must 16 * be large enough to fit a sysctldesc structure followed by the longest 17 * supported description. It must also be large enough to serve as temporary 18 * storage for data being written in the majority of cases. Finally, it must 19 * be large enough to contain an entire page, for mib_copyin_str(). 20 */ 21 #define MAXDESCLEN 1024 /* from NetBSD */ 22 #define SCRATCH_SIZE MAX(PAGE_SIZE, sizeof(struct sysctldesc) + MAXDESCLEN) 23 static char scratch[SCRATCH_SIZE] __aligned(sizeof(int32_t)); 24 25 unsigned int nodes; /* how many nodes are there in the tree? */ 26 unsigned int objects; /* how many allocated memory objects are there? */ 27 28 /* 29 * Find a node through its parent node and identifier. Return the node if it 30 * was found, and optionally store a pointer to the pointer to its dynode 31 * superstructure (for removal). If no matching node was found, return NULL. 32 */ 33 static struct mib_node * 34 mib_find(struct mib_node * parent, int id, struct mib_dynode *** prevpp) 35 { 36 struct mib_node *node; 37 struct mib_dynode **dynp; 38 39 if (id < 0) 40 return NULL; 41 42 /* 43 * Is there a static node with this identifier? The static nodes are 44 * all in a single array, so lookup is O(1) for these nodes. We use 45 * the node flags field to see whether the array entry is valid. 46 */ 47 if (IS_STATIC_ID(parent, id)) { 48 node = &parent->node_scptr[id]; 49 50 if (node->node_flags != 0) { 51 /* Found a matching static node. */ 52 if (prevpp != NULL) 53 *prevpp = NULL; 54 return node; 55 } 56 } 57 58 /* 59 * Is there a dynamic node with this identifier? The dynamic nodes 60 * form a linked list. This is predominantly because userland may pick 61 * the identifier number at creation time, so we cannot rely on all 62 * dynamically created nodes falling into a small identifier range. 63 * That eliminates the option of a dynamic array indexed by identifier, 64 * and a linked list is the simplest next option. Thus, dynamic node 65 * lookup is O(n). However, since the list is sorted by identifier, 66 * we may be able to stop the search early. 67 */ 68 for (dynp = &parent->node_dcptr; *dynp != NULL; 69 dynp = &((*dynp)->dynode_next)) { 70 if ((*dynp)->dynode_id == id) { 71 /* Found a matching dynamic node. */ 72 if (prevpp != NULL) 73 *prevpp = dynp; 74 return &(*dynp)->dynode_node; 75 } else if ((*dynp)->dynode_id > id) 76 break; /* no need to look further */ 77 } 78 79 return NULL; 80 } 81 82 /* 83 * Copy out a node to userland, using the exchange format for nodes (namely, 84 * a sysctlnode structure). Return the size of the object that is (or, if the 85 * node falls outside the requested data range, would be) copied out on 86 * success, or a negative error code on failure. The function may return 0 87 * to indicate that nothing was copied out after all (this is unused here). 88 */ 89 static ssize_t 90 mib_copyout_node(struct mib_call * call, struct mib_oldp * oldp, size_t off, 91 int id, const struct mib_node * node) 92 { 93 struct sysctlnode scn; 94 int visible; 95 96 if (!mib_inrange(oldp, off)) 97 return sizeof(scn); /* nothing to do */ 98 99 memset(&scn, 0, sizeof(scn)); 100 101 /* 102 * We use CTLFLAG_PARENT and CTLFLAG_VERIFY internally only. NetBSD 103 * uses the values of these flags for different purposes. Either way, 104 * do not expose them to userland. 105 */ 106 scn.sysctl_flags = SYSCTL_VERSION | 107 (node->node_flags & ~(CTLFLAG_PARENT | CTLFLAG_VERIFY)); 108 scn.sysctl_num = id; 109 strlcpy(scn.sysctl_name, node->node_name, sizeof(scn.sysctl_name)); 110 scn.sysctl_ver = node->node_ver; 111 scn.sysctl_size = node->node_size; 112 113 /* Some information is only visible if the user can access the node. */ 114 visible = (!(node->node_flags & CTLFLAG_PRIVATE) || mib_authed(call)); 115 116 /* 117 * For immediate types, store the immediate value in the resulting 118 * structure, unless the caller is not authorized to obtain the value. 119 */ 120 if ((node->node_flags & CTLFLAG_IMMEDIATE) && visible) { 121 switch (SYSCTL_TYPE(node->node_flags)) { 122 case CTLTYPE_BOOL: 123 scn.sysctl_bdata = node->node_bool; 124 break; 125 case CTLTYPE_INT: 126 scn.sysctl_idata = node->node_int; 127 break; 128 case CTLTYPE_QUAD: 129 scn.sysctl_qdata = node->node_quad; 130 } 131 } 132 133 /* Special rules apply to parent nodes. */ 134 if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE) { 135 /* Report the node size the way NetBSD does, just in case. */ 136 scn.sysctl_size = sizeof(scn); 137 138 /* If this is a real parent node, report child information. */ 139 if ((node->node_flags & CTLFLAG_PARENT) && visible) { 140 scn.sysctl_csize = node->node_csize; 141 scn.sysctl_clen = node->node_clen; 142 } 143 144 /* 145 * If this is a function-driven node, indicate this by setting 146 * a nonzero function address. This allows trace(1) to 147 * determine that it should not attempt to descend into this 148 * part of the tree as usual, because a) accessing subnodes may 149 * have side effects, and b) meta-identifiers may not work as 150 * expected in these parts of the tree. Do not return the real 151 * function pointer, as this would leak anti-ASR information. 152 */ 153 if (!(node->node_flags & CTLFLAG_PARENT)) 154 scn.sysctl_func = SYSCTL_NODE_FN; 155 } 156 157 /* Copy out the resulting node. */ 158 return mib_copyout(oldp, off, &scn, sizeof(scn)); 159 } 160 161 /* 162 * Given a query on a non-leaf (parent) node, provide the user with an array of 163 * this node's children. 164 */ 165 static ssize_t 166 mib_query(struct mib_call * call, struct mib_node * parent, 167 struct mib_oldp * oldp, struct mib_newp * newp, struct mib_node * root) 168 { 169 struct sysctlnode scn; 170 struct mib_node *node; 171 struct mib_dynode *dynode; 172 size_t off; 173 int r, id; 174 175 /* If the user passed in version numbers, check them. */ 176 if (newp != NULL) { 177 if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK) 178 return r; 179 180 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) 181 return EINVAL; 182 183 /* 184 * If a node version number is given, it must match the version 185 * of the parent or the root. 186 */ 187 if (scn.sysctl_ver != 0 && scn.sysctl_ver != root->node_ver && 188 scn.sysctl_ver != parent->node_ver) 189 return EINVAL; 190 } 191 192 /* 193 * We need not return the nodes strictly in ascending order of 194 * identifiers, as this is not expected by userland. For example, 195 * sysctlgetmibinfo(3) performs its own sorting after a query. 196 * Thus, we can go through the static and dynamic nodes separately. 197 */ 198 off = 0; 199 200 /* First enumerate the static nodes. */ 201 for (id = 0; IS_STATIC_ID(parent, id); id++) { 202 node = &parent->node_scptr[id]; 203 204 if (node->node_flags == 0) 205 continue; 206 207 if ((r = mib_copyout_node(call, oldp, off, id, node)) < 0) 208 return r; 209 off += r; 210 } 211 212 /* Then enumerate the dynamic nodes. */ 213 for (dynode = parent->node_dcptr; dynode != NULL; 214 dynode = dynode->dynode_next) { 215 node = &dynode->dynode_node; 216 217 if ((r = mib_copyout_node(call, oldp, off, dynode->dynode_id, 218 node)) < 0) 219 return r; 220 off += r; 221 } 222 223 return off; 224 } 225 226 /* 227 * Scan a parent node's children, as part of new node creation. Search for 228 * either a free node identifier (if given_id < 0) or collisions with the node 229 * identifier to use (if given_id >= 0). Also check for name collisions. Upon 230 * success, return OK, with the resulting node identifier stored in 'idp' and a 231 * pointer to the pointer for the new dynamic node stored in 'prevpp'. Upon 232 * failure, return an error code. If the failure is EEXIST, 'idp' will contain 233 * the ID of the conflicting node, and 'nodep' will point to the node. 234 */ 235 static int 236 mib_scan(struct mib_node * parent, int given_id, const char * name, int * idp, 237 struct mib_dynode *** prevpp, struct mib_node ** nodep) 238 { 239 struct mib_dynode **prevp, **dynp; 240 struct mib_node *node; 241 int id; 242 243 /* 244 * We must verify that no entry already exists with the given name. In 245 * addition, if a nonnegative identifier is given, we should use that 246 * identifier and make sure it does not already exist. Otherwise, we 247 * must find a free identifier. Finally, we sort the dynamic nodes in 248 * ascending identifier order, so we must find the right place at which 249 * to insert the new node. 250 * 251 * For finding a free identifier, choose an ID that falls (well) 252 * outside the static range, both to avoid accidental retrieval by an 253 * application that uses a static ID, and to simplify verifying that 254 * the ID is indeed free. The sorting of dynamic nodes by identifier 255 * ensures that searching for a free identifier is O(n). 256 * 257 * At this time, we do not support some NetBSD features. We do not 258 * force success if the new node is sufficiently like an existing one. 259 * Also, we do not use global autoincrement for dynamic identifiers, 260 * although that could easily be changed. 261 */ 262 263 /* First check the static node array, just for collisions. */ 264 for (id = 0; IS_STATIC_ID(parent, id); id++) { 265 node = &parent->node_scptr[id]; 266 if (node->node_flags == 0) 267 continue; 268 if (id == given_id || !strcmp(name, node->node_name)) { 269 *idp = id; 270 *nodep = node; 271 return EEXIST; 272 } 273 } 274 275 /* 276 * Then try to find the place to insert a new dynamic node. At the 277 * same time, check for both identifier and name collisions. 278 */ 279 if (given_id >= 0) 280 id = given_id; 281 else 282 id = MAX(CREATE_BASE, parent->node_size); 283 284 for (prevp = &parent->node_dcptr; *prevp != NULL; 285 prevp = &((*prevp)->dynode_next)) { 286 if ((*prevp)->dynode_id > id) 287 break; 288 if ((*prevp)->dynode_id == id) { 289 if (given_id >= 0) { 290 *idp = id; 291 *nodep = &(*prevp)->dynode_node; 292 return EEXIST; 293 } else 294 id++; 295 } 296 if (!strcmp(name, (*prevp)->dynode_node.node_name)) { 297 *idp = (*prevp)->dynode_id; 298 *nodep = &(*prevp)->dynode_node; 299 return EEXIST; 300 } 301 } 302 303 /* Finally, check the rest of the dynamic nodes for name collisions. */ 304 for (dynp = prevp; *dynp != NULL; dynp = &((*dynp)->dynode_next)) { 305 assert((*dynp)->dynode_id > id); 306 307 if (!strcmp(name, (*dynp)->dynode_node.node_name)) { 308 *idp = (*dynp)->dynode_id; 309 *nodep = &(*dynp)->dynode_node; 310 return EEXIST; 311 } 312 } 313 314 *idp = id; 315 *prevpp = prevp; 316 return OK; 317 } 318 319 /* 320 * Copy in a string from the user process, located at the given remote address, 321 * into the given local buffer. If no buffer is given, just compute the length 322 * of the string. On success, return OK. If 'sizep' is not NULL, it will be 323 * filled with the string size, including the null terminator. If a non-NULL 324 * buffer was given, the string will be copied into the provided buffer (also 325 * including null terminator). Return an error code on failure, which includes 326 * the case that no null terminator was found within the range of bytes that 327 * would fit in the given buffer. 328 */ 329 static int 330 mib_copyin_str(struct mib_newp * __restrict newp, vir_bytes addr, 331 char * __restrict buf, size_t bufsize, size_t * __restrict sizep) 332 { 333 char *ptr, *endp; 334 size_t chunk, len; 335 int r; 336 337 assert(newp != NULL); 338 assert(bufsize <= SSIZE_MAX); 339 340 if (addr == 0) 341 return EINVAL; 342 343 /* 344 * NetBSD has a kernel routine for copying in a string from userland. 345 * MINIX3 does not, since its system call interface has always relied 346 * on userland passing in string lengths. The sysctl(2) API does not 347 * provide the string length, and thus, we have to do a bit of guess 348 * work. If we copy too little at once, performance suffers. If we 349 * copy too much at once, we may trigger an unneeded page fault. Make 350 * use of page boundaries to strike a balance between those two. If we 351 * are requested to just get the string length, use the scratch buffer. 352 */ 353 len = 0; 354 355 while (bufsize > 0) { 356 chunk = PAGE_SIZE - (addr % PAGE_SIZE); 357 if (chunk > bufsize) 358 chunk = bufsize; 359 360 ptr = (buf != NULL) ? &buf[len] : scratch; 361 if ((r = mib_copyin_aux(newp, addr, ptr, chunk)) != OK) 362 return r; 363 364 if ((endp = memchr(ptr, '\0', chunk)) != NULL) { 365 /* A null terminator was found - success. */ 366 if (sizep != NULL) 367 *sizep = len + (size_t)(endp - ptr) + 1; 368 return OK; 369 } 370 371 addr += chunk; 372 len += chunk; 373 bufsize -= chunk; 374 } 375 376 /* No null terminator found. */ 377 return EINVAL; 378 } 379 380 /* 381 * Increase the version of the root node, and copy this new version to all 382 * nodes on the path to a node, as well as (optionally) that node itself. 383 */ 384 static void 385 mib_upgrade(struct mib_node ** stack, int depth, struct mib_node * node) 386 { 387 uint32_t ver; 388 389 /* 390 * The bottom of the stack is always the root node, which determines 391 * the version of the entire tree. Do not use version number 0, as a 392 * zero version number indicates no interest in versions elsewhere. 393 */ 394 assert(depth > 0); 395 396 ver = stack[0]->node_ver + 1; 397 if (ver == 0) 398 ver = 1; 399 400 /* Copy the new version to all the nodes on the path. */ 401 while (depth-- > 0) 402 stack[depth]->node_ver = ver; 403 404 if (node != NULL) 405 node->node_ver = stack[0]->node_ver; 406 } 407 408 /* 409 * Create a node. 410 */ 411 static ssize_t 412 mib_create(struct mib_call * call, struct mib_node * parent, 413 struct mib_oldp * oldp, struct mib_newp * newp, 414 struct mib_node ** stack, int depth) 415 { 416 struct mib_dynode *dynode, **prevp; 417 struct mib_node *node; 418 struct sysctlnode scn; 419 size_t namelen, size; 420 ssize_t len; 421 bool b; 422 char c; 423 int r, id; 424 425 /* This is a privileged operation. */ 426 if (!mib_authed(call)) 427 return EPERM; 428 429 /* The parent node must not be marked as read-only. */ 430 if (!(parent->node_flags & CTLFLAG_READWRITE)) 431 return EPERM; 432 433 /* 434 * Has the parent reached its child node limit? This check is entirely 435 * theoretical as long as we support only 32-bit virtual memory. 436 */ 437 if (parent->node_csize == INT_MAX) 438 return EINVAL; 439 assert(parent->node_clen <= parent->node_csize); 440 441 /* The caller must supply information on the child node to create. */ 442 if (newp == NULL) 443 return EINVAL; 444 445 if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK) 446 return r; 447 448 /* 449 * We perform as many checks as possible before we start allocating 450 * memory. Then again, after allocation, copying in data may still 451 * fail. Unlike when setting values, we do not first copy data into a 452 * temporary buffer here, because we do not need to: if the copy fails, 453 * the entire create operation fails, so atomicity is not an issue. 454 */ 455 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) 456 return EINVAL; 457 458 /* 459 * If a node version number is given, it must match the version of the 460 * parent or the root (which is always the bottom of the node stack). 461 * The given version number is *not* used for the node being created. 462 */ 463 assert(depth > 0); 464 465 if (scn.sysctl_ver != 0 && scn.sysctl_ver != stack[0]->node_ver && 466 scn.sysctl_ver != parent->node_ver) 467 return EINVAL; 468 469 /* 470 * Validate the node flags. In addition to the NetBSD-allowed flags, 471 * we also allow UNSIGNED, and leave its interpretation to userland. 472 */ 473 if (SYSCTL_FLAGS(scn.sysctl_flags) & 474 ~(SYSCTL_USERFLAGS | CTLFLAG_UNSIGNED)) 475 return EINVAL; 476 477 if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE)) { 478 /* 479 * Without either IMMEDIATE or OWNDATA, data pointers are 480 * actually kernel addresses--a concept we do not support. 481 * Otherwise, if IMMEDIATE is not set, we are going to have to 482 * allocate extra memory for the data, so force OWNDATA to be. 483 * set. Node-type nodes have no data, though. 484 */ 485 if (SYSCTL_TYPE(scn.sysctl_flags) != CTLTYPE_NODE) { 486 if (!(scn.sysctl_flags & CTLFLAG_OWNDATA) && 487 scn.sysctl_data != NULL) 488 return EINVAL; /* not meaningful on MINIX3 */ 489 490 scn.sysctl_flags |= CTLFLAG_OWNDATA; 491 } 492 } else if (scn.sysctl_flags & CTLFLAG_OWNDATA) 493 return EINVAL; 494 495 /* The READWRITE flag consists of multiple bits. Sanitize. */ 496 if (scn.sysctl_flags & CTLFLAG_READWRITE) 497 scn.sysctl_flags |= CTLFLAG_READWRITE; 498 499 /* Validate the node type and size, and do some additional checks. */ 500 switch (SYSCTL_TYPE(scn.sysctl_flags)) { 501 case CTLTYPE_BOOL: 502 if (scn.sysctl_size != sizeof(bool)) 503 return EINVAL; 504 break; 505 case CTLTYPE_INT: 506 if (scn.sysctl_size != sizeof(int)) 507 return EINVAL; 508 break; 509 case CTLTYPE_QUAD: 510 if (scn.sysctl_size != sizeof(u_quad_t)) 511 return EINVAL; 512 break; 513 case CTLTYPE_STRING: 514 /* 515 * For strings, a zero length means that we are supposed to 516 * allocate a buffer size based on the given string size. 517 */ 518 if (scn.sysctl_size == 0 && scn.sysctl_data != NULL) { 519 if ((r = mib_copyin_str(newp, 520 (vir_bytes)scn.sysctl_data, NULL, SSIZE_MAX, 521 &size)) != OK) 522 return r; 523 scn.sysctl_size = size; 524 } 525 /* FALLTHROUGH */ 526 case CTLTYPE_STRUCT: 527 /* 528 * We do not set an upper size on the data size, since it would 529 * still be possible to create a large number of nodes, and 530 * this is a privileged operation ayway. 531 */ 532 if (scn.sysctl_size == 0 || scn.sysctl_size > SSIZE_MAX) 533 return EINVAL; 534 if (scn.sysctl_flags & CTLFLAG_IMMEDIATE) 535 return EINVAL; 536 break; 537 case CTLTYPE_NODE: 538 /* 539 * The zero size is an API requirement, but we also rely on the 540 * zero value internally, as the node has no static children. 541 */ 542 if (scn.sysctl_size != 0) 543 return EINVAL; 544 if (scn.sysctl_flags & (CTLFLAG_IMMEDIATE | CTLFLAG_OWNDATA)) 545 return EINVAL; 546 if (scn.sysctl_csize != 0 || scn.sysctl_clen != 0 || 547 scn.sysctl_child != NULL) 548 return EINVAL; 549 break; 550 default: 551 return EINVAL; 552 } 553 554 if (scn.sysctl_func != NULL || scn.sysctl_parent != NULL) 555 return EINVAL; 556 557 /* Names must be nonempty, null terminated, C symbol style strings. */ 558 for (namelen = 0; namelen < sizeof(scn.sysctl_name); namelen++) { 559 if ((c = scn.sysctl_name[namelen]) == '\0') 560 break; 561 /* A-Z, a-z, 0-9, _ only, and no digit as first character. */ 562 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || 563 c == '_' || (c >= '0' && c <= '9' && namelen > 0))) 564 return EINVAL; 565 } 566 if (namelen == 0 || namelen == sizeof(scn.sysctl_name)) 567 return EINVAL; 568 569 /* 570 * Find a free identifier, or check for ID collisions if a specific 571 * identifier was given. At the same time, scan for name collisions, 572 * and find the location at which to insert the new node in the list. 573 */ 574 r = mib_scan(parent, scn.sysctl_num, scn.sysctl_name, &id, &prevp, 575 &node); 576 577 if (r != OK) { 578 /* 579 * On collisions, if requested, copy out the existing node. 580 * This does not quite fit the general interaction model, as 581 * the service must now return a nonzero old length from a call 582 * that actually failed (in contrast to ENOMEM failures). 583 */ 584 if (r == EEXIST && oldp != NULL) { 585 len = mib_copyout_node(call, oldp, 0, id, node); 586 587 if (len > 0) 588 mib_setoldlen(call, len); 589 } 590 591 return r; 592 } 593 594 /* 595 * All checks so far have passed. "id" now contains the new node 596 * identifier, and "prevp" points to the pointer at which to insert the 597 * new node in its parent's linked list of dynamic nodes. 598 * 599 * We can now attempt to create and initialize a new dynamic node. 600 * Allocating nodes this way may cause heavy memory fragmentation over 601 * time, but we do not expect the tree to see heavy modification at run 602 * time, and the superuser has easier ways to get the MIB service in 603 * trouble. We note that even in low-memory conditions, the MIB 604 * service is always able to provide basic functionality. 605 */ 606 size = sizeof(*dynode) + namelen; 607 if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE)) 608 size += scn.sysctl_size; 609 610 if ((dynode = malloc(size)) == NULL) 611 return EINVAL; /* do not return ENOMEM */ 612 objects++; 613 614 /* From here on, we have to free "dynode" before returning an error. */ 615 r = OK; 616 617 memset(dynode, 0, sizeof(*dynode)); /* no need to zero all of "size" */ 618 dynode->dynode_id = id; 619 strlcpy(dynode->dynode_name, scn.sysctl_name, namelen + 1); 620 621 node = &dynode->dynode_node; 622 node->node_flags = scn.sysctl_flags & ~SYSCTL_VERS_MASK; 623 if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_NODE) 624 node->node_flags |= CTLFLAG_PARENT; 625 node->node_size = scn.sysctl_size; 626 node->node_name = dynode->dynode_name; 627 628 /* Initialize the node value. */ 629 if (scn.sysctl_flags & CTLFLAG_IMMEDIATE) { 630 switch (SYSCTL_TYPE(scn.sysctl_flags)) { 631 case CTLTYPE_BOOL: 632 /* Sanitize booleans. See the C99 _Bool comment. */ 633 memcpy(&c, &scn.sysctl_bdata, sizeof(c)); 634 node->node_bool = (bool)c; 635 break; 636 case CTLTYPE_INT: 637 node->node_int = scn.sysctl_idata; 638 break; 639 case CTLTYPE_QUAD: 640 node->node_quad = scn.sysctl_qdata; 641 break; 642 default: 643 assert(0); 644 } 645 } else if (SYSCTL_TYPE(scn.sysctl_flags) != CTLTYPE_NODE) { 646 node->node_data = dynode->dynode_name + namelen + 1; 647 648 /* Did the user supply initial data? If not, use zeroes. */ 649 if (scn.sysctl_data != NULL) { 650 /* 651 * For strings, do not copy in more than needed. This 652 * is just a nice feature which allows initialization 653 * of large string buffers with short strings. 654 */ 655 if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_STRING) 656 r = mib_copyin_str(newp, 657 (vir_bytes)scn.sysctl_data, 658 node->node_data, scn.sysctl_size, NULL); 659 else 660 r = mib_copyin_aux(newp, 661 (vir_bytes)scn.sysctl_data, 662 node->node_data, scn.sysctl_size); 663 } else 664 memset(node->node_data, 0, scn.sysctl_size); 665 666 /* 667 * Sanitize booleans. See the C99 _Bool comment elsewhere. 668 * In this case it is not as big of a deal, as we will not be 669 * accessing the boolean value directly ourselves. 670 */ 671 if (r == OK && SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_BOOL) { 672 b = (bool)*(char *)node->node_data; 673 memcpy(node->node_data, &b, sizeof(b)); 674 } 675 } 676 677 /* 678 * Even though it would be entirely possible to set a description right 679 * away as well, this does not seem to be supported on NetBSD at all. 680 */ 681 682 /* Deal with earlier failures now. */ 683 if (r != OK) { 684 free(dynode); 685 objects--; 686 687 return r; 688 } 689 690 /* At this point, actual creation can no longer fail. */ 691 692 /* Link the dynamic node into the list, in the right place. */ 693 assert(prevp != NULL); 694 dynode->dynode_next = *prevp; 695 *prevp = dynode; 696 697 /* The parent node now has one more child. */ 698 parent->node_csize++; 699 parent->node_clen++; 700 701 nodes++; 702 703 /* 704 * Bump the version of all nodes on the path to the new node, including 705 * the node itself. 706 */ 707 mib_upgrade(stack, depth, node); 708 709 /* 710 * Copy out the newly created node as resulting ("old") data. Do not 711 * undo the creation if this fails, though. 712 */ 713 return mib_copyout_node(call, oldp, 0, id, node); 714 } 715 716 /* 717 * Destroy a node. 718 */ 719 static ssize_t 720 mib_destroy(struct mib_call * call, struct mib_node * parent, 721 struct mib_oldp * oldp, struct mib_newp * newp, 722 struct mib_node ** stack, int depth) 723 { 724 struct mib_dynode *dynode, **prevp; 725 struct mib_node *node; 726 struct sysctlnode scn; 727 ssize_t r; 728 729 /* This is a privileged operation. */ 730 if (!mib_authed(call)) 731 return EPERM; 732 733 /* The parent node must not be marked as read-only. */ 734 if (!(parent->node_flags & CTLFLAG_READWRITE)) 735 return EPERM; 736 737 /* The caller must specify which child node to destroy. */ 738 if (newp == NULL) 739 return EINVAL; 740 741 if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK) 742 return r; 743 744 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) 745 return EINVAL; 746 747 /* Locate the child node. */ 748 if ((node = mib_find(parent, scn.sysctl_num, &prevp)) == NULL) 749 return ENOENT; 750 751 /* The node must not be marked as permanent. */ 752 if (node->node_flags & CTLFLAG_PERMANENT) 753 return EPERM; 754 755 /* For node-type nodes, extra rules apply. */ 756 if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE) { 757 /* The node must not have an associated function. */ 758 if (!(node->node_flags & CTLFLAG_PARENT)) 759 return EPERM; 760 761 /* The target node must itself not have child nodes. */ 762 if (node->node_clen != 0) 763 return ENOTEMPTY; 764 } 765 766 /* If the user supplied a version, it must match the node version. */ 767 if (scn.sysctl_ver != 0 && scn.sysctl_ver != node->node_ver) 768 return EINVAL; /* NetBSD inconsistently throws ENOENT here */ 769 770 /* If the user supplied a name, it must match the node name. */ 771 if (scn.sysctl_name[0] != '\0') { 772 if (memchr(scn.sysctl_name, '\0', 773 sizeof(scn.sysctl_name)) == NULL) 774 return EINVAL; 775 if (strcmp(scn.sysctl_name, node->node_name)) 776 return EINVAL; /* also ENOENT on NetBSD */ 777 } 778 779 /* 780 * Copy out the old node if requested, otherwise return the length 781 * anyway. The node will be destroyed even if this call fails, 782 * because that is how NetBSD behaves. 783 */ 784 r = mib_copyout_node(call, oldp, 0, scn.sysctl_num, node); 785 786 /* If the description was allocated, free it. */ 787 if (node->node_flags & CTLFLAG_OWNDESC) { 788 free(__UNCONST(node->node_desc)); 789 objects--; 790 } 791 792 /* 793 * Static nodes only use static memory, and dynamic nodes have the data 794 * area embedded in the dynode object. In neither case is data memory 795 * allocated separately, and thus, it need never be freed separately. 796 * Therefore we *must not* check CTLFLAG_OWNDATA here. 797 */ 798 799 assert(parent->node_csize > 0); 800 assert(parent->node_clen > 0); 801 802 /* 803 * Dynamic nodes must be freed. Freeing the dynode object also frees 804 * the node name and any associated data. Static nodes are zeroed out, 805 * and the static memory they referenced will become inaccessible. 806 */ 807 if (prevp != NULL) { 808 dynode = *prevp; 809 *prevp = dynode->dynode_next; 810 811 free(dynode); 812 objects--; 813 814 parent->node_csize--; 815 } else 816 memset(node, 0, sizeof(*node)); 817 818 parent->node_clen--; 819 820 nodes--; 821 822 /* Bump the version of all nodes on the path to the destroyed node. */ 823 mib_upgrade(stack, depth, NULL); 824 825 return r; 826 } 827 828 /* 829 * Copy out a node description to userland, using the exchange format for node 830 * descriptions (namely, a sysctldesc structure). Return the size of the 831 * object that is (or, if the description falls outside the requested data 832 * range, would be) copied out on success, or a negative error code on failure. 833 * The function may return 0 to indicate that nothing was copied out after all. 834 */ 835 static ssize_t 836 mib_copyout_desc(struct mib_call * call, struct mib_oldp * oldp, size_t off, 837 int id, const struct mib_node * node) 838 { 839 struct sysctldesc *scd; 840 size_t size; 841 int r; 842 843 /* Descriptions of private nodes are considered private too. */ 844 if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call)) 845 return 0; 846 847 /* The description length includes the null terminator. */ 848 if (node->node_desc != NULL) 849 size = strlen(node->node_desc) + 1; 850 else 851 size = 1; 852 853 assert(sizeof(*scd) + size <= sizeof(scratch)); 854 855 scd = (struct sysctldesc *)scratch; 856 memset(scd, 0, sizeof(*scd)); 857 scd->descr_num = id; 858 scd->descr_ver = node->node_ver; 859 scd->descr_len = size; 860 if (node->node_desc != NULL) 861 strlcpy(scd->descr_str, node->node_desc, 862 sizeof(scratch) - sizeof(*scd)); 863 else 864 scd->descr_str[0] = '\0'; 865 866 size += offsetof(struct sysctldesc, descr_str); 867 868 if ((r = mib_copyout(oldp, off, scratch, size)) < 0) 869 return r; 870 871 /* 872 * By aligning just the size, we may leave garbage between the entries 873 * copied out, which is fine because it is userland's own data. 874 */ 875 return roundup2(size, sizeof(int32_t)); 876 } 877 878 /* 879 * Retrieve node descriptions in bulk, or retrieve or assign a particular 880 * node's description. 881 */ 882 static ssize_t 883 mib_describe(struct mib_call * call, struct mib_node * parent, 884 struct mib_oldp * oldp, struct mib_newp * newp) 885 { 886 struct sysctlnode scn; 887 struct mib_node *node; 888 struct mib_dynode *dynode; 889 size_t off; 890 int r, id; 891 892 /* If new data are given, they identify a particular target node. */ 893 if (newp != NULL) { 894 if ((r = mib_copyin(newp, &scn, sizeof(scn))) != OK) 895 return r; 896 897 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) 898 return EINVAL; 899 900 /* Locate the child node. */ 901 if ((node = mib_find(parent, scn.sysctl_num, NULL)) == NULL) 902 return ENOENT; 903 904 /* Descriptions of private nodes are considered private too. */ 905 if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call)) 906 return EPERM; 907 908 /* 909 * If a description pointer was given, this is a request to 910 * set the node's description. 911 */ 912 if (scn.sysctl_desc != NULL) { 913 /* Such a request requires superuser privileges. */ 914 if (!mib_authed(call)) 915 return EPERM; 916 917 /* The node must not already have a description. */ 918 if (node->node_desc != NULL) 919 return EPERM; 920 921 /* The node must not be marked as permanent. */ 922 if (node->node_flags & CTLFLAG_PERMANENT) 923 return EPERM; 924 925 /* 926 * If the user supplied a version, it must match. 927 * NetBSD performs this check only when setting 928 * descriptions, and thus, so do we.. 929 */ 930 if (scn.sysctl_ver != 0 && 931 scn.sysctl_ver != node->node_ver) 932 return EINVAL; 933 934 /* 935 * Copy in the description to the scratch buffer. 936 * The length of the description must be reasonable. 937 */ 938 if ((r = mib_copyin_str(newp, 939 (vir_bytes)scn.sysctl_desc, scratch, MAXDESCLEN, 940 NULL)) != OK) 941 return r; 942 943 /* Allocate memory and store the description. */ 944 if ((node->node_desc = strdup(scratch)) == NULL) { 945 printf("MIB: out of memory!\n"); 946 947 return EINVAL; /* do not return ENOMEM */ 948 } 949 objects++; 950 951 /* The description must now be freed with the node. */ 952 node->node_flags |= CTLFLAG_OWNDESC; 953 } 954 955 /* 956 * Either way, copy out the requested node's description, which 957 * should indeed be the new description if one was just set. 958 * Note that we have already performed the permission check 959 * that could make this call return zero, so here it will not. 960 */ 961 return mib_copyout_desc(call, oldp, 0, scn.sysctl_num, node); 962 } 963 964 /* See also the considerations laid out in mib_query(). */ 965 off = 0; 966 967 /* First describe the static nodes. */ 968 for (id = 0; IS_STATIC_ID(parent, id); id++) { 969 node = &parent->node_scptr[id]; 970 971 if (node->node_flags == 0) 972 continue; 973 974 if ((r = mib_copyout_desc(call, oldp, off, id, node)) < 0) 975 return r; 976 off += r; 977 } 978 979 /* Then describe the dynamic nodes. */ 980 for (dynode = parent->node_dcptr; dynode != NULL; 981 dynode = dynode->dynode_next) { 982 node = &dynode->dynode_node; 983 984 if ((r = mib_copyout_desc(call, oldp, off, dynode->dynode_id, 985 node)) < 0) 986 return r; 987 off += r; 988 } 989 990 return off; 991 } 992 993 /* 994 * Return a pointer to the data associated with the given node, or NULL if the 995 * node has no associated data. Actual calls to this function should never 996 * result in NULL - as long as the proper rules are followed elsewhere. 997 */ 998 static void * 999 mib_getptr(struct mib_node * node) 1000 { 1001 1002 switch (SYSCTL_TYPE(node->node_flags)) { 1003 case CTLTYPE_BOOL: 1004 if (node->node_flags & CTLFLAG_IMMEDIATE) 1005 return &node->node_bool; 1006 break; 1007 case CTLTYPE_INT: 1008 if (node->node_flags & CTLFLAG_IMMEDIATE) 1009 return &node->node_int; 1010 break; 1011 case CTLTYPE_QUAD: 1012 if (node->node_flags & CTLFLAG_IMMEDIATE) 1013 return &node->node_quad; 1014 break; 1015 case CTLTYPE_STRING: 1016 case CTLTYPE_STRUCT: 1017 if (node->node_flags & CTLFLAG_IMMEDIATE) 1018 return NULL; 1019 break; 1020 default: 1021 return NULL; 1022 } 1023 1024 return node->node_data; 1025 } 1026 1027 /* 1028 * Read current (old) data from a regular data node, if requested. Return the 1029 * old data length. 1030 */ 1031 static ssize_t 1032 mib_read(struct mib_node * node, struct mib_oldp * oldp) 1033 { 1034 void *ptr; 1035 size_t oldlen; 1036 int r; 1037 1038 if ((ptr = mib_getptr(node)) == NULL) 1039 return EINVAL; 1040 1041 if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_STRING) 1042 oldlen = strlen(node->node_data) + 1; 1043 else 1044 oldlen = node->node_size; 1045 1046 if (oldlen > SSIZE_MAX) 1047 return EINVAL; 1048 1049 /* Copy out the current data, if requested at all. */ 1050 if (oldp != NULL && (r = mib_copyout(oldp, 0, ptr, oldlen)) < 0) 1051 return r; 1052 1053 /* Return the current length in any case. */ 1054 return (ssize_t)oldlen; 1055 } 1056 1057 /* 1058 * Write new data into a regular data node, if requested. 1059 */ 1060 static int 1061 mib_write(struct mib_call * call, struct mib_node * node, 1062 struct mib_newp * newp, mib_verify_ptr verify) 1063 { 1064 bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* explained below */ 1065 char *src, *dst; 1066 size_t newlen; 1067 int r; 1068 1069 if (newp == NULL) 1070 return OK; /* nothing to do */ 1071 1072 /* 1073 * When setting a new value, we cannot risk doing an in-place update: 1074 * the copy from userland may fail halfway through, in which case an 1075 * in-place update could leave the node value in a corrupted state. 1076 * Thus, we must first fetch any new data into a temporary buffer. 1077 * 1078 * Given that we use intermediate data storage, we could support value 1079 * swapping, where the user provides the same buffer for new and old 1080 * data. We choose not to: NetBSD does not support it, it would make 1081 * trace(1)'s job a lot harder, and it would convolute the code here. 1082 */ 1083 newlen = mib_getnewlen(newp); 1084 1085 if ((dst = mib_getptr(node)) == NULL) 1086 return EINVAL; 1087 1088 switch (SYSCTL_TYPE(node->node_flags)) { 1089 case CTLTYPE_STRING: 1090 /* 1091 * Strings must not exceed their buffer size. There is a 1092 * second check further below, because we allow userland to 1093 * give us an unterminated string. In that case we terminate 1094 * it ourselves, but then the null terminator must fit as well. 1095 */ 1096 if (newlen > node->node_size) 1097 return EINVAL; 1098 break; 1099 case CTLTYPE_BOOL: 1100 case CTLTYPE_INT: 1101 case CTLTYPE_QUAD: 1102 case CTLTYPE_STRUCT: 1103 /* Non-string types must have an exact size match. */ 1104 if (newlen != node->node_size) 1105 return EINVAL; 1106 break; 1107 default: 1108 return EINVAL; 1109 } 1110 1111 /* 1112 * If we cannot fit the data in the small stack buffer, then allocate a 1113 * temporary buffer. We add one extra byte so that we can add a null 1114 * terminator at the end of strings in case userland did not supply 1115 * one. Either way, we must free the temporary buffer later! 1116 * 1117 * The alternative is to ensure that the given memory is accessible 1118 * before starting the copy, but that would break if we ever add kernel 1119 * threads or anything that allows asynchronous memory unmapping, etc. 1120 */ 1121 if (newlen + 1 > sizeof(scratch)) { 1122 /* 1123 * In practice, the temporary buffer is at least an entire 1124 * memory page, which is reasonable by any standard. As a 1125 * result, we can get away with refusing to perform dynamic 1126 * allocation for unprivileged users. This limits the impact 1127 * that unprivileged users can have on our memory space. 1128 */ 1129 if (!mib_authed(call)) 1130 return EPERM; 1131 1132 /* 1133 * Do not return ENOMEM on allocation failure, because ENOMEM 1134 * implies that a valid old length was returned. 1135 */ 1136 if ((src = malloc(newlen + 1)) == NULL) { 1137 printf("MIB: out of memory!\n"); 1138 1139 return EINVAL; 1140 } 1141 objects++; 1142 } else 1143 src = scratch; 1144 1145 /* Copy in the data. Note that newlen may be zero. */ 1146 r = mib_copyin(newp, src, newlen); 1147 1148 if (r == OK && verify != NULL && !verify(call, node, src, newlen)) 1149 r = EINVAL; 1150 1151 if (r == OK) { 1152 /* Check and, if acceptable, store the new value. */ 1153 switch (SYSCTL_TYPE(node->node_flags)) { 1154 case CTLTYPE_BOOL: 1155 /* 1156 * Due to the nature of the C99 _Bool type, we can not 1157 * test directly whether the given boolean value is a 1158 * value that is not "true" and not "false". In the 1159 * worst case, another value could invoke undefined 1160 * behavior. We try our best to sanitize the value 1161 * without looking at it directly, which unfortunately 1162 * requires us to test for the size of the bool type. 1163 * We do that at compile time, hence the 'b' "array". 1164 * Any size other than one byte is an ABI violation. 1165 */ 1166 b[0] = (bool)src[0]; 1167 memcpy(dst, &b[0], sizeof(b[0])); 1168 break; 1169 case CTLTYPE_INT: 1170 case CTLTYPE_QUAD: 1171 case CTLTYPE_STRUCT: 1172 memcpy(dst, src, node->node_size); 1173 break; 1174 case CTLTYPE_STRING: 1175 if (newlen == node->node_size && 1176 src[newlen - 1] != '\0') { 1177 /* Our null terminator does not fit! */ 1178 r = EINVAL; 1179 break; 1180 } 1181 /* 1182 * We do not mind null characters in the middle. In 1183 * general, the buffer may contain garbage after the 1184 * first null terminator, but such garbage will never 1185 * end up being copied out. 1186 */ 1187 src[newlen] = '\0'; 1188 strlcpy(dst, src, node->node_size); 1189 break; 1190 default: 1191 r = EINVAL; 1192 } 1193 } 1194 1195 if (src != scratch) { 1196 free(src); 1197 objects--; 1198 } 1199 1200 return r; 1201 } 1202 1203 /* 1204 * Read and/or write the value of a regular data node. A regular data node is 1205 * a leaf node. Typically, a leaf node has no associated function, in which 1206 * case this function will be used instead. In addition, this function may be 1207 * used from handler functions as part of their functionality. 1208 */ 1209 ssize_t 1210 mib_readwrite(struct mib_call * call, struct mib_node * node, 1211 struct mib_oldp * oldp, struct mib_newp * newp, mib_verify_ptr verify) 1212 { 1213 ssize_t len; 1214 int r; 1215 1216 /* Copy out old data, if requested. Always get the old data length. */ 1217 if ((r = len = mib_read(node, oldp)) < 0) 1218 return r; 1219 1220 /* Copy in new data, if requested. */ 1221 if ((r = mib_write(call, node, newp, verify)) != OK) 1222 return r; 1223 1224 /* Return the old data length. */ 1225 return len; 1226 } 1227 1228 /* 1229 * Dispatch a sysctl call, by looking up the target node by its MIB name and 1230 * taking the appropriate action on the resulting node, if found. Return the 1231 * old data length on success, or a negative error code on failure. 1232 */ 1233 ssize_t 1234 mib_dispatch(struct mib_call * call, struct mib_node * root, 1235 struct mib_oldp * oldp, struct mib_newp * newp) 1236 { 1237 struct mib_node *stack[CTL_MAXNAME]; 1238 struct mib_node *parent, *node; 1239 int id, depth, is_leaf, has_verify, has_func; 1240 1241 assert(call->call_namelen <= CTL_MAXNAME); 1242 1243 /* 1244 * Resolve the name by descending into the node tree, level by level, 1245 * starting at the MIB root. 1246 */ 1247 depth = 0; 1248 1249 for (parent = root; call->call_namelen > 0; parent = node) { 1250 /* 1251 * For node creation and destruction, build a node stack, to 1252 * allow for up-propagation of new node version numbers. 1253 */ 1254 stack[depth++] = parent; 1255 1256 id = call->call_name[0]; 1257 call->call_name++; 1258 call->call_namelen--; 1259 1260 assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE); 1261 assert(parent->node_flags & CTLFLAG_PARENT); 1262 1263 /* 1264 * Check for meta-identifiers. Regular identifiers are never 1265 * negative, although node handler functions may take subpaths 1266 * with negative identifiers that are not meta-identifiers 1267 * (e.g., see KERN_PROC2). 1268 */ 1269 if (id < 0) { 1270 /* 1271 * A meta-identifier must always be the last name 1272 * component. 1273 */ 1274 if (call->call_namelen > 0) 1275 return EINVAL; 1276 1277 switch (id) { 1278 case CTL_QUERY: 1279 return mib_query(call, parent, oldp, newp, 1280 root); 1281 case CTL_CREATE: 1282 return mib_create(call, parent, oldp, newp, 1283 stack, depth); 1284 case CTL_DESTROY: 1285 return mib_destroy(call, parent, oldp, newp, 1286 stack, depth); 1287 case CTL_DESCRIBE: 1288 return mib_describe(call, parent, oldp, newp); 1289 case CTL_CREATESYM: 1290 case CTL_MMAP: 1291 default: 1292 return EOPNOTSUPP; 1293 } 1294 } 1295 1296 /* Locate the child node. */ 1297 if ((node = mib_find(parent, id, NULL /*prevp*/)) == NULL) 1298 return ENOENT; 1299 1300 /* Check if access is permitted at this level. */ 1301 if ((node->node_flags & CTLFLAG_PRIVATE) && !mib_authed(call)) 1302 return EPERM; 1303 1304 /* 1305 * Is this a leaf node, and/or is this node handled by a 1306 * function? If either is true, resolution ends at this level. 1307 * In order to save a few bytes of memory per node, we use 1308 * different ways to determine whether there is a function 1309 * depending on whether the node is a leaf or not. 1310 */ 1311 is_leaf = (SYSCTL_TYPE(node->node_flags) != CTLTYPE_NODE); 1312 if (is_leaf) { 1313 has_verify = (node->node_flags & CTLFLAG_VERIFY); 1314 has_func = (!has_verify && node->node_func != NULL); 1315 } else { 1316 has_verify = FALSE; 1317 has_func = !(node->node_flags & CTLFLAG_PARENT); 1318 } 1319 1320 /* 1321 * The name may be longer only if the node is not a leaf. That 1322 * also applies to leaves with functions, so check this first. 1323 */ 1324 if (is_leaf && call->call_namelen > 0) 1325 return ENOTDIR; 1326 1327 /* 1328 * If resolution indeed ends here, and the user supplied new 1329 * data, check if writing is allowed. For functions, it is 1330 * arguable whether we should do this check here already. 1331 * However, for now, this approach covers all our use cases. 1332 */ 1333 if ((is_leaf || has_func) && newp != NULL) { 1334 if (!(node->node_flags & CTLFLAG_READWRITE)) 1335 return EPERM; 1336 1337 /* 1338 * Unless nonprivileged users may write to this node, 1339 * ensure that the user has superuser privileges. The 1340 * ANYWRITE flag does not override the READWRITE flag. 1341 */ 1342 if (!(node->node_flags & CTLFLAG_ANYWRITE) && 1343 !mib_authed(call)) 1344 return EPERM; 1345 } 1346 1347 /* If this node has a handler function, let it do the work. */ 1348 if (has_func) 1349 return node->node_func(call, node, oldp, newp); 1350 1351 /* For regular data leaf nodes, handle generic access. */ 1352 if (is_leaf) 1353 return mib_readwrite(call, node, oldp, newp, 1354 has_verify ? node->node_verify : NULL); 1355 1356 /* No function and not a leaf? Descend further. */ 1357 } 1358 1359 /* If we get here, the name refers to a node array. */ 1360 return EISDIR; 1361 } 1362 1363 /* 1364 * Recursively initialize the static tree at initialization time. 1365 */ 1366 static void 1367 mib_tree_recurse(struct mib_node * parent) 1368 { 1369 struct mib_node *node; 1370 int id; 1371 1372 assert(SYSCTL_TYPE(parent->node_flags) == CTLTYPE_NODE); 1373 assert(parent->node_flags & CTLFLAG_PARENT); 1374 1375 /* 1376 * Later on, node_csize and node_clen will also include dynamically 1377 * created nodes. This means that we cannot use node_csize to iterate 1378 * over the static nodes. 1379 */ 1380 parent->node_csize = parent->node_size; 1381 1382 node = parent->node_scptr; 1383 1384 for (id = 0; IS_STATIC_ID(parent, id); id++, node++) { 1385 if (node->node_flags == 0) 1386 continue; 1387 1388 nodes++; 1389 1390 parent->node_clen++; 1391 1392 node->node_ver = parent->node_ver; 1393 1394 /* Recursively apply this function to all node children. */ 1395 if (SYSCTL_TYPE(node->node_flags) == CTLTYPE_NODE && 1396 (node->node_flags & CTLFLAG_PARENT)) 1397 mib_tree_recurse(node); 1398 } 1399 } 1400 1401 /* 1402 * Go through the entire static tree, recursively, initializing some values 1403 * that could not be assigned at compile time. 1404 */ 1405 void 1406 mib_tree_init(struct mib_node * root) 1407 { 1408 1409 /* Initialize some variables. */ 1410 nodes = 1; /* the root node itself */ 1411 objects = 0; 1412 1413 /* The entire tree starts with the same, nonzero node version. */ 1414 root->node_ver = 1; 1415 1416 /* Recursively initialize the static tree. */ 1417 mib_tree_recurse(root); 1418 } 1419