/elibs/port_wrapper.erl

https://github.com/GunioRobot/fuzed · Erlang · 102 lines · 88 code · 10 blank · 4 comment · 0 complexity · 9a64fd91976d69d672b5818ead3ee819 MD5 · raw file

  1. -module(port_wrapper).
  2. -export([wrap/1, wrap/2, wrap_link/1, wrap_link/2, send/2, shutdown/1, rpc/2, pure_send/2]).
  3. -author('Dave Fayram').
  4. wrap(Command) ->
  5. spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, infinity, Command) end).
  6. wrap(Command, Timeout) ->
  7. spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, Timeout, Command) end).
  8. wrap_link(Command) ->
  9. spawn_link(fun() -> process_flag(trap_exit, true), Port = create_port(Command), link(Port), loop(Port, infinity, Command) end).
  10. wrap_link(Command, Timeout) ->
  11. spawn_link(fun() -> process_flag(trap_exit, true), Port = create_port(Command), link(Port), loop(Port, Timeout, Command) end).
  12. rpc(WrappedPort, Message) ->
  13. send(WrappedPort, Message),
  14. receive
  15. {WrappedPort, Result} -> Result
  16. after 15000 ->
  17. {WrappedPort, timed_out}
  18. end.
  19. send(WrappedPort, Message) ->
  20. WrappedPort ! {self(), {command, term_to_binary(Message)}},
  21. WrappedPort.
  22. pure_send(WrappedPort, Message) ->
  23. WrappedPort ! {self(), {just_send_a_command, term_to_binary(Message)}},
  24. WrappedPort.
  25. shutdown(WrappedPort) ->
  26. WrappedPort ! shutdown,
  27. true.
  28. create_port(Command) ->
  29. open_port({spawn, Command}, [{packet, 4}, nouse_stdio, exit_status, binary]).
  30. loop(Port, Timeout, Command) ->
  31. receive
  32. noose ->
  33. port_close(Port),
  34. noose;
  35. shutdown ->
  36. port_close(Port),
  37. exit(shutdown);
  38. {Source, host} ->
  39. Source ! {Port, node()},
  40. loop(Port,Timeout,Command);
  41. {Source, heat} ->
  42. Port ! {self(), {command, term_to_binary(ping)}},
  43. Hot = term_to_binary(pong),
  44. receive
  45. {Port, {data, Hot}} ->
  46. Source ! {self(), hot}
  47. end,
  48. loop(Port, Timeout, Command);
  49. {Source, api} ->
  50. Port ! {self(), {command, term_to_binary(api)}},
  51. receive
  52. {Port, {data, Result}} ->
  53. {result, Api} = binary_to_term(Result),
  54. Source ! {self(), tuple_to_list(Api)}
  55. end,
  56. loop(Port,Timeout,Command);
  57. {Source, {command, Message}} ->
  58. Port ! {self(), {command, Message}},
  59. receive
  60. {Port, {data, Result}} ->
  61. DB = binary_to_term(Result),
  62. case DB of
  63. {last_result, X} ->
  64. Source ! {self(), {result, X}},
  65. port_close(Port),
  66. exit(last_result);
  67. Z ->
  68. Source ! {self(), Z}
  69. end
  70. after Timeout ->
  71. error_logger:error_msg("Port Wrapper ~p timed out in mid operation (~p)!~n", [self(),Message]),
  72. % We timed out, which means we need to close and then restart the port
  73. port_close(Port), % Should SIGPIPE the child.
  74. exit(timed_out)
  75. end,
  76. loop(Port,Timeout,Command);
  77. {_Source, {just_send_a_command, Message}} ->
  78. Port ! {self(), {command, Message}},
  79. loop(Port,Timeout,Command);
  80. {Port, {exit_status, _Code}} ->
  81. % Hard and Unanticipated Crash
  82. error_logger:error_msg( "Port closed! ~p~n", [Port] ),
  83. exit({error, _Code});
  84. {'EXIT',_Pid,shutdown} ->
  85. port_close(Port),
  86. exit(shutdown);
  87. Any ->
  88. error_logger:warning_msg("PortWrapper ~p got unexpected message: ~p~n", [self(), Any]),
  89. loop(Port, Timeout, Command)
  90. end.
  91. % Local API
  92. % TODO: Add retry detection