/elixir_posix

An Erlang NIF exposing additional POSIX features to your code

Primary LanguageElixir

System.POSIX

The posix Hex package gives the Erlang Runtime System access to POSIX features of the build environment.

The API of the posix library is portable, using Erlang atoms rather than OS-dependent magic constants. However, a release built using posix is not portable: a NIF is generated which "burns in" the properties of the build environment.

The posix package is thus best suited for use in SaaS server software that does not require redistribution.

Feature: sigaction(2) control

System.POSIX.SignalListener allows your Elixir release to modify the behavior of ERTS in response to POSIX signals. You can use this to:

  • refresh configuration on SIGHUP, like a regular daemon;

  • shut down gracefully on SIGINT;

  • allow users to poll for progress information using SIGINFO (Ctrl+T), like dd(1)

Usage

In your config.exs, specify one or more signal-handling modules:

config :posix, :signals,
  handlers: [HandlerFoo, HandlerBar]

System.POSIX.SignalListener is implemented as a GenEvent server; the modules you register should look like GenEvent handlers, and should just react to the signals they care about:

defmodule System.GracefulShutdownHandler do
  use GenEvent

  # handle SIGTERM -- the default signal from kill(1)
  def handle_event({:caught, :term}, state) do
    :init.stop
    {:ok, state}
  end
  def handle_event(_, state), do: {:ok, state}
end

By default, the set of signals System.POSIX.Signal registers for is [:info, :winch, :term, :hup, :usr1, :usr2]. You can define your own set:

config :posix, :signals,
  listen: [:pipe, :alrm, :urg, :abrt]

Don't be surprised, though, if overriding signals ERTS expected to catch itself causes strange behavior. The default set is safe; other signals may not be.

Feature: errno.h mappings

System.POSIX.Errno gives your Elixir programs (portable!) access to the constants from errno.h, and to the strerror(3) function. This enables you to:

  1. understand the exit statuses of spawned programs, and

  2. use :erlang.halt/1 to tell other POSIX programs why Erlang died.

Usage

Interpreting exit codes:

import System.POSIX.Errno

too_many_args = "1234567890" |> List.duplicate(100_000)

{_, exit_code} = System.cmd("true", too_many_args)

IO.puts ["Process failed: ", errno(exit_code).description]
# => Process failed: Argument list too long

Emitting exit codes:

# something terrible has happened to a process we're doing IPC with!
errno(:EPIPE).code |> :erlang.halt

Installation

System.POSIX is implemented as a NIF written in Rust. To compile this package, you therefore need Rust to be available in your build environment. Use rustup to install.

Presuming a Rust installation, just add posix to your list of dependencies in mix.exs:

def deps do
  [{:posix, "~> 0.1.0"}]
end