@@ -213,182 +213,115 @@ class ProcessFD {
213
213
new_env_ptrs.push_back (static_cast <char *>(NULL ));
214
214
char **envp = &new_env_ptrs[0 ];
215
215
216
- // TODO(mehrdadn): Use clone() on Linux or posix_spawnp() on Mac to avoid duplicating
217
- // file descriptors into the child process, as that can be problematic.
218
216
intptr_t fd = -1 ;
219
- int pipefds[2 ]; // Create pipe to get PID & track lifetime
220
- int parent_lifetime_pipe[2 ];
221
-
222
- // Create pipes to health check parent <> child.
223
- // pipefds is used for parent to check child's health.
224
- if (pipe (pipefds) == -1 ) {
217
+ // Pipe for getting startup status (PID and potential errno) from the child.
218
+ int status_pipe[2 ];
219
+ if (pipe (status_pipe) == -1 ) {
225
220
ec = std::error_code (errno, std::system_category ());
226
221
return ProcessFD (-1 , -1 );
227
222
}
228
223
224
+ // Pipe for parent lifetime tracking, connected to child's stdin.
225
+ int parent_lifetime_pipe[2 ] = {-1 , -1 };
229
226
if (pipe_to_stdin) {
230
227
if (pipe (parent_lifetime_pipe) == -1 ) {
231
- parent_lifetime_pipe[0 ] = parent_lifetime_pipe[1 ] = -1 ;
228
+ close (status_pipe[0 ]);
229
+ close (status_pipe[1 ]);
230
+ ec = std::error_code (errno, std::system_category ());
231
+ return ProcessFD (-1 , -1 );
232
232
}
233
233
}
234
234
235
- // Set the write-end of the pipe to close on exec. This is crucial for the parent
236
- // to detect a successful execve, as the pipe will close automatically.
237
- SetFdCloseOnExec (pipefds[1 ]);
238
-
239
235
pid = fork ();
240
236
241
237
if (pid == 0 ) {
242
238
// Child process (or intermediate process if decoupled).
243
- close (pipefds[0 ]); // Child only writes.
239
+ close (status_pipe[0 ]); // Child only writes to the status pipe.
240
+ if (pipe_to_stdin) {
241
+ close (parent_lifetime_pipe[1 ]); // Child only reads from the lifetime pipe.
242
+ }
244
243
245
- // Reset the SIGCHLD handler for the child.
246
244
signal (SIGCHLD, SIG_DFL);
247
245
248
246
if (decouple) {
249
- // Double-fork to create an orphaned grandchild.
250
247
if (fork () != 0 ) {
251
- // Intermediate parent exits, grandchild is orphaned and adopted by init .
248
+ // Intermediate parent exits. Grandchild is orphaned.
252
249
_exit (0 );
253
250
}
254
- // The grandchild continues from here.
255
251
}
256
252
257
- // This code is now executed by the direct child (if !decouple) or
258
- // the grandchild ( if decouple).
259
-
260
- // Redirect the read pipe to stdin so that child can track the
261
- // parent lifetime.
262
- if (pipe_to_stdin && parent_lifetime_pipe[ 0 ] != - 1 ) {
263
- dup2 (parent_lifetime_pipe[0 ], STDIN_FILENO );
253
+ // Grandchild (if decoupled) or direct child (if not) continues here.
254
+ if (pipe_to_stdin) {
255
+ if ( dup2 (parent_lifetime_pipe[ 0 ], STDIN_FILENO) == - 1 ) {
256
+ // If dup2 fails, we can't health check, so exit.
257
+ _exit (errno);
258
+ }
259
+ close (parent_lifetime_pipe[0 ]);
264
260
}
265
261
266
- // For decoupled processes, send the real PID back to the parent.
262
+ // Set FD_CLOEXEC on the status pipe's write end. If execve succeeds,
263
+ // this FD will be closed, and the parent will read EOF.
264
+ SetFdCloseOnExec (status_pipe[1 ]);
265
+
267
266
if (decouple) {
268
267
pid_t my_pid = getpid ();
269
- if (write (pipefds [1 ], &my_pid, sizeof (my_pid)) != sizeof (my_pid)) {
270
- _exit (errno); // Exit if we can't even write the PID.
268
+ if (write (status_pipe [1 ], &my_pid, sizeof (my_pid)) != sizeof (my_pid)) {
269
+ _exit (errno);
271
270
}
272
271
}
273
272
274
273
execvpe (argv[0 ], const_cast <char *const *>(argv), const_cast <char *const *>(envp));
275
274
276
- // If execvpe returns, an error occurred.
277
- if (!decouple) {
278
- // For the simple case, we can report the error via the pipe.
279
- int err = errno;
280
- (void )!write (pipefds[1 ], &err, sizeof (err));
281
- } else {
282
- // For the decoupled case, send the errno after the PID.
283
- int err = errno;
284
- (void )!write (pipefds[1 ], &err, sizeof (err));
285
- }
286
- // For both cases, exit with a non-zero status. For the decoupled case,
287
- // this will cause the lifetime-tracking pipe to close, signaling failure.
288
- _exit (errno);
275
+ // If execvpe returns, an error occurred. Write errno to the pipe.
276
+ int err = errno;
277
+ (void )!write (status_pipe[1 ], &err, sizeof (err));
278
+ _exit (err);
289
279
290
280
} else if (pid > 0 ) {
291
281
// Parent process.
292
- close (pipefds[1 ]); // Parent only reads.
282
+ close (status_pipe[1 ]); // Parent only reads from the status pipe.
283
+ if (pipe_to_stdin) {
284
+ close (parent_lifetime_pipe[0 ]); // Parent only writes to the lifetime pipe.
285
+ }
293
286
294
287
if (!decouple) {
295
- // Simple case: read execvpe status from the direct child.
296
288
int err_from_child;
297
289
ssize_t bytes_read =
298
- ReadBytesFromFd (pipefds[0 ], &err_from_child, sizeof (err_from_child));
299
-
290
+ ReadBytesFromFd (status_pipe[0 ], &err_from_child, sizeof (err_from_child));
300
291
if (bytes_read == 0 ) {
301
- // Success: child exec'd, pipe was closed by CLOEXEC.
292
+ // Success: exec'd, pipe closed by CLOEXEC.
302
293
ec = std::error_code ();
303
294
} else if (bytes_read == sizeof (err_from_child)) {
304
- // Failure: child sent a complete errno.
305
295
ec = std::error_code (err_from_child, std::system_category ());
306
- waitpid (pid, NULL , 0 ); // Reap the zombie child.
296
+ waitpid (pid, NULL , 0 );
307
297
pid = -1 ;
308
298
} else {
309
- // Pipe read error or unexpected data (e.g. partial read).
310
- // If read failed, use its errno; otherwise, it's a broken pipe.
311
299
ec = std::error_code (bytes_read < 0 ? errno : EPIPE, std::system_category ());
312
300
waitpid (pid, NULL , 0 );
313
301
pid = -1 ;
314
302
}
315
- close (pipefds[0 ]); // We're done with this short-lived pipe.
316
- fd = -1 ; // No persistent fd needed.
317
-
318
- } else {
319
- // Decoupled case: Restore original logic to get grandchild PID.
320
- int s;
321
- waitpid (pid, &s, 0 ); // Wait for the intermediate process to exit.
322
-
323
- // Read the grandchild's PID from the pipe.
324
- ssize_t bytes_read_pid = ReadBytesFromFd (pipefds[0 ], &pid, sizeof (pid));
325
- if (bytes_read_pid == sizeof (pid)) {
326
- // Successfully got PID. Now do a non-blocking read for a potential error.
327
- int flags = fcntl (pipefds[0 ], F_GETFL, 0 );
328
- fcntl (pipefds[0 ], F_SETFL, flags | O_NONBLOCK);
329
- int exec_errno = 0 ;
330
- ssize_t bytes_read_errno = read (pipefds[0 ], &exec_errno, sizeof (exec_errno));
331
- // Restore original flags.
332
- fcntl (pipefds[0 ], F_SETFL, flags);
333
-
334
- if (bytes_read_errno == sizeof (exec_errno)) {
335
- // We received an errno from the grandchild, meaning execvpe failed.
336
- ec = std::error_code (exec_errno, std::system_category ());
337
- pid = -1 ;
338
- close (pipefds[0 ]);
339
- } else if (bytes_read_errno == -1 && errno == EAGAIN) {
340
- // No data available, which means execvpe succeeded.
341
- // 'fd' will be the pipe for lifetime tracking.
342
- fd = pipefds[0 ];
343
- } else {
344
- // Grandchild died after sending PID but before we could check for error.
345
- // This is a startup failure.
346
- ec = std::error_code (ECHILD, std::system_category ());
347
- pid = -1 ;
348
- close (pipefds[0 ]);
349
- }
350
- } else if (bytes_read_pid == -1 ) {
351
- // read failed, use errno for specific error.
352
- ec = std::error_code (errno, std::system_category ());
353
- pid = -1 ;
354
- close (pipefds[0 ]);
355
- } else { // bytes_read_pid == 0 or partial read
356
- // If bytes_read_pid is 0, it means EOF (child exited before writing PID).
357
- // If bytes_read_pid is positive but less than sizeof(pid), it's a partial
358
- // read. In both cases, it's a failure to get the PID.
303
+ close (status_pipe[0 ]);
304
+ } else { // Decoupled
305
+ waitpid (pid, NULL , 0 ); // Reap the intermediate child.
306
+ ssize_t bytes_read = ReadBytesFromFd (status_pipe[0 ], &pid, sizeof (pid));
307
+ if (bytes_read != sizeof (pid)) {
308
+ // Failed to get grandchild's PID.
359
309
ec = std::error_code (ECHILD, std::system_category ());
360
310
pid = -1 ;
361
- close (pipefds[0 ]);
311
+ } else {
312
+ // Use the status pipe as the lifetime tracking fd.
313
+ fd = status_pipe[0 ];
362
314
}
363
315
}
364
-
365
316
} else {
366
317
// fork() failed.
367
318
ec = std::error_code (errno, std::system_category ());
368
- close (pipefds[0 ]);
369
- close (pipefds[1 ]);
370
- fd = -1 ;
371
- }
372
-
373
- // Handle the separate pipe for tracking parent lifetime if used.
374
- if (pipe_to_stdin) {
375
- if (pid <= 0 && parent_lifetime_pipe[1 ] != -1 ) {
376
- // Child. Close sthe write end of the pipe from child.
377
- close (parent_lifetime_pipe[1 ]);
378
- parent_lifetime_pipe[1 ] = -1 ;
379
- SetFdCloseOnExec (parent_lifetime_pipe[0 ]);
380
- }
381
- if (pid != 0 && parent_lifetime_pipe[0 ] != -1 ) {
382
- // Parent. Close the read end of the pipe.
319
+ close (status_pipe[0 ]);
320
+ close (status_pipe[1 ]);
321
+ if (pipe_to_stdin) {
383
322
close (parent_lifetime_pipe[0 ]);
384
- parent_lifetime_pipe[0 ] = -1 ;
385
- // Make sure the write end of the pipe is closed on exec.
386
- SetFdCloseOnExec (parent_lifetime_pipe[1 ]);
323
+ close (parent_lifetime_pipe[1 ]);
387
324
}
388
- } else {
389
- // parent_lifetime_pipe pipes are not used.
390
- parent_lifetime_pipe[0 ] = -1 ;
391
- parent_lifetime_pipe[1 ] = -1 ;
392
325
}
393
326
#endif
394
327
return ProcessFD (pid, fd);
0 commit comments