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