123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708(*
* Copyright (C) Citrix Systems Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*)let(|>)fg=gflet(++)fgx=f(gx)moduleOp=structtypet=|Debug|Directory|Read|Getperms|Watch|Unwatch|Transaction_start|Transaction_end|Introduce|Release|Getdomainpath|Write|Mkdir|Rm|Setperms|Watchevent|Error|Isintroduced|Resume|Set_target|Restrict(* The index of the value in the array is the integer representation used
by the wire protocol. Every element of t exists exactly once in the array. *)leton_the_wire=[|Debug;Directory;Read;Getperms;Watch;Unwatch;Transaction_start;Transaction_end;Introduce;Release;Getdomainpath;Write;Mkdir;Rm;Setperms;Watchevent;Error;Isintroduced;Resume;Set_target;Restrict|]letof_int32i=leti=Int32.to_intiinifi>=0&&i<Array.lengthon_the_wirethenSome(on_the_wire.(i))elseNoneletto_int32x=matchsnd(Array.fold_left(fun(idx,result)v->ifx=vthen(idx+1,Someidx)else(idx+1,result))(0,None)on_the_wire)with|None->assertfalse(* impossible since on_the_wire contains each element *)|Somei->Int32.of_intiletto_string=function|Debug->"debug"|Directory->"directory"|Read->"read"|Getperms->"getperms"|Watch->"watch"|Unwatch->"unwatch"|Transaction_start->"transaction_start"|Transaction_end->"transaction_end"|Introduce->"introduce"|Release->"release"|Getdomainpath->"getdomainpath"|Write->"write"|Mkdir->"mkdir"|Rm->"rm"|Setperms->"setperms"|Watchevent->"watchevent"|Error->"error"|Isintroduced->"isintroduced"|Resume->"resume"|Set_target->"set_target"|Restrict->"restrict"endletsplit_string?limit:(limit=max_int)cs=letlen=String.lengthsinletnext_cfrom=trySome(String.index_fromsfromc)with|Not_found->Noneinletdecrn=max0(n-1)inletrecloopnfromacc=matchdecrn,next_cfromwith|0,_|_,None->(* No further instances of c, or we've reached limit *)String.subsfrom(len-from)::acc|n',Someidx->leta=String.subsfrom(idx-from)in(loop[@tailcall])n'(idx+1)(a::acc)inlooplimit0[]|>List.revmoduleACL=structtypeperm=|NONE|READ|WRITE|RDWRletchar_of_perm=function|READ->'r'|WRITE->'w'|RDWR->'b'|NONE->'n'letperm_of_char=function|'r'->SomeREAD|'w'->SomeWRITE|'b'->SomeRDWR|'n'->SomeNONE|_->Nonetypedomid=inttypet={owner:domid;(** domain which "owns", has full access *)other:perm;(** default permissions for all others... *)acl:(domid*perm)list;(** ... unless overridden in the ACL *)}letto_stringperms=letstring_of_perm(id,perm)=Printf.sprintf"%c%u"(char_of_permperm)idinString.concat"\000"(List.mapstring_of_perm((perms.owner,perms.other)::perms.acl))letof_strings=(* A perm is stored as '<c>domid' *)letperm_of_char_exnx=match(perm_of_charx)withSomey->y|None->raiseNot_foundintryletperm_of_strings=ifString.lengths<2theninvalid_arg(Printf.sprintf"Permission string too short: '%s'"s);int_of_string(String.subs1(String.lengths-1)),perm_of_char_exns.[0]inletl=List.mapperm_of_string(split_string'\000's)inmatchlwith|(owner,other)::l->Some{owner=owner;other=other;acl=l}|[]->Some{owner=0;other=NONE;acl=[]}with_->Noneendtypet={tid:int32;rid:int32;ty:Op.t;len:int;data:Buffer.t;}(*
type header = {
ty: int32;
rid: int32;
tid: int32;
len: int32;
}
*)letget_header_tyb=Bytes.get_int32_leb0letset_header_tybv=Bytes.set_int32_leb0vletget_header_ridb=Bytes.get_int32_leb4letset_header_ridbv=Bytes.set_int32_leb4vletget_header_tidb=Bytes.get_int32_leb8letset_header_tidbv=Bytes.set_int32_leb8vletget_header_lenb=Bytes.get_int32_leb12letset_header_lenbv=Bytes.set_int32_leb12vletsizeof_header=16letto_bytespkt=letlen=Buffer.lengthpkt.datainletresult=Bytes.create(sizeof_header+len)inset_header_tyresult(Op.to_int32pkt.ty);set_header_ridresultpkt.rid;set_header_tidresultpkt.tid;set_header_lenresult(Int32.of_intlen);Bytes.blit(Buffer.to_bytespkt.data)0resultsizeof_headerlen;resultletget_tidpkt=pkt.tidletget_typkt=pkt.tyletget_datapkt=ifpkt.len>0&&Buffer.nthpkt.data(pkt.len-1)='\000'thenBuffer.subpkt.data0(pkt.len-1)elseBuffer.contentspkt.dataletget_ridpkt=pkt.ridmoduleParser=struct(** Incrementally parse packets *)letxenstore_payload_max=4096(* xen/include/public/io/xs_wire.h *)letallow_oversize_packets=reftruetypestate=|Unknown_operationofint32|Parser_failedofstring|Need_more_dataofint|Packetofttypeparse=|ReadingHeaderofint*bytes|ReadingBodyoft|Finishedofstateletstart()=ReadingHeader(0,Bytes.makesizeof_header'\000')letstate=function|ReadingHeader(got_already,_)->Need_more_data(sizeof_header-got_already)|ReadingBodypkt->Need_more_data(pkt.len-(Buffer.lengthpkt.data))|Finishedr->rletparse_headerbytes=letty=get_header_tybytesinletrid=get_header_ridbytesinlettid=get_header_tidbytesinletlen=get_header_lenbytesinletlen=Int32.to_intlenin(* A packet which is bigger than xenstore_payload_max is illegal.
This will leave the guest connection is a bad state and will
be hard to recover from without restarting the connection
(ie rebooting the guest) *)letlen=if!allow_oversize_packetsthenlenelsemax0(minxenstore_payload_maxlen)inbeginmatchOp.of_int32tywith|Somety->lett={tid=tid;rid=rid;ty=ty;len=len;data=Buffer.createlen;}iniflen=0thenFinished(Packett)elseReadingBodyt|None->Finished(Unknown_operationty)endletinputstate(bytes:string)=matchstatewith|ReadingHeader(got_already,(str:bytes))->letlen=String.lengthbytesinBytes.blit_stringbytes0strgot_alreadylen;letgot_already=got_already+leninifgot_already<sizeof_headerthenReadingHeader(got_already,str)elseparse_headerstr|ReadingBodyx->Buffer.add_stringx.databytes;letneeded=x.len-(Buffer.lengthx.data)inifneeded>0thenReadingBodyxelseFinished(Packetx)|Finishedf->Finishedfend(* Should we switch to an explicit stream abstraction here? *)moduletypeIO=sigtype'atvalreturn:'a->'atval(>>=):'at->('a->'bt)->'bttypechannelvalread:channel->bytes->int->int->inttvalwrite:channel->bytes->int->int->unittendexceptionUnknown_xenstore_operationofint32exceptionResponse_parser_failedofstringexceptionEOFtype('a,'b)result=|Okof'a|Exceptionof'bmodulePacketStream=functor(IO:IO)->structlet(>>=)=IO.(>>=)letreturn=IO.returntypestream={channel:IO.channel;mutableincoming_pkt:Parser.parse;(* incrementally parses the next packet *)}letmaket={channel=t;incoming_pkt=Parser.start();}(* [recv client] returns a single Packet, or fails *)letrecrecvt=letopenParserinmatchParser.statet.incoming_pktwith|Packetpkt->t.incoming_pkt<-start();return(Okpkt)|Need_more_datax->letbuf=Bytes.makex'\000'inIO.readt.channelbuf0x>>=(function|0->return(ExceptionEOF)|n->letfragment=Bytes.sub_stringbuf0nint.incoming_pkt<-inputt.incoming_pktfragment;recvt)|Unknown_operationx->return(Exception(Unknown_xenstore_operationx))|Parser_failedx->return(Exception(Response_parser_failedx))(* [send client pkt] sends [pkt] and returns (), or fails *)letsendtrequest=letreq=to_bytesrequestinIO.writet.channelreq0(Bytes.lengthreq)endmoduleToken=structtypet=string(** [to_user_string x] returns the user-supplied part of the watch token *)letto_user_stringx=Scanf.sscanfx"%d:%s"(fun_x->x)letto_debug_stringx=xletof_stringx=xletto_stringx=xendletdata_concatls=(String.concat"\000"ls)^"\000"letcreatetidridtydata=letlen=String.lengthdatainletb=Buffer.createleninBuffer.add_stringbdata;{tid=tid;rid=rid;ty=ty;len=len;data=b;}moduleResponse=structtypepayload=|Readofstring|Directoryofstringlist|GetpermsofACL.t|Getdomainpathofstring|Transaction_startofint32|Write|Mkdir|Rm|Setperms|Watch|Unwatch|Transaction_end|Debugofstringlist|Introduce|Resume|Release|Set_target|Restrict|Isintroducedofbool|Errorofstring|Watcheventofstring*stringletprettyprint_payload=letopenPrintfinfunction|Readx->sprintf"Read %s"x|Directoryxs->sprintf"Directory [ %s ]"(String.concat"; "xs)|Getpermsacl->sprintf"Getperms %s"(ACL.to_stringacl)|Getdomainpathp->sprintf"Getdomainpath %s"p|Transaction_startx->sprintf"Transaction_start %ld"x|Write->"Write"|Mkdir->"Mkdir"|Rm->"Rm"|Setperms->"Setperms"|Watch->"Watch"|Unwatch->"Unwatch"|Transaction_end->"Transaction_end"|Debugxs->sprintf"Debug [ %s ]"(String.concat"; "xs)|Introduce->"Introduce"|Resume->"Resume"|Release->"Release"|Set_target->"Set_target"|Restrict->"Restrict"|Isintroducedx->sprintf"Isintroduced %b"x|Errorx->sprintf"Error %s"x|Watchevent(x,y)->sprintf"Watchevent %s %s"xyletty_of_payload=function|Read_->Op.Read|Directory_->Op.Directory|Getperms_->Op.Getperms|Getdomainpath_->Op.Getdomainpath|Transaction_start_->Op.Transaction_start|Debug_->Op.Debug|Isintroduced_->Op.Isintroduced|Watchevent(_,_)->Op.Watchevent|Error_->Op.Error|Write->Op.Write|Mkdir->Op.Mkdir|Rm->Op.Rm|Setperms->Op.Setperms|Watch->Op.Watch|Unwatch->Op.Unwatch|Transaction_end->Op.Transaction_end|Introduce->Op.Introduce|Resume->Op.Resume|Release->Op.Release|Set_target->Op.Set_target|Restrict->Op.Restrictletok="OK\000"letdata_of_payload=function|Readx->x|Directoryls->ifls=[]then""elsedata_concatls|Getpermsperms->data_concat[ACL.to_stringperms]|Getdomainpathx->data_concat[x]|Transaction_starttid->data_concat[Int32.to_stringtid]|Debugitems->data_concatitems|Isintroducedb->data_concat[ifbthen"T"else"F"]|Watchevent(path,token)->data_concat[path;token]|Errorx->data_concat[x]|_->okletprintxtidrid=createtidrid(ty_of_payloadx)(data_of_payloadx)endmoduleRequest=structtypepath_op=|Read|Directory|Getperms|Writeofstring|Mkdir|Rm|SetpermsofACL.ttypepayload=|PathOpofstring*path_op|Getdomainpathofint|Transaction_start|Watchofstring*string|Unwatchofstring*string|Transaction_endofbool|Debugofstringlist|Introduceofint*Nativeint.t*int|Resumeofint|Releaseofint|Set_targetofint*int|Restrictofint|Isintroducedofint|Errorofstring|WatcheventofstringopenPrintfletprettyprint_pathopx=function|Read->sprintf"Read %s"x|Directory->sprintf"Directory %s"x|Getperms->sprintf"Getperms %s"x|Writev->sprintf"Write %s %s"xv|Mkdir->sprintf"Mkdir %s"x|Rm->sprintf"Rm %s"x|Setpermsacl->sprintf"Setperms %s %s"x(ACL.to_stringacl)letprettyprint_payload=function|PathOp(path,op)->prettyprint_pathoppathop|Getdomainpathx->sprintf"Getdomainpath %d"x|Transaction_start->"Transaction_start"|Watch(x,y)->sprintf"Watch %s %s"xy|Unwatch(x,y)->sprintf"Unwatch %s %s"xy|Transaction_endx->sprintf"Transaction_end %b"x|Debugxs->sprintf"Debug [ %s ]"(String.concat"; "xs)|Introduce(x,n,y)->sprintf"Introduce %d %nu %d"xny|Resumex->sprintf"Resume %d"x|Releasex->sprintf"Release %d"x|Set_target(x,y)->sprintf"Set_target %d %d"xy|Restrictx->sprintf"Restrict %d"x|Isintroducedx->sprintf"Isintroduced %d"x|Errorx->sprintf"Error %s"x|Watcheventx->sprintf"Watchevent %s"xexceptionParse_failureletstringsdata=split_string'\000'dataletone_stringdata=letargs=split_string~limit:2'\000'datainmatchargswith|x::[]->x|_->raiseParse_failurelettwo_stringsdata=letargs=split_string~limit:2'\000'datainmatchargswith|a::b::[]->a,b|a::[]->a,""(* terminating NULL removed by get_data *)|_->raiseParse_failureletaclx=matchACL.of_stringxwith|Somex->x|None->raiseParse_failureletdomids=letv=ref0inletis_digitc=c>='0'&&c<='9'inletlen=String.lengthsinleti=ref0inwhile!i<len&¬(is_digits.[!i])doincridone;while!i<len&&is_digits.[!i]doletx=(Char.codes.[!i])-(Char.code'0')inv:=!v*10+x;incridone;!vletbool=function|"F"->false|"T"->true|_->raiseParse_failureletparse_exnrequest=letdata=get_datarequestinmatchget_tyrequestwith|Op.Read->PathOp(data|>one_string,Read)|Op.Directory->PathOp(data|>one_string,Directory)|Op.Getperms->PathOp(data|>one_string,Getperms)|Op.Getdomainpath->Getdomainpath(data|>one_string|>domid)|Op.Transaction_start->Transaction_start|Op.Write->letpath,value=two_stringsdatainPathOp(path,Writevalue)|Op.Mkdir->PathOp(data|>one_string,Mkdir)|Op.Rm->PathOp(data|>one_string,Rm)|Op.Setperms->letpath,perms=two_stringsdatainletperms=aclpermsinPathOp(path,Setpermsperms)|Op.Watch->letpath,token=two_stringsdatainWatch(path,token)|Op.Unwatch->letpath,token=two_stringsdatainUnwatch(path,token)|Op.Transaction_end->Transaction_end(data|>one_string|>bool)|Op.Debug->Debug(stringsdata)|Op.Introduce->beginmatchstringsdatawith|d::mfn::port::_->letd=domiddinletmfn=Nativeint.of_stringmfninletport=int_of_stringportinIntroduce(d,mfn,port)|_->raiseParse_failureend|Op.Resume->Resume(data|>one_string|>domid)|Op.Release->Release(data|>one_string|>domid)|Op.Set_target->letmine,yours=two_stringsdatainletmine=domidmineandyours=domidyoursinSet_target(mine,yours)|Op.Restrict->Restrict(data|>one_string|>domid)|Op.Isintroduced->Isintroduced(data|>one_string|>domid)|Op.Error->Error(data|>one_string)|Op.Watchevent->Watchevent(data|>one_string)letparserequest=trySome(parse_exnrequest)with_->Noneletprettyprintrequest=Printf.sprintf"tid = %ld; rid = %ld; payload = %s"(get_tidrequest)(get_ridrequest)(matchparserequestwith|None->"None"|Somex->"Some "^(prettyprint_payloadx))letty_of_payload=function|PathOp(_,Directory)->Op.Directory|PathOp(_,Read)->Op.Read|PathOp(_,Getperms)->Op.Getperms|Debug_->Op.Debug|Watch(_,_)->Op.Watch|Unwatch(_,_)->Op.Unwatch|Transaction_start->Op.Transaction_start|Transaction_end_->Op.Transaction_end|Introduce(_,_,_)->Op.Introduce|Release_->Op.Release|Resume_->Op.Resume|Getdomainpath_->Op.Getdomainpath|PathOp(_,Write_)->Op.Write|PathOp(_,Mkdir)->Op.Mkdir|PathOp(_,Rm)->Op.Rm|PathOp(_,Setperms_)->Op.Setperms|Set_target(_,_)->Op.Set_target|Restrict_->Op.Restrict|Isintroduced_->Op.Isintroduced|Error_->Op.Error|Watchevent_->Op.Watcheventlettransactional_of_payload=function|PathOp(_,_)|Transaction_end_->true|_->falseletdata_of_payload=function|PathOp(path,Writevalue)->path^"\000"^value(* no NULL at the end *)|PathOp(path,Setpermsperms)->data_concat[path;ACL.to_stringperms]|PathOp(path,_)->data_concat[path]|Debugcommands->data_concatcommands|Watch(path,token)|Unwatch(path,token)->data_concat[path;token]|Transaction_start->data_concat[]|Transaction_endcommit->data_concat[ifcommitthen"T"else"F"]|Introduce(domid,mfn,port)->data_concat[Printf.sprintf"%u"domid;Printf.sprintf"%nu"mfn;string_of_intport;]|Releasedomid|Resumedomid|Getdomainpathdomid|Restrictdomid|Isintroduceddomid->data_concat[Printf.sprintf"%u"domid;]|Set_target(mine,yours)->data_concat[Printf.sprintf"%u"mine;Printf.sprintf"%u"yours;]|Error_->failwith"Unimplemented: data_of_payload (Error)"|Watchevent_->failwith"Unimplemented: data_of_payload (Watchevent)"letprintxtidrid=create(iftransactional_of_payloadxthentidelse0l)rid(ty_of_payloadx)(data_of_payloadx)endmoduleUnmarshal=structletsomex=Somexletint_of_string_optx=trySome(int_of_stringx)with_->Noneletint32_of_string_optx=trySome(Int32.of_stringx)with_->Noneletunit_of_string_optx=ifx=""thenSome()elseNoneletokx=ifx="OK"thenSome()elseNoneletstring=some++get_dataletlist=some++split_string'\000'++get_dataletacl=ACL.of_string++get_dataletint=int_of_string_opt++get_dataletint32=int32_of_string_opt++get_dataletunit=unit_of_string_opt++get_dataletok=ok++get_dataendexceptionEnoentofstringexceptionEagainexceptionEexistexceptionInvalidexceptionErrorofstringletresponsehintsentreceivedf=matchget_tysent,get_tyreceivedwith|_,Op.Error->beginmatchget_datareceivedwith|"ENOENT"->raise(Enoenthint)|"EAGAIN"->raiseEagain|"EINVAL"->raiseInvalid|"EEXIST"->raiseEexist|s->raise(Errors)end|x,ywhenx=y->beginmatchfreceivedwith|None->raise(Error(Printf.sprintf"failed to parse response (hint:%s) (payload:%s)"hint(get_datareceived)))|Somez->zend|x,y->raise(Error(Printf.sprintf"unexpected packet: expected %s; got %s"(Op.to_stringx)(Op.to_stringy)))typeaddress=|Unixofstring|Domainofintletstring_of_address=function|Unixx->x|Domainx->string_of_intxletdomain_of_address=function|Unix_->0|Domainx->x