Skip to content

Commit c02fc01

Browse files
committed
fix(node): ignore benign port exits in trap_exit device servers
ThinPool, Img.Server, Layer.Server and Img.Mutable each run privileged commands through `System.cmd`, which links a transient port to the caller. Because these servers trap exits (for `terminate/2` teardown), the port's normal close arrives as `{:EXIT, port, :normal}`. With no matching handle_info clause the server crash-looped on it -- Layer.Server crashed while mounting a layer, which surfaced to create_vm as :no_capacity. Add the same `{:EXIT, _port, :normal} -> :noreply` ignore clause to all four servers. An abnormal exit carries a non-`:normal` reason and still falls through to the default handler.
1 parent 9ee5de4 commit c02fc01

4 files changed

Lines changed: 24 additions & 0 deletions

File tree

lib/hyper/node/img/mutable.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ defmodule Hyper.Node.Img.Mutable do
119119
@impl true
120120
def handle_info(:idle_timeout, state), do: {:noreply, state}
121121

122+
@impl true
123+
# Each privileged command runs through `System.cmd`, which links a transient
124+
# port to this process; because we trap exits (for `terminate/2` teardown),
125+
# the port's normal close is delivered here. Ignore it.
126+
def handle_info({:EXIT, _port, :normal}, state), do: {:noreply, state}
127+
122128
@impl true
123129
def terminate(_reason, state) do
124130
# Destroy the thin volume, then release the image (its monitor on us also

lib/hyper/node/img/server.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ defmodule Hyper.Node.Img.Server do
128128
{:noreply, state}
129129
end
130130

131+
@impl true
132+
# Each privileged command runs through `System.cmd`, which links a transient
133+
# port to this process; because we trap exits (for `terminate/2` teardown),
134+
# the port's normal close is delivered here. Ignore it.
135+
def handle_info({:EXIT, _port, :normal}, state), do: {:noreply, state}
136+
131137
@impl true
132138
def terminate(_reason, %State{dm_names: dm_names}) do
133139
# Remove top-down (a snapshot's origin is the device below it). Layers are

lib/hyper/node/img/thin_pool.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ defmodule Hyper.Node.Img.ThinPool do
9494
{:reply, :ok, id_free(state, id)}
9595
end
9696

97+
@impl true
98+
# Each privileged command runs through `System.cmd`, which links a transient
99+
# port to this process; because we trap exits (for `terminate/2` teardown),
100+
# the port's normal close is delivered here. Ignore it.
101+
def handle_info({:EXIT, _port, :normal}, state), do: {:noreply, state}
102+
97103
@impl true
98104
def terminate(_reason, state) do
99105
_ = SuidHelper.Dmsetup.remove(@pool_name)

lib/hyper/node/layer/server.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ defmodule Hyper.Node.Layer.Server do
119119
{:noreply, state}
120120
end
121121

122+
@impl true
123+
# Each privileged command runs through `System.cmd`, which links a transient
124+
# port to this process; because we trap exits (for `terminate/2` teardown),
125+
# the port's normal close is delivered here. Ignore it.
126+
def handle_info({:EXIT, _port, :normal}, state), do: {:noreply, state}
127+
122128
@impl true
123129
def terminate(_reason, %State{blk_path: blk_path}) do
124130
case SuidHelper.Losetup.detach(blk_path) do

0 commit comments

Comments
 (0)