16f3e0bcdSDavid van Moolenbroek /* Service support for remote MIB subtrees - by D.C. van Moolenbroek */
26f3e0bcdSDavid van Moolenbroek /*
36f3e0bcdSDavid van Moolenbroek * In effect, this is a lightweight version of the MIB service's main and tree
46f3e0bcdSDavid van Moolenbroek * code. Some parts of the code have even been copied almost as is, even
56f3e0bcdSDavid van Moolenbroek * though the copy here operates on slightly different data structures in order
66f3e0bcdSDavid van Moolenbroek * to keep the implementation more lightweight. For clarification on many
76f3e0bcdSDavid van Moolenbroek * aspects of the source code here, see the source code of the MIB service.
8*0f03189aSDavid van Moolenbroek * One unique feature here is support for sparse nodes, which is needed for
9*0f03189aSDavid van Moolenbroek * net.inet/inet6 as those are using subtrees with protocol-based identifiers.
106f3e0bcdSDavid van Moolenbroek *
116f3e0bcdSDavid van Moolenbroek * There is no way for this module to get to know about MIB service deaths
126f3e0bcdSDavid van Moolenbroek * without possibly interfering with the main code of the service this module
136f3e0bcdSDavid van Moolenbroek * is a part of. As a result, re-registration of mount points after a MIB
14241ebcaeSDavid van Moolenbroek * service restart is not automatic. Instead, the main service code should
15241ebcaeSDavid van Moolenbroek * provide detection of MIB service restarts, and call rmib_reregister() after
16241ebcaeSDavid van Moolenbroek * such a restart in order to remount any previously mounted subtrees.
176f3e0bcdSDavid van Moolenbroek */
186f3e0bcdSDavid van Moolenbroek
196f3e0bcdSDavid van Moolenbroek #include <minix/drivers.h>
206f3e0bcdSDavid van Moolenbroek #include <minix/sysctl.h>
216f3e0bcdSDavid van Moolenbroek #include <minix/rmib.h>
226f3e0bcdSDavid van Moolenbroek
236f3e0bcdSDavid van Moolenbroek /* Structures for outgoing and incoming data, deliberately distinctly named. */
246f3e0bcdSDavid van Moolenbroek struct rmib_oldp {
256f3e0bcdSDavid van Moolenbroek cp_grant_id_t oldp_grant;
266f3e0bcdSDavid van Moolenbroek size_t oldp_len;
276f3e0bcdSDavid van Moolenbroek };
286f3e0bcdSDavid van Moolenbroek
296f3e0bcdSDavid van Moolenbroek struct rmib_newp {
306f3e0bcdSDavid van Moolenbroek cp_grant_id_t newp_grant;
316f3e0bcdSDavid van Moolenbroek size_t newp_len;
326f3e0bcdSDavid van Moolenbroek };
336f3e0bcdSDavid van Moolenbroek
346f3e0bcdSDavid van Moolenbroek /*
356f3e0bcdSDavid van Moolenbroek * The maximum field size, in bytes, for which updates (i.e., writes) to the
366f3e0bcdSDavid van Moolenbroek * field do not require dynamic memory allocation. By policy, non-root users
376f3e0bcdSDavid van Moolenbroek * may not update fields exceeding this size at all. For strings, this size
386f3e0bcdSDavid van Moolenbroek * includes an extra byte for adding a null terminator if missing. As the name
396f3e0bcdSDavid van Moolenbroek * indicates, a buffer of this size is placed on the stack.
406f3e0bcdSDavid van Moolenbroek */
416f3e0bcdSDavid van Moolenbroek #define RMIB_STACKBUF 257
426f3e0bcdSDavid van Moolenbroek
436f3e0bcdSDavid van Moolenbroek /*
446f3e0bcdSDavid van Moolenbroek * The maximum number of subtrees that this service can mount. This value can
456f3e0bcdSDavid van Moolenbroek * be increased without any problems, but it is already quite high in practice.
466f3e0bcdSDavid van Moolenbroek */
476f3e0bcdSDavid van Moolenbroek #define RMIB_MAX_SUBTREES 16
486f3e0bcdSDavid van Moolenbroek
496f3e0bcdSDavid van Moolenbroek /*
506f3e0bcdSDavid van Moolenbroek * The array of subtree root nodes. Each root node's array index is the root
516f3e0bcdSDavid van Moolenbroek * identifier used in communication with the MIB service.
526f3e0bcdSDavid van Moolenbroek */
53241ebcaeSDavid van Moolenbroek static struct {
54241ebcaeSDavid van Moolenbroek struct rmib_node *rno_node;
55241ebcaeSDavid van Moolenbroek unsigned int rno_namelen;
56241ebcaeSDavid van Moolenbroek int rno_name[CTL_SHORTNAME];
57241ebcaeSDavid van Moolenbroek } rnodes[RMIB_MAX_SUBTREES] = { { NULL, 0, { 0 } } };
586f3e0bcdSDavid van Moolenbroek
596f3e0bcdSDavid van Moolenbroek /*
606f3e0bcdSDavid van Moolenbroek * Return TRUE or FALSE indicating whether the given offset is within the range
616f3e0bcdSDavid van Moolenbroek * of data that is to be copied out. This call can be used to test whether
626f3e0bcdSDavid van Moolenbroek * certain bits of data need to be prepared for copying at all.
636f3e0bcdSDavid van Moolenbroek */
646f3e0bcdSDavid van Moolenbroek int
rmib_inrange(struct rmib_oldp * oldp,size_t off)656f3e0bcdSDavid van Moolenbroek rmib_inrange(struct rmib_oldp * oldp, size_t off)
666f3e0bcdSDavid van Moolenbroek {
676f3e0bcdSDavid van Moolenbroek
686f3e0bcdSDavid van Moolenbroek if (oldp == NULL)
696f3e0bcdSDavid van Moolenbroek return FALSE;
706f3e0bcdSDavid van Moolenbroek
716f3e0bcdSDavid van Moolenbroek return (off < oldp->oldp_len);
726f3e0bcdSDavid van Moolenbroek }
736f3e0bcdSDavid van Moolenbroek
746f3e0bcdSDavid van Moolenbroek /*
756f3e0bcdSDavid van Moolenbroek * Return the total length of the requested data. This should not be used
766f3e0bcdSDavid van Moolenbroek * directly except in highly unusual cases, such as particular node requests
776f3e0bcdSDavid van Moolenbroek * where the request semantics blatantly violate overall sysctl(2) semantics.
786f3e0bcdSDavid van Moolenbroek */
796f3e0bcdSDavid van Moolenbroek size_t
rmib_getoldlen(struct rmib_oldp * oldp)806f3e0bcdSDavid van Moolenbroek rmib_getoldlen(struct rmib_oldp * oldp)
816f3e0bcdSDavid van Moolenbroek {
826f3e0bcdSDavid van Moolenbroek
836f3e0bcdSDavid van Moolenbroek if (oldp == NULL)
846f3e0bcdSDavid van Moolenbroek return 0;
856f3e0bcdSDavid van Moolenbroek
866f3e0bcdSDavid van Moolenbroek return oldp->oldp_len;
876f3e0bcdSDavid van Moolenbroek }
886f3e0bcdSDavid van Moolenbroek
896f3e0bcdSDavid van Moolenbroek /*
906f3e0bcdSDavid van Moolenbroek * Copy out (partial) data to the user. The copy is automatically limited to
916f3e0bcdSDavid van Moolenbroek * the range of data requested by the user. Return the requested length on
926f3e0bcdSDavid van Moolenbroek * success (for the caller's convenience) or an error code on failure.
936f3e0bcdSDavid van Moolenbroek */
946f3e0bcdSDavid van Moolenbroek ssize_t
rmib_copyout(struct rmib_oldp * __restrict oldp,size_t off,const void * __restrict buf,size_t size)956f3e0bcdSDavid van Moolenbroek rmib_copyout(struct rmib_oldp * __restrict oldp, size_t off,
966f3e0bcdSDavid van Moolenbroek const void * __restrict buf, size_t size)
976f3e0bcdSDavid van Moolenbroek {
986f3e0bcdSDavid van Moolenbroek size_t len;
996f3e0bcdSDavid van Moolenbroek int r;
1006f3e0bcdSDavid van Moolenbroek
1016f3e0bcdSDavid van Moolenbroek len = size;
1026f3e0bcdSDavid van Moolenbroek assert(len <= SSIZE_MAX);
1036f3e0bcdSDavid van Moolenbroek
1046f3e0bcdSDavid van Moolenbroek if (oldp == NULL || off >= oldp->oldp_len)
1056f3e0bcdSDavid van Moolenbroek return size; /* nothing to do */
1066f3e0bcdSDavid van Moolenbroek
1076f3e0bcdSDavid van Moolenbroek if (len > oldp->oldp_len - off)
1086f3e0bcdSDavid van Moolenbroek len = oldp->oldp_len - off;
1096f3e0bcdSDavid van Moolenbroek
1106f3e0bcdSDavid van Moolenbroek if ((r = sys_safecopyto(MIB_PROC_NR, oldp->oldp_grant, off,
1116f3e0bcdSDavid van Moolenbroek (vir_bytes)buf, len)) != OK)
1126f3e0bcdSDavid van Moolenbroek return r;
1136f3e0bcdSDavid van Moolenbroek
1146f3e0bcdSDavid van Moolenbroek return size;
1156f3e0bcdSDavid van Moolenbroek }
1166f3e0bcdSDavid van Moolenbroek
1176f3e0bcdSDavid van Moolenbroek /*
118f221d2ceSDavid van Moolenbroek * Copy out (partial) data to the user, from a vector of up to RMIB_IOV_MAX
119f221d2ceSDavid van Moolenbroek * local buffers. The copy is automatically limited to the range of data
120f221d2ceSDavid van Moolenbroek * requested by the user. Return the total requested length on success or an
121f221d2ceSDavid van Moolenbroek * error code on failure.
122f221d2ceSDavid van Moolenbroek */
123f221d2ceSDavid van Moolenbroek ssize_t
rmib_vcopyout(struct rmib_oldp * oldp,size_t off,const iovec_t * iov,unsigned int iovcnt)124f221d2ceSDavid van Moolenbroek rmib_vcopyout(struct rmib_oldp * oldp, size_t off, const iovec_t * iov,
125f221d2ceSDavid van Moolenbroek unsigned int iovcnt)
126f221d2ceSDavid van Moolenbroek {
127f221d2ceSDavid van Moolenbroek static struct vscp_vec vec[RMIB_IOV_MAX];
128f221d2ceSDavid van Moolenbroek size_t size, chunk;
129f221d2ceSDavid van Moolenbroek unsigned int i;
130f221d2ceSDavid van Moolenbroek ssize_t r;
131f221d2ceSDavid van Moolenbroek
132f221d2ceSDavid van Moolenbroek assert(iov != NULL);
133f221d2ceSDavid van Moolenbroek assert(iovcnt <= __arraycount(vec));
134f221d2ceSDavid van Moolenbroek
135f221d2ceSDavid van Moolenbroek /* Take a shortcut for single-vector elements, saving a kernel copy. */
136f221d2ceSDavid van Moolenbroek if (iovcnt == 1)
137f221d2ceSDavid van Moolenbroek return rmib_copyout(oldp, off, (const void *)iov->iov_addr,
138f221d2ceSDavid van Moolenbroek iov->iov_size);
139f221d2ceSDavid van Moolenbroek
140f221d2ceSDavid van Moolenbroek /*
141f221d2ceSDavid van Moolenbroek * Iterate through the full vector even if we cannot copy out all of
142f221d2ceSDavid van Moolenbroek * it, because we need to compute the total length.
143f221d2ceSDavid van Moolenbroek */
144f221d2ceSDavid van Moolenbroek for (size = i = 0; iovcnt > 0; iov++, iovcnt--) {
145f221d2ceSDavid van Moolenbroek if (oldp != NULL && off < oldp->oldp_len) {
146f221d2ceSDavid van Moolenbroek chunk = oldp->oldp_len - off;
147f221d2ceSDavid van Moolenbroek if (chunk > iov->iov_size)
148f221d2ceSDavid van Moolenbroek chunk = iov->iov_size;
149f221d2ceSDavid van Moolenbroek
150f221d2ceSDavid van Moolenbroek vec[i].v_from = SELF;
151f221d2ceSDavid van Moolenbroek vec[i].v_to = MIB_PROC_NR;
152f221d2ceSDavid van Moolenbroek vec[i].v_gid = oldp->oldp_grant;
153f221d2ceSDavid van Moolenbroek vec[i].v_offset = off;
154f221d2ceSDavid van Moolenbroek vec[i].v_addr = iov->iov_addr;
155f221d2ceSDavid van Moolenbroek vec[i].v_bytes = chunk;
156f221d2ceSDavid van Moolenbroek
157f221d2ceSDavid van Moolenbroek off += chunk;
158f221d2ceSDavid van Moolenbroek i++;
159f221d2ceSDavid van Moolenbroek }
160f221d2ceSDavid van Moolenbroek
161f221d2ceSDavid van Moolenbroek size += iov->iov_size;
162f221d2ceSDavid van Moolenbroek }
163f221d2ceSDavid van Moolenbroek
164f221d2ceSDavid van Moolenbroek /* Perform the copy, if there is anything to copy, that is. */
165f221d2ceSDavid van Moolenbroek if (i > 0 && (r = sys_vsafecopy(vec, i)) != OK)
166f221d2ceSDavid van Moolenbroek return r;
167f221d2ceSDavid van Moolenbroek
168f221d2ceSDavid van Moolenbroek return size;
169f221d2ceSDavid van Moolenbroek }
170f221d2ceSDavid van Moolenbroek
171f221d2ceSDavid van Moolenbroek /*
1726f3e0bcdSDavid van Moolenbroek * Copy in data from the user. The given length must match exactly the length
1736f3e0bcdSDavid van Moolenbroek * given by the user. Return OK or an error code.
1746f3e0bcdSDavid van Moolenbroek */
1756f3e0bcdSDavid van Moolenbroek int
rmib_copyin(struct rmib_newp * __restrict newp,void * __restrict buf,size_t len)1766f3e0bcdSDavid van Moolenbroek rmib_copyin(struct rmib_newp * __restrict newp, void * __restrict buf,
1776f3e0bcdSDavid van Moolenbroek size_t len)
1786f3e0bcdSDavid van Moolenbroek {
1796f3e0bcdSDavid van Moolenbroek
1806f3e0bcdSDavid van Moolenbroek if (newp == NULL || len != newp->newp_len)
1816f3e0bcdSDavid van Moolenbroek return EINVAL;
1826f3e0bcdSDavid van Moolenbroek
1836f3e0bcdSDavid van Moolenbroek if (len == 0)
1846f3e0bcdSDavid van Moolenbroek return OK;
1856f3e0bcdSDavid van Moolenbroek
1866f3e0bcdSDavid van Moolenbroek return sys_safecopyfrom(MIB_PROC_NR, newp->newp_grant, 0,
1876f3e0bcdSDavid van Moolenbroek (vir_bytes)buf, len);
1886f3e0bcdSDavid van Moolenbroek }
1896f3e0bcdSDavid van Moolenbroek
1906f3e0bcdSDavid van Moolenbroek /*
1916f3e0bcdSDavid van Moolenbroek * Copy out a node to userland, using the exchange format for nodes (namely,
1926f3e0bcdSDavid van Moolenbroek * a sysctlnode structure). Return the size of the object that is (or, if the
1936f3e0bcdSDavid van Moolenbroek * node falls outside the requested data range, would be) copied out on
1946f3e0bcdSDavid van Moolenbroek * success, or a negative error code on failure.
1956f3e0bcdSDavid van Moolenbroek */
1966f3e0bcdSDavid van Moolenbroek static ssize_t
rmib_copyout_node(struct rmib_call * call,struct rmib_oldp * oldp,ssize_t off,unsigned int id,const struct rmib_node * rnode)1976f3e0bcdSDavid van Moolenbroek rmib_copyout_node(struct rmib_call * call, struct rmib_oldp * oldp,
1986f3e0bcdSDavid van Moolenbroek ssize_t off, unsigned int id, const struct rmib_node * rnode)
1996f3e0bcdSDavid van Moolenbroek {
2006f3e0bcdSDavid van Moolenbroek struct sysctlnode scn;
2016f3e0bcdSDavid van Moolenbroek int visible;
2026f3e0bcdSDavid van Moolenbroek
2036f3e0bcdSDavid van Moolenbroek if (!rmib_inrange(oldp, off))
2046f3e0bcdSDavid van Moolenbroek return sizeof(scn); /* nothing to do */
2056f3e0bcdSDavid van Moolenbroek
2066f3e0bcdSDavid van Moolenbroek memset(&scn, 0, sizeof(scn));
2076f3e0bcdSDavid van Moolenbroek
2086f3e0bcdSDavid van Moolenbroek /*
209*0f03189aSDavid van Moolenbroek * We use CTLFLAG_SPARSE internally only. NetBSD uses these flags for
210*0f03189aSDavid van Moolenbroek * different purposes. Either way, do not expose it to userland.
2116f3e0bcdSDavid van Moolenbroek * hide any of them from the user.
2126f3e0bcdSDavid van Moolenbroek */
213*0f03189aSDavid van Moolenbroek scn.sysctl_flags = SYSCTL_VERSION |
214*0f03189aSDavid van Moolenbroek (rnode->rnode_flags & ~CTLFLAG_SPARSE);
2156f3e0bcdSDavid van Moolenbroek scn.sysctl_num = id;
2166f3e0bcdSDavid van Moolenbroek strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name));
2176f3e0bcdSDavid van Moolenbroek scn.sysctl_ver = call->call_rootver;
2186f3e0bcdSDavid van Moolenbroek scn.sysctl_size = rnode->rnode_size;
2196f3e0bcdSDavid van Moolenbroek
2206f3e0bcdSDavid van Moolenbroek /* Some information is only visible if the user can access the node. */
2216f3e0bcdSDavid van Moolenbroek visible = (!(rnode->rnode_flags & CTLFLAG_PRIVATE) ||
2226f3e0bcdSDavid van Moolenbroek (call->call_flags & RMIB_FLAG_AUTH));
2236f3e0bcdSDavid van Moolenbroek
2246f3e0bcdSDavid van Moolenbroek /*
2256f3e0bcdSDavid van Moolenbroek * For immediate types, store the immediate value in the resulting
2266f3e0bcdSDavid van Moolenbroek * structure, unless the caller is not authorized to obtain the value.
2276f3e0bcdSDavid van Moolenbroek */
2286f3e0bcdSDavid van Moolenbroek if ((rnode->rnode_flags & CTLFLAG_IMMEDIATE) && visible) {
2296f3e0bcdSDavid van Moolenbroek switch (SYSCTL_TYPE(rnode->rnode_flags)) {
2306f3e0bcdSDavid van Moolenbroek case CTLTYPE_BOOL:
2316f3e0bcdSDavid van Moolenbroek scn.sysctl_bdata = rnode->rnode_bool;
2326f3e0bcdSDavid van Moolenbroek break;
2336f3e0bcdSDavid van Moolenbroek case CTLTYPE_INT:
2346f3e0bcdSDavid van Moolenbroek scn.sysctl_idata = rnode->rnode_int;
2356f3e0bcdSDavid van Moolenbroek break;
2366f3e0bcdSDavid van Moolenbroek case CTLTYPE_QUAD:
2376f3e0bcdSDavid van Moolenbroek scn.sysctl_qdata = rnode->rnode_quad;
2386f3e0bcdSDavid van Moolenbroek break;
2396f3e0bcdSDavid van Moolenbroek }
2406f3e0bcdSDavid van Moolenbroek }
2416f3e0bcdSDavid van Moolenbroek
2426f3e0bcdSDavid van Moolenbroek /* Special rules apply to parent nodes. */
2436f3e0bcdSDavid van Moolenbroek if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) {
2446f3e0bcdSDavid van Moolenbroek /* Report the node size the way NetBSD does, just in case. */
2456f3e0bcdSDavid van Moolenbroek scn.sysctl_size = sizeof(scn);
2466f3e0bcdSDavid van Moolenbroek
2476f3e0bcdSDavid van Moolenbroek /*
2486f3e0bcdSDavid van Moolenbroek * For real parent nodes, report child information, but only if
2496f3e0bcdSDavid van Moolenbroek * the node itself is accessible by the caller. For function-
2506f3e0bcdSDavid van Moolenbroek * driven nodes, set a nonzero function address, for trace(1).
2516f3e0bcdSDavid van Moolenbroek */
2526f3e0bcdSDavid van Moolenbroek if (rnode->rnode_func == NULL && visible) {
2536f3e0bcdSDavid van Moolenbroek scn.sysctl_csize = rnode->rnode_size;
2546f3e0bcdSDavid van Moolenbroek scn.sysctl_clen = rnode->rnode_clen;
2556f3e0bcdSDavid van Moolenbroek } else if (rnode->rnode_func != NULL)
2566f3e0bcdSDavid van Moolenbroek scn.sysctl_func = SYSCTL_NODE_FN;
2576f3e0bcdSDavid van Moolenbroek }
2586f3e0bcdSDavid van Moolenbroek
2596f3e0bcdSDavid van Moolenbroek /* Copy out the resulting node. */
2606f3e0bcdSDavid van Moolenbroek return rmib_copyout(oldp, off, &scn, sizeof(scn));
2616f3e0bcdSDavid van Moolenbroek }
2626f3e0bcdSDavid van Moolenbroek
2636f3e0bcdSDavid van Moolenbroek /*
2646f3e0bcdSDavid van Moolenbroek * Given a query on a non-leaf (parent) node, provide the user with an array of
2656f3e0bcdSDavid van Moolenbroek * this node's children.
2666f3e0bcdSDavid van Moolenbroek */
2676f3e0bcdSDavid van Moolenbroek static ssize_t
rmib_query(struct rmib_call * call,struct rmib_node * rparent,struct rmib_oldp * oldp,struct rmib_newp * newp)2686f3e0bcdSDavid van Moolenbroek rmib_query(struct rmib_call * call, struct rmib_node * rparent,
2696f3e0bcdSDavid van Moolenbroek struct rmib_oldp * oldp, struct rmib_newp * newp)
2706f3e0bcdSDavid van Moolenbroek {
2716f3e0bcdSDavid van Moolenbroek struct sysctlnode scn;
2726f3e0bcdSDavid van Moolenbroek struct rmib_node *rnode;
273*0f03189aSDavid van Moolenbroek unsigned int i, id;
2746f3e0bcdSDavid van Moolenbroek ssize_t r, off;
2756f3e0bcdSDavid van Moolenbroek
2766f3e0bcdSDavid van Moolenbroek /* If the user passed in version numbers, check them. */
2776f3e0bcdSDavid van Moolenbroek if (newp != NULL) {
2786f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK)
2796f3e0bcdSDavid van Moolenbroek return r;
2806f3e0bcdSDavid van Moolenbroek
2816f3e0bcdSDavid van Moolenbroek if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
2826f3e0bcdSDavid van Moolenbroek return EINVAL;
2836f3e0bcdSDavid van Moolenbroek
2846f3e0bcdSDavid van Moolenbroek /*
2856f3e0bcdSDavid van Moolenbroek * If a node version number is given, it must match the version
2866f3e0bcdSDavid van Moolenbroek * of the subtree or the root of the entire MIB version.
2876f3e0bcdSDavid van Moolenbroek */
2886f3e0bcdSDavid van Moolenbroek if (scn.sysctl_ver != 0 &&
2896f3e0bcdSDavid van Moolenbroek scn.sysctl_ver != call->call_rootver &&
2906f3e0bcdSDavid van Moolenbroek scn.sysctl_ver != call->call_treever)
2916f3e0bcdSDavid van Moolenbroek return EINVAL;
2926f3e0bcdSDavid van Moolenbroek }
2936f3e0bcdSDavid van Moolenbroek
2946f3e0bcdSDavid van Moolenbroek /* Enumerate the child nodes of the given parent node. */
2956f3e0bcdSDavid van Moolenbroek off = 0;
2966f3e0bcdSDavid van Moolenbroek
297*0f03189aSDavid van Moolenbroek for (i = 0; i < rparent->rnode_size; i++) {
298*0f03189aSDavid van Moolenbroek if (rparent->rnode_flags & CTLFLAG_SPARSE) {
299*0f03189aSDavid van Moolenbroek id = rparent->rnode_icptr[i].rindir_id;
300*0f03189aSDavid van Moolenbroek rnode = rparent->rnode_icptr[i].rindir_node;
301*0f03189aSDavid van Moolenbroek } else {
302*0f03189aSDavid van Moolenbroek id = i;
303*0f03189aSDavid van Moolenbroek rnode = &rparent->rnode_cptr[i];
3046f3e0bcdSDavid van Moolenbroek
3056f3e0bcdSDavid van Moolenbroek if (rnode->rnode_flags == 0)
3066f3e0bcdSDavid van Moolenbroek continue;
307*0f03189aSDavid van Moolenbroek }
3086f3e0bcdSDavid van Moolenbroek
3096f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0)
3106f3e0bcdSDavid van Moolenbroek return r;
3116f3e0bcdSDavid van Moolenbroek off += r;
3126f3e0bcdSDavid van Moolenbroek }
3136f3e0bcdSDavid van Moolenbroek
3146f3e0bcdSDavid van Moolenbroek return off;
3156f3e0bcdSDavid van Moolenbroek }
3166f3e0bcdSDavid van Moolenbroek
3176f3e0bcdSDavid van Moolenbroek /*
3186f3e0bcdSDavid van Moolenbroek * Copy out a node description to userland, using the exchange format for node
3196f3e0bcdSDavid van Moolenbroek * descriptions (namely, a sysctldesc structure). Return the size of the
3206f3e0bcdSDavid van Moolenbroek * object that is (or, if the description falls outside the requested data
3216f3e0bcdSDavid van Moolenbroek * range, would be) copied out on success, or a negative error code on failure.
3226f3e0bcdSDavid van Moolenbroek * The function may return 0 to indicate that nothing was copied out after all.
3236f3e0bcdSDavid van Moolenbroek */
3246f3e0bcdSDavid van Moolenbroek static ssize_t
rmib_copyout_desc(struct rmib_call * call,struct rmib_oldp * oldp,ssize_t off,unsigned int id,const struct rmib_node * rnode)3256f3e0bcdSDavid van Moolenbroek rmib_copyout_desc(struct rmib_call * call, struct rmib_oldp * oldp,
3266f3e0bcdSDavid van Moolenbroek ssize_t off, unsigned int id, const struct rmib_node * rnode)
3276f3e0bcdSDavid van Moolenbroek {
3286f3e0bcdSDavid van Moolenbroek struct sysctldesc scd;
3296f3e0bcdSDavid van Moolenbroek size_t len, size;
3306f3e0bcdSDavid van Moolenbroek ssize_t r;
3316f3e0bcdSDavid van Moolenbroek
3326f3e0bcdSDavid van Moolenbroek /* Descriptions of private nodes are considered private too. */
3336f3e0bcdSDavid van Moolenbroek if ((rnode->rnode_flags & CTLFLAG_PRIVATE) &&
3346f3e0bcdSDavid van Moolenbroek !(call->call_flags & RMIB_FLAG_AUTH))
3356f3e0bcdSDavid van Moolenbroek return 0;
3366f3e0bcdSDavid van Moolenbroek
3376f3e0bcdSDavid van Moolenbroek /*
3386f3e0bcdSDavid van Moolenbroek * Unfortunately, we do not have a scratch buffer here. Instead, copy
3396f3e0bcdSDavid van Moolenbroek * out the description structure and the actual description string
3406f3e0bcdSDavid van Moolenbroek * separately. This is more costly, but remote subtrees are already
3416f3e0bcdSDavid van Moolenbroek * not going to give the best performance ever. We do optimize for the
3426f3e0bcdSDavid van Moolenbroek * case that there is no description, because that is relatively easy.
3436f3e0bcdSDavid van Moolenbroek */
3446f3e0bcdSDavid van Moolenbroek /* The description length includes the null terminator. */
3456f3e0bcdSDavid van Moolenbroek if (rnode->rnode_desc != NULL)
3466f3e0bcdSDavid van Moolenbroek len = strlen(rnode->rnode_desc) + 1;
3476f3e0bcdSDavid van Moolenbroek else
3486f3e0bcdSDavid van Moolenbroek len = 1;
3496f3e0bcdSDavid van Moolenbroek
3506f3e0bcdSDavid van Moolenbroek memset(&scd, 0, sizeof(scd));
3516f3e0bcdSDavid van Moolenbroek scd.descr_num = id;
3526f3e0bcdSDavid van Moolenbroek scd.descr_ver = call->call_rootver;
3536f3e0bcdSDavid van Moolenbroek scd.descr_len = len;
3546f3e0bcdSDavid van Moolenbroek
3556f3e0bcdSDavid van Moolenbroek size = offsetof(struct sysctldesc, descr_str);
3566f3e0bcdSDavid van Moolenbroek
3576f3e0bcdSDavid van Moolenbroek if (len == 1) {
3586f3e0bcdSDavid van Moolenbroek scd.descr_str[0] = '\0'; /* superfluous */
3596f3e0bcdSDavid van Moolenbroek size++;
3606f3e0bcdSDavid van Moolenbroek }
3616f3e0bcdSDavid van Moolenbroek
3626f3e0bcdSDavid van Moolenbroek /* Copy out the structure, possibly including a null terminator. */
3636f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyout(oldp, off, &scd, size)) < 0)
3646f3e0bcdSDavid van Moolenbroek return r;
3656f3e0bcdSDavid van Moolenbroek
3666f3e0bcdSDavid van Moolenbroek if (len > 1) {
3676f3e0bcdSDavid van Moolenbroek /* Copy out the description itself. */
3686f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyout(oldp, off + size, rnode->rnode_desc,
3696f3e0bcdSDavid van Moolenbroek len)) < 0)
3706f3e0bcdSDavid van Moolenbroek return r;
3716f3e0bcdSDavid van Moolenbroek
3726f3e0bcdSDavid van Moolenbroek size += len;
3736f3e0bcdSDavid van Moolenbroek }
3746f3e0bcdSDavid van Moolenbroek
3756f3e0bcdSDavid van Moolenbroek /*
3766f3e0bcdSDavid van Moolenbroek * By aligning just the size, we may leave garbage between the entries
3776f3e0bcdSDavid van Moolenbroek * copied out, which is fine because it is userland's own data.
3786f3e0bcdSDavid van Moolenbroek */
3796f3e0bcdSDavid van Moolenbroek return roundup2(size, sizeof(int32_t));
3806f3e0bcdSDavid van Moolenbroek }
3816f3e0bcdSDavid van Moolenbroek
3826f3e0bcdSDavid van Moolenbroek /*
383*0f03189aSDavid van Moolenbroek * Look up a child node given a parent node and a child node identifier.
384*0f03189aSDavid van Moolenbroek * Return a pointer to the child node if found, or NULL otherwise. The lookup
385*0f03189aSDavid van Moolenbroek * procedure differs based on whether the parent node is sparse or not.
386*0f03189aSDavid van Moolenbroek */
387*0f03189aSDavid van Moolenbroek static struct rmib_node *
rmib_lookup(struct rmib_node * rparent,unsigned int id)388*0f03189aSDavid van Moolenbroek rmib_lookup(struct rmib_node * rparent, unsigned int id)
389*0f03189aSDavid van Moolenbroek {
390*0f03189aSDavid van Moolenbroek struct rmib_node *rnode;
391*0f03189aSDavid van Moolenbroek struct rmib_indir *rindir;
392*0f03189aSDavid van Moolenbroek unsigned int i;
393*0f03189aSDavid van Moolenbroek
394*0f03189aSDavid van Moolenbroek if (rparent->rnode_flags & CTLFLAG_SPARSE) {
395*0f03189aSDavid van Moolenbroek rindir = rparent->rnode_icptr;
396*0f03189aSDavid van Moolenbroek for (i = 0; i < rparent->rnode_size; i++, rindir++)
397*0f03189aSDavid van Moolenbroek if (rindir->rindir_id == id)
398*0f03189aSDavid van Moolenbroek return rindir->rindir_node;
399*0f03189aSDavid van Moolenbroek } else {
400*0f03189aSDavid van Moolenbroek if (id >= rparent->rnode_size)
401*0f03189aSDavid van Moolenbroek return NULL;
402*0f03189aSDavid van Moolenbroek rnode = &rparent->rnode_cptr[id];
403*0f03189aSDavid van Moolenbroek if (rnode->rnode_flags != 0)
404*0f03189aSDavid van Moolenbroek return rnode;
405*0f03189aSDavid van Moolenbroek }
406*0f03189aSDavid van Moolenbroek
407*0f03189aSDavid van Moolenbroek return NULL;
408*0f03189aSDavid van Moolenbroek }
409*0f03189aSDavid van Moolenbroek
410*0f03189aSDavid van Moolenbroek /*
4116f3e0bcdSDavid van Moolenbroek * Retrieve node descriptions in bulk, or retrieve a particular node's
4126f3e0bcdSDavid van Moolenbroek * description.
4136f3e0bcdSDavid van Moolenbroek */
4146f3e0bcdSDavid van Moolenbroek static ssize_t
rmib_describe(struct rmib_call * call,struct rmib_node * rparent,struct rmib_oldp * oldp,struct rmib_newp * newp)4156f3e0bcdSDavid van Moolenbroek rmib_describe(struct rmib_call * call, struct rmib_node * rparent,
4166f3e0bcdSDavid van Moolenbroek struct rmib_oldp * oldp, struct rmib_newp * newp)
4176f3e0bcdSDavid van Moolenbroek {
4186f3e0bcdSDavid van Moolenbroek struct sysctlnode scn;
4196f3e0bcdSDavid van Moolenbroek struct rmib_node *rnode;
420*0f03189aSDavid van Moolenbroek unsigned int i, id;
4216f3e0bcdSDavid van Moolenbroek ssize_t r, off;
4226f3e0bcdSDavid van Moolenbroek
4236f3e0bcdSDavid van Moolenbroek if (newp != NULL) {
4246f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK)
4256f3e0bcdSDavid van Moolenbroek return r;
4266f3e0bcdSDavid van Moolenbroek
4276f3e0bcdSDavid van Moolenbroek if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION)
4286f3e0bcdSDavid van Moolenbroek return EINVAL;
4296f3e0bcdSDavid van Moolenbroek
4306f3e0bcdSDavid van Moolenbroek /* Locate the child node. */
431*0f03189aSDavid van Moolenbroek if ((rnode = rmib_lookup(rparent, scn.sysctl_num)) == NULL)
4326f3e0bcdSDavid van Moolenbroek return ENOENT;
4336f3e0bcdSDavid van Moolenbroek
4346f3e0bcdSDavid van Moolenbroek /* Descriptions of private nodes are considered private too. */
4356f3e0bcdSDavid van Moolenbroek if ((rnode->rnode_flags & CTLFLAG_PRIVATE) &&
4366f3e0bcdSDavid van Moolenbroek !(call->call_flags & RMIB_FLAG_AUTH))
4376f3e0bcdSDavid van Moolenbroek return EPERM;
4386f3e0bcdSDavid van Moolenbroek
4396f3e0bcdSDavid van Moolenbroek /*
4406f3e0bcdSDavid van Moolenbroek * If a description pointer was given, this is a request to
4416f3e0bcdSDavid van Moolenbroek * set the node's description. We do not allow this, nor would
4426f3e0bcdSDavid van Moolenbroek * we be able to support it, since we cannot access the data.
4436f3e0bcdSDavid van Moolenbroek */
4446f3e0bcdSDavid van Moolenbroek if (scn.sysctl_desc != NULL)
4456f3e0bcdSDavid van Moolenbroek return EPERM;
4466f3e0bcdSDavid van Moolenbroek
4476f3e0bcdSDavid van Moolenbroek /*
4486f3e0bcdSDavid van Moolenbroek * Copy out the requested node's description. At this point we
4496f3e0bcdSDavid van Moolenbroek * should be sure that this call does not return zero.
4506f3e0bcdSDavid van Moolenbroek */
4516f3e0bcdSDavid van Moolenbroek return rmib_copyout_desc(call, oldp, 0, scn.sysctl_num, rnode);
4526f3e0bcdSDavid van Moolenbroek }
4536f3e0bcdSDavid van Moolenbroek
4546f3e0bcdSDavid van Moolenbroek /* Describe the child nodes of the given parent node. */
4556f3e0bcdSDavid van Moolenbroek off = 0;
4566f3e0bcdSDavid van Moolenbroek
457*0f03189aSDavid van Moolenbroek for (i = 0; i < rparent->rnode_size; i++) {
458*0f03189aSDavid van Moolenbroek if (rparent->rnode_flags & CTLFLAG_SPARSE) {
459*0f03189aSDavid van Moolenbroek id = rparent->rnode_icptr[i].rindir_id;
460*0f03189aSDavid van Moolenbroek rnode = rparent->rnode_icptr[i].rindir_node;
461*0f03189aSDavid van Moolenbroek } else {
462*0f03189aSDavid van Moolenbroek id = i;
463*0f03189aSDavid van Moolenbroek rnode = &rparent->rnode_cptr[i];
4646f3e0bcdSDavid van Moolenbroek
4656f3e0bcdSDavid van Moolenbroek if (rnode->rnode_flags == 0)
4666f3e0bcdSDavid van Moolenbroek continue;
467*0f03189aSDavid van Moolenbroek }
4686f3e0bcdSDavid van Moolenbroek
4696f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0)
4706f3e0bcdSDavid van Moolenbroek return r;
4716f3e0bcdSDavid van Moolenbroek off += r;
4726f3e0bcdSDavid van Moolenbroek }
4736f3e0bcdSDavid van Moolenbroek
4746f3e0bcdSDavid van Moolenbroek return off;
4756f3e0bcdSDavid van Moolenbroek }
4766f3e0bcdSDavid van Moolenbroek
4776f3e0bcdSDavid van Moolenbroek /*
4786f3e0bcdSDavid van Moolenbroek * Return a pointer to the data associated with the given node, or NULL if the
4796f3e0bcdSDavid van Moolenbroek * node has no associated data. Actual calls to this function should never
4806f3e0bcdSDavid van Moolenbroek * result in NULL - as long as the proper rules are followed elsewhere.
4816f3e0bcdSDavid van Moolenbroek */
4826f3e0bcdSDavid van Moolenbroek static void *
rmib_getptr(struct rmib_node * rnode)4836f3e0bcdSDavid van Moolenbroek rmib_getptr(struct rmib_node * rnode)
4846f3e0bcdSDavid van Moolenbroek {
4856f3e0bcdSDavid van Moolenbroek
4866f3e0bcdSDavid van Moolenbroek switch (SYSCTL_TYPE(rnode->rnode_flags)) {
4876f3e0bcdSDavid van Moolenbroek case CTLTYPE_BOOL:
4886f3e0bcdSDavid van Moolenbroek if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
4896f3e0bcdSDavid van Moolenbroek return &rnode->rnode_bool;
4906f3e0bcdSDavid van Moolenbroek break;
4916f3e0bcdSDavid van Moolenbroek case CTLTYPE_INT:
4926f3e0bcdSDavid van Moolenbroek if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
4936f3e0bcdSDavid van Moolenbroek return &rnode->rnode_int;
4946f3e0bcdSDavid van Moolenbroek break;
4956f3e0bcdSDavid van Moolenbroek case CTLTYPE_QUAD:
4966f3e0bcdSDavid van Moolenbroek if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
4976f3e0bcdSDavid van Moolenbroek return &rnode->rnode_quad;
4986f3e0bcdSDavid van Moolenbroek break;
4996f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRING:
5006f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRUCT:
5016f3e0bcdSDavid van Moolenbroek if (rnode->rnode_flags & CTLFLAG_IMMEDIATE)
5026f3e0bcdSDavid van Moolenbroek return NULL;
5036f3e0bcdSDavid van Moolenbroek break;
5046f3e0bcdSDavid van Moolenbroek default:
5056f3e0bcdSDavid van Moolenbroek return NULL;
5066f3e0bcdSDavid van Moolenbroek }
5076f3e0bcdSDavid van Moolenbroek
5086f3e0bcdSDavid van Moolenbroek return rnode->rnode_data;
5096f3e0bcdSDavid van Moolenbroek }
5106f3e0bcdSDavid van Moolenbroek
5116f3e0bcdSDavid van Moolenbroek /*
5126f3e0bcdSDavid van Moolenbroek * Read current (old) data from a regular data node, if requested. Return the
5136f3e0bcdSDavid van Moolenbroek * old data length.
5146f3e0bcdSDavid van Moolenbroek */
5156f3e0bcdSDavid van Moolenbroek static ssize_t
rmib_read(struct rmib_node * rnode,struct rmib_oldp * oldp)5166f3e0bcdSDavid van Moolenbroek rmib_read(struct rmib_node * rnode, struct rmib_oldp * oldp)
5176f3e0bcdSDavid van Moolenbroek {
5186f3e0bcdSDavid van Moolenbroek void *ptr;
5196f3e0bcdSDavid van Moolenbroek size_t oldlen;
5206f3e0bcdSDavid van Moolenbroek int r;
5216f3e0bcdSDavid van Moolenbroek
5226f3e0bcdSDavid van Moolenbroek if ((ptr = rmib_getptr(rnode)) == NULL)
5236f3e0bcdSDavid van Moolenbroek return EINVAL;
5246f3e0bcdSDavid van Moolenbroek
5256f3e0bcdSDavid van Moolenbroek if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_STRING)
5266f3e0bcdSDavid van Moolenbroek oldlen = strlen(rnode->rnode_data) + 1;
5276f3e0bcdSDavid van Moolenbroek else
5286f3e0bcdSDavid van Moolenbroek oldlen = rnode->rnode_size;
5296f3e0bcdSDavid van Moolenbroek
5306f3e0bcdSDavid van Moolenbroek if (oldlen > SSIZE_MAX)
5316f3e0bcdSDavid van Moolenbroek return EINVAL;
5326f3e0bcdSDavid van Moolenbroek
5336f3e0bcdSDavid van Moolenbroek /* Copy out the current data, if requested at all. */
5346f3e0bcdSDavid van Moolenbroek if (oldp != NULL && (r = rmib_copyout(oldp, 0, ptr, oldlen)) < 0)
5356f3e0bcdSDavid van Moolenbroek return r;
5366f3e0bcdSDavid van Moolenbroek
5376f3e0bcdSDavid van Moolenbroek /* Return the current length in any case. */
5386f3e0bcdSDavid van Moolenbroek return (ssize_t)oldlen;
5396f3e0bcdSDavid van Moolenbroek }
5406f3e0bcdSDavid van Moolenbroek
5416f3e0bcdSDavid van Moolenbroek /*
5426f3e0bcdSDavid van Moolenbroek * Write new data into a regular data node, if requested.
5436f3e0bcdSDavid van Moolenbroek */
5446f3e0bcdSDavid van Moolenbroek static int
rmib_write(struct rmib_call * call,struct rmib_node * rnode,struct rmib_newp * newp)5456f3e0bcdSDavid van Moolenbroek rmib_write(struct rmib_call * call, struct rmib_node * rnode,
5466f3e0bcdSDavid van Moolenbroek struct rmib_newp * newp)
5476f3e0bcdSDavid van Moolenbroek {
5486f3e0bcdSDavid van Moolenbroek bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* for sanitizing */
5496f3e0bcdSDavid van Moolenbroek char *src, *dst, buf[RMIB_STACKBUF];
5506f3e0bcdSDavid van Moolenbroek size_t newlen;
5516f3e0bcdSDavid van Moolenbroek int r;
5526f3e0bcdSDavid van Moolenbroek
5536f3e0bcdSDavid van Moolenbroek if (newp == NULL)
5546f3e0bcdSDavid van Moolenbroek return OK; /* nothing to do */
5556f3e0bcdSDavid van Moolenbroek
5566f3e0bcdSDavid van Moolenbroek /*
5576f3e0bcdSDavid van Moolenbroek * When setting a new value, we cannot risk doing an in-place update:
5586f3e0bcdSDavid van Moolenbroek * the copy from userland may fail halfway through, in which case an
5596f3e0bcdSDavid van Moolenbroek * in-place update could leave the node value in a corrupted state.
5606f3e0bcdSDavid van Moolenbroek * Thus, we must first fetch any new data into a temporary buffer.
5616f3e0bcdSDavid van Moolenbroek */
5626f3e0bcdSDavid van Moolenbroek newlen = newp->newp_len;
5636f3e0bcdSDavid van Moolenbroek
5646f3e0bcdSDavid van Moolenbroek if ((dst = rmib_getptr(rnode)) == NULL)
5656f3e0bcdSDavid van Moolenbroek return EINVAL;
5666f3e0bcdSDavid van Moolenbroek
5676f3e0bcdSDavid van Moolenbroek switch (SYSCTL_TYPE(rnode->rnode_flags)) {
5686f3e0bcdSDavid van Moolenbroek case CTLTYPE_BOOL:
5696f3e0bcdSDavid van Moolenbroek case CTLTYPE_INT:
5706f3e0bcdSDavid van Moolenbroek case CTLTYPE_QUAD:
5716f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRUCT:
5726f3e0bcdSDavid van Moolenbroek /* Non-string types must have an exact size match. */
5736f3e0bcdSDavid van Moolenbroek if (newlen != rnode->rnode_size)
5746f3e0bcdSDavid van Moolenbroek return EINVAL;
5756f3e0bcdSDavid van Moolenbroek break;
5766f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRING:
5776f3e0bcdSDavid van Moolenbroek /*
5786f3e0bcdSDavid van Moolenbroek * Strings must not exceed their buffer size. There is a
5796f3e0bcdSDavid van Moolenbroek * second check further below, because we allow userland to
5806f3e0bcdSDavid van Moolenbroek * give us an unterminated string. In that case we terminate
5816f3e0bcdSDavid van Moolenbroek * it ourselves, but then the null terminator must fit as well.
5826f3e0bcdSDavid van Moolenbroek */
5836f3e0bcdSDavid van Moolenbroek if (newlen > rnode->rnode_size)
5846f3e0bcdSDavid van Moolenbroek return EINVAL;
5856f3e0bcdSDavid van Moolenbroek break;
5866f3e0bcdSDavid van Moolenbroek default:
5876f3e0bcdSDavid van Moolenbroek return EINVAL;
5886f3e0bcdSDavid van Moolenbroek }
5896f3e0bcdSDavid van Moolenbroek
5906f3e0bcdSDavid van Moolenbroek /*
5916f3e0bcdSDavid van Moolenbroek * If we cannot fit the data in the small stack buffer, then allocate a
5926f3e0bcdSDavid van Moolenbroek * temporary buffer. We add one extra byte so that we can add a null
5936f3e0bcdSDavid van Moolenbroek * terminator at the end of strings in case userland did not supply
5946f3e0bcdSDavid van Moolenbroek * one. Either way, we must free the temporary buffer later!
5956f3e0bcdSDavid van Moolenbroek */
5966f3e0bcdSDavid van Moolenbroek if (newlen + 1 > sizeof(buf)) {
5976f3e0bcdSDavid van Moolenbroek /*
5986f3e0bcdSDavid van Moolenbroek * For regular users, we do not want to perform dynamic memory
5996f3e0bcdSDavid van Moolenbroek * allocation. Thus, for CTLTYPE_ANYWRITE nodes, only the
6006f3e0bcdSDavid van Moolenbroek * superuser may set values exceeding the small buffer in size.
6016f3e0bcdSDavid van Moolenbroek */
6026f3e0bcdSDavid van Moolenbroek if (!(call->call_flags & RMIB_FLAG_AUTH))
6036f3e0bcdSDavid van Moolenbroek return EPERM;
6046f3e0bcdSDavid van Moolenbroek
6056f3e0bcdSDavid van Moolenbroek /* Do not return ENOMEM on allocation failure. */
6066f3e0bcdSDavid van Moolenbroek if ((src = malloc(newlen + 1)) == NULL)
6076f3e0bcdSDavid van Moolenbroek return EINVAL;
6086f3e0bcdSDavid van Moolenbroek } else
6096f3e0bcdSDavid van Moolenbroek src = buf;
6106f3e0bcdSDavid van Moolenbroek
6116f3e0bcdSDavid van Moolenbroek /* Copy in the data. Note that the given new length may be zero. */
6126f3e0bcdSDavid van Moolenbroek if ((r = rmib_copyin(newp, src, newlen)) == OK) {
6136f3e0bcdSDavid van Moolenbroek /* Check and, if acceptable, store the new value. */
6146f3e0bcdSDavid van Moolenbroek switch (SYSCTL_TYPE(rnode->rnode_flags)) {
6156f3e0bcdSDavid van Moolenbroek case CTLTYPE_BOOL:
6166f3e0bcdSDavid van Moolenbroek /* Sanitize booleans. See the MIB code for details. */
6176f3e0bcdSDavid van Moolenbroek b[0] = (bool)src[0];
6186f3e0bcdSDavid van Moolenbroek memcpy(dst, &b[0], sizeof(b[0]));
6196f3e0bcdSDavid van Moolenbroek break;
6206f3e0bcdSDavid van Moolenbroek case CTLTYPE_INT:
6216f3e0bcdSDavid van Moolenbroek case CTLTYPE_QUAD:
6226f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRUCT:
6236f3e0bcdSDavid van Moolenbroek memcpy(dst, src, rnode->rnode_size);
6246f3e0bcdSDavid van Moolenbroek break;
6256f3e0bcdSDavid van Moolenbroek case CTLTYPE_STRING:
6266f3e0bcdSDavid van Moolenbroek if (newlen == rnode->rnode_size &&
6276f3e0bcdSDavid van Moolenbroek src[newlen - 1] != '\0') {
6286f3e0bcdSDavid van Moolenbroek /* Our null terminator does not fit! */
6296f3e0bcdSDavid van Moolenbroek r = EINVAL;
6306f3e0bcdSDavid van Moolenbroek break;
6316f3e0bcdSDavid van Moolenbroek }
6326f3e0bcdSDavid van Moolenbroek src[newlen] = '\0';
6336f3e0bcdSDavid van Moolenbroek strlcpy(dst, src, rnode->rnode_size);
6346f3e0bcdSDavid van Moolenbroek break;
6356f3e0bcdSDavid van Moolenbroek default:
6366f3e0bcdSDavid van Moolenbroek r = EINVAL;
6376f3e0bcdSDavid van Moolenbroek }
6386f3e0bcdSDavid van Moolenbroek }
6396f3e0bcdSDavid van Moolenbroek
6406f3e0bcdSDavid van Moolenbroek if (src != buf)
6416f3e0bcdSDavid van Moolenbroek free(src);
6426f3e0bcdSDavid van Moolenbroek
6436f3e0bcdSDavid van Moolenbroek return r;
6446f3e0bcdSDavid van Moolenbroek }
6456f3e0bcdSDavid van Moolenbroek
6466f3e0bcdSDavid van Moolenbroek /*
6476f3e0bcdSDavid van Moolenbroek * Read and/or write the value of a regular data node. A regular data node is
6486f3e0bcdSDavid van Moolenbroek * a leaf node. Typically, a leaf node has no associated function, in which
6496f3e0bcdSDavid van Moolenbroek * case this function will be used instead. In addition, this function may be
6506f3e0bcdSDavid van Moolenbroek * used from handler functions as part of their functionality.
6516f3e0bcdSDavid van Moolenbroek */
6526f3e0bcdSDavid van Moolenbroek ssize_t
rmib_readwrite(struct rmib_call * call,struct rmib_node * rnode,struct rmib_oldp * oldp,struct rmib_newp * newp)6536f3e0bcdSDavid van Moolenbroek rmib_readwrite(struct rmib_call * call, struct rmib_node * rnode,
6546f3e0bcdSDavid van Moolenbroek struct rmib_oldp * oldp, struct rmib_newp * newp)
6556f3e0bcdSDavid van Moolenbroek {
6566f3e0bcdSDavid van Moolenbroek ssize_t len;
6576f3e0bcdSDavid van Moolenbroek int r;
6586f3e0bcdSDavid van Moolenbroek
6596f3e0bcdSDavid van Moolenbroek /* Copy out old data, if requested. Always get the old data length. */
6606f3e0bcdSDavid van Moolenbroek if ((r = len = rmib_read(rnode, oldp)) < 0)
6616f3e0bcdSDavid van Moolenbroek return r;
6626f3e0bcdSDavid van Moolenbroek
6636f3e0bcdSDavid van Moolenbroek /* Copy in new data, if requested. */
6646f3e0bcdSDavid van Moolenbroek if ((r = rmib_write(call, rnode, newp)) != OK)
6656f3e0bcdSDavid van Moolenbroek return r;
6666f3e0bcdSDavid van Moolenbroek
6676f3e0bcdSDavid van Moolenbroek /* Return the old data length. */
6686f3e0bcdSDavid van Moolenbroek return len;
6696f3e0bcdSDavid van Moolenbroek }
6706f3e0bcdSDavid van Moolenbroek
6716f3e0bcdSDavid van Moolenbroek /*
6726f3e0bcdSDavid van Moolenbroek * Handle a sysctl(2) call from a user process, relayed by the MIB service to
6736f3e0bcdSDavid van Moolenbroek * us. If the call succeeds, return the old length. The MIB service will
6746f3e0bcdSDavid van Moolenbroek * perform a check against the given old length and return ENOMEM to the caller
6756f3e0bcdSDavid van Moolenbroek * when applicable, so we do not have to do that here. If the call fails,
6766f3e0bcdSDavid van Moolenbroek * return a negative error code.
6776f3e0bcdSDavid van Moolenbroek */
6786f3e0bcdSDavid van Moolenbroek static ssize_t
rmib_call(const message * m_in)6796f3e0bcdSDavid van Moolenbroek rmib_call(const message * m_in)
6806f3e0bcdSDavid van Moolenbroek {
6816f3e0bcdSDavid van Moolenbroek struct rmib_node *rnode, *rparent;
6826f3e0bcdSDavid van Moolenbroek struct rmib_call call;
6836f3e0bcdSDavid van Moolenbroek struct rmib_oldp oldp_data, *oldp;
6846f3e0bcdSDavid van Moolenbroek struct rmib_newp newp_data, *newp;
685241ebcaeSDavid van Moolenbroek unsigned int root_id, prefixlen, namelen;
6866f3e0bcdSDavid van Moolenbroek int r, id, is_leaf, has_func, name[CTL_MAXNAME];
6876f3e0bcdSDavid van Moolenbroek
6886f3e0bcdSDavid van Moolenbroek /*
6896f3e0bcdSDavid van Moolenbroek * Look up the root of the subtree that is the subject of the call. If
6906f3e0bcdSDavid van Moolenbroek * the call is for a subtree that is not registered, return ERESTART to
6916f3e0bcdSDavid van Moolenbroek * indicate to the MIB service that it should deregister the subtree it
6926f3e0bcdSDavid van Moolenbroek * thinks we have. This case may occur in practice if a deregistration
6936f3e0bcdSDavid van Moolenbroek * request from us crosses a sysctl call request from the MIB service.
6946f3e0bcdSDavid van Moolenbroek */
6956f3e0bcdSDavid van Moolenbroek root_id = m_in->m_mib_lsys_call.root_id;
696241ebcaeSDavid van Moolenbroek if (root_id >= __arraycount(rnodes) ||
697241ebcaeSDavid van Moolenbroek (rnode = rnodes[root_id].rno_node) == NULL)
6986f3e0bcdSDavid van Moolenbroek return ERESTART;
699241ebcaeSDavid van Moolenbroek
700241ebcaeSDavid van Moolenbroek /*
701241ebcaeSDavid van Moolenbroek * Use the name of the mounted subtree as prefix to the given name, so
702241ebcaeSDavid van Moolenbroek * that call_oname will point to the complete name of the node. This
703241ebcaeSDavid van Moolenbroek * is necessary for the few queries that make use of call_oname.
704241ebcaeSDavid van Moolenbroek */
705241ebcaeSDavid van Moolenbroek prefixlen = rnodes[root_id].rno_namelen;
706241ebcaeSDavid van Moolenbroek memcpy(name, rnodes[root_id].rno_name, prefixlen * sizeof(name[0]));
7076f3e0bcdSDavid van Moolenbroek
7086f3e0bcdSDavid van Moolenbroek /*
7096f3e0bcdSDavid van Moolenbroek * Set up all data structures that we need to use while handling the
7106f3e0bcdSDavid van Moolenbroek * call processing. Start by copying in the remainder of the MIB name.
7116f3e0bcdSDavid van Moolenbroek */
7126f3e0bcdSDavid van Moolenbroek /* A zero name length is valid and should always yield EISDIR. */
7136f3e0bcdSDavid van Moolenbroek namelen = m_in->m_mib_lsys_call.name_len;
714241ebcaeSDavid van Moolenbroek if (prefixlen + namelen > __arraycount(name))
7156f3e0bcdSDavid van Moolenbroek return EINVAL;
7166f3e0bcdSDavid van Moolenbroek
7176f3e0bcdSDavid van Moolenbroek if (namelen > 0) {
7186f3e0bcdSDavid van Moolenbroek r = sys_safecopyfrom(m_in->m_source,
719241ebcaeSDavid van Moolenbroek m_in->m_mib_lsys_call.name_grant, 0,
720241ebcaeSDavid van Moolenbroek (vir_bytes)&name[prefixlen], sizeof(name[0]) * namelen);
7216f3e0bcdSDavid van Moolenbroek if (r != OK)
7226f3e0bcdSDavid van Moolenbroek return r;
7236f3e0bcdSDavid van Moolenbroek }
7246f3e0bcdSDavid van Moolenbroek
7256f3e0bcdSDavid van Moolenbroek oldp_data.oldp_grant = m_in->m_mib_lsys_call.oldp_grant;
7266f3e0bcdSDavid van Moolenbroek oldp_data.oldp_len = m_in->m_mib_lsys_call.oldp_len;
7276f3e0bcdSDavid van Moolenbroek oldp = (GRANT_VALID(oldp_data.oldp_grant)) ? &oldp_data : NULL;
7286f3e0bcdSDavid van Moolenbroek
7296f3e0bcdSDavid van Moolenbroek newp_data.newp_grant = m_in->m_mib_lsys_call.newp_grant;
7306f3e0bcdSDavid van Moolenbroek newp_data.newp_len = m_in->m_mib_lsys_call.newp_len;
7316f3e0bcdSDavid van Moolenbroek newp = (GRANT_VALID(newp_data.newp_grant)) ? &newp_data : NULL;
7326f3e0bcdSDavid van Moolenbroek
7336f3e0bcdSDavid van Moolenbroek call.call_endpt = m_in->m_mib_lsys_call.user_endpt;
734241ebcaeSDavid van Moolenbroek call.call_oname = name;
735241ebcaeSDavid van Moolenbroek call.call_name = &name[prefixlen];
7366f3e0bcdSDavid van Moolenbroek call.call_namelen = namelen;
7376f3e0bcdSDavid van Moolenbroek call.call_flags = m_in->m_mib_lsys_call.flags;
7386f3e0bcdSDavid van Moolenbroek call.call_rootver = m_in->m_mib_lsys_call.root_ver;
7396f3e0bcdSDavid van Moolenbroek call.call_treever = m_in->m_mib_lsys_call.tree_ver;
7406f3e0bcdSDavid van Moolenbroek
7416f3e0bcdSDavid van Moolenbroek /*
7426f3e0bcdSDavid van Moolenbroek * Dispatch the call.
7436f3e0bcdSDavid van Moolenbroek */
7446f3e0bcdSDavid van Moolenbroek for (rparent = rnode; call.call_namelen > 0; rparent = rnode) {
7456f3e0bcdSDavid van Moolenbroek id = call.call_name[0];
7466f3e0bcdSDavid van Moolenbroek call.call_name++;
7476f3e0bcdSDavid van Moolenbroek call.call_namelen--;
7486f3e0bcdSDavid van Moolenbroek
7496f3e0bcdSDavid van Moolenbroek assert(SYSCTL_TYPE(rparent->rnode_flags) == CTLTYPE_NODE);
7506f3e0bcdSDavid van Moolenbroek
7516f3e0bcdSDavid van Moolenbroek /* Check for meta-identifiers. */
7526f3e0bcdSDavid van Moolenbroek if (id < 0) {
7536f3e0bcdSDavid van Moolenbroek /*
7546f3e0bcdSDavid van Moolenbroek * A meta-identifier must always be the last name
7556f3e0bcdSDavid van Moolenbroek * component.
7566f3e0bcdSDavid van Moolenbroek */
7576f3e0bcdSDavid van Moolenbroek if (call.call_namelen > 0)
7586f3e0bcdSDavid van Moolenbroek return EINVAL;
7596f3e0bcdSDavid van Moolenbroek
7606f3e0bcdSDavid van Moolenbroek switch (id) {
7616f3e0bcdSDavid van Moolenbroek case CTL_QUERY:
7626f3e0bcdSDavid van Moolenbroek return rmib_query(&call, rparent, oldp, newp);
7636f3e0bcdSDavid van Moolenbroek case CTL_DESCRIBE:
7646f3e0bcdSDavid van Moolenbroek return rmib_describe(&call, rparent, oldp,
7656f3e0bcdSDavid van Moolenbroek newp);
7666f3e0bcdSDavid van Moolenbroek case CTL_CREATE:
7676f3e0bcdSDavid van Moolenbroek case CTL_DESTROY:
7686f3e0bcdSDavid van Moolenbroek /* We support fully static subtrees only. */
7696f3e0bcdSDavid van Moolenbroek return EPERM;
7706f3e0bcdSDavid van Moolenbroek default:
7716f3e0bcdSDavid van Moolenbroek return EOPNOTSUPP;
7726f3e0bcdSDavid van Moolenbroek }
7736f3e0bcdSDavid van Moolenbroek }
7746f3e0bcdSDavid van Moolenbroek
7756f3e0bcdSDavid van Moolenbroek /* Locate the child node. */
776*0f03189aSDavid van Moolenbroek if ((rnode = rmib_lookup(rparent, id)) == NULL)
7776f3e0bcdSDavid van Moolenbroek return ENOENT;
7786f3e0bcdSDavid van Moolenbroek
7796f3e0bcdSDavid van Moolenbroek /* Check if access is permitted at this level. */
7806f3e0bcdSDavid van Moolenbroek if ((rnode->rnode_flags & CTLFLAG_PRIVATE) &&
7816f3e0bcdSDavid van Moolenbroek !(call.call_flags & RMIB_FLAG_AUTH))
7826f3e0bcdSDavid van Moolenbroek return EPERM;
7836f3e0bcdSDavid van Moolenbroek
7846f3e0bcdSDavid van Moolenbroek /*
7856f3e0bcdSDavid van Moolenbroek * Is this a leaf node, and/or is this node handled by a
7866f3e0bcdSDavid van Moolenbroek * function? If either is true, resolution ends at this level.
7876f3e0bcdSDavid van Moolenbroek */
7886f3e0bcdSDavid van Moolenbroek is_leaf = (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE);
7896f3e0bcdSDavid van Moolenbroek has_func = (rnode->rnode_func != NULL);
7906f3e0bcdSDavid van Moolenbroek
7916f3e0bcdSDavid van Moolenbroek /*
7926f3e0bcdSDavid van Moolenbroek * The name may be longer only if the node is not a leaf. That
7936f3e0bcdSDavid van Moolenbroek * also applies to leaves with functions, so check this first.
7946f3e0bcdSDavid van Moolenbroek */
7956f3e0bcdSDavid van Moolenbroek if (is_leaf && call.call_namelen > 0)
7966f3e0bcdSDavid van Moolenbroek return ENOTDIR;
7976f3e0bcdSDavid van Moolenbroek
7986f3e0bcdSDavid van Moolenbroek /*
7996f3e0bcdSDavid van Moolenbroek * If resolution indeed ends here, and the user supplied new
8006f3e0bcdSDavid van Moolenbroek * data, check if writing is allowed.
8016f3e0bcdSDavid van Moolenbroek */
8026f3e0bcdSDavid van Moolenbroek if ((is_leaf || has_func) && newp != NULL) {
8036f3e0bcdSDavid van Moolenbroek if (!(rnode->rnode_flags & CTLFLAG_READWRITE))
8046f3e0bcdSDavid van Moolenbroek return EPERM;
8056f3e0bcdSDavid van Moolenbroek
8066f3e0bcdSDavid van Moolenbroek if (!(rnode->rnode_flags & CTLFLAG_ANYWRITE) &&
8076f3e0bcdSDavid van Moolenbroek !(call.call_flags & RMIB_FLAG_AUTH))
8086f3e0bcdSDavid van Moolenbroek return EPERM;
8096f3e0bcdSDavid van Moolenbroek }
8106f3e0bcdSDavid van Moolenbroek
8116f3e0bcdSDavid van Moolenbroek /* If this node has a handler function, let it do the work. */
8126f3e0bcdSDavid van Moolenbroek if (has_func)
8136f3e0bcdSDavid van Moolenbroek return rnode->rnode_func(&call, rnode, oldp, newp);
8146f3e0bcdSDavid van Moolenbroek
8156f3e0bcdSDavid van Moolenbroek /* For regular data leaf nodes, handle generic access. */
8166f3e0bcdSDavid van Moolenbroek if (is_leaf)
8176f3e0bcdSDavid van Moolenbroek return rmib_readwrite(&call, rnode, oldp, newp);
8186f3e0bcdSDavid van Moolenbroek
8196f3e0bcdSDavid van Moolenbroek /* No function and not a leaf? Descend further. */
8206f3e0bcdSDavid van Moolenbroek }
8216f3e0bcdSDavid van Moolenbroek
8226f3e0bcdSDavid van Moolenbroek /* If we get here, the name refers to a node array. */
8236f3e0bcdSDavid van Moolenbroek return EISDIR;
8246f3e0bcdSDavid van Moolenbroek }
8256f3e0bcdSDavid van Moolenbroek
8266f3e0bcdSDavid van Moolenbroek /*
8276f3e0bcdSDavid van Moolenbroek * Initialize the given node and recursively all its node-type children,
8286f3e0bcdSDavid van Moolenbroek * assigning the proper child length value to each of them.
8296f3e0bcdSDavid van Moolenbroek */
8306f3e0bcdSDavid van Moolenbroek static void
rmib_init(struct rmib_node * rparent)831*0f03189aSDavid van Moolenbroek rmib_init(struct rmib_node * rparent)
8326f3e0bcdSDavid van Moolenbroek {
833*0f03189aSDavid van Moolenbroek struct rmib_node *rnode;
834*0f03189aSDavid van Moolenbroek unsigned int i;
8356f3e0bcdSDavid van Moolenbroek
836*0f03189aSDavid van Moolenbroek for (i = 0; i < rparent->rnode_size; i++) {
837*0f03189aSDavid van Moolenbroek if (rparent->rnode_flags & CTLFLAG_SPARSE) {
838*0f03189aSDavid van Moolenbroek /* Indirect lists must be sorted ascending by ID. */
839*0f03189aSDavid van Moolenbroek assert(i == 0 || rparent->rnode_icptr[i].rindir_id >
840*0f03189aSDavid van Moolenbroek rparent->rnode_icptr[i - 1].rindir_id);
8416f3e0bcdSDavid van Moolenbroek
842*0f03189aSDavid van Moolenbroek rnode = rparent->rnode_icptr[i].rindir_node;
843*0f03189aSDavid van Moolenbroek } else {
844*0f03189aSDavid van Moolenbroek rnode = &rparent->rnode_cptr[i];
845*0f03189aSDavid van Moolenbroek
846*0f03189aSDavid van Moolenbroek if (rnode->rnode_flags == 0)
8476f3e0bcdSDavid van Moolenbroek continue;
848*0f03189aSDavid van Moolenbroek }
8496f3e0bcdSDavid van Moolenbroek
850*0f03189aSDavid van Moolenbroek rparent->rnode_clen++;
8516f3e0bcdSDavid van Moolenbroek
852*0f03189aSDavid van Moolenbroek if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE)
853*0f03189aSDavid van Moolenbroek rmib_init(rnode); /* recurse */
8546f3e0bcdSDavid van Moolenbroek }
8556f3e0bcdSDavid van Moolenbroek }
8566f3e0bcdSDavid van Moolenbroek
8576f3e0bcdSDavid van Moolenbroek /*
858241ebcaeSDavid van Moolenbroek * Request that the MIB service (re)mount the subtree identified by the given
859241ebcaeSDavid van Moolenbroek * identifier. This is a one-way request, so we never hear whether mounting
860241ebcaeSDavid van Moolenbroek * succeeds. There is not that much we can do if it fails anyway though.
861241ebcaeSDavid van Moolenbroek */
862241ebcaeSDavid van Moolenbroek static void
rmib_send_reg(int id)863241ebcaeSDavid van Moolenbroek rmib_send_reg(int id)
864241ebcaeSDavid van Moolenbroek {
865241ebcaeSDavid van Moolenbroek message m;
866241ebcaeSDavid van Moolenbroek int r;
867241ebcaeSDavid van Moolenbroek
868241ebcaeSDavid van Moolenbroek memset(&m, 0, sizeof(m));
869241ebcaeSDavid van Moolenbroek
870241ebcaeSDavid van Moolenbroek m.m_type = MIB_REGISTER;
871241ebcaeSDavid van Moolenbroek m.m_lsys_mib_register.root_id = id;
872241ebcaeSDavid van Moolenbroek m.m_lsys_mib_register.flags = SYSCTL_VERSION |
873*0f03189aSDavid van Moolenbroek (rnodes[id].rno_node->rnode_flags & ~CTLFLAG_SPARSE);
874241ebcaeSDavid van Moolenbroek m.m_lsys_mib_register.csize = rnodes[id].rno_node->rnode_size;
875241ebcaeSDavid van Moolenbroek m.m_lsys_mib_register.clen = rnodes[id].rno_node->rnode_clen;
876241ebcaeSDavid van Moolenbroek m.m_lsys_mib_register.miblen = rnodes[id].rno_namelen;
877241ebcaeSDavid van Moolenbroek memcpy(m.m_lsys_mib_register.mib, rnodes[id].rno_name,
878241ebcaeSDavid van Moolenbroek sizeof(rnodes[id].rno_name[0]) * rnodes[id].rno_namelen);
879241ebcaeSDavid van Moolenbroek
880241ebcaeSDavid van Moolenbroek if ((r = asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY)) != OK)
881241ebcaeSDavid van Moolenbroek panic("asynsend3 call to MIB service failed: %d", r);
882241ebcaeSDavid van Moolenbroek }
883241ebcaeSDavid van Moolenbroek
884241ebcaeSDavid van Moolenbroek /*
8856f3e0bcdSDavid van Moolenbroek * Register a MIB subtree. Initialize the subtree, add it to the local set,
8866f3e0bcdSDavid van Moolenbroek * and send a registration request for it to the MIB service.
8876f3e0bcdSDavid van Moolenbroek */
8886f3e0bcdSDavid van Moolenbroek int
rmib_register(const int * name,unsigned int namelen,struct rmib_node * rnode)8896f3e0bcdSDavid van Moolenbroek rmib_register(const int * name, unsigned int namelen, struct rmib_node * rnode)
8906f3e0bcdSDavid van Moolenbroek {
8916f3e0bcdSDavid van Moolenbroek unsigned int id, free_id;
8926f3e0bcdSDavid van Moolenbroek
8936f3e0bcdSDavid van Moolenbroek /* A few basic sanity checks. */
894241ebcaeSDavid van Moolenbroek if (namelen == 0 || namelen >= __arraycount(rnodes[0].rno_name))
8956f3e0bcdSDavid van Moolenbroek return EINVAL;
8966f3e0bcdSDavid van Moolenbroek if (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE)
8976f3e0bcdSDavid van Moolenbroek return EINVAL;
8986f3e0bcdSDavid van Moolenbroek
8996f3e0bcdSDavid van Moolenbroek /* Make sure this is a new subtree, and find a free slot for it. */
9006f3e0bcdSDavid van Moolenbroek for (id = free_id = 0; id < __arraycount(rnodes); id++) {
901241ebcaeSDavid van Moolenbroek if (rnodes[id].rno_node == rnode)
9026f3e0bcdSDavid van Moolenbroek return EEXIST;
903241ebcaeSDavid van Moolenbroek else if (rnodes[id].rno_node == NULL &&
904241ebcaeSDavid van Moolenbroek rnodes[free_id].rno_node != NULL)
9056f3e0bcdSDavid van Moolenbroek free_id = id;
9066f3e0bcdSDavid van Moolenbroek }
9076f3e0bcdSDavid van Moolenbroek
908241ebcaeSDavid van Moolenbroek if (rnodes[free_id].rno_node != NULL)
9096f3e0bcdSDavid van Moolenbroek return ENOMEM;
9106f3e0bcdSDavid van Moolenbroek
911241ebcaeSDavid van Moolenbroek rnodes[free_id].rno_node = rnode;
912241ebcaeSDavid van Moolenbroek rnodes[free_id].rno_namelen = namelen;
913241ebcaeSDavid van Moolenbroek memcpy(rnodes[free_id].rno_name, name, sizeof(name[0]) * namelen);
914241ebcaeSDavid van Moolenbroek
9156f3e0bcdSDavid van Moolenbroek /*
9166f3e0bcdSDavid van Moolenbroek * Initialize the entire subtree. This will also compute rnode_clen
9176f3e0bcdSDavid van Moolenbroek * for the given rnode, so do this before sending the message.
9186f3e0bcdSDavid van Moolenbroek */
9196f3e0bcdSDavid van Moolenbroek rmib_init(rnode);
9206f3e0bcdSDavid van Moolenbroek
921241ebcaeSDavid van Moolenbroek /* Send the registration request to the MIB service. */
922241ebcaeSDavid van Moolenbroek rmib_send_reg(free_id);
9236f3e0bcdSDavid van Moolenbroek
924241ebcaeSDavid van Moolenbroek return OK;
9256f3e0bcdSDavid van Moolenbroek }
9266f3e0bcdSDavid van Moolenbroek
9276f3e0bcdSDavid van Moolenbroek /*
9286f3e0bcdSDavid van Moolenbroek * Deregister a previously registered subtree, both internally and with the MIB
9296f3e0bcdSDavid van Moolenbroek * service. Return OK if the deregistration procedure has been started, in
9306f3e0bcdSDavid van Moolenbroek * which case the given subtree is guaranteed to no longer be accessed. Return
9316f3e0bcdSDavid van Moolenbroek * a negative error code on failure.
9326f3e0bcdSDavid van Moolenbroek */
9336f3e0bcdSDavid van Moolenbroek int
rmib_deregister(struct rmib_node * rnode)9346f3e0bcdSDavid van Moolenbroek rmib_deregister(struct rmib_node * rnode)
9356f3e0bcdSDavid van Moolenbroek {
9366f3e0bcdSDavid van Moolenbroek message m;
9376f3e0bcdSDavid van Moolenbroek unsigned int id;
9386f3e0bcdSDavid van Moolenbroek
9396f3e0bcdSDavid van Moolenbroek for (id = 0; id < __arraycount(rnodes); id++)
940241ebcaeSDavid van Moolenbroek if (rnodes[id].rno_node == rnode)
9416f3e0bcdSDavid van Moolenbroek break;
9426f3e0bcdSDavid van Moolenbroek
9436f3e0bcdSDavid van Moolenbroek if (id == __arraycount(rnodes))
9446f3e0bcdSDavid van Moolenbroek return ENOENT;
9456f3e0bcdSDavid van Moolenbroek
946241ebcaeSDavid van Moolenbroek rnodes[id].rno_node = NULL;
9476f3e0bcdSDavid van Moolenbroek
9486f3e0bcdSDavid van Moolenbroek /*
9496f3e0bcdSDavid van Moolenbroek * Request that the MIB service unmount the subtree. We completely
9506f3e0bcdSDavid van Moolenbroek * ignore failure here, because the caller would not be able to do
9516f3e0bcdSDavid van Moolenbroek * anything about it anyway. We may also still receive sysctl call
9526f3e0bcdSDavid van Moolenbroek * requests for the node we just deregistered, but this is caught
9536f3e0bcdSDavid van Moolenbroek * during request processing. Reuse of the rnodes[] slot could be a
9546f3e0bcdSDavid van Moolenbroek * potential problem though. We could use sequence numbers in the root
9556f3e0bcdSDavid van Moolenbroek * identifiers to resolve that problem if it ever occurs in reality.
9566f3e0bcdSDavid van Moolenbroek */
9576f3e0bcdSDavid van Moolenbroek memset(&m, 0, sizeof(m));
9586f3e0bcdSDavid van Moolenbroek
9596f3e0bcdSDavid van Moolenbroek m.m_type = MIB_DEREGISTER;
9606f3e0bcdSDavid van Moolenbroek m.m_lsys_mib_register.root_id = id;
9616f3e0bcdSDavid van Moolenbroek
9626f3e0bcdSDavid van Moolenbroek (void)asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY);
9636f3e0bcdSDavid van Moolenbroek
9646f3e0bcdSDavid van Moolenbroek return OK;
9656f3e0bcdSDavid van Moolenbroek }
9666f3e0bcdSDavid van Moolenbroek
9676f3e0bcdSDavid van Moolenbroek /*
968241ebcaeSDavid van Moolenbroek * Reregister all previously registered subtrees. This routine should be
969241ebcaeSDavid van Moolenbroek * called after the main program has determined that the MIB service has been
970241ebcaeSDavid van Moolenbroek * restarted.
971241ebcaeSDavid van Moolenbroek */
972241ebcaeSDavid van Moolenbroek void
rmib_reregister(void)973241ebcaeSDavid van Moolenbroek rmib_reregister(void)
974241ebcaeSDavid van Moolenbroek {
975241ebcaeSDavid van Moolenbroek unsigned int id;
976241ebcaeSDavid van Moolenbroek
977241ebcaeSDavid van Moolenbroek for (id = 0; id < __arraycount(rnodes); id++)
978241ebcaeSDavid van Moolenbroek if (rnodes[id].rno_node != NULL)
979241ebcaeSDavid van Moolenbroek rmib_send_reg(id);
980241ebcaeSDavid van Moolenbroek }
981241ebcaeSDavid van Moolenbroek
982241ebcaeSDavid van Moolenbroek /*
983241ebcaeSDavid van Moolenbroek * Reset all registrations, without involving MIB communication. This routine
984241ebcaeSDavid van Moolenbroek * exists for testing purposes only, and may disappear in the future.
9856f3e0bcdSDavid van Moolenbroek */
9866f3e0bcdSDavid van Moolenbroek void
rmib_reset(void)9876f3e0bcdSDavid van Moolenbroek rmib_reset(void)
9886f3e0bcdSDavid van Moolenbroek {
9896f3e0bcdSDavid van Moolenbroek
9906f3e0bcdSDavid van Moolenbroek memset(rnodes, 0, sizeof(rnodes));
9916f3e0bcdSDavid van Moolenbroek }
9926f3e0bcdSDavid van Moolenbroek
9936f3e0bcdSDavid van Moolenbroek /*
9946f3e0bcdSDavid van Moolenbroek * Process a request from the MIB service for information about the root node
9956f3e0bcdSDavid van Moolenbroek * of a subtree, specifically its name and description.
9966f3e0bcdSDavid van Moolenbroek */
9976f3e0bcdSDavid van Moolenbroek static int
rmib_info(const message * m_in)9986f3e0bcdSDavid van Moolenbroek rmib_info(const message * m_in)
9996f3e0bcdSDavid van Moolenbroek {
10006f3e0bcdSDavid van Moolenbroek struct rmib_node *rnode;
10016f3e0bcdSDavid van Moolenbroek unsigned int id;
10026f3e0bcdSDavid van Moolenbroek const char *ptr;
10036f3e0bcdSDavid van Moolenbroek size_t size;
10046f3e0bcdSDavid van Moolenbroek int r;
10056f3e0bcdSDavid van Moolenbroek
10066f3e0bcdSDavid van Moolenbroek id = m_in->m_mib_lsys_info.root_id;
1007241ebcaeSDavid van Moolenbroek if (id >= __arraycount(rnodes) || rnodes[id].rno_node == NULL)
10086f3e0bcdSDavid van Moolenbroek return ENOENT;
1009241ebcaeSDavid van Moolenbroek rnode = rnodes[id].rno_node;
10106f3e0bcdSDavid van Moolenbroek
10116f3e0bcdSDavid van Moolenbroek /* The name must fit. If it does not, the service writer messed up. */
10126f3e0bcdSDavid van Moolenbroek size = strlen(rnode->rnode_name) + 1;
10136f3e0bcdSDavid van Moolenbroek if (size > m_in->m_mib_lsys_info.name_size)
10146f3e0bcdSDavid van Moolenbroek return ENAMETOOLONG;
10156f3e0bcdSDavid van Moolenbroek
10166f3e0bcdSDavid van Moolenbroek r = sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.name_grant, 0,
10176f3e0bcdSDavid van Moolenbroek (vir_bytes)rnode->rnode_name, size);
10186f3e0bcdSDavid van Moolenbroek if (r != OK)
10196f3e0bcdSDavid van Moolenbroek return r;
10206f3e0bcdSDavid van Moolenbroek
10216f3e0bcdSDavid van Moolenbroek /* If there is no (optional) description, copy out an empty string. */
10226f3e0bcdSDavid van Moolenbroek ptr = (rnode->rnode_desc != NULL) ? rnode->rnode_desc : "";
10236f3e0bcdSDavid van Moolenbroek size = strlen(ptr) + 1;
10246f3e0bcdSDavid van Moolenbroek
10256f3e0bcdSDavid van Moolenbroek if (size > m_in->m_mib_lsys_info.desc_size)
10266f3e0bcdSDavid van Moolenbroek size = m_in->m_mib_lsys_info.desc_size;
10276f3e0bcdSDavid van Moolenbroek
10286f3e0bcdSDavid van Moolenbroek return sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.desc_grant,
10296f3e0bcdSDavid van Moolenbroek 0, (vir_bytes)ptr, size);
10306f3e0bcdSDavid van Moolenbroek }
10316f3e0bcdSDavid van Moolenbroek
10326f3e0bcdSDavid van Moolenbroek /*
10336f3e0bcdSDavid van Moolenbroek * Process a request from the MIB service. The given message should originate
10346f3e0bcdSDavid van Moolenbroek * from the MIB service and have one of the COMMON_MIB_ requests as type.
10356f3e0bcdSDavid van Moolenbroek */
10366f3e0bcdSDavid van Moolenbroek void
rmib_process(const message * m_in,int ipc_status)10376f3e0bcdSDavid van Moolenbroek rmib_process(const message * m_in, int ipc_status)
10386f3e0bcdSDavid van Moolenbroek {
10396f3e0bcdSDavid van Moolenbroek message m_out;
10406f3e0bcdSDavid van Moolenbroek uint32_t req_id;
10416f3e0bcdSDavid van Moolenbroek ssize_t r;
10426f3e0bcdSDavid van Moolenbroek
10436f3e0bcdSDavid van Moolenbroek /* Only the MIB service may issue these requests. */
10446f3e0bcdSDavid van Moolenbroek if (m_in->m_source != MIB_PROC_NR)
10456f3e0bcdSDavid van Moolenbroek return;
10466f3e0bcdSDavid van Moolenbroek
10476f3e0bcdSDavid van Moolenbroek /* Process the actual request. */
10486f3e0bcdSDavid van Moolenbroek switch (m_in->m_type) {
10496f3e0bcdSDavid van Moolenbroek case COMMON_MIB_INFO:
10506f3e0bcdSDavid van Moolenbroek req_id = m_in->m_mib_lsys_info.req_id;
10516f3e0bcdSDavid van Moolenbroek
10526f3e0bcdSDavid van Moolenbroek r = rmib_info(m_in);
10536f3e0bcdSDavid van Moolenbroek
10546f3e0bcdSDavid van Moolenbroek break;
10556f3e0bcdSDavid van Moolenbroek
10566f3e0bcdSDavid van Moolenbroek case COMMON_MIB_CALL:
10576f3e0bcdSDavid van Moolenbroek req_id = m_in->m_mib_lsys_call.req_id;
10586f3e0bcdSDavid van Moolenbroek
10596f3e0bcdSDavid van Moolenbroek r = rmib_call(m_in);
10606f3e0bcdSDavid van Moolenbroek
10616f3e0bcdSDavid van Moolenbroek break;
10626f3e0bcdSDavid van Moolenbroek
10636f3e0bcdSDavid van Moolenbroek default:
10646f3e0bcdSDavid van Moolenbroek /*
10656f3e0bcdSDavid van Moolenbroek * HACK: assume that for all current and future requests, the
10666f3e0bcdSDavid van Moolenbroek * request ID field is in the same place. We could create a
10676f3e0bcdSDavid van Moolenbroek * m_mib_lsys_unknown pseudo message type for this, but, eh.
10686f3e0bcdSDavid van Moolenbroek */
10696f3e0bcdSDavid van Moolenbroek req_id = m_in->m_mib_lsys_info.req_id;
10706f3e0bcdSDavid van Moolenbroek
10716f3e0bcdSDavid van Moolenbroek r = ENOSYS;
10726f3e0bcdSDavid van Moolenbroek }
10736f3e0bcdSDavid van Moolenbroek
10746f3e0bcdSDavid van Moolenbroek /* Construct and send a reply message to the MIB service. */
10756f3e0bcdSDavid van Moolenbroek memset(&m_out, 0, sizeof(m_out));
10766f3e0bcdSDavid van Moolenbroek
10776f3e0bcdSDavid van Moolenbroek m_out.m_type = COMMON_MIB_REPLY;
10786f3e0bcdSDavid van Moolenbroek m_out.m_lsys_mib_reply.req_id = req_id;
10796f3e0bcdSDavid van Moolenbroek m_out.m_lsys_mib_reply.status = r;
10806f3e0bcdSDavid van Moolenbroek
10816f3e0bcdSDavid van Moolenbroek if (IPC_STATUS_CALL(ipc_status) == SENDREC)
10826f3e0bcdSDavid van Moolenbroek r = ipc_sendnb(m_in->m_source, &m_out);
10836f3e0bcdSDavid van Moolenbroek else
10846f3e0bcdSDavid van Moolenbroek r = asynsend3(m_in->m_source, &m_out, AMF_NOREPLY);
10856f3e0bcdSDavid van Moolenbroek
10866f3e0bcdSDavid van Moolenbroek if (r != OK)
10871ccb488dSDavid van Moolenbroek printf("lsys:rmib: unable to send reply to %d: %zd\n",
10886f3e0bcdSDavid van Moolenbroek m_in->m_source, r);
10896f3e0bcdSDavid van Moolenbroek }
1090