Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file daemonize.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163typestatus=|Startedof{daemon_info:string;pid:Pid.t}|Already_runningof{daemon_info:string;pid:Pid.t}|Finishedletretry?message?(count=100)f=letrecloop=function|xwhenx>=count->Result.Error(Printf.sprintf"too many retries (%i)"x^matchmessagewith|None->""|Somemsg->": "^msg)|x->(matchf()with|Somev->Result.Okv|None->Unix.sleepf0.1;loop(x+1))inloop0letmake_beaconpath=Option.iter~f:Path.mkdir_p(Path.parentpath);letp=Path.to_stringpathinletfd=Unix.openfilep[Unix.O_RDWR;Unix.O_CREAT]0o600inifFcntl.lock_tryfdFcntl.WritethenResult.OkfdelseResult.Error"already running"letseal_beaconpathfdcontents=letp=Path.to_stringpathandlength=String.lengthcontentsinifUnix.writefd(Bytes.of_stringcontents)0length<>lengththen(Unix.closefd;Result.Error(Printf.sprintf"couldn't write whole endpoint to port file \"%s\""p))else(Fcntl.lockfdFcntl.Read;Result.Okfd)letcheck_beacon?(close=true)p=matchResult.try_with(fun()->Unix.openfilep[Unix.O_RDONLY]0o600)with|Result.Okfd->letf()=letopenResult.Oinretry(fun()->matchFcntl.lock_getfdFcntl.Writewith|None->SomeNone|Some(Fcntl.Read,pid)->Some(Somepid)|Some(Fcntl.Write,_)->None)>>|Option.map~f:(funpid->(Io.read_all(Unix.in_channel_of_descrfd),pid,fd))andfinally()=ifclosethenUnix.closefdinExn.protect~f~finally|Result.Error(Unix.Unix_error(Unix.ENOENT,_,_))->Result.OkNone|Result.Error(Unix.Unix_error(c,_,_))->Result.Error(Printf.sprintf"unable to open %s: %s"p(Unix.error_messagec))|Result.Error_->Result.Error(Printf.sprintf"unable to open %s"p)letdaemonize?workdir?(foreground=false)beacon(f:(daemon_info:string->unit)->unit)=letffd=letf()=f(fun~daemon_info->ignore(seal_beaconbeaconfddaemon_info))andfinally()=Unix.truncate(Path.to_stringbeacon)0inExn.protect~f~finallyinletpath=Path.to_stringbeaconinletpath=matchworkdirwith|SomeworkdirwhenFilename.is_relativepath->Filename.concat(Path.to_stringworkdir)path|_->pathinletopenResult.Oincheck_beaconpath>>=function|None->ifforegroundthen(let+fd=make_beaconbeaconinffd;Finished)elseifUnix.fork()=0then(ignore(Unix.setsid());Sys.set_signalSys.sighupSys.Signal_ignore;Sys.set_signalSys.sigpipeSys.Signal_ignore;ifUnix.fork()=0then(Option.iter~f:(funp->Path.mkdir_pp;Unix.chdir(Path.to_stringp))workdir;letnull=open_in"/dev/null"andout=open_out"stdout"anderr=open_out"stderr"inUnix.dup2(Unix.descr_of_in_channelnull)(Unix.descr_of_in_channelstdin);Unix.dup2(Unix.descr_of_out_channelout)(Unix.descr_of_out_channelstdout);Unix.dup2(Unix.descr_of_out_channelerr)(Unix.descr_of_out_channelstderr);close_innull;close_outout;close_outerr;ignore(Unix.umask0);ignore(let+fd=make_beaconbeaconinffd));exit0)elseletopenResult.Oinlet*fd=retry~message:(Printf.sprintf"waiting for beacon file \"%s\" to be created"path)(fun()->trySome(Unix.openfilepath[Unix.O_RDONLY]0o600)withUnix.Unix_error(Unix.ENOENT,_,_)->None)inlet+daemon_info,pid=retry~message:(Printf.sprintf"waiting for beacon file \"%s\" to be locked"path)(fun()->matchFcntl.lock_getfdFcntl.Writewith|Some(Fcntl.Read,pid)->Some(Io.read_all(Unix.in_channel_of_descrfd),pid)|_->None)inStarted{daemon_info;pid=Pid.of_intpid}|Some(daemon_info,pid,_)->Result.Ok(Already_running{daemon_info;pid=Pid.of_intpid})letstopbeacon=letopenResult.Oincheck_beacon~close:false(Path.to_stringbeacon)>>=function|None->Result.Error"not running"|Some(_,pid,fd)->(letkillsignal=Unix.killpidsignal;retry~message:(Printf.sprintf"waiting for daemon to stop (PID %i)"pid)(fun()->Option.some_if(Fcntl.lock_getfdFcntl.Write=None)())inmatchkillSys.sigtermwith|Error_->(* Unfortunately the logger may not be set. Print on stderr directly? *)(* Log.info "unable to terminate daemon with SIGTERM, using SIGKILL"; *)killSys.sigkill|ok->ok)