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