summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/couchdb/couch_config.erl186
1 files changed, 105 insertions, 81 deletions
diff --git a/src/couchdb/couch_config.erl b/src/couchdb/couch_config.erl
index dcdbc9eb..9a2e739a 100644
--- a/src/couchdb/couch_config.erl
+++ b/src/couchdb/couch_config.erl
@@ -10,42 +10,47 @@
% License for the specific language governing permissions and limitations under
% the License.
-%% @doc Reads CouchDB's ini file and gets queried for configuration parameters.
-%% This module is initialized with a list of ini files that it
-%% consecutively reads Key/Value pairs from and saves them in an ets
-%% table. If more an one ini file is specified, the last one is used to
-%% write changes that are made with store/2 back to that ini file.
+% Reads CouchDB's ini file and gets queried for configuration parameters.
+% This module is initialized with a list of ini files that it consecutively
+% reads Key/Value pairs from and saves them in an ets table. If more an one
+% ini file is specified, the last one is used to write changes that are made
+% with store/2 back to that ini file.
-module(couch_config).
+-behaviour(gen_server).
+
-include("couch_db.hrl").
--behaviour(gen_server).
--export([start_link/1, init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
--export([all/0, get/1, get/2, get/3, delete/2, set/3, set/4, register/1,
- register/2, load_ini_file/1]).
--record(config,
- {notify_funs=[],
- write_filename=""
- }).
+-export([start_link/1, stop/0]).
+-export([all/0, get/1, get/2, get/3, set/3, set/4, delete/2, delete/3]).
+-export([register/1, register/2]).
+-export([parse_ini_file/1]).
-%% Public API %%
+-export([init/1, terminate/2, code_change/3]).
+-export([handle_call/3, handle_cast/2, handle_info/2]).
+
+-record(config, {
+ notify_funs=[],
+ write_filename=undefined
+}).
-%% @type etstable() = integer().
start_link(IniFiles) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, IniFiles, []).
+stop() ->
+ gen_server:cast(?MODULE, stop).
+
+
all() ->
- lists:sort(ets:tab2list(?MODULE)).
+ lists:sort(gen_server:call(?MODULE, all)).
+
get(Section) when is_binary(Section) ->
?MODULE:get(?b2l(Section));
get(Section) ->
- Matches = ets:match(?MODULE, {{Section, '$1'}, '$2'}),
- [{Key, Value} || [Key, Value] <- Matches].
-
+ gen_server:call(?MODULE, {get, Section}).
get(Section, Key) ->
?MODULE:get(Section, Key, undefined).
@@ -53,21 +58,28 @@ get(Section, Key) ->
get(Section, Key, Default) when is_binary(Section) and is_binary(Key) ->
?MODULE:get(?b2l(Section), ?b2l(Key), Default);
get(Section, Key, Default) ->
- case ets:lookup(?MODULE, {Section, Key}) of
- [] -> Default;
- [{_,Result}] -> Result
- end.
+ gen_server:call(?MODULE, {get, Section, Key, Default}).
+
set(Section, Key, Value) ->
- set(Section, Key, Value, true).
+ ?MODULE:set(Section, Key, Value, true).
set(Section, Key, Value, Persist) when is_binary(Section) and is_binary(Key) ->
- set(?b2l(Section), ?b2l(Key), Value, Persist);
+ ?MODULE:set(?b2l(Section), ?b2l(Key), Value, Persist);
set(Section, Key, Value, Persist) ->
- gen_server:call(?MODULE, {set, [{{Section, Key}, Value}], Persist}).
+ gen_server:call(?MODULE, {set, Section, Key, Value, Persist}).
+
+delete(Section, Key) when is_binary(Section) and is_binary(Key) ->
+ delete(?b2l(Section), ?b2l(Key));
delete(Section, Key) ->
- set(Section, Key, "").
+ delete(Section, Key, true).
+
+delete(Section, Key, Persist) when is_binary(Section) and is_binary(Key) ->
+ delete(?b2l(Section), ?b2l(Key), Persist);
+delete(Section, Key, Persist) ->
+ ?MODULE:set(Section, Key, "", Persist).
+
register(Fun) ->
?MODULE:register(Fun, self()).
@@ -75,54 +87,78 @@ register(Fun) ->
register(Fun, Pid) ->
gen_server:call(?MODULE, {register, Fun, Pid}).
-%% Private API %%
-%% @spec init(List::list([])) -> {ok, Tab::etsatable()}
-%% @doc Creates a new ets table of the type "set".
init(IniFiles) ->
- ets:new(?MODULE, [named_table, set, protected]),
+ ets:new(?MODULE, [named_table, set, private]),
lists:map(fun(IniFile) ->
{ok, ParsedIniValues} = parse_ini_file(IniFile),
ets:insert(?MODULE, ParsedIniValues)
end, IniFiles),
- {ok, #config{write_filename=lists:last(IniFiles)}}.
-
-handle_call({set, KVs, Persist}, _From, Config) ->
- lists:map(
- fun({{Section, Key}, Value}=KV) ->
- true = ets:insert(?MODULE, KV),
- if Persist ->
- ok = couch_config_writer:save_to_file(KV,
- Config#config.write_filename);
- true -> ok
- end,
- [catch F(Section, Key, Value)
- || {_Pid, F} <- Config#config.notify_funs]
- end, KVs),
- {reply, ok, Config};
+ WriteFile = case length(IniFiles) > 0 of
+ true -> lists:last(IniFiles);
+ _ -> undefined
+ end,
+ {ok, #config{write_filename=WriteFile}}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+
+handle_call(all, _From, Config) ->
+ Resp = lists:sort((ets:tab2list(?MODULE))),
+ {reply, Resp, Config};
+handle_call({get, Section}, _From, Config) ->
+ Matches = ets:match(?MODULE, {{Section, '$1'}, '$2'}),
+ Resp = [{Key, Value} || [Key, Value] <- Matches],
+ {reply, Resp, Config};
+handle_call({get, Section, Key, Default}, _From, Config) ->
+ Resp = case ets:lookup(?MODULE, {Section, Key}) of
+ [] -> Default;
+ [{_, Match}] -> Match
+ end,
+ {reply, Resp, Config};
+handle_call({set, Sec, Key, Val, Persist}, _From, Config) ->
+ true = ets:insert(?MODULE, {{Sec, Key}, Val}),
+ case {Persist, Config#config.write_filename} of
+ {true, undefined} ->
+ ok;
+ {true, FileName} ->
+ couch_config_writer:save_to_file({{Sec, Key}, Val}, FileName);
+ _ ->
+ ok
+ end,
+ [catch F(Sec, Key, Val) || {_Pid, F} <- Config#config.notify_funs],
+ {reply, ok, Config};
handle_call({register, Fun, Pid}, _From, #config{notify_funs=PidFuns}=Config) ->
erlang:monitor(process, Pid),
% convert 1 and 2 arity to 3 arity
- Fun2 =
- if is_function(Fun, 1) ->
- fun(Section, _Key, _Value) -> Fun(Section) end;
- is_function(Fun, 2) ->
- fun(Section, Key, _Value) -> Fun(Section, Key) end;
- is_function(Fun, 3) ->
- Fun
+ Fun2 =
+ case Fun of
+ _ when is_function(Fun, 1) ->
+ fun(Section, _Key, _Value) -> Fun(Section) end;
+ _ when is_function(Fun, 2) ->
+ fun(Section, Key, _Value) -> Fun(Section, Key) end;
+ _ when is_function(Fun, 3) ->
+ Fun
end,
- {reply, ok, Config#config{notify_funs=[{Pid, Fun2}|PidFuns]}}.
+ {reply, ok, Config#config{notify_funs=[{Pid, Fun2} | PidFuns]}}.
+
+
+handle_cast(stop, State) ->
+ {stop, normal, State};
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info({'DOWN', _, _, DownPid, _}, #config{notify_funs=PidFuns}=Config) ->
+ % remove any funs registered by the downed process
+ FilteredPidFuns = [{Pid,Fun} || {Pid,Fun} <- PidFuns, Pid /= DownPid],
+ {noreply, Config#config{notify_funs=FilteredPidFuns}}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
-%% @spec load_ini_file(IniFile::filename()) -> ok
-%% @doc Parses an ini file and stores Key/Value Pairs into the ets table.
-load_ini_file(IniFile) ->
- {ok, KVs} = parse_ini_file(IniFile),
- gen_server:call(?MODULE, {set, KVs, false}).
-%% @spec load_ini_file(IniFile::filename()) -> {ok, [KV]}
-%% KV = {{Section::string(), Key::string()}, Value::String()}
-%% @throws {startup_error, Msg::string()}
parse_ini_file(IniFile) ->
IniFilename = couch_util:abs_pathname(IniFile),
IniBin =
@@ -130,7 +166,8 @@ parse_ini_file(IniFile) ->
{ok, IniBin0} ->
IniBin0;
{error, enoent} ->
- Msg = ?l2b(io_lib:format("Couldn't find server configuration file ~s.", [IniFilename])),
+ Fmt = "Couldn't find server configuration file ~s.",
+ Msg = ?l2b(io_lib:format(Fmt, [IniFilename])),
?LOG_ERROR("~s~n", [Msg]),
throw({startup_error, Msg})
end,
@@ -156,25 +193,12 @@ parse_ini_file(IniFile) ->
{AccSectionName, AccValues};
{ok, [ValueName|LineValues]} -> % yeehaw, got a line!
RemainingLine = couch_util:implode(LineValues, "="),
- {ok, [LineValue | _Rest]} = regexp:split(RemainingLine, " ;|\t;"), % removes comments
- {AccSectionName, [{{AccSectionName, ValueName}, LineValue} | AccValues]}
+ {ok, [LineValue | _Rest]} =
+ regexp:split(RemainingLine, " ;|\t;"), % removes comments
+ {AccSectionName,
+ [{{AccSectionName, ValueName}, LineValue} | AccValues]}
end
end
end, {"", []}, Lines),
{ok, ParsedIniValues}.
-% Unused gen_server behaviour API functions that we need to declare.
-
-%% @doc Unused
-handle_cast(foo, State) -> {noreply, State}.
-
-handle_info({'DOWN', _, _, DownPid, _}, #config{notify_funs=PidFuns}=Config) ->
- % remove any funs registered by the downed process
- FilteredPidFuns = [{Pid,Fun} || {Pid,Fun} <- PidFuns, Pid /= DownPid],
- {noreply, Config#config{notify_funs=FilteredPidFuns}}.
-
-%% @doc Unused
-terminate(_Reason, _State) -> ok.
-
-%% @doc Unused
-code_change(_OldVersion, State, _Extra) -> {ok, State}.