Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file oBus_transport.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292(*
* oBus_transport.ml
* -----------------
* Copyright : (c) 2009, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of obus, an ocaml implementation of D-Bus.
*)letsection=Lwt_log.Section.make"obus(transport)"openUnixopenPrintfopenOBus_addressopenLwt.Infix(* +-----------------------------------------------------------------+
| Types and constructors |
+-----------------------------------------------------------------+ *)typet={recv:unit->OBus_message.tLwt.t;send:OBus_message.t->unitLwt.t;capabilities:OBus_auth.capabilitylist;shutdown:unit->unitLwt.t;}letmake?switch~recv~send?(capabilities=[])~shutdown()=lettransport={recv=recv;send=send;capabilities=capabilities;shutdown=shutdown;}inLwt_switch.add_hookswitchtransport.shutdown;transportletrecvt=t.recv()letsendtmessage=t.sendmessageletcapabilitiest=t.capabilitiesletshutdownt=t.shutdown()(* +-----------------------------------------------------------------+
| Socket transport |
+-----------------------------------------------------------------+ *)letsocket?switch?(capabilities=[])fd=lettransport=ifList.mem`Unix_fdcapabilitiesthenletreader=OBus_wire.readerfdandwriter=OBus_wire.writerfdin{recv=(fun_->OBus_wire.read_message_with_fdsreader);send=(funmsg->OBus_wire.write_message_with_fdswritermsg);capabilities=capabilities;shutdown=(fun_->let%lwt()=OBus_wire.close_readerreader<&>OBus_wire.close_writerwriterinLwt_unix.shutdownfdSHUTDOWN_ALL;Lwt_unix.closefd)}elseletic=Lwt_io.of_fd~mode:Lwt_io.input~close:Lwt.returnfdandoc=Lwt_io.of_fd~mode:Lwt_io.output~close:Lwt.returnfdin{recv=(fun_->OBus_wire.read_messageic);send=(funmsg->OBus_wire.write_messageocmsg);capabilities=capabilities;shutdown=(fun_->let%lwt()=Lwt_io.closeic<&>Lwt_io.closeocinLwt_unix.shutdownfdSHUTDOWN_ALL;Lwt_unix.closefd)}inLwt_switch.add_hookswitchtransport.shutdown;transport(* +-----------------------------------------------------------------+
| Loopback transport |
+-----------------------------------------------------------------+ *)letloopback()=letmvar=Lwt_mvar.create_empty()in{recv=(fun_->Lwt_mvar.takemvar);send=(funm->Lwt_mvar.putmvar{mwithOBus_message.body=OBus_value.V.sequence_dup(OBus_message.bodym)});capabilities=[`Unix_fd];shutdown=Lwt.return}(* +-----------------------------------------------------------------+
| Addresses -> transport |
+-----------------------------------------------------------------+ *)letmake_socketdomaintypaddr=letfd=Lwt_unix.socketdomaintyp0in(tryLwt_unix.set_close_on_execfdwith_->());try%lwtlet%lwt()=Lwt_unix.connectfdaddrinLwt.return(fd,domain)withexn->let%lwt()=Lwt_unix.closefdinLwt.failexnletrecwrite_noncefdnonceposlen=Lwt_unix.write_stringfdnonce016>>=function|0->Lwt.fail(Failure"OBus_transport.connect: failed to send the nonce to the server")|n->ifn=lenthenLwt.return()elsewrite_noncefdnonce(pos+n)(len-n)letmake_socket_noncenonce_filedomaintypaddr=matchnonce_filewith|None->Lwt.fail(Invalid_argument"OBus_transport.connect: missing 'noncefile' parameter")|Somefile_name->let%lwtnonce=try%lwtLwt_io.with_file~mode:Lwt_io.inputfile_name(Lwt_io.read~count:16)with|Unix.Unix_error(err,_,_)->Lwt.fail(Failure(Printf.sprintf"failed to read the nonce file '%s': %s"file_name(Unix.error_messageerr)))|End_of_file->Lwt.fail(Failure(Printf.sprintf"OBus_transport.connect: '%s' is an invalid nonce-file"file_name))inifString.lengthnonce<>16thenLwt.fail(Failure(Printf.sprintf"OBus_transport.connect: '%s' is an invalid nonce-file"file_name))elsebeginlet%lwtfd,domain=make_socketdomaintypaddrinlet%lwt()=write_noncefdnonce016inLwt.return(fd,domain)endletrecconnectaddress=matchOBus_address.nameaddresswith|"unix"->beginmatch(OBus_address.arg"path"address,OBus_address.arg"abstract"address,OBus_address.arg"tmpdir"address)with|Somepath,None,None->make_socketPF_UNIXSOCK_STREAM(ADDR_UNIXpath)|None,Someabst,None->make_socketPF_UNIXSOCK_STREAM(ADDR_UNIX("\x00"^abst))|None,None,Sometmpd->Lwt.fail(Invalid_argument"OBus_transport.connect: unix tmpdir can only be used as a listening address")|_->Lwt.fail(Invalid_argument"OBus_transport.connect: invalid unix address, must supply exactly one of 'path', 'abstract', 'tmpdir'")end|("tcp"|"nonce-tcp")asname->beginlethost=matchOBus_address.arg"host"addresswith|Somehost->host|None->""andport=matchOBus_address.arg"port"addresswith|Someport->port|None->"0"inletopts=[AI_SOCKTYPESOCK_STREAM]inletopts=matchOBus_address.arg"family"addresswith|Some"ipv4"->AI_FAMILYPF_INET::opts|Some"ipv6"->AI_FAMILYPF_INET6::opts|Somefamily->Printf.ksprintfinvalid_arg"OBus_transport.connect: unknown address family '%s'"family|None->optsinLwt_unix.getaddrinfohostportopts>>=function|[]->Lwt.fail(Failure(Printf.sprintf"OBus_transport.connect: no address info for host=%s port=%s%s"hostport(matchOBus_address.arg"family"addresswith|None->""|Somef->" family="^f)))|ai::ais->letmake_socket=ifname="nonce-tcp"thenmake_socket_nonce(OBus_address.arg"noncefile"address)elsemake_socketintry%lwtmake_socketai.ai_familyai.ai_socktypeai.ai_addrwithexn->(* If the first connection failed, try with all the
other ones: *)letrecfind=function|[]->(* If all connection failed, raise the error for
the first address: *)Lwt.failexn|ai::ais->try%lwtmake_socketai.ai_familyai.ai_socktypeai.ai_addrwithexn->findaisinfindaisend|"launchd"->beginmatchOBus_address.arg"env"addresswith|Someenv->let%lwtpath=try%lwtLwt_process.pread_line("launchctl",[|"launchctl";"getenv";env|])withexn->let%lwt()=Lwt_log.error_f~exn~section"launchctl failed"inLwt.failexninmake_socketPF_UNIXSOCK_STREAM(ADDR_UNIXpath)|None->Lwt.fail(Invalid_argument"OBus_transport.connect: missing 'env' in launchd address")end|"autolaunch"->beginlet%lwtaddresses=let%lwtuuid=Lazy.forceOBus_info.machine_uuidinlet%lwtline=try%lwtLwt_process.pread_line("dbus-launch",[|"dbus-launch";"--autolaunch";OBus_uuid.to_stringuuid;"--binary-syntax"|])withexn->let%lwt()=Lwt_log.error_f~exn~section"autolaunch failed"inLwt.failexninletline=tryString.subline0(String.indexline'\000')with_->lineintry%lwtLwt.return(OBus_address.of_stringline)withOBus_address.Parse_failure(addr,pos,reason)asexn->let%lwt()=Lwt_log.error_f~section"autolaunch returned an invalid address %S, at position %d: %s"addrposreasoninLwt.failexninmatchaddresseswith|[]->let%lwt()=Lwt_log.error_f~section"'autolaunch' returned no addresses"inLwt.fail(Failure"'autolaunch' returned no addresses")|address::rest->try%lwtconnectaddresswithexn->letrecfind=function|[]->Lwt.failexn|address::rest->try%lwtconnectaddresswithexn->findrestinfindrestend|name->Lwt.fail(Failure("unknown transport type: "^name))letof_addresses?switch?(capabilities=OBus_auth.capabilities)?mechanismsaddresses=Lwt_switch.checkswitch;matchaddresseswith|[]->Lwt.fail(Invalid_argument"OBus_transport.of_addresses: no address given")|addr::rest->(* Search an address for which connection succeed: *)let%lwtfd,domain=try%lwtconnectaddrwithexn->(* If the first try fails, try with the others: *)letrecfind=function|[]->(* If they all fail, raise the first exception: *)Lwt.failexn|addr::rest->try%lwtconnectaddrwithexn->findrestinfindrestin(* Do authentication only once: *)try%lwtLwt_unix.write_stringfd"\x00"01>>=function|0->Lwt.fail(OBus_auth.Auth_failure"failed to send the initial null byte")|1->let%lwtguid,capabilities=OBus_auth.Client.authenticate~capabilities:(List.filter(function`Unix_fd->domain=PF_UNIX)capabilities)?mechanisms~stream:(OBus_auth.stream_of_fdfd)()inLwt.return(guid,socket?switch~capabilitiesfd)|n->assertfalsewithexn->Lwt_unix.shutdownfdSHUTDOWN_ALL;let%lwt()=Lwt_unix.closefdinLwt.failexn