# `Forcola.Daemon`
[🔗](https://github.com/joshrotenberg/forcola/blob/v0.3.2/lib/forcola/daemon.ex#L1)

A long-running external server process under a supervision tree.

For processes that are supposed to outlive a single call (redis-server,
a dev proxy) but must never outlive their supervisor. The child runs in
its own process group; when this GenServer terminates for any reason,
including supervisor shutdown and owner crash, the shim kills the group
(SIGTERM, then SIGKILL after the kill grace) and the terminate blocks
until the shim confirms the group is dead. If the daemon process is
killed brutally (no terminate), the port closes, the shim sees stdin
EOF, and the group is killed anyway; the same path covers BEAM death.

    children = [
      {Forcola.Daemon,
       argv: ["redis-server", "--port", "6399"],
       name: MyApp.Redis}
    ]

Unlike bounded runs, a daemon has no `:timeout_ms`; its bound is its
supervisor. Passing `:timeout_ms` raises `ArgumentError`.

## Options

  * `:argv` - required, `[binary | args]` as in `Forcola.run/2`.
  * `:name` - optional GenServer registration name.
  * `:cd`, `:env`, `:merge_stderr` - as in `Forcola.run/2`.
  * `:user`, `:group` - run the child as a different user/group, as in
    `Forcola.run/2`. POSIX-only, a one-way drop, and requires a
    privileged shim; failures fail closed and surface as the daemon's
    normal spawn-error exit.
  * `:cgroup` - opt in to Linux cgroup v2 containment, as in
    `Forcola.run/2`. Linux only, requires a delegated cgroup v2 subtree
    (systemd `Delegate=yes` or `systemd-run --scope`), and falls back to
    the process-group kill with a warning elsewhere. A daemon is the
    typical place to want this: a long-running server that might spawn a
    helper which daemonizes away from the process group is reaped anyway
    on shutdown. Default `false`.
  * `:kill_grace_ms` - SIGTERM-to-SIGKILL grace, default `5_000`.
  * `:output` - where child output goes; see below. Default `:logger`.
  * `:log_output` - `Logger` level for `output: :logger`, default `:info`.
  * `:log_prefix` - string prepended to each logged line, default `""`.
  * `:ready` - optional zero-arity readiness check; see below.
  * `:ready_timeout_ms` - readiness deadline, default `5_000`.
  * `:ready_poll_ms` - readiness poll interval, default `100`.

## Output handling

  * `output: :logger` (default) - stdout and stderr are logged line by
    line at the `:log_output` level, prefixed with `:log_prefix`. A
    partial line held across frames is logged once its newline arrives
    (or at termination).
  * `output: fun` - a 2-arity function called as
    `fun.(:stdout | :stderr, chunk)` with raw chunks, from the daemon
    process.
  * `output: {:send, pid}` - `pid` receives
    `{Forcola.Daemon, daemon_pid, {:stdout | :stderr, chunk}}` messages.

With `merge_stderr: true` everything arrives as `:stdout`.

## Readiness

With `ready: fun`, `init` polls `fun.()` (every `:ready_poll_ms`) until
it returns a truthy value, so `start_link` and supervisor startup block
until the server accepts connections:

    {Forcola.Daemon,
     argv: ["redis-server", "--port", "6399"],
     ready: fn -> match?({:ok, _}, :gen_tcp.connect(~c"localhost", 6399, [])) end}

If the check does not pass within `:ready_timeout_ms`, or the child
exits first, the group is killed and `start_link` returns
`{:error, :ready_timeout}` or `{:error, {:exited_before_ready, reason}}`.

Without `:ready`, `start_link` returns as soon as the spawn request is
written to the shim: it succeeds without any proof that the shim or the
child is alive, and a failed spawn only surfaces later as a daemon
exit. Use `:ready` for any daemon whose availability matters.

## Exit and restart behavior

When the child exits on its own, the daemon stops: status 0 stops
`:normal`, a non-zero status stops `{:exit_status, n}`, and death by
signal stops `{:exit_signal, n}`. Under `restart: :permanent` any of
these restarts the daemon; under `:transient` only the abnormal ones do.

# `output`

```elixir
@type output() :: :logger | (:stdout | :stderr, binary() -&gt; any()) | {:send, pid()}
```

Where child output is routed.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a daemon running `opts[:argv]` under the shim.

See the module docs for options. Raises `ArgumentError` on invalid
options (missing `:argv`, a `:timeout_ms`, a bad `:output` shape).

---

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