Search code examples
erlangejabberdmnesia

ejabberd supervisor module


I need to keep a gen_mod process running as it loops every minute and does some cleanup. However once every few days it will crash and I'll have to manually start it back up again.

I could use a basic example of implementing a supervisor into ejabberd_sup so it can keep going. I am struggling to understand the examples that use gen_server.

Thanks for the help.


Solution

  • Here's an example module combining ejabberd's gen_mod and OTP's gen_server. Explanation is inlined in the code.

    -module(cleaner).
    
    -behaviour(gen_server).
    -behaviour(gen_mod).
    
    %% gen_mod requires these exports
    -export([start/2, stop/1]).
    
    %% these are exports for gen_server
    -export([start_link/0]).
    -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
             terminate/2, code_change/3]).
    
    -define(INTERVAL, timer:minutes(1)).
    
    -record(state, {}).
    
    %% ejabberd calls this function when this module is loaded
    %% basically it adds gen_server defined by this module to 
    %% ejabberd main supervisor
    start(Host, Opts) ->
        Proc = gen_mod:get_module_proc(Host, ?MODULE),
        ChildSpec = {Proc,
                     {?MODULE, start_link, [Host, Opts]},
                     permanent,
                     1000,
                     worker,
                     [?MODULE]},
        supervisor:start_child(ejabberd_sup, ChildSpec).
    
    %% this is called by ejabberd when module is unloaded, so it
    %% does the opposite of start/2 :)
    stop(Host) ->
        Proc = gen_mod:get_module_proc(Host, ?MODULE),
        supervisor:terminate_child(ejabberd_sup, Proc),
        supervisor:delete_child(ejabberd_sup, Proc).
    
    %% it will be called by supervisor when it is time to start
    %% this gen_server under control of supervisor
    start_link(_Host, _Opts) ->
        gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
    
    %% it is an initialization function for gen_server
    %% it starts a timer, which sends 'tick' message periodically to itself
    init(_) ->
        timer:send_interval(?INTERVAL, self(), tick),
        {ok, #state{}}.
    
    handle_call(_Request, _From, State) ->
        Reply = ok,
        {reply, Reply, State}.
    
    handle_cast(_Msg, State) ->
        {noreply, State}.
    
    %% this function is called whenever gen_server receives a 'tick' message
    handle_info(tick, State) ->
        State2 = do_cleanup(State),
        {noreply, State2};
    
    handle_info(_Info, State) ->
        {noreply, State}.
    
    terminate(_Reason, _State) ->
        ok.
    
    code_change(_OldVsn, State, _Extra) ->
        {ok, State}.
    
    
    %% this function is called by handle_info/2 when tick message is received
    %% so put all cleanup code here
    do_cleanup(State) ->
        %% do all cleanup work here
        State.
    

    This blog post gives a good explanation how gen_servers work. Of course make sure to re-read OTP design principles on gen_server and on supervisor.

    Ejabberd's module developement is described here