Merge remote-tracking branch 'upstream/master' into 1843-feature-bigcouch

Conflicts:
	src/config.erl
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index c57e7cf..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-ebin
-deps
-.eunit
-*~
-*.swp
diff --git a/rebar b/rebar
deleted file mode 100755
index ceabf62..0000000
--- a/rebar
+++ /dev/null
Binary files differ
diff --git a/rebar.config b/rebar.config
deleted file mode 100644
index d65fc31..0000000
--- a/rebar.config
+++ /dev/null
@@ -1,17 +0,0 @@
-% Copyright 2011 Cloudant
-%
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-{deps, [
-    {twig, ".*", {git, "https://github.com/cloudant/twig.git", master}}
-]}.
diff --git a/src/config.app.src b/src/config.app.src
index 2841202..7f8eef6 100644
--- a/src/config.app.src
+++ b/src/config.app.src
@@ -1,3 +1,15 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
 {application, config, [
     {description, "INI file configuration system for Apache CouchDB"},
     {vsn, git},
diff --git a/src/config.erl b/src/config.erl
index 1a728b0..d123b12 100644
--- a/src/config.erl
+++ b/src/config.erl
@@ -20,7 +20,7 @@
 -behaviour(gen_server).
 -vsn(1).
 
--export([start_link/1, stop/0]).
+-export([start_link/1, stop/0, reload/0]).
 
 -export([all/0]).
 -export([get/1, get/2, get/3]).
@@ -39,6 +39,7 @@
 
 -record(config, {
     notify_funs=[],
+    ini_files=undefined,
     write_filename=undefined
 }).
 
@@ -50,6 +51,9 @@
     gen_server:cast(?MODULE, stop).
 
 
+reload() ->
+    gen_server:call(?MODULE, reload).
+
 all() ->
     lists:sort(gen_server:call(?MODULE, all, infinity)).
 
@@ -183,7 +187,8 @@
         [_|_] -> lists:last(IniFiles);
         _ -> undefined
     end,
-    {ok, #config{write_filename=WriteFile}}.
+    debug_config(),
+    {ok, #config{ini_files=IniFiles, write_filename=WriteFile}}.
 
 
 terminate(_Reason, _State) ->
@@ -195,11 +200,8 @@
     {reply, Resp, Config};
 handle_call({set, Sec, Key, Val, Persist, Reason}, _From, Config) ->
     true = ets:insert(?MODULE, {{Sec, Key}, Val}),
-    twig:log(
-        notice,
-        "~p: [~s] ~s set to ~s for reason ~p",
-        [?MODULE, Sec, Key, Val, Reason]
-    ),
+    couch_log:notice("~p: [~s] ~s set to ~s for reason ~p",
+        [?MODULE, Sec, Key, Val, Reason]),
     case {Persist, Config#config.write_filename} of
         {true, undefined} ->
             ok;
@@ -213,11 +215,8 @@
     {reply, ok, Config};
 handle_call({delete, Sec, Key, Persist, Reason}, _From, Config) ->
     true = ets:delete(?MODULE, {Sec,Key}),
-    twig:log(
-        notice,
-        "~p: [~s] ~s deleted for reason ~p",
-        [?MODULE, Sec, Key, Reason]
-    ),
+    couch_log:notice("~p: [~s] ~s deleted for reason ~p",
+        [?MODULE, Sec, Key, Reason]),
     case {Persist, Config#config.write_filename} of
         {true, undefined} ->
             ok;
@@ -228,6 +227,29 @@
     end,
     Event = {config_change, Sec, Key, deleted, Persist},
     gen_event:sync_notify(config_event, Event),
+    {reply, ok, Config};
+handle_call(reload, _From, Config) ->
+    DiskKVs = lists:foldl(fun(IniFile, DiskKVs0) ->
+        {ok, ParsedIniValues} = parse_ini_file(IniFile),
+        lists:foldl(fun({K, V}, DiskKVs1) ->
+            dict:store(K, V, DiskKVs1)
+        end, DiskKVs0, ParsedIniValues)
+    end, dict:new(), Config#config.ini_files),
+    % Update ets with anything we just read
+    % from disk
+    dict:fold(fun(K, V, _) ->
+        ets:insert(?MODULE, {K, V})
+    end, nil, DiskKVs),
+    % And remove anything in ets that wasn't
+    % on disk.
+    ets:foldl(fun({K, _}, _) ->
+        case dict:is_key(K, DiskKVs) of
+            true ->
+                ok;
+            false ->
+                ets:delete(?MODULE, K)
+        end
+    end, nil, ?MODULE),
     {reply, ok, Config}.
 
 
@@ -237,7 +259,7 @@
     {noreply, State}.
 
 handle_info(Info, State) ->
-    twig:log(error, "config:handle_info Info: ~p~n", [Info]),
+    couch_log:error("config:handle_info Info: ~p~n", [Info]),
     {noreply, State}.
 
 code_change(_OldVsn, State, _Extra) ->
@@ -253,7 +275,7 @@
         {error, enoent} ->
             Fmt = "Couldn't find server configuration file ~s.",
             Msg = list_to_binary(io_lib:format(Fmt, [IniFilename])),
-            twig:log(error, "~s~n", [Msg]),
+            couch_log:error("~s~n", [Msg]),
             throw({startup_error, Msg})
     end,
 
@@ -312,3 +334,16 @@
             end
         end, {"", []}, Lines),
     {ok, ParsedIniValues}.
+
+
+debug_config() ->
+    case ?MODULE:get("log", "level") of
+        "debug" ->
+            io:format("Configuration Settings:~n", []),
+            lists:foreach(fun({{Mod, Key}, Val}) ->
+                io:format("  [~s] ~s=~p~n", [Mod, Key, Val])
+            end, lists:sort(ets:tab2list(?MODULE)));
+        _ ->
+            ok
+    end.
+
diff --git a/src/config_app.erl b/src/config_app.erl
index 54f2433..5c5515a 100644
--- a/src/config_app.erl
+++ b/src/config_app.erl
@@ -28,14 +28,25 @@
     ok.
 
 get_ini_files() ->
+    hd([L || L <- [command_line(), env(), default()], L =/= skip]).
+
+env() ->
+    case application:get_env(config, ini_files) of
+        undefined ->
+            skip;
+        {ok, IniFiles} ->
+            IniFiles
+    end.
+
+command_line() ->
+    case init:get_argument(couch_ini) of
+        error ->
+            skip;
+        {ok, [IniFiles]} ->
+            IniFiles
+    end.
+
+default() ->
     Etc = filename:join(code:root_dir(), "etc"),
     Default = [filename:join(Etc,"default.ini"), filename:join(Etc,"local.ini")],
-    DefaultExists = lists:filter(fun filelib:is_file/1, Default),
-    case init:get_argument(couch_ini) of
-    error ->
-        DefaultExists;
-    {ok, [[]]} ->
-        DefaultExists;
-    {ok, [Values]} ->
-        Values
-    end.
+    lists:filter(fun filelib:is_file/1, Default).
diff --git a/src/config_listener.erl b/src/config_listener.erl
index 2f2c68e..79d3806 100644
--- a/src/config_listener.erl
+++ b/src/config_listener.erl
@@ -1,3 +1,15 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
 -module(config_listener).
 
 -behaviour(gen_event).