1 /* Service support for remote MIB subtrees - by D.C. van Moolenbroek */
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. */
25 cp_grant_id_t oldp_grant
;
30 cp_grant_id_t newp_grant
;
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.
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.
65 rmib_inrange(struct rmib_oldp
* oldp
, size_t off
)
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.
80 rmib_getoldlen(struct rmib_oldp
* oldp
)
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.
95 rmib_copyout(struct rmib_oldp
* __restrict oldp
, size_t off
,
96 const void * __restrict buf
, size_t 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
)
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.
124 rmib_vcopyout(struct rmib_oldp
* oldp
, size_t off
, const iovec_t
* iov
,
127 static struct vscp_vec vec
[RMIB_IOV_MAX
];
133 assert(iovcnt
<= __arraycount(vec
));
135 /* Take a shortcut for single-vector elements, saving a kernel copy. */
137 return rmib_copyout(oldp
, off
, (const void *)iov
->iov_addr
,
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
;
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
)
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
,
180 if (newp
== NULL
|| len
!= newp
->newp_len
)
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.
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
;
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
);
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
)) {
231 scn
.sysctl_bdata
= rnode
->rnode_bool
;
234 scn
.sysctl_idata
= rnode
->rnode_int
;
237 scn
.sysctl_qdata
= rnode
->rnode_quad
;
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.
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
;
276 /* If the user passed in version numbers, check them. */
278 if ((r
= rmib_copyin(newp
, &scn
, sizeof(scn
))) != OK
)
281 if (SYSCTL_VERS(scn
.sysctl_flags
) != SYSCTL_VERSION
)
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
)
294 /* Enumerate the child nodes of the given parent node. */
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
;
303 rnode
= &rparent
->rnode_cptr
[i
];
305 if (rnode
->rnode_flags
== 0)
309 if ((r
= rmib_copyout_node(call
, oldp
, off
, id
, rnode
)) < 0)
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.
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
;
332 /* Descriptions of private nodes are considered private too. */
333 if ((rnode
->rnode_flags
& CTLFLAG_PRIVATE
) &&
334 !(call
->call_flags
& RMIB_FLAG_AUTH
))
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;
350 memset(&scd
, 0, sizeof(scd
));
352 scd
.descr_ver
= call
->call_rootver
;
355 size
= offsetof(struct sysctldesc
, descr_str
);
358 scd
.descr_str
[0] = '\0'; /* superfluous */
362 /* Copy out the structure, possibly including a null terminator. */
363 if ((r
= rmib_copyout(oldp
, off
, &scd
, size
)) < 0)
367 /* Copy out the description itself. */
368 if ((r
= rmib_copyout(oldp
, off
+ size
, rnode
->rnode_desc
,
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
;
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
;
400 if (id
>= rparent
->rnode_size
)
402 rnode
= &rparent
->rnode_cptr
[id
];
403 if (rnode
->rnode_flags
!= 0)
411 * Retrieve node descriptions in bulk, or retrieve a particular node's
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
;
424 if ((r
= rmib_copyin(newp
, &scn
, sizeof(scn
))) != OK
)
427 if (SYSCTL_VERS(scn
.sysctl_flags
) != SYSCTL_VERSION
)
430 /* Locate the child node. */
431 if ((rnode
= rmib_lookup(rparent
, scn
.sysctl_num
)) == NULL
)
434 /* Descriptions of private nodes are considered private too. */
435 if ((rnode
->rnode_flags
& CTLFLAG_PRIVATE
) &&
436 !(call
->call_flags
& RMIB_FLAG_AUTH
))
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
)
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. */
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
;
463 rnode
= &rparent
->rnode_cptr
[i
];
465 if (rnode
->rnode_flags
== 0)
469 if ((r
= rmib_copyout_desc(call
, oldp
, off
, id
, rnode
)) < 0)
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.
483 rmib_getptr(struct rmib_node
* rnode
)
486 switch (SYSCTL_TYPE(rnode
->rnode_flags
)) {
488 if (rnode
->rnode_flags
& CTLFLAG_IMMEDIATE
)
489 return &rnode
->rnode_bool
;
492 if (rnode
->rnode_flags
& CTLFLAG_IMMEDIATE
)
493 return &rnode
->rnode_int
;
496 if (rnode
->rnode_flags
& CTLFLAG_IMMEDIATE
)
497 return &rnode
->rnode_quad
;
501 if (rnode
->rnode_flags
& CTLFLAG_IMMEDIATE
)
508 return rnode
->rnode_data
;
512 * Read current (old) data from a regular data node, if requested. Return the
516 rmib_read(struct rmib_node
* rnode
, struct rmib_oldp
* oldp
)
522 if ((ptr
= rmib_getptr(rnode
)) == NULL
)
525 if (SYSCTL_TYPE(rnode
->rnode_flags
) == CTLTYPE_STRING
)
526 oldlen
= strlen(rnode
->rnode_data
) + 1;
528 oldlen
= rnode
->rnode_size
;
530 if (oldlen
> SSIZE_MAX
)
533 /* Copy out the current data, if requested at all. */
534 if (oldp
!= NULL
&& (r
= rmib_copyout(oldp
, 0, ptr
, oldlen
)) < 0)
537 /* Return the current length in any case. */
538 return (ssize_t
)oldlen
;
542 * Write new data into a regular data node, if requested.
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
];
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
)
567 switch (SYSCTL_TYPE(rnode
->rnode_flags
)) {
572 /* Non-string types must have an exact size match. */
573 if (newlen
!= rnode
->rnode_size
)
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
)
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
))
605 /* Do not return ENOMEM on allocation failure. */
606 if ((src
= malloc(newlen
+ 1)) == NULL
)
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
)) {
616 /* Sanitize booleans. See the MIB code for details. */
618 memcpy(dst
, &b
[0], sizeof(b
[0]));
623 memcpy(dst
, src
, rnode
->rnode_size
);
626 if (newlen
== rnode
->rnode_size
&&
627 src
[newlen
- 1] != '\0') {
628 /* Our null terminator does not fit! */
633 strlcpy(dst
, src
, rnode
->rnode_size
);
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.
653 rmib_readwrite(struct rmib_call
* call
, struct rmib_node
* rnode
,
654 struct rmib_oldp
* oldp
, struct rmib_newp
* newp
)
659 /* Copy out old data, if requested. Always get the old data length. */
660 if ((r
= len
= rmib_read(rnode
, oldp
)) < 0)
663 /* Copy in new data, if requested. */
664 if ((r
= rmib_write(call
, rnode
, newp
)) != OK
)
667 /* Return the old data length. */
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.
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
)
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
))
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
);
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
;
744 for (rparent
= rnode
; call
.call_namelen
> 0; rparent
= rnode
) {
745 id
= call
.call_name
[0];
749 assert(SYSCTL_TYPE(rparent
->rnode_flags
) == CTLTYPE_NODE
);
751 /* Check for meta-identifiers. */
754 * A meta-identifier must always be the last name
757 if (call
.call_namelen
> 0)
762 return rmib_query(&call
, rparent
, oldp
, newp
);
764 return rmib_describe(&call
, rparent
, oldp
,
768 /* We support fully static subtrees only. */
775 /* Locate the child node. */
776 if ((rnode
= rmib_lookup(rparent
, id
)) == NULL
)
779 /* Check if access is permitted at this level. */
780 if ((rnode
->rnode_flags
& CTLFLAG_PRIVATE
) &&
781 !(call
.call_flags
& RMIB_FLAG_AUTH
))
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)
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
))
806 if (!(rnode
->rnode_flags
& CTLFLAG_ANYWRITE
) &&
807 !(call
.call_flags
& RMIB_FLAG_AUTH
))
811 /* If this node has a handler function, let it do the work. */
813 return rnode
->rnode_func(&call
, rnode
, oldp
, newp
);
815 /* For regular data leaf nodes, handle generic access. */
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. */
827 * Initialize the given node and recursively all its node-type children,
828 * assigning the proper child length value to each of them.
831 rmib_init(struct rmib_node
* rparent
)
833 struct rmib_node
*rnode
;
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
;
844 rnode
= &rparent
->rnode_cptr
[i
];
846 if (rnode
->rnode_flags
== 0)
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.
863 rmib_send_reg(int id
)
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
))
896 if (SYSCTL_TYPE(rnode
->rnode_flags
) != CTLTYPE_NODE
)
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
)
903 else if (rnodes
[id
].rno_node
== NULL
&&
904 rnodes
[free_id
].rno_node
!= NULL
)
908 if (rnodes
[free_id
].rno_node
!= NULL
)
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.
921 /* Send the registration request to the MIB service. */
922 rmib_send_reg(free_id
);
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
)
939 for (id
= 0; id
< __arraycount(rnodes
); id
++)
940 if (rnodes
[id
].rno_node
== rnode
)
943 if (id
== __arraycount(rnodes
))
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
);
968 * Reregister all previously registered subtrees. This routine should be
969 * called after the main program has determined that the MIB service has been
973 rmib_reregister(void)
977 for (id
= 0; id
< __arraycount(rnodes
); id
++)
978 if (rnodes
[id
].rno_node
!= NULL
)
983 * Reset all registrations, without involving MIB communication. This routine
984 * exists for testing purposes only, and may disappear in the future.
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.
998 rmib_info(const message
* m_in
)
1000 struct rmib_node
*rnode
;
1006 id
= m_in
->m_mib_lsys_info
.root_id
;
1007 if (id
>= __arraycount(rnodes
) || rnodes
[id
].rno_node
== NULL
)
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
);
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.
1037 rmib_process(const message
* m_in
, int ipc_status
)
1043 /* Only the MIB service may issue these requests. */
1044 if (m_in
->m_source
!= MIB_PROC_NR
)
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
);
1056 case COMMON_MIB_CALL
:
1057 req_id
= m_in
->m_mib_lsys_call
.req_id
;
1059 r
= rmib_call(m_in
);
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
;
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
);
1084 r
= asynsend3(m_in
->m_source
, &m_out
, AMF_NOREPLY
);
1087 printf("lsys:rmib: unable to send reply to %d: %zd\n",