%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(vxworks_client).
-export([open/1, close/1, send_data/2, send_data/3, send_data_wait_for_close/2, reboot/1]).
-export([init/2]).
-include("ts.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% This is a client talking to a test server daemon on a VxWorks card.
%%%
%%% User interface:
%%%
%%% open/1
%%% Start a client and establish the connection with the test server daemon
%%%
%%% send_data/2
%%% Send data/command to the test server daemon, don't wait for any return
%%%
%%% send_data/3
%%% Send data/command to the test server daemon and wait for the given
%%% return value.
%%%
%%% send_data_wait_for_close/2
%%% Send data/command to the test server daemon and wait for the daemon to
%%% close the connection.
%%%
%%% close/1
%%% Close the client.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% User interface
%%
reboot(Target) ->
{ok, {_,_,_,_,_,[Addr|_]}} = inet:gethostbyname(Target),
Fun = fun({ok, Socket}) ->
gen_tcp:send(Socket, "q\n"),
receive
{tcp_closed, Socket} ->
gen_tcp:close(Socket),
{ok, socket_closed}
after 5000 ->
exit({timeout, tryagain})
end
end,
io:format("Stopping (rebooting) ~p ",[Target]),
case fun_target(Addr, Fun) of
{ok, socket_closed} ->
ok;
_Else ->
io:format("No contact with ts daemon - exiting ...~n"),
exit({stop, no_ts_daemon_contact})
end.
%% open(Target) -> {ok,Client} | {error, Reason}
open(Target) ->
{ok, {_,_,_,_,_,[Addr|_]}} = inet:gethostbyname(Target),
Fun = fun({ok, Socket}) ->
P = spawn(?MODULE,init,[Target,Socket]),
inet_tcp:controlling_process(Socket,P),
{ok,P}
end,
case fun_target(Addr,Fun) of
{ok, Pid} ->
{ok, Pid};
{error,Reason} ->
{error, Reason}
end.
%% send_data(Client,Data) -> ok
send_data(Pid,Data) ->
Pid ! {send_data,Data++"\n"},
ok.
%% send_data(Client,Data,ExpectedReturn) -> {ok,ExpectedReturn} | {error,Reason}
send_data(Pid,Data,Return) ->
Pid ! {send_data,Data++"\n",Return,self()},
receive {Pid,Result} -> Result end.
%% send_data_wait_for_close(Client,Data) -> ok | {error,Reason}
send_data_wait_for_close(Pid,Data) ->
send_data(Pid,Data,tcp_closed).
%% close(Client) -> ok
close(Pid) ->
Pid ! close,
ok.
%%
%% Internal
%%
init(Target,Socket) ->
process_flag(trap_exit,true),
loop(Target,Socket).
loop(Target,Socket) ->
receive
{send_data,Data} ->
%% io:format("vx client sending: ~p~n", [Data]),
gen_tcp:send(Socket, Data),
loop(Socket,Target);
{send_data,Data,tcp_closed,From} ->
%% io:format("vx client sending: ~p~n", [Data]),
gen_tcp:send(Socket, Data),
receive
{tcp_closed, Socket} ->
From ! {self(),ok}
after 5000 ->
From ! {self(),{error,timeout}}
end,
closed(Socket,normal);
{send_data,Data,Return,From} ->
%% io:format("vx client sending: ~p~n", [Data]),
gen_tcp:send(Socket, Data),
case receive_line(Socket,[],Return,200) of
{tcp_closed, Socket} ->
From ! {self(),{error,{socket_closed,Target}}},
closed(Socket,{socket_closed,Target});
{tcp,Socket,_Rest} ->
From ! {self(),{ok,Data}},
got_data(Target,Socket,Data);
error ->
From ! {self(),{error,{catatonic,Target}}}
end;
close ->
closed(Socket,normal);
{tcp_closed, Socket} ->
closed(Socket,{socket_closed,Target});
{tcp,Socket,Data} ->
got_data(Target,Socket,Data)
end.
closed(Socket,Reason) ->
gen_tcp:close(Socket),
exit(Reason).
got_data(Target,Socket,Data) ->
if is_atom(Target) ->
io:format("~w: ~s",[Target,uncr(Data)]);
true ->
io:format("~s: ~s",[Target,uncr(Data)])
end,
loop(Target,Socket).
uncr([]) ->
[];
uncr([$\r | T]) ->
uncr(T);
uncr([H | T]) ->
[H | uncr(T)].
strip_line(Line) ->
RPos = string:rchr(Line, $\n),
string:substr(Line,RPos+1).
maybe_done_receive(Socket,Ack,Match,C) ->
case string:str(Ack,Match) of
0 ->
receive_line(Socket,strip_line(Ack),Match,C);
_ ->
{tcp,Socket,strip_line(Ack)}
end.
receive_line(_Socket,_Ack,_Match,0) ->
error;
receive_line(Socket,Ack,Match,Counter) ->
receive
{tcp_closed, Socket} ->
{tcp_closed, Socket};
{tcp,Socket,Data} ->
NewAck = Ack ++ Data,
case {string:str(NewAck,"\r") > 0,
string:str(NewAck,"\n") > 0} of
{true,_} ->
maybe_done_receive(Socket,NewAck,Match,Counter-1);
{_,true} ->
maybe_done_receive(Socket,NewAck,Match,Counter-1);
_ ->
receive_line(Socket,NewAck,Match,Counter)
end
after 20000 ->
error
end.
%% Misc functions
fun_target(Addr, Fun) ->
io:format("["),
fun_target(Addr, Fun, 60). %Vx-cards need plenty of time.
fun_target(_Addr, _Fun, 0) ->
io:format(" no contact with ts daemon]~n"),
{error,failed_to_connect};
fun_target(Addr, Fun, Tries_left) ->
receive after 1 -> ok end,
case do_connect(Addr, Fun) of
{ok, Value} ->
io:format(" ok]~n"),
{ok, Value};
_Error -> % typical {error, econnrefused}
io:format("."),
receive after 10000 -> ok end,
fun_target(Addr, Fun, Tries_left-1)
end.
do_connect(Addr, Fun) ->
case gen_tcp:connect(Addr, ?TS_PORT, [{reuseaddr, true}], 60000) of
{ok, Socket} ->
Fun({ok, Socket});
Error ->
Error
end.