%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(os_sup). -behaviour(gen_server). %% API -export([start_link/1, start/0, stop/0]). -export([error_report/2]). -export([enable/0, enable/2, disable/0, disable/2]). -export([param_type/2, param_default/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, {port, mfa, config, path, conf}). %%---------------------------------------------------------------------- %% API %%---------------------------------------------------------------------- start_link({win32, _OSname}) -> Identifier = os_sup, MFA = os_mon:get_env(os_sup, os_sup_mfa), gen_server:start_link({local, os_sup_server}, nteventlog, [Identifier, MFA], []); start_link(_OS) -> gen_server:start_link({local, os_sup_server}, os_sup, [], []). start() -> % for testing gen_server:start({local, os_sup_server}, os_sup, [], []). stop() -> gen_server:call(os_sup_server, stop). error_report(LogData, Tag) -> error_logger:error_report(Tag, LogData). enable() -> command(enable). enable(Path, Conf) -> command(enable, Path, Conf). disable() -> command(disable). disable(Path, Conf) -> command(disable, Path, Conf). param_type(os_sup_errortag, Val) when is_atom(Val) -> true; param_type(os_sup_own, Val) -> io_lib:printable_list(Val); param_type(os_sup_syslogconf, Val) -> io_lib:printable_list(Val); param_type(os_sup_enable, Val) when Val==true; Val==false -> true; param_type(os_sup_mfa, {Mod,Func,Args}) when is_atom(Mod), is_atom(Func), is_list(Args) -> true; param_type(_Param, _Val) -> false. param_default(os_sup_errortag) -> std_error; param_default(os_sup_own) -> "/etc"; param_default(os_sup_syslogconf) -> "/etc/syslog.conf"; param_default(os_sup_enable) -> true; param_default(os_sup_mfa) -> {os_sup, error_report, [std_error]}. %%---------------------------------------------------------------------- %% gen_server callbacks %%---------------------------------------------------------------------- init([]) -> process_flag(trap_exit, true), process_flag(priority, low), case os:type() of {unix, sunos} -> init2(); OS -> {stop, {unsupported_os, OS}} end. init2() -> % Enable service if configured to do so ConfigP = os_mon:get_env(os_sup, os_sup_enable), case ConfigP of true -> % ..yes -- do enable Path = os_mon:get_env(os_sup, os_sup_own), Conf = os_mon:get_env(os_sup, os_sup_syslogconf), case enable(Path, Conf) of ok -> init3(#state{config=ConfigP, path=Path, conf=Conf}); {error, Error} -> {stop, {mod_syslog, Error}} end; false -> % ..no -- skip directly to init3/1 init3(#state{config=ConfigP}) end. init3(State0) -> Port = start_portprogram(), %% Read the values of some configuration parameters MFA = case os_mon:get_env(os_sup, os_sup_mfa) of {os_sup, error_report, _} -> Tag = os_mon:get_env(os_sup, os_sup_errortag), {os_sup, error_report, [Tag]}; MFA0 -> MFA0 end, {ok, State0#state{port=Port, mfa=MFA}}. handle_call(stop, _From, State) -> {stop, normal, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({_Port, {data, Data}}, #state{mfa={M,F,A}} = State) -> apply(M, F, [Data | A]), {noreply, State}; handle_info({'EXIT', _Port, Reason}, State) -> {stop, {port_died, Reason}, State#state{port=not_used}}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, #state{port=Port} = State) -> case State#state.config of true when is_port(Port) -> Port ! {self(), {command, "only_stdin"}}, Res = disable(State#state.path, State#state.conf), port_close(Port), if Res/="0" -> exit({mod_syslog, Res}); true -> ok end; true -> Res = disable(State#state.path, State#state.conf), if Res/="0" -> exit({mod_syslog, Res}); true -> ok end; false when is_port(Port) -> Port ! {self(), {command, "only_stdin"}}, port_close(Port); false -> ok end. %% os_mon-2.0 %% For live downgrade to/upgrade from os_mon-1.8[.1] code_change(Vsn, PrevState, "1.8") -> case Vsn of %% Downgrade from this version {down, _Vsn} -> %% Find out the error tag used {DefM, DefF, _} = param_default(os_sup_mfa), Tag = case PrevState#state.mfa of %% Default callback function is used, then use %% the corresponding tag {DefM, DefF, [Tag0]} -> Tag0; %% Default callback function is *not* used %% (before the downgrade, that is) %% -- check the configuration parameter _ -> case application:get_env(os_mon, os_sup_errortag) of {ok, Tag1} -> Tag1; %% (actually, if it has no value, %% the process should terminate %% according to 1.8.1 version, but that %% seems too harsh here) _ -> std_error end end, %% Downgrade to old state record State = {state, PrevState#state.port, Tag}, {ok, State}; %% Upgrade to this version _Vsn -> {state, Port, Tag} = PrevState, {DefM, DefF, _} = param_default(os_sup_mfa), MFA = {DefM, DefF, [Tag]}, %% We can safely assume the following configuration %% parameters are defined, otherwise os_sup would never had %% started in the first place. %% (We can *not* safely assume they haven't been changed, %% but that's a weakness inherited from the 1.8.1 version) Path = application:get_env(os_mon, os_sup_own), Conf = application:get_env(os_mon, os_sup_syslogconf), %% Upgrade to this state record State = #state{port=Port, mfa=MFA, config=true, path=Path, conf=Conf}, {ok, State} end; code_change(_OldVsn, State, _Extra) -> {ok, State}. %%---------------------------------------------------------------------- %% Internal functions %%---------------------------------------------------------------------- start_portprogram() -> OwnPath = os_mon:get_env(os_sup, os_sup_own), Command = "\"" ++ filename:join([code:priv_dir(os_mon), "bin", "ferrule"]) ++ "\" " ++ OwnPath, open_port({spawn, Command}, [{packet, 2}]). %% os:cmd(cmd_str(enable)) should be done BEFORE starting os_sup %% os:cmd(cmd_str(disable)) should be done AFTER os_sup is terminated %% Both commands return "0" if successful command(Mode) -> command(Mode, "/etc", "/etc/syslog.conf"). command(Mode, Path, Conf) -> case os:cmd(cmd_str(Mode, Path, Conf)) of "0" -> ok; Error -> {error, Error} end. cmd_str(Mode, Path, Conf) -> %% modpgm modesw ownpath syslogconf PrivDir = code:priv_dir(os_mon), ModeSw = case Mode of enable -> " otp "; disable -> " nootp " end, PrivDir ++ "/bin/mod_syslog" ++ ModeSw ++ Path ++ " " ++ Conf.