1%% ``Licensed under the Apache License, Version 2.0 (the "License");
2%% you may not use this file except in compliance with the License.
3%% You may obtain a copy of the License at
4%%
5%%     http://www.apache.org/licenses/LICENSE-2.0
6%%
7%% Unless required by applicable law or agreed to in writing, software
8%% distributed under the License is distributed on an "AS IS" BASIS,
9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10%% See the License for the specific language governing permissions and
11%% limitations under the License.
12%%
13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
15%% AB. All Rights Reserved.''
16%%
17%%     $Id$
18%%
19-module(uds_dist).
20
21%% Handles the connection setup phase with other Erlang nodes.
22
23-export([childspecs/0, listen/1, accept/1, accept_connection/5,
24	 setup/4, close/1, select/1, is_node_name/1]).
25
26%% internal exports
27
28-export([accept_loop/2,do_accept/6,do_setup/5, getstat/1,tick/1]).
29
30-import(error_logger,[error_msg/2]).
31
32-include("net_address.hrl").
33
34
35
36-define(to_port(Socket, Data),
37	case uds:send(Socket, Data) of
38	    {error, closed} ->
39		self() ! {uds_closed, Socket},
40	        {error, closed};
41	    R ->
42	        R
43        end).
44
45
46-include("dist.hrl").
47-include("dist_util.hrl").
48-record(tick, {read = 0,
49	       write = 0,
50	       tick = 0,
51	       ticked = 0
52	       }).
53
54
55%% -------------------------------------------------------------
56%% This function should return a valid childspec, so that
57%% the primitive ssl_server gets supervised
58%% -------------------------------------------------------------
59childspecs() ->
60    {ok, [{uds_server,{uds_server, start_link, []},
61	   permanent, 2000, worker, [uds_server]}]}.
62
63
64%% ------------------------------------------------------------
65%%  Select this protocol based on node name
66%%  select(Node) => Bool
67%% ------------------------------------------------------------
68
69select(Node) ->
70    {ok, MyHost} = inet:gethostname(),
71    case split_node(atom_to_list(Node), $@, []) of
72	[_, MyHost] ->
73	    true;
74	_ ->
75	    false
76    end.
77
78%% ------------------------------------------------------------
79%% Create the listen socket, i.e. the port that this erlang
80%% node is accessible through.
81%% ------------------------------------------------------------
82
83listen(Name) ->
84    case uds:listen(atom_to_list(Name)) of
85	{ok, Socket} ->
86	    {ok, {Socket,
87		  #net_address{address = [],
88			       host = inet:gethostname(),
89			       protocol = uds,
90			       family = uds},
91		  uds:get_creation(Socket)}};
92	Error ->
93	    Error
94    end.
95
96%% ------------------------------------------------------------
97%% Accepts new connection attempts from other Erlang nodes.
98%% ------------------------------------------------------------
99
100accept(Listen) ->
101    spawn_link(?MODULE, accept_loop, [self(), Listen]).
102
103accept_loop(Kernel, Listen) ->
104    process_flag(priority, max),
105    case uds:accept(Listen) of
106	{ok, Socket} ->
107	    Kernel ! {accept,self(),Socket,uds,uds},
108	    controller(Kernel, Socket),
109	    accept_loop(Kernel, Listen);
110	Error ->
111	    exit(Error)
112    end.
113
114controller(Kernel, Socket) ->
115    receive
116	{Kernel, controller, Pid} ->
117	    uds:controlling_process(Socket, Pid),
118	    Pid ! {self(), controller};
119	{Kernel, unsupported_protocol} ->
120	    exit(unsupported_protocol)
121    end.
122
123%% ------------------------------------------------------------
124%% Accepts a new connection attempt from another Erlang node.
125%% Performs the handshake with the other side.
126%% ------------------------------------------------------------
127
128accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
129    spawn_link(?MODULE, do_accept,
130	       [self(), AcceptPid, Socket, MyNode,
131		Allowed, SetupTime]).
132
133do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
134    process_flag(priority, max),
135    receive
136	{AcceptPid, controller} ->
137	    Timer = dist_util:start_timer(SetupTime),
138	    HSData = #hs_data{
139	      kernel_pid = Kernel,
140	      this_node = MyNode,
141	      socket = Socket,
142	      timer = Timer,
143	      this_flags = ?DFLAG_PUBLISHED bor
144	      ?DFLAG_ATOM_CACHE bor
145	      ?DFLAG_EXTENDED_REFERENCES bor
146	      ?DFLAG_DIST_MONITOR bor
147	      ?DFLAG_FUN_TAGS,
148	      allowed = Allowed,
149	      f_send = fun(S,D) -> uds:send(S,D) end,
150	      f_recv = fun(S,N,T) -> uds:recv(S)
151		       end,
152	      f_setopts_pre_nodeup =
153	      fun(S) ->
154		      uds:set_mode(S, intermediate)
155	      end,
156	      f_setopts_post_nodeup =
157	      fun(S) ->
158		      uds:set_mode(S, data)
159	      end,
160	      f_getll = fun(S) ->
161				uds:get_port(S)
162			end,
163	      f_address = fun get_remote_id/2,
164	      mf_tick = {?MODULE, tick},
165	      mf_getstat = {?MODULE,getstat}
166	     },
167	    dist_util:handshake_other_started(HSData)
168    end.
169
170%% ------------------------------------------------------------
171%% Get remote information about a Socket.
172%% ------------------------------------------------------------
173
174get_remote_id(Socket, Node) ->
175    [_, Host] = split_node(atom_to_list(Node), $@, []),
176    #net_address {
177		  address = [],
178		  host = Host,
179		  protocol = uds,
180		  family = uds }.
181
182%% ------------------------------------------------------------
183%% Setup a new connection to another Erlang node.
184%% Performs the handshake with the other side.
185%% ------------------------------------------------------------
186
187setup(Node, MyNode, LongOrShortNames,SetupTime) ->
188    spawn_link(?MODULE, do_setup, [self(),
189				   Node,
190				   MyNode,
191				   LongOrShortNames,
192				   SetupTime]).
193
194do_setup(Kernel, Node, MyNode, LongOrShortNames,SetupTime) ->
195    process_flag(priority, max),
196    ?trace("~p~n",[{uds_dist,self(),setup,Node}]),
197    [Name, Address] = splitnode(Node, LongOrShortNames),
198    {ok, MyName} = inet:gethostname(),
199    case Address of
200	MyName ->
201	    Timer = dist_util:start_timer(SetupTime),
202	    case uds:connect(Name) of
203		{ok, Socket} ->
204		    HSData = #hs_data{
205		      kernel_pid = Kernel,
206		      other_node = Node,
207		      this_node = MyNode,
208		      socket = Socket,
209		      timer = Timer,
210		      this_flags = ?DFLAG_PUBLISHED bor
211		      ?DFLAG_ATOM_CACHE bor
212		      ?DFLAG_EXTENDED_REFERENCES bor
213		      ?DFLAG_DIST_MONITOR bor
214		      ?DFLAG_FUN_TAGS,
215		      other_version = 1,
216		      f_send = fun(S,D) ->
217				       uds:send(S,D)
218			       end,
219			      f_recv = fun(S,N,T) ->
220					       uds:recv(S)
221				       end,
222		      f_setopts_pre_nodeup =
223		      fun(S) ->
224			      uds:set_mode(S, intermediate)
225		      end,
226		      f_setopts_post_nodeup =
227		      fun(S) ->
228			      uds:set_mode(S, data)
229		      end,
230		      f_getll = fun(S) ->
231					uds:get_port(S)
232				end,
233		      f_address =
234		      fun(_,_) ->
235			      #net_address{
236			        address = [],
237			        host = Address,
238			        protocol = uds,
239			        family = uds}
240		      end,
241		      mf_tick = {?MODULE, tick},
242		      mf_getstat = {?MODULE,getstat}
243		     },
244		    dist_util:handshake_we_started(HSData);
245		_ ->
246		    ?shutdown(Node)
247	    end;
248	Other ->
249	    ?shutdown(Node)
250    end.
251
252%%
253%% Close a socket.
254%%
255close(Socket) ->
256    uds:close(Socket).
257
258
259%% If Node is illegal terminate the connection setup!!
260splitnode(Node, LongOrShortNames) ->
261    case split_node(atom_to_list(Node), $@, []) of
262	[Name|Tail] when Tail /= [] ->
263	    Host = lists:append(Tail),
264	    case split_node(Host, $., []) of
265		[_] when LongOrShortNames == longnames ->
266		    error_msg("** System running to use "
267			      "fully qualified "
268			      "hostnames **~n"
269			      "** Hostname ~s is illegal **~n",
270			      [Host]),
271		    ?shutdown(Node);
272		L when length(L) > 1, LongOrShortNames == shortnames ->
273		    error_msg("** System NOT running to use fully qualified "
274			      "hostnames **~n"
275			      "** Hostname ~s is illegal **~n",
276			      [Host]),
277		    ?shutdown(Node);
278		_ ->
279		    [Name, Host]
280	    end;
281	[_] ->
282	    error_msg("** Nodename ~p illegal, no '@' character **~n",
283		      [Node]),
284	    ?shutdown(Node);
285	_ ->
286	    error_msg("** Nodename ~p illegal **~n", [Node]),
287	    ?shutdown(Node)
288    end.
289
290split_node([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_node(T, Chr, [])];
291split_node([H|T], Chr, Ack)   -> split_node(T, Chr, [H|Ack]);
292split_node([], _, Ack)        -> [lists:reverse(Ack)].
293
294is_node_name(Node) when atom(Node) ->
295    case split_node(atom_to_list(Node), $@, []) of
296	[_, Host] -> true;
297	_ -> false
298    end;
299is_node_name(Node) ->
300    false.
301
302tick(Sock) ->
303    uds:tick(Sock).
304getstat(Socket) ->
305    uds:get_status_counters(Socket).
306