ex11: Smart connection patch

Shawn Pearce <>
Thu Jan 15 06:39:53 CET 2004


Joe, et. al:

Attached is a "smart connection" patch I just put together.  The
changes are mostly down within the ex11_lib_driver and xauth code.
The idea is that ex11_lib_driver:resolve_display_address/1 will take
almost any valid DISPLAY environment variable value that an xdm, gdm,
VNC server, etc. might set, and provide the proper cookie and the
proper host/port.

Usage would be:

	1> inet:gethostname().
	{ok,"asimov"}
	2> ex11_lib_driver:resolve_display_address("").
	{"asimov",0}
	3> ex11_lib_driver:resolve_display_address(":0").
	{"asimov",0}
	4> ex11_lib_driver:resolve_display_address(":0.0").
	{"asimov",0}
	5> ex11_lib_driver:resolve_display_address("span:36.3").
	{"span",36}
	6> ex11_lib_driver:resolve_display_address("span").     
	{"span",0}
	7> ex11_lib_driver:resolve_display_address("localhost").
	{"asimov",0}
	8> ex11_lib_driver:resolve_display_address("127.0.0.1").
	{"asimov",0}

Any string which resolve_display_address can take can be given to
wConnect/2, and whatever it calls to actually setup the X server
connection.  I think this works for just about any crazy case that is
out there, assuming you are using TCP/IP and MIT-MAGIC-COOKIE-1.

I've added a new print/0 and print/1 to the xauth library, useful for
debugging your xauth environment:

	9> ex11_lib_xauth:print().
	ip         10.123.0.2:10        MIT-MAGIC- 761e70c61ae4ab48a294d4de549ab018
	local      debian:10            MIT-MAGIC- 03256abfd0f91fa56d609dc55e01c6b2

The hex cookie data should match up with the output of xauth list.  The
patch will only use cookies which are of the ip family.  The local family
is a UNIX socket connection, which xauth list reports as:

	debian/unix:10  MIT-MAGIC-COOKIE-1  03256abfd0f91fa56d609dc55e01c6b2

and as such will be ignored.  (Before the code did not do this, but I
think it should have been doing so).

The patch does a DNS lookup of the hostname or IP address supplied, then
searches through the xauth data for any cookie with any of those
addresses.  For example, my home server "asimov" thinks that possible
addresses for it would be:

	6> ex11_lib_xauth:display2cookie("asimov",1,X).   
	Hostent={hostent,"localhost",["asimov","asimov.vp"],inet,4,[{127,0,0,1}]}
	PossNames=["asimov","localhost","127.0.0.1","asimov","asimov.vp"]
	{true,[222...162]}

This should help deal with any convoluted cases the code might run into.

I also patched wConnect/2 to have a wConnect/1 which gets the server
address using os:getenv("DISPLAY").  This makes the code behave more
like a traditional X library where applications would (by default) open
on the current display for the user.

--
Shawn.

  Sailing is fun, but scrubbing the decks is aardvark.
  		-- Heard on Noahs' ark
-------------- next part --------------
*** ex11-latest-stable-release/lib/ex11_lib_driver.erl	Sat Jan 10 09:09:17 2004
--- ex11-spearce/lib/ex11_lib_driver.erl	Wed Jan 14 23:54:45 2004
***************
*** 8,23 ****
  %%    {ok, Pid}    if the connection works
  %%    {error, Why} otherwise
  
! -export([test/0, start/0, start/1, send_cmd/2, new_id/1, get_display/2]).
  
  -import(ex11_lib, [pError/1, pEvent/1]).
  -import(lists, [reverse/1]).
  
  -include("ex11_lib.hrl").
  
- test() ->
-     start("enfield.sics.se").
- 
  start() -> start("localhost").
  
  start(Host) ->
--- 8,21 ----
  %%    {ok, Pid}    if the connection works
  %%    {error, Why} otherwise
  
! -export([start/0, start/1, send_cmd/2, new_id/1, get_display/2]).
! -export([resolve_display_address/1]).
  
  -import(ex11_lib, [pError/1, pEvent/1]).
  -import(lists, [reverse/1]).
  
  -include("ex11_lib.hrl").
  
  start() -> start("localhost").
  
  start(Host) ->
***************
*** 42,54 ****
  	    Reply
      end.
  
! init(From, Host) ->
      process_flag(trap_exit,true),
!     %% io:format("Here Host=~p~n",[Host]),
!     case get_cookie(Host) of
  	{true, Cookie} ->
! 	    Opts = [{packet,raw},binary],
! 	    case gen_tcp:connect(Host,6000,Opts) of
  		{ok, Fd} -> 
  		    Res = gen_tcp_send(Fd, ex11_lib:eConnect(Cookie)),
  		    Bin = get_connect_reply(Fd, <<>>),
--- 40,53 ----
  	    Reply
      end.
  
! init(From, Address) ->
      process_flag(trap_exit,true),
! 	{Host, Screen} = resolve_display_address(Address),
!     %% io:format("init Address=~p (~p:~p)~n",[Address, Host, Screen]),
!     case get_cookie(Host, Screen) of
  	{true, Cookie} ->
! 	    Opts = [{packet, raw}, binary],
! 	    case gen_tcp:connect(Host, 6000 + Screen, Opts) of
  		{ok, Fd} -> 
  		    Res = gen_tcp_send(Fd, ex11_lib:eConnect(Cookie)),
  		    Bin = get_connect_reply(Fd, <<>>),
***************
*** 71,90 ****
  	    return(From, {error, noCookie})
      end.
  
! get_cookie(Host) ->
      case read_all_cookies() of
! 	error ->
! 	    {error, cannotFindCookies};
! 	{ok, Cookies} ->
! 	    get_cookie(Host, Cookies)
      end.
  
! get_cookie(Host, Xauth) ->
!     ex11_lib_xauth:host2cookie(Host, Xauth).
  
  read_all_cookies() ->
      case ex11_lib_xauth:filename() of
! 	""   -> {error, "No cookie file"};
  	FName -> ex11_lib_xauth:read(FName)
      end.
  
--- 70,117 ----
  	    return(From, {error, noCookie})
      end.
  
! resolve_display_address(Name) ->
! 	case parse_display_string(string:strip(Name)) of
! 	{"", Number}          -> resolve_localhost_address(Number);
! 	{"localhost", Number} -> resolve_localhost_address(Number);
! 	{"127.0.0.1", Number} -> resolve_localhost_address(Number);
! 	{Server, Number}      -> {Server, Number}
! 	end.
! 
! parse_display_string(":" ++ Name) ->
! 	{"localhost", parse_display_number(Name)};
! parse_display_string(Name) ->
! 	case string:tokens(Name, ":") of
! 	[Host, Num]    -> {Host, parse_display_number(Num)};
! 	[Host]         -> {Host, 0};
! 	[]             -> {"localhost", 0}
! 	end.
! 
! parse_display_number(Str) ->
! 	case string:tokens(Str, ".") of
! 	[Num, _] -> list_to_integer(Num);
! 	[Num]    -> list_to_integer(Num);
! 	[]       -> 0
! 	end.
! 
! resolve_localhost_address(Number) ->
! 	case inet:gethostname() of
! 	{ok, Me} -> {Me, Number};
! 	_        -> {"127.0.0.1", Number}
! 	end.
! 
! get_cookie(Host, Screen) ->
      case read_all_cookies() of
! 	{error, Reason}  -> {error, Reason};
! 	{ok, Cookies} -> get_cookie(Host, Screen, Cookies)
      end.
  
! get_cookie(Host, Screen, Cookies) ->
!     ex11_lib_xauth:display2cookie(Host, Screen, Cookies).
  
  read_all_cookies() ->
      case ex11_lib_xauth:filename() of
! 	""    -> {error, "No cookie file found."};
  	FName -> ex11_lib_xauth:read(FName)
      end.
  
*** ex11-latest-stable-release/widgets/example0.erl	Mon Jan 12 04:12:31 2004
--- ex11-spearce/widgets/example0.erl	Wed Jan 14 23:56:48 2004
***************
*** 2,8 ****
  
  -export([start/0]).
  
! -import(sw_widgets, [mkTopLevel/3,wConnect/2,mkButton/6]).
  
  -include("sw.hrl").
  -define(bg, 16#ffffcc).
--- 2,8 ----
  
  -export([start/0]).
  
! -import(sw_widgets, [mkTopLevel/3,wConnect/1,mkButton/6]).
  
  -include("sw.hrl").
  -define(bg, 16#ffffcc).
***************
*** 11,17 ****
      spawn(fun win/0).
  
  win() ->
!     Connection = wConnect("localhost", "3.1"),
      Win  = Connection !! mkTopLevel(220, 540, ?bg),
      add_examples(Win, 10, 10, 180,
  		 [{"Quit", fun() -> erlang:halt() end},
--- 11,17 ----
      spawn(fun win/0).
  
  win() ->
!     Connection = wConnect("3.1"),
      Win  = Connection !! mkTopLevel(220, 540, ?bg),
      add_examples(Win, 10, 10, 180,
  		 [{"Quit", fun() -> erlang:halt() end},
*** ex11-latest-stable-release/lib/ex11_lib_xauth.erl	Sun Jan  4 13:07:50 2004
--- ex11-spearce/lib/ex11_lib_xauth.erl	Thu Jan 15 00:29:20 2004
***************
*** 27,33 ****
  %%%           To make it work under Windows. Added filename/0.
  %%%
  %%%---------------------------------------------------------------------
! -export([read/1,host2cookie/2, filename/0]).
  
  -import(ex11_lib_utils,[first/2,i16/2,split_list/2]).
  
--- 27,33 ----
  %%%           To make it work under Windows. Added filename/0.
  %%%
  %%%---------------------------------------------------------------------
! -export([read/1, display2cookie/3, filename/0, print/0, print/1]).
  
  -import(ex11_lib_utils,[first/2,i16/2,split_list/2]).
  
***************
*** 83,139 ****
  	{ok,Bin} ->
  	    List = binary_to_list(Bin),
  	    case catch parse_xauth(List) of
! 		{'EXIT',Reason} -> 
! 		    {error,Reason};
! 		Else -> {ok,Else}
  	    end;
  	Error ->
  	    Error
      end.
  
  %% ---------------------------------------------
! %% Get corresponding cookie for given hostname
  
! host2cookie("localhost", Adata) ->
!     case host2cookie1("localhost", Adata) of
! 	T = {true, Cookie} ->
! 	    T;
! 	false -> 
! 	    case inet:gethostname() of
! 		{ok, Host} ->
! 		    case host2cookie1(Host, Adata) of
! 			T = {true, Cookie} ->
! 			    T;
! 			false ->
! 			    get_screen_zero(Adata)
! 		    end;
! 		_ ->
! 		    get_screen_zero(Adata) 
! 	    end
!     end;
! host2cookie(Host, Adata) ->
!     host2cookie1(Host, Adata).
! 
! host2cookie1(Host,Adata) ->
!     case addr2cookie(Host,Adata) of
! 	{true,Cookie} -> {true,Cookie};
! 	false -> addr2cookie(host2ip(Host),Adata)
!     end.
! 
! get_screen_zero(Adata) ->
!     F = fun(A) when A#xauth.number == 0 -> {true,A#xauth.data};
! 	   (_) -> false
! 	end,
!     first(F,Adata).
  
  
! addr2cookie(Address,Adata) ->
!     F = fun(A) when A#xauth.address == Address -> {true,A#xauth.data};
! 	   (_) -> false
  	end,
!     first(F,Adata).
  
-     
  parse_xauth([]) -> [];
  parse_xauth([Fam1,Fam0,Alen1,Alen0|D0]) ->
      Family = i16(Fam1,Fam0),
--- 83,141 ----
  	{ok,Bin} ->
  	    List = binary_to_list(Bin),
  	    case catch parse_xauth(List) of
! 		{'EXIT',Reason} -> {error,Reason};
! 		Else            -> {ok,Else}
  	    end;
  	Error ->
  	    Error
      end.
  
  %% ---------------------------------------------
! %% Print a single xauth cookie record, or a set of records.
  
! print() ->
! 	case filename() of
! 	""    -> {error, "No cookie file"};
! 	FName -> {ok, Cookies} = read(FName), print(Cookies)
! 	end.
! print([]) ->
! 	ok;
! print([Adata|Rest]) ->
! 	print(Adata),
! 	print(Rest);
! print(A = #xauth{}) ->
! 	io:format("~-10.10s ~-20s ~-10s ~s~n", [
! 		family2str(A#xauth.family),
! 		io_lib:format("~s:~w", [A#xauth.address, A#xauth.number]),
! 		A#xauth.name, bstr2hstr(A#xauth.data)
! 	]).
  
+ %% ---------------------------------------------
+ %% Get corresponding cookie for given hostname
  
! display2cookie(Address, Number, Adata) ->
! 	PossNames = case inet:gethostbyname(Address, inet) of
! 	{ok, Hostent} ->
! 		   [Address, Hostent#hostent.h_name]
! 		++ [ip2str(A) || A <- Hostent#hostent.h_addr_list]
! 		++ Hostent#hostent.h_aliases;
! 	{error, Reason} ->
! 		Address
  	end,
! 	F = fun(A)
! 		when A#xauth.number == Number,
! 			 A#xauth.family == ?FAMILY_IP_ADDRESS
! 			->
! 			case lists:member(A#xauth.address, PossNames) of
! 			true	-> {true, A#xauth.data};
! 			false	-> false
! 			end;
! 		(_)
! 			-> false
! 	end,
! 	first(F, Adata).
! 
  
  parse_xauth([]) -> [];
  parse_xauth([Fam1,Fam0,Alen1,Alen0|D0]) ->
      Family = i16(Fam1,Fam0),
***************
*** 150,156 ****
      {Data,Rest} = split_list(Dlen,D6),
      [#xauth{family=Family,
  	    address=is_ip(Family,Address),
! 	    number=Number,
  	    name=Name,
  	    data=Data}|
       parse_xauth(Rest)].
--- 152,158 ----
      {Data,Rest} = split_list(Dlen,D6),
      [#xauth{family=Family,
  	    address=is_ip(Family,Address),
! 	    number=list_to_integer(Number),
  	    name=Name,
  	    data=Data}|
       parse_xauth(Rest)].
***************
*** 170,180 ****
  ip2str(X3,X2,X1,X0) ->
      lists:flatten(io_lib:format("~w.~w.~w.~w",[X3,X2,X1,X0])).
  
  
  
  
! 
! 
! 
! 
  
--- 172,190 ----
  ip2str(X3,X2,X1,X0) ->
      lists:flatten(io_lib:format("~w.~w.~w.~w",[X3,X2,X1,X0])).
  
+ bstr2hstr([]) 		-> [];
+ bstr2hstr([X|List]) -> byte2hex(X) ++ bstr2hstr(List).
  
+ byte2hex(X) -> [nibble2hex(X bsr 4), nibble2hex(X band 15)].
  
+ nibble2hex(X) when X >= 0, X =< 9 -> $0 + X;
+ nibble2hex(X) -> $a + (X - 10).
  
! family2str(?FAMILY_LOCAL)          -> "local";
! family2str(?FAMILY_WILD)           -> "wild";
! family2str(?FAMILY_NETNAME)        -> "netname";
! family2str(?FAMILY_KRB5_PRINCIPAL) -> "krb5";
! family2str(?FAMILY_LOCALHOST)      -> "localhost";
! family2str(?FAMILY_IP_ADDRESS)     -> "ip";
! family2str(X)                      -> X.
  
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20040115/27643ad9/attachment.bin>


More information about the erlang-questions mailing list