Index: sys/kern/kern_sysctl.c =================================================================== --- sys/kern/kern_sysctl.c +++ sys/kern/kern_sysctl.c @@ -126,7 +126,11 @@ int recurse); static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t); static int sysctl_new_kernel(struct sysctl_req *, void *, size_t); +static bool sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, + u_int namelen, int *next, int *len, int level, + bool honor_skip); + static struct sysctl_oid * sysctl_find_oidname(const char *name, struct sysctl_oid_list *list) { @@ -1101,126 +1105,148 @@ CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_name, ""); /* - * Walk the sysctl subtree at lsp until we find the given name, - * and return the next name in order by oid_number. + * Recursively tries to find the next node for @name and @namelen. + * + * Returns true if the desired next node was found, filling + * @next and @len. */ -static int -sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, +static bool +sysctl_sysctl_next_node(struct sysctl_oid *oidp, int *name, u_int *namelen, int *next, int *len, int level, bool honor_skip) { - struct sysctl_oid *oidp; + struct sysctl_oid_list *lsp; - SYSCTL_ASSERT_LOCKED(); - *len = level; - SLIST_FOREACH(oidp, lsp, oid_link) { - *next = oidp->oid_number; + *next = oidp->oid_number; - if ((oidp->oid_kind & CTLFLAG_DORMANT) != 0) - continue; + if ((oidp->oid_kind & CTLFLAG_DORMANT) != 0) + return (false); - if (honor_skip && (oidp->oid_kind & CTLFLAG_SKIP) != 0) - continue; + if (honor_skip && (oidp->oid_kind & CTLFLAG_SKIP) != 0) + return (false); - if (namelen == 0) { - /* - * We have reached a node with a full name match and are - * looking for the next oid in its children. - * - * For CTL_SYSCTL_NEXTNOSKIP we are done. - * - * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it - * has a handler) and move on to the children. - */ - if (!honor_skip) - return (0); - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) - return (0); - if (oidp->oid_handler) - return (0); - lsp = SYSCTL_CHILDREN(oidp); - if (!sysctl_sysctl_next_ls(lsp, NULL, 0, next + 1, len, - level + 1, honor_skip)) - return (0); - /* - * There were no useable children in this node. - * Continue searching for the next oid at this level. - */ - goto emptynode; - } - + if (*namelen == 0) { /* - * No match yet. Continue seeking the given name. + * We have reached a node with a full name match and are + * looking for the next oid in its children. * - * We are iterating in order by oid_number, so skip oids lower - * than the one we are looking for. + * For CTL_SYSCTL_NEXTNOSKIP we are done. * - * When the current oid_number is higher than the one we seek, - * that means we have reached the next oid in the sequence and - * should return it. + * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it + * has a handler) and move on to the children. + */ + if (!honor_skip) + return (true); + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + return (true); + /* If node does not have an iterator, treat it as leaf */ + if (oidp->oid_handler) + return (true); + lsp = SYSCTL_CHILDREN(oidp); + if (sysctl_sysctl_next_ls(lsp, NULL, 0, next + 1, len, + level + 1, honor_skip)) + return (true); + /* + * There were no useable children in this node. + * Continue searching for the next oid at this level. + */ + return (false); + } + + /* + * No match yet. Continue seeking the given name. + * + * We are iterating in order by oid_number, so skip oids lower + * than the one we are looking for. + * + * When the current oid_number is higher than the one we seek, + * that means we have reached the next oid in the sequence and + * should return it. + * + * If the oid_number matches the name at this level then we + * have to find a node to continue searching at the next level. + */ + if (oidp->oid_number < *name) + return (false); + if (oidp->oid_number > *name) { + /* + * We have reached the next oid. * - * If the oid_number matches the name at this level then we - * have to find a node to continue searching at the next level. + * For CTL_SYSCTL_NEXTNOSKIP we are done. + * + * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it + * has a handler) and move on to the children. */ - if (oidp->oid_number < *name) - continue; - if (oidp->oid_number > *name) { - /* - * We have reached the next oid. - * - * For CTL_SYSCTL_NEXTNOSKIP we are done. - * - * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it - * has a handler) and move on to the children. - */ - if (!honor_skip) - return (0); - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) - return (0); - if (oidp->oid_handler) - return (0); - lsp = SYSCTL_CHILDREN(oidp); - if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1, - next + 1, len, level + 1, honor_skip)) - return (0); - goto next; - } + if (!honor_skip) + return (true); if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) - continue; + return (true); + /* If node does not have an iterator, treat it as leaf */ if (oidp->oid_handler) - continue; + return (true); lsp = SYSCTL_CHILDREN(oidp); - if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1, + if (sysctl_sysctl_next_ls(lsp, name + 1, *namelen - 1, next + 1, len, level + 1, honor_skip)) - return (0); - next: - /* - * There were no useable children in this node. - * Continue searching for the next oid at the root level. - */ - namelen = 1; - emptynode: - /* Reset len in case a failed recursive call changed it. */ - *len = level; + return (true); + goto next; } - return (ENOENT); + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + return (false); + if (oidp->oid_handler) + return (false); + lsp = SYSCTL_CHILDREN(oidp); + if (sysctl_sysctl_next_ls(lsp, name + 1, *namelen - 1, + next + 1, len, level + 1, honor_skip)) + return (true); +next: + /* + * There were no useable children in this node. + * Continue searching for the next oid at the root level. + */ + *namelen = 1; + + return (false); } +/* + * Recursively walk the sysctl subtree at lsp until we find the given name. + * Returns true and fills in next oid data in @next and @len if oid is found. + */ +static bool +sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, + int *next, int *len, int level, bool honor_skip) +{ + struct sysctl_oid *oidp; + + SYSCTL_ASSERT_LOCKED(); + SLIST_FOREACH(oidp, lsp, oid_link) { + if (!sysctl_sysctl_next_node(oidp, name, &namelen, + next, len, level, honor_skip)) + continue; + if (level > *len) + *len = level; + return (true); + } + return (false); +} + static int sysctl_sysctl_next(SYSCTL_HANDLER_ARGS) { int *name = (int *) arg1; u_int namelen = arg2; int len, error; + bool success; struct sysctl_oid_list *lsp = &sysctl__children; struct rm_priotracker tracker; int next[CTL_MAXNAME]; + len = 0; SYSCTL_RLOCK(&tracker); - error = sysctl_sysctl_next_ls(lsp, name, namelen, next, &len, 1, + success = sysctl_sysctl_next_ls(lsp, name, namelen, next, &len, 1, oidp->oid_number == CTL_SYSCTL_NEXT); SYSCTL_RUNLOCK(&tracker); - if (error) - return (error); + if (!success) + return (ENOENT); error = SYSCTL_OUT(req, next, len * sizeof (int)); return (error); }