From 2bc4be3dbf9e8ea3b67c62f2d99087ff4b43c17b Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Wed, 20 Aug 2008 13:55:41 +0000 Subject: Merge runtimeconfig branch back into trunk git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@687336 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/Makefile.am | 70 +++++++-- src/couchdb/couch.app.tpl.in | 4 +- src/couchdb/couch_db_update_notifier.erl | 2 - src/couchdb/couch_file.erl | 2 +- src/couchdb/couch_ft_query.erl | 32 ++-- src/couchdb/couch_httpd.erl | 99 +++++++++++- src/couchdb/couch_log.erl | 24 ++- src/couchdb/couch_query_servers.erl | 50 ++++-- src/couchdb/couch_server.erl | 38 +++-- src/couchdb/couch_server_sup.erl | 260 ++++++++++++++----------------- src/couchdb/couch_util.erl | 49 ++---- src/couchdb/couch_view.erl | 20 ++- 12 files changed, 397 insertions(+), 253 deletions(-) (limited to 'src/couchdb') 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. -- cgit v1.2.3