problems with the std pipes

Hi,

I wrote a program with C++ which telecommands another console application. It simulates the user by redirecting the standard pipes for input (keyboard) and output(console).
The program works fine with OpenSUSE11.1, but after the update to 11.3 my program hangs. A read call to to the standard output pipe, which delivers the text the other programm want to write to the console, blocks and doesn’t return. The other program writes text in the output and flushs the buffer. So I have no idea what is going on. It worked well with the older SUSE version. Any hints, clues? What’s the problem? Perhaps it’s a kind of configuration problem? Help would be much appreciated. Thanks.

Best regards

Pellaeon

It’s a bit hard to guess on that little information.

The usual cause of hangs with pipes, is that there is an additional unclosed file descriptor somewhere which potentially could receive data. I suggest that you use “lsof” on the running (hanging) process to see what file descriptors you have.

This is the code to create the pipes, And as I wrote: it worked fine with SUSE 11.1. So I don’t think it’s an programming issue.

bool CFEMRemoteControl::initialize(const CFemInitData& data)
{
if (m_run)
{
return false;
}
// init
m_run = true;
m_initData = data;
pipe(m_parentToChild);
pipe(m_childToParent);
pipe(m_childToParentError);
// change to non blocking
if (!setNonblocking(m_childToParent[0]))
{
std::cout << “Couldn’t set non blocking i/o mode” << std::endl;
}

// clone process
m_childID = fork();
// 1. error while cloning
if (m_childID < 0)
{
return false;
}
else if (m_childID == 0) // child process
{
// std::cout << “child process running” << std::endl;
// change stdin
dup2(m_parentToChild[0], 0); // read == standard input
close(m_parentToChild[1]); // no write
// change stdout
dup2(m_childToParent[1], 1); // write == standard output
close(m_childToParent[0]); // no read
//
dup2(m_childToParentError[1], 2); // write = standard error
close(m_childToParentError[0]); // no read
int ret = chdir(m_initData.m_appPath.c_str());
if (ret == -1)
{
// std::cout << "Couldn’t change working directory to " << workingPath << “.” << std::endl;
exit(-1);
}
std::string args = “”;
ret = execl(m_initData.m_appName.c_str(), args.c_str(), (char*)0);
if (ret == -1)
{
// std::cout << “Couldn’t start the programm.” << std::endl;
exit(-1);
}
}
// parent process
// dup2(m_childToParent[0], 0);
// close(childToParent[1]);
// dup2(m_parentToChild[1], 1);
// close(parentToChild[0]);
return true;
}

I always advise my students to count file descriptors (or fds).

For example, consider your pipe m_parentToChild . When you create the pipe, it has two fds. Now look at it in the child process. After your dup2(), it has 3 fd. You close one of those. That leaves two fds open. You should have only one fd open, else you are likely to have a problem with hangs. So you need another close. I suggest similar counting for the other pipes, and in both parent and child processes.

Leaving an unused open fd makes the behavior dependent on event timings. And that is probably why you happen to see a change between 11.1 and 11.3

I hope that helps.

Hi nrickert,

first, thanks for your help!
I changed the code to the following:

// child process
dup2(m_parentToChild[0], 0); // read == standard input
dup2(m_childToParent[1], 1); // write == standard output
dup2(m_childToParentError[1], 2); // write == standard error

close(m_parentToChild[0]);
close(m_parentToChild[1]);
close(m_childToParent[0]);
close(m_childToParent[1]);
close(m_childToParentError[0]);
close(m_childToParentError[1]);

// parent process
close(m_parentToChild[0]);
close(m_childToParent[1]);
close(m_childToParentError[1]);

But this doesn’t solve the problem. The read function hangs at some point int the program anyway.

Best regards

Those changes should have taken care of pipe problems.

I assume that the execl() is working, and the other program is running in the child process.

I would guess that the change from 11.1 to 11.3 is in whatever program your lexec() uses. If that tries to read its stdin before writing any output, and if your parent process tries reading its output before writing to that process, you would get a hang. Whenever parent and child are not in sync with there read/write expectations, you can get pipe hangs.

I’m wondering whether you really need that stderr pipe. In the code you provided, you set the stdout pipe to non-blocking, but you do not set the stderr pipe to non-blocking. That leads me to wonder whether your program is monitoring what is written to stderr.

If the target program (the one to which you execl()) writes more than a buffer to stderr, then unless you are reading stderr, that will cause it to hang waiting for more space in the pipe.

If you are using a separate pipe, you need to keep it empty. Possible alternatives would be to have stderr output actually go to stdout (that would be a dup2() on the stdout pipe), or have stderr go to “/dev/null”, or skip doing anything and have it go to the terminal or screen, at least during testing.