Drop main() prototype. Syncs with NetBSD-8
[minix.git] / minix / lib / libsys / rmib.c
blobe4b686389b71a91c83ddfa2377d7369c7b3833c7
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 * One unique feature here is support for sparse nodes, which is needed for
9 * net.inet/inet6 as those are using subtrees with protocol-based identifiers.
11 * There is no way for this module to get to know about MIB service deaths
12 * without possibly interfering with the main code of the service this module
13 * is a part of. As a result, re-registration of mount points after a MIB
14 * service restart is not automatic. Instead, the main service code should
15 * provide detection of MIB service restarts, and call rmib_reregister() after
16 * such a restart in order to remount any previously mounted subtrees.
19 #include <minix/drivers.h>
20 #include <minix/sysctl.h>
21 #include <minix/rmib.h>
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;
29 struct rmib_newp {
30 cp_grant_id_t newp_grant;
31 size_t newp_len;
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.
41 #define RMIB_STACKBUF 257
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.
47 #define RMIB_MAX_SUBTREES 16
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.
53 static struct {
54 struct rmib_node *rno_node;
55 unsigned int rno_namelen;
56 int rno_name[CTL_SHORTNAME];
57 } rnodes[RMIB_MAX_SUBTREES] = { { NULL, 0, { 0 } } };
60 * Return TRUE or FALSE indicating whether the given offset is within the range
61 * of data that is to be copied out. This call can be used to test whether
62 * certain bits of data need to be prepared for copying at all.
64 int
65 rmib_inrange(struct rmib_oldp * oldp, size_t off)
68 if (oldp == NULL)
69 return FALSE;
71 return (off < oldp->oldp_len);
75 * Return the total length of the requested data. This should not be used
76 * directly except in highly unusual cases, such as particular node requests
77 * where the request semantics blatantly violate overall sysctl(2) semantics.
79 size_t
80 rmib_getoldlen(struct rmib_oldp * oldp)
83 if (oldp == NULL)
84 return 0;
86 return oldp->oldp_len;
90 * Copy out (partial) data to the user. The copy is automatically limited to
91 * the range of data requested by the user. Return the requested length on
92 * success (for the caller's convenience) or an error code on failure.
94 ssize_t
95 rmib_copyout(struct rmib_oldp * __restrict oldp, size_t off,
96 const void * __restrict buf, size_t size)
98 size_t len;
99 int r;
101 len = size;
102 assert(len <= SSIZE_MAX);
104 if (oldp == NULL || off >= oldp->oldp_len)
105 return size; /* nothing to do */
107 if (len > oldp->oldp_len - off)
108 len = oldp->oldp_len - off;
110 if ((r = sys_safecopyto(MIB_PROC_NR, oldp->oldp_grant, off,
111 (vir_bytes)buf, len)) != OK)
112 return r;
114 return size;
118 * Copy out (partial) data to the user, from a vector of up to RMIB_IOV_MAX
119 * local buffers. The copy is automatically limited to the range of data
120 * requested by the user. Return the total requested length on success or an
121 * error code on failure.
123 ssize_t
124 rmib_vcopyout(struct rmib_oldp * oldp, size_t off, const iovec_t * iov,
125 unsigned int iovcnt)
127 static struct vscp_vec vec[RMIB_IOV_MAX];
128 size_t size, chunk;
129 unsigned int i;
130 ssize_t r;
132 assert(iov != NULL);
133 assert(iovcnt <= __arraycount(vec));
135 /* Take a shortcut for single-vector elements, saving a kernel copy. */
136 if (iovcnt == 1)
137 return rmib_copyout(oldp, off, (const void *)iov->iov_addr,
138 iov->iov_size);
141 * Iterate through the full vector even if we cannot copy out all of
142 * it, because we need to compute the total length.
144 for (size = i = 0; iovcnt > 0; iov++, iovcnt--) {
145 if (oldp != NULL && off < oldp->oldp_len) {
146 chunk = oldp->oldp_len - off;
147 if (chunk > iov->iov_size)
148 chunk = iov->iov_size;
150 vec[i].v_from = SELF;
151 vec[i].v_to = MIB_PROC_NR;
152 vec[i].v_gid = oldp->oldp_grant;
153 vec[i].v_offset = off;
154 vec[i].v_addr = iov->iov_addr;
155 vec[i].v_bytes = chunk;
157 off += chunk;
158 i++;
161 size += iov->iov_size;
164 /* Perform the copy, if there is anything to copy, that is. */
165 if (i > 0 && (r = sys_vsafecopy(vec, i)) != OK)
166 return r;
168 return size;
172 * Copy in data from the user. The given length must match exactly the length
173 * given by the user. Return OK or an error code.
176 rmib_copyin(struct rmib_newp * __restrict newp, void * __restrict buf,
177 size_t len)
180 if (newp == NULL || len != newp->newp_len)
181 return EINVAL;
183 if (len == 0)
184 return OK;
186 return sys_safecopyfrom(MIB_PROC_NR, newp->newp_grant, 0,
187 (vir_bytes)buf, len);
191 * Copy out a node to userland, using the exchange format for nodes (namely,
192 * a sysctlnode structure). Return the size of the object that is (or, if the
193 * node falls outside the requested data range, would be) copied out on
194 * success, or a negative error code on failure.
196 static ssize_t
197 rmib_copyout_node(struct rmib_call * call, struct rmib_oldp * oldp,
198 ssize_t off, unsigned int id, const struct rmib_node * rnode)
200 struct sysctlnode scn;
201 int visible;
203 if (!rmib_inrange(oldp, off))
204 return sizeof(scn); /* nothing to do */
206 memset(&scn, 0, sizeof(scn));
209 * We use CTLFLAG_SPARSE internally only. NetBSD uses these flags for
210 * different purposes. Either way, do not expose it to userland.
211 * hide any of them from the user.
213 scn.sysctl_flags = SYSCTL_VERSION |
214 (rnode->rnode_flags & ~CTLFLAG_SPARSE);
215 scn.sysctl_num = id;
216 strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name));
217 scn.sysctl_ver = call->call_rootver;
218 scn.sysctl_size = rnode->rnode_size;
220 /* Some information is only visible if the user can access the node. */
221 visible = (!(rnode->rnode_flags & CTLFLAG_PRIVATE) ||
222 (call->call_flags & RMIB_FLAG_AUTH));
225 * For immediate types, store the immediate value in the resulting
226 * structure, unless the caller is not authorized to obtain the value.
228 if ((rnode->rnode_flags & CTLFLAG_IMMEDIATE) && visible) {
229 switch (SYSCTL_TYPE(rnode->rnode_flags)) {
230 case CTLTYPE_BOOL:
231 scn.sysctl_bdata = rnode->rnode_bool;
232 break;
233 case CTLTYPE_INT:
234 scn.sysctl_idata = rnode->rnode_int;
235 break;
236 case CTLTYPE_QUAD:
237 scn.sysctl_qdata = rnode->rnode_quad;
238 break;
242 /* Special rules apply to parent nodes. */
243 if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) {
244 /* Report the node size the way NetBSD does, just in case. */
245 scn.sysctl_size = sizeof(scn);
248 * For real parent nodes, report child information, but only if
249 * the node itself is accessible by the caller. For function-
250 * driven nodes, set a nonzero function address, for trace(1).
252 if (rnode->rnode_func == NULL && visible) {
253 scn.sysctl_csize = rnode->rnode_size;
254 scn.sysctl_clen = rnode->rnode_clen;
255 } else if (rnode->rnode_func != NULL)
256 scn.sysctl_func = SYSCTL_NODE_FN;
259 /* Copy out the resulting node. */
260 return rmib_copyout(oldp, off, &scn, sizeof(scn));
264 * Given a query on a non-leaf (parent) node, provide the user with an array of
265 * this node's children.
267 static ssize_t
268 rmib_query(struct rmib_call * call, struct rmib_node * rparent,
269 struct rmib_oldp * oldp, struct rmib_newp * newp)
271 struct sysctlnode scn;
272 struct rmib_node *rnode;
273 unsigned int i, id;
274 ssize_t r, off;
276 /* If the user passed in version numbers, check them. */
277 if (newp != NULL) {
278 if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK)
279 return r;
281 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
282 return EINVAL;
285 * If a node version number is given, it must match the version
286 * of the subtree or the root of the entire MIB version.
288 if (scn.sysctl_ver != 0 &&
289 scn.sysctl_ver != call->call_rootver &&
290 scn.sysctl_ver != call->call_treever)
291 return EINVAL;
294 /* Enumerate the child nodes of the given parent node. */
295 off = 0;
297 for (i = 0; i < rparent->rnode_size; i++) {
298 if (rparent->rnode_flags & CTLFLAG_SPARSE) {
299 id = rparent->rnode_icptr[i].rindir_id;
300 rnode = rparent->rnode_icptr[i].rindir_node;
301 } else {
302 id = i;
303 rnode = &rparent->rnode_cptr[i];
305 if (rnode->rnode_flags == 0)
306 continue;
309 if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0)
310 return r;
311 off += r;
314 return off;
318 * Copy out a node description to userland, using the exchange format for node
319 * descriptions (namely, a sysctldesc structure). Return the size of the
320 * object that is (or, if the description falls outside the requested data
321 * range, would be) copied out on success, or a negative error code on failure.
322 * The function may return 0 to indicate that nothing was copied out after all.
324 static ssize_t
325 rmib_copyout_desc(struct rmib_call * call, struct rmib_oldp * oldp,
326 ssize_t off, unsigned int id, const struct rmib_node * rnode)
328 struct sysctldesc scd;
329 size_t len, size;
330 ssize_t r;
332 /* Descriptions of private nodes are considered private too. */
333 if ((rnode->rnode_flags & CTLFLAG_PRIVATE) &&
334 !(call->call_flags & RMIB_FLAG_AUTH))
335 return 0;
338 * Unfortunately, we do not have a scratch buffer here. Instead, copy
339 * out the description structure and the actual description string
340 * separately. This is more costly, but remote subtrees are already
341 * not going to give the best performance ever. We do optimize for the
342 * case that there is no description, because that is relatively easy.
344 /* The description length includes the null terminator. */
345 if (rnode->rnode_desc != NULL)
346 len = strlen(rnode->rnode_desc) + 1;
347 else
348 len = 1;
350 memset(&scd, 0, sizeof(scd));
351 scd.descr_num = id;
352 scd.descr_ver = call->call_rootver;
353 scd.descr_len = len;
355 size = offsetof(struct sysctldesc, descr_str);
357 if (len == 1) {
358 scd.descr_str[0] = '\0'; /* superfluous */
359 size++;
362 /* Copy out the structure, possibly including a null terminator. */
363 if ((r = rmib_copyout(oldp, off, &scd, size)) < 0)
364 return r;
366 if (len > 1) {
367 /* Copy out the description itself. */
368 if ((r = rmib_copyout(oldp, off + size, rnode->rnode_desc,
369 len)) < 0)
370 return r;
372 size += len;
376 * By aligning just the size, we may leave garbage between the entries
377 * copied out, which is fine because it is userland's own data.
379 return roundup2(size, sizeof(int32_t));
383 * Look up a child node given a parent node and a child node identifier.
384 * Return a pointer to the child node if found, or NULL otherwise. The lookup
385 * procedure differs based on whether the parent node is sparse or not.
387 static struct rmib_node *
388 rmib_lookup(struct rmib_node * rparent, unsigned int id)
390 struct rmib_node *rnode;
391 struct rmib_indir *rindir;
392 unsigned int i;
394 if (rparent->rnode_flags & CTLFLAG_SPARSE) {
395 rindir = rparent->rnode_icptr;
396 for (i = 0; i < rparent->rnode_size; i++, rindir++)
397 if (rindir->rindir_id == id)
398 return rindir->rindir_node;
399 } else {
400 if (id >= rparent->rnode_size)
401 return NULL;
402 rnode = &rparent->rnode_cptr[id];
403 if (rnode->rnode_flags != 0)
404 return rnode;
407 return NULL;
411 * Retrieve node descriptions in bulk, or retrieve a particular node's
412 * description.
414 static ssize_t
415 rmib_describe(struct rmib_call * call, struct rmib_node * rparent,
416 struct rmib_oldp * oldp, struct rmib_newp * newp)
418 struct sysctlnode scn;
419 struct rmib_node *rnode;
420 unsigned int i, id;
421 ssize_t r, off;
423 if (newp != NULL) {
424 if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK)
425 return r;
427 if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
428 return EINVAL;
430 /* Locate the child node. */
431 if ((rnode = rmib_lookup(rparent, scn.sysctl_num)) == NULL)
432 return ENOENT;
434 /* Descriptions of private nodes are considered private too. */
435 if ((rnode->rnode_flags & CTLFLAG_PRIVATE) &&
436 !(call->call_flags & RMIB_FLAG_AUTH))
437 return EPERM;
440 * If a description pointer was given, this is a request to
441 * set the node's description. We do not allow this, nor would
442 * we be able to support it, since we cannot access the data.
444 if (scn.sysctl_desc != NULL)
445 return EPERM;
448 * Copy out the requested node's description. At this point we
449 * should be sure that this call does not return zero.
451 return rmib_copyout_desc(call, oldp, 0, scn.sysctl_num, rnode);
454 /* Describe the child nodes of the given parent node. */
455 off = 0;
457 for (i = 0; i < rparent->rnode_size; i++) {
458 if (rparent->rnode_flags & CTLFLAG_SPARSE) {
459 id = rparent->rnode_icptr[i].rindir_id;
460 rnode = rparent->rnode_icptr[i].rindir_node;
461 } else {
462 id = i;
463 rnode = &rparent->rnode_cptr[i];
465 if (rnode->rnode_flags == 0)
466 continue;
469 if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0)
470 return r;
471 off += r;
474 return off;
478 * Return a pointer to the data associated with the given node, or NULL if the
479 * node has no associated data. Actual calls to this function should never
480 * result in NULL - as long as the proper rules are followed elsewhere.
482 static void *
483 rmib_getptr(struct rmib_node * rnode)
486 switch (SYSCTL_TYPE(rnode->rnode_flags)) {
487 case CTLTYPE_BOOL:
488 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
489 return &rnode->rnode_bool;
490 break;
491 case CTLTYPE_INT:
492 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
493 return &rnode->rnode_int;
494 break;
495 case CTLTYPE_QUAD:
496 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
497 return &rnode->rnode_quad;
498 break;
499 case CTLTYPE_STRING:
500 case CTLTYPE_STRUCT:
501 if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
502 return NULL;
503 break;
504 default:
505 return NULL;
508 return rnode->rnode_data;
512 * Read current (old) data from a regular data node, if requested. Return the
513 * old data length.
515 static ssize_t
516 rmib_read(struct rmib_node * rnode, struct rmib_oldp * oldp)
518 void *ptr;
519 size_t oldlen;
520 int r;
522 if ((ptr = rmib_getptr(rnode)) == NULL)
523 return EINVAL;
525 if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_STRING)
526 oldlen = strlen(rnode->rnode_data) + 1;
527 else
528 oldlen = rnode->rnode_size;
530 if (oldlen > SSIZE_MAX)
531 return EINVAL;
533 /* Copy out the current data, if requested at all. */
534 if (oldp != NULL && (r = rmib_copyout(oldp, 0, ptr, oldlen)) < 0)
535 return r;
537 /* Return the current length in any case. */
538 return (ssize_t)oldlen;
542 * Write new data into a regular data node, if requested.
544 static int
545 rmib_write(struct rmib_call * call, struct rmib_node * rnode,
546 struct rmib_newp * newp)
548 bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* for sanitizing */
549 char *src, *dst, buf[RMIB_STACKBUF];
550 size_t newlen;
551 int r;
553 if (newp == NULL)
554 return OK; /* nothing to do */
557 * When setting a new value, we cannot risk doing an in-place update:
558 * the copy from userland may fail halfway through, in which case an
559 * in-place update could leave the node value in a corrupted state.
560 * Thus, we must first fetch any new data into a temporary buffer.
562 newlen = newp->newp_len;
564 if ((dst = rmib_getptr(rnode)) == NULL)
565 return EINVAL;
567 switch (SYSCTL_TYPE(rnode->rnode_flags)) {
568 case CTLTYPE_BOOL:
569 case CTLTYPE_INT:
570 case CTLTYPE_QUAD:
571 case CTLTYPE_STRUCT:
572 /* Non-string types must have an exact size match. */
573 if (newlen != rnode->rnode_size)
574 return EINVAL;
575 break;
576 case CTLTYPE_STRING:
578 * Strings must not exceed their buffer size. There is a
579 * second check further below, because we allow userland to
580 * give us an unterminated string. In that case we terminate
581 * it ourselves, but then the null terminator must fit as well.
583 if (newlen > rnode->rnode_size)
584 return EINVAL;
585 break;
586 default:
587 return EINVAL;
591 * If we cannot fit the data in the small stack buffer, then allocate a
592 * temporary buffer. We add one extra byte so that we can add a null
593 * terminator at the end of strings in case userland did not supply
594 * one. Either way, we must free the temporary buffer later!
596 if (newlen + 1 > sizeof(buf)) {
598 * For regular users, we do not want to perform dynamic memory
599 * allocation. Thus, for CTLTYPE_ANYWRITE nodes, only the
600 * superuser may set values exceeding the small buffer in size.
602 if (!(call->call_flags & RMIB_FLAG_AUTH))
603 return EPERM;
605 /* Do not return ENOMEM on allocation failure. */
606 if ((src = malloc(newlen + 1)) == NULL)
607 return EINVAL;
608 } else
609 src = buf;
611 /* Copy in the data. Note that the given new length may be zero. */
612 if ((r = rmib_copyin(newp, src, newlen)) == OK) {
613 /* Check and, if acceptable, store the new value. */
614 switch (SYSCTL_TYPE(rnode->rnode_flags)) {
615 case CTLTYPE_BOOL:
616 /* Sanitize booleans. See the MIB code for details. */
617 b[0] = (bool)src[0];
618 memcpy(dst, &b[0], sizeof(b[0]));
619 break;
620 case CTLTYPE_INT:
621 case CTLTYPE_QUAD:
622 case CTLTYPE_STRUCT:
623 memcpy(dst, src, rnode->rnode_size);
624 break;
625 case CTLTYPE_STRING:
626 if (newlen == rnode->rnode_size &&
627 src[newlen - 1] != '\0') {
628 /* Our null terminator does not fit! */
629 r = EINVAL;
630 break;
632 src[newlen] = '\0';
633 strlcpy(dst, src, rnode->rnode_size);
634 break;
635 default:
636 r = EINVAL;
640 if (src != buf)
641 free(src);
643 return r;
647 * Read and/or write the value of a regular data node. A regular data node is
648 * a leaf node. Typically, a leaf node has no associated function, in which
649 * case this function will be used instead. In addition, this function may be
650 * used from handler functions as part of their functionality.
652 ssize_t
653 rmib_readwrite(struct rmib_call * call, struct rmib_node * rnode,
654 struct rmib_oldp * oldp, struct rmib_newp * newp)
656 ssize_t len;
657 int r;
659 /* Copy out old data, if requested. Always get the old data length. */
660 if ((r = len = rmib_read(rnode, oldp)) < 0)
661 return r;
663 /* Copy in new data, if requested. */
664 if ((r = rmib_write(call, rnode, newp)) != OK)
665 return r;
667 /* Return the old data length. */
668 return len;
672 * Handle a sysctl(2) call from a user process, relayed by the MIB service to
673 * us. If the call succeeds, return the old length. The MIB service will
674 * perform a check against the given old length and return ENOMEM to the caller
675 * when applicable, so we do not have to do that here. If the call fails,
676 * return a negative error code.
678 static ssize_t
679 rmib_call(const message * m_in)
681 struct rmib_node *rnode, *rparent;
682 struct rmib_call call;
683 struct rmib_oldp oldp_data, *oldp;
684 struct rmib_newp newp_data, *newp;
685 unsigned int root_id, prefixlen, namelen;
686 int r, id, is_leaf, has_func, name[CTL_MAXNAME];
689 * Look up the root of the subtree that is the subject of the call. If
690 * the call is for a subtree that is not registered, return ERESTART to
691 * indicate to the MIB service that it should deregister the subtree it
692 * thinks we have. This case may occur in practice if a deregistration
693 * request from us crosses a sysctl call request from the MIB service.
695 root_id = m_in->m_mib_lsys_call.root_id;
696 if (root_id >= __arraycount(rnodes) ||
697 (rnode = rnodes[root_id].rno_node) == NULL)
698 return ERESTART;
701 * Use the name of the mounted subtree as prefix to the given name, so
702 * that call_oname will point to the complete name of the node. This
703 * is necessary for the few queries that make use of call_oname.
705 prefixlen = rnodes[root_id].rno_namelen;
706 memcpy(name, rnodes[root_id].rno_name, prefixlen * sizeof(name[0]));
709 * Set up all data structures that we need to use while handling the
710 * call processing. Start by copying in the remainder of the MIB name.
712 /* A zero name length is valid and should always yield EISDIR. */
713 namelen = m_in->m_mib_lsys_call.name_len;
714 if (prefixlen + namelen > __arraycount(name))
715 return EINVAL;
717 if (namelen > 0) {
718 r = sys_safecopyfrom(m_in->m_source,
719 m_in->m_mib_lsys_call.name_grant, 0,
720 (vir_bytes)&name[prefixlen], sizeof(name[0]) * namelen);
721 if (r != OK)
722 return r;
725 oldp_data.oldp_grant = m_in->m_mib_lsys_call.oldp_grant;
726 oldp_data.oldp_len = m_in->m_mib_lsys_call.oldp_len;
727 oldp = (GRANT_VALID(oldp_data.oldp_grant)) ? &oldp_data : NULL;
729 newp_data.newp_grant = m_in->m_mib_lsys_call.newp_grant;
730 newp_data.newp_len = m_in->m_mib_lsys_call.newp_len;
731 newp = (GRANT_VALID(newp_data.newp_grant)) ? &newp_data : NULL;
733 call.call_endpt = m_in->m_mib_lsys_call.user_endpt;
734 call.call_oname = name;
735 call.call_name = &name[prefixlen];
736 call.call_namelen = namelen;
737 call.call_flags = m_in->m_mib_lsys_call.flags;
738 call.call_rootver = m_in->m_mib_lsys_call.root_ver;
739 call.call_treever = m_in->m_mib_lsys_call.tree_ver;
742 * Dispatch the call.
744 for (rparent = rnode; call.call_namelen > 0; rparent = rnode) {
745 id = call.call_name[0];
746 call.call_name++;
747 call.call_namelen--;
749 assert(SYSCTL_TYPE(rparent->rnode_flags) == CTLTYPE_NODE);
751 /* Check for meta-identifiers. */
752 if (id < 0) {
754 * A meta-identifier must always be the last name
755 * component.
757 if (call.call_namelen > 0)
758 return EINVAL;
760 switch (id) {
761 case CTL_QUERY:
762 return rmib_query(&call, rparent, oldp, newp);
763 case CTL_DESCRIBE:
764 return rmib_describe(&call, rparent, oldp,
765 newp);
766 case CTL_CREATE:
767 case CTL_DESTROY:
768 /* We support fully static subtrees only. */
769 return EPERM;
770 default:
771 return EOPNOTSUPP;
775 /* Locate the child node. */
776 if ((rnode = rmib_lookup(rparent, id)) == NULL)
777 return ENOENT;
779 /* Check if access is permitted at this level. */
780 if ((rnode->rnode_flags & CTLFLAG_PRIVATE) &&
781 !(call.call_flags & RMIB_FLAG_AUTH))
782 return EPERM;
785 * Is this a leaf node, and/or is this node handled by a
786 * function? If either is true, resolution ends at this level.
788 is_leaf = (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE);
789 has_func = (rnode->rnode_func != NULL);
792 * The name may be longer only if the node is not a leaf. That
793 * also applies to leaves with functions, so check this first.
795 if (is_leaf && call.call_namelen > 0)
796 return ENOTDIR;
799 * If resolution indeed ends here, and the user supplied new
800 * data, check if writing is allowed.
802 if ((is_leaf || has_func) && newp != NULL) {
803 if (!(rnode->rnode_flags & CTLFLAG_READWRITE))
804 return EPERM;
806 if (!(rnode->rnode_flags & CTLFLAG_ANYWRITE) &&
807 !(call.call_flags & RMIB_FLAG_AUTH))
808 return EPERM;
811 /* If this node has a handler function, let it do the work. */
812 if (has_func)
813 return rnode->rnode_func(&call, rnode, oldp, newp);
815 /* For regular data leaf nodes, handle generic access. */
816 if (is_leaf)
817 return rmib_readwrite(&call, rnode, oldp, newp);
819 /* No function and not a leaf? Descend further. */
822 /* If we get here, the name refers to a node array. */
823 return EISDIR;
827 * Initialize the given node and recursively all its node-type children,
828 * assigning the proper child length value to each of them.
830 static void
831 rmib_init(struct rmib_node * rparent)
833 struct rmib_node *rnode;
834 unsigned int i;
836 for (i = 0; i < rparent->rnode_size; i++) {
837 if (rparent->rnode_flags & CTLFLAG_SPARSE) {
838 /* Indirect lists must be sorted ascending by ID. */
839 assert(i == 0 || rparent->rnode_icptr[i].rindir_id >
840 rparent->rnode_icptr[i - 1].rindir_id);
842 rnode = rparent->rnode_icptr[i].rindir_node;
843 } else {
844 rnode = &rparent->rnode_cptr[i];
846 if (rnode->rnode_flags == 0)
847 continue;
850 rparent->rnode_clen++;
852 if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE)
853 rmib_init(rnode); /* recurse */
858 * Request that the MIB service (re)mount the subtree identified by the given
859 * identifier. This is a one-way request, so we never hear whether mounting
860 * succeeds. There is not that much we can do if it fails anyway though.
862 static void
863 rmib_send_reg(int id)
865 message m;
866 int r;
868 memset(&m, 0, sizeof(m));
870 m.m_type = MIB_REGISTER;
871 m.m_lsys_mib_register.root_id = id;
872 m.m_lsys_mib_register.flags = SYSCTL_VERSION |
873 (rnodes[id].rno_node->rnode_flags & ~CTLFLAG_SPARSE);
874 m.m_lsys_mib_register.csize = rnodes[id].rno_node->rnode_size;
875 m.m_lsys_mib_register.clen = rnodes[id].rno_node->rnode_clen;
876 m.m_lsys_mib_register.miblen = rnodes[id].rno_namelen;
877 memcpy(m.m_lsys_mib_register.mib, rnodes[id].rno_name,
878 sizeof(rnodes[id].rno_name[0]) * rnodes[id].rno_namelen);
880 if ((r = asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY)) != OK)
881 panic("asynsend3 call to MIB service failed: %d", r);
885 * Register a MIB subtree. Initialize the subtree, add it to the local set,
886 * and send a registration request for it to the MIB service.
889 rmib_register(const int * name, unsigned int namelen, struct rmib_node * rnode)
891 unsigned int id, free_id;
893 /* A few basic sanity checks. */
894 if (namelen == 0 || namelen >= __arraycount(rnodes[0].rno_name))
895 return EINVAL;
896 if (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE)
897 return EINVAL;
899 /* Make sure this is a new subtree, and find a free slot for it. */
900 for (id = free_id = 0; id < __arraycount(rnodes); id++) {
901 if (rnodes[id].rno_node == rnode)
902 return EEXIST;
903 else if (rnodes[id].rno_node == NULL &&
904 rnodes[free_id].rno_node != NULL)
905 free_id = id;
908 if (rnodes[free_id].rno_node != NULL)
909 return ENOMEM;
911 rnodes[free_id].rno_node = rnode;
912 rnodes[free_id].rno_namelen = namelen;
913 memcpy(rnodes[free_id].rno_name, name, sizeof(name[0]) * namelen);
916 * Initialize the entire subtree. This will also compute rnode_clen
917 * for the given rnode, so do this before sending the message.
919 rmib_init(rnode);
921 /* Send the registration request to the MIB service. */
922 rmib_send_reg(free_id);
924 return OK;
928 * Deregister a previously registered subtree, both internally and with the MIB
929 * service. Return OK if the deregistration procedure has been started, in
930 * which case the given subtree is guaranteed to no longer be accessed. Return
931 * a negative error code on failure.
934 rmib_deregister(struct rmib_node * rnode)
936 message m;
937 unsigned int id;
939 for (id = 0; id < __arraycount(rnodes); id++)
940 if (rnodes[id].rno_node == rnode)
941 break;
943 if (id == __arraycount(rnodes))
944 return ENOENT;
946 rnodes[id].rno_node = NULL;
949 * Request that the MIB service unmount the subtree. We completely
950 * ignore failure here, because the caller would not be able to do
951 * anything about it anyway. We may also still receive sysctl call
952 * requests for the node we just deregistered, but this is caught
953 * during request processing. Reuse of the rnodes[] slot could be a
954 * potential problem though. We could use sequence numbers in the root
955 * identifiers to resolve that problem if it ever occurs in reality.
957 memset(&m, 0, sizeof(m));
959 m.m_type = MIB_DEREGISTER;
960 m.m_lsys_mib_register.root_id = id;
962 (void)asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY);
964 return OK;
968 * Reregister all previously registered subtrees. This routine should be
969 * called after the main program has determined that the MIB service has been
970 * restarted.
972 void
973 rmib_reregister(void)
975 unsigned int id;
977 for (id = 0; id < __arraycount(rnodes); id++)
978 if (rnodes[id].rno_node != NULL)
979 rmib_send_reg(id);
983 * Reset all registrations, without involving MIB communication. This routine
984 * exists for testing purposes only, and may disappear in the future.
986 void
987 rmib_reset(void)
990 memset(rnodes, 0, sizeof(rnodes));
994 * Process a request from the MIB service for information about the root node
995 * of a subtree, specifically its name and description.
997 static int
998 rmib_info(const message * m_in)
1000 struct rmib_node *rnode;
1001 unsigned int id;
1002 const char *ptr;
1003 size_t size;
1004 int r;
1006 id = m_in->m_mib_lsys_info.root_id;
1007 if (id >= __arraycount(rnodes) || rnodes[id].rno_node == NULL)
1008 return ENOENT;
1009 rnode = rnodes[id].rno_node;
1011 /* The name must fit. If it does not, the service writer messed up. */
1012 size = strlen(rnode->rnode_name) + 1;
1013 if (size > m_in->m_mib_lsys_info.name_size)
1014 return ENAMETOOLONG;
1016 r = sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.name_grant, 0,
1017 (vir_bytes)rnode->rnode_name, size);
1018 if (r != OK)
1019 return r;
1021 /* If there is no (optional) description, copy out an empty string. */
1022 ptr = (rnode->rnode_desc != NULL) ? rnode->rnode_desc : "";
1023 size = strlen(ptr) + 1;
1025 if (size > m_in->m_mib_lsys_info.desc_size)
1026 size = m_in->m_mib_lsys_info.desc_size;
1028 return sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.desc_grant,
1029 0, (vir_bytes)ptr, size);
1033 * Process a request from the MIB service. The given message should originate
1034 * from the MIB service and have one of the COMMON_MIB_ requests as type.
1036 void
1037 rmib_process(const message * m_in, int ipc_status)
1039 message m_out;
1040 uint32_t req_id;
1041 ssize_t r;
1043 /* Only the MIB service may issue these requests. */
1044 if (m_in->m_source != MIB_PROC_NR)
1045 return;
1047 /* Process the actual request. */
1048 switch (m_in->m_type) {
1049 case COMMON_MIB_INFO:
1050 req_id = m_in->m_mib_lsys_info.req_id;
1052 r = rmib_info(m_in);
1054 break;
1056 case COMMON_MIB_CALL:
1057 req_id = m_in->m_mib_lsys_call.req_id;
1059 r = rmib_call(m_in);
1061 break;
1063 default:
1065 * HACK: assume that for all current and future requests, the
1066 * request ID field is in the same place. We could create a
1067 * m_mib_lsys_unknown pseudo message type for this, but, eh.
1069 req_id = m_in->m_mib_lsys_info.req_id;
1071 r = ENOSYS;
1074 /* Construct and send a reply message to the MIB service. */
1075 memset(&m_out, 0, sizeof(m_out));
1077 m_out.m_type = COMMON_MIB_REPLY;
1078 m_out.m_lsys_mib_reply.req_id = req_id;
1079 m_out.m_lsys_mib_reply.status = r;
1081 if (IPC_STATUS_CALL(ipc_status) == SENDREC)
1082 r = ipc_sendnb(m_in->m_source, &m_out);
1083 else
1084 r = asynsend3(m_in->m_source, &m_out, AMF_NOREPLY);
1086 if (r != OK)
1087 printf("lsys:rmib: unable to send reply to %d: %zd\n",
1088 m_in->m_source, r);