diff --git a/src/commands.cpp b/src/commands.cpp index 09a0241..0951de8 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -728,6 +729,34 @@ int compose_up_command(const std::string& images_directory, const std::string& c // state file (no -u/--up ever ran for this file) isn't an error -- just // nothing to stop. // +// A killed session's own sandboxed process (what kill_session() actually +// waits for, via the cgroup/pid-namespace/tracked-pid strategy it picks) +// isn't the same process as the one that started it: run_mounted_container() +// runs in the compose service's own daemonized process, blocked in its own +// waitpid() on bwrap -- once that returns, *that* process still has to run +// its own post-exit cleanup (stop_tap_relay() among it) before a tap+relay +// join's own host-side device is actually removed from the bridge. Since +// this happens in a completely separate, independently-scheduled process, +// checking is_network_in_use() the instant kill_session() itself returns +// can genuinely still see it as attached -- confirmed on the real Android +// target device, where the two kill+check calls above landed only single- +// digit milliseconds apart, well within the gap that process's own +// scheduling can take on a loaded device. This retries for a bounded time +// (nanosleep()-based, matching this project's own existing polling style) +// rather than giving up on the very first still-attached answer. +bool network_becomes_unused(const NetworkEntry& network, int timeout_ms, int interval_ms) { + for (int waited = 0; waited < timeout_ms; waited += interval_ms) { + if (!is_network_in_use(network)) { + return true; + } + struct timespec ts { + interval_ms / 1000, (interval_ms % 1000) * 1000000L + }; + nanosleep(&ts, nullptr); + } + return !is_network_in_use(network); +} + // Networks and volumes are then handled asymmetrically, matching real // Compose's own default and the user's own explicit direction: volumes // always persist (never touched here at all -- a compose-managed volume @@ -768,7 +797,7 @@ int compose_down_command(const std::string& compose_file_name, AppConfig& config if (it == config.networks.end()) { continue; // never created, or already removed } - if (is_network_in_use(*it)) { + if (!network_becomes_unused(*it, 10000, 200)) { fmt::print("network '{}' is still in use, leaving it\n", actual_name); continue; }