aboutsummaryrefslogtreecommitdiff
path: root/src/er_xdg_pipe_menu_socket.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/er_xdg_pipe_menu_socket.erl')
-rw-r--r--src/er_xdg_pipe_menu_socket.erl66
1 files changed, 66 insertions, 0 deletions
diff --git a/src/er_xdg_pipe_menu_socket.erl b/src/er_xdg_pipe_menu_socket.erl
new file mode 100644
index 0000000..1bcca91
--- /dev/null
+++ b/src/er_xdg_pipe_menu_socket.erl
@@ -0,0 +1,66 @@
+%%%-------------------------------------------------------------------
+%% @doc Serves the cached menu over a Unix domain socket.
+%%
+%% Connecting to the socket *is* the request: each accepted connection
+%% is handed the current `er_xdg_pipe_menu_cache:get_menu/0' binary and
+%% closed. There is no request payload to parse.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(er_xdg_pipe_menu_socket).
+
+-export([start_link/0, socket_path/0]).
+-export([init/1]).
+
+-spec start_link() -> {ok, pid()}.
+start_link() ->
+ proc_lib:start_link(?MODULE, init, [self()]).
+
+-spec socket_path() -> file:filename_all().
+socket_path() ->
+ case application:get_env(er_xdg_pipe_menu, socket_path) of
+ {ok, Path} -> Path;
+ undefined -> default_socket_path()
+ end.
+
+default_socket_path() ->
+ case env("XDG_RUNTIME_DIR") of
+ undefined -> filename:join("/tmp", "er_xdg_pipe_menu-" ++ os:getenv("USER", "unknown") ++ ".sock");
+ Dir -> filename:join(Dir, "er_xdg_pipe_menu.sock")
+ end.
+
+env(Name) ->
+ case os:getenv(Name) of
+ false -> undefined;
+ "" -> undefined;
+ Value -> Value
+ end.
+
+init(Parent) ->
+ Path = socket_path(),
+ %% Clean up a stale socket file left behind by a previous run that
+ %% didn't shut down cleanly; gen_tcp:listen fails with eaddrinuse
+ %% otherwise.
+ _ = file:delete(Path),
+ {ok, Listen} = gen_tcp:listen(0, [
+ {ifaddr, {local, Path}},
+ binary,
+ {active, false},
+ {packet, raw},
+ {reuseaddr, true}
+ ]),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ accept_loop(Listen).
+
+accept_loop(Listen) ->
+ case gen_tcp:accept(Listen) of
+ {ok, Socket} ->
+ spawn(fun() -> serve(Socket) end),
+ accept_loop(Listen);
+ {error, Reason} ->
+ exit(Reason)
+ end.
+
+serve(Socket) ->
+ gen_tcp:send(Socket, er_xdg_pipe_menu_cache:get_menu()),
+ gen_tcp:close(Socket).