Browse files

source code added (very primitive ones)

  • Loading branch information...
1 parent d70e70c commit d08dec30fc708b2dd9a609b3d8b4a8e5d688afad @jj1bdx committed Feb 25, 2010
Showing with 1,053 additions and 42 deletions.
  1. +1 −0 .gitignore
  2. +286 −0 EPLICENSE
  3. +4 −42 README
  4. +34 −0 src/Makefile
  5. +136 −0 src/client_subsystest.erl
  6. +58 −0 src/readme.txt
  7. +62 −0 src/server_test.erl
  8. +24 −0 src/sshrpc.hrl
  9. +288 −0 src/sshrpc_client.erl
  10. +160 −0 src/sshrpc_subsystem.erl
View
1 .gitignore
@@ -0,0 +1 @@
+k2r-local/*
View
286 EPLICENSE
@@ -0,0 +1,286 @@
+ERLANG PUBLIC LICENSE
+Version 1.1
+
+1. Definitions.
+
+1.1. ``Contributor'' means each entity that creates or contributes to
+the creation of Modifications.
+
+1.2. ``Contributor Version'' means the combination of the Original
+Code, prior Modifications used by a Contributor, and the Modifications
+made by that particular Contributor.
+
+1.3. ``Covered Code'' means the Original Code or Modifications or the
+combination of the Original Code and Modifications, in each case
+including portions thereof.
+
+1.4. ``Electronic Distribution Mechanism'' means a mechanism generally
+accepted in the software development community for the electronic
+transfer of data.
+
+1.5. ``Executable'' means Covered Code in any form other than Source
+Code.
+
+1.6. ``Initial Developer'' means the individual or entity identified
+as the Initial Developer in the Source Code notice required by Exhibit
+A.
+
+1.7. ``Larger Work'' means a work which combines Covered Code or
+portions thereof with code not governed by the terms of this License.
+
+1.8. ``License'' means this document.
+
+1.9. ``Modifications'' means any addition to or deletion from the
+substance or structure of either the Original Code or any previous
+Modifications. When Covered Code is released as a series of files, a
+Modification is:
+
+A. Any addition to or deletion from the contents of a file containing
+ Original Code or previous Modifications.
+
+B. Any new file that contains any part of the Original Code or
+ previous Modifications.
+
+1.10. ``Original Code'' means Source Code of computer software code
+which is described in the Source Code notice required by Exhibit A as
+Original Code, and which, at the time of its release under this
+License is not already Covered Code governed by this License.
+
+1.11. ``Source Code'' means the preferred form of the Covered Code for
+making modifications to it, including all modules it contains, plus
+any associated interface definition files, scripts used to control
+compilation and installation of an Executable, or a list of source
+code differential comparisons against either the Original Code or
+another well known, available Covered Code of the Contributor's
+choice. The Source Code can be in a compressed or archival form,
+provided the appropriate decompression or de-archiving software is
+widely available for no charge.
+
+1.12. ``You'' means an individual or a legal entity exercising rights
+under, and complying with all of the terms of, this License. For legal
+entities,``You'' includes any entity which controls, is controlled by,
+or is under common control with You. For purposes of this definition,
+``control'' means (a) the power, direct or indirect, to cause the
+direction or management of such entity, whether by contract or
+otherwise, or (b) ownership of fifty percent (50%) or more of the
+outstanding shares or beneficial ownership of such entity.
+
+2. Source Code License.
+
+2.1. The Initial Developer Grant.
+The Initial Developer hereby grants You a world-wide, royalty-free,
+non-exclusive license, subject to third party intellectual property
+claims:
+
+(a) to use, reproduce, modify, display, perform, sublicense and
+ distribute the Original Code (or portions thereof) with or without
+ Modifications, or as part of a Larger Work; and
+
+(b) under patents now or hereafter owned or controlled by Initial
+ Developer, to make, have made, use and sell (``Utilize'') the
+ Original Code (or portions thereof), but solely to the extent that
+ any such patent is reasonably necessary to enable You to Utilize
+ the Original Code (or portions thereof) and not to any greater
+ extent that may be necessary to Utilize further Modifications or
+ combinations.
+
+2.2. Contributor Grant.
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license, subject to third party intellectual property
+claims:
+
+(a) to use, reproduce, modify, display, perform, sublicense and
+ distribute the Modifications created by such Contributor (or
+ portions thereof) either on an unmodified basis, with other
+ Modifications, as Covered Code or as part of a Larger Work; and
+
+(b) under patents now or hereafter owned or controlled by Contributor,
+ to Utilize the Contributor Version (or portions thereof), but
+ solely to the extent that any such patent is reasonably necessary
+ to enable You to Utilize the Contributor Version (or portions
+ thereof), and not to any greater extent that may be necessary to
+ Utilize further Modifications or combinations.
+
+3. Distribution Obligations.
+
+3.1. Application of License.
+The Modifications which You contribute are governed by the terms of
+this License, including without limitation Section 2.2. The Source
+Code version of Covered Code may be distributed only under the terms
+of this License, and You must include a copy of this License with
+every copy of the Source Code You distribute. You may not offer or
+impose any terms on any Source Code version that alters or restricts
+the applicable version of this License or the recipients' rights
+hereunder. However, You may include an additional document offering
+the additional rights described in Section 3.5.
+
+3.2. Availability of Source Code.
+Any Modification which You contribute must be made available in Source
+Code form under the terms of this License either on the same media as
+an Executable version or via an accepted Electronic Distribution
+Mechanism to anyone to whom you made an Executable version available;
+and if made available via Electronic Distribution Mechanism, must
+remain available for at least twelve (12) months after the date it
+initially became available, or at least six (6) months after a
+subsequent version of that particular Modification has been made
+available to such recipients. You are responsible for ensuring that
+the Source Code version remains available even if the Electronic
+Distribution Mechanism is maintained by a third party.
+
+3.3. Description of Modifications.
+You must cause all Covered Code to which you contribute to contain a
+file documenting the changes You made to create that Covered Code and
+the date of any change. You must include a prominent statement that
+the Modification is derived, directly or indirectly, from Original
+Code provided by the Initial Developer and including the name of the
+Initial Developer in (a) the Source Code, and (b) in any notice in an
+Executable version or related documentation in which You describe the
+origin or ownership of the Covered Code.
+
+3.4. Intellectual Property Matters
+
+(a) Third Party Claims.
+ If You have knowledge that a party claims an intellectual property
+ right in particular functionality or code (or its utilization
+ under this License), you must include a text file with the source
+ code distribution titled ``LEGAL'' which describes the claim and
+ the party making the claim in sufficient detail that a recipient
+ will know whom to contact. If you obtain such knowledge after You
+ make Your Modification available as described in Section 3.2, You
+ shall promptly modify the LEGAL file in all copies You make
+ available thereafter and shall take other steps (such as notifying
+ appropriate mailing lists or newsgroups) reasonably calculated to
+ inform those who received the Covered Code that new knowledge has
+ been obtained.
+
+(b) Contributor APIs.
+ If Your Modification is an application programming interface and
+ You own or control patents which are reasonably necessary to
+ implement that API, you must also include this information in the
+ LEGAL file.
+
+3.5. Required Notices.
+You must duplicate the notice in Exhibit A in each file of the Source
+Code, and this License in any documentation for the Source Code, where
+You describe recipients' rights relating to Covered Code. If You
+created one or more Modification(s), You may add your name as a
+Contributor to the notice described in Exhibit A. If it is not
+possible to put such notice in a particular Source Code file due to
+its structure, then you must include such notice in a location (such
+as a relevant directory file) where a user would be likely to look for
+such a notice. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations to one or more
+recipients of Covered Code. However, You may do so only on Your own
+behalf, and not on behalf of the Initial Developer or any
+Contributor. You must make it absolutely clear than any such warranty,
+support, indemnity or liability obligation is offered by You alone,
+and You hereby agree to indemnify the Initial Developer and every
+Contributor for any liability incurred by the Initial Developer or
+such Contributor as a result of warranty, support, indemnity or
+liability terms You offer.
+
+3.6. Distribution of Executable Versions.
+You may distribute Covered Code in Executable form only if the
+requirements of Section 3.1-3.5 have been met for that Covered Code,
+and if You include a notice stating that the Source Code version of
+the Covered Code is available under the terms of this License,
+including a description of how and where You have fulfilled the
+obligations of Section 3.2. The notice must be conspicuously included
+in any notice in an Executable version, related documentation or
+collateral in which You describe recipients' rights relating to the
+Covered Code. You may distribute the Executable version of Covered
+Code under a license of Your choice, which may contain terms different
+from this License, provided that You are in compliance with the terms
+of this License and that the license for the Executable version does
+not attempt to limit or alter the recipient's rights in the Source
+Code version from the rights set forth in this License. If You
+distribute the Executable version under a different license You must
+make it absolutely clear that any terms which differ from this License
+are offered by You alone, not by the Initial Developer or any
+Contributor. You hereby agree to indemnify the Initial Developer and
+every Contributor for any liability incurred by the Initial Developer
+or such Contributor as a result of any such terms You offer.
+
+3.7. Larger Works.
+You may create a Larger Work by combining Covered Code with other code
+not governed by the terms of this License and distribute the Larger
+Work as a single product. In such a case, You must make sure the
+requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Code due to statute
+or regulation then You must: (a) comply with the terms of this License
+to the maximum extent possible; and (b) describe the limitations and
+the code they affect. Such description must be included in the LEGAL
+file described in Section 3.4 and must be included with all
+distributions of the Source Code. Except to the extent prohibited by
+statute or regulation, such description must be sufficiently detailed
+for a recipient of ordinary skill to be able to understand it.
+
+5. Application of this License.
+
+This License applies to code to which the Initial Developer has
+attached the notice in Exhibit A, and to related Covered Code.
+
+6. CONNECTION TO MOZILLA PUBLIC LICENSE
+
+This Erlang License is a derivative work of the Mozilla Public
+License, Version 1.0. It contains terms which differ from the Mozilla
+Public License, Version 1.0.
+
+7. DISCLAIMER OF WARRANTY.
+
+COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS,
+WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR
+NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF
+THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE
+IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER
+CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR
+CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART
+OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER
+EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+This License and the rights granted hereunder will terminate
+automatically if You fail to comply with terms herein and fail to cure
+such breach within 30 days of becoming aware of the breach. All
+sublicenses to the Covered Code which are properly granted shall
+survive any termination of this License. Provisions which, by their
+nature, must remain in effect beyond the termination of this License
+shall survive.
+
+9. DISCLAIMER OF LIABILITY
+Any utilization of Covered Code shall not cause the Initial Developer
+or any Contributor to be liable for any damages (neither direct nor
+indirect).
+
+10. MISCELLANEOUS
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision is held to be unenforceable, such
+provision shall be reformed only to the extent necessary to make it
+enforceable. This License shall be construed by and in accordance with
+the substantive laws of Sweden. Any dispute, controversy or claim
+arising out of or relating to this License, or the breach, termination
+or invalidity thereof, shall be subject to the exclusive jurisdiction
+of Swedish courts, with the Stockholm City Court as the first
+instance.
+
+EXHIBIT A.
+
+``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 via the world wide web 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.
+
+The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+AB. All Rights Reserved.''
View
46 README
@@ -1,50 +1,12 @@
-sshrpc readme.txt
+sshrpc README
by Kenji Rikitake
This is sshrpc, an experimental module for Erlang RPC over SSH.
WARNING: this code should not be used for a production system.
-* ssh module configuration requirement
+See src/readme.txt for the details.
-Option {nodelay, true} MUST be set on both sides of
-ssh:connect and ssh:daemon, for ensuring immediate
-answers during the exchange.
-
-* subsystem
-
-sshrpc is implemented as an SSH subsystem, named as
- sshrpc@k2r.org
-Note: this name is local and temporary
-
-subsystem code: sshrpc_subsystem.erl
-
-* client code
-
-client code is still incomplete and under development.
-
-* SSH channel usage
-
-sshrpc uses SSH Channel Type 0 for RPC exchange.
-Usage for other Types are reserved.
-Out-of-band signaling is not implemented.
-
-* RPC exchange format
-
-Larger binary packet for each exchange:
- << Length:32/unsigned-big-integer,
- %% 4-byte binary (?UINT32())
- Content/binary
- %% of Erlang External Format of Length bytes
- >>
-
-Content for each exchange contains tuples only.
-
-Command tuples:
-
-{mfa, {Module, Function, Args}}
- execute erlang:apply(Module, Function, Args) at the server node.
-
-{answer, {Reply, Status}}
- result of the {mfa, {...}} command tuple.
+This code is licensed under Erlang Public License 1.1.
+See EPLICENSE or http://www.erlang.org/ for the further details.
[End of memorandum]
View
34 src/Makefile
@@ -0,0 +1,34 @@
+# Copyright (c) 2009-2010 Kenji Rikitake. 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.
+
+# leave these lines alone
+.SUFFIXES: .erl .beam .yrl
+
+.erl.beam:
+ erlc -W $<
+
+.yrl.erl:
+ erlc -W $<
+
+ERL = erl -boot start_clean
+
+MODS = sshrpc_subsystem server_test client_subsystest
+
+all: compile
+
+compile: ${MODS:%=%.beam}
+
+clean:
+ rm -rf *.beam erl_crash.dump
+
+# end of file
View
136 src/client_subsystest.erl
@@ -0,0 +1,136 @@
+%% Copyright (c) 2009-2010 Kenji Rikitake. 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.
+
+%% @author Kenji Rikitake
+%% @copyright 2009-2010 Kenji Rikitake
+
+%% @doc example code for testing SSH RPC client site
+%% TODO: this code must be OTP-nized!
+
+-module(client_subsystest).
+-export([startup/0, linkup/0, doit/2, test/2]).
+
+-include("sshrpc.hrl").
+
+-define(TIMEOUT, 30000). % in milliseconds
+
+%% specifyclient configuration key directory
+%% (put id_rsa and id_rsa.pub)
+
+-define(CLIENT_CONFIG, "/your/client/directory").
+
+startup() ->
+ ok = crypto:start(),
+ ok = ssh:start().
+
+linkup() ->
+ {ok, Pid} = ssh:connect("127.0.0.1", % server address
+ 11122, % port number
+ [
+ %% note: private user key (id_rsa) and
+ %% public host key (known_hosts) must exist
+ %% in the "user_dir" for public-key auth
+ %% NOTE WELL: user key has NULL password protected
+ %% (password-protected is UNSUPPORTED)
+ {user_dir, ?CLIENT_CONFIG},
+ %% the following user/password pair needed for
+ %% plain password-based authentication
+ %% {user,"test"}
+ %% {password,"shiken"}
+ %%
+ %% nodelay must be set true for immediate response!
+ {nodelay, true}
+ ]),
+ {ok, Chanid} = ssh_connection:session_channel(Pid, infinity),
+ io:format("Pid: ~p Chanid: ~p ~n", [Pid, Chanid]),
+ Status = ssh_connection:subsystem(Pid, Chanid, "sshrpc@k2r.org", ?TIMEOUT),
+ io:format("subsystem connection status: ~p ~n", [Status]),
+ {Pid, Chanid}.
+
+doit({Pid, Chanid}, Command) ->
+ Bin = term_to_binary(Command),
+ Len = size(Bin),
+ ssh_connection:send(Pid, Chanid, 0, <<?UINT32(Len), Bin/binary>>, ?TIMEOUT),
+ ssh_receive(Pid, Chanid).
+
+ssh_receive(CM, Channel) ->
+ ssh_loop(CM, Channel, <<>>).
+
+-define(RWINSIZE, 4096).
+
+%% SSH client loop: this hasn't been OTP-nized yet.
+
+ssh_loop(CM, Channel, Buf) ->
+ receive
+ stop ->
+ % Closing channel
+ ssh_connection:close(CM, Channel);
+
+ {ssh_cm, CM, {data, Channel, 0, Data}} ->
+ % looks like this is a must for flow control...
+ ssh_connection:adjust_window(CM, Channel, size(Data)),
+ State = handle_reply(<<Buf/binary, Data/binary>>),
+ case State of
+ {moredata, _, Data1} ->
+ ssh_loop(CM, Channel, Data1);
+ {success, Term, Data2} ->
+ case Term of
+ {answer, {Reply, Status}} ->
+ io:format("Answer: Reply: ~p Status: ~p~n",
+ [Reply, Status]),
+ case Data2 of
+ <<>> -> ok;
+ Other -> ssh_loop(CM, Channel, Other)
+ end;
+ Other ->
+ io:format("[~p] Unknown Term returned: ~p~n", [?MODULE, Other])
+ end;
+ Other ->
+ io:format("[~p] Unknown State returned: ~p~n", [?MODULE, Other])
+ end;
+
+ {ssh_cm, CM, {closed, Channel}} ->
+ io:format("detached: ~p~n", [Channel]),
+ % Closing channel
+ io:format("close channel: ~p~n", [Channel]),
+ ssh_connection:close(CM, Channel);
+
+ {ssh_cm, CM, {eof, Channel}} ->
+ io:format("eof: ~p~n", [Channel]);
+
+ E ->
+ io:format("[~p] Received: ~p~n", [?MODULE, E]),
+ ssh_loop(CM, Channel, Buf)
+ after 1000 ->
+ io:format("[~p] Timed Out 1sec~n", [?MODULE])
+ end.
+
+handle_reply(<<?UINT32(Len), Msg:Len/binary, Rest/binary>>) ->
+ %io:format("handle_reply: Len: ~p Msg: ~p Rest: ~p~n", [Len, Msg, Rest]),
+ {success, binary_to_term(Msg), Rest};
+handle_reply(Data) ->
+ %io:format("handle_reply: Data: ~p~n", [Data]),
+ {moredata, {}, Data}.
+
+test(M,N) ->
+ L = linkup(),
+ Status = lists:map(fun(X) ->
+ doit(L, {mfa, {lists, seq, [1, M]}}),
+ io:format("NR: ~p Time: ~p~n", [X, erlang:now()]) end,
+ lists:seq(1,N)),
+ io:format("Time: ~p Status: ~p~n", [erlang:now(),Status]),
+ {Pid, Chanid} = L,
+ ssh_connection:close(Pid, Chanid),
+ ssh_loop(Pid, Chanid, <<>>).
+
+%% end of file
View
58 src/readme.txt
@@ -0,0 +1,58 @@
+sshrpc readme.txt
+by Kenji Rikitake
+25-FEB-2010
+
+This is sshrpc, an experimental module for Erlang RPC over SSH.
+
+* ssh module configuration requirement
+
+Option {nodelay, true} MUST be set on both sides of
+ssh:connect and ssh:daemon, for ensuring immediate
+answers during the exchange.
+
+* subsystem
+
+sshrpc is implemented as an SSH subsystem, named as
+ sshrpc@k2r.org
+Note: this name is local and temporary
+
+subsystem code: sshrpc_subsystem.erl
+
+* client code
+
+client code is still incomplete and under development.
+client_subsystest.erl is an ad-hoc implementation;
+sshrpc_client.erl will be the ssh_channel based implementation.
+
+* NOTYET: sshrpc_client module functions
+
+sync_call(Pid, Module, Function, Arguments) -> result | {badrpc, Reason}
+ synchronous function evaluation on the remote node
+ as connected through sshrpc subsystem via process Pid.
+
+* SSH channel usage
+
+sshrpc uses SSH Channel Type 0 for RPC exchange.
+Usage for other Types are reserved.
+Out-of-band signaling is not implemented.
+
+* RPC exchange format
+
+Larger binary packet for each exchange:
+ << Length:32/unsigned-big-integer,
+ %% 4-byte binary (?UINT32())
+ Content/binary
+ %% of Erlang External Format of Length bytes
+ >>
+
+Content for each exchange contains tuples only.
+
+Command tuples:
+
+{mfa, {Module, Function, Args}}
+ execute erlang:apply(Module, Function, Args) at the server node.
+
+{answer, {Reply, Status}}
+ result of the {mfa, {...}} command tuple.
+
+[End of memorandum]
View
62 src/server_test.erl
@@ -0,0 +1,62 @@
+%% Copyright (c) 2009-2010 Kenji Rikitake. 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.
+
+%% @author Kenji Rikitake
+%% @copyright 2009-2010 Kenji Rikitake
+
+%% @doc example code for testing SSH RPC server site
+
+-module(server_test).
+-export([startup/0, invoke/0]).
+
+-define(TIMEOUT, 30000). % in milliseconds
+
+%% specify server configuration key directory
+%% (put id_rsa.pub, ssh_host_rsa_key{.pub}, and authorized_keys
+
+-define(SERVER_CONFIG,
+ "/your/server/directory").
+
+startup() ->
+ ok = crypto:start(),
+ ok = ssh:start().
+
+invoke() ->
+ Pid = ssh:daemon({127,0,0,1}, % IP address
+ 11122, % port number
+ [
+ %% server configuration directory
+ %% including the host keys
+ {system_dir, ?SERVER_CONFIG},
+ %% note: public user key (authorized_keys) must exist
+ %% in the following *user* directory for public-key auth
+ %% NOTE WELL: user key has NULL password protected
+ %% (password-protected key not tested yet)
+ {user_dir, ?SERVER_CONFIG},
+ %%
+ %% for subsystem test
+ {subsystems,[sshrpc_subsystem:subsystem_spec([])]},
+ %%
+ %% the following user/password pair list of
+ %% user_password needed for
+ %% plain password-based authentication
+ %% {user_passwords,
+ %% [{"test","shiken"}]
+ %%
+ %% nodelay option required for faster immediate response!
+ %%
+ {nodelay, true}
+ ]),
+ Pid.
+
+% end of file
View
24 src/sshrpc.hrl
@@ -0,0 +1,24 @@
+%% sshrpc definitions
+
+%% basic binary constructor
+%% see ssh.hrl
+-define(UINT32(X), X:32/unsigned-big-integer).
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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%
+%%
View
288 src/sshrpc_client.erl
@@ -0,0 +1,288 @@
+%% Copyright (c) 2009-2010 Kenji Rikitake. 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.
+
+%% @author Kenji Rikitake
+%% @copyright 2009-2010 Kenji Rikitake
+
+%% @doc general sshrpc client-side subsystem (incomplete)
+%% NOTE: this code does compile but WILL NOT WORK AS IS.
+
+%% see ssh_sftp.erl
+
+-module(sshrpc_client).
+
+-behaviour(ssh_channel).
+
+-include("sshrpc.hrl").
+
+%% own functions
+-export([sync_call/4, call/3]).
+
+%% ssh_channel callbacks
+-export([init/1, handle_ssh_msg/2, handle_msg/2, handle_call/3]).
+-export([terminate/2, code_change/3]).
+-export([start_channel/1, start_channel/2, start_channel/3, stop_channel/1]).
+
+%% state
+-record(state, {
+ cm,
+ channel,
+ from,
+ req_id,
+ reply_buf % binary()
+ }).
+
+-define(SSHRPC_PACKET_SIZE, 32768).
+-define(SSHRPC_WINDOW_SIZE, 4*?SSHRPC_PACKET_SIZE).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start_channel(Cm) when is_pid(Cm) ->
+ start_channel(Cm, []);
+start_channel(Host) when is_list(Host) ->
+ start_channel(Host, []).
+start_channel(Cm, Opts) when is_pid(Cm) ->
+ Timeout = proplists:get_value(timeout, Opts, infinity),
+ case open_subsystem(Cm, []) of
+ {ok, ChannelId, Cm} ->
+ case ssh_channel:start(Cm, ChannelId, ?MODULE, [Cm,
+ ChannelId, Timeout]) of
+ {ok, Pid} ->
+ {ok, Pid, Cm};
+ {error, Reason} ->
+ {error, Reason};
+ ignore ->
+ {error, ignore}
+ end;
+ Error ->
+ Error
+ end;
+
+start_channel(Host, Opts) ->
+ start_channel(Host, 22, Opts).
+start_channel(Host, Port, Opts) ->
+ Timeout = proplists:get_value(timeout, Opts, infinity),
+ case connect(Host, Port, proplists:delete(timeout, Opts)) of
+ {ok, ChannelId, Cm} ->
+ case ssh_channel:start(Cm, ChannelId, ?MODULE, [Cm,
+ ChannelId, Timeout]) of
+ {ok, Pid} ->
+ {ok, Pid, Cm};
+ {error, Reason} ->
+ {error, Reason};
+ ignore ->
+ {error, ignore}
+ end;
+ Error ->
+ Error
+ end.
+
+stop_channel(Pid) ->
+ case process_info(Pid, [trap_exit]) of
+ [{trap_exit, Bool}] ->
+ process_flag(trap_exit, true),
+ link(Pid),
+ exit(Pid, sshrpc_stop_channel),
+ receive
+ {'EXIT', Pid, normal} ->
+ ok
+ after 5000 ->
+ exit(Pid, kill),
+ receive
+ {'EXIT', Pid, killed} ->
+ ok
+ end
+ end,
+ process_flag(trap_exit, Bool),
+ ok;
+ undefined ->
+ ok
+ end.
+
+%% remote eval function
+
+sync_call(Pid, M, F, A) ->
+ call(Pid, {mfa, {M, F, A}}, infinity).
+
+%%====================================================================
+%% SSh channel callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+
+init([Cm, ChannelId, Timeout]) ->
+ erlang:monitor(process, Cm),
+ case ssh_connection:subsystem(Cm, ChannelId,
+ "sshrpc@k2r.org",
+ Timeout) of
+ success ->
+ {ok, #state{cm = Cm,
+ channel = ChannelId,
+ req_id = 0,
+ reply_buf = <<>>}};
+ failure ->
+ {stop, {error, "server failed to start sftp subsystem"}};
+ Error ->
+ {stop, Error}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: handle_call/3
+%% Description: Handling call messages
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+
+handle_call({{timeout, infinity}, Msg}, From, State) ->
+ do_handle_call(Msg, From, State);
+
+handle_call({{timeout, Timeout}, Msg}, From, #state{req_id = Id} = State) ->
+ timer:send_after(Timeout, {timeout, Id, From}),
+ do_handle_call(Msg, From, State).
+
+%% actual processing function
+%% how should I handle the "From" argument?
+do_handle_call({mfa, {M, F, A}}, From, #state{
+ cm = ConnectionManager,
+ channel = ChannelId} = State) ->
+ send_command(ConnectionManager, ChannelId, {mfa, {M, F, A}}),
+ {noreply, State#state{from = From}}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State}
+%%
+%% Description: Handles channel messages
+%%--------------------------------------------------------------------
+
+handle_ssh_msg({ssh_cm, _ConnectionManager,
+ {data, _ChannelId, 0, Data}}, #state{reply_buf = Data0} =
+ State0) ->
+ State = handle_reply(State0, <<Data0/binary,Data/binary>>),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) ->
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _, _}},
+ State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _}}, State) ->
+ {stop, ChannelId, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_msg(Args) -> {ok, State} | {stop, ChannelId, State}
+%%
+%% Description: Handles channel messages
+%%--------------------------------------------------------------------
+
+handle_msg({ssh_channel_up, _, _}, State) ->
+ {ok, State};
+
+%% Connection manager goes down
+handle_msg({'DOWN', _Ref, _Type, _Process, _},
+ #state{channel = ChannelId} = State) ->
+ {stop, ChannelId, State};
+
+%% Stopped by user
+handle_msg({'EXIT', _, sshrpc_stop_channel},
+ #state{channel = ChannelId} = State) ->
+ {stop, ChannelId, State};
+
+handle_msg(_, State) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: Called when the channel process is terminated
+%%--------------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% see ssh_channel manual
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%% ssh_xfer:connect
+connect(Host, Port, Opts) ->
+ case ssh:connect(Host, Port, Opts) of
+ {ok, CM} ->
+ open_subsystem(CM, Opts);
+ Error -> Error
+ end.
+
+% internal
+% see ssh_xfer:open_xfer/2
+
+open_subsystem(CM, Opts) ->
+ Timeout = proplists:get_value(timeout, Opts, infinity),
+ case ssh_connection:session_channel(CM,
+ ?SSHRPC_WINDOW_SIZE,
+ ?SSHRPC_PACKET_SIZE,
+ Timeout) of
+ {ok, ChannelId} ->
+ {ok, ChannelId, CM};
+ Error ->
+ Error
+ end.
+
+%% general form of call:
+%% call(Pid, Operation, Timeout)
+%% operation part: use tuple for multiple arguments
+%% passing messages onto ssh_channel:call/3 has infinity timeout
+
+call(Pid, Msg, TimeOut) ->
+ ssh_channel:call(Pid, {{timeout, TimeOut}, Msg}, infinity).
+
+send_command(Pid, ChannelId, Cmd) ->
+ Bin = term_to_binary(Cmd),
+ Len = size(Bin),
+ ssh_connection:send(Pid, ChannelId,
+ <<?UINT32(Len), Bin/binary>>,
+ infinity).
+
+handle_reply(State, <<?UINT32(Len),Reply:Len/binary,Rest/binary>>) ->
+ do_handle_reply(State, Reply, Rest);
+handle_reply(State, Data) ->
+ State#state{reply_buf = Data}.
+
+do_handle_reply(#state{from = From} = State, Reply, Rest) ->
+ Msg = binary_to_term(Reply),
+ ssh_channel:reply(From, Msg),
+ handle_reply(State, Rest).
+
+%% end of file
+
+
View
160 src/sshrpc_subsystem.erl
@@ -0,0 +1,160 @@
+%% Copyright (c) 2009-2010 Kenji Rikitake. 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.
+
+%% @author Kenji Rikitake
+%% @copyright 2009-2010 Kenji Rikitake
+
+%% @doc general sshrpc server-side subsystem.
+
+-module(sshrpc_subsystem).
+
+-behaviour(ssh_channel).
+
+-include("sshrpc.hrl").
+
+%% ssh_channel callbacks
+-export([subsystem_spec/1]).
+-export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2, code_change/3]).
+
+%% state
+-record(state, {
+ cm,
+ channel,
+ pending % binary()
+ }).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%% subsystem name conforms the private method and algorithm
+%% convention on RFC4251 Section 6.
+
+subsystem_spec(Options) ->
+ {"sshrpc@k2r.org", {?MODULE, Options}}.
+
+%%====================================================================
+%% ssh_channel callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State}
+%%
+%% Description: Initiates the CLI
+%%--------------------------------------------------------------------
+init(_Options) ->
+ {ok, #state{pending = <<>>}}.
+
+%%--------------------------------------------------------------------
+%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description:
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+%%--------------------------------------------------------------------
+%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State}
+%%
+%% Description: Handles channel messages received on the ssh-connection.
+%%--------------------------------------------------------------------
+%% Type 0: RPC messages
+%% other types: reserved
+handle_ssh_msg({ssh_cm, _ConnectionManager,
+ {data, _ChannelId, 0, Data}},
+ State) ->
+ State1 = handle_data(Data, State),
+ {ok, State1};
+
+handle_ssh_msg({ssh_cm, _, {eof, ChannelId}}, State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, State) ->
+ Report = io_lib:format("Connection closed by peer ~n Error ~p~n",
+ [Error]),
+ error_logger:error_report(Report),
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, 0}}, State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State) ->
+ Report = io_lib:format("Connection closed by peer ~n Status ~p~n",
+ [Status]),
+ error_logger:error_report(Report),
+ {stop, ChannelId, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_msg(Args) -> {ok, State} | {stop, ChannelId, State}
+%%
+%% Description: Handles other channel messages.
+%%--------------------------------------------------------------------
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ {ok, State#state{
+ channel = ChannelId,
+ cm = ConnectionManager}}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: Called when the channel process is trminated
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+%% handling received data
+%% see ssh_sftpd.erl
+
+handle_data(<<?UINT32(Len), Msg:Len/binary, Rest/binary>>,
+ #state{cm = ConnectionManager,
+ channel = ChannelId,
+ pending = <<>>} = State) ->
+ {Reply, Status} = exec_bin(Msg),
+ Bin = term_to_binary({answer,{Reply, Status}}),
+ Len = size(Bin),
+ ssh_connection:send(ConnectionManager, ChannelId,
+ <<?UINT32(Len), Bin/binary>>),
+ case Rest of
+ <<>> -> State;
+ _ -> handle_data(Rest, State)
+ end;
+
+handle_data(Data, State = #state{pending = <<>>}) ->
+ State#state{pending = Data};
+
+handle_data(Data, State = #state{pending = Pending}) ->
+ handle_data(<<Pending/binary, Data/binary>>,
+ State#state{pending = <<>>}).
+
+%% parsing a tuple and apply it
+
+exec_bin(Cmdbin) ->
+ case binary_to_term(Cmdbin) of
+ {mfa, {M, F, A}} ->
+ case catch apply(M, F, A) of
+ {'EXIT', _} = Exit ->
+ {{exec_badrpc, Exit}, -1};
+ Reply ->
+ {Reply, 0}
+ end;
+ Error ->
+ {{exec_error,Error}, -1}
+ end.
+
+%% end of file
+

0 comments on commit d08dec3

Please sign in to comment.