/src/pipe.erl

https://github.com/ogolosovskiy/erl_unix_sockets · Erlang · 285 lines · 198 code · 59 blank · 28 comment · 0 complexity · a60a7d61509a64cc32742e96d70ec909 MD5 · raw file

  1. -module(pipe).
  2. -behaviour(gen_server).
  3. -define(DRV_NAME, "pipe_drv").
  4. % API
  5. -export([
  6. start/0,
  7. start_link/0,
  8. open/2, %% debug
  9. connect/2,
  10. bind/2,
  11. close/1,
  12. send/2,
  13. recv/1,
  14. socket/0,
  15. test/0
  16. ]).
  17. % gen_server callbacks
  18. -export([
  19. init/1,
  20. handle_call/3,
  21. handle_cast/2,
  22. handle_info/2,
  23. terminate/2,
  24. code_change/3
  25. ]).
  26. -record(state, {port}).
  27. start() ->
  28. gen_server:start({local, ?MODULE}, ?MODULE, [], []).
  29. start_link() ->
  30. gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  31. %% Open socket return socket(AF_UNIX, SOCK_STREAM, 0);
  32. %% Return string represent handle of socket
  33. -spec socket() -> {ok, integer()} | {error, any()}.
  34. socket() ->
  35. gen_server:call(?MODULE, {socket}).
  36. %% strcpy(serveraddr.sun_path, SERVER_PATH);
  37. %% rc = connect(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
  38. %% ServerAddr is file path: example: /var/run/rtpserver.sock
  39. -spec connect(string(), integer()) -> ok | {error, any()}.
  40. connect(ServerAddr, SocketHandle) ->
  41. gen_server:call(?MODULE, {connect, ServerAddr, SocketHandle}).
  42. -spec bind(string(), integer()) -> ok | {error, any()}.
  43. bind(LocalAddr, SocketHandle) ->
  44. gen_server:call(?MODULE, {bind, LocalAddr, SocketHandle}).
  45. %% rc = send(sd, buffer, sizeof(buffer), 0);
  46. -spec send(string(), integer()) -> ok | {error, any()}.
  47. send(Data, SocketHandle) ->
  48. gen_server:call(?MODULE, {send, Data, SocketHandle}).
  49. %% close(sd);
  50. -spec close(integer()) -> ok | {error, any()}.
  51. close(Handle) ->
  52. gen_server:call(?MODULE, {close, Handle}).
  53. %% recv(sd, &buffer);
  54. -spec recv(integer()) -> {ok, string()} | {error, any()}.
  55. recv(SocketHandle) ->
  56. gen_server:call(?MODULE, {recv, SocketHandle}).
  57. %% Debug
  58. open(StrArg,IntArg) ->
  59. gen_server:call(?MODULE, {open, StrArg, IntArg}).
  60. test() ->
  61. io:format("Load driver ~n ",[]),
  62. {ok, _} = start(),
  63. io:format("Open STREAM SOCKET ~n ",[]),
  64. {ok, Handle} = socket(),
  65. io:format("Opened socket ~p ~n ",[Handle]),
  66. %% LocalAddr = "/var/tmp/unixsocketclient.sock",
  67. %% io:format("Binding to ~p ~n ",[LocalAddr]),
  68. %% ok = bind(LocalAddr, Handle),
  69. %% io:format("Binded to ~p ~p ~n ",[LocalAddr, Handle]),
  70. Addr = "/var/run/unison/tcp_socket_test.sock",
  71. io:format("Connecting to ~p ~n ",[Addr]),
  72. ok = connect(Addr, Handle),
  73. io:format("Connected to ~p ~p ~n ",[Addr, Handle]),
  74. Data = "Data portion 1",
  75. io:format("Sending ~p ~n ",[Data]),
  76. ok = send(Data, Handle),
  77. io:format("Sended ~n "),
  78. io:format("Receiving ~n "),
  79. {ok, Res} = recv(Handle),
  80. io:format("Received ~p ~n ", [Res]),
  81. io:format("Closing socket ~p ~n ",[Handle]),
  82. ok = close(Handle),
  83. io:format("Closed socket ~p ~n ",[Handle]),
  84. io:format("Open STREAM SOCKET ~n ",[]),
  85. {ok, Handle1} = socket(),
  86. io:format("Opened socket ~p ~n ",[Handle1]),
  87. io:format("Connecting to ~p ~n ",[Addr]),
  88. ok = connect(Addr, Handle1),
  89. io:format("Connected to ~p ~p ~n ",[Addr, Handle1]),
  90. Data1 = "Data portion 2",
  91. io:format("Sending ~p ~n ",[Data1]),
  92. ok = send(Data1, Handle1),
  93. io:format("Sended ~n "),
  94. io:format("Receiving ~n "),
  95. {ok, Res1} = recv(Handle1),
  96. io:format("Received ~p ~n ", [Res1]),
  97. Data2 = "Data portion 3",
  98. io:format("Sending ~p ~n ",[Data2]),
  99. ok = send(Data2, Handle1),
  100. io:format("Sended ~n "),
  101. io:format("Receiving ~n "),
  102. {ok, Res2} = recv(Handle1),
  103. io:format("Received ~p ~n ", [Res2]),
  104. io:format("Closing socket ~p ~n ",[Handle1]),
  105. ok = close(Handle1),
  106. io:format("Closed socket ~p ~n ",[Handle1]).
  107. init([]) ->
  108. erl_ddll:start(),
  109. Path = case code:lib_dir(erl_unix_sockets,priv) of
  110. {error, _} ->
  111. case load_path(?DRV_NAME++".so") of
  112. {error, _} ->
  113. error;
  114. {ok, P} ->
  115. P
  116. end;
  117. P ->
  118. P
  119. end,
  120. io:format("Load driver ~p ~p ~n ",[Path, ?DRV_NAME]),
  121. case Path of
  122. error ->
  123. {stop, no_driver};
  124. Path ->
  125. case erl_ddll:load_driver(Path, ?DRV_NAME) of
  126. ok ->
  127. Port = open_port({spawn, ?DRV_NAME}, [binary]),
  128. {ok, #state{port = Port}};
  129. {error, Error} ->
  130. error_logger:format("Error loading driver: " ++ erl_ddll:format_error(Error), []),
  131. {stop, bad_driver}
  132. end
  133. end.
  134. handle_call({open, StrArg, IntArg}, _From, #state{port = Port} = State) ->
  135. port_command(State#state.port, erlang:term_to_binary({open, StrArg, IntArg})),
  136. Reply = receive {Port, {data, Bin}} ->
  137. io:format("Recieved from driver: ~p", [{data,Bin}]),
  138. binary_to_term(Bin)
  139. after 1000 -> {error, timeout}
  140. end,
  141. {reply, Reply, State};
  142. handle_call({socket}, _From, #state{port = Port} = State) ->
  143. %% int socket_type
  144. %% 0 - SOCK_STREAM
  145. %% 1 - SOCK_DGRAM
  146. port_command(State#state.port, erlang:term_to_binary({socket, 0})),
  147. Reply = receive {Port, {data, Bin}} ->
  148. %% io:format("Recv: ~p", [{data,Bin}]),
  149. binary_to_term(Bin)
  150. after 1000 -> {error, timeout}
  151. end,
  152. %% io:format("Opened socket reply: ~p ~n", [Reply]),
  153. case Reply of
  154. {ok, Handle} ->
  155. {reply, {ok, Handle}, State};
  156. {error, ErrorDesc} ->
  157. {reply, {error, ErrorDesc}, State}
  158. end;
  159. handle_call({connect, ServerAddr, SocketHandle}, _From, #state{port = Port} = State) ->
  160. %% io:format("Socket: ~p", [ServerAddr]),
  161. port_command(State#state.port, erlang:term_to_binary({connect, ServerAddr, SocketHandle})),
  162. Reply = receive {Port, {data, Bin}} ->
  163. %% io:format("Recv: ~p", [{data,Bin}]),
  164. binary_to_term(Bin)
  165. after 1000 -> {error, timeout}
  166. end,
  167. {reply, Reply, State};
  168. handle_call({bind, LocalAddr, SocketHandle}, _From, #state{port = Port} = State) ->
  169. %% io:format("Socket: ~p", [ServerAddr]),
  170. port_command(State#state.port, erlang:term_to_binary({bind, LocalAddr, SocketHandle})),
  171. Reply = receive {Port, {data, Bin}} ->
  172. %% io:format("Recv: ~p", [{data,Bin}]),
  173. binary_to_term(Bin)
  174. after 1000 -> {error, timeout}
  175. end,
  176. {reply, Reply, State};
  177. handle_call({send, Data, SocketHandle}, _From, #state{port = Port} = State) ->
  178. port_command(State#state.port, erlang:term_to_binary({send, Data, SocketHandle})),
  179. Reply = receive {Port, {data, Bin}} ->
  180. %% io:format("Recv: ~p", [{data,Bin}]),
  181. binary_to_term(Bin)
  182. after 1000 -> {error, timeout}
  183. end,
  184. {reply, Reply, State};
  185. handle_call({close, Handle}, _From, #state{port = Port} = State) ->
  186. port_command(State#state.port, erlang:term_to_binary({close, Handle})),
  187. Reply = receive {Port, {data, Bin}} ->
  188. %% io:format("Recv: ~p", [{data,Bin}]),
  189. binary_to_term(Bin)
  190. after 1000 -> {error, timeout}
  191. end,
  192. {reply, Reply, State};
  193. handle_call({recv, SocketHandle}, _From, #state{port = Port} = State) ->
  194. port_command(State#state.port, erlang:term_to_binary({recv, SocketHandle})),
  195. Reply = receive {Port, {data, Bin}} ->
  196. %% io:format("Recv: ~p", [{data,Bin}]),
  197. binary_to_term(Bin)
  198. after 1000 -> {error, timeout}
  199. end,
  200. %% io:format("Recv: ~p", [Reply]),
  201. {reply, Reply, State};
  202. handle_call(_Msg, _From, State) ->
  203. {reply, ok, State}.
  204. handle_cast(_Msg, State) ->
  205. {noreply, State}.
  206. handle_info(_Info, State) ->
  207. {noreply, State}.
  208. terminate(_Reason, _State) ->
  209. ok.
  210. code_change(_, _, _) ->
  211. ok.
  212. load_path(File) ->
  213. case lists:zf(fun(Ebin) ->
  214. Priv = Ebin ++ "/../priv/",
  215. case file:read_file_info(Priv ++ File) of
  216. {ok, _} -> {true, Priv};
  217. _ -> false
  218. end
  219. end,
  220. code:get_path()) of
  221. [Dir|_] ->
  222. {ok, Dir};
  223. [] ->
  224. error_logger:format("Error: ~s not found in code path", [File]),
  225. {error, enoent}
  226. end.