diff --git a/minix/include/minix/rmib.h b/minix/include/minix/rmib.h index a7a59125d..35ea2c079 100644 --- a/minix/include/minix/rmib.h +++ b/minix/include/minix/rmib.h @@ -45,6 +45,22 @@ struct rmib_newp; typedef ssize_t (*rmib_func_ptr)(struct rmib_call *, struct rmib_node *, struct rmib_oldp *, struct rmib_newp *); +/* + * Indirect node, used for sparse nodes. Sparse nodes are node-type nodes with + * the CTLFLAG_SPARSE flag set. A sparse node points not to an array of child + * nodes (using rnode_cptr), but to a array of {id,child pointer} elements + * (using rnode_icptr). At the cost of O(n) lookups, sparse nodes save memory. + * Currently for presentation reasons only, indirect lists must be sorted + * ascending by node identifiers. They may also not have ID duplicates, may not + * have NULL node pointers, and may not point to nodes with zero flags fields. + */ +#define CTLFLAG_SPARSE CTLFLAG_ROOT /* overloaded NetBSD flag */ + +struct rmib_indir { + unsigned int rindir_id; /* node identifier */ + struct rmib_node *rindir_node; /* pointer to actual node */ +}; + /* * The central structure for remote MIB nodes. This is essentially a somewhat * cut-down version of the node structure used within the MIB service. See the @@ -64,6 +80,7 @@ struct rmib_node { union pxfer_rnode_ptr_u { void *rpu_data; /* struct or string data pointer */ struct rmib_node *rpu_cptr; /* child node array */ + struct rmib_indir *rpu_icptr; /* indirect child node array */ } rnode_ptr_u; rmib_func_ptr rnode_func; /* handler function */ const char *rnode_name; /* node name string */ @@ -75,6 +92,7 @@ struct rmib_node { #define rnode_clen rnode_val_u.rvu_clen #define rnode_data rnode_ptr_u.rpu_data #define rnode_cptr rnode_ptr_u.rpu_cptr +#define rnode_icptr rnode_ptr_u.rpu_icptr /* Various macros to initialize nodes at compile time. */ #define RMIB_NODE(f,t,n,d) { \ @@ -85,6 +103,14 @@ struct rmib_node { .rnode_name = n, \ .rnode_desc = d \ } +#define RMIB_SNODE(f,t,n,d) { \ + .rnode_flags = CTLTYPE_NODE | CTLFLAG_READONLY | \ + CTLFLAG_PERMANENT | CTLFLAG_SPARSE | f, \ + .rnode_size = 0, \ + .rnode_icptr = t, \ + .rnode_name = n, \ + .rnode_desc = d \ +} #define RMIB_FUNC(f,s,fp,n,d) { \ .rnode_flags = CTLFLAG_PERMANENT | f, \ .rnode_size = s, \ diff --git a/minix/lib/libsys/rmib.c b/minix/lib/libsys/rmib.c index ac581b981..e4b686389 100644 --- a/minix/lib/libsys/rmib.c +++ b/minix/lib/libsys/rmib.c @@ -5,6 +5,8 @@ * though the copy here operates on slightly different data structures in order * to keep the implementation more lightweight. For clarification on many * aspects of the source code here, see the source code of the MIB service. + * One unique feature here is support for sparse nodes, which is needed for + * net.inet/inet6 as those are using subtrees with protocol-based identifiers. * * There is no way for this module to get to know about MIB service deaths * without possibly interfering with the main code of the service this module @@ -204,10 +206,12 @@ rmib_copyout_node(struct rmib_call * call, struct rmib_oldp * oldp, memset(&scn, 0, sizeof(scn)); /* - * The RMIB implementation does not overload flags, so it also need not + * We use CTLFLAG_SPARSE internally only. NetBSD uses these flags for + * different purposes. Either way, do not expose it to userland. * hide any of them from the user. */ - scn.sysctl_flags = SYSCTL_VERSION | rnode->rnode_flags; + scn.sysctl_flags = SYSCTL_VERSION | + (rnode->rnode_flags & ~CTLFLAG_SPARSE); scn.sysctl_num = id; strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name)); scn.sysctl_ver = call->call_rootver; @@ -266,7 +270,7 @@ rmib_query(struct rmib_call * call, struct rmib_node * rparent, { struct sysctlnode scn; struct rmib_node *rnode; - unsigned int id; + unsigned int i, id; ssize_t r, off; /* If the user passed in version numbers, check them. */ @@ -290,11 +294,17 @@ rmib_query(struct rmib_call * call, struct rmib_node * rparent, /* Enumerate the child nodes of the given parent node. */ off = 0; - for (id = 0; id < rparent->rnode_size; id++) { - rnode = &rparent->rnode_cptr[id]; + for (i = 0; i < rparent->rnode_size; i++) { + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + id = rparent->rnode_icptr[i].rindir_id; + rnode = rparent->rnode_icptr[i].rindir_node; + } else { + id = i; + rnode = &rparent->rnode_cptr[i]; - if (rnode->rnode_flags == 0) - continue; + if (rnode->rnode_flags == 0) + continue; + } if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0) return r; @@ -369,6 +379,34 @@ rmib_copyout_desc(struct rmib_call * call, struct rmib_oldp * oldp, return roundup2(size, sizeof(int32_t)); } +/* + * Look up a child node given a parent node and a child node identifier. + * Return a pointer to the child node if found, or NULL otherwise. The lookup + * procedure differs based on whether the parent node is sparse or not. + */ +static struct rmib_node * +rmib_lookup(struct rmib_node * rparent, unsigned int id) +{ + struct rmib_node *rnode; + struct rmib_indir *rindir; + unsigned int i; + + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + rindir = rparent->rnode_icptr; + for (i = 0; i < rparent->rnode_size; i++, rindir++) + if (rindir->rindir_id == id) + return rindir->rindir_node; + } else { + if (id >= rparent->rnode_size) + return NULL; + rnode = &rparent->rnode_cptr[id]; + if (rnode->rnode_flags != 0) + return rnode; + } + + return NULL; +} + /* * Retrieve node descriptions in bulk, or retrieve a particular node's * description. @@ -379,7 +417,7 @@ rmib_describe(struct rmib_call * call, struct rmib_node * rparent, { struct sysctlnode scn; struct rmib_node *rnode; - unsigned int id; + unsigned int i, id; ssize_t r, off; if (newp != NULL) { @@ -390,10 +428,7 @@ rmib_describe(struct rmib_call * call, struct rmib_node * rparent, return EINVAL; /* Locate the child node. */ - if ((unsigned int)scn.sysctl_num >= rparent->rnode_size) - return ENOENT; - rnode = &rparent->rnode_cptr[scn.sysctl_num]; - if (rnode->rnode_flags == 0) + if ((rnode = rmib_lookup(rparent, scn.sysctl_num)) == NULL) return ENOENT; /* Descriptions of private nodes are considered private too. */ @@ -419,11 +454,17 @@ rmib_describe(struct rmib_call * call, struct rmib_node * rparent, /* Describe the child nodes of the given parent node. */ off = 0; - for (id = 0; id < rparent->rnode_size; id++) { - rnode = &rparent->rnode_cptr[id]; + for (i = 0; i < rparent->rnode_size; i++) { + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + id = rparent->rnode_icptr[i].rindir_id; + rnode = rparent->rnode_icptr[i].rindir_node; + } else { + id = i; + rnode = &rparent->rnode_cptr[i]; - if (rnode->rnode_flags == 0) - continue; + if (rnode->rnode_flags == 0) + continue; + } if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0) return r; @@ -732,10 +773,7 @@ rmib_call(const message * m_in) } /* Locate the child node. */ - if ((unsigned int)id >= rparent->rnode_size) - return ENOENT; - rnode = &rparent->rnode_cptr[id]; - if (rnode->rnode_flags == 0) + if ((rnode = rmib_lookup(rparent, id)) == NULL) return ENOENT; /* Check if access is permitted at this level. */ @@ -790,21 +828,29 @@ rmib_call(const message * m_in) * assigning the proper child length value to each of them. */ static void -rmib_init(struct rmib_node * rnode) +rmib_init(struct rmib_node * rparent) { - struct rmib_node *rchild; - unsigned int id; + struct rmib_node *rnode; + unsigned int i; - rchild = rnode->rnode_cptr; + for (i = 0; i < rparent->rnode_size; i++) { + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + /* Indirect lists must be sorted ascending by ID. */ + assert(i == 0 || rparent->rnode_icptr[i].rindir_id > + rparent->rnode_icptr[i - 1].rindir_id); - for (id = 0; id < rnode->rnode_size; id++, rchild++) { - if (rchild->rnode_flags == 0) - continue; + rnode = rparent->rnode_icptr[i].rindir_node; + } else { + rnode = &rparent->rnode_cptr[i]; - rnode->rnode_clen++; + if (rnode->rnode_flags == 0) + continue; + } - if (SYSCTL_TYPE(rchild->rnode_flags) == CTLTYPE_NODE) - rmib_init(rchild); /* recurse */ + rparent->rnode_clen++; + + if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) + rmib_init(rnode); /* recurse */ } } @@ -824,7 +870,7 @@ rmib_send_reg(int id) m.m_type = MIB_REGISTER; m.m_lsys_mib_register.root_id = id; m.m_lsys_mib_register.flags = SYSCTL_VERSION | - rnodes[id].rno_node->rnode_flags; + (rnodes[id].rno_node->rnode_flags & ~CTLFLAG_SPARSE); m.m_lsys_mib_register.csize = rnodes[id].rno_node->rnode_size; m.m_lsys_mib_register.clen = rnodes[id].rno_node->rnode_clen; m.m_lsys_mib_register.miblen = rnodes[id].rno_namelen;