Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file CCUnix.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348(* This file is free software, part of containers. See file "license" for more details. *)(** {1 High-level Functions on top of Unix} *)type'aor_error=('a,string)resulttype'agen=unit->'aoption(** {2 Calling Commands} *)letint_of_process_status=function|Unix.WEXITEDi|Unix.WSIGNALEDi|Unix.WSTOPPEDi->iletstr_existssp=letrecfspi=ifi=String.lengthsthenfalseelseps.[i]||fsp(i+1)infsp0letreciter_genfg=matchg()with|None->()|Somex->fx;iter_genfgletfinally_fx~h=trylety=fxinignore(h());ywithe->ignore(h());raisee(* print a string, but escaped if required *)letescape_strs=ifstr_existss(function' '|'"'|'\''|'\n'|'\t'->true|_->false)then(letbuf=Buffer.create(String.lengths)inBuffer.add_charbuf'\'';String.iter(function|'\''->Buffer.add_stringbuf"'\\''"|c->Buffer.add_charbufc)s;Buffer.add_charbuf'\'';Buffer.contentsbuf)elses(*$T
escape_str "foo" = "foo"
escape_str "foo bar" = "'foo bar'"
escape_str "fo'o b'ar" = "'fo'\\''o b'\\''ar'"
*)letread_all?(size=1024)ic=letbuf=ref(Bytes.createsize)inletlen=ref0intrywhiletruedo(* resize *)if!len=Bytes.length!bufthen(buf:=Bytes.extend!buf0!len;);assert(Bytes.length!buf>!len);letn=inputic!buf!len(Bytes.length!buf-!len)inlen:=!len+n;ifn=0thenraiseExit;(* exhausted *)done;assertfalse(* never reached*)withExit->Bytes.sub_string!buf0!lentypecall_result=<stdout:string;stderr:string;status:Unix.process_status;errcode:int;(** Extracted from status *)>letkbprintf'buffmtk=Printf.kbprintfkbuffmtletcall_full_inner?(bufsize=2048)?(stdin=`Str"")?(env=Unix.environment())~fcmd=(* render the command *)letbuf=Buffer.create256inkbprintf'bufcmd(funbuf->letcmd=Buffer.contentsbufinletoc,ic,errc=Unix.open_process_fullcmdenvin(* send stdin *)beginmatchstdinwith|`Strs->output_stringics|`Geng->iter_gen(output_stringic)gend;close_outic;(* read out and err *)letout=read_all~size:bufsizeocinleterr=read_all~size:bufsizeerrcinletstatus=Unix.close_process_full(oc,ic,errc)inf(out,err,status))letcall_full?bufsize?stdin?envcmd=call_full_inner?bufsize?stdin?envcmd~f:(fun(out,err,status)->objectmethodstdout=outmethodstderr=errmethodstatus=statusmethoderrcode=int_of_process_statusstatusend)(*$T
call_full ~stdin:(`Str "abc") "cat" |> stdout = "abc"
call_full "echo %s" (escape_str "a'b'c") |> stdout = "a'b'c\n"
call_full "echo %s" "a'b'c" |> stdout = "abc\n"
*)letcall?bufsize?stdin?envcmd=call_full_inner?bufsize?stdin?envcmd~f:(fun(out,err,status)->out,err,int_of_process_statusstatus)letcall_stdout?bufsize?stdin?envcmd=call_full_inner?bufsize?stdin?envcmd~f:(fun(out,_err,_status)->out)(*$T
call_stdout ~stdin:(`Str "abc") "cat" = "abc"
call_stdout "echo %s" (escape_str "a'b'c") = "a'b'c\n"
call_stdout "echo %s" "a'b'c" = "abc\n"
*)typeline=stringtypeasync_call_result=<stdout:linegen;stderr:linegen;stdin:line->unit;(* send a line *)close_in:unit;(* close stdin *)close_err:unit;close_out:unit;close_all:unit;(* close all 3 channels *)wait:Unix.process_status;(* block until the process ends *)wait_errcode:int;(* block until the process ends, then extract errcode *)>letasync_call?(env=Unix.environment())cmd=(* render the command *)letbuf=Buffer.create256inkbprintf'bufcmd(funbuf->letcmd=Buffer.contentsbufinletoc,ic,errc=Unix.open_process_fullcmdenvinobject(self)methodstdout()=trySome(input_lineoc)withEnd_of_file->Nonemethodstderr()=trySome(input_lineerrc)withEnd_of_file->Nonemethodstdinl=output_stringicl;output_charic'\n'methodclose_in=close_outicmethodclose_out=close_inocmethodclose_err=close_inerrcmethodclose_all=close_outic;close_inoc;close_inerrc;()methodwait=Unix.close_process_full(oc,ic,errc)methodwait_errcode=int_of_process_statusself#waitend)letstdoutx=x#stdoutletstderrx=x#stderrletstatusx=x#statusleterrcodex=x#errcodeletwith_in?(mode=0o644)?(flags=[])file~f=letfd=Unix.openfilefile(Unix.O_RDONLY::flags)modeinletic=Unix.in_channel_of_descrfdinfinally_fic~h:(fun()->Unix.closefd)letwith_out?(mode=0o644)?(flags=[Unix.O_CREAT;Unix.O_TRUNC])file~f=letfd=Unix.openfilefile(Unix.O_WRONLY::flags)modeinletoc=Unix.out_channel_of_descrfdinfinally_foc~h:(fun()->flushoc;Unix.closefd)letwith_process_incmd~f=letic=Unix.open_process_incmdinfinally_fic~h:(fun()->ignore(Unix.close_process_inic))letwith_process_outcmd~f=letoc=Unix.open_process_outcmdinfinally_foc~h:(fun()->ignore(Unix.close_process_outoc))typeprocess_full=<stdin:out_channel;stdout:in_channel;stderr:in_channel;close:Unix.process_status;>letwith_process_full?envcmd~f=letenv=matchenvwithNone->Unix.environment()|Somee->einletoc,ic,err=Unix.open_process_fullcmdenvinletclose=lazy(Unix.close_process_full(oc,ic,err))inletp=objectmethodstdin=icmethodstdout=ocmethodstderr=errmethodclose=Lazy.forcecloseendinfinally_fp~h:(fun()->p#close)letwith_connectionaddr~f=letic,oc=Unix.open_connectionaddrinfinally_(fun()->ficoc)()~h:(fun()->Unix.shutdown_connectionic)(* make sure that we are a session leader; that is, our children die if we die *)letensure_session_leader=letthunk=lazy(ifnotSys.win32&¬Sys.cygwinthenignore(Unix.setsid()))infun()->Lazy.forcethunkexceptionExitServer(* version of {!Unix.establish_server} that doesn't fork *)letestablish_serversockaddr~f=letsock=Unix.socket(Unix.domain_of_sockaddrsockaddr)Unix.SOCK_STREAM0inUnix.setsockoptsockUnix.SO_REUSEADDRtrue;Unix.bindsocksockaddr;Unix.listensock5;letcontinue=reftrueinwhile!continuedotrylets,_=Unix.acceptsockinletic=Unix.in_channel_of_descrsinletoc=Unix.out_channel_of_descrsinignore(ficoc)withExitServer->continue:=falsedone(** {2 Locking} *)letwith_file_lock~kindfilenamef=letlock_file=Unix.openfilefilename[Unix.O_CREAT;Unix.O_WRONLY]0o644inletlock_action=matchkindwith|`Read->Unix.F_RLOCK|`Write->Unix.F_LOCKinUnix.lockflock_filelock_action0;tryletx=f()inUnix.lockflock_fileUnix.F_ULOCK0;Unix.closelock_file;xwithe->Unix.lockflock_fileUnix.F_ULOCK0;Unix.closelock_file;raisee(*$R
let m = 200 in
let n = 50 in
let write_atom filename s =
with_file_lock ~kind:`Write filename
(fun () ->
CCIO.with_out ~flags:[Open_append; Open_creat]
filename (fun oc -> output_string oc s; flush oc))
in
let f filename =
for j=1 to m do
write_atom filename "foo\n"
done
in
CCIO.File.with_temp ~prefix:"containers_" ~suffix:".txt"
(fun filename ->
let a = Array.init n (fun _ -> Thread.create f filename) in
Array.iter Thread.join a;
let lines = CCIO.with_in filename CCIO.read_lines_l in
assert_equal ~printer:string_of_int (n * m) (List.length lines);
assert_bool "all valid" (List.for_all ((=) "foo") lines))
*)moduleInfix=structlet(?|)fmt=call_fullfmtlet(?|&)fmt=async_callfmtendincludeInfix(** {2 Temporary directory} *)letrand_digits_=letst=lazy(Random.State.make_self_init())infun()->letrand=Random.State.bits(Lazy.forcest)land0xFFFFFFinPrintf.sprintf"%06x"randletrmdir_dir=tryignore(Sys.command("rm -r "^dir):int)with_->()letwith_temp_dir?(mode=0o700)?dirpat(f:string->'a):'a=letdir=matchdirwith|Somed->d|None->Filename.get_temp_dir_name()inletraise_errmsg=raise(Sys_errormsg)inletrecloopcount=ifcount<0then(raise_err"mk_temp_dir: too many failing attemps")else(letdir=Filename.concatdir(pat^rand_digits_())inmatchUnix.mkdirdirmodewith|()->finally_fdir~h:(fun()->rmdir_dir)|exceptionUnix.Unix_error(Unix.EEXIST,_,_)->loop(count-1)|exceptionUnix.Unix_error(Unix.EINTR,_,_)->loopcount|exceptionUnix.Unix_error(e,_,_)->raise_err("mk_temp_dir: "^(Unix.error_messagee)))inloop1000(*$R
let filename = with_temp_dir "test_containers"
(fun dir ->
let name = Filename.concat dir "test" in
CCIO.with_out name (fun oc -> output_string oc "content"; flush oc);
assert_bool ("file exists:"^name) (Sys.file_exists name);
name)
in
assert_bool ("file does not exist"^filename) (not (Sys.file_exists filename));
()
*)