src/execdaemon.erl
author Tom Parker <palfrey@lshift.net>
Fri Dec 03 12:19:42 2010 +0000 (14 months ago)
changeset 278 18023c8db394
parent 109290a00503f6f
permissions -rw-r--r--
Handle authentication errors in lastfm:scrobble
     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 startup() ->
    13     Port = open_port({spawn, jukebox:priv_dir() ++ "/execdaemon/execdaemon"}, [stream, use_stdio, eof]),
    14     mainloop(Port, 1, [], [], "").
    15 
    16 mainloop(Port, ReqNum, Requests, Subscribers, Acc) ->
    17     receive
    18 	{subscribe, Pid} ->
    19 	    Pid ! {subscribed, self()},
    20 	    mainloop(Port, ReqNum, Requests, [Pid | Subscribers], Acc);
    21 	execdaemon_terminate ->
    22 	    port_command(Port, [0]),
    23 	    mainloop(Port, ReqNum, Requests, Subscribers, Acc);
    24 	{execdaemon_command, Pid, Command, Arg} ->
    25 	    port_command(Port, lists:flatten([integer_to_list(ReqNum), ":",
    26 					      atom_to_list(Command), ",", Arg, [0]])),
    27 	    mainloop(Port, ReqNum + 1, [{ReqNum, Pid} | Requests], Subscribers, Acc);
    28 	{_Port1, eof} ->
    29 	    {_, Pids} = lists:unzip(Requests),
    30 	    send_to_subs(Pids ++ Subscribers, {execdaemon_eof, self()}),
    31 	    port_close(Port);
    32 	{_Port1, {data, Data}} ->
    33 	    {NewRequests, NewAcc} = process_received(Subscribers, Requests, Acc, Data),
    34 	    mainloop(Port, ReqNum, NewRequests, Subscribers, NewAcc)
    35     end.
    36 
    37 send_to_subs(Subscribers, Msg) ->
    38     lists:foreach(fun (Subscriber) -> Subscriber ! Msg end, Subscribers).
    39 
    40 process_received(_Subscribers, Requests, Acc, []) ->
    41     {Requests, Acc};
    42 process_received(Subscribers, Requests, Acc, [0 | Rest]) ->
    43     {ReqNum, Code, Aux} = split_response(lists:reverse(Acc)),
    44     if
    45 	ReqNum == 0 ->
    46 	    send_to_subs(Subscribers, {execdaemon_event, self(), Code, Aux}),
    47 	    process_received(Subscribers, Requests, [], Rest);
    48 	true ->
    49 	    case proplists:get_value(ReqNum, Requests, none) of
    50 		none -> process_received(Subscribers, Requests, [], Rest);
    51 		Pid ->
    52 		    Pid ! {execdaemon_response, self(), Code, Aux},
    53 		    process_received(Subscribers, proplists:delete(ReqNum, Requests), [], Rest)
    54 	    end
    55     end;
    56 process_received(Subscribers, Requests, Acc, [Ch | Rest]) ->
    57     process_received(Subscribers, Requests, [Ch | Acc], Rest).
    58 
    59 split_response(Str) ->
    60     {ReqNum, ":" ++ CmdArg} = lists:split(string:chr(Str, $:) - 1, Str),
    61     {Cmd, "," ++ Arg} = lists:split(string:chr(CmdArg, $,) - 1, CmdArg),
    62     {list_to_integer(ReqNum), list_to_atom(Cmd), Arg}.
    63 
    64 terminate(Pid) ->
    65     Pid ! execdaemon_terminate.
    66 
    67 command(Pid, Command, Arg) ->
    68     Pid ! {execdaemon_command, self(), Command, Arg},
    69     read_from(Pid).
    70 
    71 read_from(Pid) ->
    72     receive
    73 	{execdaemon_response, Pid1, Code, Aux} when Pid == Pid1 ->
    74 	    {Code, Aux};
    75 	{execdaemon_eof, Pid1} when Pid == Pid1 ->
    76 	    eof
    77     end.
    78 
    79 wait_for_event(Pid) ->
    80     receive
    81 	{execdaemon_event, Pid1, Code, Aux} when Pid == Pid1 ->
    82 	    {Code, Aux};
    83 	{execdaemon_eof, Pid1} when Pid == Pid1 ->
    84 	    eof
    85     end.
    86 
    87 run(Program, Argvec) ->
    88     Pid = open(),
    89     command(Pid, program, Program),
    90     command(Pid, argc, integer_to_list(length(Argvec))),
    91     lists:foldl(fun (Arg, Count) ->
    92 			command(Pid, arg, [integer_to_list(Count), ",", Arg]),
    93 			Count + 1
    94 		end, 0, Argvec),
    95     command(Pid, execv, ""),
    96     Pid.