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