1 /* Service support for remote MIB subtrees - by D.C. van Moolenbroek */ 2 /* 3 * In effect, this is a lightweight version of the MIB service's main and tree 4 * code. Some parts of the code have even been copied almost as is, even 5 * though the copy here operates on slightly different data structures in order 6 * to keep the implementation more lightweight. For clarification on many 7 * aspects of the source code here, see the source code of the MIB service. 8 * 9 * There is no way for this module to get to know about MIB service deaths 10 * without possibly interfering with the main code of the service this module 11 * is a part of. As a result, re-registration of mount points after a MIB 12 * service restart is not automatic. Instead, the main service code could 13 * implement re-registration by first calling rmib_reset() and then making the 14 * appropriate rmib_register() calls again. TODO: it would be nicer if this 15 * module implemented re-registration, but that requires saving the MIB path 16 * for each of the registered subtrees. 17 */ 18 19 #include <minix/drivers.h> 20 #include <minix/sysctl.h> 21 #include <minix/rmib.h> 22 23 /* Structures for outgoing and incoming data, deliberately distinctly named. */ 24 struct rmib_oldp { 25 cp_grant_id_t oldp_grant; 26 size_t oldp_len; 27 }; 28 29 struct rmib_newp { 30 cp_grant_id_t newp_grant; 31 size_t newp_len; 32 }; 33 34 /* 35 * The maximum field size, in bytes, for which updates (i.e., writes) to the 36 * field do not require dynamic memory allocation. By policy, non-root users 37 * may not update fields exceeding this size at all. For strings, this size 38 * includes an extra byte for adding a null terminator if missing. As the name 39 * indicates, a buffer of this size is placed on the stack. 40 */ 41 #define RMIB_STACKBUF 257 42 43 /* 44 * The maximum number of subtrees that this service can mount. This value can 45 * be increased without any problems, but it is already quite high in practice. 46 */ 47 #define RMIB_MAX_SUBTREES 16 48 49 /* 50 * The array of subtree root nodes. Each root node's array index is the root 51 * identifier used in communication with the MIB service. 52 */ 53 static struct rmib_node *rnodes[RMIB_MAX_SUBTREES] = { NULL }; 54 55 /* 56 * Return TRUE or FALSE indicating whether the given offset is within the range 57 * of data that is to be copied out. This call can be used to test whether 58 * certain bits of data need to be prepared for copying at all. 59 */ 60 int 61 rmib_inrange(struct rmib_oldp * oldp, size_t off) 62 { 63 64 if (oldp == NULL) 65 return FALSE; 66 67 return (off < oldp->oldp_len); 68 } 69 70 /* 71 * Return the total length of the requested data. This should not be used 72 * directly except in highly unusual cases, such as particular node requests 73 * where the request semantics blatantly violate overall sysctl(2) semantics. 74 */ 75 size_t 76 rmib_getoldlen(struct rmib_oldp * oldp) 77 { 78 79 if (oldp == NULL) 80 return 0; 81 82 return oldp->oldp_len; 83 } 84 85 /* 86 * Copy out (partial) data to the user. The copy is automatically limited to 87 * the range of data requested by the user. Return the requested length on 88 * success (for the caller's convenience) or an error code on failure. 89 */ 90 ssize_t 91 rmib_copyout(struct rmib_oldp * __restrict oldp, size_t off, 92 const void * __restrict buf, size_t size) 93 { 94 size_t len; 95 int r; 96 97 len = size; 98 assert(len <= SSIZE_MAX); 99 100 if (oldp == NULL || off >= oldp->oldp_len) 101 return size; /* nothing to do */ 102 103 if (len > oldp->oldp_len - off) 104 len = oldp->oldp_len - off; 105 106 if ((r = sys_safecopyto(MIB_PROC_NR, oldp->oldp_grant, off, 107 (vir_bytes)buf, len)) != OK) 108 return r; 109 110 return size; 111 } 112 113 /* 114 * Copy in data from the user. The given length must match exactly the length 115 * given by the user. Return OK or an error code. 116 */ 117 int 118 rmib_copyin(struct rmib_newp * __restrict newp, void * __restrict buf, 119 size_t len) 120 { 121 122 if (newp == NULL || len != newp->newp_len) 123 return EINVAL; 124 125 if (len == 0) 126 return OK; 127 128 return sys_safecopyfrom(MIB_PROC_NR, newp->newp_grant, 0, 129 (vir_bytes)buf, len); 130 } 131 132 /* 133 * Copy out a node to userland, using the exchange format for nodes (namely, 134 * a sysctlnode structure). Return the size of the object that is (or, if the 135 * node falls outside the requested data range, would be) copied out on 136 * success, or a negative error code on failure. 137 */ 138 static ssize_t 139 rmib_copyout_node(struct rmib_call * call, struct rmib_oldp * oldp, 140 ssize_t off, unsigned int id, const struct rmib_node * rnode) 141 { 142 struct sysctlnode scn; 143 int visible; 144 145 if (!rmib_inrange(oldp, off)) 146 return sizeof(scn); /* nothing to do */ 147 148 memset(&scn, 0, sizeof(scn)); 149 150 /* 151 * The RMIB implementation does not overload flags, so it also need not 152 * hide any of them from the user. 153 */ 154 scn.sysctl_flags = SYSCTL_VERSION | rnode->rnode_flags; 155 scn.sysctl_num = id; 156 strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name)); 157 scn.sysctl_ver = call->call_rootver; 158 scn.sysctl_size = rnode->rnode_size; 159 160 /* Some information is only visible if the user can access the node. */ 161 visible = (!(rnode->rnode_flags & CTLFLAG_PRIVATE) || 162 (call->call_flags & RMIB_FLAG_AUTH)); 163 164 /* 165 * For immediate types, store the immediate value in the resulting 166 * structure, unless the caller is not authorized to obtain the value. 167 */ 168 if ((rnode->rnode_flags & CTLFLAG_IMMEDIATE) && visible) { 169 switch (SYSCTL_TYPE(rnode->rnode_flags)) { 170 case CTLTYPE_BOOL: 171 scn.sysctl_bdata = rnode->rnode_bool; 172 break; 173 case CTLTYPE_INT: 174 scn.sysctl_idata = rnode->rnode_int; 175 break; 176 case CTLTYPE_QUAD: 177 scn.sysctl_qdata = rnode->rnode_quad; 178 break; 179 } 180 } 181 182 /* Special rules apply to parent nodes. */ 183 if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) { 184 /* Report the node size the way NetBSD does, just in case. */ 185 scn.sysctl_size = sizeof(scn); 186 187 /* 188 * For real parent nodes, report child information, but only if 189 * the node itself is accessible by the caller. For function- 190 * driven nodes, set a nonzero function address, for trace(1). 191 */ 192 if (rnode->rnode_func == NULL && visible) { 193 scn.sysctl_csize = rnode->rnode_size; 194 scn.sysctl_clen = rnode->rnode_clen; 195 } else if (rnode->rnode_func != NULL) 196 scn.sysctl_func = SYSCTL_NODE_FN; 197 } 198 199 /* Copy out the resulting node. */ 200 return rmib_copyout(oldp, off, &scn, sizeof(scn)); 201 } 202 203 /* 204 * Given a query on a non-leaf (parent) node, provide the user with an array of 205 * this node's children. 206 */ 207 static ssize_t 208 rmib_query(struct rmib_call * call, struct rmib_node * rparent, 209 struct rmib_oldp * oldp, struct rmib_newp * newp) 210 { 211 struct sysctlnode scn; 212 struct rmib_node *rnode; 213 unsigned int id; 214 ssize_t r, off; 215 216 /* If the user passed in version numbers, check them. */ 217 if (newp != NULL) { 218 if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK) 219 return r; 220 221 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) 222 return EINVAL; 223 224 /* 225 * If a node version number is given, it must match the version 226 * of the subtree or the root of the entire MIB version. 227 */ 228 if (scn.sysctl_ver != 0 && 229 scn.sysctl_ver != call->call_rootver && 230 scn.sysctl_ver != call->call_treever) 231 return EINVAL; 232 } 233 234 /* Enumerate the child nodes of the given parent node. */ 235 off = 0; 236 237 for (id = 0; id < rparent->rnode_size; id++) { 238 rnode = &rparent->rnode_cptr[id]; 239 240 if (rnode->rnode_flags == 0) 241 continue; 242 243 if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0) 244 return r; 245 off += r; 246 } 247 248 return off; 249 } 250 251 /* 252 * Copy out a node description to userland, using the exchange format for node 253 * descriptions (namely, a sysctldesc structure). Return the size of the 254 * object that is (or, if the description falls outside the requested data 255 * range, would be) copied out on success, or a negative error code on failure. 256 * The function may return 0 to indicate that nothing was copied out after all. 257 */ 258 static ssize_t 259 rmib_copyout_desc(struct rmib_call * call, struct rmib_oldp * oldp, 260 ssize_t off, unsigned int id, const struct rmib_node * rnode) 261 { 262 struct sysctldesc scd; 263 size_t len, size; 264 ssize_t r; 265 266 /* Descriptions of private nodes are considered private too. */ 267 if ((rnode->rnode_flags & CTLFLAG_PRIVATE) && 268 !(call->call_flags & RMIB_FLAG_AUTH)) 269 return 0; 270 271 /* 272 * Unfortunately, we do not have a scratch buffer here. Instead, copy 273 * out the description structure and the actual description string 274 * separately. This is more costly, but remote subtrees are already 275 * not going to give the best performance ever. We do optimize for the 276 * case that there is no description, because that is relatively easy. 277 */ 278 /* The description length includes the null terminator. */ 279 if (rnode->rnode_desc != NULL) 280 len = strlen(rnode->rnode_desc) + 1; 281 else 282 len = 1; 283 284 memset(&scd, 0, sizeof(scd)); 285 scd.descr_num = id; 286 scd.descr_ver = call->call_rootver; 287 scd.descr_len = len; 288 289 size = offsetof(struct sysctldesc, descr_str); 290 291 if (len == 1) { 292 scd.descr_str[0] = '\0'; /* superfluous */ 293 size++; 294 } 295 296 /* Copy out the structure, possibly including a null terminator. */ 297 if ((r = rmib_copyout(oldp, off, &scd, size)) < 0) 298 return r; 299 300 if (len > 1) { 301 /* Copy out the description itself. */ 302 if ((r = rmib_copyout(oldp, off + size, rnode->rnode_desc, 303 len)) < 0) 304 return r; 305 306 size += len; 307 } 308 309 /* 310 * By aligning just the size, we may leave garbage between the entries 311 * copied out, which is fine because it is userland's own data. 312 */ 313 return roundup2(size, sizeof(int32_t)); 314 } 315 316 /* 317 * Retrieve node descriptions in bulk, or retrieve a particular node's 318 * description. 319 */ 320 static ssize_t 321 rmib_describe(struct rmib_call * call, struct rmib_node * rparent, 322 struct rmib_oldp * oldp, struct rmib_newp * newp) 323 { 324 struct sysctlnode scn; 325 struct rmib_node *rnode; 326 unsigned int id; 327 ssize_t r, off; 328 329 if (newp != NULL) { 330 if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK) 331 return r; 332 333 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) 334 return EINVAL; 335 336 /* Locate the child node. */ 337 if ((unsigned int)scn.sysctl_num >= rparent->rnode_size) 338 return ENOENT; 339 rnode = &rparent->rnode_cptr[scn.sysctl_num]; 340 if (rnode->rnode_flags == 0) 341 return ENOENT; 342 343 /* Descriptions of private nodes are considered private too. */ 344 if ((rnode->rnode_flags & CTLFLAG_PRIVATE) && 345 !(call->call_flags & RMIB_FLAG_AUTH)) 346 return EPERM; 347 348 /* 349 * If a description pointer was given, this is a request to 350 * set the node's description. We do not allow this, nor would 351 * we be able to support it, since we cannot access the data. 352 */ 353 if (scn.sysctl_desc != NULL) 354 return EPERM; 355 356 /* 357 * Copy out the requested node's description. At this point we 358 * should be sure that this call does not return zero. 359 */ 360 return rmib_copyout_desc(call, oldp, 0, scn.sysctl_num, rnode); 361 } 362 363 /* Describe the child nodes of the given parent node. */ 364 off = 0; 365 366 for (id = 0; id < rparent->rnode_size; id++) { 367 rnode = &rparent->rnode_cptr[id]; 368 369 if (rnode->rnode_flags == 0) 370 continue; 371 372 if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0) 373 return r; 374 off += r; 375 } 376 377 return off; 378 } 379 380 /* 381 * Return a pointer to the data associated with the given node, or NULL if the 382 * node has no associated data. Actual calls to this function should never 383 * result in NULL - as long as the proper rules are followed elsewhere. 384 */ 385 static void * 386 rmib_getptr(struct rmib_node * rnode) 387 { 388 389 switch (SYSCTL_TYPE(rnode->rnode_flags)) { 390 case CTLTYPE_BOOL: 391 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) 392 return &rnode->rnode_bool; 393 break; 394 case CTLTYPE_INT: 395 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) 396 return &rnode->rnode_int; 397 break; 398 case CTLTYPE_QUAD: 399 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) 400 return &rnode->rnode_quad; 401 break; 402 case CTLTYPE_STRING: 403 case CTLTYPE_STRUCT: 404 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) 405 return NULL; 406 break; 407 default: 408 return NULL; 409 } 410 411 return rnode->rnode_data; 412 } 413 414 /* 415 * Read current (old) data from a regular data node, if requested. Return the 416 * old data length. 417 */ 418 static ssize_t 419 rmib_read(struct rmib_node * rnode, struct rmib_oldp * oldp) 420 { 421 void *ptr; 422 size_t oldlen; 423 int r; 424 425 if ((ptr = rmib_getptr(rnode)) == NULL) 426 return EINVAL; 427 428 if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_STRING) 429 oldlen = strlen(rnode->rnode_data) + 1; 430 else 431 oldlen = rnode->rnode_size; 432 433 if (oldlen > SSIZE_MAX) 434 return EINVAL; 435 436 /* Copy out the current data, if requested at all. */ 437 if (oldp != NULL && (r = rmib_copyout(oldp, 0, ptr, oldlen)) < 0) 438 return r; 439 440 /* Return the current length in any case. */ 441 return (ssize_t)oldlen; 442 } 443 444 /* 445 * Write new data into a regular data node, if requested. 446 */ 447 static int 448 rmib_write(struct rmib_call * call, struct rmib_node * rnode, 449 struct rmib_newp * newp) 450 { 451 bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* for sanitizing */ 452 char *src, *dst, buf[RMIB_STACKBUF]; 453 size_t newlen; 454 int r; 455 456 if (newp == NULL) 457 return OK; /* nothing to do */ 458 459 /* 460 * When setting a new value, we cannot risk doing an in-place update: 461 * the copy from userland may fail halfway through, in which case an 462 * in-place update could leave the node value in a corrupted state. 463 * Thus, we must first fetch any new data into a temporary buffer. 464 */ 465 newlen = newp->newp_len; 466 467 if ((dst = rmib_getptr(rnode)) == NULL) 468 return EINVAL; 469 470 switch (SYSCTL_TYPE(rnode->rnode_flags)) { 471 case CTLTYPE_BOOL: 472 case CTLTYPE_INT: 473 case CTLTYPE_QUAD: 474 case CTLTYPE_STRUCT: 475 /* Non-string types must have an exact size match. */ 476 if (newlen != rnode->rnode_size) 477 return EINVAL; 478 break; 479 case CTLTYPE_STRING: 480 /* 481 * Strings must not exceed their buffer size. There is a 482 * second check further below, because we allow userland to 483 * give us an unterminated string. In that case we terminate 484 * it ourselves, but then the null terminator must fit as well. 485 */ 486 if (newlen > rnode->rnode_size) 487 return EINVAL; 488 break; 489 default: 490 return EINVAL; 491 } 492 493 /* 494 * If we cannot fit the data in the small stack buffer, then allocate a 495 * temporary buffer. We add one extra byte so that we can add a null 496 * terminator at the end of strings in case userland did not supply 497 * one. Either way, we must free the temporary buffer later! 498 */ 499 if (newlen + 1 > sizeof(buf)) { 500 /* 501 * For regular users, we do not want to perform dynamic memory 502 * allocation. Thus, for CTLTYPE_ANYWRITE nodes, only the 503 * superuser may set values exceeding the small buffer in size. 504 */ 505 if (!(call->call_flags & RMIB_FLAG_AUTH)) 506 return EPERM; 507 508 /* Do not return ENOMEM on allocation failure. */ 509 if ((src = malloc(newlen + 1)) == NULL) 510 return EINVAL; 511 } else 512 src = buf; 513 514 /* Copy in the data. Note that the given new length may be zero. */ 515 if ((r = rmib_copyin(newp, src, newlen)) == OK) { 516 /* Check and, if acceptable, store the new value. */ 517 switch (SYSCTL_TYPE(rnode->rnode_flags)) { 518 case CTLTYPE_BOOL: 519 /* Sanitize booleans. See the MIB code for details. */ 520 b[0] = (bool)src[0]; 521 memcpy(dst, &b[0], sizeof(b[0])); 522 break; 523 case CTLTYPE_INT: 524 case CTLTYPE_QUAD: 525 case CTLTYPE_STRUCT: 526 memcpy(dst, src, rnode->rnode_size); 527 break; 528 case CTLTYPE_STRING: 529 if (newlen == rnode->rnode_size && 530 src[newlen - 1] != '\0') { 531 /* Our null terminator does not fit! */ 532 r = EINVAL; 533 break; 534 } 535 src[newlen] = '\0'; 536 strlcpy(dst, src, rnode->rnode_size); 537 break; 538 default: 539 r = EINVAL; 540 } 541 } 542 543 if (src != buf) 544 free(src); 545 546 return r; 547 } 548 549 /* 550 * Read and/or write the value of a regular data node. A regular data node is 551 * a leaf node. Typically, a leaf node has no associated function, in which 552 * case this function will be used instead. In addition, this function may be 553 * used from handler functions as part of their functionality. 554 */ 555 ssize_t 556 rmib_readwrite(struct rmib_call * call, struct rmib_node * rnode, 557 struct rmib_oldp * oldp, struct rmib_newp * newp) 558 { 559 ssize_t len; 560 int r; 561 562 /* Copy out old data, if requested. Always get the old data length. */ 563 if ((r = len = rmib_read(rnode, oldp)) < 0) 564 return r; 565 566 /* Copy in new data, if requested. */ 567 if ((r = rmib_write(call, rnode, newp)) != OK) 568 return r; 569 570 /* Return the old data length. */ 571 return len; 572 } 573 574 /* 575 * Handle a sysctl(2) call from a user process, relayed by the MIB service to 576 * us. If the call succeeds, return the old length. The MIB service will 577 * perform a check against the given old length and return ENOMEM to the caller 578 * when applicable, so we do not have to do that here. If the call fails, 579 * return a negative error code. 580 */ 581 static ssize_t 582 rmib_call(const message * m_in) 583 { 584 struct rmib_node *rnode, *rparent; 585 struct rmib_call call; 586 struct rmib_oldp oldp_data, *oldp; 587 struct rmib_newp newp_data, *newp; 588 unsigned int root_id, namelen; 589 int r, id, is_leaf, has_func, name[CTL_MAXNAME]; 590 591 /* 592 * Look up the root of the subtree that is the subject of the call. If 593 * the call is for a subtree that is not registered, return ERESTART to 594 * indicate to the MIB service that it should deregister the subtree it 595 * thinks we have. This case may occur in practice if a deregistration 596 * request from us crosses a sysctl call request from the MIB service. 597 */ 598 root_id = m_in->m_mib_lsys_call.root_id; 599 if (root_id >= __arraycount(rnodes) || rnodes[root_id] == NULL) 600 return ERESTART; 601 rnode = rnodes[root_id]; 602 603 /* 604 * Set up all data structures that we need to use while handling the 605 * call processing. Start by copying in the remainder of the MIB name. 606 */ 607 /* A zero name length is valid and should always yield EISDIR. */ 608 namelen = m_in->m_mib_lsys_call.name_len; 609 if (namelen > __arraycount(name)) 610 return EINVAL; 611 612 if (namelen > 0) { 613 r = sys_safecopyfrom(m_in->m_source, 614 m_in->m_mib_lsys_call.name_grant, 0, (vir_bytes)name, 615 sizeof(name[0]) * namelen); 616 if (r != OK) 617 return r; 618 } 619 620 oldp_data.oldp_grant = m_in->m_mib_lsys_call.oldp_grant; 621 oldp_data.oldp_len = m_in->m_mib_lsys_call.oldp_len; 622 oldp = (GRANT_VALID(oldp_data.oldp_grant)) ? &oldp_data : NULL; 623 624 newp_data.newp_grant = m_in->m_mib_lsys_call.newp_grant; 625 newp_data.newp_len = m_in->m_mib_lsys_call.newp_len; 626 newp = (GRANT_VALID(newp_data.newp_grant)) ? &newp_data : NULL; 627 628 call.call_endpt = m_in->m_mib_lsys_call.user_endpt; 629 call.call_name = name; 630 call.call_namelen = namelen; 631 call.call_flags = m_in->m_mib_lsys_call.flags; 632 call.call_rootver = m_in->m_mib_lsys_call.root_ver; 633 call.call_treever = m_in->m_mib_lsys_call.tree_ver; 634 635 /* 636 * Dispatch the call. 637 */ 638 for (rparent = rnode; call.call_namelen > 0; rparent = rnode) { 639 id = call.call_name[0]; 640 call.call_name++; 641 call.call_namelen--; 642 643 assert(SYSCTL_TYPE(rparent->rnode_flags) == CTLTYPE_NODE); 644 645 /* Check for meta-identifiers. */ 646 if (id < 0) { 647 /* 648 * A meta-identifier must always be the last name 649 * component. 650 */ 651 if (call.call_namelen > 0) 652 return EINVAL; 653 654 switch (id) { 655 case CTL_QUERY: 656 return rmib_query(&call, rparent, oldp, newp); 657 case CTL_DESCRIBE: 658 return rmib_describe(&call, rparent, oldp, 659 newp); 660 case CTL_CREATE: 661 case CTL_DESTROY: 662 /* We support fully static subtrees only. */ 663 return EPERM; 664 default: 665 return EOPNOTSUPP; 666 } 667 } 668 669 /* Locate the child node. */ 670 if ((unsigned int)id >= rparent->rnode_size) 671 return ENOENT; 672 rnode = &rparent->rnode_cptr[id]; 673 if (rnode->rnode_flags == 0) 674 return ENOENT; 675 676 /* Check if access is permitted at this level. */ 677 if ((rnode->rnode_flags & CTLFLAG_PRIVATE) && 678 !(call.call_flags & RMIB_FLAG_AUTH)) 679 return EPERM; 680 681 /* 682 * Is this a leaf node, and/or is this node handled by a 683 * function? If either is true, resolution ends at this level. 684 */ 685 is_leaf = (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE); 686 has_func = (rnode->rnode_func != NULL); 687 688 /* 689 * The name may be longer only if the node is not a leaf. That 690 * also applies to leaves with functions, so check this first. 691 */ 692 if (is_leaf && call.call_namelen > 0) 693 return ENOTDIR; 694 695 /* 696 * If resolution indeed ends here, and the user supplied new 697 * data, check if writing is allowed. 698 */ 699 if ((is_leaf || has_func) && newp != NULL) { 700 if (!(rnode->rnode_flags & CTLFLAG_READWRITE)) 701 return EPERM; 702 703 if (!(rnode->rnode_flags & CTLFLAG_ANYWRITE) && 704 !(call.call_flags & RMIB_FLAG_AUTH)) 705 return EPERM; 706 } 707 708 /* If this node has a handler function, let it do the work. */ 709 if (has_func) 710 return rnode->rnode_func(&call, rnode, oldp, newp); 711 712 /* For regular data leaf nodes, handle generic access. */ 713 if (is_leaf) 714 return rmib_readwrite(&call, rnode, oldp, newp); 715 716 /* No function and not a leaf? Descend further. */ 717 } 718 719 /* If we get here, the name refers to a node array. */ 720 return EISDIR; 721 } 722 723 /* 724 * Initialize the given node and recursively all its node-type children, 725 * assigning the proper child length value to each of them. 726 */ 727 static void 728 rmib_init(struct rmib_node * rnode) 729 { 730 struct rmib_node *rchild; 731 unsigned int id; 732 733 rchild = rnode->rnode_cptr; 734 735 for (id = 0; id < rnode->rnode_size; id++, rchild++) { 736 if (rchild->rnode_flags == 0) 737 continue; 738 739 rnode->rnode_clen++; 740 741 if (SYSCTL_TYPE(rchild->rnode_flags) == CTLTYPE_NODE) 742 rmib_init(rchild); /* recurse */ 743 } 744 } 745 746 /* 747 * Register a MIB subtree. Initialize the subtree, add it to the local set, 748 * and send a registration request for it to the MIB service. 749 */ 750 int 751 rmib_register(const int * name, unsigned int namelen, struct rmib_node * rnode) 752 { 753 message m; 754 unsigned int id, free_id; 755 int r; 756 757 /* A few basic sanity checks. */ 758 if (namelen == 0 || namelen >= CTL_SHORTNAME) 759 return EINVAL; 760 if (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE) 761 return EINVAL; 762 763 /* Make sure this is a new subtree, and find a free slot for it. */ 764 for (id = free_id = 0; id < __arraycount(rnodes); id++) { 765 if (rnodes[id] == rnode) 766 return EEXIST; 767 else if (rnodes[id] == NULL && rnodes[free_id] != NULL) 768 free_id = id; 769 } 770 771 if (rnodes[free_id] != NULL) 772 return ENOMEM; 773 774 /* 775 * Initialize the entire subtree. This will also compute rnode_clen 776 * for the given rnode, so do this before sending the message. 777 */ 778 rmib_init(rnode); 779 780 /* 781 * Request that the MIB service mount this subtree. This is a one-way 782 * request, so we never hear whether mounting succeeds. There is not 783 * that much we can do if it fails anyway though. 784 */ 785 memset(&m, 0, sizeof(m)); 786 787 m.m_type = MIB_REGISTER; 788 m.m_lsys_mib_register.root_id = free_id; 789 m.m_lsys_mib_register.flags = SYSCTL_VERSION | rnode->rnode_flags; 790 m.m_lsys_mib_register.csize = rnode->rnode_size; 791 m.m_lsys_mib_register.clen = rnode->rnode_clen; 792 m.m_lsys_mib_register.miblen = namelen; 793 memcpy(m.m_lsys_mib_register.mib, name, sizeof(name[0]) * namelen); 794 795 if ((r = asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY)) == OK) 796 rnodes[free_id] = rnode; 797 798 return r; 799 } 800 801 /* 802 * Deregister a previously registered subtree, both internally and with the MIB 803 * service. Return OK if the deregistration procedure has been started, in 804 * which case the given subtree is guaranteed to no longer be accessed. Return 805 * a negative error code on failure. 806 */ 807 int 808 rmib_deregister(struct rmib_node * rnode) 809 { 810 message m; 811 unsigned int id; 812 813 for (id = 0; id < __arraycount(rnodes); id++) 814 if (rnodes[id] == rnode) 815 break; 816 817 if (id == __arraycount(rnodes)) 818 return ENOENT; 819 820 rnodes[id] = NULL; 821 822 /* 823 * Request that the MIB service unmount the subtree. We completely 824 * ignore failure here, because the caller would not be able to do 825 * anything about it anyway. We may also still receive sysctl call 826 * requests for the node we just deregistered, but this is caught 827 * during request processing. Reuse of the rnodes[] slot could be a 828 * potential problem though. We could use sequence numbers in the root 829 * identifiers to resolve that problem if it ever occurs in reality. 830 */ 831 memset(&m, 0, sizeof(m)); 832 833 m.m_type = MIB_DEREGISTER; 834 m.m_lsys_mib_register.root_id = id; 835 836 (void)asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY); 837 838 return OK; 839 } 840 841 /* 842 * Reset all registrations, without involving MIB communication. This call 843 * must be issued only when the caller has determined that the MIB service has 844 * restarted, and is about to reregister its subtrees. 845 */ 846 void 847 rmib_reset(void) 848 { 849 850 memset(rnodes, 0, sizeof(rnodes)); 851 } 852 853 /* 854 * Process a request from the MIB service for information about the root node 855 * of a subtree, specifically its name and description. 856 */ 857 static int 858 rmib_info(const message * m_in) 859 { 860 struct rmib_node *rnode; 861 unsigned int id; 862 const char *ptr; 863 size_t size; 864 int r; 865 866 id = m_in->m_mib_lsys_info.root_id; 867 if (id >= __arraycount(rnodes) || rnodes[id] == NULL) 868 return ENOENT; 869 rnode = rnodes[id]; 870 871 /* The name must fit. If it does not, the service writer messed up. */ 872 size = strlen(rnode->rnode_name) + 1; 873 if (size > m_in->m_mib_lsys_info.name_size) 874 return ENAMETOOLONG; 875 876 r = sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.name_grant, 0, 877 (vir_bytes)rnode->rnode_name, size); 878 if (r != OK) 879 return r; 880 881 /* If there is no (optional) description, copy out an empty string. */ 882 ptr = (rnode->rnode_desc != NULL) ? rnode->rnode_desc : ""; 883 size = strlen(ptr) + 1; 884 885 if (size > m_in->m_mib_lsys_info.desc_size) 886 size = m_in->m_mib_lsys_info.desc_size; 887 888 return sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.desc_grant, 889 0, (vir_bytes)ptr, size); 890 } 891 892 /* 893 * Process a request from the MIB service. The given message should originate 894 * from the MIB service and have one of the COMMON_MIB_ requests as type. 895 */ 896 void 897 rmib_process(const message * m_in, int ipc_status) 898 { 899 message m_out; 900 uint32_t req_id; 901 ssize_t r; 902 903 /* Only the MIB service may issue these requests. */ 904 if (m_in->m_source != MIB_PROC_NR) 905 return; 906 907 /* Process the actual request. */ 908 switch (m_in->m_type) { 909 case COMMON_MIB_INFO: 910 req_id = m_in->m_mib_lsys_info.req_id; 911 912 r = rmib_info(m_in); 913 914 break; 915 916 case COMMON_MIB_CALL: 917 req_id = m_in->m_mib_lsys_call.req_id; 918 919 r = rmib_call(m_in); 920 921 break; 922 923 default: 924 /* 925 * HACK: assume that for all current and future requests, the 926 * request ID field is in the same place. We could create a 927 * m_mib_lsys_unknown pseudo message type for this, but, eh. 928 */ 929 req_id = m_in->m_mib_lsys_info.req_id; 930 931 r = ENOSYS; 932 } 933 934 /* Construct and send a reply message to the MIB service. */ 935 memset(&m_out, 0, sizeof(m_out)); 936 937 m_out.m_type = COMMON_MIB_REPLY; 938 m_out.m_lsys_mib_reply.req_id = req_id; 939 m_out.m_lsys_mib_reply.status = r; 940 941 if (IPC_STATUS_CALL(ipc_status) == SENDREC) 942 r = ipc_sendnb(m_in->m_source, &m_out); 943 else 944 r = asynsend3(m_in->m_source, &m_out, AMF_NOREPLY); 945 946 if (r != OK) 947 printf("lsys:rmib: unable to send reply to %d: %d\n", 948 m_in->m_source, r); 949 } 950