inet_gethost_native.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 617 行 · 第 1/2 页
ERL
617 行
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%-module(inet_gethost_native).-behaviour(supervisor_bridge).%% Supervisor bridge exports-export([start_link/0, init/1, terminate/2, start_raw/0, run_once/0]).%% Server export-export([server_init/2, main_loop/1]).%% API exports-export([gethostbyname/1, gethostbyname/2, gethostbyaddr/1, control/1]).%%% Exports for sys:handle_system_msg/6-export([system_continue/3, system_terminate/4, system_code_change/4]).-include_lib("kernel/include/inet.hrl").-define(PROCNAME_SUP, inet_gethost_native_sup).-define(OP_GETHOSTBYNAME,1).-define(OP_GETHOSTBYADDR,2).-define(OP_CANCEL_REQUEST,3).-define(OP_CONTROL,4).-define(PROTO_IPV4,1).-define(PROTO_IPV6,2).%% OP_CONTROL-define(SETOPT_DEBUG_LEVEL, 0).-define(UNIT_ERROR,0).-define(UNIT_IPV4,4).-define(UNIT_IPV6,16).-define(PORT_PROGRAM, "inet_gethost").-define(DEFAULT_POOLSIZE, 4).-define(REQUEST_TIMEOUT, (inet_db:res_option(timeout)*4)).-define(MAX_TIMEOUT, 16#7FFFFFF).-define(INVALID_SERIAL, 16#FFFFFFFF).%-define(DEBUG,1).-ifdef(DEBUG).-define(dbg(A,B), io:format(A,B)).-else.-define(dbg(A,B), noop).-endif.-define(SEND_AFTER(A,B,C),erlang:send_after(A,B,C)).-define(CANCEL_TIMER(A),erlang:cancel_timer(A)).%% In erlang, IPV6 addresses are built as 8-tuples of 16bit values (not 16-tuples of octets).%% This macro, meant to be used in guards checks one such 16bit value in the 8-tuple.-define(VALID_V6(Part), is_integer(Part), Part < 65536).%% The regular IPV4 addresses are represented as 4-tuples of octets, this macro,%% meant to be used in guards, check one such octet.-define(VALID_V4(Part), is_integer(Part), Part < 256).% Requests, one per unbique request to the PORT program, may be more than one client!!!-record(request, { rid, % Request id as sent to port op, proto, rdata, clients = [] % Can be more than one client per request (Pid's). }).% Statistics, not used yet.-record(statistics, { netdb_timeout = 0, netdb_internal = 0, port_crash = 0, notsup = 0, host_not_found = 0, try_again = 0, no_recovery = 0, no_data = 0}).% The main loopstate...-record(state, { port = noport, % Port() connected to the port program timeout = 8000, % Timeout value from inet_db:res_option requests, % Table of request req_index, % Table of {{op,proto,rdata},rid} parent, % The supervisor bridge pool_size = 4, % Number of C processes in pool. statistics % Statistics record (records error causes).}).%% The supervisor bridge codeinit([]) -> % Called by supervisor_bridge:start_link Ref = make_ref(), SaveTE = process_flag(trap_exit,true), Pid = spawn_link(?MODULE,server_init,[self(),Ref]), receive Ref -> process_flag(trap_exit,SaveTE), {ok, Pid, Pid}; {'EXIT', Pid, Message} -> process_flag(trap_exit,SaveTE), {error, Message} after 10000 -> process_flag(trap_exit,SaveTE), {error, {timeout, ?MODULE}} end.start_link() -> supervisor_bridge:start_link({local, ?PROCNAME_SUP}, ?MODULE, []).%% Only used in fallback situations, no supervisor, no bridge, serve only until%% no requests present...start_raw() -> spawn(?MODULE,run_once,[]).run_once() -> Port = do_open_port(get_poolsize(), get_extra_args()), Timeout = ?REQUEST_TIMEOUT, {Pid, R, Request} = receive {{Pid0,R0}, {?OP_GETHOSTBYNAME, Proto0, Name0}} -> {Pid0, R0, [<<1:32, ?OP_GETHOSTBYNAME:8, Proto0:8>>,Name0,0]}; {{Pid1,R1}, {?OP_GETHOSTBYADDR, Proto1, Data1}} -> {Pid1, R1, <<1:32, ?OP_GETHOSTBYADDR:8, Proto1:8, Data1/binary>>} after Timeout -> exit(normal) end, (catch port_command(Port, Request)), receive {Port, {data, <<1:32, BinReply/binary>>}} -> Pid ! {R, {ok, BinReply}} after Timeout -> Pid ! {R,{error,timeout}} end.terminate(_Reason,Pid) -> (catch exit(Pid,kill)), ok.%%-----------------------------------------------------------------------%% Server API%%-----------------------------------------------------------------------server_init(Starter, Ref) -> process_flag(trap_exit,true), case whereis(?MODULE) of undefined -> case (catch register(?MODULE,self())) of true -> Starter ! Ref; _-> exit({already_started,whereis(?MODULE)}) end; Winner -> exit({already_started,Winner}) end, Poolsize = get_poolsize(), Port = do_open_port(Poolsize, get_extra_args()), Timeout = ?REQUEST_TIMEOUT, put(rid,0), put(num_requests,0), RequestTab = ets:new(ign_requests,[{keypos,#request.rid},set,protected]), RequestIndex = ets:new(ign_req_index,[set,protected]), State = #state{port = Port, timeout = Timeout, requests = RequestTab, req_index = RequestIndex, pool_size = Poolsize, statistics = #statistics{}, parent = Starter}, main_loop(State).main_loop(State) -> receive Any -> handle_message(Any,State) end.handle_message({{Pid,_} = Client, {?OP_GETHOSTBYNAME, Proto, Name} = R}, State) when is_pid(Pid) -> NewState = do_handle_call(R,Client,State, [<<?OP_GETHOSTBYNAME:8, Proto:8>>, Name,0]), main_loop(NewState);handle_message({{Pid,_} = Client, {?OP_GETHOSTBYADDR, Proto, Data} = R}, State) when is_pid(Pid) -> NewState = do_handle_call(R,Client,State, <<?OP_GETHOSTBYADDR:8, Proto:8, Data/binary>>), main_loop(NewState);handle_message({{Pid,Ref}, {?OP_CONTROL, Ctl, Data}}, State) when is_pid(Pid) -> catch port_command(State#state.port, <<?INVALID_SERIAL:32, ?OP_CONTROL:8, Ctl:8, Data/binary>>), Pid ! {Ref, ok}, main_loop(State);handle_message({{Pid,Ref}, restart_port}, State) when is_pid(Pid) -> NewPort=restart_port(State), Pid ! {Ref, ok}, main_loop(State#state{port=NewPort});handle_message({Port, {data, Data}}, State = #state{port = Port}) -> NewState = case Data of <<RID:32, BinReply/binary>> -> case BinReply of <<Unit, _/binary>> when Unit =:= ?UNIT_ERROR; Unit =:= ?UNIT_IPV4; Unit =:= ?UNIT_IPV6 -> case pick_request(State,RID) of false -> State; Req -> lists:foreach(fun({P,R,TR}) -> ?CANCEL_TIMER(TR), P ! {R, {ok, BinReply}} end, Req#request.clients), State end; _UnitError -> %% Unexpected data, let's restart it, %% it must be broken. NewPort=restart_port(State), State#state{port=NewPort} end; _BasicFormatError -> NewPort=restart_port(State), State#state{port=NewPort} end, main_loop(NewState); handle_message({'EXIT',Port,_Reason}, State = #state{port = Port}) -> ?dbg("Port died.~n",[]), NewPort=restart_port(State), main_loop(State#state{port=NewPort});handle_message({Port,eof}, State = #state{port = Port}) -> ?dbg("Port eof'ed.~n",[]), NewPort=restart_port(State), main_loop(State#state{port=NewPort});handle_message({timeout, Pid, RID}, State) -> case pick_client(State,RID,Pid) of false -> false; {more, {P,R,_}} -> P ! {R,{error,timeout}}; {last, {LP,LR,_}} -> LP ! {LR, {error,timeout}}, %% Remove the whole request structure... pick_request(State, RID), %% Also cancel the request to the port program... (catch port_command(State#state.port, <<RID:32,?OP_CANCEL_REQUEST>>)) end, main_loop(State);handle_message({system, From, Req}, State) -> sys:handle_system_msg(Req, From, State#state.parent, ?MODULE, [], State);handle_message(_, State) -> % Stray messages from dying ports etc. main_loop(State).do_handle_call(R,Client0,State,RData) -> Req = find_request(State,R), Timeout = State#state.timeout, {P,Ref} = Client0, TR = ?SEND_AFTER(Timeout,self(),{timeout, P, Req#request.rid}), Client = {P,Ref,TR}, case Req#request.clients of [] -> RealRData = [<<(Req#request.rid):32>>|RData], (catch port_command(State#state.port, RealRData)), ets:insert(State#state.requests,Req#request{clients = [Client]}); Tail -> ets:insert(State#state.requests,Req#request{clients = [Client | Tail]}) end, State.find_request(State, R = {Op, Proto, Data}) -> case ets:lookup(State#state.req_index,R) of [{R, Rid}] -> [Ret] = ets:lookup(State#state.requests,Rid), Ret; [] ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?