Changeset View
Changeset View
Standalone View
Standalone View
lib/libcasper/services/cap_sysctl/cap_sysctl.c
Show All 28 Lines | |||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/dnv.h> | |||||
#include <sys/nv.h> | #include <sys/nv.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <libcasper.h> | #include <libcasper.h> | ||||
#include <libcasper_service.h> | #include <libcasper_service.h> | ||||
#include "cap_sysctl.h" | #include "cap_sysctl.h" | ||||
static nvlist_t *sysctl_mibs = NULL; | |||||
int | int | ||||
cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp, | cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp, | ||||
size_t *oldlenp, const void *newp, size_t newlen) | size_t *oldlenp, const void *newp, size_t newlen) | ||||
{ | { | ||||
nvlist_t *nvl; | nvlist_t *nvl; | ||||
const uint8_t *retoldp; | const uint8_t *retoldp; | ||||
uint8_t operation; | uint8_t operation; | ||||
size_t oldlen; | size_t oldlen; | ||||
Show All 31 Lines | if (oldp == NULL && oldlenp != NULL) { | ||||
if (oldlenp != NULL) | if (oldlenp != NULL) | ||||
*oldlenp = oldlen; | *oldlenp = oldlen; | ||||
} | } | ||||
nvlist_destroy(nvl); | nvlist_destroy(nvl); | ||||
return (0); | return (0); | ||||
} | } | ||||
int | |||||
cap_sysctl(cap_channel_t *chan, const int *name, u_int namelen, | |||||
void *oldp, size_t *oldlenp, const void *newp, size_t newlen) | |||||
{ | |||||
nvlist_t *nvl; | |||||
const uint8_t *retoldp; | |||||
size_t oldlen; | |||||
uint8_t operation; | |||||
operation = 0; | |||||
if (oldp != NULL) | |||||
operation |= CAP_SYSCTL_READ; | |||||
if (newp != NULL) | |||||
operation |= CAP_SYSCTL_WRITE; | |||||
nvl = nvlist_create(0); | |||||
nvlist_add_string(nvl, "cmd", "sysctl"); | |||||
nvlist_add_binary(nvl, "name", (const void*)name, namelen * sizeof(int)); | |||||
nvlist_add_number(nvl, "operation", (uint64_t)operation); | |||||
if (oldp == NULL && oldlenp != NULL) | |||||
nvlist_add_null(nvl, "justsize"); | |||||
else if (oldlenp != NULL) | |||||
nvlist_add_number(nvl, "oldlen", (uint64_t)*oldlenp); | |||||
if (newp != NULL) | |||||
nvlist_add_binary(nvl, "newp", newp, newlen); | |||||
nvl = cap_xfer_nvlist(chan, nvl); | |||||
if (nvl == NULL) | |||||
return (-1); | |||||
if (nvlist_get_number(nvl, "error") != 0) { | |||||
errno = (int)nvlist_get_number(nvl, "error"); | |||||
nvlist_destroy(nvl); | |||||
return (-1); | |||||
} | |||||
if (oldp == NULL && oldlenp != NULL) { | |||||
*oldlenp = (size_t)nvlist_get_number(nvl, "oldlen"); | |||||
} else if (oldp != NULL) { | |||||
retoldp = nvlist_get_binary(nvl, "oldp", &oldlen); | |||||
memcpy(oldp, retoldp, oldlen); | |||||
if (oldlenp != NULL) | |||||
*oldlenp = oldlen; | |||||
} | |||||
nvlist_destroy(nvl); | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
* Service functions. | * Service functions. | ||||
*/ | */ | ||||
static int | static int | ||||
sysctl_check_one(const nvlist_t *nvl, bool islimit) | sysctl_check_one(const nvlist_t *nvl, bool islimit) | ||||
{ | { | ||||
const char *name; | const char *name; | ||||
void *cookie; | void *cookie; | ||||
int type; | int type; | ||||
unsigned int fields; | unsigned int fields; | ||||
/* NULL nvl is of course invalid. */ | /* NULL nvl is of course invalid. */ | ||||
if (nvl == NULL) | if (nvl == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (nvlist_error(nvl) != 0) | if (nvlist_error(nvl) != 0) | ||||
return (nvlist_error(nvl)); | return (nvlist_error(nvl)); | ||||
#define HAS_NAME 0x01 | #define HAS_NAME_OR_MIB 0x01 | ||||
#define HAS_OPERATION 0x02 | #define HAS_OPERATION 0x02 | ||||
fields = 0; | fields = 0; | ||||
cookie = NULL; | cookie = NULL; | ||||
while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { | while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { | ||||
/* We accept only one 'name' and one 'operation' in nvl. */ | /* We accept only one 'name' and one 'operation' in nvl. */ | ||||
if (strcmp(name, "name") == 0) { | if (strcmp(name, "name") == 0) { | ||||
if (type != NV_TYPE_STRING) | if (type != NV_TYPE_STRING && type != NV_TYPE_BINARY) | ||||
return (EINVAL); | return (EINVAL); | ||||
oshogbo: style. | |||||
/* Only one 'name' can be present. */ | /* Only one 'name' can be present. */ | ||||
if ((fields & HAS_NAME) != 0) | if ((fields & HAS_NAME_OR_MIB) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
fields |= HAS_NAME; | fields |= HAS_NAME_OR_MIB; | ||||
} else if (strcmp(name, "operation") == 0) { | } else if (strcmp(name, "operation") == 0) { | ||||
uint64_t operation; | uint64_t operation; | ||||
if (type != NV_TYPE_NUMBER) | if (type != NV_TYPE_NUMBER) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* | /* | ||||
* We accept only CAP_SYSCTL_READ and | * We accept only CAP_SYSCTL_READ and | ||||
* CAP_SYSCTL_WRITE flags. | * CAP_SYSCTL_WRITE flags. | ||||
Show All 10 Lines | if (strcmp(name, "name") == 0) { | ||||
fields |= HAS_OPERATION; | fields |= HAS_OPERATION; | ||||
} else if (islimit) { | } else if (islimit) { | ||||
/* If this is limit, there can be no other fields. */ | /* If this is limit, there can be no other fields. */ | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
/* Both fields has to be there. */ | /* Both fields has to be there. */ | ||||
if (fields != (HAS_NAME | HAS_OPERATION)) | if (fields != (HAS_NAME_OR_MIB | HAS_OPERATION)) | ||||
return (EINVAL); | return (EINVAL); | ||||
#undef HAS_OPERATION | #undef HAS_OPERATION | ||||
#undef HAS_NAME | #undef HAS_NAME_OR_MIB | ||||
return (0); | return (0); | ||||
} | } | ||||
static bool | static bool | ||||
sysctl_allowed_mib(const nvlist_t *limits, const int *chmib, size_t chmiblen, | |||||
uint64_t choperation) | |||||
{ | |||||
uint64_t operation; | |||||
const char *name; | |||||
const int *mib; | |||||
size_t len; | |||||
size_t miblen; | |||||
void *cookie; | |||||
int type; | |||||
if (limits == NULL) | |||||
return (true); | |||||
cookie = NULL; | |||||
while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { | |||||
assert(type == NV_TYPE_NUMBER); | |||||
operation = nvlist_get_number(limits, name); | |||||
if ((operation & choperation) != choperation) | |||||
continue; | |||||
mib = (const int*)dnvlist_get_binary(sysctl_mibs, name, &len, | |||||
NULL, 0); | |||||
miblen = len / sizeof(int); | |||||
if ((operation & CAP_SYSCTL_RECURSIVE) == 0) { | |||||
if (miblen != chmiblen || | |||||
memcmp(mib, chmib, miblen) != 0) | |||||
continue; | |||||
} else | |||||
if (miblen > chmiblen || | |||||
memcmp(mib, chmib, miblen) != 0) | |||||
continue; | |||||
return (true); | |||||
} | |||||
return (false); | |||||
} | |||||
static bool | |||||
sysctl_allowed(const nvlist_t *limits, const char *chname, uint64_t choperation) | sysctl_allowed(const nvlist_t *limits, const char *chname, uint64_t choperation) | ||||
{ | { | ||||
uint64_t operation; | uint64_t operation; | ||||
const char *name; | const char *name; | ||||
void *cookie; | void *cookie; | ||||
int type; | int type; | ||||
if (limits == NULL) | if (limits == NULL) | ||||
Show All 21 Lines | while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { | ||||
} | } | ||||
return (true); | return (true); | ||||
} | } | ||||
return (false); | return (false); | ||||
} | } | ||||
static void | |||||
sysctl_lookup_mibs(const nvlist_t *limits) | |||||
{ | |||||
nvlist_t *mibs; | |||||
int mib[CTL_MAXNAME]; | |||||
const char *name; | |||||
void *cookie; | |||||
size_t miblen; | |||||
int type; | |||||
mibs = nvlist_create(0); | |||||
cookie = NULL; | |||||
while ((name = nvlist_next(limits, &type, &cookie)) != NULL) | |||||
if (sysctlnametomib(name, mib, &miblen) == 0) | |||||
nvlist_add_binary(mibs, name, mib, miblen * sizeof(int)); | |||||
nvlist_destroy(sysctl_mibs); | |||||
sysctl_mibs = mibs; | |||||
} | |||||
static int | static int | ||||
sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) | sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) | ||||
{ | { | ||||
const char *name; | const char *name; | ||||
void *cookie; | void *cookie; | ||||
uint64_t operation; | uint64_t operation; | ||||
int type; | int type; | ||||
cookie = NULL; | cookie = NULL; | ||||
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { | while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { | ||||
if (type != NV_TYPE_NUMBER) | if (type != NV_TYPE_NUMBER) | ||||
return (EINVAL); | return (EINVAL); | ||||
operation = nvlist_get_number(newlimits, name); | operation = nvlist_get_number(newlimits, name); | ||||
if ((operation & ~(CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) != 0) | if ((operation & ~(CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if ((operation & (CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) == 0) | if ((operation & (CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) == 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (!sysctl_allowed(oldlimits, name, operation)) | if (!sysctl_allowed(oldlimits, name, operation)) | ||||
return (ENOTCAPABLE); | return (ENOTCAPABLE); | ||||
} | } | ||||
sysctl_lookup_mibs(newlimits); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, | sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, | ||||
nvlist_t *nvlout) | nvlist_t *nvlout) | ||||
{ | { | ||||
const char *name; | const char *name; | ||||
const void *newp; | const void *newp; | ||||
const int *mib; | |||||
void *oldp; | void *oldp; | ||||
uint64_t operation; | uint64_t operation; | ||||
size_t miblen; | |||||
size_t oldlen, newlen; | size_t oldlen, newlen; | ||||
size_t *oldlenp; | size_t *oldlenp; | ||||
int error; | int error; | ||||
if (strcmp(cmd, "sysctl") != 0) | if (strcmp(cmd, "sysctl") != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = sysctl_check_one(nvlin, false); | error = sysctl_check_one(nvlin, false); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
name = nvlist_get_string(nvlin, "name"); | mib = NULL; | ||||
operation = nvlist_get_number(nvlin, "operation"); | operation = nvlist_get_number(nvlin, "operation"); | ||||
if (nvlist_exists_binary(nvlin, "name")) { | |||||
mib = (const int*)nvlist_get_binary(nvlin, "name", &miblen); | |||||
miblen = miblen / sizeof(int); | |||||
if (!sysctl_allowed_mib(limits, mib, miblen, operation)) | |||||
return (ENOTCAPABLE); | |||||
} else { | |||||
name = nvlist_get_string(nvlin, "name"); | |||||
if (!sysctl_allowed(limits, name, operation)) | if (!sysctl_allowed(limits, name, operation)) | ||||
return (ENOTCAPABLE); | return (ENOTCAPABLE); | ||||
} | |||||
if ((operation & CAP_SYSCTL_WRITE) != 0) { | if ((operation & CAP_SYSCTL_WRITE) != 0) { | ||||
if (!nvlist_exists_binary(nvlin, "newp")) | if (!nvlist_exists_binary(nvlin, "newp")) | ||||
return (EINVAL); | return (EINVAL); | ||||
newp = nvlist_get_binary(nvlin, "newp", &newlen); | newp = nvlist_get_binary(nvlin, "newp", &newlen); | ||||
assert(newp != NULL && newlen > 0); | assert(newp != NULL && newlen > 0); | ||||
} else { | } else { | ||||
newp = NULL; | newp = NULL; | ||||
newlen = 0; | newlen = 0; | ||||
Show All 16 Lines | if (nvlist_exists_null(nvlin, "justsize")) { | ||||
oldlenp = &oldlen; | oldlenp = &oldlen; | ||||
} | } | ||||
} else { | } else { | ||||
oldp = NULL; | oldp = NULL; | ||||
oldlen = 0; | oldlen = 0; | ||||
oldlenp = NULL; | oldlenp = NULL; | ||||
} | } | ||||
if (mib != NULL) { | |||||
if (sysctl(mib, miblen, oldp, oldlenp, newp, newlen) == -1) { | |||||
error = errno; | |||||
free(oldp); | |||||
return(error); | |||||
} | |||||
} else { | |||||
if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1) { | if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1) { | ||||
error = errno; | error = errno; | ||||
free(oldp); | free(oldp); | ||||
return (error); | return (error); | ||||
} | |||||
Not Done Inline ActionsThis is very hackish. W eshould just send the name of function we want to use. oshogbo: This is very hackish. W eshould just send the name of function we want to use. | |||||
} | } | ||||
if ((operation & CAP_SYSCTL_READ) != 0) { | if ((operation & CAP_SYSCTL_READ) != 0) { | ||||
if (nvlist_exists_null(nvlin, "justsize")) | if (nvlist_exists_null(nvlin, "justsize")) | ||||
nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen); | nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen); | ||||
else | else | ||||
nvlist_move_binary(nvlout, "oldp", oldp, oldlen); | nvlist_move_binary(nvlout, "oldp", oldp, oldlen); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command, 0); | CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command, 0); |
style.