1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2017. 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(systools_rc).
21-export([translate_scripts/3, translate_scripts/4, format_error/1]).
22
23-include("systools.hrl").
24
25%%-----------------------------------------------------------------
26%% High-level
27%% ==========
28%% mnesia_backup (not yet implemented)
29%% {update, Mod, Change, PrePurge, PostPurge, [Mod]}
30%% {update, Mod, Timeout, Change, PrePurge, PostPurge, [Mod]}
31%% {update, Mod, ModType, , Change, PrePurge, PostPurge, [Mod]}
32%% {update, Mod, ModType, Timeout, Change, PrePurge, PostPurge, [Mod]}
33%% {load_module, Mod, PrePurge, PostPurge, [Mod]}
34%% {add_module, Mod}
35%% {add_module, Mod, [Mod]}
36%% {restart_application, Appl}
37%% {add_application, Appl, Type}
38%% {remove_application, Appl}
39%%
40%% Low-level
41%% =========
42%% {load_object_code, {Lib, LibVsn, Mods}}
43%% point_of_no_return
44%% {load, {Mod, PrePurge, PostPurge}}
45%% {remove, {Mod, PrePurge, PostPurge}}
46%% {purge, Mods}
47%% {suspend, Mods}
48%% {resume, Mods}
49%% {code_change, [{Mod, Extra}]}
50%% {code_change, Mode, [{Mod, Extra}]}
51%% {stop, Mods}
52%% {start, Mods}
53%% {sync_nodes, Id, {M, F, A}}
54%% {sync_nodes, Id, Nodes}
55%% {apply, {M, F, A}}
56%% restart_new_emulator
57%% restart_emulator
58%%-----------------------------------------------------------------
59
60%% High-level instructions that contain dependencies
61%%
62-define(DEP_INSTRS, [update, load_module, add_module, delete_module]).
63
64%%-----------------------------------------------------------------
65%% translate_scripts(Scripts, Appls, PreAppls) -> Res
66%% Mode = up | dn
67%% Scripts = [AppupScript]
68%% Appls = PreAppls = [#application]
69%% Res = {ok, LowLevelScript} | {error, ?MODULE, Reason}
70%%-----------------------------------------------------------------
71translate_scripts(Scripts, Appls, PreAppls) ->
72    translate_scripts(up, Scripts, Appls, PreAppls).
73
74translate_scripts(Mode, Scripts, Appls, PreAppls) ->
75    Scripts2 = expand_scripts(Scripts),
76    case catch do_translate_scripts(Mode, Scripts2, Appls, PreAppls) of
77	{ok, NewScript} -> {ok, NewScript};
78	{error, Reason} -> {error, ?MODULE, Reason};
79	{'EXIT', Reason} -> {error, ?MODULE, Reason}
80    end.
81
82expand_scripts([Script|Scripts]) ->
83    [expand_script(Script)|expand_scripts(Scripts)];
84expand_scripts([]) ->
85    [].
86
87expand_script([I|Script]) ->
88    I2 = case I of
89	     {load_module, Mod} ->
90		 {load_module, Mod, brutal_purge, brutal_purge, []};
91	     {load_module, Mod, Mods} when is_list(Mods) ->
92		 {load_module, Mod, brutal_purge, brutal_purge, Mods};
93	     {update, Mod} ->
94		 {update, Mod, soft, brutal_purge, brutal_purge, []};
95	     {update, Mod, supervisor} ->
96		 {update, Mod, static, default, {advanced,[]},
97		  brutal_purge, brutal_purge, []};
98	     {update, Mod, Change} when is_tuple(Change) ->
99		 {update, Mod, Change, brutal_purge, brutal_purge, []};
100	     {update, Mod, Change} when Change==soft ->
101		 {update, Mod, Change, brutal_purge, brutal_purge, []};
102	     {update, Mod, Mods} when is_list(Mods) ->
103		 {update, Mod, soft, brutal_purge, brutal_purge, Mods};
104	     {update, Mod, Change, Mods} when is_tuple(Change),
105					      is_list(Mods) ->
106		 {update, Mod, Change, brutal_purge,brutal_purge, Mods};
107	     {update, Mod, Change, Mods} when Change==soft,
108					      is_list(Mods) ->
109		 {update, Mod, Change, brutal_purge,brutal_purge, Mods};
110	     {add_application, Application} ->
111		 {add_application, Application, permanent};
112	     _ ->
113		 I
114	 end,
115    if
116	is_list(I2) ->
117	    I2 ++ expand_script(Script);
118	true ->
119	    [I2|expand_script(Script)]
120    end;
121expand_script([]) ->
122    [].
123
124do_translate_scripts(Mode, Scripts, Appls, PreAppls) ->
125    MergedScript = merge_scripts(Scripts),
126    translate_merged_script(Mode, MergedScript, Appls, PreAppls).
127
128%%-----------------------------------------------------------------
129%% All check_ functions performs checks, and throws {error, Reason}
130%% (or fails) in case of error.  Called functions may throw error or
131%% fail. The script is split into instructions before and after
132%% point_of_no_return.  In Before, only load_object_code and apply are
133%% allowed.
134%% %%-----------------------------------------------------------------
135translate_merged_script(Mode, Script, Appls, PreAppls) ->
136    check_syntax(Script),
137    Script1 = normalize_instrs(Script),
138    {Before, After} = split_script(Script1),
139    check_script(Before, After),
140
141    {Before1, After1} = translate_independent_instrs(Before, After, Appls, PreAppls),
142    {Before2, After2} = translate_dependent_instrs(Mode, Before1, After1,
143						  Appls),
144    Before3 = merge_load_object_code(Before2),
145
146    {Before4,After4} = sort_emulator_restart(Mode,Before3,After2),
147    NewScript = Before4 ++ [point_of_no_return | After4],
148
149    check_syntax(NewScript),
150    {ok, NewScript}.
151
152%%-----------------------------------------------------------------
153%% SPLIT AND MERGE
154%%-----------------------------------------------------------------
155
156%%-----------------------------------------------------------------
157%% merge_scripts(Scripts) -> Script
158%%
159%% Splits each script into before and after, and merges the before and
160%% after parts.
161%%-----------------------------------------------------------------
162merge_scripts(Scripts) ->
163    {Before, After} =
164	lists:foldl(
165	  fun(Script, {B1, A1}) ->
166		  {B2, A2} = split_script(Script),
167		  {B1 ++ B2, A1 ++ A2}
168	  end, {[], []},Scripts),
169    Before ++ [point_of_no_return | After].
170
171%%-----------------------------------------------------------------
172%% split_script(Script) -> {Before, After}
173%%
174%% Splits the script into instructions before and after
175%% point_of_no_return. Puts all load_object_code instructions in
176%% Before.  Checks that there is at most one point_of_no_return.
177%% Makes sure that if there was a point_of_no_return, only apply and
178%% load_object_code are before the point_of_no_return.
179%%-----------------------------------------------------------------
180split_script(Script) ->
181    {Before, After} = split_instrs(Script),
182    lists:foreach(
183      fun({load_object_code, _}) -> ok;
184	 ({apply, _}) -> ok;
185	 (Instruction) ->
186	      throw({error, {bad_op_before_point_of_no_return,
187			     Instruction}})
188      end, Before),
189    {Found, Rest} = split(fun({load_object_code, _}) -> true;
190			     (_) -> false
191			  end, After),
192    {Before ++ Found, Rest}.
193
194%% split_instrs(Script) -> {Before, After} Split the
195%% instructions into the set of those that appear before
196%% point_of_no_return, and the set of those that appear after. If
197%% there is no point_of_no_return instruction {[], Script} is
198%% returned.
199split_instrs(Script) ->
200    split_instrs(Script, []).
201split_instrs([point_of_no_return | T], Before) ->
202    case lists:member(point_of_no_return, T) of
203	true -> throw({error, too_many_point_of_no_return});
204	false -> {lists:reverse(Before), T}
205    end;
206split_instrs([H | T], Before) ->
207    split_instrs(T, [H | Before]);
208split_instrs([], Before) ->
209    {[], lists:reverse(Before)}.
210
211%%-----------------------------------------------------------------
212%% CHECKS
213%%-----------------------------------------------------------------
214
215check_script(Before, After) ->
216    check_load(Before, After),
217    check_suspend_resume(After),
218    check_start_stop(After).
219
220%%-----------------------------------------------------------------
221%% Checks that each load has a corresponding load_object_code.
222%%-----------------------------------------------------------------
223check_load(Before, After) ->
224    lists:foreach(
225      fun({load, {Mod, _, _}}) ->
226	      case find_object_code(Mod, Before) of
227		  true -> ok;
228		  false -> throw({error, {no_object_code, Mod}})
229	      end;
230	 (_) -> ok
231      end, After).
232
233find_object_code(Mod, [{load_object_code, {_, _, Mods}} | T]) ->
234    case lists:member(Mod, Mods) of
235	true -> true;
236	false -> find_object_code(Mod, T)
237    end;
238find_object_code(Mod, [_|T]) ->
239    find_object_code(Mod, T);
240find_object_code(_Mod, []) ->
241    false.
242
243%%-----------------------------------------------------------------
244%% Checks that all suspended Mods are resumed, and that none are
245%% resumed/code_changed but not suspended.
246%%-----------------------------------------------------------------
247check_suspend_resume(Script) ->
248    Suspended   = lists:map(fun({Mod, _Timeout}) -> Mod;
249			       (Mod) -> Mod
250			    end,
251			    lists:flatten([X || {suspend, X} <- Script])),
252    Resumed = lists:flatten([X || {resume, X} <- Script]),
253    CodeChanged = lists:flatten([X || {code_change, _, {X, _}} <- Script]),
254    case difference(Suspended, Resumed) of
255	[] -> ok;
256	S2 -> throw({error, {suspended_not_resumed, S2}})
257    end,
258    case difference(Resumed, Suspended) of
259	[] -> ok;
260	R2 -> throw({error, {resumed_not_suspended, R2}})
261    end,
262    case difference(CodeChanged, Suspended) of
263	[] -> ok;
264	C2 -> throw({error, {code_change_not_suspended, C2}})
265    end.
266
267%%-----------------------------------------------------------------
268%% Checks that all stops are started, and that all starts are
269%% stopped.
270%%-----------------------------------------------------------------
271check_start_stop(Script) ->
272    Start = lists:flatten([X || {start, X} <- Script]),
273    Stop  = lists:flatten([X || {stop, X}  <- Script]),
274    case difference(Start, Stop) of
275	[] -> ok;
276	S2 -> throw({error, {start_not_stop, S2}})
277    end,
278    case difference(Stop, Start) of
279	[] -> ok;
280	S3 -> throw({error, {stop_not_start, S3}})
281    end.
282
283
284%%-----------------------------------------------------------------
285%% NORMALISATION
286%%-----------------------------------------------------------------
287%%-----------------------------------------------------------------
288%% Normalize those instructions that have variants (update and
289%% add_module).
290%%-----------------------------------------------------------------
291normalize_instrs(Script) ->
292    lists:map(fun({update, Mod, Change, PrePurge, PostPurge, Mods}) ->
293		      {update, Mod, dynamic, default, Change, PrePurge,
294		       PostPurge, Mods};
295		 ({update, Mod, Timeout, Change, PrePurge, PostPurge,
296		   Mods}) ->
297		      {update, Mod, dynamic, Timeout, Change, PrePurge,
298		       PostPurge, Mods};
299		 ({add_module, Mod}) ->
300		      {add_module, Mod, []};
301		 ({delete_module, Mod}) ->
302		      {delete_module, Mod, []};
303		 (I) ->
304		      I
305	      end, Script).
306
307%%-----------------------------------------------------------------
308%% TRANSLATION OF INDEPENDENT INSTRUCTIONS
309%%-----------------------------------------------------------------
310
311%% translate_independent_instrs(Before, After, Appls, PreAppls) ->
312%%						{NBefore, NAfter}
313%%
314translate_independent_instrs(Before, After, Appls, PreAppls) ->
315    After1 = translate_application_instrs(After, Appls, PreAppls),
316    translate_add_module_instrs(Before, After1).
317
318%%-----------------------------------------------------------------
319%% Translates add_application, remove_application  and restart_application
320%% into add_module, remove, purge and apply.
321%%-----------------------------------------------------------------
322translate_application_instrs(Script, Appls, PreAppls) ->
323    %% io:format("Appls ~n~p~n",[Appls]),
324    L = lists:map(
325	  fun({add_application, Appl, Type}) ->
326		  case lists:keysearch(Appl, #application.name, Appls) of
327		      {value, Application} ->
328			  Mods = Application#application.modules,
329			  ApplyL = case Type of
330			      none -> [];
331			      load -> [{apply, {application, load, [Appl]}}];
332			      _ -> [{apply, {application, start, [Appl, Type]}}]
333			  end,
334			  [{add_module, M, []} || M <- Mods] ++
335			      ApplyL;
336		      false ->
337			  throw({error, {no_such_application, Appl}})
338		  end;
339
340	     ({remove_application, Appl}) ->
341		  case lists:keysearch(Appl, #application.name, Appls) of
342		      {value, _Application} ->
343			  throw({error, {removed_application_present,
344					 Appl}});
345		      false ->
346			  ignore
347		  end,
348		  case lists:keysearch(Appl, #application.name, PreAppls) of
349		      {value, RemApplication} ->
350			  Mods = RemApplication#application.modules,
351
352			  [{apply, {application, stop, [Appl]}}] ++
353			      [{remove, {M, brutal_purge, brutal_purge}}
354			       || M <- Mods] ++
355			      [{purge, Mods},
356			       {apply, {application, unload, [Appl]}}];
357		      false ->
358			  throw({error, {no_such_application, Appl}})
359		  end;
360	     ({restart_application, Appl}) ->
361		  case lists:keysearch(Appl, #application.name, PreAppls) of
362		      {value, PreApplication} ->
363			  PreMods = PreApplication#application.modules,
364			  case lists:keysearch(Appl, #application.name, Appls) of
365			      {value, PostApplication} ->
366				  PostMods = PostApplication#application.modules,
367				  Type = PostApplication#application.type,
368				  Apply =
369				      case Type of
370					  none -> [];
371					  load -> [{apply, {application, load,
372							    [Appl]}}];
373					  _ -> [{apply, {application, start,
374							 [Appl, Type]}}]
375				      end,
376
377				  [{apply, {application, stop, [Appl]}}] ++
378				      [{remove, {M, brutal_purge, brutal_purge}}
379				       || M <- PreMods] ++
380				      [{purge, PreMods}] ++
381				      [{add_module, M, []} || M <- PostMods] ++
382				      Apply;
383			      false ->
384				  throw({error, {no_such_application, Appl}})
385			  end;
386
387		      false ->
388			  throw({error, {no_such_application, Appl}})
389		  end;
390	     (X) -> X
391	  end, Script),
392    lists:flatten(L).
393
394%%-----------------------------------------------------------------
395%% Translates add_module into load_module (high-level transformation)
396%%-----------------------------------------------------------------
397translate_add_module_instrs(Before, After) ->
398    NAfter = lists:map(
399	       fun({add_module, Mod, Mods}) ->
400		       %% Purge method really doesn't matter. Module
401		       %% is new.
402		       {load_module, Mod, brutal_purge, brutal_purge, Mods};
403		  (I) ->
404		       I
405	       end, After),
406    {Before, NAfter}.
407
408
409%%-----------------------------------------------------------------
410%% TRANSLATION OF INSTRUCTIONS WITH DEPENDENCIES
411%%-----------------------------------------------------------------
412
413%%-----------------------------------------------------------------
414%% Translates update, load_module and delete_module, and reorder the
415%% instructions according to dependencies. Leaves other instructions
416%% unchanged.
417%%-----------------------------------------------------------------
418translate_dependent_instrs(Mode, Before, After, Appls) ->
419    %% G is the total dependency graph, WCs is the decomposition of
420    %% the vertices (lists of vertices) of G.
421    G = make_dependency_graph(After),
422    WCs = digraph_utils:components(G),
423    {NBefore, NAfter} = translate_dep_loop(G, WCs, After, Appls,
424					   [], [], Mode),
425    digraph:delete(G),
426    {Before ++ NBefore, NAfter}.
427
428translate_dep_loop(G, WCs, [I| Is], Appls, Before, After, Mode)
429  when is_tuple(I), size(I) > 1 ->
430    IName = element(1, I),
431    case lists:member(IName, ?DEP_INSTRS) of
432	true ->
433	    Mod = element(2, I),
434	    DepIs = get_dependent_instructions(G, WCs, Mod),
435	    {B2, A2} = translate_dep_to_low(Mode, DepIs, Appls),
436	    RemIs = difference([I| Is], DepIs),
437	    translate_dep_loop(G, WCs, RemIs, Appls, Before ++ B2,
438			       After ++ A2, Mode);
439	false ->
440	    translate_dep_loop(G, WCs, Is, Appls, Before,
441			       After ++ [I], Mode)  % hmm
442    end;
443translate_dep_loop(G, WCs, [I| Is], Appls, Before, After, Mode) ->
444    translate_dep_loop(G, WCs, Is, Appls, Before, After ++ [I], Mode);  % hmm
445translate_dep_loop(_G, _WCs, [], _Appls, Before, After, _Mode) ->
446    {Before, After}.
447
448
449%%-----------------------------------------------------------------
450%% make_dependency_graph(Instructions) -> graph()
451%%
452%% The return value is a digraph graph(). A vertex is a module name
453%% Mod, and the associated data is {N, I} where I is the corresponding
454%% instruction, and N numbers the instruction in the order given at
455%% input. Only instructions that have dependencies are considered.
456%% %%-----------------------------------------------------------------
457make_dependency_graph(Instructions) ->
458    %% Filter out dependent instructions
459    DepIs = lists:filter(fun(I) when is_tuple(I) ->
460			       IName = element(1, I),
461			       lists:member(IName, ?DEP_INSTRS);
462			    (_) ->
463				 false
464			 end, Instructions),
465    {VDs, _} = lists:mapfoldl(
466			     fun(I, N) ->
467				     Mod = element(2, I),
468				     Mods = element(size(I), I),
469				     {{Mod, Mods, {N, I}}, N+1}
470			     end, 1, DepIs),
471    G = digraph:new(),
472    %% Add vertices
473    lists:foreach(
474      fun({Mod, _Mods, Data}) ->
475	      case digraph:vertex(G, Mod) of
476		  false ->
477		      digraph:add_vertex(G, Mod, Data);
478		  _  ->
479		      throw({error, {muldef_module, Mod}})
480	      end
481      end, VDs),
482    %% Add edges
483    lists:foreach(
484      fun({Mod, Mods, _Data}) ->
485	      lists:foreach(
486		fun(M) ->
487			case digraph:add_edge(G, Mod, M) of
488			    {error, _Reason} ->
489				throw({error, {undef_module, M}});
490			    _ ->
491				ok
492			end
493		end, Mods)
494      end, VDs),
495    G.
496
497%% get_dependent_instructions(G, WCs, Mod) -> DepIs
498%%
499%% G is the global dependency graph, WCs are the weak components
500%% (lists of vertices) of G, and Mod is the module for which we will
501%% pick up all instructions that Mod depends on, or that depend on
502%% Mod.
503%%
504get_dependent_instructions(G, WCs, Mod) ->
505    case lists:filter(fun(C) -> lists:member(Mod, C) end, WCs) of
506	[WC] ->
507	    %% restrict G to WC
508	    H = restriction(WC, G),
509	    %% vertices of S are strong components of H
510	    S = condensation(H),
511	    Ts = digraph_utils:topsort(S),
512	    DepIss = lists:map(
513		       fun(T) ->
514			       NIs = lists:map(
515				       fun(V) ->
516					       {_, Data} =
517						   digraph:vertex(H, V),
518					       Data
519				       end, T),
520			       %% NIs = [{N, I}]
521			       SortedNIs = lists:keysort(1, NIs),
522			       lists:map(fun({_N, I}) -> I end, SortedNIs)
523		       end, Ts),
524	    DepIs = lists:flatten(DepIss),	% XXX One level flat only
525	    digraph:delete(H),
526	    digraph:delete(S),
527	    DepIs;
528	[] ->
529	    throw({error, {undef_module, Mod}});
530	_ ->
531	    throw({error, {muldef_module, Mod}})
532    end.
533
534%% translate_dep_to_low(Mode, Instructions, Appls) -> {Before, After}
535%%
536%% Mode = up | dn
537%% Instructions are in order of dependency.
538%% Appls = [#application]
539%%
540%% Instructions translated are: update, load_module, and delete_module
541%%
542%% Before =	[{load_object_code, ...}]
543%% After = 	[{suspend, ...}] ++ CodeInstrs ++ [{resume, ...}]
544%% CodeInstrs = [{load, ...}] ++ [{code_change, ...}]  (Mode == up)
545%% 	      = [{code_change, ...}] ++ [{load, ...}] ++
546%% 		[{code_change, ...}]  (Mode == dn)
547%%
548translate_dep_to_low(Mode, Instructions, Appls) ->
549    UpdateMods =
550	filtermap(fun({update, Mod, _, default, _, _, _, _}) ->
551			  {true, Mod};
552		     ({update, Mod, _, T, _, _, _, _}) ->
553			  {true, {Mod, T}};
554		     (_) ->
555			  false
556		  end,
557		  Instructions),
558    RevUpdateMods = lists:reverse(UpdateMods),
559
560    %% Processes are suspended in the order of dependency.
561    SuspendInstrs =
562	if
563	    UpdateMods == [] -> [];
564	    true -> [{suspend, UpdateMods}]
565	end,
566
567
568    %% Processes are resumed in the reversed order of dependency.
569    ResumeInstrs =
570	if
571	    UpdateMods == [] -> [];
572	    true -> [{resume,
573		      lists:map(fun({Mod, _T}) -> Mod;
574				   (Mod) -> Mod
575				end, RevUpdateMods)}]
576	end,
577
578    LoadRemoveInstrs0 =
579	filtermap(fun({update, Mod, _, _, _, PreP, PostP, _}) ->
580			  {true, {load, {Mod, PreP, PostP}}};
581		     ({load_module, Mod, PreP, PostP, _}) ->
582			  {true, {load, {Mod, PreP, PostP}}};
583		     ({delete_module, Mod, _}) ->
584			  {true,[{remove, {Mod, brutal_purge, brutal_purge}},
585				 {purge, [Mod]}]};
586		     (_) -> false
587		  end,
588		  Instructions),
589    LoadRemoveInstrs = lists:flatten(LoadRemoveInstrs0),
590    RevLoadRemoveInstrs = lists:flatten(lists:reverse(LoadRemoveInstrs0)),
591
592    %% The order of loading object code is unimportant. The order
593    %% chosen is the order of dependency.
594    LoadObjCodeInstrs =
595	filtermap(fun({load, {Mod, _, _}}) ->
596		    {Lib, LibVsn} = get_lib(Mod, Appls),
597		    {true, {load_object_code, {Lib, LibVsn, [Mod]}}};
598		     (_) -> false
599	    end, LoadRemoveInstrs),
600    if
601	Mode == up ->
602	    %% The order of changing code is unimportant (processes
603	    %% are suspended). The order chosen is the order of
604	    %% dependency.
605	    CodeChangeMods =
606		filtermap(fun({update, Mod, _, _,
607			       {advanced, Extra}, _, _, _}) ->
608				  {true, {Mod, Extra}};
609			     (_) ->
610				  false
611			  end, Instructions),
612	    CodeChangeInstrs =
613		if
614		    CodeChangeMods == [] -> [];
615		    true -> [{code_change, up, CodeChangeMods}]
616		end,
617	    %% RevLoadRemoveInstrs: When upgrading modules are loaded
618	    %% in the reversed order of dependency.
619	    {LoadObjCodeInstrs,
620	     SuspendInstrs ++ RevLoadRemoveInstrs ++ CodeChangeInstrs ++
621	     ResumeInstrs};
622
623	Mode == dn ->
624	    %% PreCodeChangeMods is the list of all modules that have
625	    %% to change code *before* the code is loaded (when
626	    %% downgrading). The order is not important (processes are
627	    %% suspended). The order chosen is the order of
628	    %% dependency.
629	    PreCodeChangeMods =
630		[{Mod, Extra} ||
631		    {update, Mod, dynamic, _, {advanced, Extra}, _, _, _}
632			<- Instructions],
633	    PreCodeChangeInstrs =
634		if
635		    PreCodeChangeMods == [] -> [];
636		    true -> [{code_change, down, PreCodeChangeMods}]
637		end,
638	    %% PostCodeChangeMods is the list of all modules that have
639	    %% to change code *after* the code is loaded (when
640	    %% downgrading). The order is not important (processes are
641	    %% suspended). The order chosen is the order of
642	    %% dependency.
643	    PostCodeChangeMods =
644		[{Mod, Extra} ||
645		    {update, Mod, static, _, {advanced, Extra}, _, _, _}
646			<- Instructions],
647	    PostCodeChangeInstrs =
648		if
649		    PostCodeChangeMods == [] -> [];
650		    true -> [{code_change, down, PostCodeChangeMods}]
651		end,
652	    %% LoadRemoveInstrs: When downgrading modules are loaded
653	    %% in the order of dependency.
654	    {LoadObjCodeInstrs,
655	     SuspendInstrs ++ PreCodeChangeInstrs ++
656	     LoadRemoveInstrs ++ PostCodeChangeInstrs ++ ResumeInstrs}
657    end.
658
659get_lib(Mod, [#application{name = Name, vsn = Vsn, modules = Modules} | T]) ->
660    case lists:member(Mod, Modules) of
661	true -> {Name, Vsn};
662	false ->   get_lib(Mod, T)
663    end;
664get_lib(Mod, []) ->
665    throw({error, {no_such_module, Mod}}).
666
667%%-----------------------------------------------------------------
668%% MERGE LOAD_OBJECT_CODE
669%%-----------------------------------------------------------------
670%%-----------------------------------------------------------------
671%% Merge load_object_code instructions into one load_object_code
672%% instruction per lib (optimization). Order is preserved.
673%%-----------------------------------------------------------------
674merge_load_object_code(Before) ->
675    {Found, Rest} = split(fun({load_object_code, _}) -> true;
676			     (_) -> false
677			  end, Before),
678    mlo(Found) ++ Rest.
679
680mlo([{load_object_code, {Lib, LibVsn, Mods}} | T]) ->
681    {Same, Other} = split(fun({load_object_code, {Lib2, LibVsn2, _Mods2}})
682			       when Lib == Lib2, LibVsn == LibVsn2 -> true;
683			     ({load_object_code, {Lib2, LibVsn2, _Mods2}})
684			       when Lib == Lib2 ->
685				  throw({error, {conflicting_versions,
686						 Lib, LibVsn, LibVsn2}});
687			     (_) -> false
688			  end, T),
689    %% io:format("Same = ~p, Other = ~p~n", [Same, Other]),
690    %% foldr to preserver order.
691    OCode0 = lists:foldr(fun({load_object_code, {_, _, Ms}}, Res) ->
692				 U = union(Ms, Res),
693				 %% io:format("Ms = ~p, Res = ~p, U = ~p~n",
694				 %% [Ms, Res, U]),
695				 U
696			 end, [], Same),
697    OCode1 = union(Mods, OCode0),		% preserve order
698    %% io:format("OCode0 = ~p, OCode1 = ~p~n", [OCode0, OCode1]),
699    [{load_object_code, {Lib, LibVsn, OCode1}} | mlo(Other)];
700mlo([]) -> [].
701
702%%-----------------------------------------------------------------
703%% RESTART EMULATOR
704%% -----------------------------------------------------------------
705%% -----------------------------------------------------------------
706%% Check if there are any 'restart_new_emulator' instructions (i.e. if
707%% the emulator or core application version is changed). If so, this
708%% must be done first for upgrade and last for downgrade.
709%% Check if there are any 'restart_emulator' instructions, if so
710%% remove all and place one the end.
711%% -----------------------------------------------------------------
712sort_emulator_restart(Mode,Before,After) ->
713    {Before1,After1} =
714	case filter_out(restart_new_emulator, After) of
715	    After ->
716		{Before,After};
717	    A1 when Mode==up ->
718		{[restart_new_emulator|Before],A1};
719	    A1 when Mode==dn ->
720		{Before,A1++[restart_emulator]}
721	end,
722    After2 =
723	case filter_out(restart_emulator, After1) of
724	    After1 ->
725		After1;
726	    A2 ->
727		A2++[restart_emulator]
728	end,
729    {Before1,After2}.
730
731
732filter_out(What,List) ->
733    lists:filter(fun(X) when X=:=What -> false; (_) -> true end, List).
734
735%%-----------------------------------------------------------------
736%% SYNTAX CHECK
737%%-----------------------------------------------------------------
738%%-----------------------------------------------------------------
739%% Checks the syntax of all instructions.
740%%-----------------------------------------------------------------
741check_syntax([H|T]) ->
742    check_op(H),
743    check_syntax(T);
744check_syntax([]) -> ok.
745
746check_op(mnesia_backup) ->
747    throw({error, {not_yet_implemented, mnesia_backup}});
748check_op({update, Mod, Change, PrePurge, PostPurge, Mods}) ->
749    check_mod(Mod),
750    check_change(Change),
751    check_purge(PrePurge),
752    check_purge(PostPurge),
753    check_list(Mods),
754    lists:foreach(fun(M) -> check_mod(M) end, Mods);
755check_op({update, Mod, Timeout, Change, PrePurge, PostPurge, Mods}) ->
756    check_mod(Mod),
757    check_timeout(Timeout),
758    check_change(Change),
759    check_purge(PrePurge),
760    check_purge(PostPurge),
761    check_list(Mods),
762    lists:foreach(fun(M) -> check_mod(M) end, Mods);
763check_op({update, Mod, ModType, Timeout, Change, PrePurge, PostPurge,
764	  Mods}) ->
765    check_mod(Mod),
766    check_mod_type(ModType),
767    check_timeout(Timeout),
768    check_change(Change),
769    check_purge(PrePurge),
770    check_purge(PostPurge),
771    check_list(Mods),
772    lists:foreach(fun(M) -> check_mod(M) end, Mods);
773check_op({load_module, Mod, PrePurge, PostPurge, Mods}) ->
774    check_mod(Mod),
775    check_purge(PrePurge),
776    check_purge(PostPurge),
777    check_list(Mods),
778    lists:foreach(fun(M) -> check_mod(M) end, Mods);
779check_op({add_module, Mod}) ->
780    check_mod(Mod);
781check_op({add_module, Mod, Mods}) ->
782    check_mod(Mod),
783    check_list(Mods),
784    lists:foreach(fun(M) -> check_mod(M) end, Mods);
785check_op({delete_module, Mod}) ->
786    check_mod(Mod);
787check_op({delete_module, Mod, Mods}) ->
788    check_mod(Mod),
789    check_list(Mods),
790    lists:foreach(fun(M) -> check_mod(M) end, Mods);
791check_op({remove_application, Appl}) ->
792    check_appl(Appl);
793check_op({add_application, Appl, Type}) ->
794    check_appl(Appl),
795    check_start_type(Type);
796check_op({restart_application, Appl}) ->
797    check_appl(Appl);
798check_op(restart) -> ok;
799check_op(reboot) -> ok;
800check_op({load_object_code, {Lib, LibVsn, Mods}}) ->
801    check_lib(Lib),
802    check_lib_vsn(LibVsn),
803    check_list(Mods),
804    lists:foreach(fun(M) -> check_mod(M) end, Mods);
805check_op(point_of_no_return) -> ok;
806check_op({load, {Mod, PrePurge, PostPurge}}) ->
807    check_mod(Mod),
808    check_purge(PrePurge),
809    check_purge(PostPurge);
810check_op({remove, {Mod, PrePurge, PostPurge}}) ->
811    check_mod(Mod),
812    check_purge(PrePurge),
813    check_purge(PostPurge);
814check_op({purge, Mods}) ->
815    check_list(Mods),
816    lists:foreach(fun(M) -> check_mod(M) end, Mods);
817check_op({suspend, Mods}) ->
818    check_list(Mods),
819    lists:foreach(fun({M,T}) -> check_mod(M), check_timeout(T);
820		     (M) -> check_mod(M)
821		  end, Mods);
822check_op({resume, Mods}) ->
823    check_list(Mods),
824    lists:foreach(fun(M) -> check_mod(M) end, Mods);
825check_op({code_change, Mods}) ->
826    check_list(Mods),
827    lists:foreach(fun({M, _Extra}) -> check_mod(M);
828		     (X) -> throw({error, {bad_code_change, X}})
829		  end, Mods);
830check_op({code_change, Mode, Mods}) ->
831    check_list(Mods),
832    check_mode(Mode),
833    lists:foreach(fun({M, _Extra}) -> check_mod(M);
834		     (X) -> throw({error, {bad_code_change, X}})
835		  end, Mods);
836check_op({stop, Mods}) ->
837    check_list(Mods),
838    lists:foreach(fun(M) -> check_mod(M) end, Mods);
839check_op({start, Mods}) ->
840    check_list(Mods),
841    lists:foreach(fun(M) -> check_mod(M) end, Mods);
842check_op({sync_nodes, _Id, {M, F, A}}) ->
843    check_mod(M),
844    check_func(F),
845    check_args(A);
846check_op({sync_nodes, _Id, Nodes}) ->
847    check_list(Nodes),
848    lists:foreach(fun(Node) -> check_node(Node) end, Nodes);
849check_op({apply, {M, F, A}}) ->
850    check_mod(M),
851    check_func(F),
852    check_args(A);
853check_op(restart_new_emulator) -> ok;
854check_op(restart_emulator) -> ok;
855check_op(X) -> throw({error, {bad_instruction, X}}).
856
857check_mod(Mod) when is_atom(Mod) -> ok;
858check_mod(Mod) -> throw({error, {bad_module, Mod}}).
859
860check_change(soft) -> ok;
861check_change({advanced, _}) -> ok;
862check_change(Change) -> throw({error, {bad_change, Change}}).
863
864check_mod_type(static) -> ok;
865check_mod_type(dynamic) -> ok;
866check_mod_type(ModType) -> throw({error, {bad_mod_type, ModType}}).
867
868check_purge(soft_purge) -> ok;
869check_purge(brutal_purge) -> ok;
870check_purge(Purge) -> throw({error, {bad_purge_method, Purge}}).
871
872check_list(List) when is_list(List) -> ok;
873check_list(List) -> throw({error, {bad_list, List}}).
874
875check_args(Args) when is_list(Args) -> ok;
876check_args(Args) -> throw({error, {bad_args_list, Args}}).
877
878check_node(Node) when is_atom(Node) -> ok;
879check_node(Node) -> throw({error, {bad_node, Node}}).
880
881check_appl(Appl) when is_atom(Appl) -> ok;
882check_appl(Appl) -> throw({error, {bad_application, Appl}}).
883
884check_start_type(none) -> ok;
885check_start_type(load) -> ok;
886check_start_type(temporary) -> ok;
887check_start_type(transient) -> ok;
888check_start_type(permanent) -> ok;
889check_start_type(T) -> throw({error, {bad_start_type, T}}).
890
891check_func(Func) when is_atom(Func) -> ok;
892check_func(Func) -> throw({error, {bad_func, Func}}).
893
894check_lib(Lib) when is_atom(Lib) -> ok;
895check_lib(Lib) -> throw({error, {bad_lib, Lib}}).
896
897check_lib_vsn(LibVsn) when is_list(LibVsn) -> ok;
898check_lib_vsn(LibVsn) -> throw({error, {bad_lib_vsn, LibVsn}}).
899
900check_timeout(default) -> ok;
901check_timeout(infinity) -> ok;
902check_timeout(Int) when is_integer(Int), Int > 0 -> ok;
903check_timeout(T) -> throw({error, {bad_timeout, T}}).
904
905check_mode(up) -> ok;
906check_mode(down) -> ok;
907check_mode(Mode) -> throw({error, {bad_mode, Mode}}).
908
909%%-----------------------------------------------------------------
910%% Format error
911%%-----------------------------------------------------------------
912format_error({bad_op_before_point_of_no_return, Instruction}) ->
913    io_lib:format("Bad instruction ~p~nbefore point_of_no_return~n",
914		  [Instruction]);
915format_error({no_object_code, Mod}) ->
916    io_lib:format("No load_object_code found for module: ~w~n", [Mod]);
917format_error({suspended_not_resumed, Mods}) ->
918    io_lib:format("Suspended but not resumed: ~p~n", [Mods]);
919format_error({resumed_not_suspended, Mods}) ->
920    io_lib:format("Resumed but not suspended: ~p~n", [Mods]);
921format_error({code_change_not_suspended, Mods}) ->
922    io_lib:format("Code changed but not suspended: ~p~n", [Mods]);
923format_error({start_not_stop, Mods}) ->
924    io_lib:format("Started but not stopped: ~p~n", [Mods]);
925format_error({stop_not_start, Mods}) ->
926    io_lib:format("Stopped but not started: ~p~n", [Mods]);
927format_error({no_such_application, App}) ->
928    io_lib:format("Started undefined application: ~w~n", [App]);
929format_error({removed_application_present, App}) ->
930    io_lib:format("Removed application present: ~w~n", [App]);
931format_error(dup_mnesia_backup) ->
932    io_lib:format("Duplicate mnesia_backup~n", []);
933format_error(bad_mnesia_backup) ->
934    io_lib:format("mnesia_backup in bad position~n", []);
935format_error({conflicting_versions, Lib, V1, V2}) ->
936    io_lib:format("Conflicting versions for ~w, ~ts and ~ts~n", [Lib, V1, V2]);
937format_error({no_appl_vsn, Appl}) ->
938    io_lib:format("No version specified for application: ~w~n", [Appl]);
939format_error({no_such_module, Mod}) ->
940    io_lib:format("No such module: ~w~n", [Mod]);
941format_error(too_many_point_of_no_return) ->
942    io_lib:format("Too many point_of_no_return~n", []);
943
944format_error({bad_instruction, X}) ->
945    io_lib:format("Bad instruction: ~tp~n", [X]);
946format_error({bad_module, X}) ->
947    io_lib:format("Bad module: ~tp(should be atom())~n", [X]);
948format_error({bad_code_change, X}) ->
949    io_lib:format("Bad code_change: ~tp(should be {Mod, Extra})~n", [X]);
950format_error({bad_change, X}) ->
951    io_lib:format("Bad change spec: ~tp(should be soft | {advanced, E})~n", [X]);
952format_error({bad_mod_type, X}) ->
953    io_lib:format("Bad module type: ~tp(should be static | dynamic)~n", [X]);
954format_error({bad_purge_method, X}) ->
955    io_lib:format("Bad purge method: ~tp(should be soft_purge | brutal_purge)~n",
956		  [X]);
957format_error({bad_list, X}) ->
958    io_lib:format("Bad list: ~tp~n", [X]);
959format_error({bad_args_list, X}) ->
960    io_lib:format("Bad argument list: ~tp~n", [X]);
961format_error({bad_node, X}) ->
962    io_lib:format("Bad node: ~tp(should be atom())~n", [X]);
963format_error({bad_application, X}) ->
964    io_lib:format("Bad application: ~tp(should be atom())~n", [X]);
965format_error({bad_func, X}) ->
966    io_lib:format("Bad function: ~tp(should be atom())~n", [X]);
967format_error({bad_lib, X}) ->
968    io_lib:format("Bad library: ~tp(should be atom())~n", [X]);
969format_error({bad_lib_vsn, X}) ->
970    io_lib:format("Bad library version: ~tp(should be string())~n", [X]);
971format_error({bad_timeout, X}) ->
972    io_lib:format("Bad timeout: ~tp(should be infinity | int() > 0)~n", [X]);
973
974format_error({undef_module, Mod}) ->
975    io_lib:format("Undefined module: ~p~n", [Mod]);
976format_error({muldef_module, Mod}) ->
977    io_lib:format("Multiply defined module: ~p~n", [Mod]);
978format_error(E) ->
979    io_lib:format("~tp~n",[E]).
980
981
982%%-----------------------------------------------------------------
983%% MISC SUPPORT
984%%-----------------------------------------------------------------
985
986%% filtermap(F, List1) -> List2
987%% F(H) -> false | true | {true, Val}
988filtermap(F, List) ->
989    lists:zf(F, List).
990
991%% split(F, List1) -> {List2, List3}
992%% F(H) -> true | false. Preserves order.
993split(Fun, [H | T]) ->
994    {Found, Rest} = split(Fun, T),
995    case Fun(H) of
996	true -> {[H | Found], Rest};
997	false -> {Found, [H | Rest]}
998    end;
999split(_Fun, []) ->
1000    {[], []}.
1001
1002union([H|T], L) ->
1003    case lists:member(H, L) of
1004	true -> union(T,L);
1005	false -> [H | union(T, L)]
1006    end;
1007union([], L) -> L.
1008
1009difference([H | T], L) ->
1010    case lists:member(H, L) of
1011	true -> difference(T, L);
1012	false -> [H | difference(T, L)]
1013    end;
1014difference([], _) -> [].
1015
1016
1017%%-----------------------------------------------------------------
1018%% GRAPHS
1019%%-----------------------------------------------------------------
1020
1021%% Additions to digraph and digraph utils.
1022%% XXX Should be removed in future versions.
1023
1024%% This function should be included in digraph_utils.
1025
1026%% condensation(G) -> graph()
1027%%
1028%% Given a graph G, returns a new graph H where each vertex V in H is
1029%% a strong component of G, and where there is an edge from V1 to V2
1030%% in H if there are members of v1 and v2 of V1 and V2, respectively,
1031%% such that there is an edge from v1 to v2 in G.
1032%%
1033condensation(G) ->
1034    H = digraph:new(),
1035    HVs = digraph_utils:strong_components(G),
1036    %% Add all vertices
1037    lists:foreach(fun(HV) -> digraph:add_vertex(H, HV) end, HVs),
1038    %% Add edges
1039    lists:foreach(
1040      fun(HV1) ->
1041	      GRs = digraph_utils:reachable(HV1, G),
1042	      lists:foreach(
1043		fun(HV2) ->
1044			if
1045			    HV1 /= HV2 ->
1046				case lists:member(hd(HV2), GRs) of
1047				    true ->
1048					digraph:add_edge(H, HV1, HV2);
1049				    _  ->
1050					ok
1051				end;
1052			    true  ->
1053				ok
1054			end
1055		end, HVs)
1056      end,  HVs),
1057    H.
1058
1059
1060%% This function should be included in digraph.
1061
1062%% restriction(Rs, G) -> graph()
1063%%
1064%% Given a graph G, returns a new graph H that is the restriction of
1065%% G to the vertices Rs.
1066%%
1067restriction(Rs, G) ->
1068    H = digraph:new(),
1069    %% Add vertices
1070    lists:foreach(
1071      fun(R) ->
1072	      case digraph:vertex(G, R) of
1073		  {R, Data} ->
1074		      digraph:add_vertex(H, R, Data);
1075		  _  ->
1076		      ok
1077	      end
1078      end, Rs),
1079    %% Add edges
1080    GEs = digraph:edges(G),
1081    lists:foreach(
1082      fun(GE) ->
1083	      {_, GV1, GV2, GData} = digraph:edge(G, GE),
1084	      case {digraph:vertex(H, GV1), digraph:vertex(H, GV2)} of
1085		  {{GV1, _}, {GV2, _}} ->
1086		      digraph:add_edge(H, GE, GV1, GV2, GData);
1087		  _  ->
1088		      ok
1089	      end
1090      end, GEs),
1091    H.
1092
1093
1094