# `Linx.Sysctl.Native`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/sysctl/native.ex#L1)

NIF binding for `Linx.Sysctl`'s cross-namespace verbs. Loads
`priv/linx_sysctl.so` (built by the `:linx_sysctl` Mix compiler).

Production callers should not use this module directly — go through
`Linx.Sysctl`, which handles key validation, value rendering, the
`:in` option, and `%Linx.Sysctl.Error{}` wrapping.

## The `ns_paths` argument

Each fallible function takes an `ns_paths` argument: a list of
binaries naming `/proc/<pid>/ns/<kind>` files (or any other
pinned-namespace file). The NIF opens every fd FIRST in the BEAM's
own namespace (so the paths resolve correctly), then spawns a
throwaway pthread that `unshare(CLONE_FS)`s + `setns(2)`s each fd
in order, then performs the I/O, then exits the thread. The BEAM's
own scheduler threads never enter the target namespaces.

An empty list (`[]`) skips the setns dance — useful for testing
the NIF in isolation, though the public `Linx.Sysctl` verbs use
the pure-Elixir host path in that case.

## Error shape

Every fallible function returns `:ok` / `{:ok, ...}` or
`{:error, {stage_atom, errno_atom_or_int}}`. Stages:

  * `:read` / `:write` / `:list` — the I/O itself failed.
  * `:open_ns` — couldn't open one of the namespace files
    (target process gone, BEAM lacks read access).
  * `:unshare` — `unshare(CLONE_FS)` failed (vanishingly rare).
  * `:setns` — couldn't enter one of the target namespaces
    (typically `EPERM` in the rootless case).
  * `:thread` — couldn't create the worker thread.

# `error`

```elixir
@type error() :: {:error, {stage(), atom() | pos_integer()}}
```

# `stage`

```elixir
@type stage() :: :read | :write | :list | :open_ns | :unshare | :setns | :thread
```

Native error shape: `{stage_atom, errno_atom_or_int}`.

# `list_in_ns`

```elixir
@spec list_in_ns(binary(), [binary()]) :: {:ok, [{binary(), binary()}]} | error()
```

Recursively walks the directory tree rooted at `root` from inside
the target namespace stack named by `ns_paths`, returning every
readable regular file as a `{path_binary, value_binary}` tuple.

Unreadable directories and unreadable files are silently skipped
(matches the pure-Elixir walker behaviour). A non-existent or
non-directory `root` returns `{:error, {:list, errno}}`.

The returned list is in walker-discovery order; callers sort by
whatever key they want.

# `read_in_ns`

```elixir
@spec read_in_ns(binary(), [binary()]) :: {:ok, binary()} | error()
```

Reads the procfs file at `path` from inside the target namespace
stack named by `ns_paths`.

Returns `{:ok, binary}` with the file's untrimmed bytes (callers
trim) or `{:error, {stage, errno}}`.

# `version`

```elixir
@spec version() :: charlist()
```

Returns the NIF identifier string.

# `write_in_ns`

```elixir
@spec write_in_ns(binary(), binary(), [binary()]) :: :ok | error()
```

Writes `data` to the procfs file at `path` from inside the target
namespace stack named by `ns_paths`. One write call; no `\n` is
appended.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
