[an error occurred while processing this directive]
[an error occurred while processing this directive][an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive] (none)
[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive][an error occurred while processing this directive]
[an error occurred while processing this directive][an error occurred while processing this directive]
[an error occurred while processing this directive][an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive] (none)
[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive][an error occurred while processing this directive]
Glemte at sætte pipe'sene i O_NONBLOCK mode (og teste på EAGAIN).
Nedenstående virker med simultan input/output/output på 10^6 bytes hver.
Hans-Christian Stadler
------------------------------
/* run_command - Execute shell command with given stdin input and
* collect stdout and stderr output of the command.
* Copyright (C) 2004 Hans-Christian Stadler
* Licence: GPL
*/
#define __USE_POSIX
#include <signal.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
int run_command (const char *command, /* command to be executed with sh -c */
const char *input_buffer, /* inout */ size_t *input_buf_len, /* stdin for command */
char *output_buffer, /* inout */ size_t *output_buf_len, /* stdout of command */
char *error_buffer, /* inout */ size_t *error_buf_len /* stderr of command */
)
/* precondition: all 3 buffer sizes should not exceed SSIZE_MAX
* postcondition: (ret_val = -1 AND (buffer sizes undefined but within buffer bounds) OR
* (ret_val = 0 AND buffer sizes coorespond to used sizes)
* temporary side effects: blocks SIGPIPE signal for a while
*
* The function will close the stdout and stderr of the command process on error, which should
* cause a SIGPIPE signal to be sent to the command process. On error, stdin of the command
* process will be closed, which will result in an EOF at the end of the command process.
*
*/
{
int pipe_stdin[2], pipe_stdout[2], pipe_stderr[2], is_member, ret_val=-1, nfds;
pid_t pid;
ssize_t n_bytes;
size_t input_off=0, output_off=0, error_off=0;
sigset_t sigmask, oldmask;
struct pollfd fds[3];
short event;
if (sigemptyset(&sigmask))
return -1; /* sigemptyset() failed, errno is set */
if (*input_buf_len && pipe(pipe_stdin))
goto pipe_err_stdin; /* pipe() failed, errno is set */
if (*output_buf_len && pipe(pipe_stdout))
goto pipe_err_stdout;
if (*error_buf_len && pipe(pipe_stderr))
goto pipe_err_stderr;
if ((pid = fork()) == 0) {
/* child process */
if (*input_buf_len) {
while(close(pipe_stdin[1]));
if (dup2(pipe_stdin[0], 0) == -1)
exit(-1);
while(close(pipe_stdin[0]));
}
if (*output_buf_len) {
while(close(pipe_stdout[0]));
if (dup2(pipe_stdout[1], 1) == -1)
exit(-1);
while(close(pipe_stdout[1]));
}
if (*error_buf_len) {
while(close(pipe_stderr[0]));
if(dup2(pipe_stderr[1], 2) == -1)
exit(-1);
while(close(pipe_stderr[1]));
}
ret_val = system(command);
exit(ret_val);
}
/* parent process */
if (pid == -1)
goto fork_err; /* fork() failed, errno is set */
if (*input_buf_len && close(pipe_stdin[0]))
goto close_err_stdin; /* close() failed, errno is set */
if (*output_buf_len && close(pipe_stdout[1]))
goto close_err_stdout;
if (*error_buf_len && close(pipe_stderr[1]))
goto close_err_stderr;
if (*input_buf_len && fcntl(pipe_stdin[1], F_SETFL, O_NONBLOCK))
goto cleanup; /* fcntl() failed, errno is set */
if (*output_buf_len && fcntl(pipe_stdout[0], F_SETFL, O_NONBLOCK))
goto cleanup;
if (*error_buf_len && fcntl(pipe_stderr[0], F_SETFL, O_NONBLOCK))
goto cleanup;
if (sigaddset(&sigmask, SIGPIPE))
goto cleanup; /* sigaddset() failed, errno is set */
if (sigprocmask(SIG_BLOCK, &sigmask, &oldmask)) {
while (sigemptyset(&sigmask)); /* clear set here or risk bogus signal mask */
goto cleanup; /* sigprocmask() failed, errno is set */
}
while (*input_buf_len || *output_buf_len || *error_buf_len) {
nfds = 0;
if (*input_buf_len)
fds[nfds++] = (struct pollfd){ pipe_stdin[1], POLLOUT, 0 };
if (*output_buf_len)
fds[nfds++] = (struct pollfd){ pipe_stdout[0], POLLIN, 0 };
if (*error_buf_len)
fds[nfds++] = (struct pollfd){ pipe_stderr[0], POLLIN, 0 };
if (poll(fds, nfds, -1) == -1)
goto cleanup; /* poll() failed, errno is set */
nfds = 0;
if (*input_buf_len) {
if ((event = fds[nfds++].revents) != 0) {
if (event & POLLOUT) {
n_bytes = write(pipe_stdin[1], &input_buffer[input_off], *input_buf_len);
if (n_bytes == -1) {
if (errno == EPIPE) {
*input_buf_len = 0;
while(close(pipe_stdin[1]));
} else if (errno != EAGAIN)
goto cleanup; /* write() failed, errno is set */
} else {
*input_buf_len -= n_bytes;
input_off += n_bytes;
if (*input_buf_len == 0)
while (close(pipe_stdin[1]) != 0); /* close here or hang for sure */
}
} else if (event) { /* error on pipe */
*input_buf_len = 0;
while(close(pipe_stdin[1]));
}
}
}
if (*output_buf_len) {
if ((event = fds[nfds++].revents) != 0) {
if (event & POLLIN) {
n_bytes = read(pipe_stdout[0], &output_buffer[output_off], *output_buf_len);
if (n_bytes == -1) {
if (errno != EAGAIN)
goto cleanup; /* read() failed, errno is set */
} else {
*output_buf_len -= n_bytes;
output_off += n_bytes;
if (n_bytes == 0)
*output_buf_len = 0;
if (*output_buf_len == 0)
while (close(pipe_stdout[0]) != 0); /* close here or hang if unlucky */
}
} else if (event) { /* POLLHUP or error on pipe */
*output_buf_len = 0;
while(close(pipe_stdout[0]));
}
}
}
if (*error_buf_len) {
if ((event = fds[nfds].revents) != 0) {
if (event & POLLIN) {
n_bytes = read(pipe_stderr[0], &error_buffer[error_off], *error_buf_len);
if (n_bytes == -1) {
if (errno != EAGAIN)
goto cleanup; /* read() failed, errno is set */
} else {
*error_buf_len -= n_bytes;
error_off += n_bytes;
if (n_bytes == 0)
*error_buf_len = 0;
if (*error_buf_len == 0)
while (close(pipe_stderr[0]) != 0); /* close here or hang if unlucky */
}
} else if (event) { /* POLLHUP or error on pipe */
*error_buf_len = 0;
while(close(pipe_stderr[0]));
}
}
}
} /* while */
ret_val = 0;
goto cleanup;
pipe_err_stderr:
if (*output_buf_len) {
while(close(pipe_stdout[0]));
while(close(pipe_stdout[1]));
}
pipe_err_stdout:
if (*error_buf_len) {
while(close(pipe_stdin[0]));
while(close(pipe_stdin[1]));
}
pipe_err_stdin:
return -1;
fork_err:
close_err_stdin:
if (*input_buf_len)
while(close(pipe_stdin[0]));
close_err_stdout:
if (*output_buf_len)
while(close(pipe_stdout[1]));
close_err_stderr:
if (*error_buf_len)
while(close(pipe_stderr[1]));
cleanup:
if (*input_buf_len)
while(close(pipe_stdin[1]));
if (*output_buf_len)
while(close(pipe_stdout[0]));
if (*error_buf_len)
while(close(pipe_stderr[0]));
if (pid != -1)
while (waitpid(pid, 0, 0) != -1);
*input_buf_len = input_off;
*output_buf_len = output_off;
*error_buf_len = error_off;
while ((is_member = sigismember(&sigmask, SIGPIPE)) == -1); /* succeed here or risk falsely blocked SIGPIPE */
if (is_member)
while (sigprocmask(SIG_SETMASK, &oldmask, 0)); /* succeed here or risk falsely blocked SIGPIPE */
return ret_val;
}
Last modified
2005-08-10, 22:43 CEST
[an error occurred while processing this directive] This page is maintained by
[an error occurred while processing this directive]MHonArc
[an error occurred while processing this directive] #
[an error occurred while processing this directive] *