1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-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%%
21%%----------------------------------------------------------------------
22%% Purpose: The top supervisor for the http server (httpd) hangs under
23%%          inets_sup.
24%%----------------------------------------------------------------------
25
26-module(httpd_sup).
27
28-behaviour(supervisor).
29
30%% Internal application API
31-export([start_link/1, start_link/2]).
32-export([start_child/1, restart_child/3, stop_child/3]).
33
34%% Supervisor callback
35-export([init/1]).
36
37-export([listen_init/4]).
38
39-define(TIMEOUT, 15000).
40-include("httpd_internal.hrl").
41
42%%%=========================================================================
43%%%  API
44%%%=========================================================================
45start_link(HttpdServices) ->
46    supervisor:start_link({local, ?MODULE}, ?MODULE, [HttpdServices]).
47
48start_link(HttpdServices, stand_alone) ->
49    supervisor:start_link(?MODULE, [HttpdServices]).
50
51start_child(Config) ->
52    try httpd_config(Config) of
53	{ok, NewConfig} ->
54	    Spec = httpd_child_spec(NewConfig, ?TIMEOUT, []),
55	    case supervisor:start_child(?MODULE, Spec) of
56		{error, {invalid_child_spec, Error}} ->
57		    Error;
58		Other ->
59		    Other
60	    end
61    catch
62	throw:Error ->
63	    Error
64    end.
65
66
67restart_child(Address, Port, Profile) ->
68    Name = id(Address, Port, Profile),
69    case supervisor:terminate_child(?MODULE, Name) of
70	ok ->
71             supervisor:restart_child(?MODULE, Name);
72	Error ->
73             Error
74     end.
75
76stop_child(Address, Port, Profile) ->
77    Name = id(Address, Port, Profile),
78    case supervisor:terminate_child(?MODULE, Name) of
79         ok ->
80	    supervisor:delete_child(?MODULE, Name);
81         Error ->
82            Error
83    end.
84
85id(Address, Port, Profile) ->
86    {httpd_instance_sup, Address, Port, Profile}.
87
88
89%%%=========================================================================
90%%%  Supervisor callback
91%%%=========================================================================
92init([HttpdServices]) ->
93    RestartStrategy = one_for_one,
94    MaxR = 10,
95    MaxT = 3600,
96    Children = child_specs(HttpdServices, []),
97    {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
98
99
100%%%=========================================================================
101%%%  Internal functions
102%%%=========================================================================
103
104%% The format of the httpd service is:
105%% httpd_service() -> {httpd,httpd()}
106%% httpd()         -> [httpd_config()] | file()
107%% httpd_config()  -> {file,file()} |
108%%                    {debug,debug()} |
109%%                    {accept_timeout,integer()}
110%% debug()         -> disable | [debug_options()]
111%% debug_options() -> {all_functions,modules()} |
112%%                    {exported_functions,modules()} |
113%%                    {disable,modules()}
114%% modules()       -> [atom()]
115
116
117child_specs([], Acc) ->
118    Acc;
119child_specs([{httpd, HttpdService} | Rest], Acc) ->
120    NewHttpdService = (catch mk_tuple_list(HttpdService)),
121    case catch child_spec(NewHttpdService) of
122	{error, Reason} ->
123	    error_msg("Failed to start service: ~n~p ~n due to: ~p~n",
124		      [HttpdService, Reason]),
125	    child_specs(Rest, Acc);
126	Spec ->
127	    child_specs(Rest, [Spec | Acc])
128    end.
129
130child_spec(HttpdService) ->
131    {ok, Config}  = httpd_config(HttpdService),
132    Debug         = proplists:get_value(debug, Config, []),
133    AcceptTimeout = proplists:get_value(accept_timeout, Config, 15000),
134    httpd_util:valid_options(Debug, AcceptTimeout, Config),
135    httpd_child_spec(Config, AcceptTimeout, Debug).
136
137httpd_config([Value| _] = Config) when is_tuple(Value) ->
138    case proplists:get_value(proplist_file, Config) of
139        undefined ->
140            httpd_conf:validate_properties(Config);
141        File ->
142            try file:consult(File) of
143                {ok, [PropList]} ->
144                    httpd_conf:validate_properties(PropList)
145            catch
146                exit:_ ->
147                    throw({error,
148                           {could_not_consult_proplist_file, File}})
149            end
150    end.
151
152httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
153  when is_tuple(Value)  ->
154    Address = proplists:get_value(bind_address, Config, any),
155    Port    = proplists:get_value(port, Config, 80),
156    Profile =  proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
157    httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile).
158
159
160httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
161    case get_fd(Port) of
162	{ok, Fd} ->
163	    case Port == 0 orelse Fd =/= undefined of
164		true ->
165		    httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile);
166		false ->
167		    httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile)
168	    end;
169	Error ->
170	    Error
171    end.
172
173httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
174    case start_listen(Addr, Port, Config) of
175	{Pid, {NewPort, NewConfig, ListenSocket}} ->
176	    Name      = {httpd_instance_sup, Addr, NewPort, Profile},
177	    StartFunc = {httpd_instance_sup, start_link,
178			 [NewConfig, AcceptTimeout,
179			  {Pid, ListenSocket}, Debug]},
180	    Restart   = permanent,
181	    Shutdown  = infinity,
182	    Modules   = [httpd_instance_sup],
183	    Type      = supervisor,
184	    {Name, StartFunc, Restart, Shutdown, Type, Modules};
185	{Pid, {error, Reason}}  ->
186	    exit(Pid, normal),
187	    {error, Reason}
188    end.
189
190httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
191    Name = {httpd_instance_sup, Addr, Port, Profile},
192    StartFunc = {httpd_instance_sup, start_link,
193		 [Config, AcceptTimeout, Debug]},
194    Restart = permanent,
195    Shutdown = infinity,
196    Modules = [httpd_instance_sup],
197    Type = supervisor,
198    {Name, StartFunc, Restart, Shutdown, Type, Modules}.
199
200
201mk_tuple_list([]) ->
202    [];
203mk_tuple_list([H={_,_}|T]) ->
204    [H|mk_tuple_list(T)];
205mk_tuple_list(F) ->
206    [{file, F}].
207
208error_msg(F, A) ->
209    error_logger:error_msg(F ++ "~n", A).
210
211listen(Address, Port, Config)  ->
212    try socket_type(Config) of
213	SocketType ->
214	    case http_transport:start(SocketType) of
215		ok ->
216		    {ok, Fd} = get_fd(Port),
217		    IpFamily =  proplists:get_value(ipfamily, Config, inet),
218		    case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of
219			{ok, ListenSocket} ->
220			    NewConfig = proplists:delete(port, Config),
221			    {NewPort, _} = http_transport:sockname(SocketType, ListenSocket),
222			    {NewPort, [{port, NewPort} | NewConfig], ListenSocket};
223			{error, Reason} ->
224			    {error, {listen, Reason}}
225		    end;
226		{error, Reason} ->
227		    {error, {socket_start_failed, Reason}}
228	    end
229    catch
230	_:Reason ->
231	    {error, {socket_start_failed, Reason}}
232    end.
233
234start_listen(Address, Port, Config) ->
235    Pid = listen_owner(Address, Port, Config),
236    receive
237	{Pid, Result} ->
238	    {Pid, Result}
239    end.
240
241listen_owner(Address, Port, Config) ->
242    spawn(?MODULE, listen_init, [self(), Address, Port, Config]).
243
244listen_init(From, Address, Port, Config) ->
245    process_flag(trap_exit, true),
246    Result = listen(Address, Port, Config),
247    From ! {self(), Result},
248    listen_loop().
249
250listen_loop() ->
251    receive
252	{'EXIT', _, _} ->
253	    ok
254    end.
255
256socket_type(Config) ->
257    SocketType = proplists:get_value(socket_type, Config, ip_comm),
258    socket_type(SocketType, Config).
259
260socket_type(ip_comm = SocketType, _) ->
261    SocketType;
262socket_type({ip_comm, _} = SocketType, _) ->
263    SocketType;
264socket_type({essl, _} = SocketType, _) ->
265    SocketType;
266socket_type(_, Config) ->
267    {essl, ssl_config(Config)}.
268
269%%% Backwards compatibility
270ssl_config(Config) ->
271    ssl_certificate_key_file(Config) ++
272	ssl_verify_client(Config) ++
273	ssl_ciphers(Config) ++
274	ssl_password(Config) ++
275	ssl_verify_depth(Config) ++
276	ssl_ca_certificate_file(Config).
277
278ssl_certificate_key_file(Config) ->
279    case proplists:get_value(ssl_certificate_key_file, Config) of
280	undefined ->
281	    [];
282	SSLCertificateKeyFile ->
283	    [{keyfile,SSLCertificateKeyFile}]
284    end.
285
286ssl_verify_client(Config) ->
287    case proplists:get_value(ssl_verify_client, Config) of
288	undefined ->
289	    [];
290	SSLVerifyClient ->
291	    [{verify,SSLVerifyClient}]
292    end.
293
294ssl_ciphers(Config) ->
295    case proplists:get_value(ssl_ciphers, Config) of
296	undefined ->
297	    [];
298	Ciphers ->
299	    [{ciphers, Ciphers}]
300    end.
301
302ssl_password(Config) ->
303    case  proplists:get_value(ssl_password_callback_module, Config) of
304	undefined ->
305	    [];
306	Module ->
307	    case proplists:get_value(ssl_password_callback_function, Config) of
308		undefined ->
309		    [];
310		Function ->
311		    Args = case  proplists:get_value(ssl_password_callback_arguments, Config) of
312			       undefined ->
313				   [];
314			       Arguments  ->
315				   [Arguments]
316			   end,
317		    Password = apply(Module, Function, Args),
318		    [{password, Password}]
319	    end
320    end.
321
322ssl_verify_depth(Config) ->
323    case proplists:get_value(ssl_verify_client_depth, Config) of
324	undefined ->
325	    [];
326	Depth ->
327	    [{depth, Depth}]
328    end.
329
330ssl_ca_certificate_file(Config) ->
331    case proplists:get_value(ssl_ca_certificate_file, Config) of
332	undefined ->
333	    [];
334	File ->
335	    [{cacertfile, File}]
336    end.
337
338get_fd(0) ->
339    {ok, undefined};
340get_fd(Port) ->
341    FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)),
342    case init:get_argument(FdKey) of
343	{ok, [[Value]]} ->
344	    case (catch list_to_integer(Value)) of
345		N when is_integer(N) ->
346		    {ok, N};
347		_ ->
348		    {error, {bad_descriptor, Value}}
349	    end;
350	_ ->
351	    {ok, undefined}
352    end.
353