266 lines
6.2 KiB
Erlang
266 lines
6.2 KiB
Erlang
%% @doc
|
|
%% super simple telnet echo server daemon
|
|
%%
|
|
%% no noise yet
|
|
%%
|
|
%% pimps and hos model (from trash talk template):
|
|
%%
|
|
%% ```
|
|
%% t_sup
|
|
%% |
|
|
%% t_clients
|
|
%% |
|
|
%% +--- t_client_man pimp
|
|
%% |
|
|
%% +--- t_client_sup bottom bitch
|
|
%% |
|
|
%% +--- <0.1.0> (t_client) ho
|
|
%% +--- <0.2.0> (t_client) ho
|
|
%% +--- <0.3.0> (t_client) ho
|
|
%% '''
|
|
%%
|
|
%% each ho:
|
|
%%
|
|
%% <ul>
|
|
%% <li>Takes listen socket as arg</li>
|
|
%% <li>spawns next ho</li>
|
|
%% </ul>
|
|
%%
|
|
%% ```
|
|
%% start(ListenSocket) ->
|
|
%% t_client_sup:start_acceptor(ListenSocket).
|
|
%%
|
|
%% start_link(ListenSocket) ->
|
|
%% proc_lib:start_link(?MODULE, init, [self(), ListenSocket]).
|
|
%%
|
|
%% init(Parent, ListenSocket) ->
|
|
%% ok = io:format("~p Listening.~n", [self()]),
|
|
%% Debug = sys:debug_options([]),
|
|
%% ok = proc_lib:init_ack(Parent, {ok, self()}),
|
|
%% listen(Parent, Debug, ListenSocket).
|
|
|
|
|
|
%%
|
|
%% listen(Parent, Debug, ListenSocket) ->
|
|
%% case gen_tcp:accept(ListenSocket) of
|
|
%% {ok, Socket} ->
|
|
%% {ok, _} = start(ListenSocket),
|
|
%% {ok, Peer} = inet:peername(Socket),
|
|
%% ok = io:format("~p Connection accepted from: ~p~n", [self(), Peer]),
|
|
%% ok = t_client_man:enroll(),
|
|
%% State = #s{socket = Socket},
|
|
%% loop(Parent, Debug, State);
|
|
%% {error, closed} ->
|
|
%% ok = io:format("~p Retiring: Listen socket closed.~n", [self()]),
|
|
%% exit(normal)
|
|
%% end.
|
|
%% '''
|
|
%%
|
|
%% @end
|
|
-module(echod).
|
|
|
|
-include("./ansi.hrl").
|
|
-export([main/0]).
|
|
|
|
|
|
%% pimp state
|
|
-record(ps,
|
|
{lsock = none :: gen_tcp:socket(),
|
|
hos = [] :: [{pid(), reference()}],
|
|
ho_names = init_ho_names() :: [string()]}).
|
|
|
|
-type pimp_state() :: #ps{}.
|
|
|
|
|
|
%% ho state
|
|
-record(hs,
|
|
{name = none :: string(),
|
|
daddy = none :: pid(),
|
|
vagene = none :: gen_tcp:socket()}).
|
|
|
|
-type ho_state() :: #hs{}.
|
|
|
|
|
|
|
|
-spec main() -> no_return().
|
|
%% @doc
|
|
%% run the echo server daemon
|
|
|
|
main() ->
|
|
%% show the splash screen nigga
|
|
ok = splash(),
|
|
case gen_tcp:listen(6969, [binary, {packet, line}, {active, once}]) of
|
|
{ok, LSock} ->
|
|
%% we out here
|
|
%% we not wuz sockitz nigga
|
|
%% we **be** sockets
|
|
%% nigga
|
|
ok = pimp_logfln("Butters iz in the hizzouse: ~tp", [self()]),
|
|
pimp_loop(#ps{lsock = LSock});
|
|
{error, closed} ->
|
|
%% dont be pimp
|
|
io:format("PIMP ERROR: ~tp~n", [Error])
|
|
end.
|
|
|
|
|
|
|
|
-spec splash() -> ok.
|
|
%% @private
|
|
%% show splash screen
|
|
|
|
splash() ->
|
|
SplashStr = """
|
|
RBX example 2 telnet echo server daemon
|
|
"one" part of the many-to-one
|
|
""",
|
|
io:format("~s~n", [SplashStr]).
|
|
|
|
|
|
|
|
-spec pimp_loop(PimpState) -> NoReturn when
|
|
PimpState :: pimp_state(),
|
|
NoReturn :: no_return().
|
|
%% @private
|
|
%% runs in context of main
|
|
|
|
pimp_loop(PS = #ps{lsock = LSock, hos = Hos, ho_names = Names}) ->
|
|
%% blocks until client connects
|
|
case gen_tcp:accept(LSock) of
|
|
{ok, ASock} ->
|
|
{HoName, NewNames} = recycle_ho_names(Names),
|
|
InitHoState = #hs{name = HoName,
|
|
daddy = self(),
|
|
vagene = ASock},
|
|
SpawnHo = fun() -> do_be_ho(InitHoState) end,
|
|
NewHo = {_Pid, _Ref} = erlang:spawn_monitor(SpawnHo),
|
|
NewPimpState = PS#ps{hos = [NewHo | Hos],
|
|
ho_names = NewNames},
|
|
pimp_loop(NewPimpState);
|
|
{echo, From, Data} ->
|
|
echo_except(From, Data, Hos),
|
|
pimp_loop(PS);
|
|
{'DOWN', Ref, process, Pid, Info} ->
|
|
ok = pimp_logfln("ho down: ~tp; reason: ~tw", [Pid, Info]),
|
|
NewHos = lists:delete({Pid, Ref}, Hos),
|
|
NewPS = PS#ps{hos = NewHos},
|
|
pimp_loop(NewPS);
|
|
Bad ->
|
|
pimp_logfln("bad msg: ~tw", [Bad]),
|
|
pimp_logln("liquidating the hos"),
|
|
liquidate_hos(Hos),
|
|
gen_tcp:close(LSock)
|
|
end.
|
|
|
|
liquidate_hos([{HoPid, _HoRef} | Hos]) ->
|
|
pimp_logfln("liquidating ~tp", [HoPid]),
|
|
exit(HoPid, kill),
|
|
liquidate_hos(Hos);
|
|
liquidate_hos([]) ->
|
|
pimp_logln("it's done").
|
|
|
|
|
|
% skip if equal
|
|
echo_except(SrcPid, Data, [{SrcPid, _Ref} | Rest]) ->
|
|
echo_except(SrcPid, Data, Rest);
|
|
echo_except(SrcPid, Data, [{Pid, _Ref} | Rest]) ->
|
|
Pid ! {echo, Data},
|
|
echo_except(SrcPid, Data, Rest);
|
|
echo_except(_, _, []) ->
|
|
ok.
|
|
|
|
|
|
-spec spawn_ho(HoState) ->
|
|
|
|
|
|
-spec do_be_ho(HoState) -> NoReturn when
|
|
HoState :: ho_state(),
|
|
NoReturn :: no_return().
|
|
%% @doc
|
|
%% ain't no rest for the wicked
|
|
|
|
do_be_ho(HS = #hs{name = Name, daddy = Daddy, vagene = ASock}) ->
|
|
Msg = io:format("HI! I'm ~ts. What's your name?\r\n", [Name]),
|
|
ok = ho_send(ASock, Msg),
|
|
receive
|
|
{tcp, ASock, Data} ->
|
|
ho_rcvd(Data),
|
|
Daddy ! {echo, self(), Data},
|
|
do_be_ho(HS);
|
|
{echo, Data} ->
|
|
ho_send(ASock, Data),
|
|
do_be_ho(HS);
|
|
{tcp_closed, ASock} ->
|
|
exit(its_fine)
|
|
end.
|
|
|
|
|
|
pimp_logfln(MsgStr, Args) ->
|
|
pimp_logln(io_lib:format(MsgStr, Args)).
|
|
|
|
pimp_logln(MsgStr) ->
|
|
PimpPfx = io_lib:format("pimp ~tp :: ", [self()]),
|
|
Msg = [?ANSI_BOLD([PimpPfx, MsgStr, "\n"])],
|
|
ok = io:put_chars(Msg),
|
|
ok.
|
|
|
|
|
|
ho_rcvd(MsgStr) ->
|
|
ok = io:format("ho ~tp << ~tw~n", [self(), MsgStr]),
|
|
ok.
|
|
|
|
ho_send(Socket, Msg) ->
|
|
ok = io:format("ho ~tp >> ~tw~n", [self(), Msg]),
|
|
ok = gen_tcp:send(Socket, Msg),
|
|
ok.
|
|
|
|
|
|
init_ho_names() ->
|
|
["Crystal",
|
|
"Tiffany",
|
|
"Amber",
|
|
"Brandy",
|
|
"Lola",
|
|
"Angel",
|
|
"Ginger",
|
|
"Candy",
|
|
"Charity",
|
|
"Anastasia",
|
|
"Cherry",
|
|
"Kitty",
|
|
"Jade",
|
|
"Destiny",
|
|
"Devon",
|
|
"Chastity",
|
|
"Raven",
|
|
"Scarlett",
|
|
"Bambi",
|
|
"Star",
|
|
"Paris",
|
|
"Dallas",
|
|
"Diamond",
|
|
"Skye",
|
|
"Trinity",
|
|
"Tawny",
|
|
"Layla",
|
|
"Lexie",
|
|
"Roxy",
|
|
"Porsche",
|
|
"Nevaeh",
|
|
"Ashlynn",
|
|
"Aspen",
|
|
"Chyna",
|
|
"Lexus",
|
|
"Unique",
|
|
"Chardonnay",
|
|
"Houston",
|
|
"London",
|
|
"Coco",
|
|
"Luscious",
|
|
"Delight",
|
|
"Capri",
|
|
"Trixie",
|
|
"Cinnamon"].
|
|
|
|
recycle_ho_names([N | Ns]) ->
|
|
{N, Ns ++ [N]}.
|