1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(global_group).
21
22%% Groups nodes into global groups with an own global name space.
23
24-behaviour(gen_server).
25
26%% External exports
27-export([start/0, start_link/0, stop/0, init/1]).
28-export([handle_call/3, handle_cast/2, handle_info/2,
29	 terminate/2, code_change/3]).
30
31-export([global_groups/0]).
32-export([monitor_nodes/1]).
33-export([own_nodes/0]).
34-export([registered_names/1]).
35-export([send/2]).
36-export([send/3]).
37-export([whereis_name/1]).
38-export([whereis_name/2]).
39-export([global_groups_changed/1]).
40-export([global_groups_added/1]).
41-export([global_groups_removed/1]).
42-export([sync/0]).
43-export([ng_add_check/2, ng_add_check/3]).
44
45-export([info/0]).
46-export([registered_names_test/1]).
47-export([send_test/2]).
48-export([whereis_name_test/1]).
49-export([get_own_nodes/0, get_own_nodes_with_errors/0]).
50-export([publish_on_nodes/0]).
51
52-export([config_scan/1, config_scan/2]).
53
54%% Internal exports
55-export([sync_init/4]).
56
57
58-define(cc_vsn, 2).
59
60%%%====================================================================================
61
62-type publish_type() :: 'hidden' | 'normal'.
63-type sync_state()   :: 'no_conf' | 'synced'.
64
65-type group_name()  :: atom().
66-type group_tuple() :: {GroupName :: group_name(), [node()]}
67                     | {GroupName :: group_name(),
68                        PublishType :: publish_type(),
69                        [node()]}.
70
71%%%====================================================================================
72%%% The state of the global_group process
73%%%
74%%% sync_state =  no_conf (global_groups not defined, inital state) |
75%%%               synced
76%%% group_name =  Own global group name
77%%% nodes =       Nodes in the own global group
78%%% no_contact =  Nodes which we haven't had contact with yet
79%%% sync_error =  Nodes which we haven't had contact with yet
80%%% other_grps =  list of other global group names and nodes, [{otherName, [Node]}]
81%%% node_name =   Own node
82%%% monitor =     List of Pids requesting nodeup/nodedown
83%%%====================================================================================
84
85-record(state, {sync_state = no_conf        :: sync_state(),
86		connect_all                 :: boolean(),
87		group_name = []             :: group_name() | [],
88		nodes = []                  :: [node()],
89		no_contact = []             :: [node()],
90		sync_error = [],
91		other_grps = [],
92		node_name = node()          :: node(),
93		monitor = [],
94		publish_type = normal       :: publish_type(),
95		group_publish_type = normal :: publish_type()}).
96
97
98%%%====================================================================================
99%%% External exported
100%%%====================================================================================
101
102-spec global_groups() -> {GroupName, GroupNames} | undefined when
103      GroupName :: group_name(),
104      GroupNames :: [GroupName].
105global_groups() ->
106    request(global_groups).
107
108-spec monitor_nodes(Flag) -> 'ok' when
109      Flag :: boolean().
110monitor_nodes(Flag) ->
111    case Flag of
112	true -> request({monitor_nodes, Flag});
113	false -> request({monitor_nodes, Flag});
114	_ -> {error, not_boolean}
115    end.
116
117-spec own_nodes() -> Nodes when
118      Nodes :: [Node :: node()].
119own_nodes() ->
120    request(own_nodes).
121
122-type name()  :: atom().
123-type where() :: {'node', node()} | {'group', group_name()}.
124
125-spec registered_names(Where) -> Names when
126      Where :: where(),
127      Names :: [Name :: name()].
128registered_names(Arg) ->
129    request({registered_names, Arg}).
130
131-spec send(Name, Msg) -> pid() | {'badarg', {Name, Msg}} when
132      Name :: name(),
133      Msg :: term().
134send(Name, Msg) ->
135    request({send, Name, Msg}).
136
137-spec send(Where, Name, Msg) -> pid() | {'badarg', {Name, Msg}} when
138      Where :: where(),
139      Name :: name(),
140      Msg :: term().
141send(Group, Name, Msg) ->
142    request({send, Group, Name, Msg}).
143
144-spec whereis_name(Name) -> pid() | 'undefined' when
145      Name :: name().
146whereis_name(Name) ->
147    request({whereis_name, Name}).
148
149-spec whereis_name(Where, Name) -> pid() | 'undefined' when
150      Where :: where(),
151      Name :: name().
152whereis_name(Group, Name) ->
153    request({whereis_name, Group, Name}).
154
155global_groups_changed(NewPara) ->
156    request({global_groups_changed, NewPara}).
157
158global_groups_added(NewPara) ->
159    request({global_groups_added, NewPara}).
160
161global_groups_removed(NewPara) ->
162    request({global_groups_removed, NewPara}).
163
164-spec sync() -> 'ok'.
165sync() ->
166    request(sync).
167
168ng_add_check(Node, OthersNG) ->
169    ng_add_check(Node, normal, OthersNG).
170
171ng_add_check(Node, PubType, OthersNG) ->
172    request({ng_add_check, Node, PubType, OthersNG}).
173
174-type info_item() :: {'state', State :: sync_state()}
175                   | {'own_group_name', GroupName :: group_name()}
176                   | {'own_group_nodes', Nodes :: [node()]}
177                   | {'synched_nodes', Nodes :: [node()]}
178                   | {'sync_error', Nodes :: [node()]}
179                   | {'no_contact', Nodes :: [node()]}
180                   | {'other_groups', Groups :: [group_tuple()]}
181                   | {'monitoring', Pids :: [pid()]}.
182
183-spec info() -> [info_item()].
184info() ->
185    request(info, 3000).
186
187%% ==== ONLY for test suites ====
188registered_names_test(Arg) ->
189    request({registered_names_test, Arg}).
190send_test(Name, Msg) ->
191    request({send_test, Name, Msg}).
192whereis_name_test(Name) ->
193    request({whereis_name_test, Name}).
194%% ==== ONLY for test suites ====
195
196
197request(Req) ->
198    request(Req, infinity).
199
200request(Req, Time) ->
201    case whereis(global_group) of
202	P when is_pid(P) ->
203	    gen_server:call(global_group, Req, Time);
204	_Other ->
205	    {error, global_group_not_runnig}
206    end.
207
208%%%====================================================================================
209%%% gen_server start
210%%%
211%%% The first thing to happen is to read if the global_groups key is defined in the
212%%% .config file. If not defined, the whole system is started as one global_group,
213%%% and the services of global_group are superfluous.
214%%% Otherwise a sync process is started to check that all nodes in the own global
215%%% group have the same configuration. This is done by sending 'conf_check' to all
216%%% other nodes and requiring 'conf_check_result' back.
217%%% If the nodes are not in agreement of the configuration the global_group process
218%%% will remove these nodes from the #state.nodes list. This can be a normal case
219%%% at release upgrade when all nodes are not yet upgraded.
220%%%
221%%% It is possible to manually force a sync of the global_group. This is done for
222%%% instance after a release upgrade, after all nodes in the group beeing upgraded.
223%%% The nodes are not synced automatically because it would cause the node to be
224%%% disconnected from those not yet beeing upgraded.
225%%%
226%%% The three process dictionary variables (registered_names, send, and whereis_name)
227%%% are used to store information needed if the search process crashes.
228%%% The search process is a help process to find registered names in the system.
229%%%====================================================================================
230start() -> gen_server:start({local, global_group}, global_group, [], []).
231start_link() -> gen_server:start_link({local, global_group}, global_group,[],[]).
232stop() -> gen_server:call(global_group, stop, infinity).
233
234init([]) ->
235    process_flag(priority, max),
236    ok = net_kernel:monitor_nodes(true),
237    put(registered_names, [undefined]),
238    put(send, [undefined]),
239    put(whereis_name, [undefined]),
240    process_flag(trap_exit, true),
241    Ca = case init:get_argument(connect_all) of
242	     {ok, [["false"]]} ->
243		 false;
244	     _ ->
245		 true
246	 end,
247    PT = publish_arg(),
248    case application:get_env(kernel, global_groups) of
249	undefined ->
250	    update_publish_nodes(PT),
251	    {ok, #state{publish_type = PT,
252			connect_all = Ca}};
253	{ok, []} ->
254	    update_publish_nodes(PT),
255	    {ok, #state{publish_type = PT,
256			connect_all = Ca}};
257	{ok, NodeGrps} ->
258	    {DefGroupName, PubTpGrp, DefNodes, DefOther} =
259		case catch config_scan(NodeGrps, publish_type) of
260		    {error, _Error2} ->
261			update_publish_nodes(PT),
262			exit({error, {'invalid global_groups definition', NodeGrps}});
263		    {DefGroupNameT, PubType, DefNodesT, DefOtherT} ->
264			update_publish_nodes(PT, {PubType, DefNodesT}),
265			%% First disconnect any nodes not belonging to our own group
266			disconnect_nodes(nodes(connected) -- DefNodesT),
267			lists:foreach(fun(Node) ->
268					      erlang:monitor_node(Node, true)
269				      end,
270				      DefNodesT),
271			{DefGroupNameT, PubType, lists:delete(node(), DefNodesT), DefOtherT}
272		end,
273	    {ok, #state{publish_type = PT, group_publish_type = PubTpGrp,
274			sync_state = synced, group_name = DefGroupName,
275			no_contact = lists:sort(DefNodes),
276			other_grps = DefOther, connect_all = Ca}}
277    end.
278
279
280%%%====================================================================================
281%%% sync() -> ok
282%%%
283%%% An operator ordered sync of the own global group. This must be done after
284%%% a release upgrade. It can also be ordered if somthing has made the nodes
285%%% to disagree of the global_groups definition.
286%%%====================================================================================
287handle_call(sync, _From, S) ->
288%    io:format("~p sync ~p~n",[node(), application:get_env(kernel, global_groups)]),
289    case application:get_env(kernel, global_groups) of
290	undefined ->
291	    update_publish_nodes(S#state.publish_type),
292	    {reply, ok, S};
293	{ok, []} ->
294	    update_publish_nodes(S#state.publish_type),
295	    {reply, ok, S};
296	{ok, NodeGrps} ->
297	    {DefGroupName, PubTpGrp, DefNodes, DefOther} =
298		case catch config_scan(NodeGrps, publish_type) of
299		    {error, _Error2} ->
300			exit({error, {'invalid global_groups definition', NodeGrps}});
301		    {DefGroupNameT, PubType, DefNodesT, DefOtherT} ->
302			update_publish_nodes(S#state.publish_type, {PubType, DefNodesT}),
303			%% First inform global on all nodes not belonging to our own group
304			disconnect_nodes(nodes(connected) -- DefNodesT),
305			%% Sync with the nodes in the own group
306			kill_global_group_check(),
307			Pid = spawn_link(?MODULE, sync_init,
308					 [sync, DefGroupNameT, PubType, DefNodesT]),
309			register(global_group_check, Pid),
310			{DefGroupNameT, PubType, lists:delete(node(), DefNodesT), DefOtherT}
311		end,
312	    {reply, ok, S#state{sync_state = synced, group_name = DefGroupName,
313				no_contact = lists:sort(DefNodes),
314				other_grps = DefOther, group_publish_type = PubTpGrp}}
315    end;
316
317
318
319%%%====================================================================================
320%%% global_groups() -> {OwnGroupName, [OtherGroupName]} | undefined
321%%%
322%%% Get the names of the global groups
323%%%====================================================================================
324handle_call(global_groups, _From, S) ->
325    Result = case S#state.sync_state of
326		 no_conf ->
327		     undefined;
328		 synced ->
329		     Other = lists:foldl(fun({N,_L}, Acc) -> Acc ++ [N]
330					 end,
331					 [], S#state.other_grps),
332		     {S#state.group_name, Other}
333	     end,
334    {reply, Result, S};
335
336
337
338%%%====================================================================================
339%%% monitor_nodes(bool()) -> ok
340%%%
341%%% Monitor nodes in the own global group.
342%%%   True => send nodeup/nodedown to the requesting Pid
343%%%   False => stop sending nodeup/nodedown to the requesting Pid
344%%%====================================================================================
345handle_call({monitor_nodes, Flag}, {Pid, _}, StateIn) ->
346%    io:format("***** handle_call ~p~n",[monitor_nodes]),
347    {Res, State} = monitor_nodes(Flag, Pid, StateIn),
348    {reply, Res, State};
349
350
351%%%====================================================================================
352%%% own_nodes() -> [Node]
353%%%
354%%% Get a list of nodes in the own global group
355%%%====================================================================================
356handle_call(own_nodes, _From, S) ->
357    Nodes = case S#state.sync_state of
358		no_conf ->
359		    [node() | nodes()];
360		synced ->
361		    get_own_nodes()
362%		    S#state.nodes
363	    end,
364    {reply, Nodes, S};
365
366
367
368%%%====================================================================================
369%%% registered_names({node, Node}) -> [Name] | {error, ErrorMessage}
370%%% registered_names({group, GlobalGroupName}) -> [Name] | {error, ErrorMessage}
371%%%
372%%% Get the registered names from a specified Node, or GlobalGroupName.
373%%%====================================================================================
374handle_call({registered_names, {group, Group}}, _From, S) when Group =:= S#state.group_name ->
375    Res = global:registered_names(),
376    {reply, Res, S};
377handle_call({registered_names, {group, Group}}, From, S) ->
378    case lists:keysearch(Group, 1, S#state.other_grps) of
379	false ->
380	    {reply, [], S};
381	{value, {Group, []}} ->
382	    {reply, [], S};
383	{value, {Group, Nodes}} ->
384	    Pid = global_search:start(names, {group, Nodes, From}),
385	    Wait = get(registered_names),
386	    put(registered_names, [{Pid, From} | Wait]),
387	    {noreply, S}
388    end;
389handle_call({registered_names, {node, Node}}, _From, S) when Node =:= node() ->
390    Res = global:registered_names(),
391    {reply, Res, S};
392handle_call({registered_names, {node, Node}}, From, S) ->
393    Pid = global_search:start(names, {node, Node, From}),
394%    io:format(">>>>> registered_names Pid ~p~n",[Pid]),
395    Wait = get(registered_names),
396    put(registered_names, [{Pid, From} | Wait]),
397    {noreply, S};
398
399
400
401%%%====================================================================================
402%%% send(Name, Msg) -> Pid | {badarg, {Name, Msg}}
403%%% send({node, Node}, Name, Msg) -> Pid | {badarg, {Name, Msg}}
404%%% send({group, GlobalGroupName}, Name, Msg) -> Pid | {badarg, {Name, Msg}}
405%%%
406%%% Send the Msg to the specified globally registered Name in own global group,
407%%% in specified Node, or GlobalGroupName.
408%%% But first the receiver is to be found, the thread is continued at
409%%% handle_cast(send_res)
410%%%====================================================================================
411%% Search in the whole known world, but check own node first.
412handle_call({send, Name, Msg}, From, S) ->
413    case global:whereis_name(Name) of
414	undefined ->
415	    Pid = global_search:start(send, {any, S#state.other_grps, Name, Msg, From}),
416	    Wait = get(send),
417	    put(send, [{Pid, From, Name, Msg} | Wait]),
418	    {noreply, S};
419	Found ->
420	    Found ! Msg,
421	    {reply, Found, S}
422    end;
423%% Search in the specified global group, which happens to be the own group.
424handle_call({send, {group, Grp}, Name, Msg}, _From, S) when Grp =:= S#state.group_name ->
425    case global:whereis_name(Name) of
426	undefined ->
427	    {reply, {badarg, {Name, Msg}}, S};
428	Pid ->
429	    Pid ! Msg,
430	    {reply, Pid, S}
431    end;
432%% Search in the specified global group.
433handle_call({send, {group, Group}, Name, Msg}, From, S) ->
434    case lists:keysearch(Group, 1, S#state.other_grps) of
435	false ->
436	    {reply, {badarg, {Name, Msg}}, S};
437	{value, {Group, []}} ->
438	    {reply, {badarg, {Name, Msg}}, S};
439	{value, {Group, Nodes}} ->
440	    Pid = global_search:start(send, {group, Nodes, Name, Msg, From}),
441	    Wait = get(send),
442	    put(send, [{Pid, From, Name, Msg} | Wait]),
443	    {noreply, S}
444    end;
445%% Search on the specified node.
446handle_call({send, {node, Node}, Name, Msg}, From, S) ->
447    Pid = global_search:start(send, {node, Node, Name, Msg, From}),
448    Wait = get(send),
449    put(send, [{Pid, From, Name, Msg} | Wait]),
450    {noreply, S};
451
452
453
454%%%====================================================================================
455%%% whereis_name(Name) -> Pid | undefined
456%%% whereis_name({node, Node}, Name) -> Pid | undefined
457%%% whereis_name({group, GlobalGroupName}, Name) -> Pid | undefined
458%%%
459%%% Get the Pid of a globally registered Name in own global group,
460%%% in specified Node, or GlobalGroupName.
461%%% But first the process is to be found,
462%%% the thread is continued at handle_cast(find_name_res)
463%%%====================================================================================
464%% Search in the whole known world, but check own node first.
465handle_call({whereis_name, Name}, From, S) ->
466    case global:whereis_name(Name) of
467	undefined ->
468	    Pid = global_search:start(whereis, {any, S#state.other_grps, Name, From}),
469	    Wait = get(whereis_name),
470	    put(whereis_name, [{Pid, From} | Wait]),
471	    {noreply, S};
472	Found ->
473	    {reply, Found, S}
474    end;
475%% Search in the specified global group, which happens to be the own group.
476handle_call({whereis_name, {group, Group}, Name}, _From, S)
477  when Group =:= S#state.group_name ->
478    Res = global:whereis_name(Name),
479    {reply, Res, S};
480%% Search in the specified global group.
481handle_call({whereis_name, {group, Group}, Name}, From, S) ->
482    case lists:keysearch(Group, 1, S#state.other_grps) of
483	false ->
484	    {reply, undefined, S};
485	{value, {Group, []}} ->
486	    {reply, undefined, S};
487	{value, {Group, Nodes}} ->
488	    Pid = global_search:start(whereis, {group, Nodes, Name, From}),
489	    Wait = get(whereis_name),
490	    put(whereis_name, [{Pid, From} | Wait]),
491	    {noreply, S}
492    end;
493%% Search on the specified node.
494handle_call({whereis_name, {node, Node}, Name}, From, S) ->
495    Pid = global_search:start(whereis, {node, Node, Name, From}),
496    Wait = get(whereis_name),
497    put(whereis_name, [{Pid, From} | Wait]),
498    {noreply, S};
499
500
501%%%====================================================================================
502%%% global_groups parameter changed
503%%% The node is not resynced automatically because it would cause this node to
504%%% be disconnected from those nodes not yet been upgraded.
505%%%====================================================================================
506handle_call({global_groups_changed, NewPara}, _From, S) ->
507    {NewGroupName, PubTpGrp, NewNodes, NewOther} =
508	case catch config_scan(NewPara, publish_type) of
509	    {error, _Error2} ->
510		exit({error, {'invalid global_groups definition', NewPara}});
511	    {DefGroupName, PubType, DefNodes, DefOther} ->
512		update_publish_nodes(S#state.publish_type, {PubType, DefNodes}),
513		{DefGroupName, PubType, DefNodes, DefOther}
514	end,
515
516    %% #state.nodes is the common denominator of previous and new definition
517    NN = NewNodes -- (NewNodes -- S#state.nodes),
518    %% rest of the nodes in the new definition are marked as not yet contacted
519    NNC = (NewNodes -- S#state.nodes) --  S#state.sync_error,
520    %% remove sync_error nodes not belonging to the new group
521    NSE = NewNodes -- (NewNodes -- S#state.sync_error),
522
523    %% Disconnect the connection to nodes which are not in our old global group.
524    %% This is done because if we already are aware of new nodes (to our global
525    %% group) global is not going to be synced to these nodes. We disconnect instead
526    %% of connect because upgrades can be done node by node and we cannot really
527    %% know what nodes these new nodes are synced to. The operator can always
528    %% manually force a sync of the nodes after all nodes beeing uppgraded.
529    %% We must disconnect also if some nodes to which we have a connection
530    %% will not be in any global group at all.
531    force_nodedown(nodes(connected) -- NewNodes),
532
533    NewS = S#state{group_name = NewGroupName,
534		   nodes = lists:sort(NN),
535		   no_contact = lists:sort(lists:delete(node(), NNC)),
536		   sync_error = lists:sort(NSE),
537		   other_grps = NewOther,
538		   group_publish_type = PubTpGrp},
539    {reply, ok, NewS};
540
541
542%%%====================================================================================
543%%% global_groups parameter added
544%%% The node is not resynced automatically because it would cause this node to
545%%% be disconnected from those nodes not yet been upgraded.
546%%%====================================================================================
547handle_call({global_groups_added, NewPara}, _From, S) ->
548%    io:format("### global_groups_changed, NewPara ~p ~n",[NewPara]),
549    {NewGroupName, PubTpGrp, NewNodes, NewOther} =
550	case catch config_scan(NewPara, publish_type) of
551	    {error, _Error2} ->
552		exit({error, {'invalid global_groups definition', NewPara}});
553	    {DefGroupName, PubType, DefNodes, DefOther} ->
554		update_publish_nodes(S#state.publish_type, {PubType, DefNodes}),
555		{DefGroupName, PubType, DefNodes, DefOther}
556	end,
557
558    %% disconnect from those nodes which are not going to be in our global group
559    force_nodedown(nodes(connected) -- NewNodes),
560
561    %% Check which nodes are already updated
562    OwnNG = get_own_nodes(),
563    NGACArgs = case S#state.group_publish_type of
564		   normal ->
565		       [node(), OwnNG];
566		   _ ->
567		       [node(), S#state.group_publish_type, OwnNG]
568	       end,
569    {NN, NNC, NSE} =
570	lists:foldl(fun(Node, {NN_acc, NNC_acc, NSE_acc}) ->
571			    case rpc:call(Node, global_group, ng_add_check, NGACArgs) of
572				{badrpc, _} ->
573				    {NN_acc, [Node | NNC_acc], NSE_acc};
574				agreed ->
575				    {[Node | NN_acc], NNC_acc, NSE_acc};
576				not_agreed ->
577				    {NN_acc, NNC_acc, [Node | NSE_acc]}
578			    end
579		    end,
580		    {[], [], []}, lists:delete(node(), NewNodes)),
581    NewS = S#state{sync_state = synced, group_name = NewGroupName, nodes = lists:sort(NN),
582		   sync_error = lists:sort(NSE), no_contact = lists:sort(NNC),
583		   other_grps = NewOther, group_publish_type = PubTpGrp},
584    {reply, ok, NewS};
585
586
587%%%====================================================================================
588%%% global_groups parameter removed
589%%%====================================================================================
590handle_call({global_groups_removed, _NewPara}, _From, S) ->
591%    io:format("### global_groups_removed, NewPara ~p ~n",[_NewPara]),
592    update_publish_nodes(S#state.publish_type),
593    NewS = S#state{sync_state = no_conf, group_name = [], nodes = [],
594		   sync_error = [], no_contact = [],
595		   other_grps = []},
596    {reply, ok, NewS};
597
598
599%%%====================================================================================
600%%% global_groups parameter added to some other node which thinks that we
601%%% belong to the same global group.
602%%% It could happen that our node is not yet updated with the new node_group parameter
603%%%====================================================================================
604handle_call({ng_add_check, Node, PubType, OthersNG}, _From, S) ->
605    %% Check which nodes are already updated
606    OwnNG = get_own_nodes(),
607    case S#state.group_publish_type =:= PubType of
608	true ->
609	    case OwnNG of
610		OthersNG ->
611		    NN = [Node | S#state.nodes],
612		    NSE = lists:delete(Node, S#state.sync_error),
613		    NNC = lists:delete(Node, S#state.no_contact),
614		    NewS = S#state{nodes = lists:sort(NN),
615				   sync_error = NSE,
616				   no_contact = NNC},
617		    {reply, agreed, NewS};
618		_ ->
619		    {reply, not_agreed, S}
620	    end;
621	_ ->
622	    {reply, not_agreed, S}
623    end;
624
625
626
627%%%====================================================================================
628%%% Misceleaneous help function to read some variables
629%%%====================================================================================
630handle_call(info, _From, S) ->
631    Reply = [{state,          S#state.sync_state},
632	     {own_group_name, S#state.group_name},
633	     {own_group_nodes, get_own_nodes()},
634%	     {"nodes()",      lists:sort(nodes())},
635	     {synced_nodes,   S#state.nodes},
636	     {sync_error,     S#state.sync_error},
637	     {no_contact,     S#state.no_contact},
638	     {other_groups,   S#state.other_grps},
639	     {monitoring,     S#state.monitor}],
640
641    {reply, Reply, S};
642
643handle_call(get, _From, S) ->
644    {reply, get(), S};
645
646
647%%%====================================================================================
648%%% Only for test suites. These tests when the search process exits.
649%%%====================================================================================
650handle_call({registered_names_test, {node, 'test3844zty'}}, From, S) ->
651    Pid = global_search:start(names_test, {node, 'test3844zty'}),
652    Wait = get(registered_names),
653    put(registered_names, [{Pid, From} | Wait]),
654    {noreply, S};
655handle_call({registered_names_test, {node, _Node}}, _From, S) ->
656    {reply, {error, illegal_function_call}, S};
657handle_call({send_test, Name, 'test3844zty'}, From, S) ->
658    Pid = global_search:start(send_test, 'test3844zty'),
659    Wait = get(send),
660    put(send, [{Pid, From, Name, 'test3844zty'} | Wait]),
661    {noreply, S};
662handle_call({send_test, _Name, _Msg }, _From, S) ->
663    {reply, {error, illegal_function_call}, S};
664handle_call({whereis_name_test, 'test3844zty'}, From, S) ->
665    Pid = global_search:start(whereis_test, 'test3844zty'),
666    Wait = get(whereis_name),
667    put(whereis_name, [{Pid, From} | Wait]),
668    {noreply, S};
669handle_call({whereis_name_test, _Name}, _From, S) ->
670    {reply, {error, illegal_function_call}, S};
671
672handle_call(Call, _From, S) ->
673%    io:format("***** handle_call ~p~n",[Call]),
674    {reply, {illegal_message, Call}, S}.
675
676
677
678
679
680%%%====================================================================================
681%%% registered_names({node, Node}) -> [Name] | {error, ErrorMessage}
682%%% registered_names({group, GlobalGroupName}) -> [Name] | {error, ErrorMessage}
683%%%
684%%% Get a list of nodes in the own global group
685%%%====================================================================================
686handle_cast({registered_names, User}, S) ->
687%    io:format(">>>>> registered_names User ~p~n",[User]),
688    Res = global:registered_names(),
689    User ! {registered_names_res, Res},
690    {noreply, S};
691
692handle_cast({registered_names_res, Result, Pid, From}, S) ->
693%    io:format(">>>>> registered_names_res Result ~p~n",[Result]),
694    unlink(Pid),
695    Pid ! kill,
696    Wait = get(registered_names),
697    NewWait = lists:delete({Pid, From},Wait),
698    put(registered_names, NewWait),
699    gen_server:reply(From, Result),
700    {noreply, S};
701
702
703
704%%%====================================================================================
705%%% send(Name, Msg) -> Pid | {error, ErrorMessage}
706%%% send({node, Node}, Name, Msg) -> Pid | {error, ErrorMessage}
707%%% send({group, GlobalGroupName}, Name, Msg) -> Pid | {error, ErrorMessage}
708%%%
709%%% The registered Name is found; send the message to it, kill the search process,
710%%% and return to the requesting process.
711%%%====================================================================================
712handle_cast({send_res, Result, Name, Msg, Pid, From}, S) ->
713%    io:format("~p>>>>> send_res Result ~p~n",[node(), Result]),
714    case Result of
715	{badarg,{Name, Msg}} ->
716	    continue;
717	ToPid ->
718	    ToPid ! Msg
719    end,
720    unlink(Pid),
721    Pid ! kill,
722    Wait = get(send),
723    NewWait = lists:delete({Pid, From, Name, Msg},Wait),
724    put(send, NewWait),
725    gen_server:reply(From, Result),
726    {noreply, S};
727
728
729
730%%%====================================================================================
731%%% A request from a search process to check if this Name is registered at this node.
732%%%====================================================================================
733handle_cast({find_name, User, Name}, S) ->
734    Res = global:whereis_name(Name),
735%    io:format(">>>>> find_name Name ~p   Res ~p~n",[Name, Res]),
736    User ! {find_name_res, Res},
737    {noreply, S};
738
739%%%====================================================================================
740%%% whereis_name(Name) -> Pid | undefined
741%%% whereis_name({node, Node}, Name) -> Pid | undefined
742%%% whereis_name({group, GlobalGroupName}, Name) -> Pid | undefined
743%%%
744%%% The registered Name is found; kill the search process
745%%% and return to the requesting process.
746%%%====================================================================================
747handle_cast({find_name_res, Result, Pid, From}, S) ->
748%    io:format(">>>>> find_name_res Result ~p~n",[Result]),
749%    io:format(">>>>> find_name_res get() ~p~n",[get()]),
750    unlink(Pid),
751    Pid ! kill,
752    Wait = get(whereis_name),
753    NewWait = lists:delete({Pid, From},Wait),
754    put(whereis_name, NewWait),
755    gen_server:reply(From, Result),
756    {noreply, S};
757
758
759%%%====================================================================================
760%%% The node is synced successfully
761%%%====================================================================================
762handle_cast({synced, NoContact}, S) ->
763%    io:format("~p>>>>> synced ~p  ~n",[node(), NoContact]),
764    kill_global_group_check(),
765    Nodes = get_own_nodes() -- [node() | NoContact],
766    {noreply, S#state{nodes = lists:sort(Nodes),
767		      sync_error = [],
768		      no_contact = NoContact}};
769
770
771%%%====================================================================================
772%%% The node could not sync with some other nodes.
773%%%====================================================================================
774handle_cast({sync_error, NoContact, ErrorNodes}, S) ->
775%    io:format("~p>>>>> sync_error ~p ~p ~n",[node(), NoContact, ErrorNodes]),
776    Txt = io_lib:format("Global group: Could not synchronize with these nodes ~p~n"
777			"because global_groups were not in agreement. ~n", [ErrorNodes]),
778    error_logger:error_report(Txt),
779    kill_global_group_check(),
780    Nodes = (get_own_nodes() -- [node() | NoContact]) -- ErrorNodes,
781    {noreply, S#state{nodes = lists:sort(Nodes),
782		      sync_error = ErrorNodes,
783		      no_contact = NoContact}};
784
785
786%%%====================================================================================
787%%% Another node is checking this node's group configuration
788%%%====================================================================================
789handle_cast({conf_check, Vsn, Node, From, sync, CCName, CCNodes}, S) ->
790    handle_cast({conf_check, Vsn, Node, From, sync, CCName, normal, CCNodes}, S);
791
792handle_cast({conf_check, Vsn, Node, From, sync, CCName, PubType, CCNodes}, S) ->
793    CurNodes = S#state.nodes,
794%    io:format(">>>>> conf_check,sync  Node ~p~n",[Node]),
795    %% Another node is syncing,
796    %% done for instance after upgrade of global_groups parameter
797    NS =
798	case application:get_env(kernel, global_groups) of
799	    undefined ->
800		%% We didn't have any node_group definition
801		update_publish_nodes(S#state.publish_type),
802		disconnect_nodes([Node]),
803		{global_group_check, Node} ! {config_error, Vsn, From, node()},
804		S;
805	    {ok, []} ->
806		%% Our node_group definition was empty
807		update_publish_nodes(S#state.publish_type),
808		disconnect_nodes([Node]),
809		{global_group_check, Node} ! {config_error, Vsn, From, node()},
810		S;
811	    %%---------------------------------
812	    %% global_groups defined
813	    %%---------------------------------
814	    {ok, NodeGrps} ->
815		case catch config_scan(NodeGrps, publish_type) of
816		    {error, _Error2} ->
817			%% Our node_group definition was erroneous
818			disconnect_nodes([Node]),
819			{global_group_check, Node} ! {config_error, Vsn, From, node()},
820			S#state{nodes = lists:delete(Node, CurNodes)};
821
822		    {CCName, PubType, CCNodes, _OtherDef} ->
823			%% OK, add the node to the #state.nodes if it isn't there
824			update_publish_nodes(S#state.publish_type, {PubType, CCNodes}),
825			global_name_server ! {nodeup, Node},
826			{global_group_check, Node} ! {config_ok, Vsn, From, node()},
827			case lists:member(Node, CurNodes) of
828			    false ->
829				NewNodes = lists:sort([Node | CurNodes]),
830				NSE = lists:delete(Node, S#state.sync_error),
831				NNC = lists:delete(Node, S#state.no_contact),
832				S#state{nodes = NewNodes,
833				        sync_error = NSE,
834				        no_contact = NNC};
835			    true ->
836				S
837			end;
838		    _ ->
839			%% node_group definitions were not in agreement
840			disconnect_nodes([Node]),
841			{global_group_check, Node} ! {config_error, Vsn, From, node()},
842			NN = lists:delete(Node, S#state.nodes),
843			NSE = lists:delete(Node, S#state.sync_error),
844			NNC = lists:delete(Node, S#state.no_contact),
845			S#state{nodes = NN,
846				sync_error = NSE,
847				no_contact = NNC}
848		end
849	end,
850    {noreply, NS};
851
852
853handle_cast(_Cast, S) ->
854%    io:format("***** handle_cast ~p~n",[_Cast]),
855    {noreply, S}.
856
857
858
859%%%====================================================================================
860%%% A node went down. If no global group configuration inform global;
861%%% if global group configuration inform global only if the node is one in
862%%% the own global group.
863%%%====================================================================================
864handle_info({nodeup, Node}, S) when S#state.sync_state =:= no_conf ->
865%    io:format("~p>>>>> nodeup, Node ~p ~n",[node(), Node]),
866    send_monitor(S#state.monitor, {nodeup, Node}, S#state.sync_state),
867    global_name_server ! {nodeup, Node},
868    {noreply, S};
869handle_info({nodeup, Node}, S) ->
870%    io:format("~p>>>>> nodeup, Node ~p ~n",[node(), Node]),
871    OthersNG = case S#state.sync_state of
872		   synced ->
873		       X = (catch rpc:call(Node, global_group, get_own_nodes, [])),
874		       case X of
875			   X when is_list(X) ->
876			       lists:sort(X);
877			   _ ->
878			       []
879		       end;
880		   no_conf ->
881		       []
882	       end,
883
884    NNC = lists:delete(Node, S#state.no_contact),
885    NSE = lists:delete(Node, S#state.sync_error),
886    OwnNG = get_own_nodes(),
887    case OwnNG of
888	OthersNG ->
889	    send_monitor(S#state.monitor, {nodeup, Node}, S#state.sync_state),
890	    global_name_server ! {nodeup, Node},
891	    case lists:member(Node, S#state.nodes) of
892		false ->
893		    NN = lists:sort([Node | S#state.nodes]),
894		    {noreply, S#state{nodes = NN,
895				      no_contact = NNC,
896				      sync_error = NSE}};
897		true ->
898		    {noreply, S#state{no_contact = NNC,
899				      sync_error = NSE}}
900	    end;
901	_ ->
902	    case {lists:member(Node, get_own_nodes()),
903		  lists:member(Node, S#state.sync_error)} of
904		{true, false} ->
905		    NSE2 = lists:sort([Node | S#state.sync_error]),
906		    {noreply, S#state{no_contact = NNC,
907				      sync_error = NSE2}};
908		_ ->
909		    {noreply, S}
910	    end
911    end;
912
913%%%====================================================================================
914%%% A node has crashed.
915%%% nodedown must always be sent to global; this is a security measurement
916%%% because during release upgrade the global_groups parameter is upgraded
917%%% before the node is synced. This means that nodedown may arrive from a
918%%% node which we are not aware of.
919%%%====================================================================================
920handle_info({nodedown, Node}, S) when S#state.sync_state =:= no_conf ->
921%    io:format("~p>>>>> nodedown, no_conf Node ~p~n",[node(), Node]),
922    send_monitor(S#state.monitor, {nodedown, Node}, S#state.sync_state),
923    global_name_server ! {nodedown, Node},
924    {noreply, S};
925handle_info({nodedown, Node}, S) ->
926%    io:format("~p>>>>> nodedown, Node ~p  ~n",[node(), Node]),
927    send_monitor(S#state.monitor, {nodedown, Node}, S#state.sync_state),
928    global_name_server ! {nodedown, Node},
929    NN = lists:delete(Node, S#state.nodes),
930    NSE = lists:delete(Node, S#state.sync_error),
931    NNC = case {lists:member(Node, get_own_nodes()),
932		lists:member(Node, S#state.no_contact)} of
933	      {true, false} ->
934		  [Node | S#state.no_contact];
935	      _ ->
936		  S#state.no_contact
937	  end,
938    {noreply, S#state{nodes = NN, no_contact = NNC, sync_error = NSE}};
939
940
941%%%====================================================================================
942%%% A node has changed its global_groups definition, and is telling us that we are not
943%%% included in his group any more. This could happen at release upgrade.
944%%%====================================================================================
945handle_info({disconnect_node, Node}, S) ->
946%    io:format("~p>>>>> disconnect_node Node ~p CN ~p~n",[node(), Node, S#state.nodes]),
947    case {S#state.sync_state, lists:member(Node, S#state.nodes)} of
948	{synced, true} ->
949	    send_monitor(S#state.monitor, {nodedown, Node}, S#state.sync_state);
950	_ ->
951	    cont
952    end,
953    global_name_server ! {nodedown, Node}, %% nodedown is used to inform global of the
954                                           %% disconnected node
955    NN = lists:delete(Node, S#state.nodes),
956    NNC = lists:delete(Node, S#state.no_contact),
957    NSE = lists:delete(Node, S#state.sync_error),
958    {noreply, S#state{nodes = NN, no_contact = NNC, sync_error = NSE}};
959
960
961
962
963handle_info({'EXIT', ExitPid, Reason}, S) ->
964    check_exit(ExitPid, Reason),
965    {noreply, S};
966
967
968handle_info(_Info, S) ->
969%    io:format("***** handle_info = ~p~n",[_Info]),
970    {noreply, S}.
971
972
973
974terminate(_Reason, _S) ->
975    ok.
976
977
978code_change(_OldVsn, State, _Extra) ->
979    {ok, State}.
980
981
982
983
984
985%%%====================================================================================
986%%% Check the global group configuration.
987%%%====================================================================================
988
989config_scan(NodeGrps) ->
990    config_scan(NodeGrps, original).
991
992config_scan(NodeGrps, original) ->
993    case config_scan(NodeGrps, publish_type) of
994	{DefGroupName, _, DefNodes, DefOther} ->
995	    {DefGroupName, DefNodes, DefOther};
996	Error ->
997	    Error
998    end;
999config_scan(NodeGrps, publish_type) ->
1000    config_scan(node(), normal, NodeGrps, no_name, [], []).
1001
1002config_scan(_MyNode, PubType, [], Own_name, OwnNodes, OtherNodeGrps) ->
1003    {Own_name, PubType, lists:sort(OwnNodes), lists:reverse(OtherNodeGrps)};
1004config_scan(MyNode, PubType, [GrpTuple|NodeGrps], Own_name, OwnNodes, OtherNodeGrps) ->
1005    {Name, PubTypeGroup, Nodes} = grp_tuple(GrpTuple),
1006    case lists:member(MyNode, Nodes) of
1007	true ->
1008	    case Own_name of
1009		no_name ->
1010		    config_scan(MyNode, PubTypeGroup, NodeGrps, Name, Nodes, OtherNodeGrps);
1011		_ ->
1012		    {error, {'node defined twice', {Own_name, Name}}}
1013	    end;
1014	false ->
1015	    config_scan(MyNode,PubType,NodeGrps,Own_name,OwnNodes,
1016			[{Name, Nodes}|OtherNodeGrps])
1017    end.
1018
1019grp_tuple({Name, Nodes}) ->
1020    {Name, normal, Nodes};
1021grp_tuple({Name, hidden, Nodes}) ->
1022    {Name, hidden, Nodes};
1023grp_tuple({Name, normal, Nodes}) ->
1024    {Name, normal, Nodes}.
1025
1026
1027%%%====================================================================================
1028%%% The special process which checks that all nodes in the own global group
1029%%% agrees on the configuration.
1030%%%====================================================================================
1031-spec sync_init(_, _, _, _) -> no_return().
1032sync_init(Type, Cname, PubType, Nodes) ->
1033    {Up, Down} = sync_check_node(lists:delete(node(), Nodes), [], []),
1034    sync_check_init(Type, Up, Cname, Nodes, Down, PubType).
1035
1036sync_check_node([], Up, Down) ->
1037    {Up, Down};
1038sync_check_node([Node|Nodes], Up, Down) ->
1039    case net_adm:ping(Node) of
1040	pang ->
1041	    sync_check_node(Nodes, Up, [Node|Down]);
1042	pong ->
1043	    sync_check_node(Nodes, [Node|Up], Down)
1044    end.
1045
1046
1047
1048%%%-------------------------------------------------------------
1049%%% Check that all nodes are in agreement of the global
1050%%% group configuration.
1051%%%-------------------------------------------------------------
1052-spec sync_check_init(_, _, _, _, _, _) -> no_return().
1053sync_check_init(Type, Up, Cname, Nodes, Down, PubType) ->
1054    sync_check_init(Type, Up, Cname, Nodes, 3, [], Down, PubType).
1055
1056-spec sync_check_init(_, _, _, _, _, _, _, _) -> no_return().
1057sync_check_init(_Type, NoContact, _Cname, _Nodes, 0, ErrorNodes, Down, _PubType) ->
1058    case ErrorNodes of
1059	[] ->
1060	    gen_server:cast(global_group, {synced, lists:sort(NoContact ++ Down)});
1061	_ ->
1062	    gen_server:cast(global_group, {sync_error, lists:sort(NoContact ++ Down),
1063					   ErrorNodes})
1064    end,
1065    receive
1066	kill ->
1067	    exit(normal)
1068    after 5000 ->
1069	    exit(normal)
1070    end;
1071
1072sync_check_init(Type, Up, Cname, Nodes, N, ErrorNodes, Down, PubType) ->
1073    ConfCheckMsg = case PubType of
1074		       normal ->
1075			   {conf_check, ?cc_vsn, node(), self(), Type, Cname, Nodes};
1076		       _ ->
1077			   {conf_check, ?cc_vsn, node(), self(), Type, Cname, PubType, Nodes}
1078		   end,
1079    lists:foreach(fun(Node) ->
1080			  gen_server:cast({global_group, Node}, ConfCheckMsg)
1081		  end, Up),
1082    case sync_check(Up) of
1083	{ok, synced} ->
1084	    sync_check_init(Type, [], Cname, Nodes, 0, ErrorNodes, Down, PubType);
1085	{error, NewErrorNodes} ->
1086	    sync_check_init(Type, [], Cname, Nodes, 0, ErrorNodes ++ NewErrorNodes, Down, PubType);
1087	{more, Rem, NewErrorNodes} ->
1088	    %% Try again to reach the global_group,
1089	    %% obviously the node is up but not the global_group process.
1090	    sync_check_init(Type, Rem, Cname, Nodes, N-1, ErrorNodes ++ NewErrorNodes, Down, PubType)
1091    end.
1092
1093sync_check(Up) ->
1094    sync_check(Up, Up, []).
1095
1096sync_check([], _Up, []) ->
1097    {ok, synced};
1098sync_check([], _Up, ErrorNodes) ->
1099    {error, ErrorNodes};
1100sync_check(Rem, Up, ErrorNodes) ->
1101    receive
1102	{config_ok, ?cc_vsn, Pid, Node} when Pid =:= self() ->
1103	    global_name_server ! {nodeup, Node},
1104	    sync_check(Rem -- [Node], Up, ErrorNodes);
1105	{config_error, ?cc_vsn, Pid, Node} when Pid =:= self() ->
1106	    sync_check(Rem -- [Node], Up, [Node | ErrorNodes]);
1107	{no_global_group_configuration, ?cc_vsn, Pid, Node} when Pid =:= self() ->
1108	    sync_check(Rem -- [Node], Up, [Node | ErrorNodes]);
1109	%% Ignore, illegal vsn or illegal Pid
1110	_ ->
1111	    sync_check(Rem, Up, ErrorNodes)
1112    after 2000 ->
1113	    %% Try again, the previous conf_check message
1114	    %% apparently disapared in the magic black hole.
1115	    {more, Rem, ErrorNodes}
1116    end.
1117
1118
1119%%%====================================================================================
1120%%% A process wants to toggle monitoring nodeup/nodedown from nodes.
1121%%%====================================================================================
1122monitor_nodes(true, Pid, State) ->
1123    link(Pid),
1124    Monitor = State#state.monitor,
1125    {ok, State#state{monitor = [Pid|Monitor]}};
1126monitor_nodes(false, Pid, State) ->
1127    Monitor = State#state.monitor,
1128    State1 = State#state{monitor = delete_all(Pid,Monitor)},
1129    do_unlink(Pid, State1),
1130    {ok, State1};
1131monitor_nodes(_, _, State) ->
1132    {error, State}.
1133
1134delete_all(From, [From |Tail]) -> delete_all(From, Tail);
1135delete_all(From, [H|Tail]) ->  [H|delete_all(From, Tail)];
1136delete_all(_, []) -> [].
1137
1138%% do unlink if we have no more references to Pid.
1139do_unlink(Pid, State) ->
1140    case lists:member(Pid, State#state.monitor) of
1141	true ->
1142	    false;
1143	_ ->
1144%	    io:format("unlink(Pid) ~p~n",[Pid]),
1145	    unlink(Pid)
1146    end.
1147
1148
1149
1150%%%====================================================================================
1151%%% Send a nodeup/down messages to monitoring Pids in the own global group.
1152%%%====================================================================================
1153send_monitor([P|T], M, no_conf) ->
1154    _ = safesend_nc(P, M),
1155    send_monitor(T, M, no_conf);
1156send_monitor([P|T], M, SyncState) ->
1157    _ = safesend(P, M),
1158    send_monitor(T, M, SyncState);
1159send_monitor([], _, _) ->
1160    ok.
1161
1162safesend(Name, {Msg, Node}) when is_atom(Name) ->
1163    case lists:member(Node, get_own_nodes()) of
1164	true ->
1165	    case whereis(Name) of
1166		undefined ->
1167		    {Msg, Node};
1168		P when is_pid(P) ->
1169		    P ! {Msg, Node}
1170	    end;
1171	false ->
1172	    not_own_group
1173    end;
1174safesend(Pid, {Msg, Node}) ->
1175    case lists:member(Node, get_own_nodes()) of
1176	true ->
1177	    Pid ! {Msg, Node};
1178	false ->
1179	    not_own_group
1180    end.
1181
1182safesend_nc(Name, {Msg, Node}) when is_atom(Name) ->
1183    case whereis(Name) of
1184	undefined ->
1185	    {Msg, Node};
1186	P when is_pid(P) ->
1187	    P ! {Msg, Node}
1188    end;
1189safesend_nc(Pid, {Msg, Node}) ->
1190    Pid ! {Msg, Node}.
1191
1192
1193
1194
1195
1196
1197%%%====================================================================================
1198%%% Check which user is associated to the crashed process.
1199%%%====================================================================================
1200check_exit(ExitPid, Reason) ->
1201%    io:format("===EXIT===  ~p ~p ~n~p   ~n~p   ~n~p ~n~n",[ExitPid, Reason, get(registered_names), get(send), get(whereis_name)]),
1202    check_exit_reg(get(registered_names), ExitPid, Reason),
1203    check_exit_send(get(send), ExitPid, Reason),
1204    check_exit_where(get(whereis_name), ExitPid, Reason).
1205
1206
1207check_exit_reg(undefined, _ExitPid, _Reason) ->
1208    ok;
1209check_exit_reg(Reg, ExitPid, Reason) ->
1210    case lists:keysearch(ExitPid, 1, lists:delete(undefined, Reg)) of
1211	{value, {ExitPid, From}} ->
1212	    NewReg = lists:delete({ExitPid, From}, Reg),
1213	    put(registered_names, NewReg),
1214	    gen_server:reply(From, {error, Reason});
1215	false ->
1216	    not_found_ignored
1217    end.
1218
1219
1220check_exit_send(undefined, _ExitPid, _Reason) ->
1221    ok;
1222check_exit_send(Send, ExitPid, _Reason) ->
1223    case lists:keysearch(ExitPid, 1, lists:delete(undefined, Send)) of
1224	{value, {ExitPid, From, Name, Msg}} ->
1225	    NewSend = lists:delete({ExitPid, From, Name, Msg}, Send),
1226	    put(send, NewSend),
1227	    gen_server:reply(From, {badarg, {Name, Msg}});
1228	false ->
1229	    not_found_ignored
1230    end.
1231
1232
1233check_exit_where(undefined, _ExitPid, _Reason) ->
1234    ok;
1235check_exit_where(Where, ExitPid, Reason) ->
1236    case lists:keysearch(ExitPid, 1, lists:delete(undefined, Where)) of
1237	{value, {ExitPid, From}} ->
1238	    NewWhere = lists:delete({ExitPid, From}, Where),
1239	    put(whereis_name, NewWhere),
1240	    gen_server:reply(From, {error, Reason});
1241	false ->
1242	    not_found_ignored
1243    end.
1244
1245
1246
1247%%%====================================================================================
1248%%% Kill any possible global_group_check processes
1249%%%====================================================================================
1250kill_global_group_check() ->
1251    case whereis(global_group_check) of
1252	undefined ->
1253	    ok;
1254	Pid ->
1255	    unlink(Pid),
1256	    global_group_check ! kill,
1257	    unregister(global_group_check)
1258    end.
1259
1260
1261%%%====================================================================================
1262%%% Disconnect nodes not belonging to own global_groups
1263%%%====================================================================================
1264disconnect_nodes(DisconnectNodes) ->
1265    lists:foreach(fun(Node) ->
1266			  {global_group, Node} ! {disconnect_node, node()},
1267			  global:node_disconnected(Node)
1268		  end,
1269		  DisconnectNodes).
1270
1271
1272%%%====================================================================================
1273%%% Disconnect nodes not belonging to own global_groups
1274%%%====================================================================================
1275force_nodedown(DisconnectNodes) ->
1276    lists:foreach(fun(Node) ->
1277			  erlang:disconnect_node(Node),
1278			  global:node_disconnected(Node)
1279		  end,
1280		  DisconnectNodes).
1281
1282
1283%%%====================================================================================
1284%%% Get the current global_groups definition
1285%%%====================================================================================
1286get_own_nodes_with_errors() ->
1287    case application:get_env(kernel, global_groups) of
1288	undefined ->
1289	    {ok, all};
1290	{ok, []} ->
1291	    {ok, all};
1292	{ok, NodeGrps} ->
1293	    case catch config_scan(NodeGrps, publish_type) of
1294		{error, Error} ->
1295		    {error, Error};
1296		{_, _, NodesDef, _} ->
1297		    {ok, lists:sort(NodesDef)}
1298	    end
1299    end.
1300
1301get_own_nodes() ->
1302    case get_own_nodes_with_errors() of
1303	{ok, all} ->
1304	    [];
1305	{error, _} ->
1306	    [];
1307	{ok, Nodes} ->
1308	    Nodes
1309    end.
1310
1311%%%====================================================================================
1312%%% -hidden command line argument
1313%%%====================================================================================
1314publish_arg() ->
1315    case net_kernel:dist_listen() of
1316        false ->
1317            hidden;
1318        _ ->
1319            case init:get_argument(hidden) of
1320                {ok,[[]]} ->
1321                    hidden;
1322                {ok,[["true"]]} ->
1323                    hidden;
1324                _ ->
1325                    normal
1326            end
1327    end.
1328
1329%%%====================================================================================
1330%%% Own group publication type and nodes
1331%%%====================================================================================
1332own_group() ->
1333    case application:get_env(kernel, global_groups) of
1334	undefined ->
1335	    no_group;
1336	{ok, []} ->
1337	    no_group;
1338	{ok, NodeGrps} ->
1339	    case catch config_scan(NodeGrps, publish_type) of
1340		{error, _} ->
1341		    no_group;
1342		{_, PubTpGrp, NodesDef, _} ->
1343		    {PubTpGrp, NodesDef}
1344	    end
1345    end.
1346
1347
1348%%%====================================================================================
1349%%% Help function which computes publication list
1350%%%====================================================================================
1351publish_on_nodes(normal, no_group) ->
1352    all;
1353publish_on_nodes(hidden, no_group) ->
1354    [];
1355publish_on_nodes(normal, {normal, _}) ->
1356    all;
1357publish_on_nodes(hidden, {_, Nodes}) ->
1358    Nodes;
1359publish_on_nodes(_, {hidden, Nodes}) ->
1360    Nodes.
1361
1362%%%====================================================================================
1363%%% Update net_kernels publication list
1364%%%====================================================================================
1365update_publish_nodes(PubArg) ->
1366    update_publish_nodes(PubArg, no_group).
1367update_publish_nodes(PubArg, MyGroup) ->
1368    net_kernel:update_publish_nodes(publish_on_nodes(PubArg, MyGroup)).
1369
1370
1371%%%====================================================================================
1372%%% Fetch publication list
1373%%%====================================================================================
1374publish_on_nodes() ->
1375    publish_on_nodes(publish_arg(), own_group()).
1376