defmodule GSMLG.Tor do
@moduledoc """
Documentation for `GSMLG.Tor`.
"""
alias GSMLG.Tor.Config
require Logger
use GenServer
@doc """
Start tor server at
- `127.0.0.1:9050`
- `[::1]:9050`
Accept accesss from `127.0.0.1/8` or `::1/128`
Start server with
```
GSMLG.Tor.start()
```
To start server, libevent must be installed.
"""
def start() do
GenServer.start(__MODULE__, [], name: __MODULE__)
end
def get_state() do
GenServer.call(__MODULE__, :get_state)
end
def stop() do
{:os_pid, pid} = get_state() |> Map.get(:port) |> Port.info(:os_pid)
{_, code} = System.cmd("kill", ["#{pid}"])
code
end
def start_link(_) do
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
end
def init(_init) do
state = %{port: nil}
# your trap_exit call should be here
Process.flag(:trap_exit, true)
{:ok, state, {:continue, :start_server}}
end
def handle_continue(:start_server, state) do
Logger.info("Init Tor Config...")
Config.init()
Logger.info("Staring Tor Server...")
port =
Port.open(
{:spawn_executable, cmd()},
[
{:args, ["-f", Config.torrc_file()]},
{:cd, Application.app_dir(:gsmlg_tor, "priv/tor")},
:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout
]
)
state = Map.put(state, :port, port)
{:noreply, state}
end
def handle_call(:get_state, _from, state) do
{:reply, state, state}
end
def handle_info({port, {:data, msg}}, state) do
Logger.debug("Tor #{inspect(port)}: #{msg}")
{:noreply, state}
end
def handle_info({port, {:exit_status, exit_status}}, state) do
Logger.info("Tor #{inspect(port)}: exit_status: #{exit_status}")
{:noreply, state}
end
# handle the trapped exit call
def handle_info({:EXIT, _from, reason}, state) do
Logger.info("Tor exit: #{inspect(reason)}")
cleanup(reason, state)
# see GenServer docs for other return types
{:stop, reason, state}
end
# handle termination
def terminate(reason, state) do
Logger.info("terminating")
cleanup(reason, state)
state
end
defp cleanup(_reason, state) do
case state |> Map.get(:port) |> Port.info(:os_pid) do
{:os_pid, pid} ->
{_, code} = System.cmd("kill", ["#{pid}"])
code
_ ->
0
end
end
defp cmd() do
Config.command_path()
end
end