C0 code coverage information

Generated on 2009-08-09 21:22:55 with etap 0.3.4.

Name Total lines Lines of code Total coverage Code coverage
couch_os_process ?? ?? ??
0% 
....1 % Licensed under the Apache License, Version 2.0 (the "License"); you may not
....2 % use this file except in compliance with the License. You may obtain a copy of
....3 % the License at
....4 %
....5 %   http://www.apache.org/licenses/LICENSE-2.0
....6 %
....7 % Unless required by applicable law or agreed to in writing, software
....8 % distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
....9 % WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
...10 % License for the specific language governing permissions and limitations under
...11 % the License.
...12 
...13 -module(couch_os_process).
...14 -behaviour(gen_server).
...15 
...16 -export([start_link/1, start_link/2, start_link/3, stop/1]).
...17 -export([set_timeout/2, prompt/2]).
...18 -export([send/2, writeline/2, readline/1, writejson/2, readjson/1]).
...19 -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2, code_change/3]).
...20 
...21 -include("couch_db.hrl").
...22 
...23 -define(PORT_OPTIONS, [stream, {line, 1024}, binary, exit_status, hide]).
...24 
...25 -record(os_proc,
...26     {command,
...27      port,
...28      writer,
...29      reader,
...30      timeout=5000
...31     }).
...32 
...33 start_link(Command) ->
...34     start_link(Command, []).
...35 start_link(Command, Options) ->
...36     start_link(Command, Options, ?PORT_OPTIONS).
...37 start_link(Command, Options, PortOptions) ->
...38     gen_server:start_link(couch_os_process, [Command, Options, PortOptions], []).
...39 
...40 stop(Pid) ->
...41     gen_server:cast(Pid, stop).
...42 
...43 % Read/Write API
...44 set_timeout(Pid, TimeOut) when is_integer(TimeOut) ->
...45     ok = gen_server:call(Pid, {set_timeout, TimeOut}).
...46 
...47 % Used by couch_db_update_notifier.erl
...48 send(Pid, Data) ->
...49     gen_server:cast(Pid, {send, Data}).
...50 
...51 prompt(Pid, Data) ->
...52     case gen_server:call(Pid, {prompt, Data}, infinity) of
...53         {ok, Result} ->
...54             Result;
...55         Error ->
...56             ?LOG_ERROR("OS Process Error :: ~p",[Error]),
...57             throw(Error)
...58     end.
...59 
...60 % Utility functions for reading and writing
...61 % in custom functions
...62 writeline(OsProc, Data) when is_record(OsProc, os_proc) ->
...63     port_command(OsProc#os_proc.port, Data ++ "\n").
...64 
...65 readline(OsProc) when is_record(OsProc, os_proc) ->
...66     readline(OsProc, []).
...67 readline(OsProc, Acc) when is_record(OsProc, os_proc) ->
...68     #os_proc{port=Port} = OsProc,
...69     receive
...70     {Port, {data, {noeol, Data}}} ->
...71         readline(OsProc, [Data|Acc]);
...72     {Port, {data, {eol, Data}}} ->
...73         lists:reverse(Acc, Data);
...74     {Port, Err} ->
...75         catch port_close(Port),
...76         throw({os_process_error, Err})
...77     after OsProc#os_proc.timeout ->
...78         catch port_close(Port),
...79         throw({os_process_error, "OS process timed out."})
...80     end.
...81 
...82 % Standard JSON functions
...83 writejson(OsProc, Data) when is_record(OsProc, os_proc) ->
...84     % ?LOG_DEBUG("OS Process Input :: ~p", [Data]),
...85     true = writeline(OsProc, ?JSON_ENCODE(Data)).
...86 
...87 readjson(OsProc) when is_record(OsProc, os_proc) ->
...88     Line = readline(OsProc),
...89     case ?JSON_DECODE(Line) of
...90     [<<"log">>, Msg] when is_binary(Msg) ->
...91         % we got a message to log. Log it and continue
...92         ?LOG_INFO("OS Process :: ~s", [Msg]),
...93         readjson(OsProc);
...94     {[{<<"error">>, Id}, {<<"reason">>, Reason}]} ->
...95         throw({list_to_atom(binary_to_list(Id)),Reason});
...96     {[{<<"reason">>, Reason}, {<<"error">>, Id}]} ->
...97         throw({list_to_atom(binary_to_list(Id)),Reason});
...98     Result ->
...99         % ?LOG_DEBUG("OS Process Output :: ~p", [Result]),
..100         Result
..101     end.
..102 
..103 
..104 % gen_server API
..105 init([Command, Options, PortOptions]) ->
..106     case code:priv_dir(couch) of
..107     {error, bad_name} ->
..108         % small hack, in dev mode "app" is couchdb. Fixing requires renaming
..109         % src/couch to src/couch. Not really worth the hassle.-Damien
..110         PrivDir = code:priv_dir(couchdb);
..111     PrivDir -> ok
..112     end,
..113     Spawnkiller = filename:join(PrivDir, "couchspawnkillable"),
..114     BaseProc = #os_proc{
..115         command=Command,
..116         port=open_port({spawn, Spawnkiller ++ " " ++ Command}, PortOptions),
..117         writer=fun writejson/2,
..118         reader=fun readjson/1
..119     },
..120     KillCmd = readline(BaseProc),
..121     Pid = self(),
..122     spawn(fun() ->
..123             % this ensure the real os process is killed when this process dies.
..124             erlang:monitor(process, Pid),
..125             receive _ -> ok end,
..126             os:cmd(?b2l(KillCmd))
..127         end),
..128     OsProc =
..129     lists:foldl(fun(Opt, Proc) ->
..130         case Opt of
..131         {writer, Writer} when is_function(Writer) ->
..132             Proc#os_proc{writer=Writer};
..133         {reader, Reader} when is_function(Reader) ->
..134             Proc#os_proc{reader=Reader};
..135         {timeout, TimeOut} when is_integer(TimeOut) ->
..136             Proc#os_proc{timeout=TimeOut}
..137         end
..138     end, BaseProc, Options),
..139     {ok, OsProc}.
..140 
..141 terminate(_Reason, #os_proc{port=Port}) ->
..142     catch port_close(Port),
..143     ok.
..144 
..145 handle_call({set_timeout, TimeOut}, _From, OsProc) ->
..146     {reply, ok, OsProc#os_proc{timeout=TimeOut}};
..147 handle_call({prompt, Data}, _From, OsProc) ->
..148     #os_proc{writer=Writer, reader=Reader} = OsProc,
..149     try
..150         Writer(OsProc, Data),
..151         {reply, {ok, Reader(OsProc)}, OsProc}
..152     catch
..153         throw:OsError ->
..154             {stop, normal, OsError, OsProc}
..155     end.
..156 
..157 handle_cast({send, Data}, #os_proc{writer=Writer}=OsProc) ->
..158     try
..159         Writer(OsProc, Data),
..160         {noreply, OsProc}
..161     catch
..162         throw:OsError ->
..163             ?LOG_ERROR("Failed sending data: ~p -> ~p", [Data, OsError]),
..164             {stop, normal, OsProc}
..165     end;
..166 handle_cast(stop, OsProc) ->
..167     {stop, normal, OsProc};
..168 handle_cast(Msg, OsProc) ->
..169     ?LOG_DEBUG("OS Proc: Unknown cast: ~p", [Msg]),
..170     {noreply, OsProc}.
..171 
..172 handle_info({Port, {exit_status, Status}}, #os_proc{port=Port}=OsProc) ->
..173     ?LOG_ERROR("OS Process died with status: ~p", [Status]),
..174     {stop, {exit_status, Status}, OsProc};
..175 handle_info(Msg, OsProc) ->
..176     ?LOG_DEBUG("OS Proc: Unknown info: ~p", [Msg]),
..177     {noreply, OsProc}.
..178 
..179 code_change(_OldVsn, State, _Extra) ->
..180     {ok, State}.
..181 

Generated using etap 0.3.4.