Sharing what I found while researching and solving my issues with this, In case anybody needs some solutions for dealing with misbehaving ports/processes outside the beam - it’s a bit hacky, but hey, gets the work done on linux ubuntu and chromium instances.
First, use “kill” on the bash script itself, then have a function that aggregates recursively all the PIDS, kill them, checks the cleanup and as last resort “kill -9” them. kill -9 on chromium processes on linux is a bad idea, although sometimes it won’t help to just do the standard kill, as the processes will remain running, so you’re basically left with having to make sure you catch them all. In my particular case I can’t pgrep/grep on the process name, as I need to kill specific instances of chromium at certain times, and not all running instances of chromium.
For the bash script
commandline blablablabla &
pid=$!
while read line ; do
:
done
ps -ef | grep $pid | grep -v grep | awk '{print $2}' | xargs kill
kill -KILL $pid
This will grep all processes that mention the &background command PID (so the main process itself and any other forked processes that reference it as the Parent PID). Sends them a kill signal, and a -9 to the main process alone. Xargs is parallel, so all processes grepped there will receive a regular kill before the parent receives a -9.
This should work most of the time, but chromium sometimes is nasty. So I added a new layer at the application level. Since I always pass an arbitrary non-repeated debugger port to the chromium instance I can use that to identify the processes I’m interested in finding. You can use whatever, and even pass non-existing flags to identify your chromium instances, since they keep it as part of the “command” attribute that was ran to create the process:
Task.start(fn -> try_port_kill(ws_port) end)
def try_port_kill(port) do
pids = "ps aux | grep -ie port=#{port} | grep -v grep | awk '{print $2}'" |> String.to_charlist |> :os.cmd |> List.to_string |> String.split
port_kill(pids)
end
def port_kill([]), do: :ok
def port_kill([h|t]) do
pids = "ps -ef | grep #{h} | grep -v grep | awk '{print $2}'" |> String.to_charlist |> :os.cmd |> List.to_string |> String.split
Task.start(fn -> port_kill(pids -- [h]) end)
System.cmd("kill", ["#{h}"])
Task.start(fn -> brutal_kill(h) end)
port_kill(t)
end
def brutal_kill(h) do
case "kill -0 #{h}" |> String.to_charlist |> :os.cmd |> List.to_string do
"" ->
:timer.sleep 5_000
System.cmd("kill", ["-9", "#{h}"])
_ ->
:ok
end
end
This doesn’t rely on the PID from the bashscript - I’m also grepping on something I’m sure is unique in my use case “port=an_arbitrary_port_that_I_had_set” can only match for what I want so I’m fine with the hackiness of it. You could also start chromium with a flag such as --my-marker-flag=something and then grepping on this. I call this method after I “close” the port.
Lastly since it involves long-lived, always running processes and I don’t want to risk having any of them creep, and even although it seems what I’ve added before is enough to keep it tamed and under control, I went a bit further and added a sweeper based on time that will kill any chromium/chrome processes running more than 40min, and I recurrently send_after to call this:
def sweeper do
{all, _} = System.cmd("ps", ["-eo", "pid,lstart,comm"])
[header | proc_lines] = String.split(all, "\n")
Enum.each(proc_lines, fn(proc) -> sweep_zombie(proc) end)
{:noreply, state}
end
def sweep_zombie(proc_line) do
split = String.split(proc_line)
{[pid, _wday, month, day, hhmmss, year], all_comm} = Enum.split(split, 6)
comm = Enum.join(all_comm, " ")
case Regex.match?(~r/chrom/, comm) || Regex.match?(~r/Chrome/, comm) do
true ->
{:ok, datetime, 0} = DateTime.from_iso8601("#{year}-#{n_month(month)}-#{String.pad_leading(day, 2, "0")}T#{hhmmss}Z")
dt = DateTime.to_unix(datetime)
tn = System.system_time(:second)
case (tn - dt) > (40*60) do
true ->
IO.puts("DT: #{inspect dt} TN: #{inspect tn} COMM: #{inspect comm}")
System.cmd("kill", ["-9", "#{pid}"])
false ->
:ok
end
false ->
:ok
end
end
def n_month("Jan"), do: "01"
def n_month("Feb"), do: "02"
def n_month("Mar"), do: "03"
def n_month("Apr"), do: "04"
def n_month("May"), do: "05"
def n_month("Jun"), do: "06"
def n_month("Jul"), do: "07"
def n_month("Aug"), do: "08"
def n_month("Sep"), do: "09"
def n_month("Oct"), do: "10"
def n_month("Nov"), do: "11"
def n_month("Dec"), do: "12"