Okay, I needed to send some data to a unit using Secure Copy (SCP), from an Erlang program.
Well it turned out Erlang did not have a SCP implementation as part of the ssh module, only sftp, but that was not supported by the receiving device, only SCP.
Hmmm, okay I could write the data and use the os:cmd to transfer the file, but where is the fun in that.
So I decided to implement a simple SCP, the mechanics of SCP is described brilliantly here: "How the SCP protocol works".
So here are some code snippets, which shows my take on this in Erlang.
First you need to establish a SSH connection to the receiving side, this is done with the ssh module fro my case like something like this, your connection parameters will vary:
crypto:start(),
ssh:start(),
{ok, ConnectionRef} = ssh:connect("localhost",10022,
[{user,"root"},
{user_dir,"/usr/local/etc/ssh"},
{rsa_pass_phrase,"whatever"}]
, 30000),
Now you have the ConnectionRef, that is needed. With this you can open a secure channel like this:
{ok, ChannelId} =
ssh_connection:session_channel(ConnectionRef, 30000),
And execute an SCP command on the remote side using this channel:
success = ssh_connection:exec(ConnectionRef,
ChannelId,
"scp -tq /tmp", 30000),
The last parameter to the remote scp command is the directory, where your file ends up.
Now I just call:
wait_for_response(ConnectionRef,ChannelId,FileName,Content,0);
Where FileName parameter is the name of the file ending up at the remote side, and files content is in Content parameter. The wait_for_response implements the SCP protocol like this:
wait_for_response(ConnectionRef,ChannelId,FileName,Content,C) ->
receive
{ssh_cm, ConnectionRef, Msg} ->
case Msg of
{closed, ChannelId} ->
ssh_connection:close(ConnectionRef, ChannelId),ok;
{eof, ChannelId} ->
ssh_connection:close(ConnectionRef, ChannelId),
{error, <<"Error: EOF">>};
{exit_signal, ChannelId, ExitSignal,
ErrorMsg, _LanguageString} ->
ssh_connection:close(ConnectionRef, ChannelId),
{error, list_to_binary(io_lib:format(
"Remote SCP exit signal: ~p : ~p",
[ExitSignal,ErrorMsg]))};
{exit_status,ChannelId,ExitStatus} ->
ssh_connection:close(ConnectionRef, ChannelId),
case ExitStatus of
0 -> ok;
_ -> {error,
list_to_binary(io_lib:format(
"Remote SCP exit status: ~p",[ExitStatus]))}
end;
{data,ChannelId,_Type,<<0>>} ->0>
SendResponse =
case C of
0 -> %%ok send header
Header =
list_to_binary(lists:flatten(
io_lib:format("~s ~p ~s~n",
["C0644", size(Content), FileName]))),
ssh_connection:send(ConnectionRef,
ChannelId,
Header);
1 -> %%ok send file
ssh_connection:send(ConnectionRef,
ChannelId,
Content),
ssh_connection:send(ConnectionRef,
ChannelId,
<<"\0">>);
2 -> %%ok file transferred
ssh_connection:send_eof(ConnectionRef, ChannelId)
end,
case SendResponse of
ok -> %%recurse
wait_for_response(ConnectionRef,
ChannelId,
FileName,
Content,C+1);
_ -> SendResponse
end
end
end.
Notice that all files are ends up with permission 0644 as per the Header:
io_lib:format("~s ~p ~s~n",["C0644", size(Content), FileName])
and how the parameter C holds the protocol state.
And don't for get to close your connection when done: ssh:close(ConnectionRef)
I might someday get the time to write a feature complete SCP module for Erlang, But till then, these snippets might help some of you.
1 comment:
Very informative article, which you have shared here about the scp command. After reading your article I got very much information and it is very useful for us. I am thankful to you for sharing this article here.
Post a Comment