-module(tv_lib_connect). %% Copyright (C) 2004 by Joe Armstrong (joe@sics.se) %% All rights reserved. %% The copyright holder hereby grants the rights of usage, distribution %% and modification of this software to everyone and for any purpose, as %% long as this license and the copyright notice above are preserved and %% not modified. There is no warranty for this software. %% 2004-01-01 Origional version by by joe@sics.se %% 2004-01-17 Improved handling of code to resolve display addresses by %% Shawn Pearce %% 2004-01-28 Fixed get_connect_reply which only worked if the %% complete reply was read in one go with no extra data. %% Frej Drejhammar %% 2004-01-31 Added code from Luke Gorrie for handling of unix domain %% sockets %% 2004-02-15 Added initial support for multiple screens %% Frej Drejhammar %% tv_connect only handles the connection %% connection is complex - mainly because some large data structures need %% parsing. %% tv_driver:start(Host) -> %% {ok, {Pid,Display}} if the connection works %% {error, Why} otherwise %% author: joe@sics.se %% tv_connect only handles the connection %% connection is complex - mainly because some large data structures need %% parsing. %% tv_lib_connect:start(Host) -> %% {ok, {Display, Screen, Fd}} if the connection works %% {error, Why} otherwise -export([start/1, find_my_real_ip_address/0]). -import(lists, [all/2, filter/2, foldl/3, map/2, member/2, reverse/1]). -include("include/tv.hrl"). -include_lib("kernel/include/inet.hrl"). start(Display) -> io:format("Trying to open connection with:~s~n",[Display]), case tv_lib_xauth:read() of {ok, Xauth} -> %% io:format("Xauth~n"), tv_lib_xauth:print(Xauth), case parse_display(Display) of {ok, {Host, DisplayNumber, ScreenNumber}}-> %% Get all the relevant entries from Xauth Es = tv_lib_xauth:get_display( Xauth, DisplayNumber), Es1 = map(fun({X,Y,_}) -> {X,Y} end, Es), %% io:format("Start Host=~p Es=~n~p~n",[Host,Es]), Try = tryList(Host, DisplayNumber, Es), %% io:format("Try these:~p~n",[Try]), case try_to_start(Try, ScreenNumber) of error -> {ok, HostName} = inet:gethostname(), make_error_report(Display, Host, DisplayNumber, ScreenNumber, HostName, Es, Try), io:format("initialisation failed~n"), io:format("Please mail the file startup_error_report to joe@sics.se~n"), error; Other -> {ok, HostName} = inet:gethostname(), make_error_report(Display, Host, DisplayNumber, ScreenNumber, HostName, Es, Try), Other end; Error -> Error end; E -> io:format("cannot read .Xauthority~p~n",[E]), E end. make_error_report(Display, Host, DisplayNumber, ScreenNumber, HostName, Es, Try) -> {ok, S } = file:open("startup_error_report", [write]), io:format(S, "$DISPLAY = ~s~n", [Display]), io:format(S, "parsed display Host=~p DisplayNumber=~p ScreenNumber=~p~n", [Host, DisplayNumber, ScreenNumber]), io:format(S, "inet:gethostname() = ~p~n", [HostName]), Labels = make_labels(Es, 1, dict:new()), Es1 = rename_es(Es, Labels), Try1 = rename_try(Try, Labels), io:format(S, "Es~n", []), lists:foreach(fun(I) -> io:format(S, "~p~n", [I]) end, Es1), io:format(S, "Try list~n", []), lists:foreach(fun(I) -> io:format(S, "~p~n", [I]) end, Try1), file:close(S). make_labels([{_,_,Code}|T], Max, D) -> case dict:find(Code, D) of error -> make_labels(T, Max+1, dict:store(Code, Max, D)); _ -> make_labels(T, Max, D) end; make_labels([], _, D) -> D. rename_es(Es, D) -> map(fun({X,Y,C}) -> {X,Y,"Code" ++ integer_to_list(dict:fetch(C, D))} end, Es). rename_try(Try, D) -> map(fun({X,Y,C}) -> {X,Y,"Code" ++ integer_to_list(dict:fetch(C, D))} end, Try). tryList(Host, Display, Es) -> %% io:format("tryList Host=~p Dispay=~p Es=~p~n",[Host, Display, Es]), All = tryList1(Host, Display, Es) ++ tryList1({host,"localhost"}, Display, Es) ++ tryList1({host,"127.0.0.1"}, Display, Es) ++ tryList1(everything, Display, Es), remove_duplicates(All, []). remove_duplicates([H|T], L) -> case member(H, L) of true -> remove_duplicates(T, L); false -> remove_duplicates(T, [H|L]) end; remove_duplicates([], L) -> reverse(L). tryList1(everything, Display, Es) -> [{unix,Display,Code} || {unix,Name,Code} <- Es] ++ [{{ip,"localhost"},Display,Code} || {ip,Name,Code} <- Es]; tryList1(none, Display, Es) -> %% If no hostname given look up the hostname {ok, HostName} = inet:gethostname(), [{unix,Display,Code} || {unix,Name,Code}<-Es, matches(HostName,Name)] ++ [{{ip,"localhost"},Display,Code} || {ip,Name,Code}<-Es, Name == HostName]; tryList1({host, "localhost"}, Display, Es) -> %% io:format("local host specified - checking my hostname~n"), {ok, HostName} = inet:gethostname(), %% io:format("Hostname:~s~n",[HostName]), %% Hack city this is listed as a unix socket - but *really* %% it's on a port [{{ip,"localhost"},Display,Code} || {unix,Name,Code} <- Es, matches(HostName, Name)]; tryList1({host, "127.0.0.1"}, Display, Es) -> [{{ip,"localhost"},Display,Code} || {ip,"127.0.0.1", Code} <- Es]; tryList1({host, H}, Display, Es) -> [{{ip,H},Display,Code} ||{ip,Name,Code}<-Es, matches(H, Name)]. matches(X, Y) -> matches1(X, Y). matches1([], _) -> true; matches1([H|T],[H|T1]) -> matches1(T, T1); matches1(_,_) -> false. %%---------------------------------------------------------------------- %% iterate down the list of possible starting points %% try_to_start(List, Screen) %% List = [{Host,Display,Cookie}] where Host = {ip, HostName} | unix %% If Host = {ip,HostName} we open socket 6000+Display on HostName %% If Host = unix we open unix domain socket try_to_start([], Screen) -> error; try_to_start([H|T], Screen) -> case try_to_connect(H, Screen) of O = {ok, D} -> O; {error, _} -> try_to_start(T, Screen) end. %%---------------------------------------------------------------------- %% try_to_connect({Host,Display,Cookie}, Screen) -> %% {ok, {Display, Screen, Fd}} | error %% Host = unix | {ip, IP} | local try_to_connect({Host, Display, Cookie}, Screen) -> io:format("Trying Host=~p Display=~p Screen=~p~n", [Host, Display, Screen]), case connect(Host, Display) of {ok, Fd} -> io:format("Port opened sending cookie:~n"), Res = send(Fd, tv_lib:eConnect(Cookie)), Bin = get_connect_reply(Fd, <<>>), case tv_lib:pConnect(Bin, Screen) of {ok, Dpy} -> %% io:format("Display=~p~n",[Dpy]), %% ?PRINT_DISPLAY(Dpy), {ok, {Dpy, Screen, Fd}}; Error -> Error end; Error = {error, Why} -> io:format("cannot connect reason:~p~n",[Why]), Error end. connect(unix, Display) -> io:format("Connecting to unix domain socket:~p~n",[Display]), Path = lists:flatten(io_lib:format("/tmp/.X11-unix/X~p", [Display])), case gen_tcp:connect({local, Path}, 0, [{packet,raw}, binary]) of {ok, Sock} -> {ok, {tcp, Sock}}; Err -> Err end; connect({ip,IP}, Display) -> io:format("Connecting to tcp port:~p~n",[6000+Display]), case gen_tcp:connect(IP, 6000+Display, [{packet,raw}, binary]) of {ok, Sock} -> {ok, {tcp, Sock}}; Err -> Err end; connect(local, Display) -> case gen_tcp:connect("localhost", 6000+Display, [{packet,raw}, binary]) of {ok, Sock} -> {ok, {tcp, Sock}}; Err -> Err end. %%---------------------------------------------------------------------- %% If DISPLAY=Blaaa:N.M %% {V1,V2,V3} each V1 = Str | none V1 = V2 = int %% %% parse_display(DISPLAY) -> %% {ok, {unix, {Display,Screen}}} | %% {ok, {ip, Host, {Display,Screen}}} | %% {error, Why} parse_display(DISPLAY) -> case (catch parse_display1(DISPLAY)) of {'EXIT', Why} -> {error, {display,DISPLAY, Why}}; Other -> {ok, Other} end. parse_display1(":" ++ T) -> {none, parse_display_number(T), parse_screen_number(T)}; parse_display1(Name) -> case string:tokens(Name, ":") of [Host, Num] -> {{host, Host}, parse_display_number(Num), parse_screen_number(Num)}; [Host] -> {{host, Host}, 0, 0}; [] -> exit(badDisplay) end. %% parse_display_number("N.M") -> N %% parse_display_number("N") -> N parse_display_number(Str) -> case string:tokens(Str, ".") of [Num|_] -> list_to_integer(Num); Num -> list_to_integer(Num) end. parse_screen_number(Str) -> case string:tokens(Str, ".") of [_, Num] -> list_to_integer(Num); _ -> 0 end. %%---------------------------------------------------------------------- %% Get connect_reply %% Has two passes first we have to do the frameing to make sure the %% entire data structue has been retreived then we parse the reply. %% The connection reply is 8 + (LengthOfExtraData) * 4 bytes long %% the length is a two byte quantity in bytes 7..8 of the reply get_connect_reply(Fd, Bin0) -> Bin = my_concat_binary(Bin0, recv(Fd)), case Size = size(Bin) of N when N < 8 -> get_connect_reply(Fd, Bin); _ -> <<_:48, LenExtra:16,_/binary>> = Bin, Need = 8 + LenExtra*4, if Need > Size -> get_connect_reply(Fd, Bin); Need =< Size -> Bin end end. my_concat_binary(<<>>, B) -> B; my_concat_binary(B1, B2) -> list_to_binary([B1, B2]). recv({unix, S}) -> receive {unixdom, S, Data} -> Data end; recv({tcp, Fd}) -> receive {tcp,Fd,Data} -> %% io:format("Received ~w bytes from server~n~p~n", %% [size(Data), Data]), Data end. send({unix, S}, Bin) -> unixdom2:send(S, Bin), true; send({tcp, Fd}, Bin) -> %% io:format("[~w] Sending ~w bytes to server~n~p~n", %% [Seq, size(Bin), Bin]), gen_tcp:send(Fd, Bin), %sleep(1500). true. find_my_real_ip_address() -> %% return my real IP address(s) case inet:gethostname() of {ok,Host} -> case inet:gethostbyname(Host) of {ok, H} -> %% io:format("Here1:~p~n",[H]), IPs = H#hostent.h_addr_list, IPs1 = map(fun(I) -> ip2str(I) end, IPs), %% io:format("My real IP=~s~n",[IPs1]), {ok, IPs1}; {error, _} -> {error, 'cannot Determine Local IP Address'} end; {error, _}-> {error, 'connot determine Hostname'} end. %% force_hostOrIp_to_ip(HostOrIP) -> %% Host is either an IP or a hostname %% If it is a hostname then convert it to an IP %% io:format("implement force_hostOrIp_to_ip:~p~n",[HostOrIP]), %% case isIP(HostOrIP) of %% false -> %% case inet:gethostbyname(HostOrIP) of %% {ok, H} -> %% io:format("Here2:~p~n",[H]), %% IPs = H#hostent.h_addr_list, %% IPs; %% {error, _} -> %% {error, 'cannot Determine IP Address of', HostOrIP} %% end; %% true -> %% HostOrIP %% end. %% isIP("123.45....") -> true %% isIP("aaa.ddd....") -> false %% isIP(X) -> %% X1 = filter(fun($.) -> false; (_) -> true end, X), %% all(fun is_digit/1, X1). %% is_digit(X) when X >= $0, X =< $9 -> true; %% is_digit(_) -> false. ip2str({X3,X2,X1,X0}) -> ip2str(X3,X2,X1,X0); ip2str([X3,X2,X1,X0]) -> ip2str(X3,X2,X1,X0). ip2str(X3,X2,X1,X0) -> lists:flatten(io_lib:format("~w.~w.~w.~w",[X3,X2,X1,X0])).