diff --git a/dev.Dockerfile b/dev.Dockerfile index bbf2683..9acbd3c 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -8,7 +8,7 @@ FROM ${DEV_IMAGE} # Install debian packages RUN apt-get update -qq RUN apt-get install -y inotify-tools ffmpeg curl git openssh-client \ - python3 python3-pip python3-setuptools python3-wheel python3-dev locales + python3 python3-pip python3-setuptools python3-wheel python3-dev locales procps # Install nodejs RUN curl -sL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh diff --git a/lib/pinchflat/notifications/command_runner.ex b/lib/pinchflat/notifications/command_runner.ex index 8f1a806..5f51823 100644 --- a/lib/pinchflat/notifications/command_runner.ex +++ b/lib/pinchflat/notifications/command_runner.ex @@ -45,7 +45,7 @@ defmodule Pinchflat.Notifications.CommandRunner do """ @impl AppriseCommandRunner def version do - case System.cmd(backend_executable(), ["--version"]) do + case CliUtils.wrap_cmd(backend_executable(), ["--version"]) do {output, 0} -> output |> String.split(~r{\r?\n}) diff --git a/lib/pinchflat/utils/cli_utils.ex b/lib/pinchflat/utils/cli_utils.ex index 4e2a488..8d6c1d4 100644 --- a/lib/pinchflat/utils/cli_utils.ex +++ b/lib/pinchflat/utils/cli_utils.ex @@ -5,6 +5,23 @@ defmodule Pinchflat.Utils.CliUtils do alias Pinchflat.Utils.StringUtils + @doc """ + Wraps a command in a shell script that will terminate + the command if stdin is closed. Useful for stopping + commands if the job runner is cancelled. + + Delegates to `System.cmd/3` and any options/output + are passed through. + + Returns {binary(), integer()} + """ + def wrap_cmd(command, args, opts \\ []) do + wrapper_command = Path.join(:code.priv_dir(:pinchflat), "cmd_wrapper.sh") + actual_command = [command] ++ args + + System.cmd(wrapper_command, actual_command, opts) + end + @doc """ Parses a list of command options into a list of strings suitable for passing to `System.cmd/3`. diff --git a/lib/pinchflat/yt_dlp/command_runner.ex b/lib/pinchflat/yt_dlp/command_runner.ex index ed13d33..572c3ac 100644 --- a/lib/pinchflat/yt_dlp/command_runner.ex +++ b/lib/pinchflat/yt_dlp/command_runner.ex @@ -37,7 +37,7 @@ defmodule Pinchflat.YtDlp.CommandRunner do formatted_command_opts = [url] ++ CliUtils.parse_options(all_opts) Logger.info("[yt-dlp] called with: #{Enum.join(formatted_command_opts, " ")}") - case System.cmd(command, formatted_command_opts, stderr_to_stdout: true) do + case CliUtils.wrap_cmd(command, formatted_command_opts, stderr_to_stdout: true) do {_, 0} -> # IDEA: consider deleting the file after reading it. It's in the tmp dir, so it's not # a huge deal, but it's still a good idea to clean up after ourselves. diff --git a/priv/cmd_wrapper.sh b/priv/cmd_wrapper.sh new file mode 100755 index 0000000..fe5df29 --- /dev/null +++ b/priv/cmd_wrapper.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# This script is a wrapper for other programs +# that ensures they are killed when stdin closes +# (eg: a job terminates) + +# Start the program in the background +exec "$@" & +pid1=$! + +# Silence warnings from here on +exec >/dev/null 2>&1 + +# Read from stdin in the background and +# kill running program when stdin closes +exec 0<&0 $( + while read; do :; done + kill -KILL $pid1 +) & +pid2=$! + +# Clean up +wait $pid1 +ret=$? +kill -KILL $pid2 +exit $ret diff --git a/test/pinchflat/utils/cli_utils_test.exs b/test/pinchflat/utils/cli_utils_test.exs index b053158..0c676b7 100644 --- a/test/pinchflat/utils/cli_utils_test.exs +++ b/test/pinchflat/utils/cli_utils_test.exs @@ -3,6 +3,12 @@ defmodule Pinchflat.Utils.CliUtilsTest do alias Pinchflat.Utils.CliUtils + describe "wrap_cmd/3" do + test "delegates to System.cmd/3" do + assert {"output\n", 0} = CliUtils.wrap_cmd("echo", ["output"]) + end + end + describe "parse_options/1" do test "it converts symbol k-v arg keys to kebab case" do assert ["--buffer-size", "1024"] = CliUtils.parse_options(buffer_size: 1024)