Skip to content

exec: Create wrapper to capture stderr output separately from stdout#444

Merged
detsch merged 3 commits intomasterfrom
detsch-popen-with-stderr
Feb 5, 2026
Merged

exec: Create wrapper to capture stderr output separately from stdout#444
detsch merged 3 commits intomasterfrom
detsch-popen-with-stderr

Conversation

@detsch
Copy link
Member

@detsch detsch commented Jan 22, 2026

Current implementation of the exec function merges stdout and stderr, which may lead to problems when the output is meant to be parsed, but some error message is printed as well.

Properly separate both outputs from the subprocess, allowing stderr to be ignored if the execution is successful, or logged in case of errors.

@detsch detsch marked this pull request as ready for review January 23, 2026 13:58
@detsch detsch requested a review from mike-sul January 23, 2026 13:58
src/exec.cc Outdated
stderr_pipe[1] = -1;

// Read stdout and stderr
result.stdout_output = readFromPipe(stdout_pipe[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can have potential deadlock here if a child process fills up the stderr pipe and blocks while the parent is blocked at reading stdout. I suppose we need to use select or something else to avoid this potential deadlock.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can. Something like this is really hard to get right. There's going to be some sig handler or fcntl on a pipe or something we'll miss and will only happen once in a thousand times.

If we can't use an existing API from boost, I'd seriously think about doing something low tech where you use the spawn function but send stderr and stdout to different files.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty sure we can avoid deadlock by using non-blocking select/epoll call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can. then you get into learning the subtleties of handling calls to those functions correctly. Its just hard to know if we have this foolproof before a release.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the code to use select to check both pipes at the same time.

I can give Boost Process V2 API a try, if that sounds better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. Its just hard to know if we have this foolproof before a release.

That's true for any software, regardless whether we use low-level system/OS calls or higher-level libraries like boost (especially boost :)). Only decent testing can help here.

@detsch detsch force-pushed the detsch-popen-with-stderr branch from 21cf92a to b945afc Compare January 26, 2026 14:31
@mike-sul
Copy link
Contributor

@detsch I've tested on qemu and it works fine for me so far. I suggest to test it on a few arm devices in our lab if it is feasible.

src/exec.cc Outdated
};

// Spawn the process
int spawn_result = posix_spawn(&pid, "/bin/sh", &file_actions, nullptr, argv, environ);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to run the shell here? It is seems like huge overhead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

popen initiates a shell as well internally.

Without it, we need to complicate the code a bit, splitting the args in a vector, adding support for concatenating an env variable to the enviroment, and setting the execution dir explicitly.

But it it doable. Should I do it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for "splitting the args in a vector" I don't think we really need to do it since each exec() client joins (through boost format) the arguments into a string. So, the clients can pass those arguments as an array.

Having said that I don't know whether it is worth doing.

src/exec.cc Outdated
if (stderr_open) FD_SET(stderr_pipe[0], &read_fds);

// Wait for data on either pipe (with timeout)
struct timeval timeout;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the "timeout"? I think we should use either the system level timeout as we do now (i.e. timeout && ), or use the timeout in select instead of the system timeout. If so, then we should handle the timeout after select.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually prefer to call select with timeout, but in that case it is not really required. I'm removing it.

@detsch detsch force-pushed the detsch-popen-with-stderr branch 2 times, most recently from 765adfb to 52dfd34 Compare January 27, 2026 17:53
@detsch
Copy link
Member Author

detsch commented Jan 27, 2026

Fixed print_output = true, which was not implemented.

@detsch detsch force-pushed the detsch-popen-with-stderr branch from 52dfd34 to c820d36 Compare February 3, 2026 13:53
Current implementation of the exec function merges stdout and stderr,
which may lead to problems when the output is meant to be parsed, but
some error message is printed as well.

Properly separate both outputs from the subprocess, allowing stderr
to be ignored if the execution is successful, or logged in case of
errors.

Signed-off-by: Andre Detsch <andre.detsch@foundries.io>
Control execution timeout internally, leveraging select timeout.

Signed-off-by: Andre Detsch <andre.detsch@foundries.io>
New exec implementation returns a different error when the command is
not found.

Signed-off-by: Andre Detsch <andre.detsch@foundries.io>
@detsch detsch force-pushed the detsch-popen-with-stderr branch from efa21c2 to 274a18e Compare February 3, 2026 14:18
@detsch
Copy link
Member Author

detsch commented Feb 3, 2026

@mike-sul I've added 2 more commits. Can you take a look again?

@detsch detsch merged commit 770b9cc into master Feb 5, 2026
5 checks passed
@detsch detsch deleted the detsch-popen-with-stderr branch February 5, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants