diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc index 3a6f71555..04e8cb176 100644 --- a/src/libstore/unix/build/derivation-builder.cc +++ b/src/libstore/unix/build/derivation-builder.cc @@ -18,6 +18,7 @@ #include "nix/store/user-lock.hh" #include "nix/store/globals.hh" #include "nix/store/build/derivation-env-desugar.hh" +#include "nix/util/terminal.hh" #include @@ -808,8 +809,7 @@ std::optional DerivationBuilderImpl::startBuild() if (!builderOut) throw SysError("opening pseudoterminal master"); - // FIXME: not thread-safe, use ptsname_r - std::string slaveName = ptsname(builderOut.get()); + std::string slaveName = getPtsName(builderOut.get()); if (buildUser) { if (chmod(slaveName.c_str(), 0600)) @@ -923,7 +923,7 @@ void DerivationBuilderImpl::prepareSandbox() void DerivationBuilderImpl::openSlave() { - std::string slaveName = ptsname(builderOut.get()); + std::string slaveName = getPtsName(builderOut.get()); AutoCloseFD builderOut = open(slaveName.c_str(), O_RDWR | O_NOCTTY); if (!builderOut) diff --git a/src/libutil/include/nix/util/terminal.hh b/src/libutil/include/nix/util/terminal.hh index f19de268c..fa71e074e 100644 --- a/src/libutil/include/nix/util/terminal.hh +++ b/src/libutil/include/nix/util/terminal.hh @@ -36,4 +36,12 @@ void updateWindowSize(); */ std::pair getWindowSize(); +/** + * Get the slave name of a pseudoterminal in a thread-safe manner. + * + * @param fd The file descriptor of the pseudoterminal master + * @return The slave device name as a string + */ +std::string getPtsName(int fd); + } // namespace nix diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index b5765487c..656847487 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -1,6 +1,7 @@ #include "nix/util/terminal.hh" #include "nix/util/environment-variables.hh" #include "nix/util/sync.hh" +#include "nix/util/error.hh" #ifdef _WIN32 # include @@ -12,6 +13,8 @@ #endif #include #include +#include +#include // for ptsname and ptsname_r namespace { @@ -176,4 +179,29 @@ std::pair getWindowSize() return *windowSize.lock(); } +std::string getPtsName(int fd) +{ +#ifdef __APPLE__ + static std::mutex ptsnameMutex; + // macOS doesn't have ptsname_r, use mutex-protected ptsname + std::lock_guard lock(ptsnameMutex); + const char * name = ptsname(fd); + if (!name) { + throw SysError("getting pseudoterminal slave name"); + } + return name; +#else + // Use thread-safe ptsname_r on platforms that support it + // PTY names are typically short: + // - Linux: /dev/pts/N (where N is usually < 1000) + // - FreeBSD: /dev/pts/N + // 64 bytes is more than sufficient for any Unix PTY name + char buf[64]; + if (ptsname_r(fd, buf, sizeof(buf)) != 0) { + throw SysError("getting pseudoterminal slave name"); + } + return buf; +#endif +} + } // namespace nix