libutil: give Pid proper resource semantics
copy-constructing or assigning from pid_t can easily lead to duplicate Pid instances for the same process if a pid_t was used carelessly, and Pid itself was copy-constructible. both could cause surprising results such as killing processes twice (which could become very problemantic, but luckily modern systems don't reuse PIDs all that quickly), or more than one piece of the code believing it owns a process when neither do Change-Id: Ifea7445f84200b34c1a1d0acc2cdffe0f01e20c6
This commit is contained in:
parent
b43a2e84c4
commit
3d155fc509
8 changed files with 37 additions and 40 deletions
|
@ -354,7 +354,7 @@ RunPager::RunPager()
|
||||||
Pipe toPager;
|
Pipe toPager;
|
||||||
toPager.create();
|
toPager.create();
|
||||||
|
|
||||||
pid = startProcess([&]() {
|
pid = Pid{startProcess([&]() {
|
||||||
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
|
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
|
||||||
throw SysError("dupping stdin");
|
throw SysError("dupping stdin");
|
||||||
if (!getenv("LESS"))
|
if (!getenv("LESS"))
|
||||||
|
@ -366,7 +366,7 @@ RunPager::RunPager()
|
||||||
execlp("less", "less", nullptr);
|
execlp("less", "less", nullptr);
|
||||||
execlp("more", "more", nullptr);
|
execlp("more", "more", nullptr);
|
||||||
throw SysError("executing '%1%'", pager);
|
throw SysError("executing '%1%'", pager);
|
||||||
});
|
})};
|
||||||
|
|
||||||
pid.setKillSignal(SIGINT);
|
pid.setKillSignal(SIGINT);
|
||||||
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||||
|
|
|
@ -35,7 +35,7 @@ HookInstance::HookInstance()
|
||||||
builderOut.create();
|
builderOut.create();
|
||||||
|
|
||||||
/* Fork the hook. */
|
/* Fork the hook. */
|
||||||
pid = startProcess([&]() {
|
pid = Pid{startProcess([&]() {
|
||||||
|
|
||||||
if (dup2(fromHook.writeSide.get(), STDERR_FILENO) == -1)
|
if (dup2(fromHook.writeSide.get(), STDERR_FILENO) == -1)
|
||||||
throw SysError("cannot pipe standard error into log file");
|
throw SysError("cannot pipe standard error into log file");
|
||||||
|
@ -60,7 +60,7 @@ HookInstance::HookInstance()
|
||||||
execv(buildHook.c_str(), stringsToCharPtrs(args).data());
|
execv(buildHook.c_str(), stringsToCharPtrs(args).data());
|
||||||
|
|
||||||
throw SysError("executing '%s'", buildHook);
|
throw SysError("executing '%s'", buildHook);
|
||||||
});
|
})};
|
||||||
|
|
||||||
pid.setSeparatePG(true);
|
pid.setSeparatePG(true);
|
||||||
fromHook.writeSide.reset();
|
fromHook.writeSide.reset();
|
||||||
|
|
|
@ -906,7 +906,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
Pipe sendPid;
|
Pipe sendPid;
|
||||||
sendPid.create();
|
sendPid.create();
|
||||||
|
|
||||||
Pid helper = startProcess([&]() {
|
Pid helper{startProcess([&]() {
|
||||||
sendPid.readSide.close();
|
sendPid.readSide.close();
|
||||||
|
|
||||||
/* We need to open the slave early, before
|
/* We need to open the slave early, before
|
||||||
|
@ -934,7 +934,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
});
|
})};
|
||||||
|
|
||||||
sendPid.writeSide.close();
|
sendPid.writeSide.close();
|
||||||
|
|
||||||
|
@ -951,7 +951,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
auto ss = tokenizeString<std::vector<std::string>>(readLine(sendPid.readSide.get()));
|
||||||
assert(ss.size() == 1);
|
assert(ss.size() == 1);
|
||||||
pid = string2Int<pid_t>(ss[0]).value();
|
pid = Pid{string2Int<pid_t>(ss[0]).value()};
|
||||||
|
|
||||||
if (usingUserNamespace) {
|
if (usingUserNamespace) {
|
||||||
/* Set the UID/GID mapping of the builder's user namespace
|
/* Set the UID/GID mapping of the builder's user namespace
|
||||||
|
@ -1010,10 +1010,10 @@ void LocalDerivationGoal::startBuilder()
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
pid = startProcess([&]() {
|
pid = Pid{startProcess([&]() {
|
||||||
openSlave();
|
openSlave();
|
||||||
runChild();
|
runChild();
|
||||||
});
|
})};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parent */
|
/* parent */
|
||||||
|
|
|
@ -70,7 +70,7 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(const std::string
|
||||||
}
|
}
|
||||||
Finally cleanup = [&]() { logger->resume(); };
|
Finally cleanup = [&]() { logger->resume(); };
|
||||||
|
|
||||||
conn->sshPid = startProcess([&]() {
|
conn->sshPid = Pid{startProcess([&]() {
|
||||||
restoreProcessContext();
|
restoreProcessContext();
|
||||||
|
|
||||||
close(in.writeSide.get());
|
close(in.writeSide.get());
|
||||||
|
@ -99,7 +99,7 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(const std::string
|
||||||
|
|
||||||
// could not exec ssh/bash
|
// could not exec ssh/bash
|
||||||
throw SysError("unable to execute '%s'", args.front());
|
throw SysError("unable to execute '%s'", args.front());
|
||||||
}, options);
|
}, options)};
|
||||||
|
|
||||||
|
|
||||||
in.readSide.reset();
|
in.readSide.reset();
|
||||||
|
@ -147,7 +147,7 @@ Path SSHMaster::startMaster()
|
||||||
if (isMasterRunning())
|
if (isMasterRunning())
|
||||||
return state->socketPath;
|
return state->socketPath;
|
||||||
|
|
||||||
state->sshMaster = startProcess([&]() {
|
state->sshMaster = Pid{startProcess([&]() {
|
||||||
restoreProcessContext();
|
restoreProcessContext();
|
||||||
|
|
||||||
close(out.readSide.get());
|
close(out.readSide.get());
|
||||||
|
@ -160,7 +160,7 @@ Path SSHMaster::startMaster()
|
||||||
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
||||||
|
|
||||||
throw SysError("unable to execute '%s'", args.front());
|
throw SysError("unable to execute '%s'", args.front());
|
||||||
}, options);
|
}, options)};
|
||||||
|
|
||||||
out.writeSide.reset();
|
out.writeSide.reset();
|
||||||
|
|
||||||
|
|
|
@ -94,12 +94,7 @@ bool userNamespacesSupported()
|
||||||
static auto res = [&]() -> bool
|
static auto res = [&]() -> bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Pid pid = startProcess([&]()
|
Pid pid{startProcess([&]() { _exit(0); }, {.cloneFlags = CLONE_NEWUSER})};
|
||||||
{
|
|
||||||
_exit(0);
|
|
||||||
}, {
|
|
||||||
.cloneFlags = CLONE_NEWUSER
|
|
||||||
});
|
|
||||||
|
|
||||||
auto r = pid.wait();
|
auto r = pid.wait();
|
||||||
assert(!r);
|
assert(!r);
|
||||||
|
@ -120,8 +115,7 @@ bool mountAndPidNamespacesSupported()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Pid pid = startProcess([&]()
|
Pid pid{startProcess([&]() {
|
||||||
{
|
|
||||||
/* Make sure we don't remount the parent's /proc. */
|
/* Make sure we don't remount the parent's /proc. */
|
||||||
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
@ -136,7 +130,7 @@ bool mountAndPidNamespacesSupported()
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}, {
|
}, {
|
||||||
.cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)
|
.cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)
|
||||||
});
|
})};
|
||||||
|
|
||||||
if (pid.wait()) {
|
if (pid.wait()) {
|
||||||
debug("PID namespaces do not work on this system: cannot remount /proc");
|
debug("PID namespaces do not work on this system: cannot remount /proc");
|
||||||
|
|
|
@ -35,9 +35,19 @@ Pid::Pid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pid::Pid(pid_t pid)
|
Pid::Pid(Pid && other) : pid(other.pid), separatePG(other.separatePG), killSignal(other.killSignal)
|
||||||
: pid(pid)
|
|
||||||
{
|
{
|
||||||
|
other.pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pid & Pid::operator=(Pid && other)
|
||||||
|
{
|
||||||
|
Pid tmp(std::move(other));
|
||||||
|
std::swap(pid, tmp.pid);
|
||||||
|
std::swap(separatePG, tmp.separatePG);
|
||||||
|
std::swap(killSignal, tmp.killSignal);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,14 +57,6 @@ Pid::~Pid() noexcept(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Pid::operator =(pid_t pid)
|
|
||||||
{
|
|
||||||
if (this->pid != -1 && this->pid != pid) kill();
|
|
||||||
this->pid = pid;
|
|
||||||
killSignal = SIGKILL; // reset signal to default
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Pid::kill()
|
int Pid::kill()
|
||||||
{
|
{
|
||||||
assert(pid != -1);
|
assert(pid != -1);
|
||||||
|
@ -125,7 +127,7 @@ void killUser(uid_t uid)
|
||||||
users to which the current process can send signals. So we
|
users to which the current process can send signals. So we
|
||||||
fork a process, switch to uid, and send a mass kill. */
|
fork a process, switch to uid, and send a mass kill. */
|
||||||
|
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid{startProcess([&]() {
|
||||||
|
|
||||||
if (setuid(uid) == -1)
|
if (setuid(uid) == -1)
|
||||||
throw SysError("setting uid");
|
throw SysError("setting uid");
|
||||||
|
@ -147,7 +149,7 @@ void killUser(uid_t uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
});
|
})};
|
||||||
|
|
||||||
int status = pid.wait();
|
int status = pid.wait();
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
@ -288,7 +290,7 @@ void runProgram2(const RunOptions & options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fork. */
|
/* Fork. */
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid{startProcess([&]() {
|
||||||
if (options.environment)
|
if (options.environment)
|
||||||
replaceEnv(*options.environment);
|
replaceEnv(*options.environment);
|
||||||
if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1)
|
||||||
|
@ -322,7 +324,7 @@ void runProgram2(const RunOptions & options)
|
||||||
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||||
|
|
||||||
throw SysError("executing '%1%'", options.program);
|
throw SysError("executing '%1%'", options.program);
|
||||||
}, processOptions);
|
}, processOptions)};
|
||||||
|
|
||||||
out.writeSide.close();
|
out.writeSide.close();
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,10 @@ class Pid
|
||||||
int killSignal = SIGKILL;
|
int killSignal = SIGKILL;
|
||||||
public:
|
public:
|
||||||
Pid();
|
Pid();
|
||||||
Pid(pid_t pid);
|
explicit Pid(pid_t pid): pid(pid) {}
|
||||||
|
Pid(Pid && other);
|
||||||
|
Pid & operator=(Pid && other);
|
||||||
~Pid() noexcept(false);
|
~Pid() noexcept(false);
|
||||||
void operator =(pid_t pid);
|
|
||||||
explicit operator bool() const { return pid != -1; }
|
explicit operator bool() const { return pid != -1; }
|
||||||
int kill();
|
int kill();
|
||||||
int wait();
|
int wait();
|
||||||
|
|
|
@ -65,7 +65,7 @@ static void bindConnectProcHelper(
|
||||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
Pipe pipe;
|
Pipe pipe;
|
||||||
pipe.create();
|
pipe.create();
|
||||||
Pid pid = startProcess([&] {
|
Pid pid{startProcess([&] {
|
||||||
try {
|
try {
|
||||||
pipe.readSide.close();
|
pipe.readSide.close();
|
||||||
Path dir = dirOf(path);
|
Path dir = dirOf(path);
|
||||||
|
@ -83,7 +83,7 @@ static void bindConnectProcHelper(
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
writeFull(pipe.writeSide.get(), "-1\n");
|
writeFull(pipe.writeSide.get(), "-1\n");
|
||||||
}
|
}
|
||||||
});
|
})};
|
||||||
pipe.writeSide.close();
|
pipe.writeSide.close();
|
||||||
auto errNo = string2Int<int>(chomp(drainFD(pipe.readSide.get())));
|
auto errNo = string2Int<int>(chomp(drainFD(pipe.readSide.get())));
|
||||||
if (!errNo || *errNo == -1)
|
if (!errNo || *errNo == -1)
|
||||||
|
|
Loading…
Reference in a new issue