is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
- use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1]).
+ use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
+ dump/0]).
-export([start/2]).
end,
case mnesia:transaction(F) of
{atomic, _} ->
+ recompile_options(),
set_log_level();
{aborted,{no_exists,Table}} ->
MnesiaDirectory = mnesia:system_info(directory),
end.
set_log_level() ->
- Level = ejabberd_config:get_option(
+ Level = get_option(
loglevel,
fun(P) when P>=0, P=<5 -> P end,
4),
mnesia:transaction(fun() ->
mnesia:write(#local_config{key = Opt,
value = Val})
- end).
+ end),
+ recompile_options().
-spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
get_option(Opt, F, Default) when is_atom(Opt) ->
get_option({Opt, global}, F, Default);
get_option(Opt, F, Default) ->
- case Opt of
- {O, global} when is_atom(O) -> ok;
- {O, H} when is_atom(O), is_binary(H) -> ok;
- _ -> ?WARNING_MSG("Option ~p has invalid (outdated?) format. "
- "This is likely a bug", [Opt])
- end,
- case ets:lookup(local_config, Opt) of
- [#local_config{value = Val}] ->
- prepare_opt_val(Opt, Val, F, Default);
- _ ->
- case Opt of
- {Key, Host} when Host /= global ->
- get_option({Key, global}, F, Default);
- _ ->
- Default
- end
+ {Key, Host} = case Opt of
+ {O, global} when is_atom(O) -> Opt;
+ {O, H} when is_atom(O), is_binary(H) -> Opt;
+ _ ->
+ ?WARNING_MSG("Option ~p has invalid (outdated?) "
+ "format. This is likely a bug", [Opt]),
+ {undefined, global}
+ end,
+ case ejabberd_options:is_known(Key) of
+ true ->
+ try ejabberd_options:Key(Host) of
+ Val -> prepare_opt_val(Opt, Val, F, Default)
+ catch _:function_clause ->
+ Default
+ end;
+ false ->
+ Default
end.
-spec has_option(atom() | {atom(), global | binary()}) -> any().
-spec may_hide_data(any()) -> any().
may_hide_data(Data) ->
- case ejabberd_config:get_option(
+ case get_option(
hide_sensitive_log_data,
fun(false) -> false;
(true) -> true
%% NOTE: the integer value returned is in *seconds*
cache_life_time(Host) ->
get_option({cache_life_time, Host}, opt_type(cache_life_time), 3600).
+
+%%%===================================================================
+%%% Dynamic config compilation
+%%%===================================================================
+-spec recompile_options() -> ok.
+recompile_options() ->
+ Exprs = get_exprs(),
+ try compile_exprs(Exprs)
+ catch E:R ->
+ ?CRITICAL_MSG("Failed to compile ejabberd_options:~n~s",
+ [string:join(Exprs, io_lib:nl())]),
+ erlang:raise(E, R, erlang:get_stacktrace())
+ end.
+
+-spec get_exprs() -> [string()].
+get_exprs() ->
+ Opts = lists:foldl(
+ fun(#local_config{key = {Opt, Host}, value = Val}, D) ->
+ Hosts = maps:get(Opt, D, #{}),
+ maps:put(Opt, maps:put(Host, Val, Hosts), D)
+ end, #{}, ets:tab2list(local_config)),
+ Funs = maps:fold(
+ fun(Opt, Vals, Acc) ->
+ HostVals = lists:reverse(lists:keysort(1, maps:to_list(Vals))),
+ [string:join(
+ lists:map(
+ fun({global, Val}) ->
+ io_lib:format("'~s'(_) -> ~p", [Opt, Val]);
+ ({Host, Val}) ->
+ io_lib:format("'~s'(~p) -> ~p", [Opt, Host, Val])
+ end, HostVals),
+ ";" ++ io_lib:nl()) ++ "."|Acc]
+ end, [], Opts),
+ Module = "-module(ejabberd_options).",
+ Export = "-compile(export_all).",
+ Knowns = maps:fold(
+ fun(Opt, _, Acc) ->
+ io_lib:format("is_known('~s') -> true;~n", [Opt]) ++ Acc
+ end, "", Opts) ++ "is_known(_) -> false.",
+ [Module, Export, Knowns|Funs].
+
+-spec compile_exprs([string()]) -> ok.
+compile_exprs(Exprs) ->
+ Forms = lists:map(
+ fun(Expr) ->
+ {ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)),
+ {ok, Form} = erl_parse:parse_form(Tokens),
+ Form
+ end, Exprs),
+ {ok, Code} = case compile:forms(Forms, []) of
+ {ok, ejabberd_options, Bin} -> {ok, Bin};
+ {ok, ejabberd_options, Bin, _Warnings} -> {ok, Bin};
+ Error -> Error
+ end,
+ {module, _} = code:load_binary(ejabberd_options, "nofile", Code),
+ ok.
+
+%% @doc This is only for debugging purposes, likely to report a bug
+-spec dump() -> ok.
+dump() ->
+ ETSFile = filename:join("/tmp", "ejabberd_options.ets"),
+ ErlFile = filename:join("/tmp", "ejabberd_options.erl"),
+ ETSData = io_lib:format("~p~n", [ets:tab2list(local_config)]),
+ ErlData = io_lib:format("~s~n", [str:join(get_exprs(), io_lib:nl())]),
+ case file:write_file(ETSFile, ETSData) of
+ ok -> io:format("ETS data written to ~s~n", [ETSFile]);
+ {error, Reason1} ->
+ io:format("Failed to write to ~s: ~s",
+ [ETSFile, file:format_error(Reason1)])
+ end,
+ case file:write_file(ErlFile, ErlData) of
+ ok -> io:format("Dynamic module written to ~s~n", [ErlFile]);
+ {error, Reason2} ->
+ io:format("Failed to write to ~s: ~s",
+ [ErlFile, file:format_error(Reason2)])
+ end.
--- /dev/null
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @doc
+%%% This is a stub module which will be replaced during
+%%% configuration load, see ejabberd_config:recompile_options/0.
+%%% The only purpose of this file is to shut up xref/dialyzer
+%%% @end
+%%% Created : 16 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2017 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.,
+%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+%%%
+%%%-------------------------------------------------------------------
+-module(ejabberd_options).
+
+%% API
+-export([is_known/1]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+is_known(_) ->
+ false.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================