diff --git a/lix/libutil/cgroup.cc b/lix/libutil/cgroup.cc index 920c6ec69..e772da7ab 100644 --- a/lix/libutil/cgroup.cc +++ b/lix/libutil/cgroup.cc @@ -50,10 +50,9 @@ static std::map getCgroups(const Path & cgroupFile) return cgroups; } -static CgroupStats readStatistics(const std::filesystem::path & cgroup) -{ +static Result readStatistics(const std::filesystem::path & cgroup) +try { CgroupStats stats; - auto cpustatPath = cgroup / "cpu.stat"; if (pathExists(cpustatPath)) { @@ -77,13 +76,15 @@ static CgroupStats readStatistics(const std::filesystem::path & cgroup) } return stats; +} catch (...) { + return result::current_exception(); } static void killCgroup(const std::string & name, const std::filesystem::path & cgroup) { auto killFile = cgroup / "cgroup.kill"; if (pathExists(killFile)) - writeFile(killFile, "1"); + writeFileUninterruptible(killFile, "1"); else { throw SysError( "cgroup '%s' at '%s' does not possess `cgroup.kill` ; are you running Lix on a kernel " @@ -115,7 +116,7 @@ destroyCgroup(const std::string & name, const std::filesystem::path & aliveCgrou killCgroup(name, aliveCgroup); - CgroupStats stats = readStatistics(aliveCgroup); + Result stats = readStatistics(aliveCgroup); if (rmdir(aliveCgroup.c_str()) == -1) { throw SysError("deleting cgroup '%s' at '%s'", name, aliveCgroup); @@ -123,7 +124,8 @@ destroyCgroup(const std::string & name, const std::filesystem::path & aliveCgrou debug("cgroup '%s' destroyed", name); - return stats; + /* Even if this contains an exception, this is fine. */ + return stats.value(); } CgroupHierarchy getLocalHierarchy(const std::filesystem::path & cgroupFilesystem) @@ -376,7 +378,7 @@ CgroupStats AutoDestroyCgroup::getStatistics() const */ return std::visit( nix::overloaded{ - [&](const Path & aliveCgroup) { return readStatistics(aliveCgroup); }, + [&](const Path & aliveCgroup) { return readStatistics(aliveCgroup).value(); }, [&](const CgroupStats & stats) { return stats; } }, cgroup_ diff --git a/lix/libutil/file-system.cc b/lix/libutil/file-system.cc index b29361719..47fc2f7ba 100644 --- a/lix/libutil/file-system.cc +++ b/lix/libutil/file-system.cc @@ -358,14 +358,13 @@ Generator readFileSource(const Path & path) }(std::move(fd)); } - -void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) +void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync, bool allowInterrupts) { AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)}; if (!fd) throw SysError("opening file '%1%'", path); try { - writeFull(fd.get(), s); + writeFull(fd.get(), s, allowInterrupts); } catch (Error & e) { e.addTrace({}, "writing file '%1%'", path); throw; @@ -378,6 +377,10 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) syncParent(path); } +void writeFileUninterruptible(const Path & path, std::string_view s, mode_t mode, bool sync) +{ + writeFile(path, s, mode, sync, false); +} static AutoCloseFD openForWrite(const Path & path, mode_t mode) { diff --git a/lix/libutil/file-system.hh b/lix/libutil/file-system.hh index 69798cb5f..7d76b4fd0 100644 --- a/lix/libutil/file-system.hh +++ b/lix/libutil/file-system.hh @@ -189,7 +189,16 @@ Generator readFileSource(const Path & path); /** * Write a string to a file. */ -void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false); +void writeFile( + const Path & path, + std::string_view s, + mode_t mode = 0666, + bool sync = false, + bool allowInterrupts = true +); +void writeFileUninterruptible( + const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false +); void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false); kj::Promise> diff --git a/tests/nixos/cgroups/default.nix b/tests/nixos/cgroups/default.nix index a8700ae4f..339e99e82 100644 --- a/tests/nixos/cgroups/default.nix +++ b/tests/nixos/cgroups/default.nix @@ -25,6 +25,7 @@ # Start build in background host.execute("NIX_REMOTE=daemon nix build --use-cgroups --auto-allocate-uids --file ${./hang.nix} >&2 &") + pid = int(host.succeed("pgrep nix")) service = "/sys/fs/cgroup/system.slice/nix-daemon.service" # Wait for cgroups to be created @@ -35,6 +36,13 @@ host.succeed(f'[ -z "$(cat {service}/cgroup.procs)" ]') host.succeed(f'[ -n "$(cat {service}/supervisor/cgroup.procs)" ]') host.succeed(f'[ -n "$(cat {service}/nix-build-uid-*/cgroup.procs)" ]') + + # Perform an interrupt + host.execute(f"kill -SIGINT {pid}") + + # Check that there aren't any cgroups anymore, neither any state records + host.succeed(f"until [ ! -e {service}/nix-build-uid-* ]; do sleep 1; done", timeout=30) + host.succeed("until [ ! -e /nix/var/nix/cgroups/nix-build-uid-* ]; do sleep 1; done", timeout=30) ''; }