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 | ?? | ?? | ?? |
|
....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.