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