123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410(* $Id$ *)openUnixopenPrintfmoduleDebug=structletenable =reffalseendletdlog=Netlog.Debug.mk_dlog"Netsys_posix"Debug.enableletdlogr=Netlog.Debug.mk_dlogr"Netsys_posix"Debug.enablelet()=Netlog.Debug.register_module"Netsys_posix"Debug.enableexternalint64_of_file_descr:Unix.file_descr->int64="netsys_int64_of_file_descr"letint_of_file_descr=matchSys.os_typewith|"Unix"|"Cygwin"->(funfd->(Obj.magic(fd:file_descr):int))|_->(funfd->invalid_arg"Netsys.int_of_file_descr")letfile_descr_of_int=matchSys.os_typewith|"Unix"|"Cygwin"->(funn->(Obj.magic(n:int):file_descr))|_->(fun n->invalid_arg "Netsys.file_descr_of_int")(* Limits & resources *)externalsysconf_open_max:unit->int="netsys_sysconf_open_max";;(* misc *)externalfchdir:Unix.file_descr->unit="netsys_fchdir";;externalfdopendir:Unix.file_descr ->Unix.dir_handle="netsys_fdopendir";;externalrealpath:string->string="netsys_realpath"externalget_nonblock:Unix.file_descr->bool="netsys_get_nonblock"(* Process groups, sessions, terminals *)externalgetpgid:int->int="netsys_getpgid";;letgetpgrp()=getpgid0;;externalsetpgid:int->int->unit="netsys_setpgid";;letsetpgrp()=setpgid00;;externaltcgetpgrp:file_descr->int="netsys_tcgetpgrp";;externaltcsetpgrp:file_descr->int->unit="netsys_tcsetpgrp";;externalctermid:unit->string="netsys_ctermid";;external ttyname :file_descr->string="netsys_ttyname";;externalgetsid:int->int="netsys_getsid";;externalposix_openpt :bool->Unix.file_descr="netsys_posix_openpt"externalgrantpt:Unix.file_descr->unit="netsys_grantpt"externalunlockpt:Unix.file_descr->unit="netsys_unlockpt"externalptsname :Unix.file_descr->string="netsys_ptsname"letwith_ttyf=letfd=tryUnix.openfile (ctermid())[Unix.O_RDWR]0with_->failwith"Netsys_posix.with_tty: cannot open terminal"intryletr=ffdinUnix.closefd;rwitherror->Unix.close fd;raiseerrorletdescr_input_linefd=(* unbuffered! *)letb=Buffer.create80inlets=Bytes.create1inletrecloop()=tryletn=Unix.readfds01inifn>0&&Bytes.gets0<>'\n'then(#ifdefHAVE_BYTESBuffer.add_bytesbs;#elseBuffer.add_stringb(Bytes.unsafe_to_strings);#endifraise(Unix.Unix_error(Unix.EINTR,"","")))with|Unix.Unix_error(Unix.EINTR,_,_)->loop()inloop();Buffer.contentsblettty_read_password?(tty=Unix.stdin)prompt=ifUnix.isattyttythen(letcleanup=ref[]inlet f=Unix.out_channel_of_descrttyinoutput_stringfprompt;flushf;Unix.tcdraintty;Unix.tcflush ttyUnix.TCIFLUSH;letp=Unix.tcgetattrttyintryletp'={pwithUnix.c_echo=false;Unix.c_echoe=false;Unix.c_echok=false;Unix.c_echonl=false}inUnix.tcsetattrttyUnix.TCSAFLUSHp';cleanup:=(fun()->Unix.tcsetattrttyUnix.TCSAFLUSHp)::!cleanup;letold_sigint=Sys.signalSys.sigint(Sys.Signal_handle(fun_->raiseSys.Break))incleanup:=(fun()->Sys.set_signalSys.sigintold_sigint)::!cleanup;letpw=descr_input_linettyinoutput_string f"\n";flushf;List.iter(funf->f())!cleanup;pwwith|error->List.iter(funf->f())!cleanup;iferror =Sys.Breakthen(output_stringf"\n";flushf);raiseerror)elsedescr_input_linetty(* Users and groups *)externalsetreuid:int->int->unit="netsys_setreuid";;externalsetregid:int->int->unit="netsys_setregid";;externalinitgroups:string->int->unit="netsys_initgroups"(* mknod *)typenode_type=|S_IFREG|S_IFCHR ofint(* major + minor *)|S_IFBLKofint(* major + minor *)|S_IFIFO|S_IFSOCKexternalmknod :string->int->node_type->unit="netsys_mknod"(* poll *)externalpollfd_size:unit->int="netsys_pollfd_size"letthe_pollfd_size=pollfd_size()let_have_poll=the_pollfd_size >0lethave_poll()=_have_polltypepoll_req_events=inttypepoll_act_events=inttypepoll_cell={mutablepoll_fd:Unix.file_descr;mutablepoll_req_events:poll_req_events;mutablepoll_act_events:poll_act_events;}typepoll_memtypepoll_array=|Poll_memofpoll_mem*int(*length*)|Poll_emuofpoll_cellarrayletnull_poll_cell={poll_fd=Unix.stdin;poll_req_events =0;poll_act_events=0}externalmk_poll_mem:int->poll_mem="netsys_mk_poll_mem"externalset_poll_mem:poll_mem->int->Unix.file_descr->int->int->unit="netsys_set_poll_mem"externalget_poll_mem:poll_mem->int->(Unix.file_descr *int*int)="netsys_get_poll_mem"externalblit_poll_mem:poll_mem->int->poll_mem ->int->int ->unit="netsys_blit_poll_mem"externalpoll_constants:unit->intarray="netsys_poll_constants"letthe_poll_constants=poll_constants()letconst_rd_event=the_poll_constants.(0)letconst_pri_event=the_poll_constants.(1)letconst_wr_event=the_poll_constants.(2)letconst_err_event=the_poll_constants.(3)letconst_hup_event=the_poll_constants.(4)letconst_nval_event=the_poll_constants.(5)letpoll_req_eventsrdwrpri=(ifrdthenconst_rd_eventelse0)lor(ifwrthen const_wr_eventelse0)lor(ifprithenconst_pri_eventelse0)letpoll_req_triplep=(plandconst_rd_event<>0,plandconst_wr_event<>0,plandconst_pri_event<>0)letpoll_null_events()=0letpoll_resultp=p<>0letpoll_rd_resultp=plandconst_rd_event<>0letpoll_wr_resultp=plandconst_wr_event<>0letpoll_pri_resultp=plandconst_pri_event<>0letpoll_err_resultp=plandconst_err_event<>0letpoll_hup_resultp=plandconst_hup_event<>0letpoll_nval_resultp=plandconst_nval_event<>0letpoll_array_length=function|Poll_mem(_,n)->n|Poll_emue->Array.lengtheletset_poll_cell akc=ifk<0||k>=poll_array_lengthatheninvalid_arg"Netsys.set_poll_cell";matchawith|Poll_mem(s,_)->set_poll_memskc.poll_fdc.poll_req_events(* c.poll_revents *)0|Poll_emue->e.(k)<-{cwithpoll_fd=c.poll_fd}(* copy *)letget_poll_cellak=ifk<0||k>=poll_array_lengthatheninvalid_arg"Netsys.get_poll_cell";matchawith|Poll_mem(s,_)->let(fd,ev,rev)=get_poll_mem skin{poll_fd=fd;poll_req_events=ev;poll_act_events=rev}|Poll_emue->letc=e.(k)in{cwithpoll_fd=c.poll_fd}(* copy *)letblit_poll_arraya1k1a2k2len=letl1=poll_array_lengtha1inletl2=poll_array_lengtha2iniflen<0||k1<0||k1+len>l1||k2<0||k2+len>l2theninvalid_arg"Netsys.get_poll_cell";match(a1,a2)with|(Poll_mem(s1,_),Poll_mem(s2,_))->blit_poll_mems1k1s2k2len|(Poll_emue1,Poll_emue2)->Array.blite1k1e2k2len|_->assertfalseletcreate_poll_arrayn=if_have_pollthen(lets=mk_poll_memninPoll_mem(s,n))else(lete=Array.makennull_poll_cell inPoll_emue)externalnetsys_poll:poll_mem->int->int ->int="netsys_poll"letconcat_fd_listl=String.concat","(List.map(funfd->Int64.to_string(int64_of_file_descrfd))l)(*win32 only: *)externalnetsys_real_select:Unix.file_descrlist->Unix.file_descrlist->Unix.file_descrlist->float->(Unix.file_descrlist*Unix.file_descrlist*Unix.file_descrlist)="netsys_real_select"letreal_select=ifSys.os_type="Win32"thennetsys_real_selectelseUnix.selectletdo_poll aktmo=matchawith|Poll_mem(s,_)->netsys_pollsktmo|Poll_emue->(* Emulate poll using Unix.select. This is slow! *)lettmo'=iftmo<0then(-1.0)elsefloattmo*.0.001inletl_inp=ref[]inlet l_out=ref[]inlet l_pri=ref[]infor j=0tok-1doletc=e.(j)inlet(f_inp,f_out,f_pri)=poll_req_triple c.poll_req_eventsiniff_inpthenl_inp:=c.poll_fd::!l_inp;iff_out thenl_out:=c.poll_fd::!l_out;iff_pri thenl_pri:=c.poll_fd::!l_pri;done;dlogr(fun()->sprintf"poll_emulation request in=%s out=%s pri=%s tmo=%f"(concat_fd_list!l_inp)(concat_fd_list!l_out)(concat_fd_list!l_pri)tmo');let(o_inp,o_out,o_pri)=real_select !l_inp!l_out!l_pritmo'indlogr(fun()->sprintf"poll_emulation result in=%s out=%s pri=%s"(concat_fd_listo_inp)(concat_fd_listo_out)(concat_fd_listo_pri));leta_inp=Array.of_listo_inpinleta_out=Array.of_listo_outinleta_pri=Array.of_listo_priinArray.sortcomparea_inp;Array.sortcomparea_out;Array.sortcomparea_pri;letn=ref0infor j=0tok-1doletc=e.(j)inletg_inp=Netsys_impl_util.mem_sorted_arrayc.poll_fda_inpinlet g_out=Netsys_impl_util.mem_sorted_arrayc.poll_fda_outinlet g_pri=Netsys_impl_util.mem_sorted_arrayc.poll_fda_priinlet rev=(ifg_inpthenconst_rd_eventelse0)lor(ifg_outthenconst_wr_eventelse0)lor(ifg_prithenconst_pri_eventelse0)inc.poll_act_events<-rev;ifrev<>0thenincrndone;!nletpollaktmo=ifk<0||k>poll_array_lengthatheninvalid_arg"Netsys.poll";letr=Netsys_impl_util.slice_time_ms(funtmo0->(* tmo0 is now an int in milliseconds *)letn=do_pollaktmo0inifn=0thenNoneelseSomen)tmoinmatchrwith|None->0|Somen->nletrestarting_pollaktmo=Netsys_impl_util.restart_tmo(pollak)tmoletpoll_singlefdrwpritmo=leta=create_poll_array1inset_poll_cella0{poll_fd=fd;poll_req_events=poll_req_eventsrwpri;poll_act_events=poll_null_events()};polla1tmo>0letact_events_of_intn=nletint_of_act_eventsn=nletreq_events_of_intn=nletint_of_req_eventsn=n(* poll aggregation *)typenetsys_event_sourcetypenetsys_event_aggregtypetagged_event=|EV_FDofUnix.file_descrtypeevent_push={event_id:int;event_tag:tagged_event;event_api_req :int;}classtypeevent_source=objectmethodevent_id:intmethodevent_tag:tagged_eventmethodpush_record:event_pushmethodneed_push:boolmethodpost_poll:int->int->unitmethodupd_kernel_events:int->unitmethodset_post_modify_callback:(int->unit)->unitmethodclear_post_modify_callback:unit->unitmethodmodify_fd_event_source:int->unitmethodact_events:intendclassfd_event_sourcefdreq:event_source=letevent_id=ref0inletevent_api_req=refreqinletevent_kernel_req =ref0inletevent_tag=EV_FDfdinletact_events=ref0inletpost_modify_callback=refNoneinobject(self)initializer(letid=Oo.idselfinevent_id:=id)methodevent_id=!event_idmethodevent_tag=event_tagmethodpush_record={event_id=!event_id;event_tag=event_tag;event_api_req=!event_api_req}methodneed_push=!event_api_req<>!event_kernel_reqmethodupd_kernel_eventsreq=event_kernel_req:=req;methodpost_pollmaskact=event_kernel_req :=!event_kernel_reqlandmask;act_events:=actmethodset_post_modify_callbackf=match!post_modify_callbackwith|None->post_modify_callback:=Somef|Some_->failwith"Netsys_posix.add_event_source: the source is already \
added to an aggregator"methodclear_post_modify_callback()=post_modify_callback:=Nonemethodmodify_fd_event_sourcereq=event_api_req:=req;match!post_modify_callbackwith|Somef->f!event_id|None->()methodact_events=!act_eventsendletfd_event_sourcefdreq=newfd_event_source fdreqletmodify_fd_event_sourceesreq=es#modify_fd_event_source reqletget_fd_of_event_sourcees=matches #event_tagwith|EV_FDfd->fdletact_events_of_event_sourcees=es#act_eventsexternalhave_event_aggregation:unit->bool="netsys_have_event_aggregation"externalnetsys_create_event_aggreg:bool->netsys_event_aggreg="netsys_create_event_aggreg"externalnetsys_destroy_event_aggreg:netsys_event_aggreg->unit="netsys_destroy_event_aggreg"externalnetsys_event_aggreg_fd:netsys_event_aggreg->Unix.file_descr="netsys_event_aggreg_fd"externalnetsys_push_event_sources:netsys_event_aggreg->event_pushlist->unit="netsys_push_event_sources"externalnetsys_add_event_source:netsys_event_aggreg->event_push->unit="netsys_add_event_source"externalnetsys_del_event_source:netsys_event_aggreg->int->tagged_event->unit="netsys_del_event_source"externalnetsys_poll_event_sources:netsys_event_aggreg->int->(int*int *int)list(* The list are triples (event_id, mask, act) *)="netsys_poll_event_sources"externalnetsys_interrupt_aggreg:netsys_event_aggreg->unit="netsys_interrupt_aggreg"classevent_aggregatoris_interruptible=letup=reftrueinlet all_sources=Hashtbl.create 27inletupd_sources=Hashtbl.create 27inletnetsys=netsys_create_event_aggregis_interruptibleinletcheck_up()=ifnot!upthenfailwith"Netsys_posix: This event_aggregator is already destroyed"inobject(self)methodadd_event_source(es:event_source)=check_up();es#set_post_modify_callbackself#post_modify_callback;trynetsys_add_event_sourcenetsyses#push_record;Hashtbl.replaceall_sourceses#event_ides;Hashtbl.replaceupd_sourceses#event_ideswith|error->es#clear_post_modify_callback();raiseerrormethod del_event_source(es:event_source)=check_up();ifnot(Hashtbl.memall_sourceses#event_id)thenfailwith"Netsys_posix.del_event_source: not member of this aggregator";netsys_del_event_sourcenetsyses#event_ides#event_tag;es#clear_post_modify_callback();Hashtbl.removeall_sourceses#event_id;Hashtbl.removeupd_sourceses#event_idmethodpost_modify_callbackid=check_up();if(Hashtbl.memall_sourcesid)then(letes=Hashtbl.findall_sourcesidinHashtbl.replaceupd_sourcesides)methodpush_event_updates()=check_up();letl=Hashtbl.fold(fun_esacc->ifes#need_pushthenletp=es#push_recordines#upd_kernel_eventsp.event_api_req;p::accelseacc)upd_sources[]innetsys_push_event_sourcesnetsysl;Hashtbl.clearupd_sourcesmethodpoll_event_sourcestmo=self#push_event_updates();letv=Netsys_impl_util.slice_time_ms(funmillis->letevpairs0=netsys_poll_event_sourcesnetsysmillisinletevpairs1=(* without spurious events *)List.filter(fun(id,_,_)->Hashtbl.memall_sourcesid)evpairs0inletevpairs2=List.map(fun(id,mask,act)->letes=Hashtbl.findall_sourcesidines#post_pollmask act;Hashtbl.replaceupd_sourcesides;es)evpairs1inifevpairs2=[]thenNoneelseSomeevpairs2)tmoinmatch vwith|None->[]|Somel->lmethodevent_aggregator_fd=check_up();netsys_event_aggreg_fdnetsysmethodinterrupt_event_aggregator()=netsys_interrupt_aggregnetsysmethoddestroy_event_aggregator()=if!upthen(netsys_destroy_event_aggregnetsys;up:=false)endletcreate_event_aggregator=newevent_aggregatorletadd_event_sourceea=ea#add_event_sourceletdel_event_sourceea=ea#del_event_sourceletpush_event_updatesea=ea#push_event_updates()letpoll_event_sources(ea:event_aggregator)=ea#poll_event_sourcesletevent_aggregator_fdea=ea#event_aggregator_fdletdestroy_event_aggregatorea=ea#destroy_event_aggregator()letinterrupt_event_aggregatorea=ea#interrupt_event_aggregator()(* events *)typenot_eventexternalnsys_create_event:bool->not_event="netsys_create_not_event"externalset_nonblock_event:not_event->unit="netsys_set_nonblock_not_event"externalget_event_fd_nodup:not_event->Unix.file_descr ="netsys_get_not_event_fd_nodup"externalset_event:not_event->unit="netsys_set_not_event"externalwait_event:not_event->unit="netsys_wait_not_event"externalconsume_event:not_event->unit="netsys_consume_not_event"externalnsys_destroy_event:not_event->unit="netsys_destroy_not_event"externalnsys_return_all_event_fd:not_event->Unix.file_descr list="netsys_return_all_not_event_fd"letcreate_event()=lete=nsys_create_eventtrueinList.iter(funfd->Netlog.Debug.track_fd~owner:"Netsys_posix"~descr:"create_event"fd;)(nsys_return_all_event_fde);eletdestroy_evente=List.iter(funfd->Netlog.Debug.release_fdfd;)(nsys_return_all_event_fde);nsys_destroy_eventeletget_event_fde=letfd=get_event_fd_nodupeinletfd=Unix.dupfdinUnix.set_close_on_exec fd;fdletreport_signal_as_eventnesignum=(* This is simpler to implement in Ocaml than in C:
- [ne] is kept reachable all the time
- we can pass [ne] to the handler via a closure
- we can run non-async-signal-safe stuff in Ocaml signal handlers,
because they are actually called indirectly
*)Sys.set_signalsignum(Sys.Signal_handle(fun_->set_eventne))(* post fork handlers *)classtypepost_fork_handler=objectmethodname:stringmethodrun:unit->unitendmodulePFH=structtypet=post_fork_handlerletcompare=compareendmodulePFH_Set =Set.Make(PFH)letpost_fork_registry =refPFH_Set.emptyletpost_fork_mutex=!Netsys_oothr.provider#create_mutex()letregister_post_fork_handlerpfh=post_fork_mutex#lock();post_fork_registry:=PFH_Set.addpfh!post_fork_registry;post_fork_mutex#unlock()letremove_post_fork_handlerpfh=post_fork_mutex#lock();post_fork_registry:=PFH_Set.removepfh!post_fork_registry;post_fork_mutex#unlock()letrun_post_fork_handlers()=PFH_Set.iter(funpfh->try pfh#run()with|error->prerr_endline("Netsys_posix: Exception in post fork handler "^pfh#name^": "^Netexn.to_stringerror))!post_fork_registry(* "at" *)typeat_flag=AT_EACCESS|AT_SYMLINK_NOFOLLOW |AT_SYMLINK_FOLLOW |AT_REMOVEDIR(* The stubs assume these type definitions: *)#ifdefHAVE_O_KEEPEXECtypeopen_flag1=Unix.open_flag=O_RDONLY|O_WRONLY |O_RDWR|O_NONBLOCK |O_APPEND |O_CREAT|O_TRUNC|O_EXCL |O_NOCTTY |O_DSYNC|O_SYNC|O_RSYNC|O_SHARE_DELETE|O_CLOEXEC |O_KEEPEXEC#else#ifdefHAVE_O_CLOEXECtypeopen_flag1=Unix.open_flag=O_RDONLY|O_WRONLY|O_RDWR |O_NONBLOCK |O_APPEND|O_CREAT|O_TRUNC|O_EXCL|O_NOCTTY|O_DSYNC|O_SYNC|O_RSYNC|O_SHARE_DELETE|O_CLOEXEC#else#ifdefHAVE_O_SHARE_DELETEtypeopen_flag1=Unix.open_flag=O_RDONLY|O_WRONLY |O_RDWR|O_NONBLOCK |O_APPEND|O_CREAT|O_TRUNC|O_EXCL |O_NOCTTY|O_DSYNC|O_SYNC|O_RSYNC|O_SHARE_DELETE#elsetypeopen_flag1=Unix.open_flag=O_RDONLY|O_WRONLY|O_RDWR |O_NONBLOCK |O_APPEND|O_CREAT|O_TRUNC|O_EXCL|O_NOCTTY|O_DSYNC|O_SYNC|O_RSYNC#endif#endif#endiftype access_permission1=Unix.access_permission=R_OK|W_OK|X_OK|F_OKexternalnetsys_at_fdcwd:unit->Unix.file_descr="netsys_at_fdcwd"letat_fdcwd=netsys_at_fdcwd()externalhave_at :unit->bool="netsys_have_at"external openat :Unix.file_descr->string ->Unix.open_flaglist->Unix.file_perm->Unix.file_descr="netsys_openat"externalfaccessat:Unix.file_descr->string->Unix.access_permissionlist->at_flaglist->unit="netsys_faccessat"externalmkdirat:Unix.file_descr->string->int->unit="netsys_mkdirat"externalrenameat:Unix.file_descr->string->Unix.file_descr->string->unit="netsys_renameat"externallinkat:Unix.file_descr->string ->Unix.file_descr->string->at_flaglist->unit="netsys_linkat"externalunlinkat:Unix.file_descr->string->at_flaglist->unit="netsys_unlinkat"external symlinkat:string->Unix.file_descr->string->unit="netsys_symlinkat"externalmkfifoat:Unix.file_descr ->string->int->unit="netsys_mkfifoat"externalreadlinkat:Unix.file_descr->string->string="netsys_readlinkat"(* Clocks *)typetimespec =float*inttypeclock_idtypeclock=(* also in Netlog *)|CLOCK_REALTIME|CLOCK_MONOTONIC|CLOCK_IDofclock_idexternalnanosleep:timespec->timespecref->unit="netsys_nanosleep"externalclock_gettime:clock->timespec="netsys_clock_gettime"(* also in Netlog *)externalclock_settime :clock-> timespec->unit="netsys_clock_settime"externalclock_getres:clock->timespec="netsys_clock_getres"external clock_getcpuclockid:int->clock_id="netsys_clock_getcpuclockid"typetimer_expiration=|TEXP_NONE|TEXP_EVENTofnot_event|TEXP_EVENT_CREATE|TEXP_SIGNALofinttypeposix_timerexternal have_posix_timer:unit->bool="netsys_have_posix_timer"externalnsys_timer_create:clock->timer_expiration->posix_timer="netsys_timer_create"externaltimer_settime :posix_timer->bool->timespec->timespec->unit="netsys_timer_settime"externaltimer_gettime:posix_timer->timespec="netsys_timer_gettime"externaltimer_delete:posix_timer->unit="netsys_timer_delete"externaltimer_event:posix_timer ->not_event="netsys_timer_event"lettimer_createclocktexp=letv=nsys_timer_createclocktexpinGc.finalisetimer_deletev;v(* Spawn *)typewd_spec=|Wd_keep|Wd_chdirofstring|Wd_fchdirofUnix.file_descrtypepg_spec=|Pg_keep|Pg_new_bg_group|Pg_new_fg_group|Pg_join_groupofinttypefd_action=|Fda_closeofUnix.file_descr|Fda_close_ignoreofUnix.file_descr|Fda_close_exceptofboolarray|Fda_dup2ofUnix.file_descr *Unix.file_descrtype sig_action=|Sig_defaultofint|Sig_ignoreofint|Sig_maskofintlistexternal netsys_spawn :wd_spec->pg_spec->fd_actionlist->sig_actionlist->stringarray->string->stringarray->int="netsys_spawn_byte" "netsys_spawn_nat"externalnetsys_posix_spawn:pg_spec->fd_actionlist->sig_actionlist->stringarray->string->stringarray->int="netsys_posix_spawn_byte""netsys_posix_spawn_nat"externalhave_posix_spawn:unit->bool="netsys_have_posix_spawn"letspawn?(chdir=Wd_keep)?(pg=Pg_keep)?(fd_actions=[])?(sig_actions=[])?(env=Unix.environment())?(no_posix_spawn=false)cmdargs=(* Check whether we can use the faster netsys_posix_spawn *)letuse_posix_spawn=notno_posix_spawn &&have_posix_spawn()&&chdir=Wd_keep&&pg<>Pg_new_fg_group&¬(List.exists(funsa->matchsawithSig_ignore _->true|_-> false)sig_actions)intryifnotuse_posix_spawnthenfailwith"USE_FORK_EXEC";netsys_posix_spawnpgfd_actionssig_actions envcmdargs(* may also fail with "USE_FORK_EXEC" in some cases *)with|Failure"USE_FORK_EXEC"->(* Fixup: if pg = Pg_new_fg_group, we remove any Sig_default for
SIGTTOU from sig_actions. Because of special handling, the effect
of Sig_default is enforced by the implementation, but this must be
done at [execve] time.
*)letsig_actions=ifpg=Pg_new_fg_group thenList.filter(funspec ->spec<>Sig_defaultSys.sigttou)sig_actionselsesig_actionsinnetsys_spawnchdirpgfd_actions sig_actionsenvcmdargstypewatched_subprocess={atom_idx:int;mutablealive:bool;mutableallocated:bool;}external netsys_watch_subprocess :int->int->bool->Unix.file_descr*int="netsys_watch_subprocess"externalnetsys_ignore_subprocess:int->unit="netsys_ignore_subprocess"externalnetsys_forget_subprocess:int->unit="netsys_forget_subprocess"externalnetsys_get_subprocess_status:int ->Unix.process_status option="netsys_get_subprocess_status"externalinstall_subprocess_handler:unit->unit="netsys_install_sigchld_handler"externalsubprocess_cleanup_after_fork:unit->unit="netsys_subprocess_cleanup_after_fork"externalnetsys_kill_subprocess:int->int->unit="netsys_kill_subprocess"external netsys_killpg_subprocess:int->int->unit="netsys_killpg_subprocess"externalkill_all_subprocesses:int->bool->bool->unit="netsys_kill_all_subprocesses"externalkillpg_all_subprocesses:int->bool->unit="netsys_killpg_all_subprocesses"letforget_subprocessws=ifws.allocatedthen(netsys_forget_subprocessws.atom_idx;ws.allocated<-false;);ws.alive <-falseletwatch_subprocesspidpgidkill_flag=ifpid<=0||pgid<0theninvalid_arg"Netsys_posix.watch_subprocess";letfd,atom_idx=netsys_watch_subprocesspidpgidkill_flaginletws={atom_idx=atom_idx;alive=true;allocated=true}inGc.finaliseforget_subprocessws;(fd,ws)letignore_subprocessws=ifnotws.alivethenfailwith"Netsys_posix.ignore_subprocess: stale reference";netsys_ignore_subprocessws.atom_idx;ws.alive<-falseletget_subprocess_statusws=ifnotws.alive thenfailwith"Netsys_posix.get_subprocess_status: stale reference";netsys_get_subprocess_statusws.atom_idxletkill_subprocesssignal ws=ifws.alivethennetsys_kill_subprocesssignalws.atom_idxletkillpg_subprocesssignalws=ifws.alivethennetsys_killpg_subprocess signalws.atom_idxlet()=register_post_fork_handler(objectmethodname="subprocess_cleanup_after_fork"methodrun=subprocess_cleanup_after_forkend)letregister_subprocess_handler()=Netsys_signal.register_exclusive_handler~name:"Sigchld handler in Netsys_posix"~signal:Sys.sigchld~install:install_subprocess_handler()(* locales *)typelanginfo={nl_CODESET:string;nl_D_T_FMT :string;nl_D_FMT :string;nl_T_FMT:string;nl_T_FMT_AMPM:string;nl_AM_STR:string;nl_PM_STR:string;nl_DAY_1:string;nl_DAY_2:string;nl_DAY_3:string;nl_DAY_4 :string;nl_DAY_5:string;nl_DAY_6:string;nl_DAY_7:string;nl_ABDAY_1:string;nl_ABDAY_2:string;nl_ABDAY_3:string;nl_ABDAY_4:string;nl_ABDAY_5:string;nl_ABDAY_6 :string;nl_ABDAY_7 :string;nl_MON_1 :string;nl_MON_2:string;nl_MON_3:string;nl_MON_4:string;nl_MON_5:string;nl_MON_6:string;nl_MON_7:string;nl_MON_8:string;nl_MON_9:string;nl_MON_10 :string;nl_MON_11:string;nl_MON_12:string;nl_ABMON_1:string;nl_ABMON_2:string;nl_ABMON_3:string;nl_ABMON_4:string;nl_ABMON_5:string;nl_ABMON_6:string;nl_ABMON_7:string;nl_ABMON_8:string;nl_ABMON_9 :string;nl_ABMON_10 :string;nl_ABMON_11 :string;nl_ABMON_12:string;nl_ERA:string;nl_ERA_D_FMT :string;nl_ERA_D_T_FMT:string;nl_ERA_T_FMT:string;nl_ALT_DIGITS:string;nl_RADIXCHAR:string;nl_THOUSEP:string;nl_YESEXPR:string;nl_NOEXPR:string;nl_CRNCYSTR:string;}externalnetsys_query_langinfo:string->langinfo="netsys_query_langinfo"letcached_langinfo=refNoneletquery_langinfolocale=iflocale=""then(match!cached_langinfowith|None->letli=netsys_query_langinfo""incached_langinfo:=Someli;li|Someli->li)elsenetsys_query_langinfolocale(* syslog *)typelevel=Netlog.leveltypem_level=|LOG_EMERG |LOG_ALERT |LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUGlettrans_level=[`Emerg,LOG_EMERG;`Alert,LOG_ALERT;`Crit,LOG_CRIT;`Err,LOG_ERR;`Warning,LOG_WARNING;`Notice,LOG_NOTICE;`Info,LOG_INFO;`Debug,LOG_DEBUG]typesyslog_facility=[`Authpriv|`Cron|`Daemon|`Ftp|`Kern|`Local0|`Local1|`Local2|`Local3|`Local4|`Local5|`Local6|`Local7|`Lpr|`Mail|`News|`Syslog|`User|`Uucp|`Default]typem_syslog_facility=|LOG_AUTHPRIV|LOG_CRON|LOG_DAEMON|LOG_FTP|LOG_KERN|LOG_LOCAL0|LOG_LOCAL1|LOG_LOCAL2|LOG_LOCAL3|LOG_LOCAL4|LOG_LOCAL5|LOG_LOCAL6|LOG_LOCAL7|LOG_LPR|LOG_MAIL|LOG_NEWS|LOG_SYSLOG|LOG_USER|LOG_UUCP|LOG_DEFAULTlettrans_facility=[`Authpriv,LOG_AUTHPRIV;`Cron,LOG_CRON;`Daemon,LOG_DAEMON;`Ftp,LOG_FTP;`Kern,LOG_KERN;`Local0,LOG_LOCAL0;`Local1,LOG_LOCAL1;`Local2,LOG_LOCAL2;`Local3,LOG_LOCAL3;`Local4,LOG_LOCAL4;`Local5,LOG_LOCAL5;`Local6,LOG_LOCAL6;`Local7,LOG_LOCAL7;`Lpr,LOG_LPR;`Mail,LOG_MAIL;`News,LOG_NEWS;`Syslog,LOG_SYSLOG;`User,LOG_USER;`Uucp,LOG_UUCP;`Default,LOG_DEFAULT;]typesyslog_option=[`Cons|`Ndelay|`Odelay|`Nowait|`Pid]typem_syslog_option=|LOG_CONS|LOG_NDELAY|LOG_ODELAY|LOG_NOWAIT|LOG_PIDlettrans_syslog_option=[`Cons,LOG_CONS;`Ndelay,LOG_NDELAY;`Odelay,LOG_ODELAY;`Nowait,LOG_NOWAIT;`Pid,LOG_PID;]externalnetsys_openlog:stringoption ->m_syslog_optionlist->m_syslog_facility->unit="netsys_openlog"externalnetsys_syslog:m_syslog_facility->m_level->string->unit="netsys_syslog"externalnetsys_closelog :unit->unit="netsys_closelog"letopenlogid_optoptsfac=trynetsys_openlogid_opt(List.map(funp-> List.assocptrans_syslog_option)opts)(List.assocfactrans_facility)with|Not_found->assert falseletsyslogfaclevmsg=trynetsys_syslog(List.assocfactrans_facility )(List.assoclevtrans_level )msgwith|Not_found->assertfalseletcloselog=netsys_closelog(* Sync *)externalfsync:Unix.file_descr->unit="netsys_fsync"externalfdatasync:Unix.file_descr->unit="netsys_fdatasync"(* Optional POSIX functions *)externalhave_fadvise:unit->bool="netsys_have_posix_fadvise"typeadvice =|POSIX_FADV_NORMAL|POSIX_FADV_SEQUENTIAL|POSIX_FADV_RANDOM|POSIX_FADV_NOREUSE|POSIX_FADV_WILLNEED|POSIX_FADV_DONTNEED|FADV_NORMAL|FADV_SEQUENTIAL|FADV_RANDOM|FADV_NOREUSE|FADV_WILLNEED|FADV_DONTNEEDexternalfadvise:Unix.file_descr->int64->int64->advice ->unit="netsys_fadvise"externalhave_fallocate:unit->bool="netsys_have_posix_fallocate"externalfallocate:Unix.file_descr->int64->int64->unit="netsys_fallocate"(* POSIX shared memory *)externalhave_posix_shm:unit->bool="netsys_have_posix_shm"typeshm_open_flag=|SHM_O_RDONLY|SHM_O_RDWR|SHM_O_CREAT|SHM_O_EXCL|SHM_O_TRUNCexternalshm_open:string->shm_open_flaglist->int->file_descr="netsys_shm_open"external shm_unlink:string->unit="netsys_shm_unlink"letshm_createprefixsize=letpid=Unix.getpid()inlett=Unix.gettimeofday()inletrecloopn=letid=sprintf"%d/%f/%d"pidtninletdg=Digest.to_hex(Digest.stringid)inletdg8=String.subdg08inletname=sprintf"%s_%s"prefixdg8intryletfd=shm_openname[SHM_O_RDWR;SHM_O_CREAT;SHM_O_EXCL]0o600in(tryUnix.fchmodfd0o600withUnix.Unix_error(Unix.EINVAL,_,_)->()(* OSX seems to throw EINVAL here *));ifsize >0thenUnix.ftruncatefdsize;(fd,name)with|Unix.Unix_error(Unix.EEXIST,_,_)->loop(n+1)inloop0typesem_kind=[`Named|`Anonymous]type sem_reptype'sem_kindsemaphore=Netsys_types.memory*sem_rep(* We keep a reference to the bigarray to prevent that it is
collected while a semaphore is stored in it
*)typenamed_semaphore =[`Named ]semaphoretypeanon_semaphore=[`Anonymous ]semaphoretype sem_open_flag =|SEM_O_CREAT|SEM_O_EXCLtypesem_wait_behavior=|SEM_WAIT_BLOCK|SEM_WAIT_NONBLOCKletdummy_mem=Bigarray.Array1.createBigarray.charBigarray.c_layout0externalhave_anon_posix_semaphores:unit->bool="netsys_have_sem_anon"externalhave_named_posix_semaphores:unit->bool="netsys_have_sem_named"lethave_posix_semaphores()=have_anon_posix_semaphores()&&have_named_posix_semaphores()externalnetsys_sem_size :unit->int="netsys_sem_size"externalnetsys_sem_value_max:unit->int="netsys_sem_value_max"letsem_size=netsys_sem_size()letsem_value_max=netsys_sem_value_max()externalnetsys_sem_open:string->sem_open_flaglist->int->int->sem_rep="netsys_sem_open"letsem_opennameflagsmodeinit_value=ifinit_value<0||init_value >sem_value_maxtheninvalid_arg"Netsys_posix.sem_open";letsr=netsys_sem_open nameflagsmodeinit_valuein(dummy_mem,sr)externalnetsys_sem_close :sem_rep ->unit="netsys_sem_close"letsem_close(_,sr)=netsys_sem_closesrexternalsem_unlink:string->unit ="netsys_sem_unlink"letsem_createprefixinitval=letpid=Unix.getpid()inlett=Unix.gettimeofday()inlet recloopn=letid=sprintf"%d/%f/%d"pidtninletdg=Digest.to_hex(Digest.string id)inletdg8 =String.subdg08inletname=sprintf "%s_%s" prefixdg8intryletsem=sem_openname[SEM_O_CREAT;SEM_O_EXCL]0o600initvalin(sem,name)with|Unix.Unix_error(Unix.EEXIST,_,_)->loop(n+1)inloop0externalnetsys_sem_init:Netsys_types.memory->int->bool->int->sem_rep="netsys_sem_init"letsem_initmempospsharedinit_value=ifpos<0||pos>Bigarray.Array1.dimmem -sem_sizetheninvalid_arg"Netsys_posix.sem_init";ifinit_value<0||init_value>sem_value_max theninvalid_arg"Netsys_posix.sem_init";letsr=netsys_sem_initmempospsharedinit_valuein(mem,sr)externalnetsys_as_sem:Netsys_types.memory->int->sem_rep="netsys_as_sem"letas_sem mempos=ifpos<0||pos >Bigarray.Array1.dimmem-sem_size theninvalid_arg"Netsys_posix.as_sem";letsr=netsys_as_sem memposin(mem,sr)externalnetsys_sem_destroy :sem_rep ->unit="netsys_sem_destroy"letsem_destroy(_,sr)=netsys_sem_destroysrexternalnetsys_sem_getvalue:sem_rep->int="netsys_sem_getvalue"letsem_getvalue(_,sr)=netsys_sem_getvaluesrexternalnetsys_sem_post:sem_rep->unit="netsys_sem_post"letsem_post(_,sr)=netsys_sem_postsrexternalnetsys_sem_wait:sem_rep->sem_wait_behavior->unit="netsys_sem_wait"letsem_wait(_,sr)b=netsys_sem_waitsrbtypeioprio_target=|Ioprio_processofint|Ioprio_pgrpofint|Ioprio_userofinttypeioprio=|Noprio|Real_timeofint|Best_effortofint|Idleexternalioprio_get:ioprio_target->ioprio="netsys_ioprio_get"externalioprio_set:ioprio_target->ioprio->unit="netsys_ioprio_set"lethave_ioprio()=trylet_=ioprio_get(Ioprio_process(Unix.getpid()))intruewith_->false