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