Core/Posix: Rework implementation (using posix_spawn)
This commit is contained in:
parent
b63c9fcc49
commit
a02990beff
|
|
@ -24,7 +24,7 @@ namespace Nz::PlatformImpl
|
||||||
{
|
{
|
||||||
ret = ::close(fd);
|
ret = ::close(fd);
|
||||||
}
|
}
|
||||||
while (ret != -1 || errno == EINTR);
|
while (ret == -1 && errno == EINTR);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Nz::PlatformImpl
|
||||||
{
|
{
|
||||||
ret = ::read(fd, buf, count);
|
ret = ::read(fd, buf, count);
|
||||||
}
|
}
|
||||||
while (ret != -1 || errno == EINTR);
|
while (ret == -1 && errno == EINTR);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Nz::PlatformImpl
|
||||||
{
|
{
|
||||||
ret = ::write(fd, buf, count);
|
ret = ::write(fd, buf, count);
|
||||||
}
|
}
|
||||||
while (ret != -1 || errno == EINTR);
|
while (ret == -1 && errno == EINTR);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <NazaraUtils/StackArray.hpp>
|
#include <NazaraUtils/StackArray.hpp>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <spawn.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <Nazara/Core/Debug.hpp>
|
#include <Nazara/Core/Debug.hpp>
|
||||||
|
|
@ -21,7 +22,7 @@ namespace Nz::PlatformImpl
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
|
||||||
if (errno == ESRCH)
|
if (errno == ESRCH)
|
||||||
return OK(false);
|
return Ok(false);
|
||||||
|
|
||||||
return Err(Error::GetLastSystemError());
|
return Err(Error::GetLastSystemError());
|
||||||
}
|
}
|
||||||
|
|
@ -43,9 +44,10 @@ namespace Nz::PlatformImpl
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
return Err("failed to create pipe: " + Error::GetLastSystemError());
|
return Err("failed to create pipe: " + Error::GetLastSystemError());
|
||||||
|
|
||||||
|
std::filesystem::path fullpath = std::filesystem::absolute(program);
|
||||||
|
|
||||||
// Double fork (see https://0xjet.github.io/3OHA/2022/04/11/post.html)
|
// Double fork (see https://0xjet.github.io/3OHA/2022/04/11/post.html)
|
||||||
// We will create a child and a grand-child process, using a pipe to retrieve the grand-child pid
|
// We will create a child and a grand-child process, using a pipe to retrieve the grand-child pid
|
||||||
// TODO: Maybe use vfork for the child process too?
|
|
||||||
// TODO: Use posix_spawn if possible instead
|
// TODO: Use posix_spawn if possible instead
|
||||||
pid_t childPid = ::fork();
|
pid_t childPid = ::fork();
|
||||||
if (childPid == -1)
|
if (childPid == -1)
|
||||||
|
|
@ -71,40 +73,24 @@ namespace Nz::PlatformImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t grandChildPid = ::vfork();
|
StackArray<char*> args = NazaraStackArrayNoInit(char*, arguments.size() + 2);
|
||||||
if (grandChildPid == 0)
|
|
||||||
|
// It's safe to const_cast here as we're using a copy of the memory (from child) from the original process
|
||||||
|
args[0] = const_cast<char*>(program.c_str());
|
||||||
|
for (std::size_t i = 0; i < arguments.size(); ++i)
|
||||||
|
args[i + 1] = const_cast<char*>(arguments[i].data());
|
||||||
|
|
||||||
|
args.back() = nullptr;
|
||||||
|
|
||||||
|
char* envs[] = { nullptr };
|
||||||
|
|
||||||
|
pid_t grandChildPid;
|
||||||
|
if (posix_spawn(&grandChildPid, fullpath.c_str(), nullptr, nullptr, args.data(), envs) == 0)
|
||||||
{
|
{
|
||||||
// Grand-child process
|
PidOrErr pid;
|
||||||
StackArray<char*> argv = NazaraStackArrayNoInit(char*, arguments.size() + 2);
|
pid.pid = grandChildPid;
|
||||||
|
|
||||||
// It's safe to const_cast here as we're using a copy of the memory (from child) from the original process
|
pipe.Write(&pid, sizeof(pid));
|
||||||
argv[0] = const_cast<char*>(program.c_str());
|
|
||||||
for (std::size_t i = 0; i < arguments.size(); ++i)
|
|
||||||
argv[i + 1] = const_cast<char*>(arguments[i].data());
|
|
||||||
|
|
||||||
argv[argv.size() - 1] = nullptr;
|
|
||||||
|
|
||||||
char* envs[] = { nullptr };
|
|
||||||
|
|
||||||
::execve(program.c_str(), argv.data(), envs);
|
|
||||||
|
|
||||||
// If we get here, execve failed
|
|
||||||
// Remember we share the memory of our parent (vfork) so we need to exit using _exit() to avoid calling the parent exit handlers
|
|
||||||
|
|
||||||
PidOrErr err;
|
|
||||||
err.pid = -1;
|
|
||||||
err.err = errno;
|
|
||||||
|
|
||||||
pipe.Write(&err, sizeof(err));
|
|
||||||
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
else if (grandChildPid != -1)
|
|
||||||
{
|
|
||||||
PidOrErr err;
|
|
||||||
err.pid = grandChildPid;
|
|
||||||
|
|
||||||
pipe.Write(&err, sizeof(err));
|
|
||||||
|
|
||||||
// Exits the child process, at this point the grand-child should have started
|
// Exits the child process, at this point the grand-child should have started
|
||||||
std::exit(0);
|
std::exit(0);
|
||||||
|
|
@ -131,7 +117,7 @@ namespace Nz::PlatformImpl
|
||||||
::waitpid(childPid, &childStatus, 0);
|
::waitpid(childPid, &childStatus, 0);
|
||||||
|
|
||||||
PidOrErr pidOrErr;
|
PidOrErr pidOrErr;
|
||||||
if (pipe.Read(&pidOrErr, sizeof(pidOrErr) != sizeof(pidOrErr)))
|
if (pipe.Read(&pidOrErr, sizeof(pidOrErr)) != sizeof(pidOrErr))
|
||||||
{
|
{
|
||||||
// this should never happen
|
// this should never happen
|
||||||
return Err("failed to create child: couldn't retrieve status from pipe");
|
return Err("failed to create child: couldn't retrieve status from pipe");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue