Changeset View
Standalone View
sys/kern/kern_sysctl.c
Show First 20 Lines • Show All 1,128 Lines • ▼ Show 20 Lines | if (namelen == 0) { | ||||
* For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it | * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it | ||||
* has a handler) and move on to the children. | * has a handler) and move on to the children. | ||||
*/ | */ | ||||
if (!honor_skip) | if (!honor_skip) | ||||
return (2); | return (2); | ||||
if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | ||||
return (2); | return (2); | ||||
/* If node does not have an iterator, treat it as leaf */ | /* If node does not have an iterator, treat it as leaf */ | ||||
if (oidp->oid_handler) | if (!SYSCTL_NODE_CAN_ITERATE(oidp)) | ||||
asiciliano: Hi @melifaro, I have a question because I would maintain the feature of [[ https://gitlab. | |||||
asicilianoUnsubmitted Not Done Inline ActionsHi @melifaro, I have a question because I would want to maintain the features of sysctlinfo (D21700) similar to the undocumented interface: Is this a refactoring or a "change feature"? asiciliano: Hi @melifaro, I have a question because I would want to maintain the features of sysctlinfo… | |||||
melifaroAuthorUnsubmitted Done Inline ActionsHi Alfonso! Re D21700: from briefly looking into it, it looks like these are mostly orthogonal to each other. The part of this change that focuses on changing sysctl core simply extends existing interface for dynamic OIDs (oid_handler for sysctl nodes), allowing other functions iterate over dynamic tree, resolve the paths ending in these dynamic oids and retrieve formatting data. It should not result in any changes for existing sysctls, though people may potentially want to add such functions to some of the existing dynamic oids to improve userland visibility. Also - I agree, we should certainly have more visibility into what nodes are and have iterator/format interfaces documented. melifaro: Hi Alfonso!
Sorry, I haven't updated the change description yesterday, now the description… | |||||
asicilianoUnsubmitted Not Done Inline ActionsOK and thank you for your exhaustive reply. asiciliano: OK and thank you for your exhaustive reply. | |||||
return (2); | return (2); | ||||
/* Report oid as a node to iterate */ | /* Report oid as a node to iterate */ | ||||
return (1); | return (1); | ||||
} | } | ||||
/* | /* | ||||
* No match yet. Continue seeking the given name. | * No match yet. Continue seeking the given name. | ||||
Show All 16 Lines | if (oidp->oid_number > *name) { | ||||
* | * | ||||
* For CTL_SYSCTL_NEXTNOSKIP we are done. | * For CTL_SYSCTL_NEXTNOSKIP we are done. | ||||
* | * | ||||
* For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it | * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it | ||||
* has a handler) and move on to the children. | * has a handler) and move on to the children. | ||||
*/ | */ | ||||
if (!honor_skip) | if (!honor_skip) | ||||
return (2); | return (2); | ||||
if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | if (!SYSCTL_IS_NODE(oidp)) | ||||
return (2); | return (2); | ||||
/* If node does not have an iterator, treat it as leaf */ | /* If node does not have an iterator, treat it as leaf */ | ||||
if (oidp->oid_handler) | if (!SYSCTL_NODE_CAN_ITERATE(oidp)) | ||||
return (2); | return (2); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* exact match at a given level */ | /* exact match at a given level */ | ||||
if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | ||||
return (0); | return (0); | ||||
if (oidp->oid_handler) | if (!SYSCTL_NODE_CAN_ITERATE(oidp)) | ||||
return (0); | return (0); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* | /* | ||||
* Recursively walk the sysctl subtree at lsp until we find the given name. | * 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. | * Returns true and fills in next oid data in @next and @len if oid is found. | ||||
*/ | */ | ||||
static bool | static bool | ||||
sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, | sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, unsigned int namelen, | ||||
int *next, int *len, int level, bool honor_skip) | int *next, int *len, int level, bool honor_skip) | ||||
{ | { | ||||
struct sysctl_oid *oidp; | struct sysctl_oid *oidp; | ||||
bool success = false; | bool success = false; | ||||
int ret; | int next_namelen, ret; | ||||
int *next_name; | |||||
next_namelen = (namelen > 0) ? namelen - 1 : 0; | |||||
next_name = name + 1; | |||||
SYSCTL_ASSERT_LOCKED(); | SYSCTL_ASSERT_LOCKED(); | ||||
SLIST_FOREACH(oidp, lsp, oid_link) { | SLIST_FOREACH(oidp, lsp, oid_link) { | ||||
ret = sysctl_sysctl_next_node(oidp, name, namelen, honor_skip); | ret = sysctl_sysctl_next_node(oidp, name, namelen, honor_skip); | ||||
if (ret == 0) | if (ret == 0) | ||||
continue; | continue; | ||||
if (ret == 2) { | if (ret == 2) { | ||||
success = true; | success = true; | ||||
break; | break; | ||||
} | } | ||||
/* ret == 1 means we need to iterate over node children */ | /* ret == 1 means we need to iterate over node children */ | ||||
lsp = SYSCTL_CHILDREN(oidp); | if (oidp->oid_handlers != NULL) { | ||||
if (namelen == 0) { | success = oidp->oid_handlers->nextoid(oidp, | ||||
success = sysctl_sysctl_next_ls(lsp, NULL, 0, | next_name, next_namelen, next + 1, len, level + 1, | ||||
next + 1, len, level + 1, honor_skip); | honor_skip); | ||||
} else { | } else { | ||||
success = sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1, | success = sysctl_sysctl_next_ls(SYSCTL_CHILDREN(oidp), | ||||
next + 1, len, level + 1, honor_skip); | next_name, next_namelen, next + 1, len, level + 1, | ||||
honor_skip); | |||||
} | |||||
if (!success) { | if (!success) { | ||||
/* | /* | ||||
* We maintain the invariant that current node oid | * We maintain the invariant that current node oid | ||||
* is >= the oid provided in @name. | * is >= the oid provided in @name. | ||||
* As there are no usable children at this node, | * As there are no usable children at this node, | ||||
* current node oid is strictly > than the requested | * current node oid is strictly > than the requested | ||||
* oid. | * oid. | ||||
* Hence, reduce namelen to 0 to allow for picking first | * Hence, reduce namelen to 0 to allow for picking first | ||||
* nodes/leafs in the next node in list. | * nodes/leafs in the next node in list. | ||||
*/ | */ | ||||
namelen = 0; | namelen = 0; | ||||
} | next_name = NULL; | ||||
} | next_namelen = 0; | ||||
if (success) | } else | ||||
break; | break; | ||||
} | } | ||||
if (success) { | if (success) { | ||||
*next = oidp->oid_number; | *next = oidp->oid_number; | ||||
if (level > *len) | if (level > *len) | ||||
*len = level; | *len = level; | ||||
return (true); | return (true); | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | if (name == NULL || *name == '\0') { | ||||
if (oidpp) | if (oidpp) | ||||
*oidpp = oidp; | *oidpp = oidp; | ||||
return (0); | return (0); | ||||
} | } | ||||
if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) | ||||
break; | break; | ||||
if (oidp->oid_handler) | if (oidp->oid_handler) { | ||||
if (oidp->oid_handlers != NULL) | |||||
return (oidp->oid_handlers->name2oid(name, oid, len)); | |||||
break; | break; | ||||
} | |||||
lsp = SYSCTL_CHILDREN(oidp); | lsp = SYSCTL_CHILDREN(oidp); | ||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
static int | static int | ||||
sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_name2oid(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
char *p; | char *p; | ||||
int error, oid[CTL_MAXNAME], len = 0; | int error, oid[CTL_MAXNAME], len = 0; | ||||
struct sysctl_oid *op = NULL; | |||||
struct rm_priotracker tracker; | struct rm_priotracker tracker; | ||||
char buf[32]; | char buf[32]; | ||||
if (!req->newlen) | if (!req->newlen) | ||||
return (ENOENT); | return (ENOENT); | ||||
if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ | if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ | ||||
return (ENAMETOOLONG); | return (ENAMETOOLONG); | ||||
p = buf; | p = buf; | ||||
if (req->newlen >= sizeof(buf)) | if (req->newlen >= sizeof(buf)) | ||||
p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK); | p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK); | ||||
error = SYSCTL_IN(req, p, req->newlen); | error = SYSCTL_IN(req, p, req->newlen); | ||||
if (error) { | if (error) { | ||||
if (p != buf) | if (p != buf) | ||||
free(p, M_SYSCTL); | free(p, M_SYSCTL); | ||||
return (error); | return (error); | ||||
} | } | ||||
p [req->newlen] = '\0'; | p [req->newlen] = '\0'; | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_RLOCK(&tracker); | ||||
error = name2oid(p, oid, &len, &op); | error = name2oid(p, oid, &len, NULL); | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_RUNLOCK(&tracker); | ||||
if (p != buf) | if (p != buf) | ||||
free(p, M_SYSCTL); | free(p, M_SYSCTL); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
Show All 9 Lines | SYSCTL_PROC(_sysctl, CTL_SYSCTL_NAME2OID, name2oid, CTLTYPE_INT | CTLFLAG_RW | | ||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, 0, 0, | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, 0, 0, | ||||
sysctl_sysctl_name2oid, "I", ""); | sysctl_sysctl_name2oid, "I", ""); | ||||
static int | static int | ||||
sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) | sysctl_sysctl_oidfmt(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct sysctl_oid *oid; | struct sysctl_oid *oid; | ||||
struct rm_priotracker tracker; | struct rm_priotracker tracker; | ||||
int error; | int error, nindx; | ||||
unsigned int oid_kind; | |||||
const char *oid_fmt; | |||||
char oid_fmt_buf[8]; | |||||
int *name = (int *)arg1; | |||||
unsigned int namelen = (u_int)arg2; | |||||
error = sysctl_wire_old_buffer(req, 0); | error = sysctl_wire_old_buffer(req, 0); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
SYSCTL_RLOCK(&tracker); | SYSCTL_RLOCK(&tracker); | ||||
error = sysctl_find_oid(arg1, arg2, &oid, NULL, req); | error = sysctl_find_oid(arg1, arg2, &oid, &nindx, req); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if (oid->oid_fmt == NULL) { | if (oid->oid_fmt == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
goto out; | goto out; | ||||
} | } | ||||
error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); | if ((namelen > nindx) && SYSCTL_IS_NODE(oid) && (oid->oid_handlers != NULL)) { | ||||
error = oid->oid_handlers->oidfmt(oid, | |||||
&name[nindx], namelen - nindx, &oid_kind, | |||||
oid_fmt_buf, sizeof(oid_fmt_buf)); | |||||
if (error != 0) | |||||
goto out; | |||||
oid_fmt = oid_fmt_buf; | |||||
} else { | |||||
oid_kind = oid->oid_kind; | |||||
oid_fmt = oid->oid_fmt; | |||||
} | |||||
error = SYSCTL_OUT(req, &oid_kind, sizeof(oid_kind)); | |||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
error = SYSCTL_OUT(req, oid->oid_fmt, strlen(oid->oid_fmt) + 1); | error = SYSCTL_OUT(req, oid_fmt, strlen(oid_fmt) + 1); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_RUNLOCK(&tracker); | ||||
return (error); | return (error); | ||||
} | } | ||||
static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDFMT, oidfmt, CTLFLAG_RD | | static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDFMT, oidfmt, CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidfmt, ""); | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidfmt, ""); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | sysctl_sysctl_oidlabel(SYSCTL_HANDLER_ARGS) | ||||
error = SYSCTL_OUT(req, oid->oid_label, strlen(oid->oid_label) + 1); | error = SYSCTL_OUT(req, oid->oid_label, strlen(oid->oid_label) + 1); | ||||
out: | out: | ||||
SYSCTL_RUNLOCK(&tracker); | SYSCTL_RUNLOCK(&tracker); | ||||
return (error); | return (error); | ||||
} | } | ||||
static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDLABEL, oidlabel, CTLFLAG_RD | | static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDLABEL, oidlabel, CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidlabel, ""); | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidlabel, ""); | ||||
/* | |||||
* Helper functions for versioned sysctls | |||||
*/ | |||||
static char * | |||||
print_oid_buf(char *buf, size_t bufsize, struct sysctl_oid *oidp) | |||||
{ | |||||
size_t rem = bufsize - 1; | |||||
struct sysctl_oid *curr; | |||||
char obuf[6]; | |||||
const char *name; | |||||
int len; | |||||
buf[rem] = '\0'; | |||||
for (curr = oidp; curr != NULL; curr = SYSCTL_PARENT(curr)) { | |||||
len = strlen(curr->oid_name); | |||||
if (len == 0) { | |||||
snprintf(obuf, sizeof(obuf), "%d", curr->oid_number); | |||||
name = obuf; | |||||
len = strlen(name); | |||||
} else | |||||
name = curr->oid_name; | |||||
rem -= len; | |||||
if (curr != oidp) | |||||
rem -= 1; | |||||
if (rem < 0) | |||||
return (NULL); | |||||
memcpy(&buf[rem], name, len); | |||||
if (curr != oidp) | |||||
buf[rem + len] = '.'; | |||||
} | |||||
return (&buf[rem]); | |||||
} | |||||
static void | |||||
print_unversioned_warning(struct proc *p, struct sysctl_oid *oidp) | |||||
{ | |||||
char path[96], *oid_str; | |||||
bzero(&path, sizeof(path)); | |||||
oid_str = print_oid_buf(path, sizeof(path), oidp); | |||||
if (oid_str == NULL) | |||||
oid_str = ""; | |||||
printf("WARNING: pid %d (%s) uses unversioned sysctl %s\n", | |||||
p->p_pid, p->p_comm, oid_str); | |||||
} | |||||
uint32_t | |||||
sysctl_get_oid_version(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int args = (int)arg2; | |||||
int *pargs = (int *)arg1; | |||||
uint32_t version; | |||||
if (args > 0) { | |||||
version = (uint32_t)pargs[0]; | |||||
} else { | |||||
/* Compat */ | |||||
struct proc *p = curproc; | |||||
if (P_OSREL_MAJOR(p->p_osrel) == P_OSREL_MAJOR(__FreeBSD_version)) | |||||
print_unversioned_warning(p, oidp); | |||||
version = 0; | |||||
} | |||||
return (version); | |||||
} | |||||
/* | |||||
* Copies MIN(structure_size, buffer_size) bytes of the structure | |||||
* to the userland-provided buffer. | |||||
* Simplest "default" handler to fill-in multiple verisions of | |||||
* append-only structures. | |||||
*/ | |||||
int | |||||
sysctl_copyout_ver(SYSCTL_HANDLER_ARGS, void *new_data, size_t new_datalen) | |||||
{ | |||||
uint32_t version; | |||||
version = sysctl_get_oid_version(oidp, arg1, arg2, req); | |||||
/* If version != 0 -> means it exists as supported sysctl */ | |||||
printf("COPYOUT STRUCT %p len %lu REQUESTED VER %u\n", | |||||
new_data, new_datalen, version); | |||||
return (SYSCTL_OUT(req, new_data, new_datalen)); | |||||
} | |||||
/* | /* | ||||
* Default "handler" functions. | * Default "handler" functions. | ||||
*/ | */ | ||||
/* | /* | ||||
* Handle a bool. | * Handle a bool. | ||||
* Two cases: | * Two cases: | ||||
▲ Show 20 Lines • Show All 1,522 Lines • Show Last 20 Lines |
Hi @melifaro, I have a question because I would maintain the feature of sysctlinfo (D21700) similar to this interface: Is this a refactoring or a "change feature"?
Properly, so far sysctl.next[noskip] returns only the next leaf, do you want to return also the next internal node? Any other functional change?
Thank you in advance