[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



On Tue, 25 May 2004 10:35:57 +0200, Hans-Christian Stadler wrote:

>> program, men mere generelt. Din goto-baserede fejlhåndtering er i mine
>> øjne også skrøbelig. Ikke et ondt ord om goto, men hvis en anden
>> programmør om et år skal rette i koden, er det nok rimelig sandsynligt
>> at han ødelægger noget så en fil kan blive close()t to gange under en
>> eller anden fejlbetingelse. Det kunne måske endda ske for dig selv, hvis
>> du ikke brugte tid nok på at forstå program flow'et når du vendte
>> tilbage til koden et par år efter.
> 
> Jeg mener tværtimod, at læseligheden lider uden goto. Vis du har overvejet
> alternativerne, så vis os dem dog. Ellers virker dit indlæg som hul snak
> på mig.

Se nedenfor (jeg håber ikke min news klient ødelægger formatteringen).
Jeg har ikke testet koden, da formålet bare er at demonstrere en
omstrukturering.

Jeg er ikke generelt goto-modstander, men en så lang funktion bør under
alle omstændigheder deles op i flere funktioner for læsbarhedens skyld.
Når man alligevel deler den op, kan man lige så godt gøre
fejlhåndteringen lidt mere gennemskuelig.

Jeg slipper afsted med langt færre close() kald fordi jeg i child
processen lader execl lukke mine filehandles, og i parent processen lukker
jeg pipes'ne så sent som muligt incl. de ubrugte ender. Den smule ekstra
RAM det bruger at have 3 overflødige file descriptors åbne giver efter
min mening langt mere gennemskuelig kode.

#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>

static void do_child(int fd_stdin, int fd_stdout, int fd_stderr, const char *command) {
  if (fd_stdin != -1 && dup2(fd_stdin, 0) == -1)
    exit(-1);
  if (fd_stdout != -1 && dup2(fd_stdout, 1) == -1)
    exit(-1);
  if (fd_stderr != -1 && dup2(fd_stderr, 2) == -1)
    exit(-1);

  /* Remaining file descriptors will be closed by execl */
  execl("/bin/sh", "sh", "-c", command);
  exit(-1);
}

static int do_parent(
    int fd_stdin , const char * input_buffer, int * input_buf_len,
    int fd_stdout,       char *output_buffer, int *output_buf_len,
    int fd_stderr,       char * error_buffer, int * error_buf_len) {
  int nfds;
  ssize_t n_bytes;
  size_t input_off=0, output_off=0, error_off=0;
  struct pollfd fds[3];
  short event;

  if (*input_buf_len && fcntl(fd_stdin, F_SETFL, O_NONBLOCK))
    return -1;
  if (*output_buf_len && fcntl(fd_stdout, F_SETFL, O_NONBLOCK))
    return -1;
  if (*error_buf_len && fcntl(fd_stderr, F_SETFL, O_NONBLOCK))
    return -1;

  while (*input_buf_len || *output_buf_len || *error_buf_len) {
    nfds = 0;
    if (*input_buf_len)
      fds[nfds++] = (struct pollfd){ fd_stdin, POLLOUT, 0 };
    if (*output_buf_len)
      fds[nfds++] = (struct pollfd){ fd_stdout, POLLIN, 0 };
    if (*error_buf_len)
      fds[nfds++] = (struct pollfd){ fd_stderr, POLLIN, 0 };

    if (poll(fds, nfds, -1) == -1)
      return -1;

    nfds = 0;
    if (*input_buf_len) {
      if ((event = fds[nfds++].revents) != 0) {
        if (event & POLLOUT) {
          n_bytes = write(fd_stdin, &input_buffer[input_off], *input_buf_len);
          if (n_bytes == -1) {
            if (errno == EPIPE) {
              *input_buf_len = 0;
            } else if (errno != EAGAIN)
              return -1;
          } else {
            *input_buf_len -= n_bytes;
            input_off += n_bytes;
          }
        } else if (event) {  /* error on pipe */
          *input_buf_len = 0;
        }
      }
    }
    if (*output_buf_len) {
      if ((event = fds[nfds++].revents) != 0) {
        if (event & POLLIN) {
          n_bytes = read(fd_stdout, &output_buffer[output_off], *output_buf_len);
          if (n_bytes == -1) {
            if (errno != EAGAIN)
              return -1;
          } else {
            *output_buf_len -= n_bytes;
            output_off += n_bytes;
            if (n_bytes == 0)
              *output_buf_len = 0;
          }
        } else if (event) {  /* POLLHUP or error on pipe */
          *output_buf_len = 0;
        }
      }
    }
    if (*error_buf_len) {
      if ((event = fds[nfds].revents) != 0) {
        if (event & POLLIN) {
          n_bytes = read(fd_stderr, &error_buffer[error_off], *error_buf_len);
          if (n_bytes == -1) {
            if (errno != EAGAIN)
              return -1;
          } else {
            *error_buf_len -= n_bytes;
            error_off += n_bytes;
            if (n_bytes == 0)
              *error_buf_len = 0;
          }
        } else if (event) {  /* POLLHUP or error on pipe */
          *error_buf_len = 0;
        }
      }
    }
  } /* while */

  *input_buf_len = input_off;
  *output_buf_len = output_off;
  *error_buf_len = error_off;

  return 0;
}

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], ret_val=-1, is_member;
  int opened_in = 0, opened_out = 0, opened_err = 0;
  sigset_t sigmask, oldmask;

  if (sigemptyset(&sigmask))
    return -1;  /* sigemptyset() failed, errno is set */

  if ((!*input_buf_len  || (pipe(pipe_stdin)  == 0 && (opened_in = 1))) &&
      (!*output_buf_len || (pipe(pipe_stdout) == 0 && (opened_out = 1))) &&
      (!*error_buf_len  || (pipe(pipe_stderr) == 0 && (opened_err = 1)))) {

    pid_t pid = fork();
    if (pid == 0) {
      do_child(
          *input_buf_len  ? pipe_stdin[0]  : -1,
          *output_buf_len ? pipe_stdout[1] : -1,
          *error_buf_len  ? pipe_stderr[1] : -1, command);
    
    } else if (pid > 0) {
      if (sigaddset(&sigmask, SIGPIPE) == 0) {
        if (sigprocmask(SIG_BLOCK, &sigmask, &oldmask) == 0) {

          ret_val = do_parent(
              pipe_stdin[1],  input_buffer , input_buf_len,
              pipe_stdout[0], output_buffer, output_buf_len,
              pipe_stderr[0], error_buffer , error_buf_len);
          
        } else {
          while (sigemptyset(&sigmask));  /* clear set here or risk bogus signal mask */
        }
      }
    }

    while (pid > 0 && waitpid(pid, 0, 0) != -1);
  }

  if (opened_in) {
    close(pipe_stdin[0]);
    close(pipe_stdin[1]);
  }
  if (opened_out) {
    close(pipe_stdout[0]);
    close(pipe_stdout[1]);
  }
  if (opened_err) {
    close(pipe_stderr[0]);
    close(pipe_stderr[1]);
  }

  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] *