1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
%%%----------------------------------------------------------------------
%%% File : ejabberd_rdbms.erl
%%% Author : Mickael Remond <mickael.remond@process-one.net>
%%% Purpose : Manage the start of the database modules when needed
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(ejabberd_rdbms).
-author('alexey@process-one.net').
-export([start/0
,start_odbc/1
,stop_odbc/1
,running/1
]).
-include("ejabberd.hrl").
-include("ejabberd_config.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-define(SUPERVISOR, ejabberd_sup).
start() ->
%% Check if ejabberd has been compiled with ODBC
case catch ejabberd_odbc_sup:module_info() of
{'EXIT',{undef,_}} ->
?INFO_MSG("ejabberd has not been compiled with relational database support. Skipping database startup.", []);
_ ->
%% If compiled with ODBC, start ODBC on the needed host
start_hosts()
end.
%% Start relationnal DB module on the nodes where it is needed
start_hosts() ->
lists:foreach(
fun(Host) ->
case needs_odbc(Host) of
true -> start_odbc(Host);
false -> ok
end
end, ?MYHOSTS).
%% Start the ODBC module on the given host
start_odbc(Host) ->
SupervisorName = sup_name(Host),
ChildSpec =
{SupervisorName,
{ejabberd_odbc_sup, start_link, [Host]},
transient,
infinity,
supervisor,
[ejabberd_odbc_sup]},
case supervisor:start_child(?SUPERVISOR, ChildSpec) of
{ok, _PID} ->
ok;
_Error ->
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying...~n", [SupervisorName, _Error]),
start_odbc(Host)
end.
stop_odbc(Host) ->
SupervisorName = sup_name(Host),
case running(Host) of
false -> ok;
true ->
case [H || H <- dependent_hosts(Host), ejabberd_hosts:running(H)] of
[] ->
?INFO_MSG("About to terminate ~p", [SupervisorName]),
ok = supervisor:terminate_child(?SUPERVISOR, SupervisorName),
ok = supervisor:delete_child(?SUPERVISOR, SupervisorName);
RunningHosts ->
?WARNING_MSG("Not stopping ODBC for ~p because the virtual hosts ~p are still using it.",
[Host, RunningHosts]),
{error, still_in_use}
end
end.
%% Returns true if we have configured odbc_server for the given host
needs_odbc(Host) ->
try
LHost = exmpp_stringprep:nameprep(Host),
case ejabberd_config:get_local_option({odbc_server, LHost}) of
undefined ->
false;
{host, _} ->
false;
_ ->
true
end
catch
_ ->
false
end.
running(Host) ->
Supervisors = supervisor:which_children(?SUPERVISOR),
SupervisorName = gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
case lists:keysearch(SupervisorName, 1, Supervisors) of
false -> false;
{value, Cspec} when is_tuple(Cspec) -> true
end.
dependent_hosts(Host) ->
MS = ets:fun2ms(fun (#local_config{key={odbc_server, DHost},
value={host, H}})
when H =:= Host ->
DHost
end),
ejabberd_config:search(MS).
sup_name(Host) ->
gen_mod:get_module_proc(Host, ejabberd_odbc_sup).
|