Безопасное выполнение внешних программ с помощью OCaml - PullRequest
0 голосов
/ 28 августа 2018

Можно ли как-то избежать зависания программы 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 голосов
/ 28 августа 2018

Я думаю, что если вы выделите псевдотерминал и будете использовать его для стандартного ввода, тогда вы можете отловить чтение команды из стандартного ввода. В противном случае лучшее, что вы можете сделать, это запустить команду в фоновом режиме и убить ее, если она занимает слишком много времени (без вывода чего-либо).

0 голосов
/ 29 августа 2018

Библиотеки Async и Lwt предоставляют модули для управления внешними процессами без блокировки процесса порождения.

0 голосов
/ 28 августа 2018
Стандартные библиотеки

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 *)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...