%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.0, (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.erlang.org/EPL1_0.txt
%% 
%% 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 Original Code is Erlang-4.7.3, December, 1998.
%% 
%% The Initial Developer of the Original Code is Ericsson Telecom
%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
%% Telecom AB. All Rights Reserved.
%% 
%% Contributor(s): ______________________________________.''
%%
%%%----------------------------------------------------------------------
%%% File    : global_group.erl
%%% Author  : Esko Vierumaki <esko@gimli>
%%% Purpose : Groups nodes into global groups with an own global name space.
%%% Created : 23 Mar 1998 by Esko Vierumaki <esko@gimli>
%%%----------------------------------------------------------------------

-module(global_group).
-author('esko@gimli').

-behaviour(gen_server).

%% External exports
-export([start/0, start_link/0, stop/0, init/1]).
-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

-export([global_groups/0]).
-export([monitor_nodes/1]).
-export([own_nodes/0]).
-export([registered_names/1]).
-export([send/2]).
-export([send/3]).
-export([whereis_name/1]).
-export([whereis_name/2]).

-export([info/0]).
-export([registered_names_test/1]).
-export([send_test/2]).
-export([whereis_name_test/1]).


-export([config_scan/1]).


%% Internal exports
-export([sync_init/2]).


%%%====================================================================================
%%% The state of the global_group process
%%% 
%%% sync_state =  not_synced | syncing | synced | no_conf
%%% group_name =  Own global group name
%%% nodes =       Nodes in the own global group
%%% other_grps =  list of other global group names and nodes, [{otherName, [Node]}]
%%% node_name =   Own node 
%%% monitor =     List of Pids requesting nodeup/nodedown
%%%====================================================================================
-record(state, {sync_state = not_synced, connect_all, group_name = [], 
		nodes = [], other_grps = [], node_name = node(), monitor = []}).




%%%====================================================================================
%%% External exported
%%%====================================================================================
global_groups() ->
    request(global_groups).

monitor_nodes(Flag) -> 
    case Flag of
	true -> request({monitor_nodes, Flag});
	false -> request({monitor_nodes, Flag});
	_ -> {error, not_boolean}
    end.

own_nodes() ->
    request(own_nodes).

registered_names(Arg) ->
    request({registered_names, Arg}).

send(Name, Msg) ->
    request({send, Name, Msg}).

send(Group, Name, Msg) ->
    request({send, Group, Name, Msg}).

whereis_name(Name) ->
    request({whereis_name, Name}).

whereis_name(Group, Name) ->
    request({whereis_name, Group, Name}).


info() ->
    request(info).

%% ==== ONLY for test suites ====
registered_names_test(Arg) ->
    request({registered_names_test, Arg}).
send_test(Name, Msg) ->
    request({send_test, Name, Msg}).
whereis_name_test(Name) ->
    request({whereis_name_test, Name}).
%% ==== ONLY for test suites ====


request(Req) ->
    case whereis(global_group) of
	P when pid(P) ->
	    gen_server:call(global_group, Req, infinity);
	Other -> {error, global_group_not_runnig}
    end.



%%%====================================================================================
%%% gen_server start
%%%
%%% The first thing to happen is to read if the node_groups key is defined in the
%%% .config file. If not, the whole system is started as one global_group, and the
%%% services of global_group are superfluous.
%%% Otherwise a process is started which checks that all nodes in the own global
%%% group have the same configuration. This is done by sending 'conf_check' to all
%%% other nodes and requiring 'conf_check_result' back.
%%% If the nodes are not in agreement of the configuration the global_group process 
%%% exits.
%%% The three process dictionary variables are used to store information need if
%%% the search process crashes. The search process is a help process to fine
%%% registered names in the system.
%%%====================================================================================
start() -> gen_server:start({local, global_group}, global_group, [], []).
start_link() -> gen_server:start_link({local, global_group}, global_group,[],[]).
stop() -> gen_server:call(global_group, stop, infinity).

init([]) ->
%    io:format("~p global_group init ~n",[node()]),
    process_flag(priority, max),
    put(registered_names, [undefined]),
    put(send, [undefined]),
    put(whereis_name, [undefined]),
    process_flag(trap_exit, true),
    Ca = case init:get_argument(connect_all) of
	     {ok, [["false"]]} ->
		 false;
	     _ ->
		 true
	 end,

    case application:get_env(kernel, node_groups) of
	undefined ->
	    global:sync(), %% Moved here from kernel_config.erl
	    {ok, #state{sync_state = no_conf, connect_all = Ca}};
	{ok, []} ->
	    global:sync(), %% Moved here from kernel_config.erl
	    {ok, #state{sync_state = no_conf, connect_all = Ca}};
	{ok, NodeGrps} ->
	    gen_server:cast(global_group, {sync, NodeGrps}),
	    {ok, #state{connect_all = Ca}}
    end.



%%%====================================================================================
%%% global_groups() -> {OwnGroupName, [OtherGroupName]}
%%%
%%% Get the names of the global groups
%%%====================================================================================
handle_call(global_groups, From, S) ->
    Result = case S#state.sync_state of
		 no_conf ->
		     undefined;
		 not_synced ->
		     undefined;
		 syncing ->
		     Other = lists:foldl(fun({N,L}, Acc) -> Acc ++ [N]
					 end,
					 [], S#state.other_grps),
		     {S#state.group_name, Other};
		 synced ->
		     Other = lists:foldl(fun({N,L}, Acc) -> Acc ++ [N]
					 end,
					 [], S#state.other_grps),
		     {S#state.group_name, Other}
	     end,
    {reply, Result, S};



%%%====================================================================================
%%% monitor_nodes(bool()) -> ok 
%%%
%%% Monitor nodes in the own global group. 
%%%   True => send nodeup/nodedown to the requesting Pid
%%%   False => stop sending nodeup/nodedown to the requesting Pid
%%%====================================================================================
handle_call({monitor_nodes, Flag}, {Pid, _}, StateIn) ->
%    io:format("***** handle_call ~p~n",[monitor_nodes]),
    {Res, State} = monitor_nodes(Flag, Pid, StateIn),
    {reply, Res, State};


%%%====================================================================================
%%% own_nodes() -> [Node] | {error, ErrorMessage}
%%%
%%% Get a list of nodes in the own global group
%%%====================================================================================
handle_call(own_nodes, From, S) ->
    Nodes = case S#state.sync_state of
		no_conf ->
		    [node()|nodes()];
		not_synced ->
		     {error, 'global group not synced'};
		syncing ->
		    S#state.nodes;
		synced ->
		    S#state.nodes
	    end,
    {reply, Nodes, S};



%%%====================================================================================
%%% registered_names({node, Node}) -> [Name] | {error, ErrorMessage}
%%% registered_names({group, GlobalGroupName}) -> [Name] | {error, ErrorMessage}
%%%
%%% Get the registered names from a specified Node, or GlobalGroupName.
%%%====================================================================================
handle_call({registered_names, {group, Group}}, From, S) when Group == S#state.group_name ->
    Res = global:registered_names(),
    {reply, Res, S};
handle_call({registered_names, {group, Group}}, From, S) ->
    case lists:keysearch(Group, 1, S#state.other_grps) of
	false ->
	    {reply, [], S};
	{value, {Group, []}} ->
	    {reply, [], S};
	{value, {Group, Nodes}} ->
	    Pid = global_search:start(names, {group, Nodes, From}),
	    Wait = get(registered_names),
	    put(registered_names, [{Pid, From} | Wait]),
	    {noreply, S}
    end;
handle_call({registered_names, {node, Node}}, From, S) when Node == node() ->
    Res = global:registered_names(),
    {reply, Res, S};
handle_call({registered_names, {node, Node}}, From, S) ->
    Pid = global_search:start(names, {node, Node, From}),
%    io:format(">>>>> registered_names Pid ~p~n",[Pid]),
    Wait = get(registered_names),
    put(registered_names, [{Pid, From} | Wait]),
    {noreply, S};




%%%====================================================================================
%%% send(Name, Msg) -> Pid | {error, ErrorMessage}
%%% send({node, Node}, Name, Msg) -> Pid | {error, ErrorMessage}
%%% send({group, GlobalGroupName}, Name, Msg) -> Pid | {error, ErrorMessage}
%%%
%%% Send the Msg to the specified globally registered Name in own global group,
%%% in specified Node, or GlobalGroupName.
%%% But first the receiver is to be found, the thread is continued at
%%% handle_cast(send_res)
%%%====================================================================================
%% Search in the whole known world, but check own node first.
handle_call({send, Name, Msg}, From, S) ->
    case global:whereis_name(Name) of
	undefined ->
	    Pid = global_search:start(send, {any, S#state.other_grps, Name, Msg, From}),
	    Wait = get(send),
	    put(send, [{Pid, From, Name, Msg} | Wait]),
	    {noreply, S};
	Found ->
	    Found ! Msg,
	    {reply, Found, S}
    end;
%% Search in the specified global group, which happens to be the own group.
handle_call({send, {group, Grp}, Name, Msg}, From, S) when Grp == S#state.group_name ->
    case global:whereis_name(Name) of
	undefined ->
	    {reply, {badarg, {Name, Msg}}, S};
	Pid ->
	    Pid ! Msg,
	    {reply, Pid, S}
    end;
%% Search in the specified global group.
handle_call({send, {group, Group}, Name, Msg}, From, S) ->
    case lists:keysearch(Group, 1, S#state.other_grps) of
	false ->
	    {reply, {badarg, {Name, Msg}}, S};
	{value, {Group, []}} ->
	    {reply, {badarg, {Name, Msg}}, S};
	{value, {Group, Nodes}} ->
	    Pid = global_search:start(send, {group, Nodes, Name, Msg, From}),
	    Wait = get(send),
	    put(send, [{Pid, From, Name, Msg} | Wait]),
	    {noreply, S}
    end;
%% Search on the specified node.
handle_call({send, {node, Node}, Name, Msg}, From, S) ->
    Pid = global_search:start(send, {node, Node, Name, Msg, From}),
    Wait = get(send),
    put(send, [{Pid, From, Name, Msg} | Wait]),
    {noreply, S};



%%%====================================================================================
%%% whereis_name(Name) -> Pid | undefined
%%% whereis_name({node, Node}, Name) -> Pid | undefined
%%% whereis_name({group, GlobalGroupName}, Name) -> Pid | undefined
%%%
%%% Get the Pid of a globally registered Name in own global group,
%%% in specified Node, or GlobalGroupName.
%%% But first the process is to be found, 
%%% the thread is continued at handle_cast(find_name_res)
%%%====================================================================================
%% Search in the whole known world, but check own node first.
handle_call({whereis_name, Name}, From, S) ->
    case global:whereis_name(Name) of
	undefined ->
	    Pid = global_search:start(whereis, {any, S#state.other_grps, Name, From}),
	    Wait = get(whereis_name),
	    put(whereis_name, [{Pid, From} | Wait]),
	    {noreply, S};
	Found ->
	    {reply, Found, S}
    end;
%% Search in the specified global group, which happens to be the own group.
handle_call({whereis_name, {group, Group}, Name}, From, S) 
  when Group == S#state.group_name ->
    Res = global:whereis_name(Name),
    {reply, Res, S};
%% Search in the specified global group.
handle_call({whereis_name, {group, Group}, Name}, From, S) ->
    case lists:keysearch(Group, 1, S#state.other_grps) of
	false ->
	    {reply, undefined, S};
	{value, {Group, []}} ->
	    {reply, undefined, S};
	{value, {Group, Nodes}} ->
	    Pid = global_search:start(whereis, {group, Nodes, Name, From}),
	    Wait = get(whereis_name),
	    put(whereis_name, [{Pid, From} | Wait]),
	    {noreply, S}
    end;
%% Search on the specified node.
handle_call({whereis_name, {node, Node}, Name}, From, S) ->
    Pid = global_search:start(whereis, {node, Node, Name, From}),
    Wait = get(whereis_name),
    put(whereis_name, [{Pid, From} | Wait]),
    {noreply, S};





%%%====================================================================================
%%% Misceleaneous help function to read some variables
%%%====================================================================================
handle_call(info, From, S) ->
    {reply, S, S};
handle_call(get, From, S) ->
    {reply, get(), S};


%%%====================================================================================
%%% Only for test suites. These tests when the search process exits.
%%%====================================================================================
handle_call({registered_names_test, {node, 'test3844zty'}}, From, S) ->
    Pid = global_search:start(names_test, {node, 'test3844zty'}),
    Wait = get(registered_names),
    put(registered_names, [{Pid, From} | Wait]),
    {noreply, S};
handle_call({registered_names_test, {node, Node}}, From, S) ->
    {reply, {error, illegal_function_call}, S};
handle_call({send_test, Name, 'test3844zty'}, From, S) ->
    Pid = global_search:start(send_test, 'test3844zty'),
    Wait = get(send),
    put(send, [{Pid, From, Name, 'test3844zty'} | Wait]),
    {noreply, S};
handle_call({send_test, Name, Msg }, From, S) ->
    {reply, {error, illegal_function_call}, S};
handle_call({whereis_name_test, 'test3844zty'}, From, S) ->
    Pid = global_search:start(whereis_test, 'test3844zty'),
    Wait = get(whereis_name),
    put(whereis_name, [{Pid, From} | Wait]),
    {noreply, S};
handle_call({whereis_name_test, Name}, From, S) ->
    {reply, {error, illegal_function_call}, S};




handle_call(Call, From, S) ->
%    io:format("***** handle_call ~p~n",[Call]),
    {reply, {illegal_message, Call}, S}.
    




%%%====================================================================================
%%% Misceleaneous help function to read some variables
%%%====================================================================================
handle_cast({sync, NodeGrps}, S) ->

%    io:format("~p >>>>> sync  ~n",[node()]),

    {Group_Name, Nodes, Other} = 
	case catch config_scan(NodeGrps) of
	    {error, Error2} ->
		exit({error, {'invalid node_groups definition', NodeGrps}});
	    {Group_Name1, Nodes1, Other1} ->
		Pid = spawn_link(?MODULE, sync_init, [Group_Name1, Nodes1]),
		register(global_group_check, Pid),
		{Group_Name1, Nodes1, Other1}
	end,
    {noreply, S#state{sync_state = syncing, group_name = Group_Name, 
		      nodes = Nodes, other_grps = Other}};



%%%====================================================================================
%%% registered_names({node, Node}) -> [Name] | {error, ErrorMessage}
%%% registered_names({group, GlobalGroupName}) -> [Name] | {error, ErrorMessage}
%%%
%%% Get a list of nodes in the own global group
%%%====================================================================================
handle_cast({registered_names, User}, S) ->
%    io:format(">>>>> registered_names User ~p~n",[User]),
    Res = global:registered_names(),
    User ! {registered_names_res, Res},
    {noreply, S};

handle_cast({registered_names_res, Result, Pid, From}, S) ->
%    io:format(">>>>> registered_names_res Result ~p~n",[Result]),
    unlink(Pid),
    exit(Pid, normal),
    Wait = get(registered_names),
    NewWait = lists:delete({Pid, From},Wait),
    put(registered_names, NewWait),
    gen_server:reply(From, Result),
    {noreply, S};



%%%====================================================================================
%%% send(Name, Msg) -> Pid | {error, ErrorMessage}
%%% send({node, Node}, Name, Msg) -> Pid | {error, ErrorMessage}
%%% send({group, GlobalGroupName}, Name, Msg) -> Pid | {error, ErrorMessage}
%%%
%%% The registered Name is found; send the message to it, kill the search process,
%%% and return to the requesting process.
%%%====================================================================================
handle_cast({send_res, Result, Name, Msg, Pid, From}, S) ->
%    io:format("~p>>>>> send_res Result ~p~n",[node(), Result]),
    case Result of
	{badarg,{Name, Msg}} ->
	    continue;
	ToPid ->
	    ToPid ! Msg
    end,
    unlink(Pid),
    exit(Pid, normal),
    Wait = get(send),
    NewWait = lists:delete({Pid, From, Name, Msg},Wait),
    put(send, NewWait),
    gen_server:reply(From, Result),
    {noreply, S};



%%%====================================================================================
%%% A request from a search process to check if this Name is registered at this node.
%%%====================================================================================
handle_cast({find_name, User, Name}, S) ->
    Res = global:whereis_name(Name),
%    io:format(">>>>> find_name Name ~p   Res ~p~n",[Name, Res]),
    User ! {find_name_res, Res},
    {noreply, S};

%%%====================================================================================
%%% whereis_name(Name) -> Pid | undefined
%%% whereis_name({node, Node}, Name) -> Pid | undefined
%%% whereis_name({group, GlobalGroupName}, Name) -> Pid | undefined
%%%
%%% The registered Name is found; kill the search process
%%% and return to the requesting process.
%%%====================================================================================
handle_cast({find_name_res, Result, Pid, From}, S) ->
%    io:format(">>>>> find_name_res Result ~p~n",[Result]),
%    io:format(">>>>> find_name_res get() ~p~n",[get()]),
    unlink(Pid),
    exit(Pid, normal),
    Wait = get(whereis_name),
    NewWait = lists:delete({Pid, From},Wait),
    put(whereis_name, NewWait),
    gen_server:reply(From, Result),
    {noreply, S};


%%%====================================================================================
%%% The node is synced successfully
%%%====================================================================================
handle_cast(synced, S) ->
%    io:format("~p>>>>> synced  ~n",[node()]),

    unlink(whereis(global_group_check)),
    global_group_check ! kill,
    unregister(global_group_check),
    {noreply, S#state{sync_state = synced}};    


%%%====================================================================================
%%% Another node is checking this node's group configuration
%%%====================================================================================
handle_cast({conf_check, Node, Cname, Nodes}, S) when S#state.sync_state == no_conf ->
%    io:format(">>>>> conf_check   no_conf ~n"),
%    io:format(">>>>> conf_check, --no_conf-- Node ~p~n",[Node]),
    {global_group_check, Node} ! {no_global_group_configuration, node()},
    {noreply, S};
handle_cast({conf_check, Node, Cname, Nodes}, S) when S#state.sync_state == not_synced ->
%    io:format(">>>>> conf_check   not_synced ~n"),
%    io:format(">>>>> conf_check, --not_synced-- Node ~p~n",[Node]),
    case {S#state.group_name, S#state.nodes} of
	{[],[]} -> 
	    %% The check of configuraton not yet finished,
	    %% When this node comes up the check is done towards the other Node.
	    {global_group_check, Node} ! {config_ok, node()};
	_ -> 
	    {global_group_check, Node} ! {config_error, node()},
	    exit({invalid_configuration, {disagreeing_nodes, {node(), Node}}})
    end,
    {noreply, S};
handle_cast({conf_check, Node, Cname, Nodes}, S) -> %% sync_state == synced | syncing
%    io:format(">>>>> conf_check, Node ~p~n",[Node]),
    case {S#state.group_name, S#state.nodes} of 
	{Cname, Nodes} ->
	    global_name_server ! {nodeup, Node},
	    {global_group_check, Node} ! {config_ok, node()};
	_ ->
	    {global_group_check, Node} ! {config_error, node()},
	    exit({invalid_configuration, {disagreeing_nodes, {node(), Node}}})
    end,
    {noreply, S};





handle_cast(Cast, S) ->
%    io:format("***** handle_cast ~p~n",[Cast]),
    {noreply, S}.
    


%%%====================================================================================
%%% A node went down. If no global group configuration inform global;
%%% if global group configuration inform global only if the node is one in
%%% the own global group.
%%%====================================================================================
handle_info({nodeup, Node}, S) when S#state.sync_state == no_conf ->
%    io:format("~p>>>>> nodeup, Node ~p ~n",[node(), Node]),
    send_list(S#state.monitor, {nodeup, Node}),
    global_name_server ! {nodeup, Node},
    {noreply, S};
handle_info({nodeup, Node}, S) ->
%    io:format("~p>>>>> nodeup, Node ~p ~n",[node(), Node]),
    case {S#state.sync_state, lists:member(Node, S#state.nodes)} of
	{synced, true} ->
%	    io:format(" ##### Informing global {nodeup,~p}~n",[Node]),
	    send_list(S#state.monitor, {nodeup, Node}),
	    global_name_server ! {nodeup, Node};
	_ ->
	    cont
    end,
    {noreply, S};

%%%====================================================================================
%%% A node has come up. If no global group configuration inform global;
%%% if global group configuration inform global only if the node is one in
%%% the own global group.
%%%====================================================================================
handle_info({nodedown, Node}, S) when S#state.sync_state == no_conf ->
%    io:format("~p>>>>> nodedown, no_conf Node ~p~n",[node(), Node]),
    send_list(S#state.monitor, {nodedown, Node}),
    global_name_server ! {nodedown, Node},
    {noreply, S};
handle_info({nodedown, Node}, S) ->
%    io:format("~p>>>>> nodedown, Node ~p  ~n",[node(), Node]),
    case {S#state.sync_state, lists:member(Node, S#state.nodes)} of
	{synced, true} ->
	    send_list(S#state.monitor, {nodedown, Node}),
	    global_name_server ! {nodedown, Node};
	_ ->
	    cont
    end,
    {noreply, S};

handle_info({'EXIT', ExitPid, Reason}, S) ->
    check_exit(ExitPid, Reason),
    {noreply, S};


handle_info(Info, S) ->
%    io:format("***** handle_info = ~p~n",[Info]),
    {noreply, S}.



terminate(_Reason, S) ->
    ok.
    





%%%====================================================================================
%%% Check the global group configuration.
%%%====================================================================================
config_scan(NodeGrps) ->
    Sname = init:get_argument(sname),
    Lname = init:get_argument(name),
    MyNode = case {Sname, Lname} of
		 {{ok,[[MyNode1]]}, error} ->
		     list_to_atom(MyNode1);
		 {error, {ok,[[MyNode1]]}} ->
		     list_to_atom(MyNode1);
		 _ ->
		     node()
	     end,
    config_scan(node(), NodeGrps, no_name, [], []).

config_scan(MyNode, [], Own_name, OwnNodes, OtherNodeGrps) ->
    {Own_name, OwnNodes, lists:reverse(OtherNodeGrps)};
config_scan(MyNode, [{Name, Nodes}|NodeGrps], Own_name, OwnNodes, OtherNodeGrps) ->
    case lists:member(MyNode, Nodes) of
	true ->
	    case Own_name of
		no_name ->
		    config_scan(MyNode, NodeGrps, Name, Nodes, OtherNodeGrps);
		_ ->
		    {error, {'node defined twice', {Own_name, Name}}}
	    end;
	false ->
	    config_scan(MyNode, NodeGrps, Own_name, OwnNodes, [{Name, Nodes}|OtherNodeGrps])
    end.


    

    
%%%====================================================================================
%%% The special process which checks that all nodes in the own global group
%%% agrees on the configuration.
%%%====================================================================================
sync_init(Cname, Nodes) ->
%    io:format("------ sync_check_init Nodes ~p~n", [Nodes]),
    {Up1, Down} = sync_check_node(Nodes--[node()], [], []),
    sync_check_init(Up1, Cname, Nodes).

sync_check_node([], Up, Down) ->
    {Up, Down};
sync_check_node([Node|Nodes], Up, Down) ->
    case net_adm:ping(Node) of
	pang ->
	    sync_check_node(Nodes, Up, [Node|Down]);
	pong ->
	    sync_check_node(Nodes, [Node|Up], Down)
    end.



%%%-------------------------------------------------------------
%%% Check that all nodes are in agreement of the global
%%% group configuration.
%%%-------------------------------------------------------------
sync_check_init(Up, Cname, Nodes) ->
%    io:format("------ sync_check_init Up ~p~n", [Up]),
    lists:foreach(fun(Node) -> 
			  gen_server:cast({global_group, Node}, 
					  {conf_check, node(), Cname, Nodes})  
		  end, Up),
    Rem1 = sync_check(Up, Up),
    %% Try a couple of times to reach the global_group, 
    %% obviously the node is up but not the global_group process.
    lists:foreach(fun(Node) -> 
			  gen_server:cast({global_group, Node}, 
					  {conf_check, node(), Cname, Nodes})  
		  end, Rem1),
    Rem2 = sync_check(Rem1, Rem1),
    lists:foreach(fun(Node) -> 
			  gen_server:cast({global_group, Node}, 
					  {conf_check, node(), Cname, Nodes})  
		  end, Rem2),
    Rem3 = sync_check(Rem2, Rem2),
    %% There are still some nodes where the global_group process has not started yet,
    %% ignore these nodes for now. These nodes will check our node when the 
    %% global_group process starts.
    sync_check([], []).

sync_check([], Up) ->
    gen_server:cast(global_group, synced),
    receive
	kill ->
	    exit(normal)
    end;
sync_check(Rem, Up) ->
%    io:format("------ sync_check Waiting config_ok ~p~n", [Rem]),
    receive
	{config_ok, Node} ->
%	    io:format(">>>>> nodeup ~p  ~n",[Node]),
	    global_name_server ! {nodeup, Node},
	    sync_check(Rem -- [Node], Up);
	{config_error, Node} ->
	    exit({invalid_configuration, {disagreeing_nodes, {node(), Node}}});
	{no_global_group_configuration, Node} ->
	    exit({invalid_configuration, {no_global_group_configuration, Node}})
    after 2000 ->
	    %% Try again, the previous conf_check message  
	    %% apparently went to the magic black hole.
	    Rem
    end.






%%%====================================================================================
%%% A process wants to toggle monitoring nodeup/nodedown from nodes.
%%%====================================================================================
monitor_nodes(true, Pid, State) ->
    link(Pid),
    Monitor = State#state.monitor,
    {ok, State#state{monitor = [Pid|Monitor]}};
monitor_nodes(false, Pid, State) ->
    Monitor = State#state.monitor,
    State1 = State#state{monitor = delete_all(Pid,Monitor)},
    do_unlink(Pid, State1),
    {ok, State1};
monitor_nodes(_, _, State) ->
    {error, State}.

delete_all(From, [From |Tail]) -> delete_all(From, Tail);
delete_all(From, [H|Tail]) ->  [H|delete_all(From, Tail)];
delete_all(_, []) -> [].

%% do unlink if we have no more references to Pid.
do_unlink(Pid, State) ->
    case lists:member(Pid, State#state.monitor) of
	true ->
	    false;
	_ ->
%	    io:format("unlink(Pid) ~p~n",[Pid]),
	    unlink(Pid)
    end.



%%%====================================================================================
%%% Send a message to several nodes.
%%%====================================================================================
send_list([P|T], M) -> safesend(P, M), send_list(T, M);
send_list([], _) -> ok.

safesend(Name,Mess) when atom(Name) ->
    case whereis(Name) of 
	undefined ->
	    Mess;
	P when pid(P) ->
	    P ! Mess
    end;
safesend(Pid, Mess) -> Pid ! Mess.






%%%====================================================================================
%%% Check which user is associated to the crashed process.
%%%====================================================================================
check_exit(ExitPid, Reason) ->
%    io:format("===EXIT===  ~p ~p ~n~p   ~n~p   ~n~p ~n~n",[ExitPid, Reason, get(registered_names), get(send), get(whereis_name)]),
    check_exit_reg(get(registered_names), ExitPid, Reason),
    check_exit_send(get(send), ExitPid, Reason),
    check_exit_where(get(whereis_name), ExitPid, Reason).


check_exit_reg(undefined, ExitPid, Reason) ->
    ok;
check_exit_reg(Reg, ExitPid, Reason) ->
    case lists:keysearch(ExitPid, 1, lists:delete(undefined, Reg)) of
	{value, {ExitPid, From}} ->
	    NewReg = lists:delete({ExitPid, From}, Reg),
	    put(registered_names, NewReg),
	    gen_server:reply(From, {error, Reason});
	false ->
	    not_found_ignored
    end.


check_exit_send(undefined, ExitPid, Reason) ->
    ok;
check_exit_send(Send, ExitPid, Reason) ->
    case lists:keysearch(ExitPid, 1, lists:delete(undefined, Send)) of
	{value, {ExitPid, From, Name, Msg}} ->
	    NewSend = lists:delete({ExitPid, From, Name, Msg}, Send),
	    put(send, NewSend),
	    gen_server:reply(From, {badarg, {Name, Msg}});
	false ->
	    not_found_ignored
    end.


check_exit_where(undefined, ExitPid, Reason) ->
    ok;
check_exit_where(Where, ExitPid, Reason) ->
    case lists:keysearch(ExitPid, 1, lists:delete(undefined, Where)) of
	{value, {ExitPid, From}} ->
	    NewWhere = lists:delete({ExitPid, From}, Where),
	    put(whereis_name, NewWhere),
	    gen_server:reply(From, {error, Reason});
	false ->
	    not_found_ignored
    end.



