I work on custom NPAPI plugin for Mozilla/Safari which has to communicate (exchange messages) with an external native process on Mac.
I use FireBreath framework for plugin development. The external process read from and write to stdin/stdout.
So, FireBreath process (on Mozilla this is plugin container) is parent process and the external process is child. During plugin initialization pipes are created and child process is run using execv command - SetUpPipesAndRunChild procedure. Four separate threads monitor parent's and child's stdin/stdout and process messages.
On first run everything works fine, but if plugin is refreshed shutdown procedure will call TearDownPipes procedure and immediate after SetUpPipesAndRunChild procedure is called again, the parent process keep the same PID, new child process is run (with new PID).
On every refresh parent's stdin fails.
If I turn off the browser and run the plugin from the beginning there is no problem but on refresh fails.
Please take a look at the snippet bellow. Any suggestion would be very helpful.
pid_t childPID = -1;
FILE* pWriteFile = NULL;
int writepipe[2] = {-1,-1}, /* parent -> child */
readpipe [2] = {-1,-1}; /* child -> parent */
int stdinFD_copy, stdoutFD_copy;
void SetUpPipesAndRunChild()
{
// Read Write pipes
writepipe[0] = -1;
writepipe[1] = -1;
readpipe [0] = -1;
readpipe [1] = -1;
stdinFD_copy = -1;
stdoutFD_copy = -1;
stdinFD_copy = dup(fileno(stdin));
stdoutFD_copy = dup(fileno(stdout));
if (stdinFD_copy == -1) <log message>
if (stdoutFD_copy == -1) <log message>
// Command to execute
char *Env_argv[] = { (char *)"<path to the native executable>",(char *) 0 };
if ( pipe(readpipe) < 0) <log message>
if (pipe(writepipe) < 0 ) <log message>
childPID = fork();
if (childPID == 0) // child
{
if (close (stdinFD_copy) == -1) <log message>
if (close(stdoutFD_copy) == -1) <log message>
if (close(writepipe[1]) == -1) <log message>
if (close(readpipe[0]) == -1) <log message>
if (writepipe[0] != fileno(stdin))
{
if (dup2(writepipe[0], fileno(stdin)) == -1) <log message>
if (close(writepipe[0]) == -1) <log message>
}
if (readpipe[1] != fileno(stdout))
{
if (dup2(readpipe[1], fileno(stdout)) == -1) <log message>
if (close(readpipe[1]) == -1) <log message>
}
errno = 0;
int execReturn = execv(Env_argv[0], Env_argv);
if(execReturn == -1) <log message>
_exit(0); // If exec fails then exit forked process.
}
else if (childPID < 0) <log message> // failed to fork
else // parent
{
if (close(writepipe[0]) == -1) <log message>
if (close(readpipe[1]) == -1) <log message>
pWriteFile = fdopen(writepipe[1],"w");
if (pWriteFile == NULL) <log message>
// connect the source end of the readpipe to the parent process fileno(stdin)
if (readpipe[0] != fileno(stdin))
{
if (dup2(readpipe[0], fileno(stdin)) == -1) <log message>
if (close(readpipe[0]) == -1) <log message>
}
stdouThread->setFileWritter(pWriteFile) // this thread write to file, so that pWriteFile is opened on the sink end of the writepipe[1].
stdouThread->start();
stdinThread->start(); // stdinThread read std::getline(std::cin, stringVariable)
}
}
void TearDownPipes
{
if (childPID > 0)
{
stdoutThread->stop();
fclose(pWriteFile);
stdinThread->waitSignal(); //wait here until stdinThread signal it has stopped
childPID = -1;
delete pWriteFile;
if (dup2(stdinFD_copy, fileno(stdin)) == -1) <log message>
if (dup2(stdoutFD_copy, fileno(stdout)) == -1) <log message>
if (close (stdinFD_copy) == -1) <log message>
if (close(stdoutFD_copy) == -1) <log message>
}
}
Tear down steps are:
1. paren's stdoutThread->stop
2. pWriteFile is closed and the file descriptor writepipe[1] is closed
3. child's stdinThread detects broken pipe (cin::fail()) and stop signal is set.
4. childs's stdoutThread->stop
5. parents's stdinThread detects broken pipe (cin::fail()) and stop signal is set.
On every SetUpPipesAndRunChild call, file descriptors get the same values
stdinFD_copy = 9
stdoutFD_copy = 10
writepipe[0] = 11
writepipe[1] = 12
readpipe [0] = 13
readpipe [1] = 14