SYSCTL(9)

πŸ’– SYSCTL πŸ’–

okay bestie, gather round 🫢 this is the SYSCTL(9) page from the Kernel Developer's Manual, and it is literally the blueprint for every sysctl you'll ever build. it's a long one so we're doing it in parts β€” buckle up, hydrate, let's go ✨


🌸 NAME 🌸

no cap, this page is a whole friend group. it covers all of these cuties:

SYSCTL_DECL, SYSCTL_ADD_INT, SYSCTL_ADD_LONG, SYSCTL_ADD_NODE, SYSCTL_ADD_NODE_WITH_LABEL, SYSCTL_ADD_OPAQUE, SYSCTL_ADD_PROC, SYSCTL_ADD_QUAD, SYSCTL_ADD_ROOT_NODE, SYSCTL_ADD_S8, SYSCTL_ADD_S16, SYSCTL_ADD_S32, SYSCTL_ADD_S64, SYSCTL_ADD_STRING, SYSCTL_ADD_CONST_STRING, SYSCTL_ADD_STRUCT, SYSCTL_ADD_TIMEVAL_SEC, SYSCTL_ADD_U8, SYSCTL_ADD_U16, SYSCTL_ADD_U32, SYSCTL_ADD_U64, SYSCTL_ADD_UAUTO, SYSCTL_ADD_UINT, SYSCTL_ADD_ULONG, SYSCTL_ADD_UQUAD, SYSCTL_CHILDREN, SYSCTL_STATIC_CHILDREN, SYSCTL_NODE_CHILDREN, SYSCTL_PARENT, SYSCTL_INT, SYSCTL_INT_WITH_LABEL, SYSCTL_LONG, sysctl_msec_to_ticks, SYSCTL_NODE, SYSCTL_NODE_WITH_LABEL, SYSCTL_OPAQUE, SYSCTL_PROC, SYSCTL_QUAD, SYSCTL_ROOT_NODE, SYSCTL_S8, SYSCTL_S16, SYSCTL_S32, SYSCTL_S64, SYSCTL_STRING, SYSCTL_CONST_STRING, SYSCTL_STRUCT, SYSCTL_TIMEVAL_SEC, SYSCTL_U8, SYSCTL_U16, SYSCTL_U32, SYSCTL_U64, SYSCTL_UINT, SYSCTL_ULONG, SYSCTL_UQUAD β€” Dynamic and static sysctl MIB creation functions πŸ’…


✨ SYNOPSIS ✨

first things first, you gotta pull in the headers:

#include <sys/param.h>
#include <sys/sysctl.h>

then here's the whole menu of prototypes, exactly as they come, no edits, no cap πŸ“œ

SYSCTL_DECL(name);

struct sysctl_oid *
SYSCTL_ADD_INT(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int *ptr, int val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_LONG(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, long *ptr, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_NODE(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int (*handler)(SYSCTL_HANDLER_ARGS),
    const char *descr);

struct sysctl_oid *
SYSCTL_ADD_NODE_WITH_LABEL(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int (*handler)(SYSCTL_HANDLER_ARGS),
    const char *descr, const char *label);

struct sysctl_oid *
SYSCTL_ADD_OPAQUE(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, void *ptr, intptr_t len, const char *format,
    const char *descr);

struct sysctl_oid *
SYSCTL_ADD_PROC(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, void *arg1, intptr_t arg2,
    int (*handler) (SYSCTL_HANDLER_ARGS), const char *format,
    const char *descr);

struct sysctl_oid *
SYSCTL_ADD_QUAD(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int64_t *ptr, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_ROOT_NODE(struct sysctl_ctx_list *ctx, int number,
    const char *name, int ctlflags, int (*handler)(SYSCTL_HANDLER_ARGS),
    const char *descr);

struct sysctl_oid *
SYSCTL_ADD_S8(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int8_t *ptr, int8_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_S16(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int16_t *ptr, int16_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_S32(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int32_t *ptr, int32_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_S64(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, int64_t *ptr, int64_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_STRING(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, char *ptr, intptr_t len, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_CONST_STRING(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, const char *ptr, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_STRUCT(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, void *ptr, struct_type, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_TIMEVAL_SEC(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, struct timeval *ptr, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_U8(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, uint8_t *ptr, uint8_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_U16(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, uint16_t *ptr, uint16_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_U32(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, uint32_t *ptr, uint32_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_U64(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, uint64_t *ptr, uint64_t val, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_UINT(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, unsigned int *ptr, unsigned int val,
    const char *descr);

struct sysctl_oid *
SYSCTL_ADD_ULONG(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, unsigned long *ptr, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_UQUAD(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, uint64_t *ptr, const char *descr);

struct sysctl_oid *
SYSCTL_ADD_UAUTO(struct sysctl_ctx_list *ctx,
    struct sysctl_oid_list *parent, int number, const char *name,
    int ctlflags, void *ptr, const char *descr);

struct sysctl_oid_list *
SYSCTL_CHILDREN(struct sysctl_oid *oidp);

struct sysctl_oid_list *
SYSCTL_STATIC_CHILDREN(struct sysctl_oid_list OID_NAME);

struct sysctl_oid_list *
SYSCTL_NODE_CHILDREN(parent, name);

struct sysctl_oid *
SYSCTL_PARENT(struct sysctl_oid *oid);

SYSCTL_INT(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_INT_WITH_LABEL(parent, number, name, ctlflags, ptr, val, descr,
    label);

SYSCTL_LONG(parent, number, name, ctlflags, ptr, val, descr);

int
sysctl_msec_to_ticks(SYSCTL_HANDLER_ARGS);

SYSCTL_NODE(parent, number, name, ctlflags, handler, descr);

SYSCTL_NODE_WITH_LABEL(parent, number, name, ctlflags, handler, descr,
    label);

SYSCTL_OPAQUE(parent, number, name, ctlflags, ptr, len, format, descr);

SYSCTL_PROC(parent, number, name, ctlflags, arg1, arg2, handler, format,
    descr);

SYSCTL_QUAD(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_ROOT_NODE(number, name, ctlflags, handler, descr);

SYSCTL_S8(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_S16(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_S32(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_S64(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_STRING(parent, number, name, ctlflags, arg, len, descr);

SYSCTL_CONST_STRING(parent, number, name, ctlflags, arg, descr);

SYSCTL_STRUCT(parent, number, name, ctlflags, ptr, struct_type, descr);

SYSCTL_TIMEVAL_SEC(parent, number, name, ctlflags, ptr, descr);

SYSCTL_U8(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_U16(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_U32(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_U64(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_UINT(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_ULONG(parent, number, name, ctlflags, ptr, val, descr);

SYSCTL_UQUAD(parent, number, name, ctlflags, ptr, val, descr);

πŸ¦‹ DESCRIPTION πŸ¦‹

so here's the vibe πŸ’œ the SYSCTL kernel interface is what lets you create sysctl(8) MIB entries β€” either dynamically or statically. it's giving main character energy for kernel config, fr fr.

and the glow-up? ✨ all static sysctls are automatically destroyed when the module which they are part of is unloaded. no cleanup code, no manual teardown, it just handles itself. obsessed. 😌

also, most top level categories are created statically and are available to all kernel code and its modules β€” so they're just there for everyone, like the group chat that never dies. πŸ‘‘

ok but for real, this is only part 1 and there's so much more coming β€” we haven't even gotten to the OPTIONS yet bestie, stay tuned πŸ’–

✨ DESCRIPTION OF ARGUMENTS ✨

ok so before we touch literally anything, bestie, we gotta meet the cast πŸ’… every single one of these args shows up all over this page, so let's get acquainted.

  • ctx β€” your pointer to the sysctl context, or NULL if you're rolling with no context at all. Wanna make a fresh one? Go peek at sysctl_ctx_init(9). Now listen closely: programmers are strongly advised to use contexts to keep their dynamic OIDs organized, because the second a context gets destroyed, every sysctl that belongs to it goes down with it. That makes your cleanup code so much simpler. If you skip this, you gotta delete every single OID you created at module unload. no cap. πŸ™ˆ
  • parent β€” a pointer to a struct sysctl_oid_list, which is the head of the parent's list of children. Grab it with the SYSCTL_STATIC_CHILDREN() macro for static sysctls, or SYSCTL_CHILDREN() for dynamic ones. There's also SYSCTL_PARENT() if you need to get the parent of an OID β€” heads up, that macro hands you NULL when there's no parent. πŸ¦‹
  • number β€” the OID number that's getting assigned to this OID. Like, in almost literally every case you want this set to OID_AUTO, which just means "give me the next available OID number." iconic.
  • name β€” the name of the OID. Your shiny new OID keeps a copy of it. πŸŽ€
  • ctlflags β€” a bit mask of sysctl control flags. There's a whole section below about all the control flags, so go read that. πŸ‘‘
  • arg1 β€” first callback argument for procedure sysctls.
  • arg2 β€” second callback argument for procedure sysctls.
  • len β€” the length of the data that ptr points at. Special case alert: for string type OIDs, a length of zero means strlen(3) gets used to figure out the string length every single time the OID is accessed. every. single. time. 🌸
  • ptr β€” pointer to your sysctl variable or string data. For sysctl values, this pointer can be SYSCTL_NULL_XXX_PTR, which means the OID is read-only and the returned value comes from the val argument instead.
  • val β€” if ptr is SYSCTL_NULL_XXX_PTR, this gives the constant value this OID returns. Otherwise? Not used. that's it, that's the whole job. πŸ’€
  • struct_type β€” name of the structure type.
  • handler β€” a pointer to the function that handles read and write requests for this OID. There's a bunch of standard handlers supporting operations on nodes, integers, strings, and opaque objects. Wanna go custom? Define your own with the SYSCTL_PROC() macro or the SYSCTL_ADD_PROC() function. main character energy. πŸ”₯
  • format β€” a pointer to a string that describes the OID's format in a symbolic way. This is a hint that sysctl(8) uses to apply the right data formatting for display purposes. ✨

Here's the current lineup of formats:

  • N β€” node
  • A β€” char *
  • I β€” int
  • IK[n] β€” temperature in Kelvin, multiplied by an optional single digit power of ten scaling factor: 1 (the default) gives deciKelvin, 0 gives Kelvin, and 3 gives milliKelvin
  • IU β€” unsigned int
  • L β€” long
  • LU β€” unsigned long
  • Q β€” quad_t
  • QU β€” u_quad_t
  • S,TYPE β€” struct TYPE structures
  • descr β€” a pointer to a textual description of the OID.
  • label β€” a pointer to an aggregation label for this component of the OID. Want to export your sysctl data to monitoring systems that do aggregations through labels (hi, Prometheus πŸ‘‹)? This argument lets you attach a label name to an OID. The label is a hint that this component's name shouldn't be part of the metric's name β€” instead it gets attached to the metric as a label.
Only put labels on siblings that are structurally similar and encode the same type of value. If they don't, aggregation is literally of no use. πŸ™…β€β™€οΈ

🌈 NODE VALUE TYPES 🌈

Most of the macros and functions that build sysctl nodes export either a read-only constant or an in-kernel variable, and the type of that thing matches the type of the node's value. Like, SYSCTL_INT() reports the raw value of an associated variable of type int. simple.

But plot twist πŸ’« β€” nodes can also export a value that's a translation of some internal representation.

The sysctl_msec_to_ticks() handler works with SYSCTL_PROC() or SYSCTL_ADD_PROC() to export a millisecond time interval. When you use this handler, the arg2 parameter points to an in-kernel variable of type int that stores a tick count suitable for functions like tsleep(9). When it's reporting the node's value, sysctl_msec_to_ticks() converts that value into milliseconds. Same deal in reverse, bestie: it accepts new values in milliseconds and stores the equivalent tick value into *arg2. 🫢

The SYSCTL_ADD_TIMEVAL_SEC() function and the SYSCTL_TIMEVAL_SEC() macro create nodes that export an in-kernel variable of type struct timeval. Heads up though β€” these nodes do not export the full value of that associated structure. Instead they export a count in seconds as a simple integer, stored in the tv_sec field of the associated variable. These are meant for variables holding a non-negative interval rather than an absolute time. Because of that, they reject any attempt to store a negative value. periodt. 🚫


🌳 CREATING ROOT NODES 🌳

Sysctl MIBs (aka OIDs) live in a hierarchical tree. The nodes all the way at the bottom of that tree are the root nodes, and they have no parent OID. To build those bottom-of-the-tree nodes you use the SYSCTL_ROOT_NODE() macro or the SYSCTL_ADD_ROOT_NODE() function. 🌱

By default, all static sysctl node OIDs are global, which means you need a SYSCTL_DECL() statement before their SYSCTL_NODE() definition statement β€” usually in a so-called header file. iykyk. πŸ‘‘


πŸŽ€ CREATING SYSCTL STRINGS πŸŽ€

Zero-terminated character string sysctls get created with either the SYSCTL_STRING() macro or the SYSCTL_ADD_STRING() function. If the len argument is zero, the string length gets computed on every access to the OID using strlen(3). Wanna add a sysctl for a constant string? Use the SYSCTL_CONST_STRING() macro or the SYSCTL_ADD_CONST_STRING() function. πŸ’–


πŸ¦‹ CREATING OPAQUE SYSCTLS πŸ¦‹

The SYSCTL_OPAQUE() or SYSCTL_STRUCT() macros β€” or the SYSCTL_ADD_OPAQUE() or SYSCTL_ADD_STRUCT() functions β€” create an OID that handles any chunk of data, sized by the len argument and pointed at by the ptr argument. When you use the structure version, the type gets encoded as part of the created sysctl. cute. 🫢


πŸ’œ CREATING CUSTOM SYSCTLS πŸ’œ

The SYSCTL_PROC() macro and the SYSCTL_ADD_PROC() function create OIDs with whatever handler function you specify. That handler is responsible for handling all read and write requests to the OID. This OID type is especially handy when the kernel data isn't easily accessible, or when it needs to be processed before it gets exported. chef's kiss. πŸ”₯


⭐ CREATING A STATIC SYSCTL ⭐

Static sysctls get declared using one of these macros:

SYSCTL_INT(), SYSCTL_INT_WITH_LABEL(), SYSCTL_LONG(), SYSCTL_NODE(), SYSCTL_NODE_WITH_LABEL(), SYSCTL_OPAQUE(), SYSCTL_PROC(), SYSCTL_QUAD(), SYSCTL_ROOT_NODE(), SYSCTL_S8(), SYSCTL_S16(), SYSCTL_S32(), SYSCTL_S64(), SYSCTL_STRING(), SYSCTL_CONST_STRING(), SYSCTL_STRUCT(), SYSCTL_TIMEVAL_SEC(), SYSCTL_U8(), SYSCTL_U16(), SYSCTL_U32(), SYSCTL_U64(), SYSCTL_UINT(), SYSCTL_ULONG() or SYSCTL_UQUAD().


πŸ’… CREATING A DYNAMIC SYSCTL πŸ’…

Dynamic nodes get created using one of these functions:

SYSCTL_ADD_INT(), SYSCTL_ADD_LONG(), SYSCTL_ADD_NODE(), SYSCTL_ADD_NODE_WITH_LABEL(), SYSCTL_ADD_OPAQUE(), SYSCTL_ADD_PROC(), SYSCTL_ADD_QUAD(), SYSCTL_ADD_ROOT_NODE(), SYSCTL_ADD_S8(), SYSCTL_ADD_S16(), SYSCTL_ADD_S32(), SYSCTL_ADD_S64(), SYSCTL_ADD_STRING(), SYSCTL_ADD_CONST_STRING(), SYSCTL_ADD_STRUCT(), SYSCTL_ADD_TIMEVAL_SEC(), SYSCTL_ADD_U8(), SYSCTL_ADD_U16(), SYSCTL_ADD_U32(), SYSCTL_ADD_U64(), SYSCTL_ADD_UAUTO(), SYSCTL_ADD_UINT(), SYSCTL_ADD_ULONG(), or SYSCTL_UQUAD().

Wanna destroy a dynamically created OID? Go read sysctl_remove_oid(9) or sysctl_ctx_free(9) for all the deets. ✨

✨ CONTROL FLAGS ✨

okay so real talk πŸ’– for most of the functions and macros we've been yapping about up there, you don't have to declare a type as part of the access flags. but β€” and this is the part that matters β€” when you're declaring a sysctl that's implemented by a function, putting a type in the access mask is literally required. no type, no service, bestie. πŸ’…

so here's the type menu:

  • CTLTYPE_NODE β€” this is a node that's meant to be a parent for other nodes. the mom friend of the tree 🌳
  • CTLTYPE_INT β€” a signed integer.
  • CTLTYPE_STRING β€” a nul-terminated string, stored in a character array.
  • CTLTYPE_S8 β€” an 8-bit signed integer.
  • CTLTYPE_S16 β€” a 16-bit signed integer.
  • CTLTYPE_S32 β€” a 32-bit signed integer.
  • CTLTYPE_S64 β€” a 64-bit signed integer.
  • CTLTYPE_OPAQUE β€” an opaque data structure. mysterious. iconic. πŸ«₯
  • CTLTYPE_STRUCT β€” alias for CTLTYPE_OPAQUE. same vibe, different name.
  • CTLTYPE_U8 β€” an 8-bit unsigned integer.
  • CTLTYPE_U16 β€” a 16-bit unsigned integer.
  • CTLTYPE_U32 β€” a 32-bit unsigned integer.
  • CTLTYPE_U64 β€” a 64-bit unsigned integer.
  • CTLTYPE_UINT β€” an unsigned integer.
  • CTLTYPE_LONG β€” a signed long.
  • CTLTYPE_ULONG β€” an unsigned long.

so like, every sysctl type except for new node declarations needs one of these next flags set, because they tell you the read/write disposition of the sysctl. it's giving "pick a lane" energy:

  • CTLFLAG_RD β€” this is a read-only sysctl. look, don't touch. πŸ‘€
  • CTLFLAG_RDTUN β€” a read-only sysctl and tunable, and the system tries to fetch it once from the system environment early during module load or system boot.
  • CTLFLAG_WR β€” this is a writable sysctl.
  • CTLFLAG_RW β€” readable and writable. the full experience ✨
  • CTLFLAG_RWTUN β€” a readable and writeable sysctl and tunable, and the system tries to fetch it once from the system environment early during module load or system boot.
  • CTLFLAG_NOFETCH β€” if your node got marked as a tunable using the CTLFLAG_[XX]TUN, this flag stops the initial value from being fetched out of the system environment. and ngl, you should basically only reach for this in very early low level system setup code β€” not in regular drivers and modules, bestie, don't be bold.
  • CTLFLAG_MPSAFE β€” this sysctl(9) handler is MP safe, so do not grab Giant around calls to it. this one should only be used for SYSCTL_PROC() entries. πŸ›‘οΈ

and on top of all that, any of these optional flags can also be specified:

  • CTLFLAG_ANYBODY β€” any user or process can write to this sysctl.
  • CTLFLAG_CAPRD β€” a process in capability mode can read from this sysctl.
  • CTLFLAG_CAPWR β€” a process in capability mode can write to this sysctl.
  • CTLFLAG_SECURE β€” this sysctl can be written to only if the effective securelevel of the process is ≀ 0.
  • CTLFLAG_PRISON β€” this sysctl can be written to by processes in jail(2).
  • CTLFLAG_SKIP β€” when iterating the sysctl name space, do not list this sysctl. stealth mode πŸ™ˆ
  • CTLFLAG_TUN β€” advisory flag that a system tunable also exists for this variable. the initial sysctl value is tried fetched once from the system environment early during module load or system boot.
  • CTLFLAG_DYN β€” dynamically created OIDs automatically get this flag set. it's automatic, you don't gotta do anything πŸ’…
  • CTLFLAG_VNET β€” OID references a VIMAGE-enabled variable.

🌸 EXAMPLES 🌸

a lil sample of SYSCTL_DECL() declaring the security sysctl tree so new nodes can use it:

SYSCTL_DECL(_security);

and here come the integer, opaque, string, and procedure sysctl examples. buckle up πŸŽ€

/*
 * Example of a constant integer value.  Notice that the control
 * flags are CTLFLAG_RD, the variable pointer is SYSCTL_NULL_INT_PTR,
 * and the value is declared.
 */
SYSCTL_INT(_debug_sizeof, OID_AUTO, bio, CTLFLAG_RD, SYSCTL_NULL_INT_PTR,
    sizeof(struct bio), "sizeof(struct bio)");

/*
 * Example of a variable integer value.  Notice that the control
 * flags are CTLFLAG_RW, the variable pointer is set, and the
 * value is 0.
 */
static int      doingcache = 1;         /* 1 => enable the cache */
SYSCTL_INT(_debug, OID_AUTO, vfscache, CTLFLAG_RW, &doingcache, 0,
    "Enable name cache");

/*
 * Example of a variable string value.  Notice that the control
 * flags are CTLFLAG_RW, that the variable pointer and string
 * size are set.  Unlike newer sysctls, this older sysctl uses a
 * static oid number.
 */
char kernelname[MAXPATHLEN] = "/kernel";        /* XXX bloat */
SYSCTL_STRING(_kern, KERN_BOOTFILE, bootfile, CTLFLAG_RW,
    kernelname, sizeof(kernelname), "Name of kernel file booted");

/*
 * Example of an opaque data type exported by sysctl.  Notice that
 * the variable pointer and size are provided, as well as a format
 * string for sysctl(8).
 */
static l_fp pps_freq;   /* scaled frequency offset (ns/s) */
SYSCTL_OPAQUE(_kern_ntp_pll, OID_AUTO, pps_freq, CTLFLAG_RD,
    &pps_freq, sizeof(pps_freq), "I", "");

/*
 * Example of a procedure based sysctl exporting string
 * information.  Notice that the data type is declared, the NULL
 * variable pointer and 0 size, the function pointer, and the
 * format string for sysctl(8).
 */
SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING |
    CTLFLAG_RW, NULL, 0, sysctl_kern_timecounter_hardware, "A",
    "");

and here's how you'd create a brand new top-level category and hook another subtree onto an existing static node. heads up though: this example doesn't use contexts, which means you're stuck doing tedious management of all the intermediate oids, because you have to free them later. it's giving unpaid internship 😭

#include <sys/sysctl.h>
 ...
/*
 * Need to preserve pointers to newly created subtrees,
 * to be able to free them later:
 */
static struct sysctl_oid *root1;
static struct sysctl_oid *root2;
static struct sysctl_oid *oidp;
static int a_int;
static char *string = "dynamic sysctl";
 ...

root1 = SYSCTL_ADD_ROOT_NODE(NULL,
        OID_AUTO, "newtree", CTLFLAG_RW, 0, "new top level tree");
oidp = SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(root1),
        OID_AUTO, "newint", CTLFLAG_RW, &a_int, 0, "new int leaf");
 ...
root2 = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_debug),
        OID_AUTO, "newtree", CTLFLAG_RW, 0, "new tree under debug");
oidp = SYSCTL_ADD_STRING(NULL, SYSCTL_CHILDREN(root2),
        OID_AUTO, "newstring", CTLFLAG_RD, string, 0, "new string leaf");

this example creates these subtrees right here:

debug.newtree.newstring
newtree.newint
⚠️ bestie, care should be taken to free all OIDs once they are no longer needed! do not leave them lying around, that's not the vibe. 🚨

πŸ’œ SYSCTL NAMING πŸ’œ

okay so when you're adding, modifying, or removing sysctl names, you need to understand something: these interfaces may be used by users, libraries, applications, or documentation (yes, like published books πŸ“š), which means they are implicitly published application interfaces. and just like any other application interface, you have to be careful not to break existing applications β€” and you gotta think about the future use of new name spaces so you don't end up needing to rename or remove interfaces that people might depend on later. no pressure. πŸ™ƒ

the semantics you pick for a new sysctl should be as clear as possible, and the name of the sysctl must closely reflect its semantics. so yeah, the name deserves a fair amount of consideration. it should be short, but still representative of what the sysctl actually means. if the name is several words, separate them with underscore characters, like in compute_summary_at_mount. and underscore characters may be omitted only if the name is not more than two words, each being not longer than four characters, like in bootfile.

and for boolean sysctls? negative logic should be totally avoided. like, do not use names like no_foobar or foobar_disable. they're confusing and they lead to configuration errors. use positive logic instead: foobar, foobar_enable. manifest the yes, not the no ✨

a temporary sysctl node OID that should not be relied upon has to be designated as such with a leading underscore character in its name. for example: _dirty_hack. 🫣


πŸ‘‘ SEE ALSO πŸ‘‘


πŸ“œ HISTORY πŸ“œ

the sysctl(8) utility first appeared in 4.4BSD. an elder. a legend. 🫢


πŸ¦‹ AUTHORS πŸ¦‹

the sysctl implementation originally found in BSD has been extensively rewritten by Poul-Henning Kamp in order to add support for name lookups, name space iteration, and dynamic addition of MIB nodes. absolute icon behaviour. πŸ’…

this man page was written by Robert N. M. Watson.


πŸ”₯ SECURITY CONSIDERATIONS πŸ”₯

when you're creating new sysctls, pay careful attention to the security implications of the monitoring or management interface you're creating. most sysctls present in the kernel are read-only, or writable only by the superuser. and sysctls that export extensive information on system data structures and operation β€” especially those implemented using procedures β€” will want to implement access control to limit the undesired exposure of information about other processes, network connections, etc. be mindful, bestie. πŸ”

these top level sysctl name spaces are the ones commonly used:

  • compat β€” Compatibility layer information.
  • debug β€” Debugging information. Various name spaces exist under debug.
  • hw β€” Hardware and device driver information.
  • kern β€” Kernel behavior tuning; generally deprecated in favor of more specific name spaces.
  • machdep β€” Machine-dependent configuration parameters.
  • net β€” Network subsystem. Various protocols have name spaces under net.
  • regression β€” Regression test configuration and information.
  • security β€” Security and security-policy configuration and information.
  • sysctl β€” Reserved name space for the implementation of sysctl.
  • user β€” Configuration settings relating to user application behavior. Generally, configuring applications using kernel sysctls is discouraged.
  • vfs β€” Virtual file system configuration and information.
  • vm β€” Virtual memory subsystem configuration and information.

and that's the whole page, bestie!! πŸŽ€βœ¨ this one came from the Debian folks and it's dated June 11, 2019 β€” so like, vintage, but in a good way. now go forth and sysctl responsibly, my main character. mwah πŸ’–πŸ¦‹πŸ‘‘

πŸ“¦ where this page comes from