1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(os_sup). 21-behaviour(gen_server). 22 23%% API 24-export([start_link/1, start/0, stop/0]). 25-export([error_report/2]). 26-export([enable/0, enable/2, disable/0, disable/2]). 27-export([param_type/2, param_default/1]). 28 29%% gen_server callbacks 30-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 31 terminate/2, code_change/3]). 32 33-record(state, {port, mfa, config, path, conf}). 34 35%%---------------------------------------------------------------------- 36%% API 37%%---------------------------------------------------------------------- 38 39start_link({win32, _OSname}) -> 40 Identifier = os_sup, 41 MFA = os_mon:get_env(os_sup, os_sup_mfa), 42 gen_server:start_link({local, os_sup_server}, nteventlog, 43 [Identifier, MFA], []); 44start_link(_OS) -> 45 gen_server:start_link({local, os_sup_server}, os_sup, [], []). 46 47start() -> % for testing 48 gen_server:start({local, os_sup_server}, os_sup, [], []). 49 50stop() -> 51 gen_server:call(os_sup_server, stop). 52 53error_report(LogData, Tag) -> 54 error_logger:error_report(Tag, LogData). 55 56enable() -> 57 command(enable). 58enable(Path, Conf) -> 59 command(enable, Path, Conf). 60 61disable() -> 62 command(disable). 63disable(Path, Conf) -> 64 command(disable, Path, Conf). 65 66param_type(os_sup_errortag, Val) when is_atom(Val) -> true; 67param_type(os_sup_own, Val) -> io_lib:printable_list(Val); 68param_type(os_sup_syslogconf, Val) -> io_lib:printable_list(Val); 69param_type(os_sup_enable, Val) when Val==true; Val==false -> true; 70param_type(os_sup_mfa, {Mod,Func,Args}) when is_atom(Mod), 71 is_atom(Func), 72 is_list(Args) -> true; 73param_type(_Param, _Val) -> false. 74 75param_default(os_sup_errortag) -> std_error; 76param_default(os_sup_own) -> "/etc"; 77param_default(os_sup_syslogconf) -> "/etc/syslog.conf"; 78param_default(os_sup_enable) -> true; 79param_default(os_sup_mfa) -> {os_sup, error_report, [std_error]}. 80 81%%---------------------------------------------------------------------- 82%% gen_server callbacks 83%%---------------------------------------------------------------------- 84 85init([]) -> 86 process_flag(trap_exit, true), 87 process_flag(priority, low), 88 89 case os:type() of 90 {unix, sunos} -> 91 init2(); 92 OS -> {stop, {unsupported_os, OS}} 93 end. 94 95init2() -> % Enable service if configured to do so 96 ConfigP = os_mon:get_env(os_sup, os_sup_enable), 97 case ConfigP of 98 true -> % ..yes -- do enable 99 Path = os_mon:get_env(os_sup, os_sup_own), 100 Conf = os_mon:get_env(os_sup, os_sup_syslogconf), 101 case enable(Path, Conf) of 102 ok -> 103 init3(#state{config=ConfigP, path=Path, conf=Conf}); 104 {error, Error} -> 105 {stop, {mod_syslog, Error}} 106 end; 107 false -> % ..no -- skip directly to init3/1 108 init3(#state{config=ConfigP}) 109 end. 110 111init3(State0) -> 112 Port = start_portprogram(), 113 114 %% Read the values of some configuration parameters 115 MFA = case os_mon:get_env(os_sup, os_sup_mfa) of 116 {os_sup, error_report, _} -> 117 Tag = os_mon:get_env(os_sup, os_sup_errortag), 118 {os_sup, error_report, [Tag]}; 119 MFA0 -> 120 MFA0 121 end, 122 123 {ok, State0#state{port=Port, mfa=MFA}}. 124 125handle_call(stop, _From, State) -> 126 {stop, normal, ok, State}. 127 128handle_cast(_Msg, State) -> 129 {noreply, State}. 130 131handle_info({_Port, {data, Data}}, #state{mfa={M,F,A}} = State) -> 132 apply(M, F, [Data | A]), 133 {noreply, State}; 134handle_info({'EXIT', _Port, Reason}, State) -> 135 {stop, {port_died, Reason}, State#state{port=not_used}}; 136handle_info(_Info, State) -> 137 {noreply, State}. 138 139terminate(_Reason, #state{port=Port} = State) -> 140 case State#state.config of 141 true when is_port(Port) -> 142 Port ! {self(), {command, "only_stdin"}}, 143 Res = disable(State#state.path, State#state.conf), 144 port_close(Port), 145 if 146 Res/="0" -> exit({mod_syslog, Res}); 147 true -> ok 148 end; 149 true -> 150 Res = disable(State#state.path, State#state.conf), 151 if 152 Res/="0" -> exit({mod_syslog, Res}); 153 true -> ok 154 end; 155 false when is_port(Port) -> 156 Port ! {self(), {command, "only_stdin"}}, 157 port_close(Port); 158 false -> 159 ok 160 end. 161 162%% os_mon-2.0 163%% For live downgrade to/upgrade from os_mon-1.8[.1] 164code_change(Vsn, PrevState, "1.8") -> 165 case Vsn of 166 167 %% Downgrade from this version 168 {down, _Vsn} -> 169 170 %% Find out the error tag used 171 {DefM, DefF, _} = param_default(os_sup_mfa), 172 Tag = case PrevState#state.mfa of 173 174 %% Default callback function is used, then use 175 %% the corresponding tag 176 {DefM, DefF, [Tag0]} -> 177 Tag0; 178 179 %% Default callback function is *not* used 180 %% (before the downgrade, that is) 181 %% -- check the configuration parameter 182 _ -> 183 case application:get_env(os_mon, 184 os_sup_errortag) of 185 {ok, Tag1} -> 186 Tag1; 187 188 %% (actually, if it has no value, 189 %% the process should terminate 190 %% according to 1.8.1 version, but that 191 %% seems too harsh here) 192 _ -> 193 std_error 194 end 195 end, 196 197 %% Downgrade to old state record 198 State = {state, PrevState#state.port, Tag}, 199 {ok, State}; 200 201 %% Upgrade to this version 202 _Vsn -> 203 204 {state, Port, Tag} = PrevState, 205 206 {DefM, DefF, _} = param_default(os_sup_mfa), 207 MFA = {DefM, DefF, [Tag]}, 208 209 %% We can safely assume the following configuration 210 %% parameters are defined, otherwise os_sup would never had 211 %% started in the first place. 212 %% (We can *not* safely assume they haven't been changed, 213 %% but that's a weakness inherited from the 1.8.1 version) 214 Path = application:get_env(os_mon, os_sup_own), 215 Conf = application:get_env(os_mon, os_sup_syslogconf), 216 217 %% Upgrade to this state record 218 State = #state{port=Port, mfa=MFA, config=true, 219 path=Path, conf=Conf}, 220 {ok, State} 221 end; 222code_change(_OldVsn, State, _Extra) -> 223 {ok, State}. 224 225%%---------------------------------------------------------------------- 226%% Internal functions 227%%---------------------------------------------------------------------- 228 229start_portprogram() -> 230 OwnPath = os_mon:get_env(os_sup, os_sup_own), 231 Command = 232 "\"" ++ filename:join([code:priv_dir(os_mon), "bin", "ferrule"]) ++ 233 "\" " ++ OwnPath, 234 open_port({spawn, Command}, [{packet, 2}]). 235 236%% os:cmd(cmd_str(enable)) should be done BEFORE starting os_sup 237%% os:cmd(cmd_str(disable)) should be done AFTER os_sup is terminated 238%% Both commands return "0" if successful 239command(Mode) -> 240 command(Mode, "/etc", "/etc/syslog.conf"). 241command(Mode, Path, Conf) -> 242 case os:cmd(cmd_str(Mode, Path, Conf)) of 243 "0" -> 244 ok; 245 Error -> 246 {error, Error} 247 end. 248 249cmd_str(Mode, Path, Conf) -> 250 %% modpgm modesw ownpath syslogconf 251 PrivDir = code:priv_dir(os_mon), 252 ModeSw = 253 case Mode of 254 enable -> 255 " otp "; 256 disable -> 257 " nootp " 258 end, 259 PrivDir ++ "/bin/mod_syslog" ++ ModeSw ++ Path ++ " " ++ Conf. 260