Description
I work for a big security vendor, and recently we tried to shift from libc interface (fork, exec, waitpid...) to Boost::Process.
However, we are missing some crucial API's related to the underline pipe.
One of the functionality we need is: Setting the pipe capacity.
If the pipe fills up, 2 things may happen (Depends if the pipe is nonblocking or blocking):
- The producer will be blocked. From pipe man page: "If a process attempts to write to a full pipe, then blocks until sufficient data has been read from the pipe to allow the write to complete"
- The new data will be thrown. From pipe man page: "If the pipe is full, then write(2) fails, with errno set to EAGAIN."
Both cases cause us to loose live data from the producer.
increasing the pipe capacity doesn't solve this issue 100%. But the possibility of this happening, diminishing by a lot.
This is crucial for us.
Running the following code for 5 seconds:
int main()
{
bp::ipstream stdOut;
bp::child childProcess = bp::child("sysdig", bp::std_out > stdOut);
auto fdStdOut = stdOut.pipe().native_source();
while (true)
{
int bytesInPipe;
ioctl(fdStdOut, FIONREAD, &bytesInPipe); // Find number of available bytes
std::string output(bytesInPipe + 1, 0);
std::cout << "\nPipe size: " << fcntl(fdStdOut, F_GETPIPE_SZ) << std::endl;
std::cout << "Bytes available: " << bytesInPipe << std::endl;
std::cout << "Bytes read: " << read(fdStdOut, output.data(), bytesInPipe) << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Some work
}
}
Here is some of the output:
Pipe size: 65536
Bytes available: 17685
Bytes read: 17685
Pipe size: 65536
Bytes available: 5577
Bytes read: 5577
Pipe size: 65536
Bytes available: 65536
Bytes read: 65536
Pipe size: 65536
Bytes available: 65421
Bytes read: 65421
Pipe size: 65536
Bytes available: 4564
Bytes read: 4564
The capacity is easily reached.
After changing the pipe capacity to the value specified at: /proc/sys/fs/pipe-max-size, I was able to 20X the pipe capacity.
New code
int main()
{
bp::ipstream stdOut;
bp::child childProcess = bp::child("sysdig", bp::std_out > stdOut);
auto fdStdOut = stdOut.pipe().native_source();
std::cout << "new pipe capacity: " << fcntl(fdStdOut, F_SETPIPE_SZ, 1048576) << std::endl; // Changing the capacity
while (true)
{
int bytesInPipe;
ioctl(fdStdOut, FIONREAD, &bytesInPipe); // Find number of available bytes
std::string output(bytesInPipe + 1, 0);
std::cout << "\nPipe size: " << fcntl(fdStdOut, F_GETPIPE_SZ) << std::endl;
std::cout << "Bytes available: " << bytesInPipe << std::endl;
std::cout << "Bytes read: " << read(fdStdOut, output.data(), bytesInPipe) << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Some work
}
}
New output:
new pipe capacity: 1048576
Pipe size: 1048576
Bytes available: 259710
Bytes read: 259710
I have created a Pull request that will add this functionality, without the the user accessing the file descriptor or calling fcntl()
PR: #324