Untitled

 avatar
unknown
c_cpp
a year ago
5.0 kB
6
Indexable
class PipeExec {
public:
    enum Mode {
        None   = 0,
        Stdout = 1 << 0,
        Stderr = 1 << 1,
        Stdin  = 1 << 2,
        RedirectErrToOut = 1 << 3,
    };

    typedef std::function<void()> hook_t;

public:
    ~PipeExec() {
        // Ensure the process is no longer running before cleanup
        if (m_pid > 0) {
            wait();
        }
        closePipes();
    }

    bool exec(const char *argv[], int mode) {
        assert(m_stdout_pipe[0] == -1 && m_stdout_pipe[1] == -1);
        assert(m_stderr_pipe[0] == -1 && m_stderr_pipe[1] == -1);
        assert(m_stdin_pipe[0] == -1 && m_stdin_pipe[1] == -1);

        if (mode & Mode::Stdout && pipe(m_stdout_pipe) == -1) {
            perror("pipe");
            return false;
        }

        if (mode & Mode::Stderr && !(mode & RedirectErrToOut) && pipe(m_stderr_pipe) == -1) {
            perror("pipe");
            return false;
        }

        if (mode & Mode::Stdin && pipe(m_stdin_pipe) == -1) {
            perror("pipe");
            return false;
        }

        if ((m_pid = fork()) == -1) {
            perror("fork");
            return false;
        }

        if (m_pid == 0) {
            setupChildPipes(mode);

            if (m_child_hook) {
                m_child_hook();
            }

            execvpe(argv[0], const_cast<char* const*>(argv), m_envp ? m_envp : environ);
            perror("execvpe");
            _exit(127);

        } else {
            closeParentPipes();
        }

        return true;
    }

    int wait() {
        if (m_pid < 1)
            return -1;

        int ret;
        do {
            ret = waitpid(m_pid, &m_status, 0);
        } while (ret == -1 && errno == EINTR);

        if (ret == -1) {
            perror("waitpid failed");
            m_status = -1;
        }

        m_pid = -1;
        closePipes();
        return m_status;
    }

    int exitcode() const {
        return WIFEXITED(m_status) ? WEXITSTATUS(m_status) : 255;
    }

    int stdin_pipe() const {
        return m_stdin_pipe[1];
    }

    int stdout_pipe() const {
        return m_stdout_pipe[0];
    }

    int stderr_pipe() const {
        return m_stderr_pipe[0];
    }

    void close_pipes(int mode) {
        if (mode & Mode::Stdin) {
            closePipe(m_stdin_pipe);
        }

        if (mode & Mode::Stdout || mode & RedirectErrToOut) {
            closePipe(m_stdin_pipe);
        }

        if (mode & Mode::Stderr) {
            closePipe(m_stdin_pipe);
        }
    }

    void set_environ(const char *envp[]) {
        m_envp = const_cast<char* const*>(envp);
    }

    void set_child_hook(const hook_t &cb) {
        m_child_hook = cb;
    }

private:
    void setupChildPipes(int mode) {
        if (mode & Mode::Stdout) {
            close(m_stdout_pipe[0]);
            dup2(m_stdout_pipe[1], STDOUT_FILENO);
            close(m_stdout_pipe[1]);
        }

        if (mode & Mode::RedirectErrToOut) {
            dup2(STDOUT_FILENO, STDERR_FILENO);

        } else if (mode & Mode::Stderr) {
            close(m_stderr_pipe[0]);
            dup2(m_stderr_pipe[1], STDERR_FILENO);
            close(m_stderr_pipe[1]);
        }

        if (mode & Mode::Stdin) {
            close(m_stdin_pipe[1]);
            dup2(m_stdin_pipe[0], STDIN_FILENO);
            close(m_stdin_pipe[0]);
        }
    }

    void closeParentPipes() {
        close(m_stdin_pipe[0]);
        close(m_stderr_pipe[1]);
        close(m_stdout_pipe[1]);
    }

    void closePipes() {
        closePipe(m_stdin_pipe);
        closePipe(m_stdout_pipe);
        closePipe(m_stderr_pipe);
    }

    void closePipe(int (&pipe)[2]) {
        if (pipe[0] != -1) close(pipe[0]);
        if (pipe[1] != -1) close(pipe[1]);
        pipe[0] = -1;
        pipe[1] = -1;
    }

private:
    pid_t m_pid = -1;
    int m_status = -1;
    int m_stdin_pipe[2] = {-1, -1};
    int m_stdout_pipe[2] = {-1, -1};
    int m_stderr_pipe[2] = {-1, -1};
    char * const *m_envp = nullptr;
    hook_t m_child_hook;
};

int main()
{
    const char *argv[] = {
        "/bin/cat",
        "asd",
        NULL
    };

    const char *env[] = {
        "Sooqua=1",
        NULL
    };

    PipeExec pexec;
    //pexec.set_environ(env);

    pexec.set_child_hook([]() {
        printf("I'm in child process!\n");
    });

    pexec.exec(argv, PipeExec::Mode::Stdin | PipeExec::Mode::RedirectErrToOut);

    dprintf(pexec.stdin_pipe(), "Hello!\n");
    pexec.close_pipes(PipeExec::Mode::Stdin);

    write(1, "Out: \n", 6);
    while (1) {
        char tmp[128];
        int ret;
        if ((ret = read(pexec.stdout_pipe(), tmp, 128)) < 1)
            break;

        tmp[ret] = 0;
        write(1, tmp, ret);
    }

    write(2, "Err: \n", 6);
    while (1) {
        char tmp[128];
        int ret;
        if ((ret = read(pexec.stderr_pipe(), tmp, 128)) < 1)
            break;

        tmp[ret] = 0;
        write(2, tmp, ret);
    }

    pexec.wait();

    printf("retcode: %d\n", pexec.exitcode());
    return 0;
}
Editor is loading...
Leave a Comment