Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 229 lines (194 sloc) 7.68 kb
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
1 open Stringext
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
2 open Pervasiveext
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
3
4 let debug (fmt : ('a, unit, string, unit) format4) = (Printf.kprintf (fun s -> Printf.fprintf stderr "%s\n" s) fmt)
5
6 exception Cancelled
7
6887f91 @jjd27 CA-65181: Allow arbitarary keys for syslog entries
jjd27 authored
8 type syslog_stdout_t = {
9 enabled : bool;
10 key : string option;
11 }
12
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
13 type state_t = {
14 cmdargs : string list;
15 env : string list;
16 id_to_fd_map : (string * int option) list;
6887f91 @jjd27 CA-65181: Allow arbitarary keys for syslog entries
jjd27 authored
17 syslog_stdout : syslog_stdout_t;
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
18 ids_received : (string * Unix.file_descr) list;
19 fd_sock2 : Unix.file_descr option;
20 finished : bool;
21 }
22
23 open Fe_debug
24
25 let handle_fd_sock fd_sock state =
26 try
27 let (newfd,buffer) = Fecomms.receive_named_fd fd_sock in
c8921f3 @jonludlam CA-38105: The recent patch to the recv_fd function causes it to return a...
jonludlam authored
28 if Unixext.int_of_file_descr newfd = -1 then begin
29 debug "Failed to receive an fd associated with the message '%s'" buffer;
30 failwith "Didn't get an fd"
31 end;
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
32 let dest_fd = List.assoc buffer state.id_to_fd_map in
33 let fd = begin
34 match dest_fd with
35 | Some d ->
36 debug "Received fd named: %s - duping to %d (from %d)" buffer d (Unixext.int_of_file_descr newfd);
37 let d = Unixext.file_descr_of_int d in
38 begin
39 if d = newfd
40 then ()
41 else begin
42 Unix.dup2 newfd d;
43 Unix.close newfd;
44 end
45 end;
46 d
47 | None ->
48 debug "Received fd named: %s (%d)" buffer (Unixext.int_of_file_descr newfd);
49 newfd
50 end in
51 {state with ids_received = (buffer,fd) :: state.ids_received}
52 with Fecomms.Connection_closed ->
53 {state with fd_sock2 = None}
54
55 let handle_comms_sock comms_sock state =
56 let call = Fecomms.read_raw_rpc comms_sock in
57 match call with
58 | Fe.Cancel -> debug "Cancel"; raise Cancelled
59 | Fe.Exec -> debug "Exec"; {state with finished=true;}
60 | _ ->
61 debug "Ignoring unknown command";
62 state
63
64 let handle_comms_no_fd_sock2 comms_sock fd_sock state =
65 debug "Selecting in handle_comms_no_fd_sock2";
66 let (ready,_,_) = Unix.select [comms_sock; fd_sock] [] [] (-1.0) in
67 debug "Done";
68 if List.mem fd_sock ready then begin
69 debug "fd sock";
70 let fd_sock2,_ = Unix.accept fd_sock in
71 {state with fd_sock2=Some fd_sock2}
72 end else begin
73 debug "comms sock";
74 handle_comms_sock comms_sock state
75 end
76
77 let handle_comms_with_fd_sock2 comms_sock fd_sock fd_sock2 state =
78 debug "Selecting in handle_comms_with_fd_sock2";
79 let (ready,_,_) = Unix.select [comms_sock; fd_sock2] [] [] (-1.0) in
80 debug "Done";
81 if List.mem fd_sock2 ready then begin
82 debug "fd sock2";
83 handle_fd_sock fd_sock2 state
84 end else begin
85 debug "comms sock";
86 handle_comms_sock comms_sock state
87 end
88
89 let handle_comms comms_sock fd_sock state =
90 match state.fd_sock2 with
91 | None -> handle_comms_no_fd_sock2 comms_sock fd_sock state
92 | Some x -> handle_comms_with_fd_sock2 comms_sock fd_sock x state
93
b2e3a62 @jjd27 There's no need to explicitly pass in syslog_stdout to Child.run: it's a...
jjd27 authored
94 let run state comms_sock fd_sock fd_sock_path =
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
95 let rec inner state =
96 let state = handle_comms comms_sock fd_sock state in
97 if state.finished then state else inner state
98 in
99
100 try
101 debug "Started: state.cmdargs = [%s]" (String.concat ";" (state.cmdargs));
102 debug "Started: state.env = [%s]" (String.concat ";" (state.env));
103
104 let fd = Unix.openfile "/dev/null" [ Unix.O_WRONLY ] 0o0 in
105 Unix.dup2 fd Unix.stdin;
106 Unix.dup2 fd Unix.stdout;
107 Unix.dup2 fd Unix.stderr;
108
109 if fd<>Unix.stdin && fd<>Unix.stdout && fd<>Unix.stderr then Unix.close fd;
110
111 let state = inner state in
112
113 debug "Finished...";
114 Unix.close fd_sock;
115 (match state.fd_sock2 with Some x -> Unix.close x | None -> ());
116
117 Unixext.unlink_safe fd_sock_path;
118
119 (* Finally, replace placeholder uuids in the commandline arguments
120 to be the string representation of the fd (where we don't care what
121 fd it ends up being) *)
122 let args = List.map (fun arg ->
123 try
124 let (id_received,fd) = List.find (fun (id_received,fd) -> String.endswith id_received arg) state.ids_received in
125 let stem = String.sub arg 0 (String.length arg - String.length id_received) in
126 stem ^ (string_of_int (Unixext.int_of_file_descr fd));
127 with _ -> arg) state.cmdargs in
128
129 debug "Args after replacement = [%s]" (String.concat ";" args);
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
130 let name = List.hd args in
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
131
132 let fds = List.map snd state.ids_received in
133
134 debug "I've received the following fds: [%s]\n"
135 (String.concat ";" (List.map (fun fd -> string_of_int (Unixext.int_of_file_descr fd)) fds));
136
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
137 let in_childlogging = ref None in
138 let out_childlogging = ref None in
6887f91 @jjd27 CA-65181: Allow arbitarary keys for syslog entries
jjd27 authored
139 if state.syslog_stdout.enabled then begin
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
140 (* Create a pipe used to listen to the child process's stdout *)
141 let (in_fd, out_fd) = Unix.pipe () in
142 in_childlogging := Some in_fd;
143 out_childlogging := Some out_fd
144 end;
145
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
146 let result = Unix.fork () in
147
148 if result=0 then begin
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
149 (* child *)
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
150
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
151 (* Make the child's stdout go into the pipe *)
152 Opt.iter (fun out_fd -> Unix.dup2 out_fd Unix.stdout) !out_childlogging;
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
153
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
154 (* Now let's close everything except those fds mentioned in the ids_received list *)
155 Unixext.close_all_fds_except ([Unix.stdin; Unix.stdout; Unix.stderr] @ fds);
156
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
157 (* Distance ourselves from our parent process: *)
158 if Unix.setsid () == -1 then failwith "Unix.setsid failed";
507440d @djs55 CA-36554: call setsid() before exec() to make sure children don't accide...
djs55 authored
159
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
160 (* And exec *)
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
161 Unix.execve name (Array.of_list args) (Array.of_list state.env)
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
162 end else begin
163 Fecomms.write_raw_rpc comms_sock (Fe.Execed result);
164
165 List.iter (fun fd -> Unix.close fd) fds;
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
166
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
167 (* Close the end of the pipe that's only supposed to be written to by the child process. *)
168 Opt.iter Unix.close !out_childlogging;
f0ab9b8 @jjd27 CA-63225: Add fe option to output child process's stdout to syslog
jjd27 authored
169
170 let log_failure reason code =
af5c757 @djs55 CA-38120: if we fork/exec a process and it doesn't exit with 0 (ie non-z...
djs55 authored
171 (* The commandline might be too long to clip it *)
172 let cmdline = String.concat " " args in
173 let limit = 80 - 3 in
174 let cmdline' = if String.length cmdline > limit then String.sub cmdline 0 limit ^ "..." else cmdline in
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
175 if code=0 && name = "/opt/xensource/sm/ISOSR" && (String.has_substr cmdline' "sr_scan") then () else
af96d14 @jonludlam Remove logging into /var/log/messages that ISOSR sr_scan has successfull...
jonludlam authored
176 Syslog.log Syslog.Syslog Syslog.Err (Printf.sprintf "%d (%s) %s %d" result cmdline' reason code) in
af5c757 @djs55 CA-38120: if we fork/exec a process and it doesn't exit with 0 (ie non-z...
djs55 authored
177
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
178 let status = ref (Unix.WEXITED (-1)) in
179 finally
180 (fun () ->
181 Opt.iter
182 (fun in_fd ->
6887f91 @jjd27 CA-65181: Allow arbitarary keys for syslog entries
jjd27 authored
183 let key = (match state.syslog_stdout.key with None -> Filename.basename name | Some key -> key) in
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
184 (* Read from the child's stdout and write each one to syslog *)
185 Unixext.lines_iter
186 (fun line ->
6887f91 @jjd27 CA-65181: Allow arbitarary keys for syslog entries
jjd27 authored
187 Syslog.log Syslog.Daemon Syslog.Info (Printf.sprintf "%s[%d]: %s" key result line)
83d9a1b @djs55 CA-63225: update following code review feedback
djs55 authored
188 ) (Unix.in_channel_of_descr in_fd)
189 ) !in_childlogging
190 ) (fun () -> status := snd (Unix.waitpid [] result));
191
192 let pr = match !status with
af5c757 @djs55 CA-38120: if we fork/exec a process and it doesn't exit with 0 (ie non-z...
djs55 authored
193 | Unix.WEXITED n ->
6400136 @djs55 CA-47802: fe daemon will not log successful exits any more
djs55 authored
194 (* Unfortunately logging this was causing too much spam *)
195 if n <> 0 then log_failure "exitted with code" n;
af5c757 @djs55 CA-38120: if we fork/exec a process and it doesn't exit with 0 (ie non-z...
djs55 authored
196 Fe.WEXITED n
197 | Unix.WSIGNALED n ->
9ff0538 @djs55 CA-86756: add Unixext.string_of_signal and use it to pretty-print signal...
djs55 authored
198 log_failure (Printf.sprintf "exitted with signal: %s" (Unixext.string_of_signal n));
af5c757 @djs55 CA-38120: if we fork/exec a process and it doesn't exit with 0 (ie non-z...
djs55 authored
199 Fe.WSIGNALED n
200 | Unix.WSTOPPED n ->
9ff0538 @djs55 CA-86756: add Unixext.string_of_signal and use it to pretty-print signal...
djs55 authored
201 log_failure (Printf.sprintf "stopped with signal: %s" (Unixext.string_of_signal n));
af5c757 @djs55 CA-38120: if we fork/exec a process and it doesn't exit with 0 (ie non-z...
djs55 authored
202 Fe.WSTOPPED n
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
203 in
204 let result = Fe.Finished (pr) in
4a20bb1 @djs55 CA-47802: improve logging from fork/exec daemon
djs55 authored
205 (* If the controlling process has called Forkhelpers.dontwaitpid
206 then the comms_sock will be closed. We don't need to write our full debug
207 logs in syslog if this happens: *)
208 begin
209 try
210 Fecomms.write_raw_rpc comms_sock result
211 with Unix.Unix_error(Unix.EPIPE, _, _) -> ()
212 end;
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
213 Unix.close comms_sock;
214 exit 0;
215 end
216 with
217 | Cancelled ->
218 debug "Cancelling";
219 Unix.close comms_sock;
220 Unix.close fd_sock;
221 Unixext.unlink_safe fd_sock_path;
222 exit 0;
223 | e ->
224 debug "Caught unexpected exception: %s" (Printexc.to_string e);
225 write_log ();
7347962 @djs55 CA-38105: remove a unix domain socket leak in an error path.
djs55 authored
226 Unixext.unlink_safe fd_sock_path;
ada7bad @jonludlam CA-33440: add a single-threaded fork/exec daemon to fork/exec on behalf ...
jonludlam authored
227 exit 1
228
Something went wrong with that request. Please try again.