package core_unix
Unix-specific portions of Core
Install
Dune Dependency
Authors
Maintainers
Sources
v0.17.1.tar.gz
md5=9370dca36f518fcea046d2752e3de22b
sha512=c4e8ce9d5885ac8fa8d554a97e1857f3a1c933e0eb5dfd4fe874412b9d09e6d0a2973b644733855553f33f5c859719228f0e6aaf3a2b7eb5befb46fc513750de
doc/src/core_unix.daemon/daemon.ml.html
Source file daemon.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
open! Core open Poly open! Import module Unix = Core_unix let check_threads ~allow_threads_to_have_been_created = (* forking, especially to daemonize, when running multiple threads is tricky, and generally a mistake. It's so bad, and so hard to catch, that we test in two different ways *) if (not allow_threads_to_have_been_created) && Thread.threads_have_been_created () then failwith "Daemon.check_threads: may not be called if any threads have ever been created"; match Thread.num_threads () with | None -> () (* This is pretty bad, but more likely to be a problem with num_threads *) | Some (1 | 2) -> () (* main thread, or main + ticker - both ok *) | Some _ -> failwith "Daemon.check_threads: may not be called if more than 2 threads (hopefully the \ main thread + ticker thread) are running" ;; module Fd_redirection = struct type do_redirect = [ `Dev_null | `Dev_null_skip_regular_files | `File_append of string | `File_truncate of string ] type t = [ `Do_not_redirect | do_redirect ] end let redirect_fd ?perm ~mode ~src ~dst () = match src with | `Do_not_redirect -> () | #Fd_redirection.do_redirect as src -> let redirect src = Unix.dup2 ~src ~dst (); Unix.close src in let open_dev_null () = Unix.openfile "/dev/null" ~mode:[ mode ] ~perm:0o777 in (match src with | `Dev_null_skip_regular_files -> let is_regular () = try (Unix.fstat dst).Unix.st_kind = Unix.S_REG with | Unix.Unix_error (EBADF, _, _) -> false in if not (is_regular ()) then redirect (open_dev_null ()) else () | `Dev_null -> redirect (open_dev_null ()) | `File_append file -> redirect (Unix.openfile file ?perm ~mode:[ mode; Unix.O_CREAT; Unix.O_APPEND ]) | `File_truncate file -> redirect (Unix.openfile file ?perm ~mode:[ mode; Unix.O_CREAT; Unix.O_TRUNC ])) ;; let redirect_stdio_fds ?perm ~stdout ~stderr () = redirect_fd ?perm ~mode:Unix.O_RDONLY ~src:`Dev_null ~dst:Unix.stdin (); redirect_fd ?perm ~mode:Unix.O_WRONLY ~src:stdout ~dst:Unix.stdout (); redirect_fd ?perm ~mode:Unix.O_WRONLY ~src:stderr ~dst:Unix.stderr () ;; let daemonize ?(redirect_stdout = `Dev_null) ?(redirect_stderr = `Dev_null) ?(cd = "/") ?perm ?umask ?(allow_threads_to_have_been_created = false) () = check_threads ~allow_threads_to_have_been_created; let fork_no_parent () = match Unix.handle_unix_error Unix.fork with | `In_the_child -> () | `In_the_parent _ -> exit 0 in (* Fork into the background, parent exits, child continues. *) fork_no_parent (); (* Become session leader. *) ignore (Unix.Terminal_io.setsid ()); (* Fork again to ensure that we will never regain a controlling terminal. *) fork_no_parent (); (* Release old working directory. *) Unix.chdir cd; (* Ensure sensible umask. Adjust as needed. *) Option.iter umask ~f:(fun umask -> ignore (Unix.umask umask)); redirect_stdio_fds ?perm ~stdout:redirect_stdout ~stderr:redirect_stderr () ;; let process_status_to_exit_code = function | Ok () -> 0 | Error (`Exit_non_zero i) -> i | Error (`Signal s) -> (* looking at byterun/signals.c in ocaml source tree, I think this should never be zero for signals coming from [wait*] function family. *) Signal.to_caml_int s ;; let daemonize_wait ?(redirect_stdout = `Dev_null_skip_regular_files) ?(redirect_stderr = `Dev_null_skip_regular_files) ?(cd = "/") ?perm ?umask ?(allow_threads_to_have_been_created = false) () = check_threads ~allow_threads_to_have_been_created; match Unix.handle_unix_error Unix.fork with | `In_the_child -> ignore (Unix.Terminal_io.setsid ()); let read_end, write_end = Unix.pipe () in let buf = "done" in let len = String.length buf in (match Unix.handle_unix_error Unix.fork with | `In_the_child -> (* The process that will become the actual daemon. *) Unix.close read_end; Unix.chdir cd; Option.iter umask ~f:(fun umask -> ignore (Unix.umask umask)); Staged.stage (fun () -> redirect_stdio_fds ?perm ~stdout:redirect_stdout ~stderr:redirect_stderr (); let old_sigpipe_behavior = Signal.Expert.signal Signal.pipe `Ignore in (try ignore (Unix.write_substring write_end ~buf ~pos:0 ~len : int) with | _ -> ()); Signal.Expert.set Signal.pipe old_sigpipe_behavior; Unix.close write_end) | `In_the_parent pid -> (* The middle process, after it has forked its child. *) Unix.close write_end; let rec loop () = match Unix.wait_nohang (`Pid pid) with | None -> (match Unix.select ~read:[ read_end ] ~write:[] ~except:[] ~timeout:(`After (Time_ns.Span.of_sec 0.1)) () with | { Unix.Select_fds.read = [ read_end ]; write = []; except = [] } -> (* If the child process exits before detaching and the middle process happens to be in this call to select, the pipe will be closed and select will return a ready file descriptor, but with zero bytes to read. In this case, we want to loop back again and call waitpid to obtain the correct exit status to propagate on to the outermost parent (otherwise we might incorrectly return a success). *) if Unix.read read_end ~buf:(Bytes.create len) ~pos:0 ~len > 0 then exit 0 else loop () | _ -> loop ()) | Some (_pid, process_status) -> exit (process_status_to_exit_code process_status) in loop ()) | `In_the_parent pid -> exit (process_status_to_exit_code (Unix.waitpid pid)) ;;
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>