Running code when a script exits

I have investigated a bit the solution with the signal handler.

Erlang executes :init.stop() on SIGTERM by default. That means that a simple kill PID shuts the thing down orderly.

However, you need to figure out the PID of the process, and open a different terminal to execute the command. Besides, custom code passed to System.at_exit/1 is not invoked by the event handler.

Ctrl-C sends SIGINT. The only shortcut that I’ve seen sends a different signal is Ctrl-\, which sends SIGQUIT. So, I am using this event handler:

defmodule QuitScript do
  @behaviour :gen_event

  def init(_), do: {:ok, nil}
  def handle_event(:sigquit, _state), do: :remove_handler
  def handle_event(signal, state), do: :erl_signal_handler.handle_event(signal, state)
  def handle_call(_, state), do: {:ok, :ok, state}

  def terminate(_reason, _state) do
    IO.puts("My exit code")
    System.stop()
  end
end

:gen_event.swap_handler(:erl_signal_server, {:erl_signal_handler, []}, {QuitScript, []})

Notice that the terminate/2 callback allows you to run custom code on exit.

If you throw that to a script and execute it for example like this

mix run --no-halt foo.exs

Ctrl-C still behaves as always, and additionally Ctrl-\ shuts the thing down orderly (you only see ^\ echoed in the terminal, not too bad).