25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-02 16:37:52 +01:00

Merge from trunk r1600:

* src/ejabberd_rdbms.erl: fix SQL database reconnection
issues (EJAB-764) and add odbc_start_interval configuration
directive (default to 30 seconds).
* src/odbc/ejabberd_odbc.erl: likewise.
* src/odbc/ejabberd_odbc_sup.erl: likewise.
* doc/guide.tex: likewise.

SVN Revision: 1601
This commit is contained in:
Jérôme Sautret 2008-10-06 14:27:48 +00:00
parent 7224ec8cb4
commit a4ee23edfe
5 changed files with 406 additions and 187 deletions

View File

@ -1,3 +1,12 @@
2008-10-06 Jerome Sautret <jerome.sautret@process-one.net>
* src/ejabberd_rdbms.erl: fix SQL database reconnection
issues (EJAB-764) and add odbc_start_interval configuration
directive (default to 30 seconds).
* src/odbc/ejabberd_odbc.erl: likewise.
* src/odbc/ejabberd_odbc_sup.erl: likewise.
* doc/guide.tex: likewise.
2008-10-03 Jerome Sautret <jerome.sautret@process-one.net> 2008-10-03 Jerome Sautret <jerome.sautret@process-one.net>
* src/odbc/odbc_queries.erl: Fix syntax error on update_roster_sql query. * src/odbc/odbc_queries.erl: Fix syntax error on update_roster_sql query.

View File

@ -1388,6 +1388,152 @@ Examples:
\end{verbatim} \end{verbatim}
\end{itemize} \end{itemize}
<<<<<<< .courant
=======
Appendix \ref{i18ni10n} provides more details about internationalization and localization.
\makesubsection{includeconfigfile}{Include Additional Configuration Files}
\ind{options!includeconfigfile}\ind{includeconfigfile}
The option \option{include\_config\_file} in a configuration file instructs \ejabberd{} to include other configuration files immediately.
The basic usage is:
\begin{verbatim}
{include_config_file, <filename>}.
\end{verbatim}
It is also possible to specify suboptions:
\begin{verbatim}
{include_config_file, <filename>, [<suboption>, <suboption>, ...]}.
\end{verbatim}
The filename can be indicated either as an absolute path,
or relative to the main \ejabberd{} configuration file.
It isn't possible to use wildcards.
The file must exist and be readable.
The allowed suboptions are:
\begin{description}
\titem{\{disallow, [<option>, <option>, ...]\}} Disallows the usage of those options in the included configuration file.
The options that match this criteria are not accepted.
The default value is an empty list: \term{[]}
\titem{\{allow\_only, [<option>, <option>, ...]\}} Allows only the usage of those options in the included configuration file.
The options that do not match this criteria are not accepted.
The default value is: \term{all}
\end{description}
This is a basic example:
\begin{verbatim}
{include_config_file, "/etc/ejabberd/additional.cfg"}.
\end{verbatim}
In this example, the included file is not allowed to contain a \term{listen} option.
If such an option is present, the option will not be accepted.
The file is in a subdirectory from where the main configuration file is.
\begin{verbatim}
{include_config_file, "./example.org/additional_not_listen.cfg", [{disallow, [listen]}]}.
\end{verbatim}
In this example, \term{ejabberd.cfg} defines some ACL and Access rules,
and later includes another file with additional rules:
\begin{verbatim}
{acl, admin, {user, "admin", "localhost"}}.
{access, announce, [{allow, admin}]}.
{include_config_file, "/etc/ejabberd/acl_and_access.cfg", [{allow_only, [acl, access]}]}.
\end{verbatim}
and content of the file \term{acl\_and\_access.cfg} can be, for example:
\begin{verbatim}
{acl, admin, {user, "bob", "localhost"}}.
{acl, admin, {user, "jan", "localhost"}}.
\end{verbatim}
\makesubsection{optionmacros}{Option Macros in Configuration File}
\ind{options!optionmacros}\ind{optionmacros}
In the \ejabberd{} configuration file,
it is possible to define a macro for a value
and later use this macro when defining an option.
A macro is defined with this syntax:
\begin{verbatim}
{define_macro, '<MACRO>', <value>}.
\end{verbatim}
The \term{MACRO} must be surrounded by single quotation marks,
and all letters in uppercase; check the examples bellow.
The \term{value} can be any valid arbitrary Erlang term.
The first definition of a macro is preserved,
and additional definitions of the same macro are forgotten.
Macros are processed after
additional configuration files have been included,
so it is possible to use macros that
are defined in configuration files included before the usage.
It isn't possible to use a macro in the definition
of another macro.
There are two ways to use a macro:
\begin{description}
\titem{'<MACRO>'}
You can put this instead of a value in an \ejabberd{} option,
and will be replaced with the \term{value} previously defined.
If the macro is not defined previously,
the program will crash and report an error.
\titem{\{use\_macro, '<MACRO>', <defaultvalue>\}}
Use a macro even if it may not be defined.
If the macro is not defined previously,
the provided \term{defaultvalue} is used.
This usage behaves as if it were defined and used this way:
\begin{verbatim}
{define_macro, '<MACRO>', <defaultvalue>}.
'<MACRO>'
\end{verbatim}
\end{description}
This example shows the basic usage of a macro:
\begin{verbatim}
{define_macro, 'LOG_LEVEL_NUMBER', 5}.
{loglevel, 'LOG_LEVEL_NUMBER'}.
\end{verbatim}
The resulting option interpreted by \ejabberd{} is: \term{\{loglevel, 5\}}.
This example shows that values can be any arbitrary Erlang term:
\begin{verbatim}
{define_macro, 'USERBOB', {user, "bob", "localhost"}}.
{acl, admin, 'USERBOB'}.
\end{verbatim}
The resulting option interpreted by \ejabberd{} is: \term{\{acl, admin, \{user, "bob", "localhost"\}\}}.
This complex example:
\begin{verbatim}
{define_macro, 'NUMBER_PORT_C2S', 5222}.
{define_macro, 'PORT_S2S_IN', {5269, ejabberd_s2s_in, []}}.
{listen,
[
{'NUMBER_PORT_C2S', ejabberd_c2s, []},
'PORT_S2S_IN',
{{use_macro, 'NUMBER_PORT_HTTP', 5280}, ejabberd_http, []}
]
}.
\end{verbatim}
produces this result after being interpreted:
\begin{verbatim}
{listen,
[
{5222, ejabberd_c2s, []},
{5269, ejabberd_s2s_in, []},
{5280, ejabberd_http, []}
]
}.
\end{verbatim}
>>>>>>> .fusion-droit.r1600
\makesection{database}{Database and LDAP Configuration} \makesection{database}{Database and LDAP Configuration}
\ind{database} \ind{database}
%TODO: this whole section is not yet 100% optimized %TODO: this whole section is not yet 100% optimized
@ -1444,6 +1590,13 @@ Specify in seconds: for example 28800 means 8 hours.
{odbc_keepalive_interval, undefined}. {odbc_keepalive_interval, undefined}.
\end{verbatim} \end{verbatim}
If the connection to the database fails, \ejabberd{} waits 30 seconds before retrying.
You can modify this interval with this option:
\begin{verbatim}
{odbc_start_interval, 30}.
\end{verbatim}
\makesubsubsection{compilemysql}{Driver Compilation} \makesubsubsection{compilemysql}{Driver Compilation}
\ind{MySQL!Driver Compilation} \ind{MySQL!Driver Compilation}
@ -2672,6 +2825,14 @@ Options:
This option sets the full path to the directory in which the HTML files should This option sets the full path to the directory in which the HTML files should
be stored. Make sure the \ejabberd{} daemon user has write access on that be stored. Make sure the \ejabberd{} daemon user has write access on that
directory. The default value is \term{"www/muc"}. directory. The default value is \term{"www/muc"}.
<<<<<<< .courant
=======
\titem{spam\_prevention}\ind{options!spam\_prevention}
To prevent spam, the \term{spam\_prevention} option adds a special attribute
to links that prevent their indexation by search engines. The default value
is \term{true}, which mean that nofollow attributes will be added to user
submitted links.
>>>>>>> .fusion-droit.r1600
\titem{timezone}\ind{options!timezone} \titem{timezone}\ind{options!timezone}
The time zone for the logs is configurable with this option. Allowed values The time zone for the logs is configurable with this option. Allowed values
are \term{local} and \term{universal}. With the first value, the local time, are \term{local} and \term{universal}. With the first value, the local time,
@ -3592,8 +3753,16 @@ The command line parameters:
\titem{-name ejabberd} \titem{-name ejabberd}
The Erlang node will be fully identified. The Erlang node will be fully identified.
This is only useful if you plan to setup an \ejabberd{} cluster with nodes in different networks. This is only useful if you plan to setup an \ejabberd{} cluster with nodes in different networks.
<<<<<<< .courant
\titem{-kernel inetrc "/etc/ejabberd/inetrc"} \titem{-kernel inetrc "/etc/ejabberd/inetrc"}
Indicates which IP name resolution to use. It is required if using \term{-sname}. Indicates which IP name resolution to use. It is required if using \term{-sname}.
=======
\titem{-kernel inetrc "/etc/ejabberd/inetrc"}
Indicates which IP name resolution to use.
If using \term{-sname}, specify either this option or \term{ERL\_INETRC}.
\titem{-kernel inet\_dist\_listen\_min 4200 inet\_dist\_listen\_min 4210}
Define the first and last ports that \term{epmd} (section \ref{epmd}) can listen to.
>>>>>>> .fusion-droit.r1600
\titem{-detached} \titem{-detached}
Starts the Erlang system detached from the system console. Starts the Erlang system detached from the system console.
Useful for running daemons and backgrounds processes. Useful for running daemons and backgrounds processes.
@ -3775,8 +3944,14 @@ Remember to block the port so Internet doesn't have access to it.
Once an Erlang node solved the node name of another Erlang node using EPMD and port 4369, Once an Erlang node solved the node name of another Erlang node using EPMD and port 4369,
the nodes communicate directly. the nodes communicate directly.
<<<<<<< .courant
The ports used in this case are random. The ports used in this case are random.
You can limit the range of ports when starting Erlang with a command-line parameter, for example: You can limit the range of ports when starting Erlang with a command-line parameter, for example:
=======
The ports used in this case by default are random,
but can be configured in the file \term{ejabberdctl.cfg}.
The Erlang command-line parameter used internally is, for example:
>>>>>>> .fusion-droit.r1600
\begin{verbatim} \begin{verbatim}
erl ... -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375 erl ... -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
\end{verbatim} \end{verbatim}
@ -4101,6 +4276,21 @@ To exit the shell, close the window or press the keys: control+c control+c.
\makechapter{i18ni10n}{Internationalization and Localization} \makechapter{i18ni10n}{Internationalization and Localization}
\ind{xml:lang}\ind{internationalization}\ind{localization}\ind{i18n}\ind{l10n} \ind{xml:lang}\ind{internationalization}\ind{localization}\ind{i18n}\ind{l10n}
<<<<<<< .courant
=======
The source code of \ejabberd{} supports localization.
The translators can edit the
\footahref{http://www.gnu.org/software/gettext/}{gettext} .po files
using any capable program (KBabel, Lokalize, Poedit...) or a simple text editor.
Then gettext
is used to extract, update and export those .po files to the .msg format read by \ejabberd{}.
To perform those management tasks, in the \term{src/} directory execute \term{make translations}.
The translatable strings are extracted from source code to generate the file \term{ejabberd.pot}.
This file is merged with each .po file to produce updated .po files.
Finally those .po files are exported to .msg files, that have a format easily readable by \ejabberd{}.
>>>>>>> .fusion-droit.r1600
All built-in modules support the \texttt{xml:lang} attribute inside IQ queries. All built-in modules support the \texttt{xml:lang} attribute inside IQ queries.
Figure~\ref{fig:discorus}, for example, shows the reply to the following query: Figure~\ref{fig:discorus}, for example, shows the reply to the following query:
\begin{verbatim} \begin{verbatim}

View File

@ -52,14 +52,21 @@ start_hosts() ->
%% Start the ODBC module on the given host %% Start the ODBC module on the given host
start_odbc(Host) -> start_odbc(Host) ->
Supervisor_name = gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
ChildSpec = ChildSpec =
{gen_mod:get_module_proc(Host, ejabberd_odbc_sup), {Supervisor_name,
{ejabberd_odbc_sup, start_link, [Host]}, {ejabberd_odbc_sup, start_link, [Host]},
temporary, transient,
infinity, infinity,
supervisor, supervisor,
[ejabberd_odbc_sup]}, [ejabberd_odbc_sup]},
supervisor:start_child(ejabberd_sup, ChildSpec). case supervisor:start_child(ejabberd_sup, ChildSpec) of
{ok, _PID} ->
ok;
_Error ->
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying...~n", [Supervisor_name, _Error]),
start_odbc(Host)
end.
%% Returns true if we have configured odbc_server for the given host %% Returns true if we have configured odbc_server for the given host
needs_odbc(Host) -> needs_odbc(Host) ->

View File

@ -30,7 +30,7 @@
-behaviour(gen_server). -behaviour(gen_server).
%% External exports %% External exports
-export([start/1, start_link/1, -export([start/1, start_link/2,
sql_query/2, sql_query/2,
sql_query_t/1, sql_query_t/1,
sql_transaction/2, sql_transaction/2,
@ -63,8 +63,8 @@
start(Host) -> start(Host) ->
gen_server:start(ejabberd_odbc, [Host], []). gen_server:start(ejabberd_odbc, [Host], []).
start_link(Host) -> start_link(Host, StartInterval) ->
gen_server:start_link(ejabberd_odbc, [Host], []). gen_server:start_link(ejabberd_odbc, [Host, StartInterval], []).
sql_query(Host, Query) -> sql_query(Host, Query) ->
gen_server:call(ejabberd_odbc_sup:get_random_pid(Host), gen_server:call(ejabberd_odbc_sup:get_random_pid(Host),
@ -131,10 +131,10 @@ escape_like(C) -> odbc_queries:escape(C).
%% ignore | %% ignore |
%% {stop, Reason} %% {stop, Reason}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
init([Host]) -> init([Host, StartInterval]) ->
case ejabberd_config:get_local_option({odbc_keepalive_interval, Host}) of case ejabberd_config:get_local_option({odbc_keepalive_interval, Host}) of
Interval when is_integer(Interval) -> KeepaliveInterval when is_integer(KeepaliveInterval) ->
timer:apply_interval(Interval*1000, ?MODULE, keep_alive, [self()]); timer:apply_interval(KeepaliveInterval*1000, ?MODULE, keep_alive, [self()]);
undefined -> undefined ->
ok; ok;
_Other -> _Other ->
@ -144,16 +144,16 @@ init([Host]) ->
case SQLServer of case SQLServer of
%% Default pgsql port %% Default pgsql port
{pgsql, Server, DB, Username, Password} -> {pgsql, Server, DB, Username, Password} ->
pgsql_connect(Server, ?PGSQL_PORT, DB, Username, Password); pgsql_connect(Server, ?PGSQL_PORT, DB, Username, Password, StartInterval);
{pgsql, Server, Port, DB, Username, Password} when is_integer(Port) -> {pgsql, Server, Port, DB, Username, Password} when is_integer(Port) ->
pgsql_connect(Server, Port, DB, Username, Password); pgsql_connect(Server, Port, DB, Username, Password, StartInterval);
%% Default mysql port %% Default mysql port
{mysql, Server, DB, Username, Password} -> {mysql, Server, DB, Username, Password} ->
mysql_connect(Server, ?MYSQL_PORT, DB, Username, Password); mysql_connect(Server, ?MYSQL_PORT, DB, Username, Password, StartInterval);
{mysql, Server, Port, DB, Username, Password} when is_integer(Port) -> {mysql, Server, Port, DB, Username, Password} when is_integer(Port) ->
mysql_connect(Server, Port, DB, Username, Password); mysql_connect(Server, Port, DB, Username, Password, StartInterval);
_ when is_list(SQLServer) -> _ when is_list(SQLServer) ->
odbc_connect(SQLServer) odbc_connect(SQLServer, StartInterval)
end. end.
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
@ -245,7 +245,7 @@ execute_transaction(State, F, NRestarts) ->
%% part of init/1 %% part of init/1
%% Open an ODBC database connection %% Open an ODBC database connection
odbc_connect(SQLServer) -> odbc_connect(SQLServer, StartInterval) ->
application:start(odbc), application:start(odbc),
case odbc:connect(SQLServer,[{scrollable_cursors, off}]) of case odbc:connect(SQLServer,[{scrollable_cursors, off}]) of
{ok, Ref} -> {ok, Ref} ->
@ -254,8 +254,8 @@ odbc_connect(SQLServer) ->
{error, Reason} -> {error, Reason} ->
?ERROR_MSG("ODBC connection (~s) failed: ~p~n", ?ERROR_MSG("ODBC connection (~s) failed: ~p~n",
[SQLServer, Reason]), [SQLServer, Reason]),
%% If we can't connect we wait for 30 seconds before retrying %% If we can't connect we wait before retrying
timer:sleep(30000), timer:sleep(StartInterval),
{stop, odbc_connection_failed} {stop, odbc_connection_failed}
end. end.
@ -264,15 +264,15 @@ odbc_connect(SQLServer) ->
%% part of init/1 %% part of init/1
%% Open a database connection to PostgreSQL %% Open a database connection to PostgreSQL
pgsql_connect(Server, Port, DB, Username, Password) -> pgsql_connect(Server, Port, DB, Username, Password, StartInterval) ->
case pgsql:connect(Server, DB, Username, Password, Port) of case pgsql:connect(Server, DB, Username, Password, Port) of
{ok, Ref} -> {ok, Ref} ->
erlang:monitor(process, Ref), erlang:monitor(process, Ref),
{ok, #state{db_ref = Ref, db_type = pgsql}}; {ok, #state{db_ref = Ref, db_type = pgsql}};
{error, Reason} -> {error, Reason} ->
?ERROR_MSG("PostgreSQL connection failed: ~p~n", [Reason]), ?ERROR_MSG("PostgreSQL connection failed: ~p~n", [Reason]),
%% If we can't connect we wait for 30 seconds before retrying %% If we can't connect we wait before retrying
timer:sleep(30000), timer:sleep(StartInterval),
{stop, pgsql_connection_failed} {stop, pgsql_connection_failed}
end. end.
@ -303,7 +303,7 @@ pgsql_item_to_odbc(_) ->
%% part of init/1 %% part of init/1
%% Open a database connection to MySQL %% Open a database connection to MySQL
mysql_connect(Server, Port, DB, Username, Password) -> mysql_connect(Server, Port, DB, Username, Password, StartInterval) ->
NoLogFun = fun(_Level,_Format,_Argument) -> ok end, NoLogFun = fun(_Level,_Format,_Argument) -> ok end,
case mysql_conn:start(Server, Port, Username, Password, DB, NoLogFun) of case mysql_conn:start(Server, Port, Username, Password, DB, NoLogFun) of
{ok, Ref} -> {ok, Ref} ->
@ -311,9 +311,10 @@ mysql_connect(Server, Port, DB, Username, Password) ->
mysql_conn:fetch(Ref, ["set names 'utf8';"], self()), mysql_conn:fetch(Ref, ["set names 'utf8';"], self()),
{ok, #state{db_ref = Ref, db_type = mysql}}; {ok, #state{db_ref = Ref, db_type = mysql}};
{error, Reason} -> {error, Reason} ->
?ERROR_MSG("MySQL connection failed: ~p~n", [Reason]), ?ERROR_MSG("MySQL connection failed: ~p~nWaiting ~p seconds before retrying...~n",
%% If we can't connect we wait for 30 seconds before retrying [Reason, StartInterval div 1000]),
timer:sleep(30000), %% If we can't connect we wait before retrying
timer:sleep(StartInterval),
{stop, mysql_connection_failed} {stop, mysql_connection_failed}
end. end.

View File

@ -37,32 +37,44 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-define(DEFAULT_POOL_SIZE, 10). -define(DEFAULT_POOL_SIZE, 10).
-define(DEFAULT_ODBC_START_INTERVAL, 30). % 30 seconds
start_link(Host) -> start_link(Host) ->
supervisor:start_link({local, gen_mod:get_module_proc(Host, ?MODULE)}, supervisor:start_link({local, gen_mod:get_module_proc(Host, ?MODULE)},
?MODULE, [Host]). ?MODULE, [Host]).
init([Host]) -> init([Host]) ->
N = case ejabberd_config:get_local_option({odbc_pool_size, Host}) of PoolSize = case ejabberd_config:get_local_option({odbc_pool_size, Host}) of
I when is_integer(I) -> I when is_integer(I) ->
I; I;
undefined -> undefined ->
?DEFAULT_POOL_SIZE; ?DEFAULT_POOL_SIZE;
Other -> Other ->
?ERROR_MSG("Wrong odbc_pool_size definition '~p' for host ~p, default to ~p~n", ?ERROR_MSG("Wrong odbc_pool_size definition '~p' for host ~p, default to ~p~n",
[Other, Host, ?DEFAULT_POOL_SIZE]), [Other, Host, ?DEFAULT_POOL_SIZE]),
?DEFAULT_POOL_SIZE ?DEFAULT_POOL_SIZE
end, end,
{ok, {{one_for_one, 10, 6}, StartInterval = case ejabberd_config:get_local_option({odbc_start_interval, Host}) of
Interval when is_integer(Interval) ->
Interval;
undefined ->
?DEFAULT_ODBC_START_INTERVAL;
_Other2 ->
?ERROR_MSG("Wrong odbc_start_interval definition '~p' for host ~p"
", defaulting to ~p~n",
[_Other2, Host, ?DEFAULT_ODBC_START_INTERVAL]),
?DEFAULT_ODBC_START_INTERVAL
end,
{ok, {{one_for_one, PoolSize+1, StartInterval},
lists:map( lists:map(
fun(I) -> fun(I) ->
{I, {I,
{ejabberd_odbc, start_link, [Host]}, {ejabberd_odbc, start_link, [Host, StartInterval*1000]},
transient, transient,
brutal_kill, brutal_kill,
worker, worker,
[?MODULE]} [?MODULE]}
end, lists:seq(1, N))}}. end, lists:seq(1, PoolSize))}}.
get_pids(Host) -> get_pids(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE), Proc = gen_mod:get_module_proc(Host, ?MODULE),