Current section
Files
Jump to
Current section
Files
lib/exnotify.ex
defmodule Exnotify do
@moduledoc """
Documentation for `Exnotify`.
"""
use GenServer
require Unifex.CNode
defstruct [:cnode, :watcher, watch_descriptors: %{}]
@doc """
Start the GenServer.
## Options
- `:name` - The name for locally registered as. By default is `Exnotify`
- `:watcher` - The pid of watcher events. By default is pid of caller
"""
def start_link(opts \\ []) do
name = opts[:name] || __MODULE__
watcher = opts[:watcher] || self()
GenServer.start_link(__MODULE__, watcher, name: name)
end
@doc """
Watch for events on directory or file.
"""
@spec add_watch(
name :: GenServer.name(),
pathname :: String.t(),
events :: [
:notify_in_access
| :notify_in_attrib
| :notify_in_close_write
| :notify_in_close_nowrite
| :notify_in_create
| :notify_in_delete
| :notify_in_delete_self
| :notify_in_modify
| :notify_in_moved_from
| :notify_in_moved_to
| :notify_in_open
| :notify_in_move
],
opts :: []
) :: :ok | {:error, any()}
def add_watch(name, pathname, events \\ [:notify_in_create], opts \\ []) do
GenServer.call(name, {:add_watch, pathname, events, opts})
end
@impl true
def init(watcher) do
case Unifex.CNode.start_link(:inotify) do
{:ok, cnode} ->
case call(cnode, :init, []) do
:ok ->
Process.flag(:trap_exit, true)
{:ok, %Exnotify{cnode: cnode, watcher: watcher}}
_ ->
{:stop, :cnode}
end
_ ->
{:stop, :cnode}
end
end
@impl true
def handle_call({:add_watch, pathname, events, _opts}, _from, state) do
case call(state.cnode, :ex_inotify_add_watch, [pathname, events, 0]) do
{:ok, wd} ->
watch_descriptors = Map.put(state.watch_descriptors, pathname, wd)
{:reply, :ok, %{state | watch_descriptors: watch_descriptors}}
{:error, errno} ->
{:reply, {:error, errno}, state}
end
end
@impl true
def handle_info({:inotify_event, _, _, _} = event, state) do
send(state.watcher, event)
{:noreply, state}
end
@impl true
def handle_info({:EXIT, _from, _reason}, state) do
{:stop, :cnode, state}
end
@impl true
def terminate(:cnode, _state) do
{:shutdown, :cnode}
end
@impl true
def terminate(reason, state) do
for {_pathname, wd} <- state.watch_descriptors do
call(state.cnode, :ex_inotify_rm_watch, [wd])
end
reason
end
defp call(cnode, fun, args) when is_list(args) do
apply(Unifex.CNode, :call, [cnode, fun, args])
end
end