(patch install) RE: Distributed applications

Ulf Wiger <>
Thu Mar 9 16:19:05 CET 2000


> From: Sean Hinde <>

> I must admit that I have been struggling a bit with how release handling may
> be used to load small patches into a running system without creating
> complete new release files. I for one would like to see some simplified
> routines for updating small parts of applications while maintaining the
> overall properties of the system. Is this part of your new work? What else
> will be new?


You can get pretty far by just using:

load_patch(Module, SuspendProcs) ->
   [sys:suspend(P) || P <- SuspendProcs],
   code:load_file(Module),
   [sys:resume(P) || P <- SuspendProcs].
   


A slightly more involved program is attached (patch.erl).
Example:

 > mv tmp/sasl.beam tmp/sasl.beam.bak
 > erl -pa tmp/
Erlang (BEAM) emulator version 4.9.1.1

Eshell V4.9.1.1  (abort with ^G)
1> 
....
=PROGRESS REPORT==== 9-Mar-2000::16:13:06 ===
         application: sasl
          started_at: 

1> code:which(sasl).
"/home/etxuwig/work/otp/otp-r6b_1/lib/sasl-1.8.3/ebin/sasl.beam"
2> PatchF = "tmp/sasl.beam".
"tmp/sasl.beam"
3> file:rename(PatchF++".bak", PatchF).
ok
4> patch:file(PatchF).
patch:18 - supervised procs: [<0.32.0>,<0.31.0>]
patch:41 - suspend(<0.32.0>)
patch:41 - suspend(<0.31.0>)
patch:45 - resume(<0.32.0>)
patch:45 - resume(<0.31.0>)
ok
5> 



Ulf Wiger, Chief Designer AXD 301         <>
Ericsson Telecom AB                          tfn: +46  8 719 81 95
Varuvägen 9, Älvsjö                          mob: +46 70 519 81 95
S-126 25 Stockholm, Sweden                   fax: +46  8 719 43 44
-------------- next part --------------
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.0, (the "License"); you may not use this file except in
%%% compliance with the License. You may obtain a copy of the License at
%%% http://www.erlang.org/license/EPL1_0.txt
%%%
%%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations
%%% under the License.
%%%
%%% The Original Code is patch_utils-0.1.
%%%
%%% The Initial Developer of the Original Code is Ericsson Telecom
%%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
%%% Telecom AB. All Rights Reserved.
%%%
%%% Contributor(s): ______________________________________.

%%% File: patch.erl
%%% Author: Ulf Wiger <>
%%%
%%% A small program to illustrate how to apply a patch softly.
%%% The file/1 function does not call any code_change function, but
%%% This would be trivial to add.

-module(patch).
-vsn('0.1').
-date('2000-03-09').
-author('').

-export([file/1]).

-define(DBG(Fmt, Args), ok=io:format("~p:~p - " ++ Fmt, [?MODULE,?LINE|Args])).

file(FileName) ->
    BaseName = filename:basename(FileName),
    Module = list_to_atom(filename:basename(BaseName, ".beam")),
    Processes = get_supervised_procs(Module),
    ?DBG("supervised procs: ~w~n",[Processes]),
    [suspend(P) || P <- Processes],
    code:purge(Module),
    case code:load_file(Module) of
	{module, Module} -> 
	    [resume(P) || P <- Processes],
	    Which = code:which(Module),
	    if Which /= FileName ->
		    error([{module, Module},
			   {"code:which returns", Which}]),
		    {error, loaded_module_not_from_patch_dir};
	       true ->
		    ok
	    end;
	{error, LoadReason} ->
	    error([{mfa, {code, load_file, [Module]}},
		   {error, LoadReason}]),
	    [resume(P) || P <- Processes],
	    {error, module_not_loaded}
    end.


suspend(P) ->
    ?DBG("suspend(~w)~n", [P]),
    sys:suspend(P).

resume(P) ->
    ?DBG("resume(~w)~n", [P]),
    sys:resume(P).

error(Info) ->
    error_logger:error_report(Info).

%%% ----------------------------------------------------------
%%% #           get_supervised_procs
%%% Input: Module:atom()
%%% Output: 
%%% Exceptions: 
%%% Description: Lists all supervised processes that uses this
%%% module as callback module
%%% ----------------------------------------------------------

%%% io:format is used here to create a prettier and easier to read printout.
%%% All io:format statements should be commented when not debugging this func.
get_supervised_procs(Module) ->
    %io:fwrite("~nLook for Module --> ~p~n", [Module]),
    lists:foldl(
      fun(Application, Procs) ->
	      %io:fwrite("Application= ~p~n", [Application]),
              case application_controller:get_master(Application) of
                  Pid when pid(Pid) ->
                      {Root, Mod} = application_master:get_child(Pid),
		      %io:fwrite("Supervisor= ~p (~p)~n", [Mod, Root]),
                      get_procs(supervisor:which_children(Root),
				Root, Module, Indent = "   ") ++
			  case Module of
			      Mod ->
				  [Root | Procs];
			      Module ->
				  Procs
			  end;
                  _NoPid ->
		      %io:fwrite("   No PID~n", []),
		      Procs
              end
      end,
      [],
      [Appl || {Appl, _Name, _Vsn} <- application:which_applications()]).
 
get_procs([{Name, Pid, worker, dynamic} | T], Sup, Module, Indent)
when pid(Pid) ->
    %io:fwrite("~s~p (~p); worker; Modules= dynamic~n", [Indent, Name, Pid]),
    {ok, Mods} = gen:call(Pid, self(), get_modules),
    %io:fwrite("~s   ~p~n", [Indent, Mods]),
    case lists:member(Module, Mods) of
	true ->
	    [Pid | get_procs(T, Sup, Module, Indent)];
	false ->
	    get_procs(T, Sup, Module, Indent)
    end;
get_procs([{Name, Pid, worker, Mods} | T], Sup, Module, Indent)
when pid(Pid), list(Mods) ->
    %io:fwrite("~s~p (~p); worker; Modules= ~p~n", [Indent, Name, Pid, Mods]),
    case lists:member(Module, Mods) of
	true ->
	    [Pid | get_procs(T, Sup, Module, Indent)];
	false ->
	    get_procs(T, Sup, Module, Indent)
    end;

get_procs([{Name, Pid, supervisor, Mods} | T], Sup, Module, Indent)
when pid(Pid) ->
    %io:fwrite("~s~p (~p); supervisor; Modules= ~p~n",[Indent, Name, Pid, Mods]),
    case lists:member(Module, Mods) of
	true ->
	    [Pid | get_procs(T, Sup, Module, Indent)];
	false ->
	    get_procs(T, Sup, Module, Indent)
    end ++ get_procs(supervisor:which_children(Pid), Pid, Module,
		     Indent ++ "   ");

get_procs([H|T], Sup, Module, Indent) ->
    %io:fwrite("~sUnknown= ~p~n", [Indent, H]),
    get_procs(T, Sup, Module, Indent);

get_procs([], _Sup, _Module, Indent) ->
    [].



More information about the erlang-questions mailing list