%% ``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.''
%%
%% $Id$
%%
-module(uds_dist).
%% Handles the connection setup phase with other Erlang nodes.
-export([childspecs/0, listen/1, accept/1, accept_connection/5,
setup/4, close/1, select/1, is_node_name/1]).
%% internal exports
-export([accept_loop/2,do_accept/6,do_setup/5, getstat/1,tick/1]).
-import(error_logger,[error_msg/2]).
-include("net_address.hrl").
-define(to_port(Socket, Data),
case uds:send(Socket, Data) of
{error, closed} ->
self() ! {uds_closed, Socket},
{error, closed};
R ->
R
end).
-include("dist.hrl").
-include("dist_util.hrl").
-record(tick, {read = 0,
write = 0,
tick = 0,
ticked = 0
}).
%% -------------------------------------------------------------
%% This function should return a valid childspec, so that
%% the primitive ssl_server gets supervised
%% -------------------------------------------------------------
childspecs() ->
{ok, [{uds_server,{uds_server, start_link, []},
permanent, 2000, worker, [uds_server]}]}.
%% ------------------------------------------------------------
%% Select this protocol based on node name
%% select(Node) => Bool
%% ------------------------------------------------------------
select(Node) ->
{ok, MyHost} = inet:gethostname(),
case split_node(atom_to_list(Node), $@, []) of
[_, MyHost] ->
true;
_ ->
false
end.
%% ------------------------------------------------------------
%% Create the listen socket, i.e. the port that this erlang
%% node is accessible through.
%% ------------------------------------------------------------
listen(Name) ->
case uds:listen(atom_to_list(Name)) of
{ok, Socket} ->
{ok, {Socket,
#net_address{address = [],
host = inet:gethostname(),
protocol = uds,
family = uds},
uds:get_creation(Socket)}};
Error ->
Error
end.
%% ------------------------------------------------------------
%% Accepts new connection attempts from other Erlang nodes.
%% ------------------------------------------------------------
accept(Listen) ->
spawn_link(?MODULE, accept_loop, [self(), Listen]).
accept_loop(Kernel, Listen) ->
process_flag(priority, max),
case uds:accept(Listen) of
{ok, Socket} ->
Kernel ! {accept,self(),Socket,uds,uds},
controller(Kernel, Socket),
accept_loop(Kernel, Listen);
Error ->
exit(Error)
end.
controller(Kernel, Socket) ->
receive
{Kernel, controller, Pid} ->
uds:controlling_process(Socket, Pid),
Pid ! {self(), controller};
{Kernel, unsupported_protocol} ->
exit(unsupported_protocol)
end.
%% ------------------------------------------------------------
%% Accepts a new connection attempt from another Erlang node.
%% Performs the handshake with the other side.
%% ------------------------------------------------------------
accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
spawn_link(?MODULE, do_accept,
[self(), AcceptPid, Socket, MyNode,
Allowed, SetupTime]).
do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
process_flag(priority, max),
receive
{AcceptPid, controller} ->
Timer = dist_util:start_timer(SetupTime),
HSData = #hs_data{
kernel_pid = Kernel,
this_node = MyNode,
socket = Socket,
timer = Timer,
this_flags = ?DFLAG_PUBLISHED bor
?DFLAG_ATOM_CACHE bor
?DFLAG_EXTENDED_REFERENCES bor
?DFLAG_DIST_MONITOR bor
?DFLAG_FUN_TAGS,
allowed = Allowed,
f_send = fun(S,D) -> uds:send(S,D) end,
f_recv = fun(S,N,T) -> uds:recv(S)
end,
f_setopts_pre_nodeup =
fun(S) ->
uds:set_mode(S, intermediate)
end,
f_setopts_post_nodeup =
fun(S) ->
uds:set_mode(S, data)
end,
f_getll = fun(S) ->
uds:get_port(S)
end,
f_address = fun get_remote_id/2,
mf_tick = {?MODULE, tick},
mf_getstat = {?MODULE,getstat}
},
dist_util:handshake_other_started(HSData)
end.
%% ------------------------------------------------------------
%% Get remote information about a Socket.
%% ------------------------------------------------------------
get_remote_id(Socket, Node) ->
[_, Host] = split_node(atom_to_list(Node), $@, []),
#net_address {
address = [],
host = Host,
protocol = uds,
family = uds }.
%% ------------------------------------------------------------
%% Setup a new connection to another Erlang node.
%% Performs the handshake with the other side.
%% ------------------------------------------------------------
setup(Node, MyNode, LongOrShortNames,SetupTime) ->
spawn_link(?MODULE, do_setup, [self(),
Node,
MyNode,
LongOrShortNames,
SetupTime]).
do_setup(Kernel, Node, MyNode, LongOrShortNames,SetupTime) ->
process_flag(priority, max),
?trace("~p~n",[{uds_dist,self(),setup,Node}]),
[Name, Address] = splitnode(Node, LongOrShortNames),
{ok, MyName} = inet:gethostname(),
case Address of
MyName ->
Timer = dist_util:start_timer(SetupTime),
case uds:connect(Name) of
{ok, Socket} ->
HSData = #hs_data{
kernel_pid = Kernel,
other_node = Node,
this_node = MyNode,
socket = Socket,
timer = Timer,
this_flags = ?DFLAG_PUBLISHED bor
?DFLAG_ATOM_CACHE bor
?DFLAG_EXTENDED_REFERENCES bor
?DFLAG_DIST_MONITOR bor
?DFLAG_FUN_TAGS,
other_version = 1,
f_send = fun(S,D) ->
uds:send(S,D)
end,
f_recv = fun(S,N,T) ->
uds:recv(S)
end,
f_setopts_pre_nodeup =
fun(S) ->
uds:set_mode(S, intermediate)
end,
f_setopts_post_nodeup =
fun(S) ->
uds:set_mode(S, data)
end,
f_getll = fun(S) ->
uds:get_port(S)
end,
f_address =
fun(_,_) ->
#net_address{
address = [],
host = Address,
protocol = uds,
family = uds}
end,
mf_tick = {?MODULE, tick},
mf_getstat = {?MODULE,getstat}
},
dist_util:handshake_we_started(HSData);
_ ->
?shutdown(Node)
end;
Other ->
?shutdown(Node)
end.
%%
%% Close a socket.
%%
close(Socket) ->
uds:close(Socket).
%% If Node is illegal terminate the connection setup!!
splitnode(Node, LongOrShortNames) ->
case split_node(atom_to_list(Node), $@, []) of
[Name|Tail] when Tail /= [] ->
Host = lists:append(Tail),
case split_node(Host, $., []) of
[_] when LongOrShortNames == longnames ->
error_msg("** System running to use "
"fully qualified "
"hostnames **~n"
"** Hostname ~s is illegal **~n",
[Host]),
?shutdown(Node);
L when length(L) > 1, LongOrShortNames == shortnames ->
error_msg("** System NOT running to use fully qualified "
"hostnames **~n"
"** Hostname ~s is illegal **~n",
[Host]),
?shutdown(Node);
_ ->
[Name, Host]
end;
[_] ->
error_msg("** Nodename ~p illegal, no '@' character **~n",
[Node]),
?shutdown(Node);
_ ->
error_msg("** Nodename ~p illegal **~n", [Node]),
?shutdown(Node)
end.
split_node([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_node(T, Chr, [])];
split_node([H|T], Chr, Ack) -> split_node(T, Chr, [H|Ack]);
split_node([], _, Ack) -> [lists:reverse(Ack)].
is_node_name(Node) when atom(Node) ->
case split_node(atom_to_list(Node), $@, []) of
[_, Host] -> true;
_ -> false
end;
is_node_name(Node) ->
false.
tick(Sock) ->
uds:tick(Sock).
getstat(Socket) ->
uds:get_status_counters(Socket).