src/execdaemon.erl
author Tony Garnock-Jones <tonyg@lshift.net>
Wed Jun 20 16:10:07 2007 +0000 (2007-06-20)
changeset 109 290a00503f6f
parent 101 2e6223986a83
child 160 9178a7a18ba0
permissions -rw-r--r--
Locate execdaemon binary in priv_dir
     1 -module(execdaemon).
     2 
     3 -export([open/0, terminate/1, command/3, read_from/1, wait_for_event/1, run/2]).
     4 
     5 open() ->
     6     Pid = spawn(fun startup/0),
     7     Pid ! {subscribe, self()},
     8     receive
     9 	{subscribed, Pid2} when Pid == Pid2 -> Pid
    10     end.
    11 
    12 priv_dir() ->
    13     case code:priv_dir(jukebox) of
    14 	{error, bad_name} ->
    15 	    "./priv";
    16 	D ->
    17 	    D
    18     end.
    19 
    20 startup() ->
    21     Port = open_port({spawn, priv_dir() ++ "/execdaemon/execdaemon"}, [stream, use_stdio, eof]),
    22     mainloop(Port, 1, [], [], "").
    23 
    24 mainloop(Port, ReqNum, Requests, Subscribers, Acc) ->
    25     receive
    26 	{subscribe, Pid} ->
    27 	    Pid ! {subscribed, self()},
    28 	    mainloop(Port, ReqNum, Requests, [Pid | Subscribers], Acc);
    29 	execdaemon_terminate ->
    30 	    port_command(Port, [0]),
    31 	    mainloop(Port, ReqNum, Requests, Subscribers, Acc);
    32 	{execdaemon_command, Pid, Command, Arg} ->
    33 	    port_command(Port, lists:flatten([integer_to_list(ReqNum), ":",
    34 					      atom_to_list(Command), ",", Arg, [0]])),
    35 	    mainloop(Port, ReqNum + 1, [{ReqNum, Pid} | Requests], Subscribers, Acc);
    36 	{_Port1, eof} ->
    37 	    {_, Pids} = lists:unzip(Requests),
    38 	    send_to_subs(Pids ++ Subscribers, {execdaemon_eof, self()}),
    39 	    port_close(Port);
    40 	{_Port1, {data, Data}} ->
    41 	    {NewRequests, NewAcc} = process_received(Subscribers, Requests, Acc, Data),
    42 	    mainloop(Port, ReqNum, NewRequests, Subscribers, NewAcc)
    43     end.
    44 
    45 send_to_subs(Subscribers, Msg) ->
    46     lists:foreach(fun (Subscriber) -> Subscriber ! Msg end, Subscribers).
    47 
    48 process_received(_Subscribers, Requests, Acc, []) ->
    49     {Requests, Acc};
    50 process_received(Subscribers, Requests, Acc, [0 | Rest]) ->
    51     {ReqNum, Code, Aux} = split_response(lists:reverse(Acc)),
    52     if
    53 	ReqNum == 0 ->
    54 	    send_to_subs(Subscribers, {execdaemon_event, self(), Code, Aux}),
    55 	    process_received(Subscribers, Requests, [], Rest);
    56 	true ->
    57 	    case proplists:get_value(ReqNum, Requests, none) of
    58 		none -> process_received(Subscribers, Requests, [], Rest);
    59 		Pid ->
    60 		    Pid ! {execdaemon_response, self(), Code, Aux},
    61 		    process_received(Subscribers, proplists:delete(ReqNum, Requests), [], Rest)
    62 	    end
    63     end;
    64 process_received(Subscribers, Requests, Acc, [Ch | Rest]) ->
    65     process_received(Subscribers, Requests, [Ch | Acc], Rest).
    66 
    67 split_response(Str) ->
    68     {ReqNum, ":" ++ CmdArg} = lists:split(string:chr(Str, $:) - 1, Str),
    69     {Cmd, "," ++ Arg} = lists:split(string:chr(CmdArg, $,) - 1, CmdArg),
    70     {list_to_integer(ReqNum), list_to_atom(Cmd), Arg}.
    71 
    72 terminate(Pid) ->
    73     Pid ! execdaemon_terminate.
    74 
    75 command(Pid, Command, Arg) ->
    76     Pid ! {execdaemon_command, self(), Command, Arg},
    77     read_from(Pid).
    78 
    79 read_from(Pid) ->
    80     receive
    81 	{execdaemon_response, Pid1, Code, Aux} when Pid == Pid1 ->
    82 	    {Code, Aux};
    83 	{execdaemon_eof, Pid1} when Pid == Pid1 ->
    84 	    eof
    85     end.
    86 
    87 wait_for_event(Pid) ->
    88     receive
    89 	{execdaemon_event, Pid1, Code, Aux} when Pid == Pid1 ->
    90 	    {Code, Aux};
    91 	{execdaemon_eof, Pid1} when Pid == Pid1 ->
    92 	    eof
    93     end.
    94 
    95 run(Program, Argvec) ->
    96     Pid = open(),
    97     command(Pid, program, Program),
    98     command(Pid, argc, integer_to_list(length(Argvec))),
    99     lists:foldl(fun (Arg, Count) ->
   100 			command(Pid, arg, [integer_to_list(Count), ",", Arg]),
   101 			Count + 1
   102 		end, 0, Argvec),
   103     command(Pid, execv, ""),
   104     Pid.