mirror of
https://github.com/processone/ejabberd.git
synced 2024-07-21 00:19:53 +02:00
157 lines
4.6 KiB
Erlang
157 lines
4.6 KiB
Erlang
![]() |
%% Copyright (C) 2003 Joakim Greben<65> <jocke@gleipnir.com>.
|
|||
|
%% All rights reserved.
|
|||
|
%%
|
|||
|
%% Redistribution and use in source and binary forms, with or without
|
|||
|
%% modification, are permitted provided that the following conditions
|
|||
|
%% are met:
|
|||
|
%%
|
|||
|
%% 1. Redistributions of source code must retain the above copyright
|
|||
|
%% notice, this list of conditions and the following disclaimer.
|
|||
|
%% 2. Redistributions in binary form must reproduce the above
|
|||
|
%% copyright notice, this list of conditions and the following
|
|||
|
%% disclaimer in the documentation and/or other materials provided
|
|||
|
%% with the distribution.
|
|||
|
%%
|
|||
|
%% THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|||
|
%% OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|||
|
%% WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|||
|
%% ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|||
|
%% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|||
|
%% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|||
|
%% GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|||
|
%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|||
|
%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|||
|
%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|||
|
%% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
|
|||
|
-module(tcp_serv).
|
|||
|
-vsn("1.13").
|
|||
|
-author('jocke@gleipnir.com').
|
|||
|
-export([start_link/1, start_link/2, stop/1, stop/2]).
|
|||
|
-export([init/2, start_session/3]).
|
|||
|
-export([system_continue/3, system_terminate/4]).
|
|||
|
|
|||
|
-include("log.hrl").
|
|||
|
|
|||
|
-record(state, {
|
|||
|
%% int()
|
|||
|
max_sessions,
|
|||
|
%% {M, F, A}
|
|||
|
%% M = F = atom()
|
|||
|
%% A = [term()]
|
|||
|
session_handler,
|
|||
|
%% [pid()]
|
|||
|
session_list,
|
|||
|
%% socket()
|
|||
|
listen_socket,
|
|||
|
%% pid()
|
|||
|
parent,
|
|||
|
%% term()
|
|||
|
debug_info
|
|||
|
}).
|
|||
|
|
|||
|
%% Exported: start_link/{1,2}
|
|||
|
|
|||
|
start_link(Args) -> start_link(Args, 60000).
|
|||
|
|
|||
|
start_link(Args, Timeout) ->
|
|||
|
Pid = proc_lib:spawn_link(?MODULE, init, [self(), Args]),
|
|||
|
receive
|
|||
|
{Pid, started} -> {ok, Pid};
|
|||
|
{Pid, Reason} -> {error, Reason}
|
|||
|
after Timeout -> {error, timeout}
|
|||
|
end.
|
|||
|
|
|||
|
%% Exported: stop/{1,2}
|
|||
|
|
|||
|
stop(Pid) -> stop(Pid, 15000).
|
|||
|
|
|||
|
stop(Pid, Timeout) ->
|
|||
|
Pid ! {self(), stop},
|
|||
|
receive
|
|||
|
{Pid, Reply} -> Reply
|
|||
|
after
|
|||
|
Timeout -> {error, timeout}
|
|||
|
end.
|
|||
|
|
|||
|
%% Exported: init/2
|
|||
|
|
|||
|
init(Parent, [Port, MaxSessions, OptionList, SessionHandler]) ->
|
|||
|
process_flag(trap_exit, true),
|
|||
|
case gen_tcp:listen(Port, OptionList) of
|
|||
|
{ok, ListenSocket} ->
|
|||
|
self() ! start_session,
|
|||
|
Parent ! {self(), started},
|
|||
|
loop(#state{max_sessions = MaxSessions,
|
|||
|
session_handler = SessionHandler,
|
|||
|
session_list = [],
|
|||
|
listen_socket = ListenSocket,
|
|||
|
parent = Parent});
|
|||
|
Reason -> Parent ! {self(), {not_started, Reason}}
|
|||
|
end.
|
|||
|
|
|||
|
loop(#state{session_list = SessionList, listen_socket = ListenSocket,
|
|||
|
parent = Parent} = State) ->
|
|||
|
receive
|
|||
|
{From, stop} ->
|
|||
|
cleanup(State),
|
|||
|
From ! {self(), ok};
|
|||
|
start_session when length(SessionList) > State#state.max_sessions ->
|
|||
|
timer:sleep(5000),
|
|||
|
self() ! start_session,
|
|||
|
loop(State);
|
|||
|
start_session ->
|
|||
|
A = [self(), State#state.session_handler, ListenSocket],
|
|||
|
Pid = proc_lib:spawn_link(?MODULE, start_session, A),
|
|||
|
loop(State#state{session_list = [Pid|SessionList]});
|
|||
|
{'EXIT', Parent, Reason} ->
|
|||
|
cleanup(State),
|
|||
|
exit(Reason);
|
|||
|
{'EXIT', Pid, Reason} ->
|
|||
|
case lists:member(Pid, SessionList) of
|
|||
|
true ->
|
|||
|
PurgedSessionList = lists:delete(Pid, SessionList),
|
|||
|
loop(State#state{session_list = PurgedSessionList});
|
|||
|
false ->
|
|||
|
?ERROR_LOG({ignoring, {'EXIT', Pid, Reason}}),
|
|||
|
loop(State)
|
|||
|
end;
|
|||
|
{system, From, Request} ->
|
|||
|
sys:handle_system_msg(Request, From, Parent, ?MODULE,
|
|||
|
State#state.debug_info, State);
|
|||
|
UnknownMessage ->
|
|||
|
?ERROR_LOG({unknown_message, UnknownMessage}),
|
|||
|
loop(State)
|
|||
|
end.
|
|||
|
|
|||
|
cleanup(State) -> gen_tcp:close(State#state.listen_socket).
|
|||
|
|
|||
|
%% Exported: start_seesion/3
|
|||
|
|
|||
|
start_session(Parent, {M, F, A}, ListenSocket) ->
|
|||
|
case gen_tcp:accept(ListenSocket) of
|
|||
|
{ok, Socket} ->
|
|||
|
Parent ! start_session,
|
|||
|
case apply(M, F, [Socket|A]) of
|
|||
|
ok -> gen_tcp:close(Socket);
|
|||
|
{error, closed} -> ok;
|
|||
|
{error, Reason} ->
|
|||
|
?ERROR_LOG({M, F, Reason}),
|
|||
|
gen_tcp:close(Socket)
|
|||
|
end;
|
|||
|
{error, Reason} ->
|
|||
|
timer:sleep(5000),
|
|||
|
Parent ! start_session
|
|||
|
end.
|
|||
|
|
|||
|
%% Exported: system_continue/3
|
|||
|
|
|||
|
system_continue(Parent, DebugInfo, State) ->
|
|||
|
loop(State#state{parent = Parent, debug_info = DebugInfo}).
|
|||
|
|
|||
|
%% Exported: system_terminate/3
|
|||
|
|
|||
|
system_terminate(Reason, Parent, DebugInfo, State) ->
|
|||
|
cleanup(State),
|
|||
|
exit(Reason).
|