1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312moduleAsync_signal=SignalopenCoreopenImportmoduleFile_descr=Unix.File_descrmoduleExit=Unix.ExitmoduleExit_or_signal=Unix.Exit_or_signalmoduleExit_or_signal_or_stop=Unix.Exit_or_signal_or_stopmoduleSyscall_result=Unix.Syscall_resultmoduleError=Unix.Errortypeerror=Unix.Error.t=|E2BIG|EACCES|EAGAIN|EBADF|EBUSY|ECHILD|EDEADLK|EDOM|EEXIST|EFAULT|EFBIG|EINTR|EINVAL|EIO|EISDIR|EMFILE|EMLINK|ENAMETOOLONG|ENFILE|ENODEV|ENOENT|ENOEXEC|ENOLCK|ENOMEM|ENOSPC|ENOSYS|ENOTDIR|ENOTEMPTY|ENOTTY|ENXIO|EPERM|EPIPE|ERANGE|EROFS|ESPIPE|ESRCH|EXDEV|EWOULDBLOCK|EINPROGRESS|EALREADY|ENOTSOCK|EDESTADDRREQ|EMSGSIZE|EPROTOTYPE|ENOPROTOOPT|EPROTONOSUPPORT|ESOCKTNOSUPPORT|EOPNOTSUPP|EPFNOSUPPORT|EAFNOSUPPORT|EADDRINUSE|EADDRNOTAVAIL|ENETDOWN|ENETUNREACH|ENETRESET|ECONNABORTED|ECONNRESET|ENOBUFS|EISCONN|ENOTCONN|ESHUTDOWN|ETOOMANYREFS|ETIMEDOUT|ECONNREFUSED|EHOSTDOWN|EHOSTUNREACH|ELOOP|EOVERFLOW|EUNKNOWNERRofint[@@derivingsexp]exceptionUnix_error=Unix.Unix_errorincludeFd.ClosemoduleOpen_flags=Unix.Open_flagsletsystems=In_thread.syscall_exn~name:"system"(fun()->Unix.systems)letsystem_exns=let%mapstatus=systemsinifnot(Result.is_okstatus)thenraise_s[%message"system failed"~_:(s:string)(status:Exit_or_signal.t)];;letgetpid()=Unix.getpid()letgetppid()=Unix.getppid()letgetppid_exn()=Unix.getppid_exn()letthis_process_became_child_of_init?(poll_delay=sec1.)()=Deferred.create(funi->Clock.everypoll_delay~stop:(Ivar.readi)(fun()->ifPid.equal(getppid_exn())Pid.initthenIvar.filli()));;letnicei=Unix.niceiletcores=Or_error.mapLinux_ext.cores~f:(funcores()->In_thread.syscall_exn~name:"cores"cores);;(* basic input/output *)letconvert_open_flag:_->Unix.open_flag=function|`Rdonly->O_RDONLY|`Wronly->O_WRONLY|`Rdwr->O_RDWR|`Nonblock->O_NONBLOCK|`Append->O_APPEND|`Creat->O_CREAT|`Trunc->O_TRUNC|`Excl->O_EXCL|`Noctty->O_NOCTTY|`Dsync->O_DSYNC|`Sync->O_SYNC|`Rsync->O_RSYNC;;typeopen_flag=[`Rdonly|`Wronly|`Rdwr|`Nonblock|`Append|`Creat|`Trunc|`Excl|`Noctty|`Dsync|`Sync|`Rsync]typefile_perm=int[@@derivingsexp,bin_io,compare]letopenfile?permfile~mode=letmode=List.mapmode~f:convert_open_flag@[O_CLOEXEC]inlet%bindfile_descr=In_thread.syscall_exn~name:"openfile"(fun()->Unix.openfile?permfile~mode)inlet%mapkind=Fd.Kind.infer_using_statfile_descrinFd.createkindfile_descr(Info.of_stringfile);;letfcntl_getflfd=Fd.syscall_in_thread_exnfd~name:"fcntl_getfl"(funfile_descr->Unix.fcntl_getflfile_descr);;letfcntl_setflfdflags=Fd.syscall_in_thread_exnfd~name:"fcntl_setfl"(funfile_descr->Unix.fcntl_setflfile_descrflags);;letlseekfdpos~mode=letmode:Unix.seek_command=matchmodewith|`Set->SEEK_SET|`Cur->SEEK_CUR|`End->SEEK_ENDinFd.syscall_in_thread_exnfd~name:"lseek"(funfile_descr->Unix.lseekfile_descrpos~mode);;lettruncatefilename~len=In_thread.syscall_exn~name:"truncate"(fun()->Unix.truncatefilename~len);;letftruncatefd~len=Fd.syscall_in_thread_exnfd~name:"ftruncate"(funfile_descr->Unix.ftruncatefile_descr~len);;letfsyncfd=Fd.syscall_in_thread_exnfd~name:"fsync"Unix.fsyncletfdatasyncfd=Fd.syscall_in_thread_exnfd~name:"fdatasync"Unix.fdatasyncletsync()=In_thread.syscall_exn~name:"sync"Unix.syncletlockf?(len=0L)fdread_or_write=letmode:Unix.lock_command=matchread_or_writewith|`Read->F_RLOCK|`Write->F_LOCKinFd.syscall_in_thread_exnfd~name:"lockf"(funfile_descr->Unix.lockffile_descr~mode~len);;lettry_lockf?(len=0L)fdread_or_write=letmode:Unix.lock_command=matchread_or_writewith|`Read->F_TRLOCK|`Write->F_TLOCKinFd.syscall_exnfd(funfile_descr->tryUnix.lockffile_descr~mode~len;truewith|Unix_error((EACCES|EAGAIN),_,_)->false);;lettest_lockf?(len=0L)fd=Fd.syscall_exnfd(funfile_descr->tryUnix.lockffile_descr~mode:F_TEST~len;truewith|Unix_error((EACCES|EAGAIN),_,_)->false);;letunlockf?(len=0L)fd=Fd.syscall_exnfd(funfile_descr->Unix.lockffile_descr~mode:F_ULOCK~len);;letwith_file?exclusive?permfile~mode~f=letdoitf=let%bindfd=openfilefile~mode?perminFd.with_closefd~finmatchexclusivewith|None->doitf|Someread_or_write->doit(funfd->let%bind()=lockffdread_or_writeinMonitor.protect(fun()->ffd)~finally:(fun()->unlockffd;return()));;(* file status *)moduleFile_kind=structmoduleT=structtypet=[`File|`Directory|`Char|`Block|`Link|`Fifo|`Socket][@@derivingcompare,sexp,bin_io]endincludeTincludeComparable.Make(T)letof_unix:Unix.file_kind->_=function|S_REG->`File|S_DIR->`Directory|S_CHR->`Char|S_BLK->`Block|S_LNK->`Link|S_FIFO->`Fifo|S_SOCK->`Socket;;endmoduleStats=structtypet={dev:int;ino:int;kind:File_kind.t;perm:file_perm;nlink:int;uid:int;gid:int;rdev:int;size:int64;atime:Time.t;mtime:Time.t;ctime:Time.t}[@@derivingfields,sexp,bin_io,compare]letof_unix(u:Unix.stats)=letof_float_secf=Time.of_span_since_epoch(Time.Span.of_secf)in{dev=u.st_dev;ino=u.st_ino;kind=File_kind.of_unixu.st_kind;perm=u.st_perm;nlink=u.st_nlink;uid=u.st_uid;gid=u.st_gid;rdev=u.st_rdev;size=u.st_size;atime=of_float_secu.st_atime;mtime=of_float_secu.st_mtime;ctime=of_float_secu.st_ctime};;letto_stringt=Sexp.to_string(sexp_of_tt)endletfstatfd=Fd.syscall_in_thread_exnfd~name:"fstat"Unix.fstat>>|Stats.of_unixletstatfilename=In_thread.syscall_exn~name:"stat"(fun()->Unix.statfilename)>>|Stats.of_unix;;letlstatfilename=In_thread.syscall_exn~name:"lstat"(fun()->Unix.lstatfilename)>>|Stats.of_unix;;(* We treat [isatty] as a blocking operation, because it acts on a file. *)letisattyfd=Fd.syscall_in_thread_exnfd~name:"isatty"Unix.isatty(* operations on filenames *)letunlinkfilename=In_thread.syscall_exn~name:"unlink"(fun()->Unix.unlinkfilename);;letremovefilename=In_thread.syscall_exn~name:"remove"(fun()->Unix.removefilename);;letrename~src~dst=In_thread.syscall_exn~name:"rename"(fun()->Unix.rename~src~dst);;letlink?force~target~link_name()=In_thread.syscall_exn~name:"link"(fun()->Unix.link?force~target~link_name());;(* file permission and ownership *)letchmodfilename~perm=In_thread.syscall_exn~name:"chmod"(fun()->Unix.chmodfilename~perm);;letfchmodfd~perm=Fd.syscall_in_thread_exnfd~name:"fchmod"(funfile_descr->Unix.fchmodfile_descr~perm);;letchownfilename~uid~gid=In_thread.syscall_exn~name:"chown"(fun()->Unix.chownfilename~uid~gid);;letfchownfd~uid~gid=Fd.syscall_in_thread_exnfd~name:"fchown"(funfile_descr->Unix.fchownfile_descr~uid~gid);;letaccessfilenameperm=match%mapMonitor.try_with(fun()->In_thread.syscall_exn~name:"access"(fun()->Unix.accessfilenameperm))with|Okres->res|Errorexn->Error(Monitor.extract_exnexn);;letaccess_exnfilenameperm=In_thread.syscall_exn~name:"access"(fun()->Unix.access_exnfilenameperm);;(* operations on file descriptors *)letset_close_on_execfd=Fd.with_file_descr_exnfdUnix.set_close_on_execletclear_close_on_execfd=Fd.with_file_descr_exnfdUnix.clear_close_on_execletmkdir?p?(perm=0o777)dirname=matchpwith|None->In_thread.syscall_exn~name:"mkdir"(fun()->Unix.mkdirdirname~perm)|Some()->In_thread.syscall_exn~name:"mkdir"(fun()->Unix.mkdir_pdirname~perm);;letrmdirdirname=In_thread.syscall_exn~name:"rmdir"(fun()->Unix.rmdirdirname)letchdirdirname=In_thread.syscall_exn~name:"chdir"(fun()->Unix.chdirdirname)letchrootdirname=In_thread.syscall_exn~name:"chroot"(fun()->Unix.chrootdirname)letgetcwd()=In_thread.syscall_exn~name:"getcwd"(fun()->Unix.getcwd())typedir_handle=Unix.dir_handleletopendirdirname=In_thread.syscall_exn~name:"opendir"(fun()->Unix.opendirdirname);;letreaddir_opthandle=In_thread.syscall_exn~name:"readdir"(fun()->Unix.readdir_opthandle);;letreaddirhandle=In_thread.syscall_exn~name:"readdir"(fun()->(Unix.readdirhandle[@warning"-3"]));;letrewinddirhandle=In_thread.syscall_exn~name:"rewinddir"(fun()->Unix.rewinddirhandle);;letclosedirhandle=In_thread.syscall_exn~name:"closedir"(fun()->Unix.closedirhandle);;letpipeinfo=let%mapreader,writer=In_thread.syscall_exn~name:"pipe"(fun()->letr,w=Unix.pipe()inUnix.set_close_on_execr;Unix.set_close_on_execw;r,w)inletcreatefile_descrkind=Fd.createFifofile_descr(Info.taginfo~tag:kind)in`Reader(createreader"reader"),`Writer(createwriter"writer");;letmkfifo?(perm=0o666)name=In_thread.syscall_exn~name:"mkfifo"(fun()->Unix.mkfifoname~perm);;(* symlinks *)letsymlink~target~link_name=In_thread.syscall_exn~name:"symlink"(fun()->Unix.symlink~target~link_name);;letreadlinkfilename=In_thread.syscall_exn~name:"readlink"(fun()->Unix.readlinkfilename);;letmkdtempfilename=In_thread.syscall_exn~name:"mkdtemp"(fun()->Unix.mkdtempfilename);;letmkstempfilename=let%mapname,file_descr=In_thread.syscall_exn~name:"mkstemp"(fun()->Unix.mkstempfilename)inname,Fd.createFilefile_descr(Info.of_stringname);;letgetgrouplistusernamegid=In_thread.syscall_exn~name:"getgrouplist"(fun()->Unix.getgrouplistusernamegid);;typeprocess_times=Unix.process_times={tms_utime:float;tms_stime:float;tms_cutime:float;tms_cstime:float}lettimes=Unix.timestypetm=Unix.tm={tm_sec:int;tm_min:int;tm_hour:int;tm_mday:int;tm_mon:int;tm_year:int;tm_wday:int;tm_yday:int;tm_isdst:bool}lettime=Unix.timeletgettimeofday=Unix.gettimeofdayletgmtime=Unix.gmtimeletlocaltime=Unix.localtimeletmktime=Unix.mktimeletutimesname~access~modif=In_thread.syscall_exn~name:"utimes"(fun()->Unix.utimesname~access~modif);;(* environment *)typeenv=Unix.env[@@derivingsexp]letenvironment=Unix.environmentletgetenv=Sys.getenvletgetenv_exn=Sys.getenv_exnletunsafe_getenv=Sys.unsafe_getenvletunsafe_getenv_exn=Sys.unsafe_getenv_exnletputenv=Unix.putenvletunsetenv=Unix.unsetenv(* processes *)letfork_exec~prog~argv?use_path?env()=In_thread.run(fun()->Unix.fork_exec~prog~argv?use_path?env());;typewait_on=Unix.wait_on[@@derivingsexp_poly]letwait_nohang=Unix.wait_nohangletwait_nohang_untraced=Unix.wait_nohang_untracedmoduleWait:sigvalcheck_all:unit->unitvaldo_not_handle_sigchld:unit->unitvalwait:wait_on->(Pid.t*Exit_or_signal.t)Deferred.tvalwait_untraced:wait_on->(Pid.t*Exit_or_signal_or_stop.t)Deferred.tend=structmoduleKind=structtype_t=|Normal:Exit_or_signal.tt|Untraced:Exit_or_signal_or_stop.tt[@@derivingsexp_of]letwait_nohang:typea.at->wait_on->(Pid.t*a)option=funtwait_on->matchtwith|Normal->wait_nohangwait_on|Untraced->wait_nohang_untracedwait_on;;endmoduleWait=structtypet=|T:{kind:'aKind.t;result:(Pid.t*'a,exn)Result.tIvar.t;wait_on:wait_on}->t[@@derivingsexp_of]letcheck(Tt)=matchKind.wait_nohangt.kindt.wait_onwith|None->false|Somex->Ivar.fillt.result(Okx);true|exceptionexn->Ivar.fillt.result(Errorexn);true;;endletwaits:Wait.tlistref=ref[]letadd~kind~result~wait_on=waits:=T{kind;result;wait_on}::!waitsletcheck_all()=waits:=List.filter!waits~f:(Fn.nonWait.check)letshould_handle_sigchld=reftrueletam_handling_sigchld=reffalseletdo_not_handle_sigchld()=if!am_handling_sigchldthenraise_s[%message"already handling SIGCHLD"[%here]];should_handle_sigchld:=false;;letinstall_sigchld_handler_the_first_time=lazy(if!should_handle_sigchldthen(am_handling_sigchld:=true;Async_signal.handle[Signal.chld]~f:(fun_->check_all())));;letdeferred_wait(typek)wait_on~(kind:kKind.t)=(* We are going to install a handler for SIGCHLD that will call [wait_nohang wait_on]
in the future. However, we must also call [wait_nohang wait_on] right now, in case
the child already exited, and will thus never cause a SIGCHLD in the future. We
must install the SIGCHLD handler first and then call [wait_nohang]. If we did
[wait_nohang] first, we could miss a SIGCHLD that was delivered after calling
[wait_nohang] and before installing the handler. *)Lazy.forceinstall_sigchld_handler_the_first_time;matchKind.wait_nohangkindwait_onwith|Someresult->returnresult|None->Deferred.create(funresult->add~kind~result~wait_on)>>|Result.ok_exn;;letwaitwait_on=deferred_waitwait_on~kind:Normalletwait_untracedwait_on=deferred_waitwait_on~kind:Untracedendletwait=Wait.waitletwait_untraced=Wait.wait_untracedletwaitpidpid=let%mappid',exit_or_signal=wait(`Pidpid)inassert(Pid.equalpidpid');exit_or_signal;;letwaitpid_exnpid=let%mapexit_or_signal=waitpidpidinifResult.is_errorexit_or_signalthenraise_s[%message"child process didn't exit with status 0"~child_pid:(pid:Pid.t)(exit_or_signal:Exit_or_signal.t)];;moduleInet_addr=structincludeUnix.Inet_addrletof_string_or_getbynames=matchof_stringswith|t->Deferred.returnt|exception_->In_thread.run(fun()->of_string_or_getbynames);;endmoduleCidr=Unix.Cidrletbind_to_interface_exn=Or_error.mapLinux_ext.bind_to_interface~f:(funbind_to_interfacefdspec->Fd.with_file_descr_exnfd(funfile_descr->bind_to_interfacefile_descrspec));;moduleSocket=structmoduleAddress=structmoduleInet=structtypet=[`InetofInet_addr.t*int][@@derivingbin_io,compare,hash]letto_string_internal~show_port_in_test(`Inet(a,p))=sprintf"%s:%s"(Inet_addr.to_stringa)(ifam_running_inline_test&¬show_port_in_testthen"PORT"elsep|>Int.to_string);;letto_string=to_string_internal~show_port_in_test:falseletsexp_of_tt:Sexp.t=Atom(to_stringt)moduleBlocking_sexp=structtypet=[`InetofInet_addr.Blocking_sexp.t*int][@@derivingbin_io,compare,hash,sexp]endmoduleShow_port_in_test=structtypet=[`InetofInet_addr.t*int][@@derivingsexp_of]letto_string=to_string_internal~show_port_in_test:trueendlett_of_sexp=Blocking_sexp.t_of_sexplet__t_of_sexp__=Blocking_sexp.__t_of_sexp__letaddr(`Inet(a,_))=aletport(`Inet(_,p))=pletto_host_and_port(`Inet(addr,port))=Host_and_port.create~host:(Inet_addr.to_stringaddr)~port;;letcreatea~port=`Inet(a,port)letcreate_bind_any~port=`Inet(Inet_addr.of_string"0.0.0.0",port)letof_sockaddr_exn:Unix.sockaddr->_=function|ADDR_INET(a,i)->`Inet(a,i)|u->raise_s[%message"Socket.Address.inet"~_:(u:Unix.sockaddr)];;letto_sockaddr(`Inet(a,i))=Unix.ADDR_INET(a,i)endmoduleUnix=structtypet=[`Unixofstring][@@derivingbin_io,compare,hash,sexp]letcreates=`Unixsletto_string(`Unixs)=sletof_sockaddr_exn:Unix.sockaddr->t=function|ADDR_UNIXs->`Unixs|u->raise_s[%message"Socket.Address.unix"~_:(u:Unix.sockaddr)];;letto_sockaddr(`Unixs)=Unix.ADDR_UNIXsendtypet=[Inet.t|Unix.t][@@derivingbin_io,sexp_of]moduleBlocking_sexp=structtypet=[Inet.Blocking_sexp.t|Unix.t][@@derivingbin_io,hash,sexp]endlett_of_sexp=Blocking_sexp.t_of_sexpletto_sockaddr=function|#Inet.tast->Inet.to_sockaddrt|#Unix.tast->Unix.to_sockaddrt;;letto_string=function|`Inet_ast->t|>Inet.to_string|`Unix_ast->t|>Unix.to_string;;endmoduleFamily=structtype'addresst={family:Unix.socket_domain;address_of_sockaddr_exn:Unix.sockaddr->'address;sexp_of_address:'address->Sexp.t}constraint'address=[<Address.t][@@derivingfields]letsexp_of_t_{address_of_sockaddr_exn=_;family;sexp_of_address=_}=[%sexp(family:Unix.socket_domain)];;letto_stringt=matcht.familywith|PF_INET->"inet"|PF_INET6->"inet6"|PF_UNIX->"unix";;letinet={family=PF_INET;address_of_sockaddr_exn=Address.Inet.of_sockaddr_exn;sexp_of_address=Address.Inet.sexp_of_t};;letunix={family=PF_UNIX;address_of_sockaddr_exn=Address.Unix.of_sockaddr_exn;sexp_of_address=Address.Unix.sexp_of_t};;endmoduleType=structtype'at={family:'aFamily.t;socket_type:Unix.socket_type}[@@derivingsexp_of]letsexp_of_addresst=t.family.sexp_of_addresslettcp={family=Family.inet;socket_type=SOCK_STREAM}letudp={family=Family.inet;socket_type=SOCK_DGRAM}letunix={family=Family.unix;socket_type=SOCK_STREAM}letunix_dgram={family=Family.unix;socket_type=SOCK_DGRAM}letphys_same(t1:_t)(t2:_t)=phys_samet1t2endmoduleFor_info=structtype'addrt={mutableconnected_to:'addroption;mutablebound_on:'addroption;mutablelistening:bool;type_:'addrType.t}letcreatetype_={connected_to=None;bound_on=None;listening=false;type_}letinfo{connected_to;bound_on;listening;type_}=lettype_=ifType.phys_sametype_Type.tcpthen[%sexp"tcp"]elseifType.phys_sametype_Type.udpthen[%sexp"udp"]else[%sexp(type_:_Type.t)]inletbound_on,listening_on=iflisteningthenNone,bound_onelsebound_on,NoneinInfo.create_s[%sexp{connected_to:([<Address.t]option[@sexp.option]);type_:Sexp.t;bound_on:([<Address.t]option[@sexp.option]);listening_on:([<Address.t]option[@sexp.option])}];;endtype'addrt_={type_:'addrType.t;fd:Fd.t;for_info:'addrFor_info.toption}type(+'state,'addr)t='addrt_constraint'state=[<`Unconnected|`Bound|`Passive|`Active]letsexp_of_t__t=Fd.sexp_of_tt.fdletfdt=t.fdletof_fdfdtype_={type_;fd;for_info=None}letsexp_of_addresst=Type.sexp_of_addresst.type_letcreate(type_:_Type.t)=letfile_descr=Unix.socket~domain:type_.family.family~kind:type_.socket_type~protocol:0inUnix.set_close_on_execfile_descr;letfd=Fd.create(Socket`Unconnected)file_descr(Info.create"socket"type_[%sexp_of:_Type.t])in{type_;fd;for_info=Some(For_info.createtype_)};;moduleOpt=structtype'at={name:string;get:File_descr.t->'a;set:File_descr.t->'a->unit}letto_stringt=t.nameletmakegetsockoptsetsockoptnameopt={name;get=(funfd->getsockoptfdopt);set=(funfda->setsockoptfdopta)};;letbool=makeUnix.getsockoptUnix.setsockoptletint=makeUnix.getsockopt_intUnix.setsockopt_intletoptint=makeUnix.getsockopt_optintUnix.setsockopt_optintletfloat=makeUnix.getsockopt_floatUnix.setsockopt_floatletdebug=bool"debug"SO_DEBUGletbroadcast=bool"broadcast"SO_BROADCASTletreuseaddr=bool"reuseaddr"SO_REUSEADDRletkeepalive=bool"keepalive"SO_KEEPALIVEletdontroute=bool"dontroute"SO_DONTROUTEletoobinline=bool"oobinline"SO_OOBINLINEletacceptconn=bool"acceptconn"SO_ACCEPTCONNletnodelay=bool"nodelay"TCP_NODELAYletsndbuf=int"sndbuf"SO_SNDBUFletrcvbuf=int"rcvbuf"SO_RCVBUFleterror=int"error"SO_ERRORlettyp=int"typ"SO_TYPEletrcvlowat=int"rcvlowat"SO_RCVLOWATletsndlowat=int"sndlowat"SO_SNDLOWATletlinger=optint"linger"SO_LINGERletrcvtimeo=float"rcvtimeo"SO_RCVTIMEOletsndtimeo=float"sndtimeo"SO_SNDTIMEO(* Since there aren't socket options like SO_MCASTLOOP or SO_MCASTTTL, we wrap
[Core.Unix] functions to match async's socket-options interface. *)letmcast_loop={name="mcast_loop";get=Unix.get_mcast_loop;set=Unix.set_mcast_loop};;letmcast_ttl={name="mcast_ttl";get=Unix.get_mcast_ttl;set=Unix.set_mcast_ttl};;endletgetoptt(opt:_Opt.t)=Fd.with_file_descr_exnt.fdopt.getletsetoptt(opt:_Opt.t)a=Fd.with_file_descr_exnt.fd(funfile_descr->opt.setfile_descra);;letmcast_join?ifname?sourcetaddress=Fd.with_file_descr_exnt.fd(funfile_descr->Unix.mcast_join?ifname?sourcefile_descr(Address.to_sockaddraddress));;letmcast_leave?ifname?sourcetaddress=Fd.with_file_descr_exnt.fd(funfile_descr->Unix.mcast_leave?ifname?sourcefile_descr(Address.to_sockaddraddress));;letmark_boundtaddress=letinfo=matcht.for_infowith|Somei->i.bound_on<-Someaddress;`Set(For_info.infoi)|None->`Extend(Info.create"socket"(`bound_onaddress)(letsexp_of_address=sexp_of_addresstin[%sexp_of:[`bound_onofaddress]]))inFd.Private.replacet.fd(Socket`Bound)info;;letbind?(reuseaddr=true)taddress=setopttOpt.reuseaddrreuseaddr;set_close_on_exect.fd;letsockaddr=Address.to_sockaddraddressinlet%map()=Fd.syscall_in_thread_exnt.fd~name:"bind"(funfile_descr->Unix.bindfile_descr~addr:sockaddr)inmark_boundtaddress;t;;letbind_inet?(reuseaddr=true)taddress=setopttOpt.reuseaddrreuseaddr;set_close_on_exect.fd;letsockaddr=Address.to_sockaddraddressinFd.syscall_exnt.fd(funfile_descr->Unix.bindfile_descr~addr:sockaddr);mark_boundtaddress;t;;letlisten?(backlog=64)t=letfd=t.fdinFd.syscall_exnfd(funfile_descr->Unix.listenfile_descr~backlog);letinfo=matcht.for_infowith|Somei->i.listening<-true;`Set(For_info.infoi)|None->`Extend(Info.of_string"listening")inFd.Private.replacefd(Socket`Passive)info;t;;letturn_off_nagle(addr:Unix.sockaddr)t=matchaddr,t.type_.socket_typewith|ADDR_INET_,SOCK_STREAM->setopttOpt.nodelaytrue|(ADDR_UNIX_|ADDR_INET_),_->();;letaccept_nonblockingt=(* We call [accept] with [~nonblocking:true] because there is no way to use
[select] to guarantee that an [accept] will not block (see Stevens' book on
Unix Network Programming, p422). *)matchFd.with_file_descrt.fd~nonblocking:true(funfile_descr->Unix.acceptfile_descr)with|`Already_closed->`Socket_closed|`Ok(file_descr,sockaddr)->Unix.set_close_on_execfile_descr;letaddress=Family.address_of_sockaddr_exnt.type_.familysockaddrinletfd=Fd.create(Fd.Kind.Socket`Active)file_descr(Info.create"socket"(`listening_ont,`clientaddress)(letsexp_of_address=sexp_of_addresstin[%sexp_of:[`listening_onof(_,_)t]*[`clientofaddress]]))inlets={fd;type_=t.type_;for_info=None}inset_close_on_execs.fd;turn_off_naglesockaddrs;`Ok(s,address)|`Error(Unix_error((EAGAIN|EWOULDBLOCK|ECONNABORTED|EINTR),_,_))->(* If [accept] would have blocked (EAGAIN|EWOULDBLOCK) or got interrupted
(EINTR), then we return [`Would_block].
If the kernel returns ECONNABORTED, this means that we first got a connection
and therefore woke up in "select" (ready to read). But due to slowness
(e.g. other long async jobs getting to run first) we could not call accept
quickly enough, and the other side terminated the connection in the meanwhile.
Though one could imagine weird client/server applications that absolutely need
to know that some client aborted the connection before we could accept it, this
seems quite contrived and unlikely. In virtually all cases people just want to
continue waiting for a new connection.
[Sys_blocked_io] cannot be raised here. This is a Unix-function, not a
standard OCaml I/O-function (e.g. for reading from channels). *)`Would_block|`Errorexn->raiseexn;;letaccept_interruptiblet~interrupt=Deferred.repeat_until_finished()(fun()->matchaccept_nonblockingtwith|(`Socket_closed|`Ok_)asx->return(`Finishedx)|`Would_block->(match%mapFd.interruptible_ready_tot.fd`Read~interruptwith|`Ready->`Repeat()|`Interruptedasx->`Finishedx|`Closed->`Finished`Socket_closed|`Bad_fd->raise_s[%message"accept on bad file descriptor"~_:(t.fd:Fd.t)]));;letacceptt=match%mapaccept_interruptiblet~interrupt:(Fd.close_startedt.fd)with|`Interrupted->`Socket_closed|(`Socket_closed|`Ok_)asx->x;;letaccept_at_most_interruptiblet~limit~interrupt=iflimit<1thenraise_s[%message"[Socket.accept_at_most_interruptible] got [limit] < 1"(limit:int)];match%mapaccept_interruptiblet~interruptwith|(`Socket_closed|`Interrupted)asx->x|`Okconnection->(* Now that we have a connection, accept without blocking as many other connections
as we can, up to [limit] total connections. *)letreclooplimitconnections=iflimit=0thenconnectionselse(matchaccept_nonblockingtwith|`Okconnection->loop(limit-1)(connection::connections)|`Socket_closed|`Would_block->connections|exceptionexn->don't_wait_for(Deferred.List.iterconnections~f:(fun(conn,_)->Fd.closeconn.fd));raiseexn)in`Ok(List.rev(loop(limit-1)[connection]));;letaccept_at_mostt~limit=match%mapaccept_at_most_interruptiblet~limit~interrupt:(Fd.close_startedt.fd)with|`Interrupted->`Socket_closed|(`Socket_closed|`Ok_)asx->x;;letconnect_interruptibletaddress~interrupt=letsockaddr=Address.to_sockaddraddressinturn_off_naglesockaddrt;letsuccess()=letinfo=matcht.for_infowith|Somei->i.connected_to<-Someaddress;`Set(For_info.infoi)|None->letsexp_of_address=sexp_of_addresstin`Extend(Info.create"connected to"address[%sexp_of:address])inFd.Private.replacet.fd(Fd.Kind.Socket`Active)info;`Oktinletfail_closed()=raise_s[%message"connect on closed fd"~_:(t.fd:Fd.t)]in(* We call [connect] with [~nonblocking:true] to initiate an asynchronous connect
(see Stevens' book on Unix Network Programming, p413). Once the connect succeeds
or fails, [select] on the socket will return it in the writeable set. *)matchFd.with_file_descrt.fd~nonblocking:true(funfile_descr->Unix.connectfile_descr~addr:sockaddr)with|`Already_closed->fail_closed()|`Ok()->return(success())|`Error(Unix_error((EINPROGRESS|EINTR),_,_))->(match%mapFd.interruptible_ready_tot.fd`Write~interruptwith|`Closed->fail_closed()|`Bad_fd->raise_s[%message"connect on bad file descriptor"~_:(t.fd:Fd.t)]|`Interruptedasx->x|`Ready->(* We call [getsockopt] to find out whether the connect has succeed or failed. *)(matchFd.with_file_descrt.fd(funfile_descr->Unix.getsockopt_intfile_descrSO_ERROR)with|`Already_closed->fail_closed()|`Errorexn->raiseexn|`Okerr->iferr=0thensuccess()elseUnix.unix_errorerr"connect"(Address.to_stringaddress)))|`Errore->raisee;;letconnecttaddr=match%mapconnect_interruptibletaddr~interrupt:(Deferred.never())with|`Interrupted->assertfalse(* impossible *)|`Okt->t;;letshutdowntmode=letmode:Unix.shutdown_command=matchmodewith|`Receive->SHUTDOWN_RECEIVE|`Send->SHUTDOWN_SEND|`Both->SHUTDOWN_ALLinFd.syscall_exnt.fd(funfile_descr->Unix.shutdownfile_descr~mode);;letgetsocknamet=Family.address_of_sockaddr_exnt.type_.family(Unix.getsockname(Fd.file_descr_exnt.fd));;letgetpeernamet=Family.address_of_sockaddr_exnt.type_.family(Unix.getpeername(Fd.file_descr_exnt.fd));;letbind_to_interface_exn=Or_error.mapbind_to_interface_exn~f:(funftifname->ft.fdifname);;endletsocketpair()=lets1,s2=Unix.socketpair~domain:PF_UNIX~kind:SOCK_STREAM~protocol:0inletmake_fds=Unix.set_close_on_execs;Fd.create(Fd.Kind.Socket`Active)s(Info.of_string"<socketpair>")inmake_fds1,make_fds2;;moduleProtocol_family=Unix.Protocol_familymoduleHost=structtypet=Unix.Host.t={name:string;aliases:stringarray;family:Protocol_family.t;addresses:Inet_addr.tarray}letgetbynamen=In_thread.syscall_exn~name:"gethostbyname"(fun()->Unix.Host.getbynamen);;letgetbyname_exnn=In_thread.syscall_exn~name:"gethostbyname"(fun()->Unix.Host.getbyname_exnn);;letgetbyaddra=In_thread.syscall_exn~name:"gethostbyaddr"(fun()->Unix.Host.getbyaddra);;letgetbyaddr_exna=In_thread.syscall_exn~name:"gethostbyaddr"(fun()->Unix.Host.getbyaddr_exna);;lethave_address_in_common=Unix.Host.have_address_in_commonendtypesocket_domain=Unix.socket_domain=|PF_UNIX|PF_INET|PF_INET6[@@derivingbin_io,compare,hash,sexp]typesocket_type=Unix.socket_type=|SOCK_STREAM|SOCK_DGRAM|SOCK_RAW|SOCK_SEQPACKET[@@derivingbin_io,compare,hash,sexp]typesockaddr=Unix.sockaddr=|ADDR_UNIXofstring|ADDR_INETofInet_addr.t*int[@@derivingbin_io,compare,sexp_of]typesockaddr_blocking_sexp=Unix.sockaddr=|ADDR_UNIXofstring|ADDR_INETofInet_addr.Blocking_sexp.t*int[@@derivingbin_io,sexp]letsockaddr_of_sexp=sockaddr_blocking_sexp_of_sexpmoduleAddr_info=structtypet=Unix.addr_info={ai_family:socket_domain;ai_socktype:socket_type;ai_protocol:int;ai_addr:sockaddr;ai_canonname:string}[@@derivingbin_io,sexp_of]moduleBlocking_sexp=structtypet=Unix.addr_info={ai_family:socket_domain;ai_socktype:socket_type;ai_protocol:int;ai_addr:sockaddr_blocking_sexp;ai_canonname:string}[@@derivingbin_io,sexp]endlett_of_sexp=Blocking_sexp.t_of_sexptypegetaddrinfo_option=Unix.getaddrinfo_option=|AI_FAMILYofsocket_domain|AI_SOCKTYPEofsocket_type|AI_PROTOCOLofint|AI_NUMERICHOST|AI_CANONNAME|AI_PASSIVE[@@derivingbin_io,sexp]letget?(service="")~hostoptions=In_thread.syscall_exn~name:"getaddrinfo"(fun()->Unix.getaddrinfohostserviceoptions);;endmoduleName_info=structtypet=Unix.name_info={ni_hostname:string;ni_service:string}[@@derivingbin_io,sexp]typegetnameinfo_option=Unix.getnameinfo_option=|NI_NOFQDN|NI_NUMERICHOST|NI_NAMEREQD|NI_NUMERICSERV|NI_DGRAM[@@derivingsexp,bin_io]letgetaddroptions=In_thread.syscall_exn~name:"getnameinfo"(fun()->Unix.getnameinfoaddroptions);;endletgethostname()=Unix.gethostname()letsetuiduid=Unix.setuiduidletgetuid()=Unix.getuid()letgetgid()=Unix.getgid()letgetegid()=Unix.getegid()letgeteuid()=Unix.geteuid()moduleTerminal_io=structincludeUnix.Terminal_iolettcgetattrfd=Fd.syscall_in_thread_exnfd~name:"tcgetattr"(funfile_descr->tcgetattrfile_descr);;lettcsetattrtfd~mode=Fd.syscall_in_thread_exnfd~name:"tcsetattr"(funfile_descr->tcsetattrtfile_descr~mode);;endmodulePasswd=structtypet=Unix.Passwd.t={name:string;passwd:string;uid:int;gid:int;gecos:string;dir:string;shell:string}[@@derivingfields,sexp](* The four [Unix.Passwd] functions call C functions that release the OCaml lock and do
a reentrant system call. *)letgetbynamen=In_thread.run(fun()->Unix.Passwd.getbynamen)letgetbyname_exnn=In_thread.run(fun()->Unix.Passwd.getbyname_exnn)letgetbyuiduid=In_thread.run(fun()->Unix.Passwd.getbyuiduid)letgetbyuid_exnuid=In_thread.run(fun()->Unix.Passwd.getbyuid_exnuid)endmoduleGroup=structtypet=Unix.Group.t={name:string;passwd:string;gid:int;mem:stringarray}[@@derivingfields,sexp](* The four [Unix.Group] functions call C functions that release the OCaml lock and do a
reentrant system call. *)letgetbynamen=In_thread.run(fun()->Unix.Group.getbynamen)letgetbyname_exnn=In_thread.run(fun()->Unix.Group.getbyname_exnn)letgetbygidgid=In_thread.run(fun()->Unix.Group.getbygidgid)letgetbygid_exngid=In_thread.run(fun()->Unix.Group.getbygid_exngid)endletgetlogin()=In_thread.syscall_exn~name:"getlogin"(fun()->Unix.getlogin())moduleIfaddr=Unix.Ifaddrletgetifaddrs()=In_thread.runUnix.getifaddrsletwordexp=Or_error.mapUnix.wordexp~f:(funwordexp?flagsglob->In_thread.syscall_exn~name:"wordexp"(fun()->wordexp?flagsglob));;modulePrivate=structmoduleWait=Waitend