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