使用OCaml安全执行外部程序

时间:2018-08-28 04:43:08

标签: unix ocaml

如果外部程序被卡住或突然请求输入(由于某些错误),是否有可能避免挂起的OCaml程序。

例如,我使用7zip通过以下代码解压缩不同格式的存档数据:

let exec_un7zip ~dpath ~fpath =
    let cmdline = "7z x -aoa -o \"" ^ dpath ^ "\" \"" ^ fpath ^ "\"" in
    Printf.printf "exec (%s)\n" cmdline;
    let stdout = Core.Unix.open_process_in cmdline in
    Core.In_channel.input_all stdout |> ignore;
    stdout

如果7zip卡在某个错误上,或者尽管提供了标志,又要求其他输入,是否可以通过超时或其他方式在此处完成执行?

3 个答案:

答案 0 :(得分:2)

OCaml的标准(带有Sys模块)和Unix库提供了两个可以解决问题的功能。 Core中可能存在等效/更高级别的函数,因为您似乎正在使用它,但是我没有检查过:

  • Unix.alarm n将在sigalrm秒后发出信号n
  • 每次提高Sys.signal Sys.sigalrm (Sys.Signal_handle (fun _ -> do_something()))时,
  • do_something()将运行Sys.sigalrm。例如,在适当的过程中调用Unix.kill。另外,Sys.signal返回给定信号的旧处理程序,以便您以后可以将其恢复。

一个非常粗糙但未经全面测试的示例如下:

let cmd_timeout cmd args timeout =
  let pid = Unix.create_process cmd args Unix.stdin Unix.stdout Unix.stderr in
  let kill _ = Unix.kill pid Sys.sigterm in
  let old = Sys.signal Sys.sigalrm (Sys.Signal_handle kill) in
  let _ = Unix.alarm timeout in
  try
    let _, res = Unix.waitpid [] pid in
    (match res with
     | Unix.WEXITED d ->
       Printf.printf "Exited with status %d\n%!" d
     | Unix.WSIGNALED d ->
       Printf.printf "Signalled with %d\n%!" d
     | Unix.WSTOPPED d ->
       Printf.printf "Stopped with %d\n%!" d);
    Sys.set_signal Sys.sigalrm old
  with Unix.Unix_error(Unix.EINTR, _, _) ->
    Printf.printf "Timeout!\n%!"; Sys.set_signal Sys.sigalrm old

let () = cmd_timeout "sleep" [| "sleep"; "10" |] 3 (* will timeout *)

let () = cmd_timeout "sleep" [| "sleep"; "3" |] 10 (* will exit normally *)

答案 1 :(得分:1)

AsyncLwt库提供了用于管理外部进程而又不会阻止生成进程的模块。

答案 2 :(得分:0)

我认为,如果您分配一个伪tty并将其用于stdin,那么您可以捕获从stdin中读取的命令。否则,您最好的办法是在后台启动该命令,然后在花费太长时间(不输出任何内容)时将其终止。