%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2001-2010. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(webtool). -behaviour(gen_server). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% The general idea is: %% %% %% %% %% %% 1. Scan through the path for *.tool files and find all the web %% %% based tools. Query each tool for configuration data. %% %% 2. Add Alias for Erlscript and html for each tool to %% %% the webserver configuration data. %% %% 3. Start the webserver. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% API functions -export([start/0, start/2, stop/0]). %% Starting Webtool from a shell script -export([script_start/0, script_start/1]). %% Web api -export([started_tools/2, toolbar/2, start_tools/2, stop_tools/2]). %% API against other tools -export([is_localhost/0]). %% Debug export s -export([get_tools1/1]). -export([debug/1, stop_debug/0, debug_app/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include_lib("kernel/include/file.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -record(state,{priv_dir,app_data,supvis,web_data,started=[]}). -define(MAX_NUMBER_OF_WEBTOOLS,256). -define(DEFAULT_PORT,8888).% must be >1024 or the user must be root on unix -define(DEFAULT_ADDR,{127,0,0,1}). -define(WEBTOOL_ALIAS,{webtool,[{alias,{erl_alias,"/webtool",[webtool]}}]}). -define(HEADER,"Pragma:no-cache\r\n Content-type: text/html\r\n\r\n"). -define(HTML_HEADER,"\r\n\r\nWebTool\r\n\r\n\r\n"). -define(HTML_HEADER_RELOAD,"\r\n\r\nWebTool \r\n\r\n \r\n"). -define(HTML_END,""). -define(SEND_URL_TIMEOUT,5000). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% For debugging only. %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start tracing with %% debug(Functions). %% Functions = local | global | FunctionList %% FunctionList = [Function] %% Function = {FunctionName,Arity} | FunctionName | %% {Module, FunctionName, Arity} | {Module,FunctionName} debug(F) -> ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes ttb:p(all,[call,timestamp]), MS = [{'_',[],[{return_trace},{message,{caller}}]}], tp(F,MS), ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func ok. tp(local,MS) -> % all functions ttb:tpl(?MODULE,MS); tp(global,MS) -> % all exported functions ttb:tp(?MODULE,MS); tp([{M,F,A}|T],MS) -> % Other module ttb:tpl(M,F,A,MS), tp(T,MS); tp([{M,F}|T],MS) when is_atom(F) -> % Other module ttb:tpl(M,F,MS), tp(T,MS); tp([{F,A}|T],MS) -> % function/arity ttb:tpl(?MODULE,F,A,MS), tp(T,MS); tp([F|T],MS) -> % function ttb:tpl(?MODULE,F,MS), tp(T,MS); tp([],_MS) -> ok. stop_debug() -> ttb:stop([format]). debug_app(Mod) -> ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]), ttb:p(all,[call,timestamp]), MS = [{'_',[],[{return_trace},{message,{caller}}]}], ttb:tp(Mod,MS), ok. out(_,{trace_ts,Pid,call,MFA={M,F,A},{W,_,_},TS},_,S) when W==webtool;W==mod_esi-> io:format("~w: (~p)~ncall ~s~n", [TS,Pid,ffunc(MFA)]), [{M,F,length(A)}|S]; out(_,{trace_ts,Pid,return_from,MFA,R,TS},_,[MFA|S]) -> io:format("~w: (~p)~nreturned from ~s -> ~p~n", [TS,Pid,ffunc(MFA),R]), S; out(_,_,_,_) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Functions called via script. %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% script_start() -> usage(), halt(). script_start([App]) -> DefaultBrowser = case os:type() of {win32,_} -> iexplore; _ -> firefox end, script_start([App,DefaultBrowser]); script_start([App,Browser]) -> io:format("Starting webtool...\n"), start(), AvailableApps = get_applications(), {OSType,_} = os:type(), case lists:keysearch(App,1,AvailableApps) of {value,{App,StartPage}} -> io:format("Starting ~w...\n",[App]), start_tools([],"app=" ++ atom_to_list(App)), PortStr = integer_to_list(get_port()), Url = case StartPage of "/" ++ Page -> "http://localhost:" ++ PortStr ++ "/" ++ Page; _ -> "http://localhost:" ++ PortStr ++ "/" ++ StartPage end, case Browser of none -> ok; iexplore when OSType == win32-> io:format("Starting internet explorer...\n"), {ok,R} = win32reg:open(""), Key="\\local_machine\\SOFTWARE\\Microsoft\\IE Setup\\Setup", win32reg:change_key(R,Key), {ok,Val} = win32reg:value(R,"Path"), IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"), os:cmd("\"" ++ IExplore ++ "\" " ++ Url); _ when OSType == win32 -> io:format("Starting ~w...\n",[Browser]), os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url); B when B==firefox; B==mozilla -> io:format("Sending URL to ~w...",[Browser]), BStr = atom_to_list(Browser), SendCmd = BStr ++ " -raise -remote \'openUrl(" ++ Url ++ ")\'", Port = open_port({spawn,SendCmd},[exit_status]), receive {Port,{exit_status,0}} -> io:format("done\n"), ok; {Port,{exit_status,_Error}} -> io:format(" not running, starting ~w...\n", [Browser]), os:cmd(BStr ++ " " ++ Url), ok after ?SEND_URL_TIMEOUT -> io:format(" failed, starting ~w...\n",[Browser]), erlang:port_close(Port), os:cmd(BStr ++ " " ++ Url) end; _ -> io:format("Starting ~w...\n",[Browser]), os:cmd(atom_to_list(Browser) ++ " " ++ Url) end, ok; false -> stop(), io:format("\n{error,{unknown_app,~p}}\n",[App]), halt() end. usage() -> io:format("Starting webtool...\n"), start(), Apps = lists:map(fun({A,_}) -> A end,get_applications()), io:format( "\nUsage: start_webtool application [ browser ]\n" "\nAvailable applications are: ~p\n" "Default browser is \'iexplore\' (Internet Explorer) on Windows " "or else \'firefox\'\n", [Apps]), stop(). get_applications() -> gen_server:call(web_tool,get_applications). get_port() -> gen_server:call(web_tool,get_port). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Api functions to the genserver. %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % %---------------------------------------------------------------------- start()-> start(standard_path,standard_data). start(Path,standard_data)-> case get_standard_data() of {error,Reason} -> {error,Reason}; Data -> start(Path,Data) end; start(standard_path,Data)-> Path=get_path(), start(Path,Data); start(Path,Port) when is_integer(Port)-> Data = get_standard_data(Port), start(Path,Data); start(Path,Data0)-> Data = Data0 ++ rest_of_standard_data(), gen_server:start({local,web_tool},webtool,{Path,Data},[]). stop()-> gen_server:call(web_tool,stoppit). %---------------------------------------------------------------------- %Web Api functions called by the web %---------------------------------------------------------------------- started_tools(Env,Input)-> gen_server:call(web_tool,{started_tools,Env,Input}). toolbar(Env,Input)-> gen_server:call(web_tool,{toolbar,Env,Input}). start_tools(Env,Input)-> gen_server:call(web_tool,{start_tools,Env,Input}). stop_tools(Env,Input)-> gen_server:call(web_tool,{stop_tools,Env,Input}). %---------------------------------------------------------------------- %Support API for other tools %---------------------------------------------------------------------- is_localhost()-> gen_server:call(web_tool,is_localhost). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %%The gen_server callback functions that builds the webbpages %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% handle_call(get_applications,_,State)-> MS = ets:fun2ms(fun({Tool,{web_data,{_,Start}}}) -> {Tool,Start} end), Tools = ets:select(State#state.app_data,MS), {reply,Tools,State}; handle_call(get_port,_,State)-> {value,{port,Port}}=lists:keysearch(port,1,State#state.web_data), {reply,Port,State}; handle_call({started_tools,_Env,_Input},_,State)-> {reply,started_tools_page(State),State}; handle_call({toolbar,_Env,_Input},_,State)-> {reply,toolbar(),State}; handle_call({start_tools,Env,Input},_,State)-> {NewState,Page}=start_tools_page(Env,Input,State), {reply,Page,NewState}; handle_call({stop_tools,Env,Input},_,State)-> {NewState,Page}=stop_tools_page(Env,Input,State), {reply,Page,NewState}; handle_call(stoppit,_From,Data)-> {stop,normal,ok,Data}; handle_call(is_localhost,_From,Data)-> Result=case proplists:get_value(bind_address, Data#state.web_data) of ?DEFAULT_ADDR -> true; _IpNumber -> false end, {reply,Result,Data}. handle_info(_Message,State)-> {noreply,State}. handle_cast(_Request,State)-> {noreply,State}. code_change(_,State,_)-> {ok,State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % The other functions needed by the gen_server behaviour % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % Start the gen_server %---------------------------------------------------------------------- init({Path,Config})-> case filelib:is_dir(Path) of true -> {ok, Table} = get_tool_files_data(), insert_app(?WEBTOOL_ALIAS, Table), case webtool_sup:start_link() of {ok, Pid} -> case start_webserver(Table, Path, Config) of {ok, _} -> print_url(Config), {ok,#state{priv_dir=Path, app_data=Table, supvis=Pid, web_data=Config}}; {error, Error} -> {stop, {error, Error}} end; Error -> {stop,Error} end; false -> {stop, {error, error_dir}} end. terminate(_Reason,Data)-> %%shut down the webbserver shutdown_server(Data), %%Shutdown the different tools that are started with application:start shutdown_apps(Data), %%Shutdown the supervisor and its children will die shutdown_supervisor(Data), ok. print_url(ConfigData)-> Server=proplists:get_value(server_name,ConfigData,"undefined"), Port=proplists:get_value(port,ConfigData,"undefined"), {A,B,C,D}=proplists:get_value(bind_address,ConfigData,"undefined"), io:format("WebTool is available at http://~s:~w/~n",[Server,Port]), io:format("Or http://~w.~w.~w.~w:~w/~n",[A,B,C,D,Port]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % begin build the pages % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %The page that shows the started tools %---------------------------------------------------------------------- started_tools_page(State)-> [?HEADER,?HTML_HEADER,started_tools(State),?HTML_END]. toolbar()-> [?HEADER,?HTML_HEADER,toolbar_page(),?HTML_END]. start_tools_page(_Env,Input,State)-> %%io:format("~n======= ~n ~p ~n============~n",[Input]), case get_tools(Input) of {tools,Tools}-> %%io:format("~n======= ~n ~p ~n============~n",[Tools]), {ok,NewState}=handle_apps(Tools,State,start), {NewState,[?HEADER,?HTML_HEADER_RELOAD,reload_started_apps(), show_unstarted_apps(NewState),?HTML_END]}; _ -> {State,[?HEADER,?HTML_HEADER,show_unstarted_apps(State),?HTML_END]} end. stop_tools_page(_Env,Input,State)-> case get_tools(Input) of {tools,Tools}-> {ok,NewState}=handle_apps(Tools,State,stop), {NewState,[?HEADER,?HTML_HEADER_RELOAD,reload_started_apps(), show_started_apps(NewState),?HTML_END]}; _ -> {State,[?HEADER,?HTML_HEADER,show_started_apps(State),?HTML_END]} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Functions that start and config the webserver %% 1. Collect the config data %% 2. Start webserver %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % Start the webserver %---------------------------------------------------------------------- start_webserver(Data,Path,Config)-> case get_conf_data(Data,Path,Config) of {ok,Conf_data}-> %%io:format("Conf_data: ~p~n",[Conf_data]), start_server(Conf_data); {error,Error} -> {error,{error_server_conf_file,Error}} end. start_server(Conf_data)-> case inets:start(httpd, Conf_data, stand_alone) of {ok,Pid}-> {ok,Pid}; Error-> {error,{server_error,Error}} end. %---------------------------------------------------------------------- % Create config data for the webserver %---------------------------------------------------------------------- get_conf_data(Data,Path,Config)-> Aliases=get_aliases(Data), ServerRoot = filename:join([Path,"root"]), MimeTypesFile = filename:join([ServerRoot,"conf","mime.types"]), case httpd_conf:load_mime_types(MimeTypesFile) of {ok,MimeTypes} -> Config1 = Config ++ Aliases, Config2 = [{server_root,ServerRoot}, {document_root,filename:join([Path,"root/doc"])}, {mime_types,MimeTypes} | Config1], {ok,Config2}; Error -> Error end. %---------------------------------------------------------------------- % Control the path for *.tools files %---------------------------------------------------------------------- get_tool_files_data()-> Tools=get_tools1(code:get_path()), %%io:format("Data : ~p ~n",[Tools]), get_file_content(Tools). %---------------------------------------------------------------------- %Control that the data in the file really is erlang terms %---------------------------------------------------------------------- get_file_content(Tools)-> Get_data=fun({tool,ToolData}) -> %%io:format("Data : ~p ~n",[ToolData]), case proplists:get_value(config_func,ToolData) of {M,F,A}-> case catch apply(M,F,A) of {'EXIT',_} -> bad_data; Data when is_tuple(Data) -> Data; _-> bad_data end; _ -> bad_data end end, insert_file_content([X ||X<-lists:map(Get_data,Tools),X/=bad_data]). %---------------------------------------------------------------------- %Insert the data from the file in to the ets:table %---------------------------------------------------------------------- insert_file_content(Content)-> Table=ets:new(app_data,[bag]), lists:foreach(fun(X)-> insert_app(X,Table) end,Content), {ok,Table}. %---------------------------------------------------------------------- %Control that we got a a tuple of a atom and a list if so add the %elements in the list to the ets:table %---------------------------------------------------------------------- insert_app({Name,Key_val_list},Table) when is_list(Key_val_list),is_atom(Name)-> %%io:format("ToolData: ~p: ~p~n",[Name,Key_val_list]), lists:foreach( fun({alias,{erl_alias,Alias,Mods}}) -> Key_val = {erl_script_alias,{Alias,Mods}}, %%io:format("Insert: ~p~n",[Key_val]), ets:insert(Table,{Name,Key_val}); (Key_val_pair)-> %%io:format("Insert: ~p~n",[Key_val_pair]), ets:insert(Table,{Name,Key_val_pair}) end, Key_val_list); insert_app(_,_)-> ok. %---------------------------------------------------------------------- % Select all the alias in the database %---------------------------------------------------------------------- get_aliases(Data)-> MS = ets:fun2ms(fun({_,{erl_script_alias,Alias}}) -> {erl_script_alias,Alias}; ({_,{alias,Alias}}) -> {alias,Alias} end), ets:select(Data,MS). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Helper functions %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get_standard_data(Port)-> [ {port,Port}, {bind_address,?DEFAULT_ADDR}, {server_name,"localhost"} ]. get_standard_data()-> case get_free_port(?DEFAULT_PORT,?MAX_NUMBER_OF_WEBTOOLS) of {error,Reason} -> {error,Reason}; Port -> [ {port,Port}, {bind_address,?DEFAULT_ADDR}, {server_name,"localhost"} ] end. get_free_port(_Port,0) -> {error,no_free_port_found}; get_free_port(Port,N) -> case gen_tcp:connect("localhost",Port,[]) of {error, _Reason} -> Port; {ok,Sock} -> gen_tcp:close(Sock), get_free_port(Port+1,N-1) end. rest_of_standard_data() -> [ %% Do not allow the server to be crashed by malformed http-request {max_header_siz,1024}, {max_header_action,reply414}, %% Go on a straight ip-socket {com_type,ip_comm}, %% Do not change the order of these module names!! {modules,[mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi, mod_dir, mod_get, mod_head, mod_log, mod_disk_log]}, {directory_index,["index.html"]}, {default_type,"text/plain"} ]. get_path()-> code:priv_dir(webtool). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % These functions is used to shutdown the webserver %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % Shut down the webbserver %---------------------------------------------------------------------- shutdown_server(State)-> {Addr,Port} = get_addr_and_port(State#state.web_data), inets:stop(httpd,{Addr,Port}). get_addr_and_port(Config) -> Addr = proplists:get_value(bind_address,Config,?DEFAULT_ADDR), Port = proplists:get_value(port,Config,?DEFAULT_PORT), {Addr,Port}. %---------------------------------------------------------------------- % Select all apps in the table and close them %---------------------------------------------------------------------- shutdown_apps(State)-> Data=State#state.app_data, MS = ets:fun2ms(fun({_,{start,HowToStart}}) -> HowToStart end), lists:foreach(fun(Start_app)-> stop_app(Start_app) end, ets:select(Data,MS)). %---------------------------------------------------------------------- %Shuts down the supervisor that supervises tools that is not %Designed as applications %---------------------------------------------------------------------- shutdown_supervisor(State)-> %io:format("~n==================~n"), webtool_sup:stop(State#state.supvis). %io:format("~n==================~n"). %---------------------------------------------------------------------- %close the individual apps. %---------------------------------------------------------------------- stop_app({child,_Real_name})-> ok; stop_app({app,Real_name})-> application:stop(Real_name); stop_app({func,_Start,Stop})-> case Stop of {M,F,A} -> catch apply(M,F,A); _NoStop -> ok end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% These functions creates the webpage where the user can select if %% to start apps or to stop apps %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% toolbar_page()-> "
Select Action
Start Tools
Stop Tools
". %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% These functions creates the webbpage that shows the started apps %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- % started_tools(State)->String (html table) % State is a record of type state %---------------------------------------------------------------------- started_tools(State)-> Names=get_started_apps(State#state.app_data,State#state.started), " "++ make_rows(Names,[],0) ++"
". %---------------------------------------------------------------------- %get_started_apps(Data,Started)-> [{web_name,link}] %selects the started apps from the ets table of apps. %---------------------------------------------------------------------- get_started_apps(Data,Started)-> SelectData=fun({Name,Link}) -> {Name,Link} end, MS = lists:map(fun(A) -> {{A,{web_data,'$1'}},[],['$1']} end,Started), [{"WebTool","/tool_management.html"} | [SelectData(X) || X <- ets:select(Data,MS)]]. %---------------------------------------------------------------------- % make_rows(List,Result,Fields)-> String (The rows of a htmltable % List a list of tupler discibed above % Result an accumulator for the result % Field, counter that counts the number of cols in each row. %---------------------------------------------------------------------- make_rows([],Result,Fields)-> Result ++ fill_out(Fields); make_rows([Data|Paths],Result,Field)when Field==0-> make_rows(Paths,Result ++ "" ++ make_field(Data),Field+1); make_rows([Path|Paths],Result,Field)when Field==4-> make_rows(Paths,Result ++ make_field(Path) ++ "",0); make_rows([Path|Paths],Result,Field)-> make_rows(Paths,Result ++ make_field(Path),Field+1). %---------------------------------------------------------------------- % make_fields(Path)-> String that is a field i a html table % Path is a name url tuple {Name,url} %---------------------------------------------------------------------- make_field(Path)-> "" ++ get_name(Path) ++ "". %---------------------------------------------------------------------- %get_name({Nae,Url})->String that represents a tag in html. %---------------------------------------------------------------------- get_name({Name,Url})-> "" ++ Name ++ "". %---------------------------------------------------------------------- % fill_out(Nr)-> String, that represent Nr fields in a html-table. %---------------------------------------------------------------------- fill_out(Nr)when Nr==0-> []; fill_out(Nr)when Nr==4-> " "; fill_out(Nr)-> " " ++ fill_out(Nr+1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %%These functions starts applicatons and builds the page showing tools %%to start %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------------------------------------- %Controls whether the user selected a tool to start %---------------------------------------------------------------------- get_tools(Input)-> case httpd:parse_query(Input) of []-> no_tools; Tools-> FormatData=fun({_Name,Data}) -> list_to_atom(Data) end, SelectData= fun({Name,_Data}) -> string:equal(Name,"app") end, {tools,[FormatData(X)||X<-Tools,SelectData(X)]} end. %---------------------------------------------------------------------- % Selects the data to start the applications the user has ordered % starting of %---------------------------------------------------------------------- handle_apps([],State,_Cmd)-> {ok,State}; handle_apps([Tool|Tools],State,Cmd)-> case ets:match_object(State#state.app_data,{Tool,{start,'_'}}) of []-> Started = case Cmd of start -> [Tool|State#state.started]; stop -> lists:delete(Tool,State#state.started) end, {ok,#state{priv_dir=State#state.priv_dir, app_data=State#state.app_data, supvis=State#state.supvis, web_data=State#state.web_data, started=Started}}; ToStart -> case handle_apps2(ToStart,State,Cmd) of {ok,NewState}-> handle_apps(Tools,NewState,Cmd); _-> handle_apps(Tools,State,Cmd) end end. %---------------------------------------------------------------------- %execute every start or stop data about a tool. %---------------------------------------------------------------------- handle_apps2([{Name,Start_data}],State,Cmd)-> case handle_app({Name,Start_data},State#state.app_data,State#state.supvis,Cmd) of ok-> Started = case Cmd of start -> [Name|State#state.started]; stop -> lists:delete(Name,State#state.started) end, {ok,#state{priv_dir=State#state.priv_dir, app_data=State#state.app_data, supvis=State#state.supvis, web_data=State#state.web_data, started=Started}}; _-> error end; handle_apps2([{Name,Start_data}|Rest],State,Cmd)-> case handle_app({Name,Start_data},State#state.app_data,State#state.supvis,Cmd)of ok-> handle_apps2(Rest,State,Cmd); _-> error end. %---------------------------------------------------------------------- % Handle start and stop of applications %---------------------------------------------------------------------- handle_app({Name,{start,{func,Start,Stop}}},Data,_Pid,Cmd)-> Action = case Cmd of start -> Start; _ -> Stop end, case Action of {M,F,A} -> case catch apply(M,F,A) of {'EXIT',_} = Exit-> %%! Here the tool disappears from the webtool interface!! io:format("\n=======ERROR (webtool, line ~w) =======\n" "Could not start application \'~p\'\n\n" "~w:~w(~s) ->\n" "~p\n\n", [?LINE,Name,M,F,format_args(A),Exit]), ets:delete(Data,Name); _OK-> ok end; _NoStart -> ok end; handle_app({Name,{start,{child,ChildSpec}}},Data,Pid,Cmd)-> case Cmd of start -> case catch supervisor:start_child(Pid,ChildSpec) of {ok,_}-> ok; {ok,_,_}-> ok; {error,Reason}-> %%! Here the tool disappears from the webtool interface!! io:format("\n=======ERROR (webtool, line ~w) =======\n" "Could not start application \'~p\'\n\n" "supervisor:start_child(~p,~p) ->\n" "~p\n\n", [?LINE,Name,Pid,ChildSpec,{error,Reason}]), ets:delete(Data,Name); Error -> %%! Here the tool disappears from the webtool interface!! io:format("\n=======ERROR (webtool, line ~w) =======\n" "Could not start application \'~p\'\n\n" "supervisor:start_child(~p,~p) ->\n" "~p\n\n", [?LINE,Name,Pid,ChildSpec,Error]), ets:delete(Data,Name) end; stop -> case catch supervisor:terminate_child(websup,element(1,ChildSpec)) of ok -> supervisor:delete_child(websup,element(1,ChildSpec)); _ -> error end end; handle_app({Name,{start,{app,Real_name}}},Data,_Pid,Cmd)-> case Cmd of start -> case application:start(Real_name,temporary) of ok-> io:write(Name), ok; {error,{already_started,_}}-> %% Remove it from the database so we dont start %% anything already started ets:match_delete(Data,{Name,{start,{app,Real_name}}}), ok; {error,_Reason}=Error-> %%! Here the tool disappears from the webtool interface!! io:format("\n=======ERROR (webtool, line ~w) =======\n" "Could not start application \'~p\'\n\n" "application:start(~p,~p) ->\n" "~p\n\n", [?LINE,Name,Real_name,temporary,Error]), ets:delete(Data,Name) end; stop -> application:stop(Real_name) end; %---------------------------------------------------------------------- % If the data is incorrect delete the app %---------------------------------------------------------------------- handle_app({Name,Incorrect},Data,_Pid,Cmd)-> %%! Here the tool disappears from the webtool interface!! io:format("\n=======ERROR (webtool, line ~w) =======\n" "Could not ~w application \'~p\'\n\n" "Incorrect data: ~p\n\n", [?LINE,Cmd,Name,Incorrect]), ets:delete(Data,Name). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% this functions creates the page that shows the unstarted tools %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% reload_started_apps()-> "". show_unstarted_apps(State)-> "
Available Tools
"++ list_available_apps(State)++"
 
To Start a Tool:
  • Select the checkbox for each tool to start.
  • Click on the button marked Start.
 
". list_available_apps(State)-> MS = ets:fun2ms(fun({Tool,{web_data,{Name,_}}}) -> {Tool,Name} end), Unstarted_apps= lists:filter( fun({Tool,_})-> false==lists:member(Tool,State#state.started) end, ets:select(State#state.app_data,MS)), case Unstarted_apps of []-> "All tools are started"; _-> list_apps(Unstarted_apps) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% these functions creates the page that shows the started apps %% %% the user can select to shutdown %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% show_started_apps(State)-> "
Started Tools
"++ list_started_apps(State)++"
 
Stop a Tool:
  • Select the checkbox for each tool to stop.
  • Click on the button marked Stop.
 
". list_started_apps(State)-> MS = lists:map(fun(A) -> {{A,{web_data,{'$1','_'}}},[],[{{A,'$1'}}]} end, State#state.started), Started_apps= ets:select(State#state.app_data,MS), case Started_apps of []-> "No tool is started yet."; _-> list_apps(Started_apps) end. list_apps(Apps) -> lists:map(fun({Tool,Name})-> " " ++ Name ++ " " end, Apps). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Collecting the data from the *.tool files %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %---------------------------------------- % get_tools(Dirs) => [{M,F,A},{M,F,A}...{M,F,A}] % Dirs - [string()] Directory names % Calls get_tools2/2 recursively for a number of directories % to retireve the configuration data for the web based tools. %---------------------------------------- get_tools1(Dirs)-> get_tools1(Dirs,[]). get_tools1([Dir|Rest],Data) when is_list(Dir) -> Tools=case filename:basename(Dir) of %% Dir is an 'ebin' directory, check in '../priv' as well "ebin" -> [get_tools2(filename:join(filename:dirname(Dir),"priv")) | get_tools2(Dir)]; _ -> get_tools2(Dir) end, get_tools1(Rest,[Tools|Data]); get_tools1([],Data) -> lists:flatten(Data). %---------------------------------------- % get_tools2(Directory) => DataList % DataList : [WebTuple]|[] % WebTuple: {tool,[{web,M,F,A}]} % %---------------------------------------- get_tools2(Dir)-> get_tools2(tool_files(Dir),[]). get_tools2([ToolFile|Rest],Data) -> case get_tools3(ToolFile) of {tool,WebData} -> get_tools2(Rest,[{tool,WebData}|Data]); {error,_Reason} -> get_tools2(Rest,Data); nodata -> get_tools2(Rest,Data) end; get_tools2([],Data) -> Data. %---------------------------------------- % get_tools3(ToolFile) => {ok,Tool}|{error,Reason}|nodata % Tool: {tool,[KeyValTuple]} % ToolFile - string() A .tool file % Now we have the file get the data and sort it out %---------------------------------------- get_tools3(ToolFile) -> case file:consult(ToolFile) of {error,open} -> {error,nofile}; {error,read} -> {error,format}; {ok,[{version,"1.2"},ToolInfo]} when is_list(ToolInfo)-> webdata(ToolInfo); {ok,[{version,_Vsn},_Info]} -> {error,old_version}; {ok,_Other} -> {error,format} end. %---------------------------------------------------------------------- % webdata(TupleList)-> ToolTuple| nodata % ToolTuple: {tool,[{config_func,{M,F,A}}]} % % There are a little unneccesary work in this format but it is extendable %---------------------------------------------------------------------- webdata(TupleList)-> case proplists:get_value(config_func,TupleList,nodata) of {M,F,A} -> {tool,[{config_func,{M,F,A}}]}; _ -> nodata end. %============================================================================= % Functions for getting *.tool configuration files %============================================================================= %---------------------------------------- % tool_files(Dir) => ToolFiles % Dir - string() Directory name % ToolFiles - [string()] % Return the list of all files in Dir ending with .tool (appended to Dir) %---------------------------------------- tool_files(Dir) -> case file:list_dir(Dir) of {ok,Files} -> filter_tool_files(Dir,Files); {error,_Reason} -> [] end. %---------------------------------------- % filter_tool_files(Dir,Files) => ToolFiles % Dir - string() Directory name % Files, ToolFiles - [string()] File names % Filters out the files in Files ending with .tool and append them to Dir %---------------------------------------- filter_tool_files(_Dir,[]) -> []; filter_tool_files(Dir,[File|Rest]) -> case filename:extension(File) of ".tool" -> [filename:join(Dir,File)|filter_tool_files(Dir,Rest)]; _ -> filter_tool_files(Dir,Rest) end. %%%----------------------------------------------------------------- %%% format functions ffunc({M,F,A}) when is_list(A) -> io_lib:format("~w:~w(~s)\n",[M,F,format_args(A)]); ffunc({M,F,A}) when is_integer(A) -> io_lib:format("~w:~w/~w\n",[M,F,A]). format_args([]) -> ""; format_args(Args) -> Str = lists:append(["~p"|lists:duplicate(length(Args)-1,",~p")]), io_lib:format(Str,Args).