[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]
 
[an error occurred while processing this directive] [an error occurred while processing this directive]
Skåne Sjælland Linux User Group - http://www.sslug.dk Home   Subscribe   Mail Archive   Forum   Calendar   Search
MhonArc Date: [Date Prev] [Date Index] [Date Next]   Thread: [Date Prev] [Thread Index] [Date Next]   MhonArc
 

Re: [PROGRAMMERING] C rutine til exec + pipe til og fra



Nedenstående ser ud til at virke - dog ikke testet grundigt.

Hilsen,
Hans-Christian Stadler

PS: anti-gotoister er opfordret til at vise en læselig version uden goto
    med komplet fejlhåndtering.
---------------------------------------------------------------------------------
/* 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 <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 (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;     /* select() 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
              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) {
          *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) {
            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) {
          *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) {
            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) {
          *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;
}



 
Home   Subscribe   Mail Archive   Index   Calendar   Search

 
 
Questions about the web-pages to <www_admin>. 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] *