123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100typec_action=Obj.ttypet={run:'a.((c_action->'a)->'a)}[@@unboxed](* A [fork_fn] is a C function that can be executed after forking. It cannot call OCaml code or
run the OCaml GC. It is passed a [Unix.file_descr] for errors and a pointer
to a [c_action]. On success it should write nothing to the error stream and
return 0. On error, it should write a message to the error FD and return a
non-zero value for the exit status (e.g. 1). *)typefork_fnletrecwith_actionsactionsfn=matchactionswith|[]->fn[]|{run}::xs->run@@func_action->with_actionsxs@@func_actions->fn(c_action::c_actions)typec_arrayexternalmake_string_array:int->c_array="eio_unix_make_string_array"externalaction_execve:unit->fork_fn="eio_unix_fork_execve"letaction_execve=action_execve()letexecvepath~argv~env=letargv_c_array=make_string_array(Array.lengthargv)inletenv_c_array=make_string_array(Array.lengthenv)in{run=funk->k(Obj.repr(action_execve,path,argv_c_array,argv,env_c_array,env))}externalaction_chdir:unit->fork_fn="eio_unix_fork_chdir"letaction_chdir=action_chdir()letchdirpath={run=funk->k(Obj.repr(action_chdir,path))}externalaction_fchdir:unit->fork_fn="eio_unix_fork_fchdir"letaction_fchdir=action_fchdir()letfchdirfd={run=funk->Fd.use_exn"fchdir"fd@@funfd->k(Obj.repr(action_fchdir,fd))}letint_of_fd:Unix.file_descr->int=Obj.magictypeaction=Inherit_fds.action={src:int;dst:int}letrecwith_fdsmappingk=matchmappingwith|[]->k[]|(dst,src,_)::xs->Fd.use_exn"inherit_fds"src@@funsrc->with_fdsxs@@funxs->k((dst,int_of_fdsrc)::xs)typeblocking=[|`Blocking|`Nonblocking|`Preserve_blocking]externalaction_dups:unit->fork_fn="eio_unix_fork_dups"letaction_dups=action_dups()letinherit_fdsm=letblocking=m|>List.filter_map(fun(dst,_,flags)->matchflagswith|`Blocking->Some(dst,true)|`Nonblocking->Some(dst,false)|`Preserve_blocking->None)inwith_fdsm@@funm->letplan:actionlist=Inherit_fds.planmin{run=funk->k(Obj.repr(action_dups,plan,blocking))}externalaction_setpgid:unit->fork_fn="eio_unix_fork_setpgid"letaction_setpgid=action_setpgid()letsetpgidpgid={run=funk->k(Obj.repr(action_setpgid,0,pgid))}externalaction_setuid:unit->fork_fn="eio_unix_fork_setuid"letaction_setuid=action_setuid()letsetuiduid={run=funk->k(Obj.repr(action_setuid,uid))}externalaction_setgid:unit->fork_fn="eio_unix_fork_setgid"letaction_setgid=action_setgid()letsetgidgid={run=funk->k(Obj.repr(action_setgid,gid))}externalaction_login_tty:unit->fork_fn="eio_unix_login_tty"letaction_login_tty=action_login_tty()letlogin_ttyfd={run=funk->Fd.use_exn"login_tty"fd@@funfd->k(Obj.repr(action_login_tty,fd))}externalerror_of_code:int->Unix.error="eio_unix_error_of_code"letreport_spawn_errormsg=(* A failed fork action writes "<fn>:<errno>" to the errors pipe. *)matchScanf.sscanf_optmsg"%[^:]:%d"(funfncode->(fn,error_of_codecode))with|Some(fn,err)->raise(Unix.Unix_error(err,fn,""))|None->failwithmsg