%%% BEGIN openflax/handler/watch.erl %%% %%% %%% openflax - Open Source web server for Erlang/OTP %%% Copyright (c)2003 Cat's Eye Technologies. All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions %%% are met: %%% %%% Redistributions of source code must retain the above copyright %%% notice, this list of conditions and the following disclaimer. %%% %%% 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. %%% %%% Neither the name of Cat's Eye Technologies nor the names of its %%% contributors may be used to endorse or promote products derived %%% from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND %%% CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. %% @doc OpenFlax connection-monitoring handler. %% %%
This handler's security/featurefulness tradeoff level is %% 0 (Security.) if access to this resource is %% properly configured (e.g. restricted to 127.0.0.1.)
%% %%This handler allows the system to be actively watched so %% that intrusion attempts can be immediately observable.
%% %% @end -module(openflax.handler.watch). -vsn('$Id: watch.erl 31 2004-04-23 07:00:11Z catseye $'). -author('cpressey@catseye.mine.nu'). -copyright('Copyright (c)2003 Cat`s Eye Technologies. All rights reserved.'). -behaviour(openflax.handler). -export([start/1, stop/1, serve/1]). % behaviour interface -include_lib("kernel/include/inet.hrl"). -import(lists). -import(io_lib). -import(inet). %% @spec start(conf()) -> conf() %% @doc Initializes the connection-monitor broadcaster. start(Conf) -> Broadcaster = spawn_link(fun() -> broadcast([]) end), openflax.conf:new( [ % this is our personal cfg_ setting, so we can tell who we are {cfg_watch_broadcaster, Broadcaster}, % this is OpenFlax' cfg_setting, so it knows we are watching {cfg_openflax_watchers, [Broadcaster]} ]). %% @spec stop(conf()) -> conf() %% @doc Shuts down the connection-monitoring subsystem. stop(Conf) -> {ok, Broadcaster} = openflax.conf:get_value(cfg_watch_broadcaster, Conf), Broadcaster ! stop, % tell all subscribers to stop Broadcaster ! {Broadcaster, stop}, % then tell the broadcaster itself Conf. %% @spec serve(conf()) -> response() %% @doc Lets the user agent watch as connections come and go. serve(Conf) -> {ok, Broadcaster} = openflax.conf:get_value(cfg_watch_broadcaster, Conf), Pid = spawn_link(fun() -> watch(Broadcaster) end), % start streaming with a known size Conf0 = openflax.conf:put_value(res_content_type, "text/plain", Conf), {{stream, Pid, unknown}, Conf0}. %% @spec watch(Broadcaster::pid()) -> na watch(Broadcaster) -> receive {Parent, stream_open} -> subscribe(Broadcaster), loop(Parent), unsubscribe(Broadcaster), Parent ! {self(), stream_close} end. loop(Parent) -> receive {connection_open, Ref, Conf} -> RemoteAddress = openflax.conf:get_string(sreq_remote_address, Conf), RequestURI = openflax.conf:get_string(sreq_uri, Conf), Name = case inet:gethostbyaddr(RemoteAddress) of {ok, Hostent} -> Hostent#hostent.h_name; Else -> io_lib:format("~p", [Else]) end, String = lists:flatten(io_lib:format("+~p ~p ~s ~s", [Ref, RemoteAddress, Name, RequestURI])) ++ "\r\n", send(Parent, String); {connection_closed, Ref, Conf} -> RemoteAddress = openflax.conf:get_string(sreq_remote_address, Conf), String = lists:flatten(io_lib:format("-~p ~s", [Ref, RemoteAddress])) ++ "\r\n", send(Parent, String); stop -> stop end. send(Parent, Data) -> % openflax.log:write("~p", [String]), Parent ! {self(), stream_data, Data}, receive {Parent, stream_acknowledge, data} -> loop(Parent); {Parent, stream_error, Error} -> openflax.log:write("ERROR: ~p", [Error]), {error, Error} end. %% --------------- broadcaster ------------------- %% @spec broadcast(Subscribers::[pid()]) -> never_returns() %% @doc Drives the broadcasting. broadcast(Subscribers) -> Broadcaster = self(), receive {Broadcaster, {subscribe, Pid}} -> case lists:member(Pid, Subscribers) of true -> broadcast(Subscribers); false -> openflax.log:write("subscribe ~p", [Pid]), broadcast(Subscribers ++ [Pid]) end; {Broadcaster, {unsubscribe, Pid}} -> case lists:member(Pid, Subscribers) of false -> broadcast(Subscribers); true -> openflax.log:write("unsubscribe ~p", [Pid]), broadcast(Subscribers -- [Pid]) end; {Broadcaster, stop} -> stop; Else -> lists:foreach(fun(Subscriber) -> Subscriber ! Else end, Subscribers), broadcast(Subscribers) end. %% @spec subscribe(Broadcaster::pid()) -> ok | {error, Reason} %% @doc Subscribes to a broadcasting service. subscribe(Broadcaster) -> Broadcaster ! {Broadcaster, {subscribe, self()}}. %% @spec unsubscribe(Broadcaster::pid()) -> ok | {error, Reason} %% @doc Cancels subscription to a broadcasting service. unsubscribe(Broadcaster) -> Broadcaster ! {Broadcaster, {unsubscribe, self()}}. %%% END of openflax/handler/watch.erl %%%