diff options
Diffstat (limited to 'lib/kernel/src')
-rw-r--r-- | lib/kernel/src/Makefile | 15 | ||||
-rw-r--r-- | lib/kernel/src/code.erl | 27 | ||||
-rw-r--r-- | lib/kernel/src/code_server.erl | 41 | ||||
-rw-r--r-- | lib/kernel/src/file.erl | 11 | ||||
-rw-r--r-- | lib/kernel/src/gen_sctp.erl | 46 | ||||
-rw-r--r-- | lib/kernel/src/hipe_unified_loader.erl | 42 | ||||
-rw-r--r-- | lib/kernel/src/inet.erl | 147 | ||||
-rw-r--r-- | lib/kernel/src/inet_config.erl | 32 | ||||
-rw-r--r-- | lib/kernel/src/inet_db.erl | 107 | ||||
-rw-r--r-- | lib/kernel/src/inet_gethost_native.erl | 72 | ||||
-rw-r--r-- | lib/kernel/src/inet_parse.erl | 285 | ||||
-rw-r--r-- | lib/kernel/src/inet_sctp.erl | 30 | ||||
-rw-r--r-- | lib/kernel/src/net_kernel.erl | 112 | ||||
-rw-r--r-- | lib/kernel/src/os.erl | 59 | ||||
-rw-r--r-- | lib/kernel/src/pg2.erl | 17 | ||||
-rw-r--r-- | lib/kernel/src/user.erl | 16 |
16 files changed, 691 insertions, 368 deletions
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index ef280058fb..9db6014a7d 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1996-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 1996-2010. All Rights Reserved. +# # 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 online 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. -# +# # %CopyrightEnd% # @@ -143,6 +143,9 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # FLAGS # ---------------------------------------------------- +ifeq ($(NATIVE_LIBS_ENABLED),yes) +ERL_COMPILE_FLAGS += +native +endif ERL_COMPILE_FLAGS += -I../include # ---------------------------------------------------- @@ -154,7 +157,7 @@ debug opt: $(TARGET_FILES) # Note: In the open-source build clean must not destroyed the preloaded # beam files. clean: - rm -f $(NON_PRECIOUS_TARGETS) + rm -f $(TARGET_FILES) rm -f core diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index fef11d7e6e..ffe58ae7a9 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(code). @@ -63,7 +63,7 @@ which/1, where_is_file/1, where_is_file/2, - set_primary_archive/2, + set_primary_archive/3, clash/0]). -include_lib("kernel/include/file.hrl"). @@ -101,7 +101,7 @@ %% unstick_dir(Dir) -> ok | error %% is_sticky(Module) -> true | false %% which(Module) -> Filename -%% set_primary_archive((FileName, Bin) -> ok | {error, Reason} +%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason} %% clash() -> -> print out %%---------------------------------------------------------------------------- @@ -420,11 +420,15 @@ where_is_file(Path, File) when is_list(Path), is_list(File) -> which(File, ".", Path) end. --spec set_primary_archive(ArchiveFile :: file:filename(), ArchiveBin :: binary()) -> 'ok' | {'error', atom()}. +-spec set_primary_archive(ArchiveFile :: file:filename(), + ArchiveBin :: binary(), + FileInfo :: #file_info{}) + -> 'ok' | {'error', atom()}. -set_primary_archive(ArchiveFile0, ArchiveBin) when is_list(ArchiveFile0), is_binary(ArchiveBin) -> +set_primary_archive(ArchiveFile0, ArchiveBin, FileInfo) + when is_list(ArchiveFile0), is_binary(ArchiveBin), is_record(FileInfo, file_info) -> ArchiveFile = filename:absname(ArchiveFile0), - case call({set_primary_archive, ArchiveFile, ArchiveBin}) of + case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of {ok, []} -> ok; {ok, _Mode, Ebins} -> @@ -461,7 +465,8 @@ search([{Dir, File} | Tail]) -> build([]) -> []; build([Dir|Tail]) -> - Files = filter(objfile_extension(), Dir, file:list_dir(Dir)), + Files = filter(objfile_extension(), Dir, + erl_prim_loader:list_dir(Dir)), [decorate(Files, Dir) | build(Tail)]. decorate([], _) -> []; diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 018f7f41d2..7aeddb73d1 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(code_server). @@ -384,8 +384,8 @@ handle_call(stop,{_From,_Tag}, S) -> handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) -> {reply, no, S}; -handle_call({set_primary_archive, File, ArchiveBin}, {_From,_Tag}, S=#state{mode=Mode}) -> - case erl_prim_loader:set_primary_archive(File, ArchiveBin) of +handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) -> + case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of {ok, Files} -> {reply, {ok, Mode, Files}, S}; {error, Reason} -> @@ -1479,13 +1479,12 @@ finish_on_load(Ref, OnLoadRes, #state{on_load=OnLoad0,moddb=Db}=State) -> end. finish_on_load_1(Mod, File, OnLoadRes, WaitingPids, Db) -> - Keep = if - is_boolean(OnLoadRes) -> OnLoadRes; - true -> false - end, + Keep = OnLoadRes =:= ok, erlang:finish_after_on_load(Mod, Keep), Res = case Keep of - false -> {error,on_load_failure}; + false -> + finish_on_load_report(Mod, OnLoadRes), + {error,on_load_failure}; true -> ets:insert(Db, {Mod,File}), {module,Mod} @@ -1493,6 +1492,24 @@ finish_on_load_1(Mod, File, OnLoadRes, WaitingPids, Db) -> [reply(Pid, Res) || Pid <- WaitingPids], ok. +finish_on_load_report(_Mod, Atom) when is_atom(Atom) -> + %% No error reports for atoms. + ok; +finish_on_load_report(Mod, Term) -> + %% Play it very safe here. The error_logger module and + %% modules it depend on may not be loaded yet and there + %% would be a dead-lock if we called it directly + %% from the code_server process. + spawn(fun() -> + F = "The on_load function for module " + "~s returned ~P\n", + + %% Express the call as an apply to simplify + %% the ext_mod_dep/1 test case. + E = error_logger, + E:warning_msg(F, [Mod,Term,10]) + end). + %% ------------------------------------------------------- %% Internal functions. %% ------------------------------------------------------- diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index fa86d53dc9..a42771dfb6 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(file). @@ -73,6 +73,7 @@ %% data types -type filename() :: string(). +-type file_info() :: #file_info{}. -type io_device() :: pid() | #file_descriptor{}. -type location() :: integer() | {'bof', integer()} | {'cur', integer()} | {'eof', integer()} | 'bof' | 'cur' | 'eof'. diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index fcd1d1564a..5a31e3976f 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% @@ -27,7 +27,7 @@ -include("inet_sctp.hrl"). -export([open/0,open/1,open/2,close/1]). --export([listen/2,connect/4,connect/5]). +-export([listen/2,connect/4,connect/5,connect_init/4,connect_init/5]). -export([eof/2,abort/2]). -export([send/3,send/4,recv/1,recv/2]). -export([error_string/1]). @@ -80,7 +80,26 @@ listen(S, Flag) -> connect(S, Addr, Port, Opts) -> connect(S, Addr, Port, Opts, infinity). -connect(S, Addr, Port, Opts, Timeout) when is_port(S), is_list(Opts) -> +connect(S, Addr, Port, Opts, Timeout) -> + case do_connect(S, Addr, Port, Opts, Timeout, true) of + badarg -> + erlang:error(badarg, [S,Addr,Port,Opts,Timeout]); + Result -> + Result + end. + +connect_init(S, Addr, Port, Opts) -> + connect_init(S, Addr, Port, Opts, infinity). + +connect_init(S, Addr, Port, Opts, Timeout) -> + case do_connect(S, Addr, Port, Opts, Timeout, false) of + badarg -> + erlang:error(badarg, [S,Addr,Port,Opts,Timeout]); + Result -> + Result + end. + +do_connect(S, Addr, Port, Opts, Timeout, ConnWait) when is_port(S), is_list(Opts) -> case inet_db:lookup_socket(S) of {ok,Mod} -> case Mod:getserv(Port) of @@ -89,21 +108,26 @@ connect(S, Addr, Port, Opts, Timeout) when is_port(S), is_list(Opts) -> Timer -> try Mod:getaddr(Addr, Timer) of {ok,IP} -> - Mod:connect(S, IP, Port, Opts, Timer); + ConnectTimer = if ConnWait == false -> + nowait; + true -> + Timer + end, + Mod:connect(S, IP, Port, Opts, ConnectTimer); Error -> Error after inet:stop_timer(Timer) end catch error:badarg -> - erlang:error(badarg, [S,Addr,Port,Opts,Timeout]) + badarg end; Error -> Error end; Error -> Error end; -connect(S, Addr, Port, Opts, Timeout) -> - erlang:error(badarg, [S,Addr,Port,Opts,Timeout]). +do_connect(_S, _Addr, _Port, _Opts, _Timeout, _ConnWait) -> + badarg. diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 7e26d57ced..f289b8110d 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -103,10 +103,15 @@ load_native_code(Mod, Bin) when is_atom(Mod), is_binary(Bin) -> case code:get_chunk(Bin, ChunkTag) of undefined -> no_native; NativeCode when is_binary(NativeCode) -> - OldReferencesToPatch = patch_to_emu_step1(Mod), - case load_module(Mod, NativeCode, Bin, OldReferencesToPatch) of - bad_crc -> no_native; - Result -> Result + erlang:system_flag(multi_scheduling, block), + try + OldReferencesToPatch = patch_to_emu_step1(Mod), + case load_module(Mod, NativeCode, Bin, OldReferencesToPatch) of + bad_crc -> no_native; + Result -> Result + end + after + erlang:system_flag(multi_scheduling, unblock) end end catch @@ -121,8 +126,17 @@ load_native_code(Mod, Bin) when is_atom(Mod), is_binary(Bin) -> post_beam_load(Mod) when is_atom(Mod) -> Architecture = erlang:system_info(hipe_architecture), - try chunk_name(Architecture) of _ChunkTag -> patch_to_emu(Mod) - catch _:_ -> ok + try chunk_name(Architecture) of + _ChunkTag -> + erlang:system_flag(multi_scheduling, block), + try + patch_to_emu(Mod) + after + erlang:system_flag(multi_scheduling, unblock) + end + catch + _:_ -> + ok end. %%======================================================================== @@ -141,6 +155,14 @@ version_check(Version, Mod) when is_atom(Mod) -> -spec load_module(Mod, binary(), _) -> 'bad_crc' | {'module',Mod} when is_subtype(Mod,atom()). load_module(Mod, Bin, Beam) -> + erlang:system_flag(multi_scheduling, block), + try + load_module_nosmp(Mod, Bin, Beam) + after + erlang:system_flag(multi_scheduling, unblock) + end. + +load_module_nosmp(Mod, Bin, Beam) -> load_module(Mod, Bin, Beam, []). load_module(Mod, Bin, Beam, OldReferencesToPatch) -> @@ -154,6 +176,14 @@ load_module(Mod, Bin, Beam, OldReferencesToPatch) -> -spec load(Mod, binary()) -> 'bad_crc' | {'module',Mod} when is_subtype(Mod,atom()). load(Mod, Bin) -> + erlang:system_flag(multi_scheduling, block), + try + load_nosmp(Mod, Bin) + after + erlang:system_flag(multi_scheduling, unblock) + end. + +load_nosmp(Mod, Bin) -> ?debug_msg("********* Loading funs in module ~w *********\n",[Mod]), %% Loading just some functions in a module; patch closures separately. put(hipe_patch_closures, true), diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index b86aa1839e..eb503235d8 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(inet). @@ -45,6 +45,7 @@ %% resolve -export([gethostbyname/1, gethostbyname/2, gethostbyname/3, gethostbyname_tm/3]). +-export([gethostbyname_string/2, gethostbyname_self/2]). -export([gethostbyaddr/1, gethostbyaddr/2, gethostbyaddr_tm/2]). @@ -411,7 +412,17 @@ gethostbyname(Name,Family,Timeout) -> Res. gethostbyname_tm(Name,Family,Timer) -> - gethostbyname_tm(Name,Family,Timer,inet_db:res_option(lookup)). + Opts0 = inet_db:res_option(lookup), + Opts = + case (lists:member(native, Opts0) orelse + lists:member(string, Opts0) orelse + lists:member(nostring, Opts0)) of + true -> + Opts0; + false -> + [string|Opts0] + end, + gethostbyname_tm(Name, Family, Timer, Opts). -spec gethostbyaddr(Address :: string() | ip_address()) -> @@ -850,75 +861,61 @@ getaddrs_tm(Address, Family, Timer) -> %% %% gethostbyname with option search %% -gethostbyname_tm(Name, Type, Timer, [dns | Opts]) -> - Res = inet_res:gethostbyname_tm(Name, Type, Timer), - case Res of - {ok,_} -> Res; - {error,timeout} -> Res; - {error,formerr} -> {error,einval}; - {error,_} -> gethostbyname_tm(Name,Type,Timer,Opts) - end; -gethostbyname_tm(Name, Type, Timer, [file | Opts]) -> - case inet_hosts:gethostbyname(Name, Type) of - {error,formerr} -> {error,einval}; - {error,_} -> gethostbyname_tm(Name,Type,Timer,Opts); - Result -> Result - end; -gethostbyname_tm(Name, Type, Timer, [yp | Opts]) -> +gethostbyname_tm(Name, Type, Timer, [string|_]=Opts) -> + Result = gethostbyname_string(Name, Type), + gethostbyname_tm(Name, Type, Timer, Opts, Result); +gethostbyname_tm(Name, Type, Timer, [dns|_]=Opts) -> + Result = inet_res:gethostbyname_tm(Name, Type, Timer), + gethostbyname_tm(Name, Type, Timer, Opts, Result); +gethostbyname_tm(Name, Type, Timer, [file|_]=Opts) -> + Result = inet_hosts:gethostbyname(Name, Type), + gethostbyname_tm(Name, Type, Timer, Opts, Result); +gethostbyname_tm(Name, Type, Timer, [yp|_]=Opts) -> gethostbyname_tm_native(Name, Type, Timer, Opts); -gethostbyname_tm(Name, Type, Timer, [nis | Opts]) -> +gethostbyname_tm(Name, Type, Timer, [nis|_]=Opts) -> gethostbyname_tm_native(Name, Type, Timer, Opts); -gethostbyname_tm(Name, Type, Timer, [nisplus | Opts]) -> +gethostbyname_tm(Name, Type, Timer, [nisplus|_]=Opts) -> gethostbyname_tm_native(Name, Type, Timer, Opts); -gethostbyname_tm(Name, Type, Timer, [wins | Opts]) -> +gethostbyname_tm(Name, Type, Timer, [wins|_]=Opts) -> gethostbyname_tm_native(Name, Type, Timer, Opts); -gethostbyname_tm(Name, Type, Timer, [native | Opts]) -> +gethostbyname_tm(Name, Type, Timer, [native|_]=Opts) -> gethostbyname_tm_native(Name, Type, Timer, Opts); -gethostbyname_tm(_, _, _, [no_default|_]) -> - %% If the native resolver has failed, we should not bother - %% to try to be smarter and parse the IP address here. - {error,nxdomain}; -gethostbyname_tm(Name, Type, Timer, [_ | Opts]) -> +gethostbyname_tm(Name, Type, Timer, [_|_]=Opts) -> gethostbyname_tm(Name, Type, Timer, Opts); -%% Last resort - parse the hostname as address -gethostbyname_tm(Name, inet, _Timer, []) -> - case inet_parse:ipv4_address(Name) of - {ok,IP4} -> - {ok,make_hostent(Name, [IP4], [], inet)}; - _ -> - gethostbyname_self(Name) - end; -gethostbyname_tm(Name, inet6, _Timer, []) -> - case inet_parse:ipv6_address(Name) of - {ok,IP6} -> - {ok,make_hostent(Name, [IP6], [], inet6)}; - _ -> - %% Even if Name is a valid IPv4 address, we can't - %% assume it's correct to return it on a IPv6 - %% format ( {0,0,0,0,0,16#ffff,?u16(A,B),?u16(C,D)} ). - %% This host might not support IPv6. - gethostbyname_self(Name) +%% Make sure we always can look up our own hostname. +gethostbyname_tm(Name, Type, Timer, []) -> + Result = gethostbyname_self(Name, Type), + gethostbyname_tm(Name, Type, Timer, [], Result). + +gethostbyname_tm(Name, Type, Timer, Opts, Result) -> + case Result of + {ok,_} -> + Result; + {error,formerr} -> + {error,einval}; + {error,_} when Opts =:= [] -> + {error,nxdomain}; + {error,_} -> + gethostbyname_tm(Name, Type, Timer, tl(Opts)) end. gethostbyname_tm_native(Name, Type, Timer, Opts) -> %% Fixme: add (global) timeout to gethost_native - case inet_gethost_native:gethostbyname(Name, Type) of - {error,formerr} -> {error,einval}; - {error,timeout} -> {error,timeout}; - {error,_} -> gethostbyname_tm(Name, Type, Timer, Opts++[no_default]); - Result -> Result - end. + Result = inet_gethost_native:gethostbyname(Name, Type), + gethostbyname_tm(Name, Type, Timer, Opts, Result). -%% Make sure we always can look up our own hostname. -gethostbyname_self(Name) -> - Type = case inet_db:res_option(inet6) of - true -> inet6; - false -> inet - end, + + +gethostbyname_self(Name, Type) when is_atom(Name) -> + gethostbyname_self(atom_to_list(Name), Type); +gethostbyname_self(Name, Type) + when is_list(Name), Type =:= inet; + is_list(Name), Type =:= inet6 -> case inet_db:gethostname() of Name -> - {ok,make_hostent(Name, [translate_ip(loopback, Type)], - [], Type)}; + {ok,make_hostent(Name, + [translate_ip(loopback, Type)], + [], Type)}; Self -> case inet_db:res_option(domain) of "" -> {error,nxdomain}; @@ -931,7 +928,31 @@ gethostbyname_self(Name) -> _ -> {error,nxdomain} end end - end. + end; +gethostbyname_self(_, _) -> + {error,formerr}. + +gethostbyname_string(Name, Type) when is_atom(Name) -> + gethostbyname_string(atom_to_list(Name), Type); +gethostbyname_string(Name, Type) + when is_list(Name), Type =:= inet; + is_list(Name), Type =:= inet6 -> + case + case Type of + inet -> + inet_parse:ipv4_address(Name); + inet6 -> + %% XXX should we really translate IPv4 addresses here + %% even if we do not know if this host can do IPv6? + inet_parse:ipv6_address(Name) + end of + {ok,IP} -> + {ok,make_hostent(Name, [IP], [], Type)}; + {error,einval} -> + {error,nxdomain} + end; +gethostbyname_string(_, _) -> + {error,formerr}. make_hostent(Name, Addrs, Aliases, Type) -> #hostent{h_name = Name, diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl index b5317f72f5..311e6bc9f9 100644 --- a/lib/kernel/src/inet_config.erl +++ b/lib/kernel/src/inet_config.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(inet_config). @@ -130,21 +130,25 @@ init() -> {unix,_} -> %% The Etc variable enables us to run tests with other %% configuration files than the normal ones - Etc = case os:getenv("ERL_INET_ETC_DIR") of - false -> ?DEFAULT_ETC; - _EtcDir -> - _EtcDir - end, + Etc = + case os:getenv("ERL_INET_ETC_DIR") of + false -> + ?DEFAULT_ETC; + _EtcDir -> + _EtcDir + end, case inet_db:res_option(resolv_conf) of undefined -> - inet_db:set_resolv_conf(filename:join(Etc, - ?DEFAULT_RESOLV)); + inet_db:res_option( + resolv_conf_name, + filename:join(Etc, ?DEFAULT_RESOLV)); _ -> ok end, case inet_db:res_option(hosts_file) of undefined -> - inet_db:set_hosts_file(filename:join(Etc, - ?DEFAULT_HOSTS)); + inet_db:res_option( + hosts_file_name, + filename:join(Etc, ?DEFAULT_HOSTS)); _ -> ok end; _ -> ok diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl index 211847014f..a05b380855 100644 --- a/lib/kernel/src/inet_db.erl +++ b/lib/kernel/src/inet_db.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% @@ -425,7 +425,9 @@ res_optname(usevc) -> res_usevc; res_optname(edns) -> res_edns; res_optname(udp_payload_size) -> res_udp_payload_size; res_optname(resolv_conf) -> res_resolv_conf; +res_optname(resolv_conf_name) -> res_resolv_conf; res_optname(hosts_file) -> res_hosts_file; +res_optname(hosts_file_name) -> res_hosts_file; res_optname(_) -> undefined. res_check_option(nameserver, NSs) -> %% Legacy @@ -458,9 +460,15 @@ res_check_option(udp_payload_size, S) when is_integer(S), S >= 512 -> true; res_check_option(resolv_conf, "") -> true; res_check_option(resolv_conf, F) -> res_check_option_absfile(F); +res_check_option(resolv_conf_name, "") -> true; +res_check_option(resolv_conf_name, F) -> + res_check_option_absfile(F); res_check_option(hosts_file, "") -> true; res_check_option(hosts_file, F) -> res_check_option_absfile(F); +res_check_option(hosts_file_name, "") -> true; +res_check_option(hosts_file_name, F) -> + res_check_option_absfile(F); res_check_option(_, _) -> false. res_check_option_absfile(F) -> @@ -503,7 +511,7 @@ res_update_hosts() -> res_update(res_hosts_file, res_hosts_file_tm, res_hosts_file_info, set_hosts_file_tm, fun set_hosts_file/1). -res_update(Tag, TagTm, TagInfo, CallTag, SetFun) -> +res_update(Tag, TagTm, TagInfo, TagSetTm, SetFun) -> case db_get(TagTm) of undefined -> ok; TM -> @@ -522,12 +530,12 @@ res_update(Tag, TagTm, TagInfo, CallTag, SetFun) -> atime = undefined}, case db_get(TagInfo) of Finfo -> - call({CallTag, Now}); + call({TagSetTm, Now}); _ -> SetFun(File) end; _ -> - call({CallTag, Now}), + call({TagSetTm, Now}), error end end; @@ -974,37 +982,55 @@ handle_call(Request, From, #state{db=Db}=State) -> {reply, error, State} end; + {res_set, hosts_file_name=Option, Fname} -> + handle_set_file( + Option, Fname, res_hosts_file_tm, res_hosts_file_info, + undefined, From, State); + {res_set, resolv_conf_name=Option, Fname} -> + handle_set_file( + Option, Fname, res_resolv_conf_tm, res_resolv_conf_info, + undefined, From, State); + {res_set, hosts_file=Option, Fname} -> - handle_set_file(Option, Fname, - res_hosts_file_tm, res_hosts_file_info, - fun (Bin) -> - case inet_parse:hosts(Fname, - {chars,Bin}) of - {ok,Opts} -> - [{load_hosts_file,Opts}]; - _ -> error - end - end, - From, State); + handle_set_file( + Option, Fname, res_hosts_file_tm, res_hosts_file_info, + fun (Bin) -> + case inet_parse:hosts( + Fname, {chars,Bin}) of + {ok,Opts} -> + [{load_hosts_file,Opts}]; + _ -> error + end + end, + From, State); %% {res_set, resolv_conf=Option, Fname} -> - handle_set_file(Option, Fname, - res_resolv_conf_tm, res_resolv_conf_info, - fun (Bin) -> - case inet_parse:resolv(Fname, - {chars,Bin}) of - {ok,Opts} -> - [del_ns, - clear_search, - clear_cache - |[Opt || - {T,_}=Opt <- Opts, - (T =:= nameserver orelse - T =:= search)]]; - _ -> error - end - end, - From, State); + handle_set_file( + Option, Fname, res_resolv_conf_tm, res_resolv_conf_info, + fun (Bin) -> + case inet_parse:resolv( + Fname, {chars,Bin}) of + {ok,Opts} -> + Search = + lists:foldl( + fun ({search,L}, _) -> + L; + ({domain,""}, S) -> + S; + ({domain,D}, _) -> + [D]; + (_, S) -> + S + end, [], Opts), + [del_ns, + clear_search, + clear_cache, + {search,Search} + |[Opt || {nameserver,_}=Opt <- Opts]]; + _ -> error + end + end, + From, State); %% {res_set, Opt, Value} -> case res_optname(Opt) of @@ -1156,6 +1182,12 @@ handle_set_file(Option, Fname, TagTm, TagInfo, ParseFun, From, ets:delete(Db, TagInfo), ets:delete(Db, TagTm), handle_set_file(ParseFun, <<>>, From, State); + true when ParseFun =:= undefined -> + File = filename:flatten(Fname), + ets:insert(Db, {res_optname(Option), File}), + ets:insert(Db, {TagInfo, undefined}), + ets:insert(Db, {TagTm, 0}), + {reply,ok,State}; true -> File = filename:flatten(Fname), ets:insert(Db, {res_optname(Option), File}), @@ -1178,7 +1210,8 @@ handle_set_file(Option, Fname, TagTm, TagInfo, ParseFun, From, handle_set_file(ParseFun, Bin, From, State) -> case ParseFun(Bin) of - error -> {reply,error,State}; + error -> + {reply,error,State}; Opts -> handle_rc_list(Opts, From, State) end. diff --git a/lib/kernel/src/inet_gethost_native.erl b/lib/kernel/src/inet_gethost_native.erl index abdbe2b8cf..fabe9bf8b3 100644 --- a/lib/kernel/src/inet_gethost_native.erl +++ b/lib/kernel/src/inet_gethost_native.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(inet_gethost_native). @@ -443,19 +443,23 @@ gethostbyname(Name) -> gethostbyname(Name, inet). gethostbyname(Name, inet) when is_list(Name) -> - getit(?OP_GETHOSTBYNAME, ?PROTO_IPV4, Name); + getit(?OP_GETHOSTBYNAME, ?PROTO_IPV4, Name, Name); gethostbyname(Name, inet6) when is_list(Name) -> - getit(?OP_GETHOSTBYNAME, ?PROTO_IPV6, Name); + getit(?OP_GETHOSTBYNAME, ?PROTO_IPV6, Name, Name); gethostbyname(Name, Type) when is_atom(Name) -> gethostbyname(atom_to_list(Name), Type); gethostbyname(_, _) -> {error, formerr}. -gethostbyaddr({A,B,C,D}) when ?VALID_V4(A), ?VALID_V4(B), ?VALID_V4(C), ?VALID_V4(D) -> - getit(?OP_GETHOSTBYADDR, ?PROTO_IPV4, <<A,B,C,D>>); -gethostbyaddr({A,B,C,D,E,F,G,H}) when ?VALID_V6(A), ?VALID_V6(B), ?VALID_V6(C), ?VALID_V6(D), - ?VALID_V6(E), ?VALID_V6(F), ?VALID_V6(G), ?VALID_V6(H) -> - getit(?OP_GETHOSTBYADDR, ?PROTO_IPV6, <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16>>); +gethostbyaddr({A,B,C,D}=Addr) + when ?VALID_V4(A), ?VALID_V4(B), ?VALID_V4(C), ?VALID_V4(D) -> + getit(?OP_GETHOSTBYADDR, ?PROTO_IPV4, <<A,B,C,D>>, Addr); +gethostbyaddr({A,B,C,D,E,F,G,H}=Addr) + when ?VALID_V6(A), ?VALID_V6(B), ?VALID_V6(C), ?VALID_V6(D), + ?VALID_V6(E), ?VALID_V6(F), ?VALID_V6(G), ?VALID_V6(H) -> + getit + (?OP_GETHOSTBYADDR, ?PROTO_IPV6, + <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16>>, Addr); gethostbyaddr(Addr) when is_list(Addr) -> case inet_parse:address(Addr) of {ok, IP} -> gethostbyaddr(IP); @@ -466,30 +470,30 @@ gethostbyaddr(Addr) when is_atom(Addr) -> gethostbyaddr(_) -> {error, formerr}. control({debug_level, Level}) when is_integer(Level) -> - getit(?OP_CONTROL, ?SETOPT_DEBUG_LEVEL, <<Level:32>>); + getit(?OP_CONTROL, ?SETOPT_DEBUG_LEVEL, <<Level:32>>, undefined); control(soft_restart) -> - getit(restart_port); + getit(restart_port, undefined); control(_) -> {error, formerr}. -getit(Op, Proto, Data) -> - getit({Op, Proto, Data}). +getit(Op, Proto, Data, DefaultName) -> + getit({Op, Proto, Data}, DefaultName). -getit(Req) -> +getit(Req, DefaultName) -> Pid = ensure_started(), Ref = make_ref(), Pid ! {{self(),Ref}, Req}, receive {Ref, {ok,BinHostent}} -> - parse_address(BinHostent); - {Ref, Error} -> - Error + parse_address(BinHostent, DefaultName); + {Ref, Result} -> + Result after 5000 -> Ref2 = erlang:monitor(process,Pid), Res2 = receive {Ref, {ok,BinHostent}} -> - parse_address(BinHostent); - {Ref, Error} -> - Error; + parse_address(BinHostent, DefaultName); + {Ref, Result} -> + Result; {'DOWN', Ref2, process, Pid, Reason} -> {error, Reason} @@ -546,21 +550,23 @@ ensure_started() -> Pid end. -parse_address(BinHostent) -> +parse_address(BinHostent, DefaultName) -> case catch begin case BinHostent of <<?UNIT_ERROR, Errstring/binary>> -> {error, list_to_atom(listify(Errstring))}; <<?UNIT_IPV4, Naddr:32, T0/binary>> -> - {T1,Addresses} = pick_addresses_v4(Naddr, T0), - [Name | Names] = pick_names(T1), + {T1, Addresses} = pick_addresses_v4(Naddr, T0), + {Name, Names} = + expand_default_name(pick_names(T1), DefaultName), {ok, #hostent{h_addr_list = Addresses, h_addrtype = inet, h_aliases = Names, h_length = ?UNIT_IPV4, h_name = Name}}; <<?UNIT_IPV6, Naddr:32, T0/binary>> -> - {T1,Addresses} = pick_addresses_v6(Naddr, T0), - [Name | Names] = pick_names(T1), + {T1, Addresses} = pick_addresses_v6(Naddr, T0), + {Name, Names} = + expand_default_name(pick_names(T1), DefaultName), {ok, #hostent{h_addr_list = Addresses, h_addrtype = inet6, h_aliases = Names, h_length = ?UNIT_IPV6, h_name = Name}}; @@ -573,7 +579,15 @@ parse_address(BinHostent) -> Normal -> Normal end. - + +expand_default_name([], DefaultName) when is_list(DefaultName) -> + {DefaultName, []}; +expand_default_name([], DefaultName) when is_tuple(DefaultName) -> + {inet_parse:ntoa(DefaultName), []}; +expand_default_name([Name|Names], DefaultName) + when is_list(DefaultName); is_tuple(DefaultName) -> + {Name, Names}. + listify(Bin) -> N = byte_size(Bin) - 1, <<Bin2:N/binary, Ch>> = Bin, diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 62d44fb723..3bd5fa0958 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(inet_parse). @@ -34,6 +34,7 @@ -export([nsswitch_conf/1, nsswitch_conf/2]). -export([ipv4_address/1, ipv6_address/1]). +-export([ipv4strict_address/1, ipv6strict_address/1]). -export([address/1]). -export([visible_string/1, domain/1]). -export([ntoa/1, dots/1]). @@ -456,17 +457,15 @@ is_dom2(_) -> %% -%% Test ipv4 address or ipv6 address +%% Parse ipv4 address or ipv6 address %% Return {ok, Address} | {error, Reason} %% address(Cs) when is_list(Cs) -> case ipv4_address(Cs) of - {ok,IP} -> {ok,IP}; + {ok,IP} -> + {ok,IP}; _ -> - case ipv6_address(Cs) of - {ok, IP} -> {ok, IP}; - Error -> Error - end + ipv6strict_address(Cs) end; address(_) -> {error, einval}. @@ -477,49 +476,145 @@ address(_) -> %% d1.d2.d4 %% d1.d4 %% d4 +%% Any d may be octal, hexadecimal or decimal by C language standards. +%% d4 fills all LSB bytes. This is legacy behaviour from Solaris +%% and FreeBSD. And partly Linux that behave the same except +%% it does not accept hexadecimal. %% %% Return {ok, IP} | {error, einval} %% ipv4_address(Cs) -> - case catch ipv4_addr(Cs) of - {'EXIT',_} -> {error,einval}; - Addr -> {ok,Addr} + try ipv4_addr(Cs) of + Addr -> + {ok,Addr} + catch + error:badarg -> + {error,einval} end. -ipv4_addr(Cs) -> - ipv4_addr(d3(Cs), []). +ipv4_addr(Cs) -> + case ipv4_addr(Cs, []) of + [D] when D < (1 bsl 32) -> + <<D1,D2,D3,D4>> = <<D:32>>, + {D1,D2,D3,D4}; + [D,D1] when D < (1 bsl 24), D1 < 256 -> + <<D2,D3,D4>> = <<D:24>>, + {D1,D2,D3,D4}; + [D,D2,D1] when D < (1 bsl 16), (D2 bor D1) < 256 -> + <<D3,D4>> = <<D:16>>, + {D1,D2,D3,D4}; + [D4,D3,D2,D1] when (D4 bor D3 bor D2 bor D1) < 256 -> + {D1,D2,D3,D4}; + _ -> + erlang:error(badarg) + end. -ipv4_addr({Cs0,[]}, A) when length(A) =< 3 -> - case [tod(Cs0)|A] of - [D4,D3,D2,D1] -> +ipv4_addr([_|_], [_,_,_,_]) -> + %% Early bailout for extra characters + erlang:error(badarg); +ipv4_addr("0x"++Cs, Ds) -> + ipv4_addr(strip0(Cs), Ds, [], 16, 8); +ipv4_addr("0X"++Cs, Ds) -> + ipv4_addr(strip0(Cs), Ds, [], 16, 8); +ipv4_addr("0"++Cs, Ds) -> + ipv4_addr(strip0(Cs), Ds, [$0], 8, 11); +ipv4_addr(Cs, Ds) when is_list(Cs) -> + ipv4_addr(Cs, Ds, [], 10, 10). + +ipv4_addr(Cs0, Ds, Rs, Base, N) -> + case ipv4_field(Cs0, N, Rs, Base) of + {D,""} -> + [D|Ds]; + {D,[$.|[_|_]=Cs]} -> + ipv4_addr(Cs, [D|Ds]); + {_,_} -> + erlang:error(badarg) + end. + +strip0("0"++Cs) -> + strip0(Cs); +strip0(Cs) when is_list(Cs) -> + Cs. + + +%% +%% Parse IPv4 strict dotted decimal address, no leading zeros: +%% d1.d2.d3.d4 +%% +%% Return {ok, IP} | {error, einval} +%% +ipv4strict_address(Cs) -> + try ipv4strict_addr(Cs) of + Addr -> + {ok,Addr} + catch + error:badarg -> + {error,einval} + end. + +ipv4strict_addr(Cs) -> + case ipv4strict_addr(Cs, []) of + [D4,D3,D2,D1] when (D4 bor D3 bor D2 bor D1) < 256 -> {D1,D2,D3,D4}; - [D4,D2,D1] -> - {D1,D2,0,D4}; - [D4,D1] -> - {D1,0,0,D4}; - [D4] -> - {0,0,0,D4} - end; -ipv4_addr({Cs0,"."++Cs1}, A) when length(A) =< 2 -> - ipv4_addr(d3(Cs1), [tod(Cs0)|A]). + _ -> + erlang:error(badarg) + end. + +ipv4strict_addr([_|_], [_,_,_,_]) -> + %% Early bailout for extra characters + erlang:error(badarg); +ipv4strict_addr("0", Ds) -> + [0|Ds]; +ipv4strict_addr("0."++Cs, Ds) -> + ipv4strict_addr(Cs, [0|Ds]); +ipv4strict_addr(Cs0, Ds) when is_list(Cs0) -> + case ipv4_field(Cs0, 3, [], 10) of + {D,""} -> + [D|Ds]; + {D,[$.|[_|_]=Cs]} -> + ipv4strict_addr(Cs, [D|Ds]); + {_,_} -> + erlang:error(badarg) + end. + -d3(Cs) -> d3(Cs, []). -d3([C|Cs], R) when C >= $0, C =< $9, length(R) =< 2 -> - d3(Cs, [C|R]); -d3(Cs, [_|_]=R) -> - {lists:reverse(R),Cs}. +ipv4_field("", _, Rs, Base) -> + {ipv4_field(Rs, Base),""}; +ipv4_field("."++_=Cs, _, Rs, Base) -> + {ipv4_field(Rs, Base),Cs}; +ipv4_field("0"++_, _, [], _) -> + erlang:error(badarg); +ipv4_field([C|Cs], N, Rs, Base) when N > 0 -> + ipv4_field(Cs, N-1, [C|Rs], Base); +ipv4_field(Cs, _, _, _) when is_list(Cs) -> + erlang:error(badarg). + +ipv4_field(Rs, Base) -> + V = erlang:list_to_integer(lists:reverse(Rs), Base), + if V < 0 -> + erlang:error(badarg); + true -> + V + end. -tod(Cs) -> - case erlang:list_to_integer(Cs) of - D when D >= 0, D =< 255 -> - D; + + +%% +%% Forgiving IPv6 address +%% +%% Accepts IPv4 address and returns it as a IPv4 compatible IPv6 address +%% +ipv6_address(Cs) -> + case ipv4_address(Cs) of + {ok,{D1,D2,D3,D4}} -> + {ok,{0,0,0,0,0,16#ffff,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}}; _ -> - erlang:error(badarg, [Cs]) + ipv6strict_address(Cs) end. %% -%% Parse IPv6 address: +%% Parse IPv6 address according to RFC 4291: %% x1:x2:x3:x4:x5:x6:x7:x8 %% x1:x2::x7:x8 %% ::x7:x8 @@ -530,77 +625,89 @@ tod(Cs) -> %% ::x5:x6:d7a.d7b.d8a.d8b %% x1:x2::d7a.d7b.d8a.d8b %% ::d7a.d7b.d8a.d8b +%% etc %% %% Return {ok, IP} | {error, einval} %% -ipv6_address(Cs) -> - case catch ipv6_addr(Cs) of - {'EXIT',_} -> {error,einval}; - Addr -> {ok,Addr} +ipv6strict_address(Cs) -> + try ipv6_addr(Cs) of + Addr -> + {ok,Addr} + catch + error:badarg -> + {error,einval} end. ipv6_addr("::") -> - ipv6_addr_done([], []); + ipv6_addr_done([], [], 0); ipv6_addr("::"++Cs) -> - ipv6_addr(x4(Cs), [], []); + ipv6_addr(hex(Cs), [], [], 0); ipv6_addr(Cs) -> - ipv6_addr(x4(Cs), []). + ipv6_addr(hex(Cs), [], 0). %% Before "::" -ipv6_addr({Cs0,[]}, A) when length(A) =:= 7 -> - ipv6_addr_done([tox(Cs0)|A]); -ipv6_addr({Cs0,"::"}, A) when length(A) =< 6 -> - ipv6_addr_done([tox(Cs0)|A], []); -ipv6_addr({Cs0,"::"++Cs1}, A) when length(A) =< 5 -> - ipv6_addr(x4(Cs1), [tox(Cs0)|A], []); -ipv6_addr({Cs0,":"++Cs1}, A) when length(A) =< 6 -> - ipv6_addr(x4(Cs1), [tox(Cs0)|A]); -ipv6_addr({Cs0,"."++Cs1}, A) when length(A) =:= 6 -> - ipv6_addr(d3(Cs1), A, [], [tod(Cs0)]). +ipv6_addr({Cs0,[]}, A, N) when N == 7 -> + ipv6_addr_done([hex_to_int(Cs0)|A]); +ipv6_addr({Cs0,"::"}, A, N) when N =< 6 -> + ipv6_addr_done([hex_to_int(Cs0)|A], [], N+1); +ipv6_addr({Cs0,"::"++Cs1}, A, N) when N =< 5 -> + ipv6_addr(hex(Cs1), [hex_to_int(Cs0)|A], [], N+1); +ipv6_addr({Cs0,":"++Cs1}, A, N) when N =< 6 -> + ipv6_addr(hex(Cs1), [hex_to_int(Cs0)|A], N+1); +ipv6_addr({Cs0,"."++_=Cs1}, A, N) when N == 6 -> + ipv6_addr_done(A, [], N, ipv4strict_addr(Cs0++Cs1)); +ipv6_addr(_, _, _) -> + erlang:error(badarg). %% After "::" -ipv6_addr({Cs0,[]}, A, B) when length(A)+length(B) =< 6 -> - ipv6_addr_done(A, [tox(Cs0)|B]); -ipv6_addr({Cs0,":"++Cs1}, A, B) when length(A)+length(B) =< 5 -> - ipv6_addr(x4(Cs1), A, [tox(Cs0)|B]); -ipv6_addr({Cs0,"."++Cs1}, A, B) when length(A)+length(B) =< 5 -> - ipv6_addr(x4(Cs1), A, B, [tod(Cs0)]). +ipv6_addr({Cs0,[]}, A, B, N) when N =< 6 -> + ipv6_addr_done(A, [hex_to_int(Cs0)|B], N+1); +ipv6_addr({Cs0,":"++Cs1}, A, B, N) when N =< 5 -> + ipv6_addr(hex(Cs1), A, [hex_to_int(Cs0)|B], N+1); +ipv6_addr({Cs0,"."++_=Cs1}, A, B, N) when N =< 5 -> + ipv6_addr_done(A, B, N, ipv4strict_addr(Cs0++Cs1)); +ipv6_addr(_, _, _, _) -> + erlang:error(badarg). -%% After "." -ipv6_addr({Cs0,[]}, A, B, C) when length(C) =:= 3 -> - ipv6_addr_done(A, B, [tod(Cs0)|C]); -ipv6_addr({Cs0,"."++Cs1}, A, B, C) when length(C) =< 2 -> - ipv6_addr(d3(Cs1), A, B, [tod(Cs0)|C]). +ipv6_addr_done(Ar, Br, N, {D1,D2,D3,D4}) -> + ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br], N+2). -ipv6_addr_done(Ar, Br, [D4,D3,D2,D1]) -> - ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br]). - -ipv6_addr_done(Ar, Br) -> - ipv6_addr_done(Br++dup(8-length(Ar)-length(Br), 0, Ar)). +ipv6_addr_done(Ar, Br, N) -> + ipv6_addr_done(Br++dup(8-N, 0, Ar)). ipv6_addr_done(Ar) -> list_to_tuple(lists:reverse(Ar)). -x4(Cs) -> x4(Cs, []). - -x4([C|Cs], R) when C >= $0, C =< $9, length(R) =< 3 -> - x4(Cs, [C|R]); -x4([C|Cs], R) when C >= $a, C =< $f, length(R) =< 3 -> - x4(Cs, [C|R]); -x4([C|Cs], R) when C >= $A, C =< $F, length(R) =< 3 -> - x4(Cs, [C|R]); -x4(Cs, [_|_]=R) -> - {lists:reverse(R),Cs}. - -tox(Cs) -> - erlang:list_to_integer(Cs, 16). +%% Collect Hex digits +hex(Cs) -> hex(Cs, []). +%% +hex([C|Cs], R) when C >= $0, C =< $9 -> + hex(Cs, [C|R]); +hex([C|Cs], R) when C >= $a, C =< $f -> + hex(Cs, [C|R]); +hex([C|Cs], R) when C >= $A, C =< $F -> + hex(Cs, [C|R]); +hex(Cs, [_|_]=R) when is_list(Cs) -> + {lists:reverse(R),Cs}; +hex(_, _) -> + erlang:error(badarg). + +%% Hex string to integer +hex_to_int(Cs0) -> + case strip0(Cs0) of + Cs when length(Cs) =< 4 -> + erlang:list_to_integer("0"++Cs, 16); + _ -> + erlang:error(badarg) + end. +%% Dup onto head of existing list dup(0, _, L) -> L; dup(N, E, L) when is_integer(N), N >= 1 -> - dup(N-1, E, [E|L]); -dup(N, E, L) -> - erlang:error(badarg, [N,E,L]). + dup(N-1, E, [E|L]). + + %% Convert IPv4 adress to ascii %% Convert IPv6 / IPV4 adress to ascii (plain format) @@ -674,7 +781,7 @@ separate(_E, [H], R) -> lists:reverse(R, [H]). %% convert to A.B decimal form -dig_to_dec(0) -> [$0,$.,$0]; +dig_to_dec(0) -> "0.0"; dig_to_dec(X) -> integer_to_list((X bsr 8) band 16#ff) ++ "." ++ integer_to_list(X band 16#ff). diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl index 30c0e85dd9..795bf83807 100644 --- a/lib/kernel/src/inet_sctp.erl +++ b/lib/kernel/src/inet_sctp.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% %% SCTP protocol contribution by Leonid Timochouk and Serge Aleynikov. @@ -64,16 +64,22 @@ close(S) -> listen(S, Flag) -> prim_inet:listen(S, Flag). +%% A non-blocking connect is implemented when the initial call is to +%% gen_sctp:connect_init which passes the value nowait as the Timer connect(S, Addr, Port, Opts, Timer) -> case prim_inet:chgopts(S, Opts) of ok -> case prim_inet:getopt(S, active) of {ok,Active} -> - Timeout = inet:timeout(Timer), + Timeout = if Timer =:= nowait -> + infinity; %% don't start driver timer in inet_drv + true -> + inet:timeout(Timer) + end, case prim_inet:connect(S, Addr, Port, Timeout) of - ok -> + ok when Timer =/= nowait -> connect_get_assoc(S, Addr, Port, Active, Timer); - Err1 -> Err1 + OkOrErr1 -> OkOrErr1 end; Err2 -> Err2 end; @@ -89,10 +95,10 @@ connect(S, Addr, Port, Opts, Timer) -> %% connect_get_assoc/5 below mistakes it for an invalid response %% for a socket in {active,false} or {active,once} modes. %% -%% In {active,true} mode it probably gets right, but it is -%% a blocking connect that is implemented even for {active,true}, -%% and that may be a shortcoming. A non-blocking connect -%% would be nice to have. +%% In {active,true} mode the window of time for the race is smaller, +%% but it is possible and also it is a blocking connect that is +%% implemented even for {active,true}, and that may be a +%% shortcoming. connect_get_assoc(S, Addr, Port, false, Timer) -> case recv(S, inet:timeout(Timer)) of diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 3afaedf274..9875044844 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(net_kernel). @@ -354,13 +354,13 @@ init({Name, LongOrShortNames, TickT}) -> %% The response is delayed until the connection is up and %% running. %% -handle_call({connect, _, Node}, _From, State) when Node =:= node() -> - {reply, true, State}; +handle_call({connect, _, Node}, From, State) when Node =:= node() -> + async_reply({reply, true, State}, From); handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), case ets:lookup(sys_dist, Node) of [Conn] when Conn#connection.state =:= up -> - {reply, true, State}; + async_reply({reply, true, State}, From); [Conn] when Conn#connection.state =:= pending -> Waiting = Conn#connection.waiting, ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), @@ -376,19 +376,19 @@ handle_call({connect, Type, Node}, From, State) -> {noreply,State#state{conn_owners=Owners}}; _ -> ?connect_failure(Node, {setup_call, failed}), - {reply, false, State} + async_reply({reply, false, State}, From) end end; %% %% Close the connection to Node. %% -handle_call({disconnect, Node}, _From, State) when Node =:= node() -> - {reply, false, State}; -handle_call({disconnect, Node}, _From, State) -> +handle_call({disconnect, Node}, From, State) when Node =:= node() -> + async_reply({reply, false, State}, From); +handle_call({disconnect, Node}, From, State) -> verbose({disconnect, Node}, 1, State), {Reply, State1} = do_disconnect(Node, State), - {reply, Reply, State1}; + async_reply({reply, Reply, State1}, From); %% %% The spawn/4 BIF ends up here. @@ -411,39 +411,40 @@ handle_call({spawn_opt,M,F,A,O,L,Gleader},{From,Tag},State) when is_pid(From) -> %% %% Only allow certain nodes. %% -handle_call({allow, Nodes}, _From, State) -> +handle_call({allow, Nodes}, From, State) -> case all_atoms(Nodes) of true -> Allowed = State#state.allowed, - {reply,ok,State#state{allowed = Allowed ++ Nodes}}; + async_reply({reply,ok,State#state{allowed = Allowed ++ Nodes}}, + From); false -> - {reply,error,State} + async_reply({reply,error,State}, From) end; %% %% authentication, used by auth. Simply works as this: %% if the message comes through, the other node IS authorized. %% -handle_call({is_auth, _Node}, _From, State) -> - {reply,yes,State}; +handle_call({is_auth, _Node}, From, State) -> + async_reply({reply,yes,State}, From); %% %% Not applicable any longer !? %% handle_call({apply,_Mod,_Fun,_Args}, {From,Tag}, State) when is_pid(From), node(From) =:= node() -> - gen_server:reply({From,Tag}, not_implemented), + async_gen_server_reply({From,Tag}, not_implemented), % Port = State#state.port, % catch apply(Mod,Fun,[Port|Args]), {noreply,State}; -handle_call(longnames, _From, State) -> - {reply, get(longnames), State}; +handle_call(longnames, From, State) -> + async_reply({reply, get(longnames), State}, From); -handle_call({update_publish_nodes, Ns}, _From, State) -> - {reply, ok, State#state{publish_on_nodes = Ns}}; +handle_call({update_publish_nodes, Ns}, From, State) -> + async_reply({reply, ok, State#state{publish_on_nodes = Ns}}, From); -handle_call({publish_on_node, Node}, _From, State) -> +handle_call({publish_on_node, Node}, From, State) -> NewState = case State#state.publish_on_nodes of undefined -> State#state{publish_on_nodes = @@ -457,11 +458,12 @@ handle_call({publish_on_node, Node}, _From, State) -> Nodes -> lists:member(Node, Nodes) end, - {reply, Publish, NewState}; + async_reply({reply, Publish, NewState}, From); -handle_call({verbose, Level}, _From, State) -> - {reply, State#state.verbose, State#state{verbose = Level}}; +handle_call({verbose, Level}, From, State) -> + async_reply({reply, State#state.verbose, State#state{verbose = Level}}, + From); %% %% Set new ticktime @@ -471,16 +473,16 @@ handle_call({verbose, Level}, _From, State) -> %% #tick_change{} record if the ticker process has been upgraded; %% otherwise, an integer or an atom. -handle_call(ticktime, _, #state{tick = #tick{time = T}} = State) -> - {reply, T, State}; -handle_call(ticktime, _, #state{tick = #tick_change{time = T}} = State) -> - {reply, {ongoing_change_to, T}, State}; +handle_call(ticktime, From, #state{tick = #tick{time = T}} = State) -> + async_reply({reply, T, State}, From); +handle_call(ticktime, From, #state{tick = #tick_change{time = T}} = State) -> + async_reply({reply, {ongoing_change_to, T}, State}, From); -handle_call({new_ticktime,T,_TP}, _, #state{tick = #tick{time = T}} = State) -> +handle_call({new_ticktime,T,_TP}, From, #state{tick = #tick{time = T}} = State) -> ?tckr_dbg(no_tick_change), - {reply, unchanged, State}; + async_reply({reply, unchanged, State}, From); -handle_call({new_ticktime,T,TP}, _, #state{tick = #tick{ticker = Tckr, +handle_call({new_ticktime,T,TP}, From, #state{tick = #tick{ticker = Tckr, time = OT}} = State) -> ?tckr_dbg(initiating_tick_change), start_aux_ticker(T, OT, TP), @@ -493,14 +495,15 @@ handle_call({new_ticktime,T,TP}, _, #state{tick = #tick{ticker = Tckr, ?tckr_dbg(shorter_ticktime), shorter end, - {reply, change_initiated, State#state{tick = #tick_change{ticker = Tckr, - time = T, - how = How}}}; + async_reply({reply, change_initiated, + State#state{tick = #tick_change{ticker = Tckr, + time = T, + how = How}}}, From); -handle_call({new_ticktime,_,_}, - _, +handle_call({new_ticktime,_T,_TP}, + From, #state{tick = #tick_change{time = T}} = State) -> - {reply, {ongoing_change_to, T}, State}. + async_reply({reply, {ongoing_change_to, T}, State}, From). %% ------------------------------------------------------------ %% handle_cast. @@ -1063,11 +1066,12 @@ safesend(Pid, Mess) -> Pid ! Mess. -endif. do_spawn(SpawnFuncArgs, SpawnOpts, State) -> + [_,From|_] = SpawnFuncArgs, case catch spawn_opt(?MODULE, spawn_func, SpawnFuncArgs, SpawnOpts) of - {'EXIT', {Reason,_}} -> - {reply, {'EXIT', {Reason,[]}}, State}; - {'EXIT', Reason} -> - {reply, {'EXIT', {Reason,[]}}, State}; + {'EXIT', {Reason,_}} -> + async_reply({reply, {'EXIT', {Reason,[]}}, State}, From); + {'EXIT', Reason} -> + async_reply({reply, {'EXIT', {Reason,[]}}, State}, From); _ -> {noreply,State} end. @@ -1409,7 +1413,7 @@ reply_waiting(_Node, Waiting, Rep) -> reply_waiting1(lists:reverse(Waiting), Rep). reply_waiting1([From|W], Rep) -> - gen_server:reply(From, Rep), + async_gen_server_reply(From, Rep), reply_waiting1(W, Rep); reply_waiting1([], _) -> ok. @@ -1511,3 +1515,21 @@ verbose(_, _, _) -> getnode(P) when is_pid(P) -> node(P); getnode(P) -> P. + +async_reply({reply, Msg, State}, From) -> + async_gen_server_reply(From, Msg), + {noreply, State}. + +async_gen_server_reply(From, Msg) -> + {Pid, Tag} = From, + M = {Tag, Msg}, + case catch erlang:send(Pid, M, [nosuspend, noconnect]) of + ok -> + ok; + nosuspend -> + spawn(fun() -> catch erlang:send(Pid, M, [noconnect]) end); + noconnect -> + ok; % The gen module takes care of this case. + {'EXIT', _}=EXIT -> + EXIT + end. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 196e6cdeb2..d0b498edc9 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(os). @@ -169,6 +169,7 @@ unix_cmd(Cmd) -> %% $1 parameter for easy identification of the resident shell. %% -define(SHELL, "/bin/sh -s unix:cmd 2>&1"). +-define(PORT_CREATOR_NAME, os_cmd_port_creator). %% %% Serializing open_port through a process to avoid smp lock contention @@ -176,18 +177,37 @@ unix_cmd(Cmd) -> %% -spec start_port() -> port(). start_port() -> - {Ref,Client} = {make_ref(),self()}, - try (os_cmd_port_creator ! {Ref,Client}) - catch - error:_ -> spawn(fun() -> start_port_srv({Ref,Client}) end) - end, + Ref = make_ref(), + Request = {Ref,self()}, + {Pid, Mon} = case whereis(?PORT_CREATOR_NAME) of + undefined -> + spawn_monitor(fun() -> + start_port_srv(Request) + end); + P -> + P ! Request, + M = erlang:monitor(process, P), + {P, M} + end, receive - {Ref,Port} when is_port(Port) -> Port; - {Ref,Error} -> exit(Error) + {Ref, Port} when is_port(Port) -> + erlang:demonitor(Mon, [flush]), + Port; + {Ref, Error} -> + erlang:demonitor(Mon, [flush]), + exit(Error); + {'DOWN', Mon, process, Pid, _Reason} -> + start_port() end. start_port_srv(Request) -> - StayAlive = try register(os_cmd_port_creator, self()) + %% We don't want a group leader of some random application. Use + %% kernel_sup's group leader. + {group_leader, GL} = process_info(whereis(kernel_sup), + group_leader), + true = group_leader(GL, self()), + process_flag(trap_exit, true), + StayAlive = try register(?PORT_CREATOR_NAME, self()) catch error:_ -> false end, @@ -196,7 +216,7 @@ start_port_srv(Request) -> start_port_srv_loop({Ref,Client}, StayAlive) -> Reply = try open_port({spawn, ?SHELL},[stream]) of Port when is_port(Port) -> - port_connect(Port, Client), + (catch port_connect(Port, Client)), unlink(Port), Port catch @@ -205,10 +225,19 @@ start_port_srv_loop({Ref,Client}, StayAlive) -> end, Client ! {Ref,Reply}, case StayAlive of - true -> start_port_srv_loop(receive Msg -> Msg end, true); + true -> start_port_srv_loop(get_open_port_request(), true); false -> exiting end. +get_open_port_request() -> + receive + {Ref, Client} = Request when is_reference(Ref), + is_pid(Client) -> + Request; + _Junk -> + get_open_port_request() + end. + %% %% unix_get_data(Port) -> Result %% diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl index fc9508a194..cb9fec2ffe 100644 --- a/lib/kernel/src/pg2.erl +++ b/lib/kernel/src/pg2.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(pg2). @@ -334,8 +334,11 @@ local_group_members(Name) -> P <- member_in_group(Pid, Name)]. member_in_group(Pid, Name) -> - [{{member, Name, Pid}, N}] = ets:lookup(pg2_table, {member, Name, Pid}), - lists:duplicate(N, Pid). + case ets:lookup(pg2_table, {member, Name, Pid}) of + [] -> []; + [{{member, Name, Pid}, N}] -> + lists:duplicate(N, Pid) + end. member_groups(Pid) -> [Name || [Name] <- ets:match(pg2_table, {{pid, Pid, '$1'}})]. diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl index edf650ec59..17dc5a56a2 100644 --- a/lib/kernel/src/user.erl +++ b/lib/kernel/src/user.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% %% -module(user). @@ -698,7 +698,11 @@ get_chars_more(State, M, F, Xa, Port, Q, Fmt) -> prompt(_Port, '') -> ok; prompt(Port, Prompt) -> - put_port(io_lib:format_prompt(Prompt), Port). + put_port(wrap_characters_to_binary(io_lib:format_prompt(Prompt),unicode, + case get(unicode) of + true -> unicode; + _ -> latin1 + end), Port). %% Convert error code to make it look as before err_func(io_lib, get_until, {_,F,_}) -> |