diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index b91c1a4..65c8a6d 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -500,6 +500,41 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, } /* + * Add a scalar at runtime. + */ +void +sysctl_add_scalar(struct sysctl_ctx_list *clist, + struct sysctl_oid_list *parent, const char *name, + int kind, void *arg1, size_t size, int sign, + const char *descr) +{ + const char *fmt; + int type; + + type = CTLFLAG_MPSAFE | (kind & ~CTLTYPE); + + switch (size) { + case 1: + case 2: + case 4: + type |= CTLTYPE_INT; + fmt = sign ? "I" : "IU"; + break; + case 8: + type |= CTLTYPE_QUAD; + fmt = sign ? "Q" : "QU"; + break; + default: + panic("Unexpected scalar size %zu", size); + } + + (void)sysctl_add_oid(clist, parent, OID_AUTO, name, + type, arg1, (int)size, + sign ? sysctl_handle_sscalar : sysctl_handle_uscalar, + fmt, descr); +} + +/* * Rename an existing oid. */ void @@ -1137,6 +1172,180 @@ retry: return (error); } + +/* + * Handle a signed scalar. + * arg1 points to it, arg2 is the size. + */ +int +sysctl_handle_sscalar(SYSCTL_HANDLER_ARGS) +{ + int64_t s64; + int32_t s32; + int error, sc32; + + CTASSERT(sizeof(intmax_t) == sizeof(int64_t)); + if (arg1 == NULL) + return (EINVAL); + + switch (arg2) { + case 1: + s64 = *(int8_t *)arg1; + break; + case 2: + s64 = *(int16_t *)arg1; + break; + case 4: + s64 = *(int32_t *)arg1; + break; + case 8: + s64 = *(int64_t *)arg1; + break; + default: + printf("Unexpected size %d for sysctl leaf \"%s\"\n", + arg2, oidp->oid_name); + return (EDOOFUS); + } + + s32 = s64; +#ifdef SCTL_MASK32 + sc32 = (req->oldlen == sizeof(int32_t) && + arg2 == sizeof(long) && + s32 == s64 && + (req->flags & SCTL_MASK32) != 0); +#else + sc32 = 0; +#endif + if (sc32 || arg2 <= sizeof(int32_t)) + error = SYSCTL_OUT(req, &s32, sizeof(s32)); + else + error = SYSCTL_OUT(req, &s64, sizeof(s64)); + + if (error != 0 || req->newptr == NULL) + return (error); + +#ifdef SCTL_MASK32 + sc32 = (req->newlen == sizeof(int32_t) && + arg2 == sizeof(long) && + (req->flags & SCTL_MASK32) != 0); +#endif + if (sc32 || arg2 <= sizeof(int32_t)) { + error = SYSCTL_IN(req, &s32, sizeof(s32)); + s64 = s32; + } else + error = SYSCTL_IN(req, &s64, sizeof(s64)); + if (error != 0) + return (error); + switch (arg2) { + case 1: + if (s64 < INT8_MIN || s64 > INT8_MAX) + return (ERANGE); + *(int8_t *)arg1 = (int8_t)s64; + break; + case 2: + if (s64 < INT16_MIN || s64 > INT16_MAX) + return (ERANGE); + *(int16_t *)arg1 = (int16_t)s64; + break; + case 4: + if (s64 < INT32_MIN || s64 > INT32_MAX) + return (ERANGE); + *(int32_t *)arg1 = (int32_t)s64; + break; + case 8: + *(int64_t *)arg1 = s64; + break; + } + return (0); +} + +/* + * Handle an unsigned scalar. + * arg1 points to it, arg2 is the size. + */ +int +sysctl_handle_uscalar(SYSCTL_HANDLER_ARGS) +{ + uint64_t u64; + uint32_t u32; + int error, sc32; + + CTASSERT(sizeof(uintmax_t) == sizeof(uint64_t)); + if (arg1 == NULL) + return (EINVAL); + + switch (arg2) { + case 1: + u64 = *(uint8_t *)arg1; + break; + case 2: + u64 = *(uint16_t *)arg1; + break; + case 4: + u64 = *(uint32_t *)arg1; + break; + case 8: + u64 = *(uint64_t *)arg1; + break; + default: + printf("Unexpected size %d for sysctl leaf \"%s\"\n", + arg2, oidp->oid_name); + return (EDOOFUS); + } + + u32 = u64; +#ifdef SCTL_MASK32 + sc32 = (req->oldlen == sizeof(uint32_t) && + + arg2 == sizeof(u_long) && + u32 == u64 && + (req->flags & SCTL_MASK32) != 0); +#else + sc32 = 0; +#endif + if (sc32 || arg2 <= sizeof(uint32_t)) + error = SYSCTL_OUT(req, &u32, sizeof(u32)); + else + error = SYSCTL_OUT(req, &u64, sizeof(u64)); + + if (error != 0 || req->newptr == NULL) + return (error); + +#ifdef SCTL_MASK32 + sc32 = (req->newlen == sizeof(uint32_t) && + arg2 == sizeof(u_long) && + (req->flags & SCTL_MASK32) != 0); +#endif + if (sc32 || arg2 <= sizeof(uint32_t)) { + error = SYSCTL_IN(req, &u32, sizeof(u32)); + u64 = u32; + } else + error = SYSCTL_IN(req, &u64, sizeof(u64)); + if (error != 0) + return (error); + switch (arg2) { + case 1: + if (u64 > UINT8_MAX) + return (ERANGE); + *(uint8_t *)arg1 = (uint8_t)u64; + break; + case 2: + if (u64 > UINT16_MAX) + return (ERANGE); + *(uint16_t *)arg1 = (uint16_t)u64; + break; + case 4: + if (u64 > UINT32_MAX) + return (ERANGE); + *(uint32_t *)arg1 = (uint32_t)u64; + break; + case 8: + *(uint64_t *)arg1 = u64; + break; + } + return (0); +} + /* * Transfer functions to/from kernel space. * XXX: rather untested at this point diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index aeb41fd..50db678 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -180,6 +180,8 @@ int sysctl_handle_quad(SYSCTL_HANDLER_ARGS); int sysctl_handle_intptr(SYSCTL_HANDLER_ARGS); int sysctl_handle_string(SYSCTL_HANDLER_ARGS); int sysctl_handle_opaque(SYSCTL_HANDLER_ARGS); +int sysctl_handle_sscalar(SYSCTL_HANDLER_ARGS); +int sysctl_handle_uscalar(SYSCTL_HANDLER_ARGS); int sysctl_dpcpu_int(SYSCTL_HANDLER_ARGS); int sysctl_dpcpu_long(SYSCTL_HANDLER_ARGS); @@ -443,6 +445,15 @@ SYSCTL_ALLOWED_TYPES(XINT64, uint64_t *a; int64_t *b; sysctl_add_oid(ctx, parent, nbr, name, (access), \ ptr, arg, handler, fmt, __DESCR(descr)) +#define __issigned(var) ((__typeof(var))-1 < 0) + +#define SYSCTL_ADD_SCALAR(ctx, parent, name, access, ptr, descr) do { \ + CTASSERT(sizeof(*(ptr)) == 1 || sizeof(*(ptr)) == 2 || \ + sizeof(*(ptr)) == 4 || sizeof(*(ptr)) == 8); \ + sysctl_add_scalar(ctx, parent, name, access, ptr, \ + sizeof(*(ptr)), __issigned(*(ptr)), __DESCR(descr)); \ +} while (0) + /* * A macro to generate a read-only sysctl to indicate the presense of optional * kernel features. @@ -790,6 +801,10 @@ struct sysctl_oid *sysctl_add_oid(struct sysctl_ctx_list *clist, int kind, void *arg1, int arg2, int (*handler) (SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr); +void sysctl_add_scalar(struct sysctl_ctx_list *clist, + struct sysctl_oid_list *parent, const char *name, + int kind, void *arg1, size_t size, int sign, + const char *descr); void sysctl_rename_oid(struct sysctl_oid *oidp, const char *name); int sysctl_move_oid(struct sysctl_oid *oidp, struct sysctl_oid_list *parent);