|
21 | 21 | #include <string.h> |
22 | 22 | #include <unistd.h> |
23 | 23 |
|
| 24 | +#include <chrono> |
24 | 25 | #include <memory> |
25 | 26 | #include <string> |
26 | 27 | #include <utility> |
@@ -48,6 +49,10 @@ namespace { |
48 | 49 | bool SetupChild(const std::map<string, string>& env, uint32_t flags) { |
49 | 50 | // Setup the environment variables. |
50 | 51 | clearenv(); |
| 52 | + if (setpgid(0, 0) != 0) { |
| 53 | + PLOG(ERROR) << "Failed to setpgid on subprocess " << getpid(); |
| 54 | + return false; |
| 55 | + } |
51 | 56 | for (const auto& key_value : env) { |
52 | 57 | setenv(key_value.first.c_str(), key_value.second.c_str(), 0); |
53 | 58 | } |
@@ -147,9 +152,11 @@ void Subprocess::ChildExitedCallback(const siginfo_t& info) { |
147 | 152 |
|
148 | 153 | // Don't print any log if the subprocess exited with exit code 0. |
149 | 154 | if (info.si_code != CLD_EXITED) { |
150 | | - LOG(INFO) << "Subprocess terminated with si_code " << info.si_code; |
| 155 | + LOG(INFO) << "Subprocess " << info.si_pid << " terminated with si_code " |
| 156 | + << info.si_code; |
151 | 157 | } else if (info.si_status != 0) { |
152 | | - LOG(INFO) << "Subprocess exited with si_status: " << info.si_status; |
| 158 | + LOG(INFO) << "Subprocess " << info.si_pid |
| 159 | + << " exited with si_status: " << info.si_status; |
153 | 160 | } |
154 | 161 |
|
155 | 162 | if (!record->stdout_str.empty()) { |
@@ -206,18 +213,42 @@ pid_t Subprocess::ExecFlags(const vector<string>& cmd, |
206 | 213 | return pid; |
207 | 214 | } |
208 | 215 |
|
| 216 | +bool WaitForProcessGroup(pid_t pid, std::chrono::milliseconds timeout) { |
| 217 | + using std::chrono::system_clock; |
| 218 | + auto start = system_clock::now(); |
| 219 | + do { |
| 220 | + pid_t w = waitpid(-pid, nullptr, WNOHANG); |
| 221 | + if (w < 0) { |
| 222 | + // When all of the child process with this process group ID exits, waitpid |
| 223 | + // will return ECHILD. Until that point, keep callilng waitpid() as there |
| 224 | + // might be multiple child processes with the same process group id. |
| 225 | + if (errno == ECHILD) { |
| 226 | + LOG(INFO) << "All processes with process group id " << pid << " exited"; |
| 227 | + return true; |
| 228 | + } |
| 229 | + PLOG(ERROR) << "Waitpid returned " << w; |
| 230 | + return false; |
| 231 | + } |
| 232 | + usleep(100); |
| 233 | + } while ((system_clock::now() - start) <= timeout); |
| 234 | + LOG(INFO) << "process group " << pid << " did not exit in " << timeout.count() |
| 235 | + << " milliseconds"; |
| 236 | + return false; |
| 237 | +} |
| 238 | + |
209 | 239 | void Subprocess::KillExec(pid_t pid) { |
| 240 | + using namespace std::chrono_literals; |
210 | 241 | auto pid_record = subprocess_records_.find(pid); |
211 | 242 | if (pid_record == subprocess_records_.end()) |
212 | 243 | return; |
213 | 244 | pid_record->second->callback.Reset(); |
214 | 245 | // We don't care about output/return code, so we use SIGKILL here to ensure it |
215 | 246 | // will be killed, SIGTERM might lead to leaked subprocess. |
216 | 247 | CHECK_EQ(pid_record->second->proc.pid(), pid); |
217 | | - if (!pid_record->second->proc.Kill(SIGKILL, 5)) { |
218 | | - PLOG(WARNING) << "Error sending SIGKILL to " |
219 | | - << pid_record->second->proc.pid(); |
| 248 | + if (kill(-pid, SIGKILL) != 0) { |
| 249 | + PLOG(WARNING) << "Failed to kill subprocess group " << pid; |
220 | 250 | } |
| 251 | + WaitForProcessGroup(pid, 5000ms); |
221 | 252 | // Release the pid now so we don't try to kill it if Subprocess is destroyed |
222 | 253 | // before the corresponding ChildExitedCallback() is called. |
223 | 254 | pid_record->second->proc.Release(); |
|
0 commit comments