%% CouchDB %% Copyright (C) 2006 Damien Katz %% %% This program is free software; you can redistribute it and/or %% modify it under the terms of the GNU General Public License %% as published by the Free Software Foundation; either version 2 %% of the License, or (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -module(couch_server_sup). -behaviour(supervisor). -define(DEFAULT_INI, "couch.ini"). -export([start_link/1,stop/0]). %% supervisor callbacks -export([init/1]). start_link(IniFilename) -> case whereis(couch_server_sup) of undefined -> start_server(IniFilename); _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) -> case init:get_argument(pidfile) of {ok, [PidFile]} -> case file:write_file(PidFile, os:getpid()) of ok -> ok; Error -> io:format("Failed to write PID file ~s, error: ~p", [PidFile, Error]) 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}) end, {ok, Ini} = couch_util:parse_ini(binary_to_list(IniBin)), ConsoleStartupMsg = proplists:get_value({"Couch", "ConsoleStartupMsg"}, Ini, "CouchDB server starting"), LogLevel = list_to_atom(proplists:get_value({"Couch", "LogLevel"}, Ini, "error")), DbRootDir = proplists:get_value({"Couch", "DbRootDir"}, Ini, "."), HttpConfigFile = proplists:get_value({"Couch", "HttpConfigFile"}, Ini, "couch_httpd.conf"), 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, "undefined")), ServerArgs = [{remote_restart, RemoteRestart}], QueryServers = [{Lang, QueryExe} || {{"Couch Query Servers", Lang}, QueryExe} <- Ini], io:format("couch ~s (LogLevel=~s)~n", [couch_server:get_version(), LogLevel]), io:format("~s~n", [ConsoleStartupMsg]), process_flag(trap_exit, true), StartResult = (catch supervisor:start_link( {local, couch_server_sup}, couch_server_sup, {[LogFile, LogLevel], [DbRootDir, ServerArgs], [QueryServers], [HttpConfigFile], [UtilDriverDir], UpdateNotifierExes, FtSearchQueryServer})), ConfigInfo = io_lib:format("Config Info ~s:~n\tCurrentWorkingDir=~s~n" ++ "\tDbRootDir=~s~n" ++ "\tHttpConfigFile=~s~n" ++ "\tLogFile=~s~n" ++ "\tUtilDriverDir=~s~n" ++ "\tDbUpdateNotificationProcesses=~s~n" ++ "\tFullTextSearchQueryServer=~s~n" ++ "~s", [IniFilename, Cwd, DbRootDir, HttpConfigFile, LogFile, UtilDriverDir, UpdateNotifierExes, FtSearchQueryServer, [lists:flatten(io_lib:format("\t~s=~s~n", [Lang, QueryExe])) || {Lang, QueryExe} <- QueryServers]]), couch_log:debug("~s", [ConfigInfo]), case StartResult of {ok,_} -> % only output when startup was successful io:format("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. stop() -> catch exit(whereis(couch_server_sup), normal), couch_log:stop(). init({LogArgs, ServerArgs, QueryServerArgs, HttpdArgs, UtilArgs, UpdateNotifierExes, FtSearchQueryServer}) -> {ok, {{one_for_one, 10, 3600}, [{couch_log, {couch_log, start_link, LogArgs}, permanent, brutal_kill, worker, [couch_server]}, {couch_server, {couch_server, sup_start_link, ServerArgs}, permanent, brutal_kill, worker, [couch_server]}, {couch_util, {couch_util, start_link, UtilArgs}, permanent, brutal_kill, worker, [couch_util]}, {couch_query_servers, {couch_query_servers, start_link, QueryServerArgs}, permanent, brutal_kill, worker, [couch_query_servers]}, {httpd, {httpd, start_link, HttpdArgs}, permanent, 1000, supervisor, [httpd]}, {couch_db_update_event, {gen_event, start_link, [{local, couch_db_update}]}, permanent, 1000, supervisor, dynamic} ] ++ 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, [httpd]}] end }}.