xref: /minix3/minix/lib/libsys/rmib.c (revision 0f03189a6a8bafe2a370ea990cf2cbb19da81b70)
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