You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/02/05 00:06:39 UTC

[26/50] couch commit: updated refs/heads/import to c3116d7

Major change to use the new config app

This replaces couch_config with the config app. The config app is mostly
a direct port of couch_config to a new namespace with the addition of a
rewritten config change notification mechanism. The new change mechanism
removes the ability to register an anonymous function that breaks code
upgrades and generally cleans up the various listening patterns using a
new behavior definition.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a7d16632
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a7d16632
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a7d16632

Branch: refs/heads/import
Commit: a7d166329b65f208d69f8be252ef3890a9df5ca0
Parents: 54a7b40
Author: Robert Newson <rn...@apache.org>
Authored: Sat Mar 9 18:22:19 2013 -0600
Committer: Paul J. Davis <pa...@gmail.com>
Committed: Tue Feb 4 17:03:24 2014 -0600

----------------------------------------------------------------------
 src/couch.app.tpl.in                 |  3 +-
 src/couch.erl                        |  8 ---
 src/couch_auth_cache.erl             | 33 ++++++++----
 src/couch_changes.erl                |  2 +-
 src/couch_compaction_daemon.erl      | 32 ++++++++----
 src/couch_compress.erl               |  2 +-
 src/couch_db.erl                     | 10 ++--
 src/couch_db_update_notifier_sup.erl | 19 ++++---
 src/couch_db_updater.erl             | 12 ++---
 src/couch_drv.erl                    |  2 +-
 src/couch_ejson_compare.erl          |  2 +-
 src/couch_external_manager.erl       | 26 +++++++---
 src/couch_external_server.erl        | 22 ++++++--
 src/couch_httpd.erl                  | 85 +++++++++++--------------------
 src/couch_httpd_auth.erl             | 28 +++++-----
 src/couch_httpd_cors.erl             |  6 +--
 src/couch_httpd_db.erl               |  2 +-
 src/couch_httpd_external.erl         |  2 +-
 src/couch_httpd_misc_handlers.erl    | 18 +++----
 src/couch_httpd_oauth.erl            | 10 ++--
 src/couch_httpd_rewrite.erl          |  4 +-
 src/couch_httpd_vhost.erl            | 38 +++++++++-----
 src/couch_log.erl                    | 51 +++++++++++--------
 src/couch_os_daemons.erl             | 33 ++++++++----
 src/couch_os_process.erl             |  4 +-
 src/couch_passwords.erl              |  4 +-
 src/couch_proc_manager.erl           |  6 +--
 src/couch_secondary_sup.erl          |  2 +-
 src/couch_server.erl                 | 56 +++++++++++++-------
 src/couch_server_sup.erl             | 53 ++++++++-----------
 src/couch_stats_aggregator.erl       | 27 +++++++---
 src/couch_users_db.erl               |  2 +-
 src/couch_uuids.erl                  | 23 +++++++--
 33 files changed, 361 insertions(+), 266 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch.app.tpl.in
----------------------------------------------------------------------
diff --git a/src/couch.app.tpl.in b/src/couch.app.tpl.in
index f83b6f6..1cd0154 100644
--- a/src/couch.app.tpl.in
+++ b/src/couch.app.tpl.in
@@ -3,7 +3,6 @@
     {vsn, "@version@"},
     {modules, [@modules@]},
     {registered, [
-        couch_config,
         couch_db_update,
         couch_db_update_notifier_sup,
         couch_external_manager,
@@ -22,6 +21,6 @@
         "%localconfdir%/@defaultini@",
         "%localconfdir%/@localini@"
     ]}},
-    {applications, [kernel, stdlib, twig]},
+    {applications, [kernel, stdlib, twig, config]},
     {included_applications, [crypto, sasl, inets, oauth, ibrowse, mochiweb, os_mon]}
 ]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch.erl
----------------------------------------------------------------------
diff --git a/src/couch.erl b/src/couch.erl
index c18df0b..7d53d0c 100644
--- a/src/couch.erl
+++ b/src/couch.erl
@@ -29,11 +29,3 @@ restart() ->
     {error, Reason} ->
         {error, Reason}
     end.
-
-reload() ->
-    case supervisor:terminate_child(couch_server_sup, couch_config) of
-    ok ->
-        supervisor:restart_child(couch_server_sup, couch_config);
-    {error, Reason} ->
-        {error, Reason}
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_auth_cache.erl
----------------------------------------------------------------------
diff --git a/src/couch_auth_cache.erl b/src/couch_auth_cache.erl
index 5f4c8f9..1650495 100644
--- a/src/couch_auth_cache.erl
+++ b/src/couch_auth_cache.erl
@@ -12,6 +12,7 @@
 
 -module(couch_auth_cache).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 % public API
 -export([get_user_creds/1]).
@@ -20,6 +21,8 @@
 -export([start_link/0, init/1, handle_call/3, handle_info/2, handle_cast/2]).
 -export([code_change/3, terminate/2]).
 
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 -include("couch_js_functions.hrl").
 
@@ -42,7 +45,7 @@ get_user_creds(UserName) when is_list(UserName) ->
     get_user_creds(?l2b(UserName));
 
 get_user_creds(UserName) ->
-    UserCreds = case couch_config:get("admins", ?b2l(UserName)) of
+    UserCreds = case config:get("admins", ?b2l(UserName)) of
     "-hashed-" ++ HashedPwdAndSalt ->
         % the name is an admin, now check to see if there is a user doc
         % which has a matching name, salt, and password_sha
@@ -120,19 +123,12 @@ init(_) ->
     ?BY_USER = ets:new(?BY_USER, [set, protected, named_table]),
     ?BY_ATIME = ets:new(?BY_ATIME, [ordered_set, private, named_table]),
     process_flag(trap_exit, true),
-    ok = couch_config:register(
-        fun("couch_httpd_auth", "auth_cache_size", SizeList) ->
-            Size = list_to_integer(SizeList),
-            ok = gen_server:call(?MODULE, {new_max_cache_size, Size}, infinity);
-        ("couch_httpd_auth", "authentication_db", _DbName) ->
-            ok = gen_server:call(?MODULE, reinit_cache, infinity)
-        end
-    ),
+    ok = config:listen_for_changes(?MODULE, nil),
     {ok, Notifier} = couch_db_update_notifier:start_link(fun handle_db_event/1),
     State = #state{
         db_notifier = Notifier,
         max_cache_size = list_to_integer(
-            couch_config:get("couch_httpd_auth", "auth_cache_size", "50")
+            config:get("couch_httpd_auth", "auth_cache_size", "50")
         )
     },
     {ok, reinit_cache(State)}.
@@ -201,6 +197,12 @@ handle_cast({cache_hit, UserName}, State) ->
     {noreply, State}.
 
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info({'DOWN', Ref, _, _, _Reason}, #state{db_mon_ref = Ref} = State) ->
     {noreply, reinit_cache(State)}.
 
@@ -217,6 +219,15 @@ code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
 
+handle_config_change("couch_httpd_auth", "auth_cache_size", SizeList, _, _) ->
+    Size = list_to_integer(SizeList),
+    {ok, gen_server:call(?MODULE, {new_max_cache_size, Size}, infinity)};
+handle_config_change("couch_httpd_auth", "authentication_db", _DbName, _, _) ->
+    {ok, gen_server:call(?MODULE, reinit_cache, infinity)};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
+
 clear_cache(State) ->
     exec_if_auth_db(fun(AuthDb) -> catch couch_db:close(AuthDb) end),
     true = ets:delete_all_objects(?BY_USER),
@@ -226,7 +237,7 @@ clear_cache(State) ->
 
 reinit_cache(State) ->
     NewState = clear_cache(State),
-    AuthDbName = ?l2b(couch_config:get("couch_httpd_auth", "authentication_db")),
+    AuthDbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")),
     true = ets:insert(?STATE, {auth_db_name, AuthDbName}),
     AuthDb = open_auth_db(),
     true = ets:insert(?STATE, {auth_db, AuthDb}),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_changes.erl
----------------------------------------------------------------------
diff --git a/src/couch_changes.erl b/src/couch_changes.erl
index cbca9cb..d36b45f 100644
--- a/src/couch_changes.erl
+++ b/src/couch_changes.erl
@@ -258,7 +258,7 @@ get_changes_timeout(Args, Callback) ->
         feed = ResponseType
     } = Args,
     DefaultTimeout = list_to_integer(
-        couch_config:get("httpd", "changes_timeout", "60000")
+        config:get("httpd", "changes_timeout", "60000")
     ),
     case Heartbeat of
     undefined ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_compaction_daemon.erl
----------------------------------------------------------------------
diff --git a/src/couch_compaction_daemon.erl b/src/couch_compaction_daemon.erl
index b9bf969..3251d5f 100644
--- a/src/couch_compaction_daemon.erl
+++ b/src/couch_compaction_daemon.erl
@@ -12,14 +12,18 @@
 
 -module(couch_compaction_daemon).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 % public API
--export([start_link/0, config_change/3]).
+-export([start_link/0]).
 
 % gen_server callbacks
 -export([init/1, handle_call/3, handle_info/2, handle_cast/2]).
 -export([code_change/3, terminate/2]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 
 -define(CONFIG_ETS, couch_compaction_daemon_config).
@@ -49,17 +53,13 @@ start_link() ->
 init(_) ->
     process_flag(trap_exit, true),
     ?CONFIG_ETS = ets:new(?CONFIG_ETS, [named_table, set, protected]),
-    ok = couch_config:register(fun ?MODULE:config_change/3),
+    ok = config:listen_for_changes(?MODULE, nil),
     load_config(),
     Server = self(),
     Loop = spawn_link(fun() -> compact_loop(Server) end),
     {ok, #state{loop_pid = Loop}}.
 
 
-config_change("compactions", DbName, NewValue) ->
-    ok = gen_server:cast(?MODULE, {config_update, DbName, NewValue}).
-
-
 handle_cast({config_update, DbName, deleted}, State) ->
     true = ets:delete(?CONFIG_ETS, ?l2b(DbName)),
     {noreply, State};
@@ -85,6 +85,12 @@ handle_call(Msg, _From, State) ->
     {stop, {unexpected_call, Msg}, State}.
 
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info({'EXIT', Pid, Reason}, #state{loop_pid = Pid} = State) ->
     {stop, {compaction_loop_died, Reason}, State}.
 
@@ -97,6 +103,12 @@ code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
 
+handle_config_change("compactions", DbName, Value, _, _) ->
+    {ok, gen_server:cast(?MODULE, {config_update, DbName, Value})};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
+
 compact_loop(Parent) ->
     {ok, _} = couch_server:all_databases(
         fun(DbName, Acc) ->
@@ -123,7 +135,7 @@ compact_loop(Parent) ->
         receive {Parent, have_config} -> ok end;
     false ->
         PausePeriod = list_to_integer(
-            couch_config:get("compaction_daemon", "check_interval", "300")),
+            config:get("compaction_daemon", "check_interval", "300")),
         ok = timer:sleep(PausePeriod * 1000)
     end,
     compact_loop(Parent).
@@ -295,7 +307,7 @@ can_db_compact(#config{db_frag = Threshold} = Config, Db) ->
         false ->
             false;
         true ->
-            Free = free_space(couch_config:get("couchdb", "database_dir")),
+            Free = free_space(config:get("couchdb", "database_dir")),
             case Free >= SpaceRequired of
             true ->
                 true;
@@ -364,7 +376,7 @@ check_frag(Threshold, Frag) ->
 frag(Props) ->
     FileSize = couch_util:get_value(disk_size, Props),
     MinFileSize = list_to_integer(
-        couch_config:get("compaction_daemon", "min_file_size", "131072")),
+        config:get("compaction_daemon", "min_file_size", "131072")),
     case FileSize < MinFileSize of
     true ->
         {0, FileSize};
@@ -396,7 +408,7 @@ load_config() ->
                 ok
             end
         end,
-        couch_config:get("compactions")).
+        config:get("compactions")).
 
 parse_config(DbName, ConfigString) ->
     case (catch do_parse_config(ConfigString)) of

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_compress.erl
----------------------------------------------------------------------
diff --git a/src/couch_compress.erl b/src/couch_compress.erl
index 0b9334a..6b47a7a 100644
--- a/src/couch_compress.erl
+++ b/src/couch_compress.erl
@@ -26,7 +26,7 @@
 
 
 get_compression_method() ->
-    case couch_config:get("couchdb", "file_compression") of
+    case config:get("couchdb", "file_compression") of
     undefined ->
         ?DEFAULT_COMPRESSION;
     Method1 ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 5f91633..56cb0d2 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -893,7 +893,7 @@ make_first_doc_on_disk(Db, Id, Pos, [{_Rev, RevValue} |_]=DocPath) ->
 set_commit_option(Options) ->
     CommitSettings = {
         [true || O <- Options, O==full_commit orelse O==delay_commit],
-        couch_config:get("couchdb", "delayed_commits", "false")
+        config:get("couchdb", "delayed_commits", "false")
     },
     case CommitSettings of
     {[true], _} ->
@@ -1016,7 +1016,7 @@ flush_att(Fd, #att{data=Data}=Att) when is_binary(Data) ->
 
 flush_att(Fd, #att{data=Fun,att_len=undefined}=Att) when is_function(Fun) ->
     MaxChunkSize = list_to_integer(
-        couch_config:get("couchdb", "attachment_stream_buffer_size", "4096")),
+        config:get("couchdb", "attachment_stream_buffer_size", "4096")),
     with_stream(Fd, Att, fun(OutputStream) ->
         % Fun(MaxChunkSize, WriterFun) must call WriterFun
         % once for each chunk of the attachment,
@@ -1067,7 +1067,7 @@ compressible_att_type(MimeType) when is_binary(MimeType) ->
     compressible_att_type(?b2l(MimeType));
 compressible_att_type(MimeType) ->
     TypeExpList = re:split(
-        couch_config:get("attachments", "compressible_types", ""),
+        config:get("attachments", "compressible_types", ""),
         "\\s*,\\s*",
         [{return, list}]
     ),
@@ -1092,12 +1092,12 @@ compressible_att_type(MimeType) ->
 % pretend that no Content-MD5 exists.
 with_stream(Fd, #att{md5=InMd5,type=Type,encoding=Enc}=Att, Fun) ->
     BufferSize = list_to_integer(
-        couch_config:get("couchdb", "attachment_stream_buffer_size", "4096")),
+        config:get("couchdb", "attachment_stream_buffer_size", "4096")),
     {ok, OutputStream} = case (Enc =:= identity) andalso
         compressible_att_type(Type) of
     true ->
         CompLevel = list_to_integer(
-            couch_config:get("attachments", "compression_level", "0")
+            config:get("attachments", "compression_level", "0")
         ),
         couch_stream:open(Fd, [{buffer_size, BufferSize},
             {encoding, gzip}, {compression_level, CompLevel}]);

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_db_update_notifier_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_update_notifier_sup.erl b/src/couch_db_update_notifier_sup.erl
index e7cc16c..9eb943a 100644
--- a/src/couch_db_update_notifier_sup.erl
+++ b/src/couch_db_update_notifier_sup.erl
@@ -21,17 +21,22 @@
 -module(couch_db_update_notifier_sup).
 
 -behaviour(supervisor).
+-behaviour(config_listener).
+
+-export([start_link/0, init/1]).
+
+% config_listener api
+-export([handle_config_change/5]).
 
--export([start_link/0, init/1, config_change/3]).
 
 start_link() ->
     supervisor:start_link({local, couch_db_update_notifier_sup},
         couch_db_update_notifier_sup, []).
 
 init([]) ->
-    ok = couch_config:register(fun ?MODULE:config_change/3),
+    ok = config:listen_for_changes(?MODULE, nil),
 
-    UpdateNotifierExes = couch_config:get("update_notification"),
+    UpdateNotifierExes = config:get("update_notification"),
 
     {ok,
         {{one_for_one, 10, 3600},
@@ -46,7 +51,7 @@ init([]) ->
 
 %% @doc when update_notification configuration changes, terminate the process
 %%      for that notifier and start a new one with the updated config
-config_change("update_notification", Id, Exe) ->
+handle_config_change("update_notification", Id, Exe, _, _) ->
     ChildSpec = {
         Id,
         {couch_db_update_notifier, start_link, [Exe]},
@@ -57,5 +62,7 @@ config_change("update_notification", Id, Exe) ->
     },
     supervisor:terminate_child(couch_db_update_notifier_sup, Id),
     supervisor:delete_child(couch_db_update_notifier_sup, Id),
-    supervisor:start_child(couch_db_update_notifier_sup, ChildSpec).
-
+    supervisor:start_child(couch_db_update_notifier_sup, ChildSpec),
+    {ok, nil};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index e1dc7c3..c64911e 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -40,7 +40,7 @@ init({DbName, Filepath, Fd, Options}) ->
         Header =  #db_header{},
         ok = couch_file:write_header(Fd, Header),
         % delete any old compaction files that might be hanging around
-        RootDir = couch_config:get("couchdb", "database_dir", "."),
+        RootDir = config:get("couchdb", "database_dir", "."),
         couch_file:delete(RootDir, Filepath ++ ".compact"),
         couch_file:delete(RootDir, Filepath ++ ".compact.data"),
         couch_file:delete(RootDir, Filepath ++ ".compact.meta");
@@ -92,7 +92,7 @@ handle_call(cancel_compact, _From, #db{compactor_pid = nil} = Db) ->
 handle_call(cancel_compact, _From, #db{compactor_pid = Pid} = Db) ->
     unlink(Pid),
     exit(Pid, kill),
-    RootDir = couch_config:get("couchdb", "database_dir", "."),
+    RootDir = config:get("couchdb", "database_dir", "."),
     ok = couch_file:delete(RootDir, Db#db.filepath ++ ".compact"),
     Db2 = Db#db{compactor_pid = nil},
     ok = gen_server:call(couch_server, {db_updated, Db2}, infinity),
@@ -228,7 +228,7 @@ handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) ->
         ?LOG_DEBUG("CouchDB swapping files ~s and ~s.",
                 [Filepath, CompactFilepath]),
         ok = file:rename(CompactFilepath, Filepath ++ ".compact"),
-        RootDir = couch_config:get("couchdb", "database_dir", "."),
+        RootDir = config:get("couchdb", "database_dir", "."),
         couch_file:delete(RootDir, Filepath),
         ok = file:rename(Filepath ++ ".compact", Filepath),
         % Delete the old meta compaction file after promoting
@@ -461,7 +461,7 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
     end,
 
     {ok, FsyncOptions} = couch_util:parse_term(
-            couch_config:get("couchdb", "fsync_options",
+            config:get("couchdb", "fsync_options",
                     "[before_header, after_header, on_file_open]")),
 
     case lists:member(on_file_open, FsyncOptions) of
@@ -946,9 +946,9 @@ copy_compact(Db, NewDb0, Retry) ->
     NewDb = NewDb0#db{compression=Compression},
     TotalChanges = couch_db:count_changes_since(Db, NewDb#db.update_seq),
     BufferSize = list_to_integer(
-        couch_config:get("database_compaction", "doc_buffer_size", "524288")),
+        config:get("database_compaction", "doc_buffer_size", "524288")),
     CheckpointAfter = couch_util:to_integer(
-        couch_config:get("database_compaction", "checkpoint_after",
+        config:get("database_compaction", "checkpoint_after",
             BufferSize * 10)),
 
     EnumBySeqFun =

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_drv.erl
----------------------------------------------------------------------
diff --git a/src/couch_drv.erl b/src/couch_drv.erl
index de4d76e..7fe119a 100644
--- a/src/couch_drv.erl
+++ b/src/couch_drv.erl
@@ -54,7 +54,7 @@ code_change(_OldVsn, State, _Extra) ->
 
 % private API
 util_driver_dir() ->
-    case couch_config:get("couchdb", "util_driver_dir", null) of
+    case config:get("couchdb", "util_driver_dir", null) of
     null ->
         couch_util:priv_dir();
     LibDir0 ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_ejson_compare.erl
----------------------------------------------------------------------
diff --git a/src/couch_ejson_compare.erl b/src/couch_ejson_compare.erl
index 083ff42..7b000fc 100644
--- a/src/couch_ejson_compare.erl
+++ b/src/couch_ejson_compare.erl
@@ -18,7 +18,7 @@
 
 
 init() ->
-    LibDir = case couch_config:get("couchdb", "util_driver_dir") of
+    LibDir = case config:get("couchdb", "util_driver_dir") of
     undefined ->
         filename:join(couch_util:priv_dir(), "lib");
     LibDir0 ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_external_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_external_manager.erl b/src/couch_external_manager.erl
index dec2880..a8cad63 100644
--- a/src/couch_external_manager.erl
+++ b/src/couch_external_manager.erl
@@ -12,10 +12,14 @@
 
 -module(couch_external_manager).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
--export([start_link/0, execute/2, config_change/2]).
+-export([start_link/0, execute/2]).
 -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 
 start_link() ->
@@ -31,15 +35,17 @@ execute(UrlName, JsonReq) ->
         couch_external_server:execute(Pid, JsonReq)
     end.
 
-config_change("external", UrlName) ->
-    gen_server:call(couch_external_manager, {config, UrlName}).
+handle_config_change("external", UrlName, _, _, _) ->
+    {ok, gen_server:call(couch_external_manager, {config, UrlName})};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
 
 % gen_server API
 
 init([]) ->
     process_flag(trap_exit, true),
     Handlers = ets:new(couch_external_manager_handlers, [set, private]),
-    couch_config:register(fun ?MODULE:config_change/2),
+    ok = config:listen_for_changes(?MODULE, nil),
     {ok, Handlers}.
 
 terminate(_Reason, Handlers) ->
@@ -52,7 +58,7 @@ terminate(_Reason, Handlers) ->
 handle_call({get, UrlName}, _From, Handlers) ->
     case ets:lookup(Handlers, UrlName) of
     [] ->
-        case couch_config:get("external", UrlName, nil) of
+        case config:get("external", UrlName, nil) of
         nil ->
             Msg = lists:flatten(
                 io_lib:format("No server configured for ~p.", [UrlName])),
@@ -94,7 +100,15 @@ handle_info({'EXIT', Pid, Reason}, Handlers) ->
     % Remove Pid from the handlers table so we don't try closing
     % it a second time in terminate/2.
     ets:match_delete(Handlers, {'_', Pid}),
-    {stop, normal, Handlers}.
+    {stop, normal, Handlers};
+
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State}.
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_external_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_external_server.erl b/src/couch_external_server.erl
index a3924d0..56406bb 100644
--- a/src/couch_external_server.erl
+++ b/src/couch_external_server.erl
@@ -12,10 +12,14 @@
 
 -module(couch_external_server).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 -export([start_link/2, stop/1, execute/2]).
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2, code_change/3]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 
 % External API
@@ -36,12 +40,10 @@ init([Name, Command]) ->
     ?LOG_INFO("EXTERNAL: Starting process for: ~s", [Name]),
     ?LOG_INFO("COMMAND: ~s", [Command]),
     process_flag(trap_exit, true),
-    Timeout = list_to_integer(couch_config:get("couchdb", "os_process_timeout",
+    Timeout = list_to_integer(config:get("couchdb", "os_process_timeout",
         "5000")),
     {ok, Pid} = couch_os_process:start_link(Command, [{timeout, Timeout}]),
-    couch_config:register(fun("couchdb", "os_process_timeout", NewTimeout) ->
-        couch_os_process:set_timeout(Pid, list_to_integer(NewTimeout))
-    end),
+    ok = config:listen_for_changes(?MODULE, {Name, Command, Pid}, Pid),
     {ok, {Name, Command, Pid}}.
 
 terminate(_Reason, {_Name, _Command, Pid}) ->
@@ -51,6 +53,12 @@ terminate(_Reason, {_Name, _Command, Pid}) ->
 handle_call({execute, JsonReq}, _From, {Name, Command, Pid}) ->
     {reply, couch_os_process:prompt(Pid, JsonReq), {Name, Command, Pid}}.
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, State),
+    {noreply, State};
 handle_info({'EXIT', _Pid, normal}, State) ->
     {noreply, State};
 handle_info({'EXIT', Pid, Reason}, {Name, Command, Pid}) ->
@@ -68,3 +76,9 @@ code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
 
+handle_config_change("couchdb", "os_process_timeout", NewTimeout, _, Pid) ->
+    couch_os_process:set_timeout(Pid, list_to_integer(NewTimeout)),
+    {ok, Pid};
+handle_config_change(_, _, _, _, Pid) ->
+    {ok, Pid}.
+

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd.erl b/src/couch_httpd.erl
index 53ae47e..db90129 100644
--- a/src/couch_httpd.erl
+++ b/src/couch_httpd.erl
@@ -13,8 +13,7 @@
 -module(couch_httpd).
 -include_lib("couch/include/couch_db.hrl").
 
--export([start_link/0, start_link/1, stop/0, config_change/2,
-        handle_request/5]).
+-export([start_link/0, start_link/1, stop/0, handle_request/5]).
 
 -export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,qs_json_value/3]).
 -export([path/1,absolute_uri/2,body_length/1]).
@@ -35,28 +34,28 @@
 start_link() ->
     start_link(http).
 start_link(http) ->
-    Port = couch_config:get("httpd", "port", "5984"),
+    Port = config:get("httpd", "port", "5984"),
     start_link(?MODULE, [{port, Port}]);
 start_link(https) ->
-    Port = couch_config:get("ssl", "port", "6984"),
-    CertFile = couch_config:get("ssl", "cert_file", nil),
-    KeyFile = couch_config:get("ssl", "key_file", nil),
+    Port = config:get("ssl", "port", "6984"),
+    CertFile = config:get("ssl", "cert_file", nil),
+    KeyFile = config:get("ssl", "key_file", nil),
     Options = case CertFile /= nil andalso KeyFile /= nil of
         true ->
             SslOpts = [{certfile, CertFile}, {keyfile, KeyFile}],
 
             %% set password if one is needed for the cert
-            SslOpts1 = case couch_config:get("ssl", "password", nil) of
+            SslOpts1 = case config:get("ssl", "password", nil) of
                 nil -> SslOpts;
                 Password ->
                     SslOpts ++ [{password, Password}]
             end,
             % do we verify certificates ?
-            FinalSslOpts = case couch_config:get("ssl",
+            FinalSslOpts = case config:get("ssl",
                     "verify_ssl_certificates", "false") of
                 "false" -> SslOpts1;
                 "true" ->
-                    case couch_config:get("ssl",
+                    case config:get("ssl",
                             "cacert_file", nil) of
                         nil ->
                             io:format("Verify SSL certificate "
@@ -65,7 +64,7 @@ start_link(https) ->
                                 ++"missing", []),
                             throw({error, missing_cacerts});
                         CaCertFile ->
-                            Depth = list_to_integer(couch_config:get("ssl",
+                            Depth = list_to_integer(config:get("ssl",
                                     "ssl_certificate_max_depth",
                                     "1")),
                             FinalOpts = [
@@ -73,7 +72,7 @@ start_link(https) ->
                                 {depth, Depth},
                                 {verify, verify_peer}],
                             % allows custom verify fun.
-                            case couch_config:get("ssl",
+                            case config:get("ssl",
                                     "verify_fun", nil) of
                                 nil -> FinalOpts;
                                 SpecStr ->
@@ -92,40 +91,35 @@ start_link(https) ->
     end,
     start_link(https, Options).
 start_link(Name, Options) ->
-    % read config and register for configuration changes
-
-    % just stop if one of the config settings change. couch_server_sup
-    % will restart us and then we will pick up the new settings.
-
-    BindAddress = couch_config:get("httpd", "bind_address", any),
+    BindAddress = config:get("httpd", "bind_address", any),
     validate_bind_address(BindAddress),
     DefaultSpec = "{couch_httpd_db, handle_request}",
     DefaultFun = make_arity_1_fun(
-        couch_config:get("httpd", "default_handler", DefaultSpec)
+        config:get("httpd", "default_handler", DefaultSpec)
     ),
 
     UrlHandlersList = lists:map(
         fun({UrlKey, SpecStr}) ->
             {?l2b(UrlKey), make_arity_1_fun(SpecStr)}
-        end, couch_config:get("httpd_global_handlers")),
+        end, config:get("httpd_global_handlers")),
 
     DbUrlHandlersList = lists:map(
         fun({UrlKey, SpecStr}) ->
             {?l2b(UrlKey), make_arity_2_fun(SpecStr)}
-        end, couch_config:get("httpd_db_handlers")),
+        end, config:get("httpd_db_handlers")),
 
     DesignUrlHandlersList = lists:map(
         fun({UrlKey, SpecStr}) ->
             {?l2b(UrlKey), make_arity_3_fun(SpecStr)}
-        end, couch_config:get("httpd_design_handlers")),
+        end, config:get("httpd_design_handlers")),
 
     UrlHandlers = dict:from_list(UrlHandlersList),
     DbUrlHandlers = dict:from_list(DbUrlHandlersList),
     DesignUrlHandlers = dict:from_list(DesignUrlHandlersList),
     {ok, ServerOptions} = couch_util:parse_term(
-        couch_config:get("httpd", "server_options", "[]")),
+        config:get("httpd", "server_options", "[]")),
     {ok, SocketOptions} = couch_util:parse_term(
-        couch_config:get("httpd", "socket_options", "[]")),
+        config:get("httpd", "socket_options", "[]")),
 
     set_auth_handlers(),
 
@@ -152,44 +146,23 @@ start_link(Name, Options) ->
             {ip, BindAddress}]]),
 
     % launch mochiweb
-    {ok, Pid} = case mochiweb_http:start(FinalOptions) of
+    case mochiweb_http:start(FinalOptions) of
         {ok, MochiPid} ->
             {ok, MochiPid};
         {error, Reason} ->
             io:format("Failure to start Mochiweb: ~s~n",[Reason]),
             throw({error, Reason})
-    end,
-
-    ok = couch_config:register(fun ?MODULE:config_change/2, Pid),
-    {ok, Pid}.
+    end.
 
 
 stop() ->
     mochiweb_http:stop(couch_httpd),
     mochiweb_http:stop(https).
 
-config_change("httpd", "bind_address") ->
-    ?MODULE:stop();
-config_change("httpd", "port") ->
-    ?MODULE:stop();
-config_change("httpd", "default_handler") ->
-    ?MODULE:stop();
-config_change("httpd", "server_options") ->
-    ?MODULE:stop();
-config_change("httpd", "socket_options") ->
-    ?MODULE:stop();
-config_change("httpd", "authentication_handlers") ->
-    set_auth_handlers();
-config_change("httpd_global_handlers", _) ->
-    ?MODULE:stop();
-config_change("httpd_db_handlers", _) ->
-    ?MODULE:stop();
-config_change("ssl", _) ->
-    ?MODULE:stop().
 
 set_auth_handlers() ->
     AuthenticationSrcs = make_fun_spec_strs(
-        couch_config:get("httpd", "authentication_handlers", "")),
+        config:get("httpd", "authentication_handlers", "")),
     AuthHandlers = lists:map(
         fun(A) -> {make_arity_1_fun(A), ?l2b(A)} end, AuthenticationSrcs),
     ok = application:set_env(couch, auth_handlers, AuthHandlers).
@@ -387,7 +360,7 @@ handle_request_int(MochiReq, DefaultFun,
 authenticate_request(#httpd{user_ctx=#user_ctx{}} = Req, _AuthHandlers) ->
     Req;
 authenticate_request(#httpd{} = Req, []) ->
-    case couch_config:get("couch_httpd_auth", "require_valid_user", "false") of
+    case config:get("couch_httpd_auth", "require_valid_user", "false") of
     "true" ->
         throw({unauthorized, <<"Authentication required.">>});
     "false" ->
@@ -493,7 +466,7 @@ path(#httpd{mochi_req=MochiReq}) ->
     MochiReq:get(path).
 
 host_for_request(#httpd{mochi_req=MochiReq}) ->
-    XHost = couch_config:get("httpd", "x_forwarded_host", "X-Forwarded-Host"),
+    XHost = config:get("httpd", "x_forwarded_host", "X-Forwarded-Host"),
     case MochiReq:get_header_value(XHost) of
         undefined ->
             case MochiReq:get_header_value("Host") of
@@ -511,11 +484,11 @@ host_for_request(#httpd{mochi_req=MochiReq}) ->
 
 absolute_uri(#httpd{mochi_req=MochiReq}=Req, Path) ->
     Host = host_for_request(Req),
-    XSsl = couch_config:get("httpd", "x_forwarded_ssl", "X-Forwarded-Ssl"),
+    XSsl = config:get("httpd", "x_forwarded_ssl", "X-Forwarded-Ssl"),
     Scheme = case MochiReq:get_header_value(XSsl) of
                  "on" -> "https";
                  _ ->
-                     XProto = couch_config:get("httpd", "x_forwarded_proto", "X-Forwarded-Proto"),
+                     XProto = config:get("httpd", "x_forwarded_proto", "X-Forwarded-Proto"),
                      case MochiReq:get_header_value(XProto) of
                          %% Restrict to "https" and "http" schemes only
                          "https" -> "https";
@@ -550,7 +523,7 @@ body_length(#httpd{mochi_req=MochiReq}) ->
 
 body(#httpd{mochi_req=MochiReq, req_body=undefined}) ->
     MaxSize = list_to_integer(
-        couch_config:get("couchdb", "max_document_size", "4294967296")),
+        config:get("couchdb", "max_document_size", "4294967296")),
     MochiReq:recv_body(MaxSize);
 body(#httpd{req_body=ReqBody}) ->
     ReqBody.
@@ -753,7 +726,7 @@ initialize_jsonp(Req) ->
         CallBack ->
             try
                 % make sure jsonp is configured on (default off)
-                case couch_config:get("httpd", "allow_jsonp", "false") of
+                case config:get("httpd", "allow_jsonp", "false") of
                 "true" ->
                     validate_callback(CallBack);
                 _Else ->
@@ -848,16 +821,16 @@ error_headers(#httpd{mochi_req=MochiReq}=Req, Code, ErrorStr, ReasonStr) ->
         % this is where the basic auth popup is triggered
         case MochiReq:get_header_value("X-CouchDB-WWW-Authenticate") of
         undefined ->
-            case couch_config:get("httpd", "WWW-Authenticate", nil) of
+            case config:get("httpd", "WWW-Authenticate", nil) of
             nil ->
                 % If the client is a browser and the basic auth popup isn't turned on
                 % redirect to the session page.
                 case ErrorStr of
                 <<"unauthorized">> ->
-                    case couch_config:get("couch_httpd_auth", "authentication_redirect", nil) of
+                    case config:get("couch_httpd_auth", "authentication_redirect", nil) of
                     nil -> {Code, []};
                     AuthRedirect ->
-                        case couch_config:get("couch_httpd_auth", "require_valid_user", "false") of
+                        case config:get("couch_httpd_auth", "require_valid_user", "false") of
                         "true" ->
                             % send the browser popup header no matter what if we are require_valid_user
                             {Code, [{"WWW-Authenticate", "Basic realm=\"server\""}]};

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_auth.erl b/src/couch_httpd_auth.erl
index 785dd79..a747869 100644
--- a/src/couch_httpd_auth.erl
+++ b/src/couch_httpd_auth.erl
@@ -83,7 +83,7 @@ default_authentication_handler(Req) ->
         true ->
             Req;
         false ->
-            case couch_config:get("couch_httpd_auth", "require_valid_user", "false") of
+            case config:get("couch_httpd_auth", "require_valid_user", "false") of
                 "true" -> Req;
                 % If no admins, and no user required, then everyone is admin!
                 % Yay, admin party!
@@ -118,11 +118,11 @@ proxy_authentification_handler(Req) ->
     end.
     
 proxy_auth_user(Req) ->
-    XHeaderUserName = couch_config:get("couch_httpd_auth", "x_auth_username",
+    XHeaderUserName = config:get("couch_httpd_auth", "x_auth_username",
                                 "X-Auth-CouchDB-UserName"),
-    XHeaderRoles = couch_config:get("couch_httpd_auth", "x_auth_roles",
+    XHeaderRoles = config:get("couch_httpd_auth", "x_auth_roles",
                                 "X-Auth-CouchDB-Roles"),
-    XHeaderToken = couch_config:get("couch_httpd_auth", "x_auth_token",
+    XHeaderToken = config:get("couch_httpd_auth", "x_auth_token",
                                 "X-Auth-CouchDB-Token"),
     case header_value(Req, XHeaderUserName) of
         undefined -> nil;
@@ -132,9 +132,9 @@ proxy_auth_user(Req) ->
                 Else ->
                     [?l2b(R) || R <- string:tokens(Else, ",")]
             end,
-            case couch_config:get("couch_httpd_auth", "proxy_use_secret", "false") of
+            case config:get("couch_httpd_auth", "proxy_use_secret", "false") of
                 "true" ->
-                    case couch_config:get("couch_httpd_auth", "secret", nil) of
+                    case config:get("couch_httpd_auth", "secret", nil) of
                         nil ->
                             Req#httpd{user_ctx=#user_ctx{name=?l2b(UserName), roles=Roles}};
                         Secret ->
@@ -168,7 +168,7 @@ cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
         end,
         % Verify expiry and hash
         CurrentTime = make_cookie_time(),
-        case couch_config:get("couch_httpd_auth", "secret", nil) of
+        case config:get("couch_httpd_auth", "secret", nil) of
         nil ->
             ?LOG_DEBUG("cookie auth secret is not set",[]),
             Req;
@@ -182,7 +182,7 @@ cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
                 ExpectedHash = crypto:sha_mac(FullSecret, User ++ ":" ++ TimeStr),
                 Hash = ?l2b(HashStr),
                 Timeout = list_to_integer(
-                    couch_config:get("couch_httpd_auth", "timeout", "600")),
+                    config:get("couch_httpd_auth", "timeout", "600")),
                 ?LOG_DEBUG("timeout ~p", [Timeout]),
                 case (catch erlang:list_to_integer(TimeStr, 16)) of
                     TimeStamp when CurrentTime < TimeStamp + Timeout ->
@@ -232,10 +232,10 @@ cookie_auth_cookie(Req, User, Secret, TimeStamp) ->
         [{path, "/"}] ++ cookie_scheme(Req) ++ max_age()).
 
 ensure_cookie_auth_secret() ->
-    case couch_config:get("couch_httpd_auth", "secret", nil) of
+    case config:get("couch_httpd_auth", "secret", nil) of
         nil ->
             NewSecret = ?b2l(couch_uuids:random()),
-            couch_config:set("couch_httpd_auth", "secret", NewSecret),
+            config:set("couch_httpd_auth", "secret", NewSecret),
             NewSecret;
         Secret -> Secret
     end.
@@ -311,9 +311,9 @@ handle_session_req(#httpd{method='GET', user_ctx=UserCtx}=Req) ->
                     {roles, UserCtx#user_ctx.roles}
                 ]}},
                 {info, {[
-                    {authentication_db, ?l2b(couch_config:get("couch_httpd_auth", "authentication_db"))},
+                    {authentication_db, ?l2b(config:get("couch_httpd_auth", "authentication_db"))},
                     {authentication_handlers, [auth_name(H) || H <- couch_httpd:make_fun_spec_strs(
-                            couch_config:get("httpd", "authentication_handlers"))]}
+                            config:get("httpd", "authentication_handlers"))]}
                 ] ++ maybe_value(authenticated, UserCtx#user_ctx.handler, fun(Handler) ->
                         auth_name(?b2l(Handler))
                     end)}}
@@ -366,11 +366,11 @@ cookie_scheme(#httpd{mochi_req=MochiReq}) ->
     end.
 
 max_age() ->
-    case couch_config:get("couch_httpd_auth", "allow_persistent_cookies", "false") of
+    case config:get("couch_httpd_auth", "allow_persistent_cookies", "false") of
         "false" ->
             [];
         "true" ->
             Timeout = list_to_integer(
-                couch_config:get("couch_httpd_auth", "timeout", "600")),
+                config:get("couch_httpd_auth", "timeout", "600")),
             [{max_age, Timeout}]
     end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_cors.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_cors.erl b/src/couch_httpd_cors.erl
index 5432fbd..d98357a 100644
--- a/src/couch_httpd_cors.erl
+++ b/src/couch_httpd_cors.erl
@@ -293,8 +293,8 @@ allow_credentials(_Origin, Host) ->
 
 
 cors_config(Host, Key, Default) ->
-    couch_config:get(cors_section(Host), Key,
-                     couch_config:get("cors", Key, Default)).
+    config:get(cors_section(Host), Key,
+                     config:get("cors", Key, Default)).
 
 cors_section(Host0) ->
     {Host, _Port} = split_host_port(Host0),
@@ -304,7 +304,7 @@ enable_cors() ->
     get_bool_config("httpd", "enable_cors", false).
 
 get_bool_config(Section, Key, Default) ->
-    case couch_config:get(Section, Key) of
+    case config:get(Section, Key) of
     undefined ->
         Default;
     "true" ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl
index 2ac4971..c70245c 100644
--- a/src/couch_httpd_db.erl
+++ b/src/couch_httpd_db.erl
@@ -63,7 +63,7 @@ handle_changes_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "GET,HEAD,POST").
 
 handle_changes_req1(Req, #db{name=DbName}=Db) ->
-    AuthDbName = ?l2b(couch_config:get("couch_httpd_auth", "authentication_db")),
+    AuthDbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")),
     case AuthDbName of
     DbName ->
         % in the authentication database, _changes is admin-only.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_external.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_external.erl b/src/couch_httpd_external.erl
index 077136d..8322dcd 100644
--- a/src/couch_httpd_external.erl
+++ b/src/couch_httpd_external.erl
@@ -63,7 +63,7 @@ json_req_obj(#httpd{mochi_req=Req,
     Body = case ReqBody of
         undefined ->
             MaxSize = list_to_integer(
-                couch_config:get("couchdb", "max_document_size", "4294967296")),
+                config:get("couchdb", "max_document_size", "4294967296")),
             Req:recv_body(MaxSize);
         Else -> Else
     end,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_misc_handlers.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_misc_handlers.erl b/src/couch_httpd_misc_handlers.erl
index ff79f14..e46dee3 100644
--- a/src/couch_httpd_misc_handlers.erl
+++ b/src/couch_httpd_misc_handlers.erl
@@ -34,7 +34,7 @@ handle_welcome_req(#httpd{method='GET'}=Req, WelcomeMessage) ->
         {couchdb, WelcomeMessage},
         {uuid, couch_server:get_uuid()},
         {version, list_to_binary(couch_server:get_version())}
-        ] ++ case couch_config:get("vendor") of
+        ] ++ case config:get("vendor") of
         [] ->
             [];
         Properties ->
@@ -137,7 +137,7 @@ handle_config_req(#httpd{method='GET', path_parts=[_]}=Req) ->
         false ->
             dict:store(Section, [{list_to_binary(Key), list_to_binary(Value)}], Acc)
         end
-    end, dict:new(), couch_config:all()),
+    end, dict:new(), config:all()),
     KVs = dict:fold(fun(Section, Values, Acc) ->
         [{list_to_binary(Section), {Values}} | Acc]
     end, [], Grouped),
@@ -146,12 +146,12 @@ handle_config_req(#httpd{method='GET', path_parts=[_]}=Req) ->
 handle_config_req(#httpd{method='GET', path_parts=[_,Section]}=Req) ->
     ok = couch_httpd:verify_is_server_admin(Req),
     KVs = [{list_to_binary(Key), list_to_binary(Value)}
-            || {Key, Value} <- couch_config:get(Section)],
+            || {Key, Value} <- config:get(Section)],
     send_json(Req, 200, {KVs});
 % GET /_config/Section/Key
 handle_config_req(#httpd{method='GET', path_parts=[_, Section, Key]}=Req) ->
     ok = couch_httpd:verify_is_server_admin(Req),
-    case couch_config:get(Section, Key, null) of
+    case config:get(Section, Key, null) of
     null ->
         throw({not_found, unknown_config_value});
     Value ->
@@ -162,7 +162,7 @@ handle_config_req(#httpd{method=Method, path_parts=[_, Section, Key]}=Req)
       when (Method == 'PUT') or (Method == 'DELETE') ->
     ok = couch_httpd:verify_is_server_admin(Req),
     Persist = couch_httpd:header_value(Req, "X-Couch-Persist") /= "false",
-    case couch_config:get(<<"httpd">>, <<"config_whitelist">>, null) of
+    case config:get(<<"httpd">>, <<"config_whitelist">>, null) of
         null ->
             % No whitelist; allow all changes.
             handle_approved_config_req(Req, Persist);
@@ -226,8 +226,8 @@ handle_approved_config_req(#httpd{method='PUT', path_parts=[_, Section, Key]}=Re
     _ ->
         couch_httpd:json_body(Req)
     end,
-    OldValue = couch_config:get(Section, Key, ""),
-    case couch_config:set(Section, Key, ?b2l(Value), Persist) of
+    OldValue = config:get(Section, Key, ""),
+    case config:set(Section, Key, ?b2l(Value), Persist) of
     ok ->
         send_json(Req, 200, list_to_binary(OldValue));
     Error ->
@@ -235,11 +235,11 @@ handle_approved_config_req(#httpd{method='PUT', path_parts=[_, Section, Key]}=Re
     end;
 % DELETE /_config/Section/Key
 handle_approved_config_req(#httpd{method='DELETE',path_parts=[_,Section,Key]}=Req, Persist) ->
-    case couch_config:get(Section, Key, null) of
+    case config:get(Section, Key, null) of
     null ->
         throw({not_found, unknown_config_value});
     OldValue ->
-        couch_config:delete(Section, Key, Persist),
+        config:delete(Section, Key, Persist),
         send_json(Req, 200, list_to_binary(OldValue))
     end.
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_oauth.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_oauth.erl b/src/couch_httpd_oauth.erl
index 57799e6..8fd4c30 100644
--- a/src/couch_httpd_oauth.erl
+++ b/src/couch_httpd_oauth.erl
@@ -309,17 +309,17 @@ oauth_credentials_info(Token, ConsumerKey) ->
         Result;
     nil ->
         {
-            case couch_config:get("oauth_consumer_secrets", ConsumerKey) of
+            case config:get("oauth_consumer_secrets", ConsumerKey) of
             undefined -> [];
             ConsumerSecret -> [{<<"consumer_secret">>, ?l2b(ConsumerSecret)}]
             end
             ++
-            case couch_config:get("oauth_token_secrets", Token) of
+            case config:get("oauth_token_secrets", Token) of
             undefined -> [];
             TokenSecret -> [{<<"token_secret">>, ?l2b(TokenSecret)}]
             end
             ++
-            case couch_config:get("oauth_token_users", Token) of
+            case config:get("oauth_token_users", Token) of
             undefined -> [];
             User -> [{<<"username">>, ?l2b(User)}]
             end
@@ -328,7 +328,7 @@ oauth_credentials_info(Token, ConsumerKey) ->
 
 
 use_auth_db() ->
-    case couch_config:get("couch_httpd_oauth", "use_users_db", "false") of
+    case config:get("couch_httpd_oauth", "use_users_db", "false") of
     "false" ->
         nil;
     "true" ->
@@ -338,7 +338,7 @@ use_auth_db() ->
 
 
 open_auth_db() ->
-    DbName = ?l2b(couch_config:get("couch_httpd_auth", "authentication_db")),
+    DbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")),
     DbOptions = [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}],
     {ok, AuthDb} = couch_db:open_int(DbName, DbOptions),
     AuthDb.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_rewrite.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_rewrite.erl b/src/couch_httpd_rewrite.erl
index 428d30c..abd6af5 100644
--- a/src/couch_httpd_rewrite.erl
+++ b/src/couch_httpd_rewrite.erl
@@ -119,7 +119,7 @@ handle_rewrite_req(#httpd{
     Prefix = <<"/", (?l2b(couch_util:url_encode(DbName)))/binary, "/", DesignId/binary>>,
     QueryList = lists:map(fun decode_query_value/1, couch_httpd:qs(Req)),
 
-    MaxRewritesList = couch_config:get("httpd", "rewrite_limit", "100"),
+    MaxRewritesList = config:get("httpd", "rewrite_limit", "100"),
     MaxRewrites = list_to_integer(MaxRewritesList),
     case get(couch_rewrite_count) of
         undefined ->
@@ -438,7 +438,7 @@ path_to_list([<<>>|R], Acc, DotDotCount) ->
 path_to_list([<<"*">>|R], Acc, DotDotCount) ->
     path_to_list(R, [?MATCH_ALL|Acc], DotDotCount);
 path_to_list([<<"..">>|R], Acc, DotDotCount) when DotDotCount == 2 ->
-    case couch_config:get("httpd", "secure_rewrites", "true") of
+    case config:get("httpd", "secure_rewrites", "true") of
     "false" ->
         path_to_list(R, [<<"..">>|Acc], DotDotCount+1);
     _Else ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_httpd_vhost.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_vhost.erl b/src/couch_httpd_vhost.erl
index 258f4eb..29fddfe 100644
--- a/src/couch_httpd_vhost.erl
+++ b/src/couch_httpd_vhost.erl
@@ -12,13 +12,17 @@
 
 -module(couch_httpd_vhost).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
--export([start_link/0, config_change/2, reload/0, get_state/0, dispatch_host/1]).
+-export([start_link/0, reload/0, get_state/0, dispatch_host/1]).
 -export([urlsplit_netloc/2, redirect_to_vhost/2]).
 -export([host/1, split_host_port/1]).
 
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 
 -define(SEPARATOR, $\/).
@@ -237,7 +241,7 @@ bind_path(_, _) ->
 %% create vhost list from ini
 
 host(MochiReq) ->
-    XHost = couch_config:get("httpd", "x_forwarded_host",
+    XHost = config:get("httpd", "x_forwarded_host",
                              "X-Forwarded-Host"),
     case MochiReq:get_header_value(XHost) of
         undefined ->
@@ -254,7 +258,7 @@ make_vhosts() ->
                     Acc;
                 ({Vhost, Path}, Acc) ->
                     [{parse_vhost(Vhost), split_path(Path)}|Acc]
-            end, [], couch_config:get("vhosts")),
+            end, [], config:get("vhosts")),
 
     lists:reverse(lists:usort(Vhosts)).
 
@@ -327,7 +331,7 @@ make_path(Parts) ->
      "/" ++ string:join(Parts,[?SEPARATOR]).
 
 init(_) ->
-    ok = couch_config:register(fun ?MODULE:config_change/2),
+    ok = config:listen_for_changes(?MODULE, nil),
 
     %% load configuration
     {VHostGlobals, VHosts, Fun} = load_conf(),
@@ -351,6 +355,12 @@ handle_call(_Msg, _From, State) ->
 handle_cast(_Msg, State) ->
     {noreply, State}.
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info(_Info, State) ->
     {noreply, State}.
 
@@ -360,16 +370,20 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-config_change("httpd", "vhost_global_handlers") ->
-    ?MODULE:reload();
-config_change("httpd", "redirect_vhost_handler") ->
-    ?MODULE:reload();
-config_change("vhosts", _) ->
-    ?MODULE:reload().
+
+handle_config_change("httpd", "vhost_global_handlers", _, _, _) ->
+    {ok, ?MODULE:reload()};
+handle_config_change("httpd", "redirect_vhost_handler", _, _, _) ->
+    {ok, ?MODULE:reload()};
+handle_config_change("vhosts", _, _, _, _) ->
+    {ok, ?MODULE:reload()};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
 
 load_conf() ->
     %% get vhost globals
-    VHostGlobals = re:split(couch_config:get("httpd",
+    VHostGlobals = re:split(config:get("httpd",
             "vhost_global_handlers",""), "\\s*,\\s*",[{return, list}]),
 
     %% build vhosts matching rules
@@ -377,7 +391,7 @@ load_conf() ->
 
     %% build vhosts handler fun
     DefaultVHostFun = "{couch_httpd_vhost, redirect_to_vhost}",
-    Fun = couch_httpd:make_arity_2_fun(couch_config:get("httpd",
+    Fun = couch_httpd:make_arity_2_fun(config:get("httpd",
             "redirect_vhost_handler", DefaultVHostFun)),
 
     {VHostGlobals, VHosts, Fun}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_log.erl
----------------------------------------------------------------------
diff --git a/src/couch_log.erl b/src/couch_log.erl
index cd4bbbb..d1aa701 100644
--- a/src/couch_log.erl
+++ b/src/couch_log.erl
@@ -12,6 +12,7 @@
 
 -module(couch_log).
 -behaviour(gen_event).
+-behaviour(config_listener).
 
 % public API
 -export([start_link/0, stop/0]).
@@ -24,6 +25,9 @@
 -export([init/1, handle_event/2, terminate/2, code_change/3]).
 -export([handle_info/2, handle_call/2]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -define(LEVEL_ERROR, 4).
 -define(LEVEL_WARN, 3).
 -define(LEVEL_INFO, 2).
@@ -71,25 +75,12 @@ stop() ->
     couch_event_sup:stop(couch_log).
 
 init([]) ->
-    % read config and register for configuration changes
-
-    % just stop if one of the config settings change. couch_server_sup
-    % will restart us and then we will pick up the new settings.
-    ok = couch_config:register(
-        fun("log", "file") ->
-            ?MODULE:stop();
-        ("log", "level") ->
-            ?MODULE:stop();
-        ("log", "include_sasl") ->
-            ?MODULE:stop();
-        ("log_level_by_module", _) ->
-            ?MODULE:stop()
-        end),
-
-    Filename = couch_config:get("log", "file", "couchdb.log"),
-    Level = level_integer(list_to_atom(couch_config:get("log", "level", "info"))),
-    Sasl = couch_config:get("log", "include_sasl", "true") =:= "true",
-    LevelByModule = couch_config:get("log_level_by_module"),
+    ok = config:listen_for_changes(?MODULE, nil),
+
+    Filename = config:get("log", "file", "couchdb.log"),
+    Level = level_integer(list_to_atom(config:get("log", "level", "info"))),
+    Sasl = config:get("log", "include_sasl", "true") =:= "true",
+    LevelByModule = config:get("log_level_by_module"),
 
     case ets:info(?MODULE) of
     undefined -> ets:new(?MODULE, [named_table]);
@@ -111,6 +102,18 @@ init([]) ->
         {stop, {error, ReasonStr, Filename}}
     end.
 
+handle_config_change("log", "file", _, _, _) ->
+    ?MODULE:stop(),
+    remove_handler;
+handle_config_change("log", "level", _, _, _) ->
+    ?MODULE:stop(),
+    remove_handler;
+handle_config_change("log", "include_sasl", _, _, _) ->
+    ?MODULE:stop(),
+    remove_handler;
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
 debug_on() ->
     get_level_integer() =< ?LEVEL_DEBUG.
 
@@ -194,6 +197,12 @@ handle_call({set_level_integer, Module, NewLevel}, State) ->
     ets:insert(?MODULE, {Module, NewLevel}),
     {ok, ok, State#state{level = NewLevel}}.
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {ok, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {ok, State};
 handle_info(_Info, State) ->
     {ok, State}.
 
@@ -231,10 +240,10 @@ get_log_messages(Pid, Level, Format, Args) ->
 % |__________| 100
 
 read(Bytes, Offset) ->
-    LogFileName = couch_config:get("log", "file"),
+    LogFileName = config:get("log", "file"),
     LogFileSize = filelib:file_size(LogFileName),
     MaxChunkSize = list_to_integer(
-        couch_config:get("httpd", "log_max_chunk_size", "1000000")),
+        config:get("httpd", "log_max_chunk_size", "1000000")),
     case Bytes > MaxChunkSize of
     true ->
         throw({bad_request, "'bytes' cannot exceed " ++

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_os_daemons.erl
----------------------------------------------------------------------
diff --git a/src/couch_os_daemons.erl b/src/couch_os_daemons.erl
index 00fd7b3..3560149 100644
--- a/src/couch_os_daemons.erl
+++ b/src/couch_os_daemons.erl
@@ -11,12 +11,16 @@
 % the License.
 -module(couch_os_daemons).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
--export([start_link/0, info/0, info/1, config_change/2]).
+-export([start_link/0, info/0, info/1]).
 
 -export([init/1, terminate/2, code_change/3]).
 -export([handle_call/3, handle_cast/2, handle_info/2]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 
 -record(daemon, {
@@ -42,12 +46,9 @@ info() ->
 info(Options) ->
     gen_server:call(?MODULE, {daemon_info, Options}).
 
-config_change(Section, Key) ->
-    gen_server:cast(?MODULE, {config_change, Section, Key}).
-
 init(_) ->
     process_flag(trap_exit, true),
-    ok = couch_config:register(fun ?MODULE:config_change/2),
+    ok = config:listen_for_changes(?MODULE, nil),
     Table = ets:new(?MODULE, [protected, set, {keypos, #daemon.port}]),
     reload_daemons(Table),
     {ok, Table}.
@@ -80,6 +81,12 @@ handle_cast(Msg, Table) ->
     ?LOG_ERROR("Unknown cast message to ~p: ~p", [?MODULE, Msg]),
     {stop, error, Table}.
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info({'EXIT', Port, Reason}, Table) ->
     case ets:lookup(Table, Port) of
         [] ->
@@ -186,6 +193,12 @@ handle_info(Msg, Table) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
+
+handle_config_change(Section, Key, _, _, _) ->
+    gen_server:cast(?MODULE, {config_change, Section, Key}),
+    {ok, nil}.
+
+
 % Internal API
 
 %
@@ -209,13 +222,13 @@ stop_port(#daemon{port=Port}=D) ->
 
 
 handle_port_message(#daemon{port=Port}=Daemon, [<<"get">>, Section]) ->
-    KVs = couch_config:get(Section),
+    KVs = config:get(Section),
     Data = lists:map(fun({K, V}) -> {?l2b(K), ?l2b(V)} end, KVs),
     Json = iolist_to_binary(?JSON_ENCODE({Data})),
     port_command(Port, <<Json/binary, "\n">>),
     {ok, Daemon};
 handle_port_message(#daemon{port=Port}=Daemon, [<<"get">>, Section, Key]) ->
-    Value = case couch_config:get(Section, Key, null) of
+    Value = case config:get(Section, Key, null) of
         null -> null;
         String -> ?l2b(String)
     end,
@@ -260,7 +273,7 @@ handle_log_message(Name, Msg, Level) ->
 
 reload_daemons(Table) ->
     % List of daemons we want to have running.
-    Configured = lists:sort(couch_config:get("os_daemons")),
+    Configured = lists:sort(config:get("os_daemons")),
     
     % Remove records for daemons that were halted.
     MSpecHalted = #daemon{name='$1', cmd='$2', status=halted, _='_'},
@@ -350,7 +363,7 @@ find_to_stop(_, [], Acc) ->
     Acc.
 
 should_halt(Errors) ->
-    RetryTimeCfg = couch_config:get("os_daemon_settings", "retry_time", "5"),
+    RetryTimeCfg = config:get("os_daemon_settings", "retry_time", "5"),
     RetryTime = list_to_integer(RetryTimeCfg),
 
     Now = now(),
@@ -358,7 +371,7 @@ should_halt(Errors) ->
         timer:now_diff(Now, Time) =< RetryTime * 1000000
     end, Errors),
 
-    RetryCfg = couch_config:get("os_daemon_settings", "max_retries", "3"),
+    RetryCfg = config:get("os_daemon_settings", "max_retries", "3"),
     Retries = list_to_integer(RetryCfg),
 
     {length(RecentErrors) >= Retries, RecentErrors}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_os_process.erl
----------------------------------------------------------------------
diff --git a/src/couch_os_process.erl b/src/couch_os_process.erl
index fcf9da5..c6e6520 100644
--- a/src/couch_os_process.erl
+++ b/src/couch_os_process.erl
@@ -178,7 +178,7 @@ pick_command1(_) ->
 init([Command, Options, PortOptions]) ->
     PrivDir = couch_util:priv_dir(),
     Spawnkiller = filename:join(PrivDir, "couchspawnkillable"),
-    V = couch_config:get("query_server_config", "os_process_idle_limit", "300"),
+    V = config:get("query_server_config", "os_process_idle_limit", "300"),
     IdleLimit = list_to_integer(V) * 1000,
     BaseProc = #os_proc{
         command=Command,
@@ -263,7 +263,7 @@ handle_info(Msg, #os_proc{idle=Idle}=OsProc) ->
     {noreply, OsProc, Idle}.
 
 code_change(_, {os_proc, Cmd, Port, W, R, Timeout} , _) ->
-    V = couch_config:get("query_server_config","os_process_idle_limit","300"),
+    V = config:get("query_server_config","os_process_idle_limit","300"),
     State = #os_proc{
         command = Cmd,
         port = Port,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_passwords.erl
----------------------------------------------------------------------
diff --git a/src/couch_passwords.erl b/src/couch_passwords.erl
index 43f3380..d0f36cc 100644
--- a/src/couch_passwords.erl
+++ b/src/couch_passwords.erl
@@ -28,7 +28,7 @@ simple(Password, Salt) ->
 %% CouchDB utility functions
 -spec hash_admin_password(binary()) -> binary().
 hash_admin_password(ClearPassword) ->
-    Iterations = couch_config:get("couch_httpd_auth", "iterations", "10000"),
+    Iterations = config:get("couch_httpd_auth", "iterations", "10000"),
     Salt = couch_uuids:random(),
     DerivedKey = couch_passwords:pbkdf2(couch_util:to_binary(ClearPassword),
                                         Salt ,list_to_integer(Iterations)),
@@ -46,7 +46,7 @@ get_unhashed_admins() ->
         ({_User, _ClearPassword}) ->
             true
         end,
-    couch_config:get("admins")).
+    config:get("admins")).
 
 %% Current scheme, much stronger.
 -spec pbkdf2(binary(), binary(), integer()) -> binary().

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 94946a1..6130fc9 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -100,7 +100,7 @@ handle_call(_Call, _From, State) ->
     {reply, ignored, State}.
 
 handle_cast({os_proc_idle, Pid}, #state{tab=Tab}=State) ->
-    Limit = couch_config:get("query_server_config", "os_process_soft_limit", "100"),
+    Limit = config:get("query_server_config", "os_process_soft_limit", "100"),
     case ets:lookup(Tab, Pid) of
         [#proc{client=nil}] ->
             case ets:info(Tab, size) > list_to_integer(Limit) of
@@ -225,9 +225,9 @@ new_proc(From, Lang, DDoc, DDocKey) ->
 new_proc_int(From, Lang) when is_binary(Lang) ->
     new_proc_int(From, binary_to_list(Lang));
 new_proc_int(From, Lang) when is_list(Lang) ->
-    case couch_config:get("query_servers", Lang) of
+    case config:get("query_servers", Lang) of
     undefined ->
-        case couch_config:get("native_query_servers", Lang) of
+        case config:get("native_query_servers", Lang) of
         undefined ->
             gen_server:reply(From, {unknown_query_language, Lang});
         SpecStr ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_secondary_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_secondary_sup.erl b/src/couch_secondary_sup.erl
index 6320fd9..4f2d1c8 100644
--- a/src/couch_secondary_sup.erl
+++ b/src/couch_secondary_sup.erl
@@ -38,5 +38,5 @@ init([]) ->
                 [Module]}
         end
         || {Name, SpecStr}
-        <- couch_config:get("daemons"), SpecStr /= ""],
+        <- config:get("daemons"), SpecStr /= ""],
     {ok, {{one_for_one, 10, 3600}, Children}}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index 40c2d7e..c23ec05 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -12,14 +12,18 @@
 
 -module(couch_server).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 -export([open/2,create/2,delete/2,get_version/0,get_uuid/0]).
 -export([all_databases/0, all_databases/2]).
 -export([init/1, handle_call/3,sup_start_link/0]).
 -export([handle_cast/2,code_change/3,handle_info/2,terminate/2]).
--export([dev_start/0,is_admin/2,has_admins/0,get_stats/0,config_change/4]).
+-export([dev_start/0,is_admin/2,has_admins/0,get_stats/0]).
 -export([close_lru/0]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 -include_lib("couch/include/couch_db.hrl").
 
 -record(server,{
@@ -46,10 +50,10 @@ get_version() ->
     end.
 
 get_uuid() ->
-    case couch_config:get("couchdb", "uuid", nil) of
+    case config:get("couchdb", "uuid", nil) of
         nil ->
             UUID = couch_uuids:random(),
-            couch_config:set("couchdb", "uuid", ?b2l(UUID)),
+            config:set("couchdb", "uuid", ?b2l(UUID)),
             UUID;
         UUID -> ?l2b(UUID)
     end.
@@ -105,7 +109,7 @@ delete(DbName, Options) ->
 maybe_add_sys_db_callbacks(DbName, Options) when is_binary(DbName) ->
     maybe_add_sys_db_callbacks(?b2l(DbName), Options);
 maybe_add_sys_db_callbacks(DbName, Options) ->
-    case couch_config:get("replicator", "db", "_replicator") of
+    case config:get("replicator", "db", "_replicator") of
     DbName ->
         [
             {before_doc_update, fun couch_replicator_manager:before_doc_update/2},
@@ -113,7 +117,7 @@ maybe_add_sys_db_callbacks(DbName, Options) ->
             sys_db | Options
         ];
     _ ->
-        case couch_config:get("couch_httpd_auth", "authentication_db", "_users") of
+        case config:get("couch_httpd_auth", "authentication_db", "_users") of
         DbName ->
         [
             {before_doc_update, fun couch_users_db:before_doc_update/2},
@@ -139,7 +143,7 @@ check_dbname(#server{dbname_regexp=RegExp}, DbName) ->
     end.
 
 is_admin(User, ClearPwd) ->
-    case couch_config:get("admins", User) of
+    case config:get("admins", User) of
     "-hashed-" ++ HashedPwdAndSalt ->
         [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
         couch_util:to_hex(crypto:sha(ClearPwd ++ Salt)) == HashedPwd;
@@ -148,7 +152,7 @@ is_admin(User, ClearPwd) ->
     end.
 
 has_admins() ->
-    couch_config:get("admins") /= [].
+    config:get("admins") /= [].
 
 get_full_filename(Server, DbName) ->
     filename:join([Server#server.root_dir, "./" ++ DbName ++ ".couch"]).
@@ -160,7 +164,7 @@ hash_admin_passwords(Persist) ->
     lists:foreach(
         fun({User, ClearPassword}) ->
             HashedPassword = couch_passwords:hash_admin_password(ClearPassword),
-            couch_config:set("admins", User, ?b2l(HashedPassword), Persist)
+            config:set("admins", User, ?b2l(HashedPassword), Persist)
         end, couch_passwords:get_unhashed_admins()).
 
 init([]) ->
@@ -169,10 +173,10 @@ init([]) ->
     % just stop if one of the config settings change. couch_server_sup
     % will restart us and then we will pick up the new settings.
 
-    RootDir = couch_config:get("couchdb", "database_dir", "."),
+    RootDir = config:get("couchdb", "database_dir", "."),
     MaxDbsOpen = list_to_integer(
-            couch_config:get("couchdb", "max_dbs_open")),
-    ok = couch_config:register(fun ?MODULE:config_change/4),
+            config:get("couchdb", "max_dbs_open")),
+    ok = config:listen_for_changes(?MODULE, nil),
     ok = couch_file:init_delete_dir(RootDir),
     hash_admin_passwords(),
     {ok, RegExp} = re:compile(
@@ -191,13 +195,31 @@ terminate(_Reason, _Srv) ->
         nil, couch_dbs),
     ok.
 
-config_change("couchdb", "database_dir", _, _) ->
-    exit(whereis(couch_server), config_change);
-config_change("couchdb", "max_dbs_open", Max, _) ->
-    gen_server:call(couch_server, {set_max_dbs_open, list_to_integer(Max)});
-config_change("admins", _, _, Persist) ->
+handle_config_change("couchdb", "database_dir", _, _, _) ->
+    exit(whereis(couch_server), config_change),
+    remove_handler;
+handle_config_change("couchdb", "max_dbs_open", Max, _, _) ->
+    {ok, gen_server:call(couch_server,{set_max_dbs_open,list_to_integer(Max)})};
+handle_config_change("admins", _, _, Persist, _) ->
     % spawn here so couch event manager doesn't deadlock
-    spawn(fun() -> hash_admin_passwords(Persist) end).
+    {ok, spawn(fun() -> hash_admin_passwords(Persist) end)};
+handle_config_change("httpd", "authentication_handlers", _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change("httpd", "bind_address", _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change("httpd", "port", _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change("httpd", "max_connections", _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change("httpd", "default_handler", _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change("httpd_global_handlers", _, _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change("httpd_db_handlers", _, _, _, _) ->
+    {ok, couch_httpd:stop()};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
 
 all_databases() ->
     {ok, DbList} = all_databases(

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_server_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_server_sup.erl b/src/couch_server_sup.erl
index 528ac7c..c42bcfb 100644
--- a/src/couch_server_sup.erl
+++ b/src/couch_server_sup.erl
@@ -12,16 +12,19 @@
 
 -module(couch_server_sup).
 -behaviour(supervisor).
+-behaviour(config_listener).
 
 
--export([start_link/1,stop/0, couch_config_start_link_wrapper/2,
-        restart_core_server/0, config_change/2]).
+-export([start_link/1,stop/0, restart_core_server/0]).
 
 -include_lib("couch/include/couch_db.hrl").
 
 %% supervisor callbacks
 -export([init/1]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 start_link(IniFiles) ->
     case whereis(couch_server_sup) of
     undefined ->
@@ -33,14 +36,6 @@ start_link(IniFiles) ->
 restart_core_server() ->
     init:restart().
 
-couch_config_start_link_wrapper(IniFiles, FirstConfigPid) ->
-    case is_process_alive(FirstConfigPid) of
-        true ->
-            link(FirstConfigPid),
-            {ok, FirstConfigPid};
-        false -> couch_config:start_link(IniFiles)
-    end.
-
 start_server(IniFiles) ->
     case init:get_argument(pidfile) of
     {ok, [PidFile]} ->
@@ -53,9 +48,7 @@ start_server(IniFiles) ->
     _ -> ok
     end,
 
-    {ok, ConfigPid} = couch_config:start_link(IniFiles),
-
-    LogLevel = couch_config:get("log", "level", "info"),
+    LogLevel = config:get("log", "level", "info"),
     % announce startup
     io:format("Apache CouchDB ~s (LogLevel=~s) is starting.~n", [
         couch_server:get_version(),
@@ -65,18 +58,12 @@ start_server(IniFiles) ->
     "debug" ->
         io:format("Configuration Settings ~p:~n", [IniFiles]),
         [io:format("  [~s] ~s=~p~n", [Module, Variable, Value])
-            || {{Module, Variable}, Value} <- couch_config:all()];
+            || {{Module, Variable}, Value} <- config:all()];
     _ -> ok
     end,
 
     BaseChildSpecs =
-    {{one_for_one, 10, 60},
-        [{couch_config,
-            {couch_server_sup, couch_config_start_link_wrapper, [IniFiles, ConfigPid]},
-            permanent,
-            brutal_kill,
-            worker,
-            [couch_config]},
+    {{one_for_one, 10, 60}, [
         {couch_primary_services,
             {couch_primary_sup, start_link, []},
             permanent,
@@ -98,13 +85,9 @@ start_server(IniFiles) ->
     {ok, Pid} = supervisor:start_link(
         {local, couch_server_sup}, couch_server_sup, BaseChildSpecs),
 
-    % launch the icu bridge
-    % just restart if one of the config settings change.
-    couch_config:register(fun ?MODULE:config_change/2, Pid),
-
-    unlink(ConfigPid),
+    ok = config:listen_for_changes(?MODULE, nil),
 
-    Ip = couch_config:get("httpd", "bind_address"),
+    Ip = config:get("httpd", "bind_address"),
     io:format("Apache CouchDB has started. Time to relax.~n"),
     Uris = [get_uri(Name, Ip) || Name <- [couch_httpd, https]],
     [begin
@@ -114,7 +97,7 @@ start_server(IniFiles) ->
         end
     end
     || Uri <- Uris],
-    case couch_config:get("couchdb", "uri_file", null) of 
+    case config:get("couchdb", "uri_file", null) of 
     null -> ok;
     UriFile ->
         Lines = [begin case Uri of
@@ -135,13 +118,17 @@ start_server(IniFiles) ->
 stop() ->
     catch exit(whereis(couch_server_sup), normal).
 
-config_change("daemons", _) ->
-    supervisor:terminate_child(couch_server_sup, couch_secondary_services),
-    supervisor:restart_child(couch_server_sup, couch_secondary_services);
-config_change("couchdb", "util_driver_dir") ->
+
+handle_config_change("daemons", _, _, _, _) ->
+    exit(whereis(couch_server_sup), shutdown),
+    remove_handler;
+handle_config_change("couchdb", "util_driver_dir", _, _, _) ->
     [Pid] = [P || {collation_driver, P, _, _}
         <- supervisor:which_children(couch_primary_services)],
-    Pid ! reload_driver.
+    Pid ! reload_driver,
+    {ok, nil};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
 
 init(ChildSpecs) ->
     {ok, ChildSpecs}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_stats_aggregator.erl
----------------------------------------------------------------------
diff --git a/src/couch_stats_aggregator.erl b/src/couch_stats_aggregator.erl
index 6090355..416c9a0 100644
--- a/src/couch_stats_aggregator.erl
+++ b/src/couch_stats_aggregator.erl
@@ -12,6 +12,7 @@
 
 -module(couch_stats_aggregator).
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 -export([start/0, start/1, stop/0]).
 -export([all/0, all/1, get/1, get/2, get_json/1, get_json/2, collect_sample/0]).
@@ -19,6 +20,10 @@
 -export([init/1, terminate/2, code_change/3]).
 -export([handle_call/3, handle_cast/2, handle_info/2]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
+
 -record(aggregate, {
     description = <<"">>,
     seconds = 0,
@@ -92,7 +97,7 @@ collect_sample() ->
 init(StatDescsFileName) ->
     % Create an aggregate entry for each {description, rate} pair.
     ets:new(?MODULE, [named_table, set, protected]),
-    SampleStr = couch_config:get("stats", "samples", "[0]"),
+    SampleStr = config:get("stats", "samples", "[0]"),
     {ok, Samples} = couch_util:parse_term(SampleStr),
     {ok, Descs} = file:consult(StatDescsFileName),
     lists:foreach(fun({Sect, Key, Value}) ->
@@ -105,12 +110,9 @@ init(StatDescsFileName) ->
         end, Samples)
     end, Descs),
     
-    Self = self(),
-    ok = couch_config:register(
-        fun("stats", _) -> exit(Self, config_change) end
-    ),
+    ok = config:listen_for_changes(?MODULE, nil),
     
-    Rate = list_to_integer(couch_config:get("stats", "rate", "1000")),
+    Rate = list_to_integer(config:get("stats", "rate", "1000")),
     % TODO: Add timer_start to kernel start options.
     {ok, TRef} = timer:apply_after(Rate, ?MODULE, collect_sample, []),
     {ok, {TRef, Rate}}.
@@ -156,6 +158,12 @@ handle_call(collect_sample, _, {OldTRef, SampleInterval}) ->
 handle_cast(stop, State) ->
     {stop, normal, State}.
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info(_Info, State) ->
     {noreply, State}.
 
@@ -163,6 +171,13 @@ code_change(_OldVersion, State, _Extra) ->
     {ok, State}.
 
 
+handle_config_change("stats", _, _, _, _) ->
+    exit(whereis(?MODULE), config_change),
+    remove_handler;
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
+
 new_value(incremental, Value, null) ->
     Value;
 new_value(incremental, Value, Current) ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_users_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_users_db.erl b/src/couch_users_db.erl
index 9fe069f..76acfee 100644
--- a/src/couch_users_db.erl
+++ b/src/couch_users_db.erl
@@ -63,7 +63,7 @@ save_doc(#doc{body={Body}} = Doc) ->
     undefined ->
         Doc;
     ClearPassword ->
-        Iterations = list_to_integer(couch_config:get("couch_httpd_auth", "iterations", "1000")),
+        Iterations = list_to_integer(config:get("couch_httpd_auth", "iterations", "1000")),
         Salt = couch_uuids:random(),
         DerivedKey = couch_passwords:pbkdf2(ClearPassword, Salt, Iterations),
         Body0 = [{?PASSWORD_SCHEME, ?PBKDF2}, {?ITERATIONS, Iterations}|Body],

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a7d16632/src/couch_uuids.erl
----------------------------------------------------------------------
diff --git a/src/couch_uuids.erl b/src/couch_uuids.erl
index ac9b463..3065938 100644
--- a/src/couch_uuids.erl
+++ b/src/couch_uuids.erl
@@ -13,6 +13,7 @@
 -include_lib("couch/include/couch_db.hrl").
 
 -behaviour(gen_server).
+-behaviour(config_listener).
 
 -export([start/0, stop/0]).
 -export([new/0, random/0, utc_random/0]).
@@ -20,6 +21,9 @@
 -export([init/1, terminate/2, code_change/3]).
 -export([handle_call/3, handle_cast/2, handle_info/2]).
 
+% config_listener api
+-export([handle_config_change/5]).
+
 start() ->
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 
@@ -44,9 +48,7 @@ utc_suffix(Suffix) ->
     list_to_binary(Prefix ++ Suffix).
 
 init([]) ->
-    ok = couch_config:register(
-        fun("uuids", _) -> gen_server:cast(?MODULE, change) end
-    ),
+    ok = config:listen_for_changes(?MODULE, nil),
     {ok, state()}.
 
 terminate(_Reason, _State) ->
@@ -74,12 +76,23 @@ handle_cast(stop, State) ->
 handle_cast(_Msg, State) ->
     {noreply, State}.
 
+handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
+    erlang:send_after(5000, self(), restart_config_listener),
+    {noreply, State};
+handle_info(restart_config_listener, State) ->
+    ok = config:listen_for_changes(?MODULE, nil),
+    {noreply, State};
 handle_info(_Info, State) ->
     {noreply, State}.
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
+handle_config_change("uuids", _, _, _, _) ->
+    {ok, gen_server:cast(?MODULE, change)};
+handle_config_change(_, _, _, _, _) ->
+    {ok, nil}.
+
 new_prefix() ->
     couch_util:to_hex((crypto:rand_bytes(13))).
 
@@ -87,14 +100,14 @@ inc() ->
     crypto:rand_uniform(1, 16#ffe).
 
 state() ->
-    AlgoStr = couch_config:get("uuids", "algorithm", "random"),
+    AlgoStr = config:get("uuids", "algorithm", "random"),
     case couch_util:to_existing_atom(AlgoStr) of
         random ->
             random;
         utc_random ->
             utc_random;
         utc_id ->
-            UtcIdSuffix = couch_config:get("uuids", "utc_id_suffix", ""),
+            UtcIdSuffix = config:get("uuids", "utc_id_suffix", ""),
             {utc_id, UtcIdSuffix};
         sequential ->
             {sequential, new_prefix(), inc()};