Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_sysctl.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/fail.h> | #include <sys/fail.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/sysctlinfo.h> | |||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/jail.h> | #include <sys/jail.h> | ||||
#include <sys/kdb.h> | #include <sys/kdb.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/rmlock.h> | #include <sys/rmlock.h> | ||||
▲ Show 20 Lines • Show All 2,769 Lines • ▼ Show 20 Lines | out: | ||||
db_flush_lex(); | db_flush_lex(); | ||||
if (error == EINVAL) { | if (error == EINVAL) { | ||||
db_sysctl_cmd_usage(); | db_sysctl_cmd_usage(); | ||||
} | } | ||||
} | } | ||||
#endif /* DDB */ | #endif /* DDB */ | ||||
/* | |||||
* sysctlinfo interface | |||||
*/ | |||||
/* Util */ | |||||
static int | |||||
sysctlinfo_next_node(struct sysctl_oid *path[CTL_MAXNAME], int id[CTL_MAXNAME], | |||||
size_t *level, int mode) | |||||
{ | |||||
struct sysctl_oid_list *children; | |||||
size_t l = *level; | |||||
SYSCTL_ASSERT_LOCKED(); | |||||
for(;;) { | |||||
children = SYSCTL_CHILDREN(path[l-1]); | |||||
if(children != NULL && !SLIST_EMPTY(children)) { /* children */ | |||||
path[l]=SLIST_FIRST(children); | |||||
l++; | |||||
} else { /* no children */ | |||||
for(;;) { | |||||
if(l <= 0) | |||||
break; | |||||
if(SLIST_NEXT(path[l-1], oid_link) != NULL) { /* brother */ | |||||
path[l-1] = SLIST_NEXT(path[l-1], oid_link); | |||||
break; | |||||
} | |||||
else /* no brother */ | |||||
l--; | |||||
} | |||||
} | |||||
if(l <= 0 || l > CTL_MAXNAME) | |||||
return (ENOATTR); | |||||
id[l-1]=path[l-1]->oid_number; | |||||
if(path[l-1]->oid_kind & (CTLFLAG_SKIP | CTLFLAG_DORMANT)) | |||||
continue; | |||||
if(mode == ENTRYNEXTLEAF || mode == ENTRYALLINFO_WITHNEXTLEAF || | |||||
mode == ENTRYALLINFOBYNAME_WITHNEXTLEAF) { | |||||
if((path[l-1]->oid_kind & CTLTYPE) != CTLTYPE_NODE || | |||||
path[l-1]->oid_handler !=NULL) | |||||
break; | |||||
} else { | |||||
break; | |||||
} | |||||
} | |||||
*level=l; | |||||
return 0; | |||||
} | |||||
/* handler for the nodes */ | |||||
static int | |||||
sysctlinfo_interface(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct rm_priotracker tracker; | |||||
int error = 0, id[CTL_MAXNAME], indx = 0, i; | |||||
size_t idlevel; | |||||
struct sysctl_oid *oid, *path[CTL_MAXNAME]; | |||||
struct sysctl_oid_list *lsp = &sysctl__children; | |||||
char buf[SYSCTLINFO_MAXFAKE], name_input[MAXPATHLEN], *name_inputp; | |||||
bool all_flag, name_flag; | |||||
/* | |||||
* Get id or name from userland | |||||
*/ | |||||
name_flag = (arg2 == ENTRYIDBYNAME || arg2 == ENTRYFAKEIDBYNAME || | |||||
arg2 == ENTRYDESCBYNAME || arg2 == ENTRYKINDBYNAME || | |||||
arg2 == ENTRYFMTBYNAME || arg2 == ENTRYLABELBYNAME || | |||||
arg2 == ENTRYALLINFOBYNAME || | |||||
arg2 == ENTRYALLINFOBYNAME_WITHNEXTNODE || | |||||
arg2 == ENTRYALLINFOBYNAME_WITHNEXTLEAF) ? true : false; | |||||
if(req->newlen == 0) | |||||
return (EINVAL); | |||||
if(name_flag) { | |||||
if (req->newlen >= MAXPATHLEN) | |||||
return (ENAMETOOLONG); | |||||
memset(name_input, 0, MAXPATHLEN); | |||||
if((error = SYSCTL_IN(req, name_input, req->newlen)) !=0 ) | |||||
return (error); | |||||
idlevel=1; | |||||
for(i=0; i<req->newlen; i++) { | |||||
if(name_input[i] == '.') { | |||||
name_input[i] = '\0'; | |||||
if(name_input[i+1] == '\0' && | |||||
arg2 == ENTRYFAKEIDBYNAME) | |||||
break; | |||||
idlevel++; | |||||
if(idlevel > CTL_MAXNAME) | |||||
return (EINVAL); | |||||
} | |||||
} | |||||
name_inputp = &name_input[0]; | |||||
} else { | |||||
if(req->newlen > CTL_MAXNAME * sizeof(int)) | |||||
return (EINVAL); | |||||
if(req->newlen % sizeof(int) != 0) | |||||
return (EINVAL); | |||||
if((error = SYSCTL_IN(req, id, req->newlen)) !=0 ) | |||||
return (error); | |||||
idlevel = req->newlen / sizeof(int); | |||||
} | |||||
/* Get lock */ | |||||
SYSCTL_RLOCK(&tracker); | |||||
/* | |||||
* Find OID | |||||
*/ | |||||
while (indx < idlevel) { | |||||
SLIST_FOREACH(oid, lsp, oid_link) { | |||||
if (name_flag) { | |||||
if(strcmp(name_inputp, oid->oid_name) == 0) | |||||
break; | |||||
} else { | |||||
if (oid->oid_number == id[indx]) | |||||
break; | |||||
} | |||||
} | |||||
if (oid == NULL) { | |||||
break; | |||||
} | |||||
path[indx] = oid; | |||||
if(name_flag) | |||||
id[indx] = oid->oid_number; | |||||
indx++; | |||||
if (indx == idlevel) { | |||||
KASSERT((oid->oid_kind & CTLFLAG_DYING) == 0, | |||||
("%s found DYING node %p", __func__, oid)); | |||||
if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE && | |||||
(oid->oid_kind & CTLFLAG_DORMANT) != 0) { | |||||
error = ENOENT; | |||||
} | |||||
} else { /* indx < idlevel */ | |||||
if(name_flag) | |||||
name_inputp += (strlen(name_inputp) + 1); | |||||
if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE && | |||||
oid->oid_handler == NULL) { | |||||
lsp = SYSCTL_CHILDREN(oid); | |||||
} else { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if(error || (indx != idlevel && arg2 != ENTRYFAKENAME)) { | |||||
error = ENOENT; | |||||
goto out; | |||||
} | |||||
/* | |||||
* Capability check | |||||
*/ | |||||
#ifdef CAPABILITY_MODE | |||||
if (IN_CAPABILITY_MODE(req->td)) { | |||||
if( !(oid->oid_kind & CTLFLAG_CAPRD) && | |||||
!(oid->oid_kind & CTLFLAG_CAPWR) && | |||||
arg2 != ENTRYFAKENAME && | |||||
arg2 != ENTRYNEXTNODE && | |||||
arg2 != ENTRYNEXTLEAF) { | |||||
error = ECAPMODE; | |||||
goto out; | |||||
} | |||||
} | |||||
#endif | |||||
/* | |||||
* Pass info to the userland | |||||
*/ | |||||
all_flag = (arg2 == ENTRYALLINFO || arg2 == ENTRYALLINFO_WITHNEXTNODE || | |||||
arg2 == ENTRYALLINFO_WITHNEXTLEAF || arg2 == ENTRYALLINFOBYNAME || | |||||
arg2 == ENTRYALLINFOBYNAME_WITHNEXTNODE || | |||||
arg2 == ENTRYALLINFOBYNAME_WITHNEXTLEAF) ? true : false; | |||||
if(all_flag) { | |||||
if ((error = SYSCTL_OUT(req, &idlevel, sizeof (size_t))) != 0) | |||||
goto out; | |||||
} | |||||
if(arg2 == ENTRYIDBYNAME || arg2 == ENTRYFAKEIDBYNAME || all_flag) { | |||||
if ((error = SYSCTL_OUT(req, id, idlevel * sizeof (int))) != 0) | |||||
goto out; | |||||
} | |||||
if(arg2 == ENTRYNAME || arg2 == ENTRYFAKENAME || all_flag) { | |||||
for(i=0; i < idlevel; i++) { | |||||
if (i > 0) | |||||
if((error = SYSCTL_OUT(req, ".", 1)) != 0) | |||||
goto out; | |||||
if (i < indx) { | |||||
error = SYSCTL_OUT(req, path[i]->oid_name, | |||||
strlen(path[i]->oid_name)); | |||||
} else { | |||||
snprintf(buf, sizeof(buf), "%d", id[i]); | |||||
error = SYSCTL_OUT(req, buf, strlen(buf)); | |||||
} | |||||
if (error) | |||||
goto out; | |||||
} | |||||
if((error = SYSCTL_OUT(req, "", 1)) != 0) | |||||
goto out; | |||||
} | |||||
if (arg2 == ENTRYDESC || arg2 == ENTRYDESCBYNAME || all_flag) { | |||||
if (oid->oid_descr != NULL) { | |||||
if ((error = SYSCTL_OUT_STR(req, oid->oid_descr)) != 0) | |||||
goto out; | |||||
} else { | |||||
if (all_flag) { | |||||
if ((error = SYSCTL_OUT(req, "", 1)) != 0) | |||||
goto out; | |||||
} else { | |||||
error = ENOATTR; | |||||
goto out; | |||||
} | |||||
} | |||||
} | |||||
if (arg2 == ENTRYKIND || arg2 == ENTRYKINDBYNAME || all_flag) { | |||||
error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); | |||||
if(error) | |||||
goto out; | |||||
} | |||||
if (arg2 == ENTRYFMT || arg2 == ENTRYFMTBYNAME || all_flag) { | |||||
if (oid->oid_fmt != NULL) { | |||||
if ((error = SYSCTL_OUT_STR(req, oid->oid_fmt)) != 0) | |||||
goto out; | |||||
} else { | |||||
if (all_flag) { | |||||
if ((error = SYSCTL_OUT(req, "", 1)) != 0) | |||||
goto out; | |||||
} else { | |||||
error = ENOATTR; | |||||
goto out; | |||||
} | |||||
} | |||||
} | |||||
if(arg2 == ENTRYLABEL || arg2 == ENTRYLABELBYNAME || all_flag) { | |||||
if (oid->oid_label != NULL) { | |||||
if ((error = SYSCTL_OUT_STR(req, oid->oid_label)) != 0) | |||||
goto out; | |||||
} else { | |||||
if (all_flag) { | |||||
if ((error = SYSCTL_OUT(req, "", 1)) != 0) | |||||
goto out; | |||||
} else { | |||||
error = ENOATTR; | |||||
goto out; | |||||
} | |||||
} | |||||
} | |||||
if(arg2 == ENTRYALLINFO_WITHNEXTNODE || | |||||
arg2 == ENTRYALLINFO_WITHNEXTLEAF || | |||||
arg2 == ENTRYALLINFOBYNAME_WITHNEXTNODE || | |||||
arg2 == ENTRYALLINFOBYNAME_WITHNEXTLEAF || | |||||
arg2 == ENTRYNEXTNODE || arg2 == ENTRYNEXTLEAF) { | |||||
error = sysctlinfo_next_node(path, id, &idlevel, arg2); | |||||
if(error != 0) { | |||||
if(all_flag) | |||||
idlevel=0; | |||||
else | |||||
goto out; | |||||
} | |||||
if(arg2 == ENTRYALLINFO_WITHNEXTNODE || | |||||
arg2 == ENTRYALLINFO_WITHNEXTLEAF) { | |||||
error = SYSCTL_OUT(req, &idlevel, sizeof (size_t)); | |||||
if (error) | |||||
goto out; | |||||
} | |||||
if(arg2 != ENTRYALLINFOBYNAME_WITHNEXTNODE && | |||||
arg2 != ENTRYALLINFOBYNAME_WITHNEXTLEAF) { | |||||
error = SYSCTL_OUT(req, id, idlevel * sizeof (int)); | |||||
} else { | |||||
for(i=0; i < idlevel; i++) { | |||||
if (i > 0) | |||||
error = SYSCTL_OUT(req, ".", 1); | |||||
if (!error) { | |||||
error = SYSCTL_OUT(req, path[i]->oid_name, | |||||
strlen(path[i]->oid_name)); | |||||
} | |||||
if (error) | |||||
goto out; | |||||
} | |||||
error = SYSCTL_OUT(req, "", 1); | |||||
} | |||||
} | |||||
out: | |||||
SYSCTL_RUNLOCK(&tracker); | |||||
return (error); | |||||
} | |||||
/* sysctlinfo nodes */ | |||||
SYSCTL_PROC(_sysctl, ENTRYFAKENAME, entryfakename, CTLTYPE_STRING | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYFAKENAME, | |||||
sysctlinfo_interface, "A", "fake name of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYNAME, entryname, CTLTYPE_STRING | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY |CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYNAME, | |||||
sysctlinfo_interface, "A", "name of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYDESC, entrydesc, CTLTYPE_STRING | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY |CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYDESC, | |||||
sysctlinfo_interface, "A", "description of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYLABEL, entrylabel, CTLTYPE_STRING | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYLABEL, | |||||
sysctlinfo_interface, "A", "label of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYKIND, entrykind, CTLTYPE_UINT | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYKIND, | |||||
sysctlinfo_interface, "IU", "kind (flags and type) of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYFMT, entryfmt, CTLTYPE_STRING | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYFMT, | |||||
sysctlinfo_interface, "A", "format string of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYNEXTNODE, entrynextnode, CTLTYPE_INT | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYNEXTNODE, | |||||
sysctlinfo_interface, "I", "next mib object (internal node or leaf)"); | |||||
SYSCTL_PROC(_sysctl, ENTRYNEXTLEAF, entrynextleaf, CTLTYPE_INT | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYNEXTLEAF, | |||||
sysctlinfo_interface, "I", "next mib object (only leaf)"); | |||||
SYSCTL_PROC(_sysctl, ENTRYALLINFO, entryallinfo, CTLTYPE_OPAQUE | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYALLINFO, | |||||
sysctlinfo_interface, "RAW", "all info of an object"); | |||||
SYSCTL_PROC(_sysctl, ENTRYALLINFO_WITHNEXTNODE, entryallinfo_withnextnode, | |||||
CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | | |||||
CTLFLAG_CAPRW, NULL, ENTRYALLINFO_WITHNEXTNODE, sysctlinfo_interface, | |||||
"RAW", "all info of an object with the next node"); | |||||
SYSCTL_PROC(_sysctl, ENTRYALLINFO_WITHNEXTLEAF, entryallinfo_withnextleaf, | |||||
CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | | |||||
CTLFLAG_CAPRW, NULL, ENTRYALLINFO_WITHNEXTLEAF, sysctlinfo_interface, | |||||
"RAW", "all info of an object with next leaf"); | |||||
SYSCTL_PROC(_sysctl, ENTRYIDBYNAME, entryidbyname, CTLTYPE_STRING | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, ENTRYIDBYNAME, | |||||
sysctlinfo_interface, "A", "id of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYFAKEIDBYNAME, entryfakeidbyname, CTLTYPE_STRING | | |||||
CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYFAKEIDBYNAME, sysctlinfo_interface, "A", | |||||
"fake id of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYDESCBYNAME, entrydescbyname, CTLTYPE_STRING | | |||||
CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYDESCBYNAME, sysctlinfo_interface, "A", | |||||
"description of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYLABELBYNAME, entrylabelbyname, CTLTYPE_STRING | | |||||
CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYLABELBYNAME, sysctlinfo_interface, "A", | |||||
"label of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYKINDBYNAME, entrykindbyname, CTLTYPE_STRING | | |||||
CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYKINDBYNAME, sysctlinfo_interface, "A", "kind of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYFMTBYNAME, entryfmtbyname, CTLTYPE_STRING | | |||||
CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYFMTBYNAME, sysctlinfo_interface, "A", "format of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYALLINFOBYNAME, entryallinfobyname, CTLTYPE_STRING | | |||||
CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYALLINFOBYNAME, sysctlinfo_interface, "A", | |||||
"all info of an object by its name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYALLINFOBYNAME_WITHNEXTNODE, | |||||
entryallinfobyname_withnextnode, CTLTYPE_OPAQUE | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYALLINFOBYNAME_WITHNEXTNODE, sysctlinfo_interface, "RAW", | |||||
"all info of an object by its name with the next node name"); | |||||
SYSCTL_PROC(_sysctl, ENTRYALLINFOBYNAME_WITHNEXTLEAF, | |||||
entryallinfobyname_withnextleaf, CTLTYPE_OPAQUE | CTLFLAG_RW | | |||||
CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, | |||||
ENTRYALLINFOBYNAME_WITHNEXTLEAF, sysctlinfo_interface, "RAW", | |||||
"all info of an object by its name with next leaf name"); |