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