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(file, Config) of
139	undefined ->
140	    case proplists:get_value(proplist_file, Config) of
141		undefined ->
142		    httpd_conf:validate_properties(Config);
143		File ->
144		   try file:consult(File) of
145		       {ok, [PropList]} ->
146			   httpd_conf:validate_properties(PropList)
147		   catch
148		       exit:_ ->
149			   throw({error,
150				  {could_not_consult_proplist_file, File}})
151		   end
152	    end;
153	File ->
154	    {ok, File}
155    end.
156
157httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
158  when is_tuple(Value)  ->
159    Address = proplists:get_value(bind_address, Config, any),
160    Port    = proplists:get_value(port, Config, 80),
161    Profile =  proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
162    httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile);
163
164%% In this case the AcceptTimeout and Debug will only have default values...
165httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
166    case httpd_conf:load(ConfigFile) of
167	{ok, ConfigList} ->
168	    case (catch httpd_conf:validate_properties(ConfigList)) of
169		{ok, Config} ->
170		    Address = proplists:get_value(bind_address, Config, any),
171		    Port    = proplists:get_value(port, Config, 80),
172		    Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
173		    AcceptTimeout =
174			proplists:get_value(accept_timeout, Config,
175					    AcceptTimeoutDef),
176		    Debug   =
177			proplists:get_value(debug, Config, DebugDef),
178		    httpd_child_spec([{file, ConfigFile} | Config],
179				     AcceptTimeout, Debug, Address, Port, Profile);
180		Error ->
181		    Error
182	    end;
183	Error ->
184	    Error
185    end.
186
187httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
188    case get_fd(Port) of
189	{ok, Fd} ->
190	    case Port == 0 orelse Fd =/= undefined of
191		true ->
192		    httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile);
193		false ->
194		    httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile)
195	    end;
196	Error ->
197	    Error
198    end.
199
200httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
201    case start_listen(Addr, Port, Config) of
202	{Pid, {NewPort, NewConfig, ListenSocket}} ->
203	    Name      = {httpd_instance_sup, Addr, NewPort, Profile},
204	    StartFunc = {httpd_instance_sup, start_link,
205			 [NewConfig, AcceptTimeout,
206			  {Pid, ListenSocket}, Debug]},
207	    Restart   = permanent,
208	    Shutdown  = infinity,
209	    Modules   = [httpd_instance_sup],
210	    Type      = supervisor,
211	    {Name, StartFunc, Restart, Shutdown, Type, Modules};
212	{Pid, {error, Reason}}  ->
213	    exit(Pid, normal),
214	    {error, Reason}
215    end.
216
217httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
218    Name = {httpd_instance_sup, Addr, Port, Profile},
219    StartFunc = {httpd_instance_sup, start_link,
220		 [Config, AcceptTimeout, Debug]},
221    Restart = permanent,
222    Shutdown = infinity,
223    Modules = [httpd_instance_sup],
224    Type = supervisor,
225    {Name, StartFunc, Restart, Shutdown, Type, Modules}.
226
227
228mk_tuple_list([]) ->
229    [];
230mk_tuple_list([H={_,_}|T]) ->
231    [H|mk_tuple_list(T)];
232mk_tuple_list(F) ->
233    [{file, F}].
234
235error_msg(F, A) ->
236    error_logger:error_msg(F ++ "~n", A).
237
238listen(Address, Port, Config)  ->
239    try socket_type(Config) of
240	SocketType ->
241	    case http_transport:start(SocketType) of
242		ok ->
243		    {ok, Fd} = get_fd(Port),
244		    IpFamily =  proplists:get_value(ipfamily, Config, inet),
245		    case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of
246			{ok, ListenSocket} ->
247			    NewConfig = proplists:delete(port, Config),
248			    {NewPort, _} = http_transport:sockname(SocketType, ListenSocket),
249			    {NewPort, [{port, NewPort} | NewConfig], ListenSocket};
250			{error, Reason} ->
251			    {error, {listen, Reason}}
252		    end;
253		{error, Reason} ->
254		    {error, {socket_start_failed, Reason}}
255	    end
256    catch
257	_:Reason ->
258	    {error, {socket_start_failed, Reason}}
259    end.
260
261start_listen(Address, Port, Config) ->
262    Pid = listen_owner(Address, Port, Config),
263    receive
264	{Pid, Result} ->
265	    {Pid, Result}
266    end.
267
268listen_owner(Address, Port, Config) ->
269    spawn(?MODULE, listen_init, [self(), Address, Port, Config]).
270
271listen_init(From, Address, Port, Config) ->
272    process_flag(trap_exit, true),
273    Result = listen(Address, Port, Config),
274    From ! {self(), Result},
275    listen_loop().
276
277listen_loop() ->
278    receive
279	{'EXIT', _, _} ->
280	    ok
281    end.
282
283socket_type(Config) ->
284    SocketType = proplists:get_value(socket_type, Config, ip_comm),
285    socket_type(SocketType, Config).
286
287socket_type(ip_comm = SocketType, _) ->
288    SocketType;
289socket_type({ip_comm, _} = SocketType, _) ->
290    SocketType;
291socket_type({essl, _} = SocketType, _) ->
292    SocketType;
293socket_type(_, Config) ->
294    {essl, ssl_config(Config)}.
295
296%%% Backwards compatibility
297ssl_config(Config) ->
298    ssl_certificate_key_file(Config) ++
299	ssl_verify_client(Config) ++
300	ssl_ciphers(Config) ++
301	ssl_password(Config) ++
302	ssl_verify_depth(Config) ++
303	ssl_ca_certificate_file(Config).
304
305ssl_certificate_key_file(Config) ->
306    case proplists:get_value(ssl_certificate_key_file, Config) of
307	undefined ->
308	    [];
309	SSLCertificateKeyFile ->
310	    [{keyfile,SSLCertificateKeyFile}]
311    end.
312
313ssl_verify_client(Config) ->
314    case proplists:get_value(ssl_verify_client, Config) of
315	undefined ->
316	    [];
317	SSLVerifyClient ->
318	    [{verify,SSLVerifyClient}]
319    end.
320
321ssl_ciphers(Config) ->
322    case proplists:get_value(ssl_ciphers, Config) of
323	undefined ->
324	    [];
325	Ciphers ->
326	    [{ciphers, Ciphers}]
327    end.
328
329ssl_password(Config) ->
330    case  proplists:get_value(ssl_password_callback_module, Config) of
331	undefined ->
332	    [];
333	Module ->
334	    case proplists:get_value(ssl_password_callback_function, Config) of
335		undefined ->
336		    [];
337		Function ->
338		    Args = case  proplists:get_value(ssl_password_callback_arguments, Config) of
339			       undefined ->
340				   [];
341			       Arguments  ->
342				   [Arguments]
343			   end,
344		    Password = apply(Module, Function, Args),
345		    [{password, Password}]
346	    end
347    end.
348
349ssl_verify_depth(Config) ->
350    case proplists:get_value(ssl_verify_client_depth, Config) of
351	undefined ->
352	    [];
353	Depth ->
354	    [{depth, Depth}]
355    end.
356
357ssl_ca_certificate_file(Config) ->
358    case proplists:get_value(ssl_ca_certificate_file, Config) of
359	undefined ->
360	    [];
361	File ->
362	    [{cacertfile, File}]
363    end.
364
365get_fd(0) ->
366    {ok, undefined};
367get_fd(Port) ->
368    FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)),
369    case init:get_argument(FdKey) of
370	{ok, [[Value]]} ->
371	    case (catch list_to_integer(Value)) of
372		N when is_integer(N) ->
373		    {ok, N};
374		_ ->
375		    {error, {bad_descriptor, Value}}
376	    end;
377	_ ->
378	    {ok, undefined}
379    end.
380