summaryrefslogtreecommitdiff
path: root/src/couchdb
diff options
context:
space:
mode:
Diffstat (limited to 'src/couchdb')
-rw-r--r--src/couchdb/Makefile.am70
-rw-r--r--src/couchdb/couch.app.tpl.in4
-rw-r--r--src/couchdb/couch_db_update_notifier.erl2
-rw-r--r--src/couchdb/couch_file.erl2
-rw-r--r--src/couchdb/couch_ft_query.erl32
-rw-r--r--src/couchdb/couch_httpd.erl99
-rw-r--r--src/couchdb/couch_log.erl24
-rw-r--r--src/couchdb/couch_query_servers.erl50
-rw-r--r--src/couchdb/couch_server.erl38
-rw-r--r--src/couchdb/couch_server_sup.erl260
-rw-r--r--src/couchdb/couch_util.erl49
-rw-r--r--src/couchdb/couch_view.erl20
12 files changed, 397 insertions, 253 deletions
diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am
index b7fe29ce..710ff1f0 100644
--- a/src/couchdb/Makefile.am
+++ b/src/couchdb/Makefile.am
@@ -7,12 +7,15 @@
## 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.
+## License for the specific language governing permissions and limitations under
+## the License.
ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS)
+devdocdir = $(localdocdir)/developer/couchdb
couchprivlibdir = $(localerlanglibdir)/couch-$(version)/priv/lib
+couchincludedir = $(localerlanglibdir)/couch-$(version)/include
+couchebindir = $(localerlanglibdir)/couch-$(version)/ebin
couchprivlib_LTLIBRARIES = couch_erl_driver.la
couch_erl_driver_la_SOURCES = couch_erl_driver.c
@@ -23,14 +26,24 @@ couch_erl_driver_la_LIBADD = -licuuc -licudata -licui18n
locallibbin_PROGRAMS = couchjs
couchjs_SOURCES = couch_js.c
-couchebindir = $(localerlanglibdir)/couch-$(version)/ebin
-couchincludedir = $(localerlanglibdir)/couch-$(version)/include
+couchinclude_DATA = couch_db.hrl
+
+couchebin_DATA = $(compiled_files)
-couch_file_collection = \
+dist_devdoc_DATA = $(doc_base) $(doc_modules)
+
+EXTRA_DIST = $(couch_files)
+
+CLEANFILES = $(compiled_files) $(doc_base) $(doc_modules) edoc-info
+
+source_files = \
cjson.erl \
couch_btree.erl \
+ couch_config.erl \
+ couch_config_writer.erl \
couch_db.erl \
couch_db_update_notifier.erl \
+ couch_db_update_notifier_sup.erl \
couch_doc.erl \
couch_event_sup.erl \
couch_file.erl \
@@ -47,12 +60,15 @@ couch_file_collection = \
couch_view.erl \
couch_db_updater.erl
-couchebin_DATA = \
+compiled_files = \
cjson.beam \
couch.app \
couch_btree.beam \
+ couch_config.beam \
+ couch_config_writer.beam \
couch_db.beam \
couch_db_update_notifier.beam \
+ couch_db_update_notifier_sup.beam \
couch_doc.beam \
couch_event_sup.beam \
couch_file.beam \
@@ -69,11 +85,36 @@ couchebin_DATA = \
couch_view.beam \
couch_db_updater.beam
-couchinclude_DATA = couch_db.hrl
-
-EXTRA_DIST = $(couch_file_collection) $(couchinclude_DATA)
+doc_base = \
+ erlang.png \
+ index.html \
+ modules-frame.html \
+ overview-summary.html \
+ packages-frame.html \
+ stylesheet.css
-CLEANFILES = $(couchebin_DATA)
+doc_modules = \
+ cjson.html \
+ couch_btree.html \
+ couch_config.html \
+ couch_config_writer.html \
+ couch_db.html \
+ couch_db_update_notifier.html \
+ couch_db_update_notifier_sup.html \
+ couch_doc.html \
+ couch_event_sup.html \
+ couch_file.html \
+ couch_ft_query.html \
+ couch_httpd.html \
+ couch_key_tree.html \
+ couch_log.html \
+ couch_query_servers.html \
+ couch_rep.html \
+ couch_server.html \
+ couch_server_sup.html \
+ couch_stream.html \
+ couch_util.html \
+ couch_view.html
couch.app: couch.app.tpl
sed -e "s|%package_name%|@package_name@|g" \
@@ -81,6 +122,15 @@ couch.app: couch.app.tpl
$@ < $<
chmod +x $@
+$(dist_devdoc_DATA): $(source_files)
+ for file in $(source_files); do \
+ if test -n "$$edoc_files"; then \
+ edoc_files="$$edoc_files, "; \
+ fi; \
+ edoc_files="$$edoc_files \"$$file\""; \
+ done; \
+ erl -noshell -run edoc_run files ["$$edoc_files"]
+
%.beam: %.erl
$(ERLC) $<
diff --git a/src/couchdb/couch.app.tpl.in b/src/couchdb/couch.app.tpl.in
index ba7f13c1..9766d0f9 100644
--- a/src/couchdb/couch.app.tpl.in
+++ b/src/couchdb/couch.app.tpl.in
@@ -17,6 +17,7 @@
couch_httpd,
couch_event_sup,
couch_db_update_notifier,
+ couch_db_update_notifier_sup,
couch_ft_query,
couch_log,
couch_rep]},
@@ -24,5 +25,6 @@
couch_server_sup,
couch_view,
couch_query_servers,
- couch_ft_query]},
+ couch_ft_query,
+ couch_db_update_notifier_sup]},
{applications,[kernel,stdlib,crypto,inets,mochiweb]}]}.
diff --git a/src/couchdb/couch_db_update_notifier.erl b/src/couchdb/couch_db_update_notifier.erl
index 96354620..f944264c 100644
--- a/src/couchdb/couch_db_update_notifier.erl
+++ b/src/couchdb/couch_db_update_notifier.erl
@@ -25,8 +25,6 @@
-export([start_link/1, notify/1]).
-export([init/1, terminate/2, handle_event/2, handle_call/2, handle_info/2, code_change/3,stop/1]).
--define(ERR_HANDLE, {Port, {exit_status, Status}} -> {stop, {unknown_error, Status}, {unknown_error, Status}, Port}).
-
start_link(Exec) ->
couch_event_sup:start_link(couch_db_update, {couch_db_update_notifier, make_ref()}, Exec).
diff --git a/src/couchdb/couch_file.erl b/src/couchdb/couch_file.erl
index 37e1d7ac..5ccb521b 100644
--- a/src/couchdb/couch_file.erl
+++ b/src/couchdb/couch_file.erl
@@ -394,7 +394,7 @@ handle_info(Info, Fd) ->
-should_close(Fd) ->
+should_close(_Fd) ->
case process_info(self(), links) of
{links, [_]} ->
% no linkers left (except our fd port). What about monitors?
diff --git a/src/couchdb/couch_ft_query.erl b/src/couchdb/couch_ft_query.erl
index 2d1b9fc5..67fa5948 100644
--- a/src/couchdb/couch_ft_query.erl
+++ b/src/couchdb/couch_ft_query.erl
@@ -13,14 +13,12 @@
-module(couch_ft_query).
-behaviour(gen_server).
--export([start_link/1, execute/2]).
+-export([start_link/0, execute/2]).
-export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3, stop/0]).
--define(ERR_HANDLE, {Port, {exit_status, Status}} -> {stop, {unknown_error, Status}, {unknown_error, Status}, Port}).
-
-start_link(QueryExec) ->
- gen_server:start_link({local, couch_ft_query}, couch_ft_query, QueryExec, []).
+start_link() ->
+ gen_server:start_link({local, couch_ft_query}, couch_ft_query, [], []).
stop() ->
exit(whereis(couch_ft_query), close).
@@ -28,15 +26,27 @@ stop() ->
execute(DatabaseName, QueryString) ->
gen_server:call(couch_ft_query, {ft_query, DatabaseName, QueryString}).
-init(QueryExec) ->
- Port = open_port({spawn, QueryExec}, [{line, 1000}, exit_status, hide]),
- {ok, Port}.
+init([]) ->
+ ok = couch_config:register(
+ fun({"Search", "QueryServer"}) ->
+ ?MODULE:stop()
+ end),
+
+ case couch_config:get({"Search", "QueryServer"}, none) of
+ none ->
+ {ok, none};
+ QueryExec ->
+ Port = open_port({spawn, QueryExec}, [{line, 1000}, exit_status, hide]),
+ {ok, Port}
+ end.
terminate(_Reason, _Server) ->
ok.
+handle_call({ft_query, _Database, _QueryText}, _From, none) ->
+ {reply, {error, no_full_test_query_specified_in_config}, none};
handle_call({ft_query, Database, QueryText}, _From, Port) ->
- %% send the database name
+ % send the database name
true = port_command(Port, Database ++ "\n"),
true = port_command(Port, QueryText ++ "\n"),
case get_line(Port) of
@@ -63,7 +73,9 @@ get_line(Port) ->
receive
{Port, {data, {eol, Line}}} ->
Line;
- ?ERR_HANDLE
+ % would love to use ?ERR_HANDLE here, but edoc doesn't like it.
+ % TODO: find a way to skip that.
+ {Port, {exit_status, Status}} -> {stop, {unknown_error, Status}, {unknown_error, Status}, Port}
end.
handle_cast(_Whatever, State) ->
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index 82b09c72..51959d7d 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -13,7 +13,7 @@
-module(couch_httpd).
-include("couch_db.hrl").
--export([start_link/3, stop/0, handle_request/2]).
+-export([start_link/0, stop/0, handle_request/1, handle_request/2]).
% Maximum size of document PUT request body (4GB)
-define(MAX_DOC_SIZE, (4*1024*1024*1024)).
@@ -38,18 +38,41 @@
group_level = 0
}).
-start_link(BindAddress, Port, DocumentRoot) ->
+start_link() ->
+ % 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", "BindAddress"}, any),
+ Port = couch_config:get({"HTTPd", "Port"}, "5984"),
+ DocumentRoot = couch_config:get({"HTTPd", "DocumentRoot"}, "../../share/www"),
+
+ % and off we go
Loop = fun (Req) -> apply(couch_httpd, handle_request, [Req, DocumentRoot]) end,
- mochiweb_http:start([
+ {ok, Pid} = mochiweb_http:start([
{loop, Loop},
{name, ?MODULE},
{ip, BindAddress},
{port, Port}
- ]).
+ ]),
+ ok = couch_config:register(
+ fun({"HTTPd", "BindAddress"}) ->
+ ?MODULE:stop();
+ ({"HTTPd", "Port"}) ->
+ ?MODULE:stop();
+ ({"HTTPd", "DocumentRoot"}) ->
+ ?MODULE:stop()
+ end, Pid),
+
+ {ok, Pid}.
stop() ->
mochiweb_http:stop(?MODULE).
+handle_request(config_change) ->
+ stop().
+
handle_request(Req, DocumentRoot) ->
% alias HEAD to GET as mochiweb takes care of stripping the body
Method = case Req:get(method) of
@@ -87,7 +110,7 @@ handle_request(Req, DocumentRoot) ->
Path,
Resp:get(code)
]).
-
+
handle_request(Req, DocumentRoot, Method, Path) ->
% Start = erlang:now(),
X = handle_request0(Req, DocumentRoot, Method, Path),
@@ -112,6 +135,8 @@ handle_request0(Req, DocumentRoot, Method, Path) ->
] ++ server_header(), <<>>})};
"/_utils/" ++ PathInfo ->
{ok, Req:serve_file(PathInfo, DocumentRoot, server_header())};
+ "/_config/" ++ Config ->
+ handle_config_request(Req, Method, {config, Config});
"/_" ++ _Path ->
throw({not_found, unknown_private_path});
"/favicon.ico" ->
@@ -771,7 +796,6 @@ handle_attachment_request(Req, Method, _DbName, Db, DocId, FileName)
handle_attachment_request(_Req, _Method, _DbName, _Db, _DocId, _FileName) ->
throw({method_not_allowed, "GET,HEAD,DELETE,PUT"}).
-
extract_header_rev(Req) ->
QueryRev = proplists:get_value("rev", Req:parse_qs()),
Etag = case Req:get_header_value("If-Match") of
@@ -787,6 +811,69 @@ extract_header_rev(Req) ->
throw({bad_request, "Document rev and etag have different values"})
end.
+% Config request handlers
+
+handle_config_request(_Req, Method, {config, Config}) ->
+ [Module, Key] = string:tokens(Config, "/"),
+ handle_config_request(_Req, Method, {[Module, Key]});
+
+
+% PUT /_config/Module/Key
+% "value"
+handle_config_request(_Req, 'PUT', {[Module, Key]}) ->
+ handle_config_request(_Req, 'POST', {[Module, Key]});
+
+% POST,PUT /_config/Module/Key
+% "value"
+
+handle_config_request(Req, 'POST', {[Module, Key]}) ->
+ Value = binary_to_list(Req:recv_body()),
+ ok = couch_config:store({Module, Key}, Value),
+ send_json(Req, 200, {obj, [
+ {ok, true},
+ {module, Module},
+ {key, Key},
+ {value, Value}
+ ]});
+
+% GET /_config/Module/Key
+handle_config_request(Req, 'GET', {[Module, Key]}) ->
+ case couch_config:get({Module, Key},null) of
+ null ->
+ throw({not_found, unknown_config_value});
+ Value ->
+ send_json(Req, 200, {obj, [
+ {ok, true},
+ {module, Module},
+ {key, Key},
+ {value, Value}
+ ]})
+ end;
+
+
+% DELETE /_config/Key
+handle_config_request(Req, 'DELETE', {[Module, Key]}) ->
+ case couch_config:get({Module, Key}, null) of
+ null ->
+ throw({not_found, unknown_config_value});
+ OldValue ->
+ couch_config:unset({Module, Key}),
+ send_json(Req, 200, {obj, [
+ {ok, true},
+ {module, Module},
+ {key, Key},
+ {old_value, OldValue}
+ ]})
+ end.
+
+
+% TODO:
+% POST,PUT /_config/
+% [{Key, Value}, {K2, V2}, {K3, V3}]
+%
+% POST,PUT/_config/Key?value=Value
+
+
% View request handling internals
reverse_key_default(nil) -> <<>>;
diff --git a/src/couchdb/couch_log.erl b/src/couchdb/couch_log.erl
index 95ddc47f..0c2f9df7 100644
--- a/src/couchdb/couch_log.erl
+++ b/src/couchdb/couch_log.erl
@@ -13,7 +13,7 @@
-module(couch_log).
-behaviour(gen_event).
--export([start_link/2,stop/0]).
+-export([start_link/0,stop/0]).
-export([debug_on/0,info_on/0,get_level/0,get_level_integer/0, set_level/1]).
-export([init/1, handle_event/2, terminate/2, code_change/3, handle_info/2, handle_call/2]).
@@ -34,15 +34,29 @@ level_atom(?LEVEL_DEBUG) -> debug;
level_atom(?LEVEL_TMI) -> tmi.
-start_link(Filename, Level) ->
- couch_event_sup:start_link({local, couch_log}, error_logger, couch_log, {Filename, Level}).
+start_link() ->
+ couch_event_sup:start_link({local, couch_log}, error_logger, couch_log, []).
stop() ->
couch_event_sup:stop(couch_log).
-init({Filename, Level}) ->
+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()
+ end),
+
+ Filename = couch_config:get({"Log", "File"}, "couchdb.log"),
+ Level = couch_config:get({"Log", "Level"},"info"),
+
{ok, Fd} = file:open(Filename, [append]),
- {ok, {Fd, level_integer(Level)}}.
+ {ok, {Fd, level_integer(list_to_atom(Level))}}.
debug_on() ->
get_level_integer() =< ?LEVEL_DEBUG.
diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl
index ffacbc26..0160e8c7 100644
--- a/src/couchdb/couch_query_servers.erl
+++ b/src/couchdb/couch_query_servers.erl
@@ -13,21 +13,17 @@
-module(couch_query_servers).
-behaviour(gen_server).
--export([start_link/1]).
+-export([start_link/0]).
-export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3,stop/0]).
-export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
-export([reduce/3, rereduce/3]).
--export([test/0, test/1]).
+-export([test/0]).
-include("couch_db.hrl").
-timeout() ->
- % hardcoded 5 sec timeout per document
- 5000.
-
-start_link(QueryServerList) ->
- gen_server:start_link({local, couch_query_servers}, couch_query_servers, QueryServerList, []).
+start_link() ->
+ gen_server:start_link({local, couch_query_servers}, couch_query_servers, [], []).
stop() ->
exit(whereis(couch_query_servers), close).
@@ -36,6 +32,14 @@ readline(Port) ->
readline(Port, []).
readline(Port, Acc) ->
+
+ case get(query_server_timeout) of
+ undefined ->
+ Timeout = list_to_integer(couch_config:get(
+ {"CouchDB Query Server Options", "QueryTimeout"}, "5000")),
+ put(timeout, Timeout);
+ Timeout -> ok
+ end,
receive
{Port, {data, {noeol, Data}}} ->
readline(Port, [Data|Acc]);
@@ -44,7 +48,7 @@ readline(Port, Acc) ->
{Port, Err} ->
catch port_close(Port),
throw({map_process_error, Err})
- after timeout() ->
+ after Timeout ->
catch port_close(Port),
throw({map_process_error, "map function timed out"})
end.
@@ -166,7 +170,21 @@ reduce(Lang, RedSrcs, KVs) ->
{ok, tuple_to_list(Results)}.
-init(QueryServerList) ->
+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({"CouchDB Query Server" ++ _, _}) ->
+ ?MODULE:stop()
+ end),
+
+ QueryServerList = couch_config:lookup_match(
+ {{"CouchDB Query Servers", '$1'}, '$2'}, []),
+
{ok, {QueryServerList, []}}.
terminate(_Reason, _Server) ->
@@ -216,18 +234,16 @@ handle_info({Port, {exit_status, Status}}, {QueryServerList, LangPorts}) ->
{noreply, {QueryServerList, lists:keydelete(Port, 2, LangPorts)}};
_ ->
?LOG_ERROR("Unknown linked port/process crash: ~p", [Port])
- end;
-handle_info(_Whatever, {Cmd, Ports}) ->
- {noreply, {Cmd, Ports}}.
+ end.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-test() ->
- test("../js/js -f main.js").
+% test() ->
+% test("../js/js -f main.js").
-test(Cmd) ->
- start_link(Cmd),
+test() ->
+ start_link(),
{ok, DocMap} = start_doc_map("javascript", ["function(doc) {if (doc[0] == 'a') return doc[1];}"]),
{ok, Results} = map_docs(DocMap, [#doc{body={"a", "b"}}, #doc{body={"c", "d"}},#doc{body={"a", "c"}}]),
io:format("Results: ~w~n", [Results]),
diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl
index b89f5abc..3fa23867 100644
--- a/src/couchdb/couch_server.erl
+++ b/src/couchdb/couch_server.erl
@@ -14,9 +14,9 @@
-behaviour(gen_server).
-behaviour(application).
--export([start/0,start/1,start/2,stop/0,stop/1]).
+-export([start/0,start/1,start/2,stop/0,stop/1,restart/0]).
-export([open/2,create/2,delete/1,all_databases/0,get_version/0]).
--export([init/1, handle_call/3,sup_start_link/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,remote_restart/0]).
@@ -31,18 +31,18 @@
}).
start() ->
- start("").
+ start(["couch.ini"]).
-start(IniFile) when is_atom(IniFile) ->
- couch_server_sup:start_link(atom_to_list(IniFile) ++ ".ini");
-start(IniNum) when is_integer(IniNum) ->
- couch_server_sup:start_link("couch" ++ integer_to_list(IniNum) ++ ".ini");
-start(IniFile) ->
- couch_server_sup:start_link(IniFile).
+start(IniFiles) ->
+ couch_server_sup:start_link(IniFiles).
start(_Type, _Args) ->
start().
+restart() ->
+ stop(),
+ start().
+
stop() ->
couch_server_sup:stop().
@@ -63,8 +63,8 @@ get_version() ->
"0.0.0"
end.
-sup_start_link(RootDir, Options) ->
- gen_server:start_link({local, couch_server}, couch_server, {RootDir, Options}, []).
+sup_start_link() ->
+ gen_server:start_link({local, couch_server}, couch_server, [], []).
open(DbName, Options) ->
gen_server:call(couch_server, {open, DbName, Options}).
@@ -89,7 +89,21 @@ check_dbname(#server{dbname_regexp=RegExp}, DbName) ->
get_full_filename(Server, DbName) ->
filename:join([Server#server.root_dir, "./" ++ DbName ++ ".couch"]).
-init({RootDir, Options}) ->
+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.
+
+ RootDir = couch_config:get({"CouchDB", "RootDirectory"}, "."),
+ Options = couch_config:get({"CouchDB", "ServerOptions"}, []),
+ Self = self(),
+ ok = couch_config:register(
+ fun({"CouchDB", "RootDirectory"}) ->
+ exit(Self, config_change);
+ ({"CouchDB", "ServerOptions"}) ->
+ exit(Self, config_change)
+ end),
{ok, RegExp} = regexp:parse("^[a-z][a-z0-9\\_\\$()\\+\\-\\/]*$"),
ets:new(couch_dbs_by_name, [set, private, named_table]),
ets:new(couch_dbs_by_pid, [set, private, named_table]),
diff --git a/src/couchdb/couch_server_sup.erl b/src/couchdb/couch_server_sup.erl
index 5d4099bf..b90ec07e 100644
--- a/src/couchdb/couch_server_sup.erl
+++ b/src/couchdb/couch_server_sup.erl
@@ -13,35 +13,31 @@
-module(couch_server_sup).
-behaviour(supervisor).
--define(DEFAULT_INI, "couch.ini").
--export([start_link/1,stop/0]).
+-export([start_link/1,stop/0,couch_config_start_link_wrapper/2,start_primary_services/0]).
-include("couch_db.hrl").
%% supervisor callbacks
-export([init/1]).
-start_link(IniFilename) ->
+start_link(IniFiles) ->
case whereis(couch_server_sup) of
undefined ->
- start_server(IniFilename);
+ start_server(IniFiles);
_Else ->
{error, already_started}
end.
-start_server("") ->
- % no ini file specified, check the command line args
- IniFile =
- case init:get_argument(couchini) of
- {ok, [CmdLineIniFilename]} ->
- CmdLineIniFilename;
- _Else ->
- ?DEFAULT_INI
- end,
- start_server(IniFile);
-start_server(InputIniFilename) ->
+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]} ->
case file:write_file(PidFile, os:getpid()) of
@@ -50,146 +46,124 @@ start_server(InputIniFilename) ->
end;
_ -> ok
end,
-
- {ok, Cwd} = file:get_cwd(),
- IniFilename = couch_util:abs_pathname(InputIniFilename),
- IniBin =
- case file:read_file(IniFilename) of
- {ok, IniBin0} ->
- IniBin0;
- {error, enoent} ->
- Msg = io_lib:format("Couldn't find server configuration file ~s.", [InputIniFilename]),
- io:format("~s~n", [Msg]),
- throw({startup_error, Msg})
+
+ {ok, ConfigPid} = couch_config:start_link(IniFiles),
+
+ LogLevel = couch_config:get({"Log", "Level"}, "info"),
+ % announce startup
+ io:format("Apache CouchDB ~s (LogLevel=~s) is starting.~n", [
+ couch_server:get_version(),
+ LogLevel
+ ]),
+ case LogLevel of
+ "debug" ->
+ io:format("Configuration Settings ~p:~n", [IniFiles]),
+ [io:format(" [~s] ~s=~p~n", [Module, Variable, Value])
+ || {{Module, Variable}, Value} <- couch_config:all()];
+ _ -> ok
end,
- {ok, Ini} = couch_util:parse_ini(binary_to_list(IniBin)),
-
- ConsoleStartupMsg = proplists:get_value({"Couch", "ConsoleStartupMsg"}, Ini, "Apache CouchDB is starting."),
- LogLevel = list_to_atom(proplists:get_value({"Couch", "LogLevel"}, Ini, "error")),
- DbRootDir = proplists:get_value({"Couch", "DbRootDir"}, Ini, "."),
- BindAddress = proplists:get_value({"Couch", "BindAddress"}, Ini, any),
- Port = proplists:get_value({"Couch", "Port"}, Ini, 5984),
- DocumentRoot = proplists:get_value({"Couch", "DocumentRoot"}, Ini, "share/www"),
- LogFile = proplists:get_value({"Couch", "LogFile"}, Ini, "couchdb.log"),
- UtilDriverDir = proplists:get_value({"Couch", "UtilDriverDir"}, Ini, ""),
- UpdateNotifierExes = proplists:get_all_values({"Couch", "DbUpdateNotificationProcess"}, Ini),
- FtSearchQueryServer = proplists:get_value({"Couch", "FullTextSearchQueryServer"}, Ini, ""),
- RemoteRestart = list_to_atom(proplists:get_value({"Couch", "AllowRemoteRestart"}, Ini, "false")),
- MaxDbsOpen = proplists:get_value({"Couch", "MaxDbsOpen"}, Ini, 100),
- ServerOptions = [{remote_restart, RemoteRestart}, {max_dbs_open, MaxDbsOpen}],
- QueryServers = [{Lang, QueryExe} || {{"Couch Query Servers", Lang}, QueryExe} <- Ini],
-
- ChildProcesses =
- [{couch_log,
- {couch_log, start_link, [LogFile, LogLevel]},
+
+ LibDir =
+ case couch_config:get({"CouchDB", "UtilDriverDir"}, null) of
+ null ->
+ filename:join(code:priv_dir(couch), "lib");
+ LibDir0 -> LibDir0
+ end,
+
+ ok = couch_util:start_driver(LibDir),
+
+ BaseServices =
+ {{one_for_all, 10, 3600},
+ [{couch_config,
+ {couch_server_sup, couch_config_start_link_wrapper, [IniFiles, ConfigPid]},
permanent,
brutal_kill,
worker,
- [couch_server]},
- {couch_db_update_event,
- {gen_event, start_link, [{local, couch_db_update}]},
- permanent,
- 1000,
- supervisor,
dynamic},
- {couch_server,
- {couch_server, sup_start_link, [DbRootDir, ServerOptions]},
- permanent,
- brutal_kill,
- worker,
- [couch_server]},
- {couch_query_servers,
- {couch_query_servers, start_link, [QueryServers]},
- permanent,
- brutal_kill,
- worker,
- [couch_query_servers]},
- {couch_view,
- {couch_view, start_link, [DbRootDir]},
+ {couch_primary_services,
+ {couch_server_sup, start_primary_services, []},
permanent,
- brutal_kill,
- worker,
- [couch_view]},
- {couch_httpd,
- {couch_httpd, start_link, [BindAddress, Port, DocumentRoot]},
- permanent,
- 1000,
+ infinity,
supervisor,
- [couch_httpd]}
- ] ++
- lists:map(fun(UpdateNotifierExe) ->
- {UpdateNotifierExe,
- {couch_db_update_notifier, start_link, [UpdateNotifierExe]},
- permanent,
- 1000,
- supervisor,
- [couch_db_update_notifier]}
- end, UpdateNotifierExes)
- ++
- case FtSearchQueryServer of
- "" ->
- [];
- _ ->
- [{couch_ft_query,
- {couch_ft_query, start_link, [FtSearchQueryServer]},
- permanent,
- 1000,
- supervisor,
- [couch_ft_query]}]
- end,
-
- io:format("Apache CouchDB ~s (LogLevel=~s)~n", [couch_server:get_version(), LogLevel]),
- io:format("~s~n~n", [ConsoleStartupMsg]),
-
- couch_util:start_driver(UtilDriverDir),
+ [couch_server_sup]}
+ ]},
+
% ensure these applications are running
application:start(inets),
application:start(crypto),
- process_flag(trap_exit, true),
- StartResult = (catch supervisor:start_link(
- {local, couch_server_sup}, couch_server_sup, ChildProcesses)),
-
- ConfigInfo = io_lib:format("Config Info ~s:~n\tCurrentWorkingDir=~s~n" ++
- "\tDbRootDir=~s~n" ++
- "\tBindAddress=~p~n" ++
- "\tPort=~p~n" ++
- "\tDocumentRoot=~s~n" ++
- "\tLogFile=~s~n" ++
- "\tUtilDriverDir=~s~n" ++
- "\tDbUpdateNotificationProcesses=~s~n" ++
- "\tFullTextSearchQueryServer=~s~n" ++
- "~s",
- [IniFilename,
- Cwd,
- DbRootDir,
- BindAddress,
- Port,
- DocumentRoot,
- LogFile,
- UtilDriverDir,
- UpdateNotifierExes,
- FtSearchQueryServer,
- [lists:flatten(io_lib:format("\t~s=~s~n", [Lang, QueryExe])) || {Lang, QueryExe} <- QueryServers]]),
- ?LOG_DEBUG("~s", [ConfigInfo]),
-
- case StartResult of
- {ok,_} ->
- % only output when startup was successful
- %io:format("Find Futon, the management interface, at:~nhttp://~s:~s/_utils/index.html~n~n", [BindAddress, Port]),
- io:format("Apache CouchDB has started. Time to relax.~n");
- _ ->
- % Since we failed startup, unconditionally dump configuration data to console
- io:format("~s", [ConfigInfo]),
- ok
- end,
- process_flag(trap_exit, false),
- StartResult.
+ {ok, Pid} = supervisor:start_link(
+ {local, couch_server_sup}, couch_server_sup, BaseServices),
+
+ % launch the icu bridge
+ % just restart if one of the config settings change.
+
+ couch_config:register(
+ fun({"CouchDB", "UtilDriverDir"}) ->
+ ?MODULE:stop()
+ end, Pid),
+
+ unlink(ConfigPid),
+
+ io:format("Apache CouchDB has started. Time to relax.~n"),
+
+ {ok, Pid}.
+
+start_primary_services() ->
+ supervisor:start_link(couch_server_sup,
+ {{one_for_one, 10, 3600},
+ [{couch_log,
+ {couch_log, start_link, []},
+ permanent,
+ brutal_kill,
+ worker,
+ [couch_log]},
+ {couch_db_update_event,
+ {gen_event, start_link, [{local, couch_db_update}]},
+ permanent,
+ brutal_kill,
+ supervisor,
+ dynamic},
+ {couch_server,
+ {couch_server, sup_start_link, []},
+ permanent,
+ brutal_kill,
+ supervisor,
+ [couch_server]},
+ {couch_query_servers,
+ {couch_query_servers, start_link, []},
+ permanent,
+ brutal_kill,
+ supervisor,
+ [couch_query_servers]},
+ {couch_view,
+ {couch_view, start_link, []},
+ permanent,
+ brutal_kill,
+ supervisor,
+ [couch_view]},
+ {couch_httpd,
+ {couch_httpd, start_link, []},
+ permanent,
+ brutal_kill,
+ supervisor,
+ [couch_httpd]},
+ {couch_ft_query,
+ {couch_ft_query, start_link, []},
+ permanent,
+ brutal_kill,
+ supervisor,
+ [couch_ft_query]},
+ {couch_db_update_notifier_sup,
+ {couch_db_update_notifier_sup, start_link, []},
+ permanent,
+ brutal_kill,
+ supervisor,
+ [couch_db_update_notifier_sup]}]}).
stop() ->
- catch exit(whereis(couch_server_sup), normal),
- couch_log:stop().
+ catch exit(whereis(couch_server_sup), normal).
-init(ChildProcesses) ->
- {ok, {{one_for_one, 10, 3600}, ChildProcesses}}.
+init(ChildSpecs) ->
+ {ok, ChildSpecs}.
diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl
index 1d058005..83f3e9a5 100644
--- a/src/couchdb/couch_util.erl
+++ b/src/couchdb/couch_util.erl
@@ -13,7 +13,7 @@
-module(couch_util).
-export([start_driver/1]).
--export([parse_ini/1,should_flush/0, should_flush/1]).
+-export([should_flush/0, should_flush/1]).
-export([new_uuid/0, rand32/0, implode/2, collate/2, collate/3]).
-export([abs_pathname/1,abs_pathname/2, trim/1, ascii_lower/1]).
-export([encodeBase64/1, decodeBase64/1, to_hex/1]).
@@ -22,17 +22,18 @@
% arbitrarily chosen amount of memory to use before flushing to disk
-define(FLUSH_MAX_MEM, 10000000).
-start_driver("") ->
- start_driver(filename:join(code:priv_dir(couch), "lib"));
start_driver(LibDir) ->
+ % read config and register for configuration changes
+
case erl_ddll:load_driver(LibDir, "couch_erl_driver") of
- ok -> ok;
- {error, already_loaded} -> ok;
- {error, Error} -> exit(erl_ddll:format_error(Error))
+ ok ->
+ ok;
+ {error, already_loaded} ->
+ ok = erl_ddll:reload_driver(LibDir, "couch_erl_driver");
+ {error, Error} ->
+ exit(erl_ddll:format_error(Error))
end.
-
-
-
+
new_uuid() ->
to_hex(crypto:rand_bytes(16)).
@@ -158,34 +159,6 @@ implode([H|T], Sep, Acc) ->
% {{"Another Section", "oops"}, "\"it doesn't qet quoted strings with semis quite right"}]
%
-parse_ini(FileContents) ->
- {ok, Lines} = regexp:split(FileContents, "\r\n|\n|\r|\032"),
- {_, ParsedIniValues} =
- lists:foldl(fun(Line, {AccSectionName, AccValues}) ->
- case string:strip(Line) of
- "[" ++ Rest ->
- case regexp:split(Rest, "\\]") of
- {ok, [NewSectionName, ""]} ->
- {NewSectionName, AccValues};
- _Else -> % end bracket not at end, ignore this line
- {AccSectionName, AccValues}
- end;
- ";" ++ _Comment ->
- {AccSectionName, AccValues};
- Line2 ->
- case regexp:split(Line2, "=") of
- {ok, [_SingleElement]} -> % no "=" found, ignore this line
- {AccSectionName, AccValues};
- {ok, [""|_LineValues]} -> % line begins with "=", ignore
- {AccSectionName, AccValues};
- {ok, [ValueName|LineValues]} -> % yeehaw, got a line!
- RemainingLine = implode(LineValues, "="),
- {ok, [LineValue | _Rest]} = regexp:split(RemainingLine, " ;|\t;"), % removes comments
- {AccSectionName, [{{AccSectionName, ValueName}, LineValue} | AccValues]}
- end
- end
- end, {"", []}, Lines),
- {ok, lists:reverse(ParsedIniValues)}.
drv_port() ->
case get(couch_drv_port) of
@@ -294,5 +267,3 @@ enc(C) ->
dec(C) ->
62*?st(C,43) + ?st(C,47) + (C-59)*?st(C,48) - 69*?st(C,65) - 6*?st(C,97).
-
-
diff --git a/src/couchdb/couch_view.erl b/src/couchdb/couch_view.erl
index fc304dee..ec6b16cf 100644
--- a/src/couchdb/couch_view.erl
+++ b/src/couchdb/couch_view.erl
@@ -13,7 +13,7 @@
-module(couch_view).
-behaviour(gen_server).
--export([start_link/1,fold/4,fold/5,less_json/2, start_update_loop/3, start_temp_update_loop/5]).
+-export([start_link/0,fold/4,fold/5,less_json/2, start_update_loop/3, start_temp_update_loop/5]).
-export([init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2,code_change/3]).
-export([get_reduce_view/1, get_map_view/1,get_row_count/1,reduce_to_count/1, fold_reduce/7]).
@@ -43,10 +43,8 @@
{root_dir
}).
-start_link(RootDir) ->
- gen_server:start_link({local, couch_view}, couch_view, RootDir, []).
-
-
+start_link() ->
+ gen_server:start_link({local, couch_view}, couch_view, [], []).
get_temp_updater(DbName, Type, MapSrc, RedSrc) ->
{ok, Pid} = gen_server:call(couch_view, {start_temp_updater, DbName, Type, MapSrc, RedSrc}),
@@ -222,7 +220,15 @@ fold(#view{btree=Btree}, StartKey, Dir, Fun, Acc) ->
{ok, _AccResult} = couch_btree:fold(Btree, StartKey, Dir, WrapperFun, Acc).
-init(RootDir) ->
+init([]) ->
+ % read configuration settings and register for configuration changes
+ RootDir = couch_config:get({"CouchDB", "RootDirectory"}),
+ Self = self(),
+ ok = couch_config:register(
+ fun({"CouchDB", "RootDirectory"})->
+ exit(Self, config_change)
+ end),
+
couch_db_update_notifier:start_link(
fun({deleted, DbName}) ->
gen_server:cast(couch_view, {reset_indexes, DbName});
@@ -238,7 +244,7 @@ init(RootDir) ->
process_flag(trap_exit, true),
{ok, #server{root_dir=RootDir}}.
-terminate(_Reason, _) ->
+terminate(_Reason,_State) ->
ok.