On Wed, 21 Mar 2001, Anil Madhavapeddy wrote:

> This has come up a few times, but is there any chance of having a
> bi-directional popen() that doesn't depend on the underlying system
> call supporting it?

Hm.. it would be possible to make one that uses socketpair() instead of
pipe() to do it's work.

Something to the effect of:

/* Real global */
int return_code = 0;
pid_t last_pid = 0; /* This means it can only have one popen() going at
                        once.. */

void php_sigchld_handler(int sig)
{
        int status;
        pid_t pid;
        pid = wait4(-1, &status, WNOHANG, NULL);
        if (pid > 0)
        {
                if (WIFEXITED(status))
                {
                        return_code = WEXITSTATUS(status);
                }
        }
        return;
}

int php_pclose(FILE *pipe)
{
        int fd = _fileno(pipe);
        int ret;
        int status;

        fclose(pipe);
        ret = wait4(last_pid, &status, WNOHANG, NULL);
        if (ret == 0)
        {
                kill(last_pid, SIGTERM);
                if (ret == 0)
                        return return_code;
                else
                        return WEXITSTATUS(status);
        }
        else
        {
                return WEXITSTATUS(status);
        }
}

FILE* php_popen(char *cmd, char *mode)
{
        int fds[2];
        FILE *ret;
        pid_t pid;
        int i;

        if ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '\0')
        {
                return popen(cmd, mode);
        }
        else
        {
                if (socketpair(AF_INET, SOCK_STREAM, 0, fds[2]) == 0)
                {
                        pid = fork();
                        if (pid > 0)
                        {
                                /* Parent process */
                                /* Catch SIGCHLD to tell us when it's exited. */
                                (void) signal(SIGCHLD, php_sigchld_handler); 
                                /* Create a FILE* from the file descriptor */
                                ret = fdopen(fds[0], "w+");
                                if (ret == NULL)
                                {
                                        /* Oh -- can't fdopen()? */
                                        (void) kill(ret, SIGKILL);
                                }
                                /* And we're done */
                                return ret;
                        }
                        else if (pid == 0)
                        {
                                /* Child process */
                                for (i = 0; i < 1024; i++) {
                                        if (i != fds[1])
                                        {
                                                (void) close(i);
                                        }
                                }

                                /* Create stdin/stdout/stderr */
                                dup2(fds[1], 0);
                                dup2(fds[1], 1);
                                dup2(fds[1], 2);

                                execl("/bin/sh", "-c", cmd, (char *) 0);
                                exit(127);
                        }
                        else
                        {
                                return NULL;
                        }
                }
        }
}

PS: This is just a quick hack. I think it might work as is, but it's not
something I would try using as-is without checking it out first.

Chris



-- 
PHP Development Mailing List <http://www.php.net/>
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]

Reply via email to