English version
Accueil     À propos     Téléchargement     Ressources     Contactez-nous    

Ce site est rarement mis à jour. Pour les informations les plus récentes, rendez-vous sur le nouveau site OCaml à l'adresse ocaml.org.

Browse thread
Closing all open file descriptors
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: 2007-09-14 (07:33)
From: Dave Benjamin <dave@r...>
Subject: Re: [Caml-list] Closing all open file descriptors
Erik de Castro Lopo wrote:
> Dave Benjamin wrote:
>> Did your program "daemonize" at all, then? Or did you use daemontools?
> Yes, it called Unix.fork and then in the child called Unix.setsid.
> Err, what's daemontools?

daemontools is a service management framework by D. J. Bernstein which 
those pages you linked were partially in reference to. The basic 
philosophy is that you don't write daemons at all; you write regular 
programs that read from stdin and write to stdout. You don't fork, you 
don't setsid, you don't worry about the environment and you don't keep 
track of PID files.

(IMHO, that's why they're so worked up about closing file descriptors. 
They're trying to get daemons to stop being daemons so they can be 
controlled by daemontools, and this is one of the sticking points.)

The daemontools style is actually a little bit like functional 
programming in its use of wrapper programs. A typical daemontools 
solution to the file descriptor closing problem would be to write a tiny 
C program that closes the file descriptors and then forks and execs its 
argument, like this:

closefds mydaemon arg1 arg2 ...

where "closefds" closes the file descriptors and then runs "mydaemon" 
with the supplied arguments. This way, "closefds" would just be another 
reusable tool.

Incidentally, Linux comes with a "setsid" program that does the same 
think with fork and setsid. It doesn't seem to be available for BSD, 
though (though "nohup" is, which is similar as well).

> My program has passes around a logging function of type "string -> unit".
> If it not a daemon it uses print_endline for logging and if it is a daemon 
> it uses a wrapper around syslog (via the debian/ubuntu libsyslog-ocaml-dev)
> package.

Makes sense to me.

>> Yeah, the whole operation could be done in a C function too, and 
>> probably wouldn't be much more code to write.
> My daemon is only tiny, but I'm, still glad its in Ocaml.

I agree, overall the Unix module is pretty nice to use.

Here's a port of the "daemonize" C function described in APUE to OCaml, 
and it's where my earlier code example came from:

(* Based on Adv. Programming in the UNIX Environment, 2nd Ed. *)

#use "topfind";;
#require "unix";;
#require "syslog";;

open Printf

let daemonize cmd =
   (* Clear file creation mask. *)
   ignore (Unix.umask 0);

   (* Become a session leader to lose controlling TTY. *)
   if Unix.fork () > 0 then exit 0;
   ignore (Unix.setsid ());

   (* Ensure future opens won't allocate controlling TTYs. *)
   Sys.set_signal Sys.sighup Sys.Signal_ignore;
   if Unix.fork () > 0 then exit 0;

   (* Change the current working directory to the root so
      we won't prevent file systems from being unmounted. *)
   Unix.chdir "/";

   (* Close all open file descriptors. *)
   for fd = 0 to 1024 do
     try Unix.close (Obj.magic fd : Unix.file_descr)
     with Unix.Unix_error _ -> ()

   (* Attach file descriptors 0, 1, and 2 to /dev/null. *)
   let fd0 = Unix.openfile "/dev/null" [Unix.O_RDWR] 0o666 in
   let fd1 = Unix.dup fd0 in
   let fd2 = Unix.dup fd0 in

   (* Initialize the log file. *)
   let log = Syslog.openlog cmd in
   if fd0 <> Unix.stdin || fd1 <> Unix.stdout || fd2 <> U
       Syslog.syslog log `LOG_ERR
         (sprintf "unexpected file descriptors %d %d %d"
            (Obj.magic fd0 : int)
            (Obj.magic fd1 : int)
            (Obj.magic fd2 : int));
       exit 1

let () =
   let log = daemonize "daemonize" in
   Syslog.syslog log `LOG_INFO "Starting daemon";
   Unix.sleep 10;
   Syslog.syslog log `LOG_INFO "Farewell"