New sysctl(8) binary. Performs auto-discovery and can add/remove
nodes from the tree. Never needs to be recompiled again.
This commit is contained in:
parent
6fc962bc06
commit
c915b3168c
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.14 2002/11/30 03:10:54 lukem Exp $
|
||||
# $NetBSD: Makefile,v 1.15 2003/12/04 19:49:39 atatat Exp $
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/6/93
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
@ -6,7 +6,6 @@
|
|||
PROG= sysctl
|
||||
MAN= sysctl.8
|
||||
|
||||
CPPFLAGS+=-DINET6 -DIPSEC -I${.CURDIR} -I${NETBSDSRCDIR}/sys
|
||||
#CPPFLAGS+=-DTCP6
|
||||
SRCS= sysctl.c sysctlutil.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
|
2886
sbin/sysctl/sysctl.c
2886
sbin/sysctl/sysctl.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,518 @@
|
|||
/* $NetBSD: sysctlutil.c,v 1.1 2003/12/04 19:49:39 atatat Exp $ */
|
||||
|
||||
#include <sys/param.h>
|
||||
#define __USE_NEW_SYSCTL
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _REENTRANT
|
||||
#include "reentrant.h"
|
||||
#endif /* _REENTRANT */
|
||||
|
||||
/*
|
||||
* the place where we attach stuff we learn on the fly, not
|
||||
* necessarily used.
|
||||
*/
|
||||
static struct sysctlnode sysctl_mibroot = {
|
||||
#if defined(lint)
|
||||
/*
|
||||
* lint doesn't like my initializers
|
||||
*/
|
||||
0
|
||||
#else /* !lint */
|
||||
.sysctl_flags = SYSCTL_ROOT|SYSCTL_PERMANENT|SYSCTL_TYPE(CTLTYPE_NODE),
|
||||
.sysctl_size = sizeof(struct sysctlnode),
|
||||
.sysctl_name = "(root)",
|
||||
#endif /* !lint */
|
||||
};
|
||||
|
||||
/*
|
||||
* routines to handle learning and cleanup
|
||||
*/
|
||||
static int compar(const void *, const void *);
|
||||
int learn_tree(int *, u_int, struct sysctlnode *);
|
||||
static void free_children(struct sysctlnode *);
|
||||
static void relearnhead(void);
|
||||
|
||||
/*
|
||||
* for ordering nodes -- a query may or may not be given them in
|
||||
* numeric order
|
||||
*/
|
||||
static int
|
||||
compar(const void *a, const void *b)
|
||||
{
|
||||
|
||||
return (((const struct sysctlnode *)a)->sysctl_num -
|
||||
((const struct sysctlnode *)b)->sysctl_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* sucks in the children at a given level and attaches it to the tree.
|
||||
*/
|
||||
int
|
||||
learn_tree(int *name, u_int namelen, struct sysctlnode *pnode)
|
||||
{
|
||||
int rc;
|
||||
size_t sz;
|
||||
|
||||
if (pnode == NULL)
|
||||
pnode = &sysctl_mibroot;
|
||||
if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (pnode->sysctl_child != NULL)
|
||||
return (0);
|
||||
|
||||
if (pnode->sysctl_clen == 0)
|
||||
sz = SYSCTL_DEFSIZE * sizeof(struct sysctlnode);
|
||||
else
|
||||
sz = pnode->sysctl_clen * sizeof(struct sysctlnode);
|
||||
pnode->sysctl_child = malloc(sz);
|
||||
if (pnode->sysctl_child == NULL)
|
||||
return (-1);
|
||||
|
||||
name[namelen] = CTL_QUERY;
|
||||
pnode->sysctl_clen = 0;
|
||||
pnode->sysctl_csize = 0;
|
||||
rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz, NULL, 0);
|
||||
if (sz == 0)
|
||||
return (rc);
|
||||
if (rc) {
|
||||
free(pnode->sysctl_child);
|
||||
pnode->sysctl_child = NULL;
|
||||
if (errno != ENOMEM)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
if (pnode->sysctl_child == NULL) {
|
||||
pnode->sysctl_child = malloc(sz);
|
||||
if (pnode->sysctl_child == NULL)
|
||||
return (-1);
|
||||
|
||||
rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz, NULL, 0);
|
||||
if (rc) {
|
||||
free(pnode->sysctl_child);
|
||||
pnode->sysctl_child = NULL;
|
||||
return (rc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* how many did we get?
|
||||
*/
|
||||
pnode->sysctl_clen = sz / sizeof(struct sysctlnode);
|
||||
pnode->sysctl_csize = sz / sizeof(struct sysctlnode);
|
||||
|
||||
/*
|
||||
* you know, the kernel doesn't really keep them in any
|
||||
* particular order...just like entries in a directory
|
||||
*/
|
||||
qsort(pnode->sysctl_child, pnode->sysctl_clen,
|
||||
sizeof(struct sysctlnode), compar);
|
||||
|
||||
/*
|
||||
* rearrange parent<->child linkage
|
||||
*/
|
||||
for (rc = 0; rc < pnode->sysctl_clen; rc++) {
|
||||
pnode->sysctl_child[rc].sysctl_parent = pnode;
|
||||
if (SYSCTL_TYPE(pnode->sysctl_child[rc].sysctl_flags) ==
|
||||
CTLTYPE_NODE) {
|
||||
/*
|
||||
* these nodes may have children, but we
|
||||
* haven't discovered that yet.
|
||||
*/
|
||||
pnode->sysctl_child[rc].sysctl_child = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* recursively nukes a branch or an entire tree from the given node
|
||||
*/
|
||||
static void
|
||||
free_children(struct sysctlnode *rnode)
|
||||
{
|
||||
struct sysctlnode *node;
|
||||
|
||||
if (rnode == NULL ||
|
||||
SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE ||
|
||||
rnode->sysctl_child == NULL)
|
||||
return;
|
||||
|
||||
for (node = rnode->sysctl_child;
|
||||
node < &rnode->sysctl_child[rnode->sysctl_clen];
|
||||
node++) {
|
||||
free_children(node);
|
||||
}
|
||||
free(rnode->sysctl_child);
|
||||
rnode->sysctl_child = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* verifies that the head of the tree in the kernel is the same as the
|
||||
* head of the tree we already got, integrating new stuff and removing
|
||||
* old stuff, if it's not.
|
||||
*/
|
||||
static void
|
||||
relearnhead(void)
|
||||
{
|
||||
struct sysctlnode *h, *i, *o;
|
||||
size_t si, so;
|
||||
int rc, name, nlen, olen, ni, oi, t;
|
||||
|
||||
/*
|
||||
* if there's nothing there, there's no need to expend any
|
||||
* effort
|
||||
*/
|
||||
if (sysctl_mibroot.sysctl_child == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* attempt to pull out the head of the tree, starting with the
|
||||
* size we have now, and looping if we need more (or less)
|
||||
* space
|
||||
*/
|
||||
si = 0;
|
||||
so = sysctl_mibroot.sysctl_clen * sizeof(struct sysctlnode);
|
||||
name = CTL_QUERY;
|
||||
do {
|
||||
si = so;
|
||||
h = malloc(si);
|
||||
rc = sysctl(&name, 1, h, &so, NULL, 0);
|
||||
if (rc == -1 && errno != ENOMEM)
|
||||
return;
|
||||
if (si < so)
|
||||
free(h);
|
||||
} while (si < so);
|
||||
|
||||
/*
|
||||
* order the new copy of the head
|
||||
*/
|
||||
nlen = so / sizeof(struct sysctlnode);
|
||||
qsort(h, (size_t)nlen, sizeof(struct sysctlnode), compar);
|
||||
|
||||
/*
|
||||
* verify that everything is the same. if it is, we don't
|
||||
* need to do any more work here.
|
||||
*/
|
||||
olen = sysctl_mibroot.sysctl_clen;
|
||||
rc = (nlen == olen) ? 0 : 1;
|
||||
o = sysctl_mibroot.sysctl_child;
|
||||
for (ni = 0; rc == 0 && ni < nlen; ni++) {
|
||||
if (h[ni].sysctl_num != o[ni].sysctl_num ||
|
||||
h[ni].sysctl_ver != o[ni].sysctl_ver)
|
||||
rc = 1;
|
||||
}
|
||||
if (rc == 0) {
|
||||
free(h);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* something changed. h will become the new head, and we need
|
||||
* pull over any subtrees we already have if they're the same
|
||||
* version.
|
||||
*/
|
||||
i = h;
|
||||
ni = oi = 0;
|
||||
while (ni < nlen && oi < olen) {
|
||||
/*
|
||||
* something was inserted or deleted
|
||||
*/
|
||||
if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE)
|
||||
i[ni].sysctl_child = NULL;
|
||||
if (i[ni].sysctl_num != o[oi].sysctl_num) {
|
||||
if (i[ni].sysctl_num < o[oi].sysctl_num) {
|
||||
ni++;
|
||||
}
|
||||
else {
|
||||
free_children(&o[oi]);
|
||||
oi++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* same number, but different version, so throw away
|
||||
* any accumulated children
|
||||
*/
|
||||
if (i[ni].sysctl_ver != o[oi].sysctl_ver)
|
||||
free_children(&o[oi]);
|
||||
|
||||
/*
|
||||
* this node is the same, but we only need to
|
||||
* move subtrees.
|
||||
*/
|
||||
else if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE) {
|
||||
/*
|
||||
* move subtree to new parent
|
||||
*/
|
||||
i[ni].sysctl_clen = o[oi].sysctl_clen;
|
||||
i[ni].sysctl_csize = o[oi].sysctl_csize;
|
||||
i[ni].sysctl_child = o[oi].sysctl_child;
|
||||
/*
|
||||
* reparent inherited subtree
|
||||
*/
|
||||
for (t = 0;
|
||||
i[ni].sysctl_child != NULL &&
|
||||
t < i[ni].sysctl_clen;
|
||||
t++)
|
||||
i[ni].sysctl_child[t].sysctl_parent = &i[ni];
|
||||
}
|
||||
ni++;
|
||||
oi++;
|
||||
}
|
||||
|
||||
/*
|
||||
* left over new nodes need to have empty subtrees cleared
|
||||
*/
|
||||
while (ni < nlen) {
|
||||
if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE)
|
||||
i[ni].sysctl_child = NULL;
|
||||
ni++;
|
||||
}
|
||||
|
||||
/*
|
||||
* left over old nodes need to be cleaned out
|
||||
*/
|
||||
while (oi < olen) {
|
||||
free_children(&o[oi]);
|
||||
oi++;
|
||||
}
|
||||
|
||||
/*
|
||||
* pop new head in
|
||||
*/
|
||||
sysctl_mibroot.sysctl_clen = nlen;
|
||||
sysctl_mibroot.sysctl_csize = nlen;
|
||||
sysctl_mibroot.sysctl_child = h;
|
||||
free(o);
|
||||
}
|
||||
|
||||
/*
|
||||
* that's "given name" as a string, the integer form of the name fit
|
||||
* to be passed to sysctl(), "canonicalized name" (optional), and a
|
||||
* pointer to the length of the integer form. oh, and then a pointer
|
||||
* to the node, in case you (the caller) care. you can leave them all
|
||||
* NULL except for gname, though that might be rather pointless,
|
||||
* unless all you wanna do is verify that a given name is acceptable.
|
||||
*
|
||||
* returns either 0 (everything was fine) or -1 and sets errno
|
||||
* accordingly. if errno is set to EAGAIN, we detected a change to
|
||||
* the mib while parsing, and you should try again. in the case of an
|
||||
* invalid node name, cname will be set to contain the offending name.
|
||||
*/
|
||||
#ifdef _REENTRANT
|
||||
static mutex_t sysctl_mutex = MUTEX_INITIALIZER;
|
||||
static int sysctlnametomib_unlocked(const char *, int *, u_int *,
|
||||
char *, size_t *, struct sysctlnode **);
|
||||
#endif /* __REENTRANT */
|
||||
|
||||
int
|
||||
sysctlnametomib(const char *gname, int *iname, u_int *namelenp,
|
||||
char *cname, size_t *csz, struct sysctlnode **rnode)
|
||||
#ifdef _REENTRANT
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&sysctl_mutex);
|
||||
rc = sysctlnametomib_unlocked(gname, iname, namelenp, cname, csz,
|
||||
rnode);
|
||||
mutex_unlock(&sysctl_mutex);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctlnametomib_unlocked(const char *gname, int *iname, u_int *namelenp,
|
||||
char *cname, size_t *csz, struct sysctlnode **rnode)
|
||||
#endif /* _REENTRANT */
|
||||
{
|
||||
struct sysctlnode *pnode, *node;
|
||||
int name[CTL_MAXNAME], ni, n, haven;
|
||||
u_int nl;
|
||||
quad_t q;
|
||||
char sep[2], token[SYSCTL_NAMELEN],
|
||||
pname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME];
|
||||
const char *piece, *dot;
|
||||
char *t;
|
||||
size_t l;
|
||||
|
||||
if (rnode == NULL || *rnode == NULL)
|
||||
pnode = &sysctl_mibroot;
|
||||
else
|
||||
pnode = *rnode;
|
||||
|
||||
if (sysctl_rootof(pnode) == &sysctl_mibroot)
|
||||
relearnhead();
|
||||
|
||||
nl = ni = 0;
|
||||
token[0] = '\0';
|
||||
pname[0] = '\0';
|
||||
node = NULL;
|
||||
|
||||
/*
|
||||
* default to using '.' as the separator, but allow '/' as
|
||||
* well, and then allow a leading separator
|
||||
*/
|
||||
if ((dot = strpbrk(gname, "./")) == NULL)
|
||||
sep[0] = '.';
|
||||
else
|
||||
sep[0] = dot[0];
|
||||
sep[1] = '\0';
|
||||
if (gname[0] == sep[0]) {
|
||||
strlcat(pname, sep, sizeof(pname));
|
||||
gname++;
|
||||
}
|
||||
|
||||
#define COPY_OUT_DATA(t, c, cs, nlp, l) do { \
|
||||
if ((c) != NULL && (cs) != NULL) \
|
||||
*(cs) = strlcpy((c), (t), *(cs)); \
|
||||
else if ((cs) != NULL) \
|
||||
*(cs) = strlen(t) + 1; \
|
||||
if ((nlp) != NULL) \
|
||||
*(nlp) = (l); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
piece = gname;
|
||||
while (piece != NULL && *piece != '\0') {
|
||||
/*
|
||||
* what was i looking for?
|
||||
*/
|
||||
dot = strchr(piece, sep[0]);
|
||||
if (dot == NULL) {
|
||||
l = strlcpy(token, piece, sizeof(token));
|
||||
if (l > sizeof(token)) {
|
||||
COPY_OUT_DATA(piece, cname, csz, namelenp, nl);
|
||||
errno = ENAMETOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
else if (dot - piece > sizeof(token) - 1) {
|
||||
COPY_OUT_DATA(token, cname, csz, namelenp, nl);
|
||||
errno = ENAMETOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
else {
|
||||
strncpy(token, piece, (size_t)(dot - piece));
|
||||
token[dot - piece] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* i wonder if this "token" is an integer?
|
||||
*/
|
||||
errno = 0;
|
||||
q = strtoq(token, &t, 0);
|
||||
n = (int)q;
|
||||
if (errno != 0 || *t != '\0')
|
||||
haven = 0;
|
||||
else if (q < INT_MIN || q > UINT_MAX)
|
||||
haven = 0;
|
||||
else
|
||||
haven = 1;
|
||||
|
||||
/*
|
||||
* make sure i have something to look at
|
||||
*/
|
||||
if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) {
|
||||
if (haven && nl > 0) {
|
||||
strlcat(pname, sep, sizeof(pname));
|
||||
goto just_numbers;
|
||||
}
|
||||
COPY_OUT_DATA(token, cname, csz, namelenp, nl);
|
||||
errno = ENOTDIR;
|
||||
return (-1);
|
||||
}
|
||||
if (pnode->sysctl_child == NULL) {
|
||||
if (learn_tree(name, nl, pnode) == -1) {
|
||||
COPY_OUT_DATA(token, cname, csz, namelenp, nl);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
node = pnode->sysctl_child;
|
||||
if (node == NULL) {
|
||||
COPY_OUT_DATA(token, cname, csz, namelenp, nl);
|
||||
errno = ENOENT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* now...is it there?
|
||||
*/
|
||||
for (ni = 0; ni < pnode->sysctl_clen; ni++)
|
||||
if ((haven && ((n == node[ni].sysctl_num) ||
|
||||
(node[ni].sysctl_flags & SYSCTL_ANYNUMBER))) ||
|
||||
strcmp(token, node[ni].sysctl_name) == 0)
|
||||
break;
|
||||
if (ni >= pnode->sysctl_clen) {
|
||||
COPY_OUT_DATA(token, cname, csz, namelenp, nl);
|
||||
errno = ENOENT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* ah...it is.
|
||||
*/
|
||||
pnode = &node[ni];
|
||||
if (nl > 0)
|
||||
strlcat(pname, sep, sizeof(pname));
|
||||
if (haven && n != pnode->sysctl_num) {
|
||||
just_numbers:
|
||||
strlcat(pname, token, sizeof(pname));
|
||||
name[nl] = n;
|
||||
}
|
||||
else {
|
||||
strlcat(pname, pnode->sysctl_name, sizeof(pname));
|
||||
name[nl] = pnode->sysctl_num;
|
||||
}
|
||||
piece = (dot != NULL) ? dot + 1 : NULL;
|
||||
nl++;
|
||||
if (nl == CTL_MAXNAME) {
|
||||
COPY_OUT_DATA(token, cname, csz, namelenp, nl);
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (nl == 0) {
|
||||
if (namelenp != NULL)
|
||||
*namelenp = 0;
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
COPY_OUT_DATA(pname, cname, csz, namelenp, nl);
|
||||
if (iname != NULL && namelenp != NULL)
|
||||
memcpy(iname, &name[0], MIN(nl, *namelenp) * sizeof(int));
|
||||
if (namelenp != NULL)
|
||||
*namelenp = nl;
|
||||
if (rnode != NULL) {
|
||||
if (*rnode != NULL)
|
||||
/*
|
||||
* they gave us a private tree to work in, so
|
||||
* we give back a pointer into that private
|
||||
* tree
|
||||
*/
|
||||
*rnode = pnode;
|
||||
else {
|
||||
/*
|
||||
* they gave us a place to put the node data,
|
||||
* so give them a copy
|
||||
*/
|
||||
*rnode = malloc(sizeof(struct sysctlnode));
|
||||
if (*rnode != NULL) {
|
||||
**rnode = *pnode;
|
||||
(*rnode)->sysctl_child = NULL;
|
||||
(*rnode)->sysctl_parent = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
Loading…
Reference in New Issue