[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



> Se nedenfor (jeg håber ikke min news klient ødelægger formatteringen).
Bravo, det ser ikke så tosset ud, selvom du har måttet ty til tricks
(ikke lukke ubrugte fd's med det samme, indførsel af nye flags, opsplitning i funktioner)
for at få det til at se akzeptabelt ud. Jeg tør da også vedde min salige
farmors backenbart på, at du savnede fornemmelsen af "straightforwardness"
under kodningen.
Jeg kan umiddelbart lige se én bommert, se nedenfor.

> 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.
Jeg er ikke goto-tilhænger - bevares. Dog prioriterer jeg læselighed højere,
og jeg synes stadigvæk, versionen med goto er mere læseligt, netop fordi
den er straightforward. Og så synes jeg ikke, en lang funktion per se er
ulæseligt så længe den har et fint flow fra top til bund.

> Jeg slipper afsted med langt færre close() kald fordi jeg i child
> processen lader execl lukke mine filehandles
execl lukker ikke dine fd's, din child process bærer rundt på mugne
fd's uden at vide det.

>, og i parent processen lukker
> jeg pipes'ne så sent som muligt incl. de ubrugte ender.
Det er en bommert, du skal lukke stdin af child processen, ellers
risikerer du at hænge, fordi child venter på input, og parent på
output fra child.

>     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;                <--- minimum her SKAL stdin til 'command' lukkes!
>           }
>         } else if (event) {  /* error on pipe */
>           *input_buf_len = 0;
>         }
>       }
>     }


Ok, din version er da fint nok, men for at sammenligne bør man så lige
have alternativversionen med tricks. Versgod.

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>

/* close pipe fds */
static void close_pipes (int pipe_fd[2])
{
  while(close(pipe_fd[0]));
  while(close(pipe_fd[1]));
}

/* Perform io operations on pipe pipe_fd */
static int do_pipe_io (size_t *buf_len, struct pollfd fds[3], int *nfds, short wait_for_event,
                       char *buf, int pipe_fd[2], size_t *offset, int *flag_fd)
{
  ssize_t n_bytes;
  short event;

  if (! *buf_len)
    return 0;
  if ((event = fds[(*nfds)++].revents) == 0)
    return 0;
  if (event & wait_for_event) {
    if (wait_for_event == POLLOUT)
      n_bytes = write(pipe_fd[1], &buf[*offset], *buf_len);
    else /* wait_for_event == POLLIN */
      n_bytes = read(pipe_fd[0], &buf[*offset], *buf_len);
    if (n_bytes == -1) {
      if (errno == EPIPE) /* never happens if wait_for_event == POLLIN */
        *buf_len = 0;
      else if (errno != EAGAIN)
        return -1;        /* write() failed, errno is set */
    } else {
      *buf_len -= n_bytes;
      *offset += n_bytes;
      if (*buf_len == 0) {
        close_pipes(pipe_fd); /* close here or hang for sure */
        *flag_fd = 0;
      }
    }
  } else if (event) {   /* POLLHUP or error on pipe */
    *buf_len = 0;
    close_pipes(pipe_fd);
    *flag_fd = 0;
  }
  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], is_member, ret_val=-1, nfds,
      flag_stdin=0, flag_stdout=0, flag_stderr=0;
  pid_t pid;
  size_t input_off=0, output_off=0, error_off=0;
  sigset_t sigmask, oldmask;
  struct pollfd fds[3];

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

  if ((*input_buf_len && pipe(pipe_stdin) && (flag_stdin=1)) ||
      (*output_buf_len && pipe(pipe_stdout) && (flag_stdout=1)) ||
      (*error_buf_len && pipe(pipe_stderr) && (flag_stderr=1)))
    goto close_pipes_and_exit;

  if ((pid = fork()) == 0) {
    /* child process */
    if ((*input_buf_len && (dup2(pipe_stdin[0], 0) == -1)) ||
        (*output_buf_len && (dup2(pipe_stdout[1], 1) == -1)) ||
        (*error_buf_len && (dup2(pipe_stderr[1], 2) == -1)))
      exit(-1);
    execl("/bin/sh", "sh", "-c", command);
    exit(-1);
  }

  /* parent process */
  if (pid == -1)
    goto close_pipes_and_exit;  /* fork() failed, errno is set */

  if ((*input_buf_len && fcntl(pipe_stdin[1], F_SETFL, O_NONBLOCK)) ||
      (*output_buf_len && fcntl(pipe_stdout[0], F_SETFL, O_NONBLOCK)) ||
      (*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 (do_pipe_io(input_buf_len, fds, &nfds, POLLOUT, (char *)input_buffer, pipe_stdin, &input_off, &flag_stdin) ||
        do_pipe_io(output_buf_len, fds, &nfds, POLLIN, output_buffer, pipe_stdout, &output_off, &flag_stdout) ||
        do_pipe_io(error_buf_len, fds, &nfds, POLLIN, error_buffer, pipe_stderr, &error_off, &flag_stderr))
      goto cleanup;
  } /* while */

  ret_val = 0;

 cleanup:
  while (waitpid(pid, 0, 0) != -1);
  *input_buf_len = input_off;
  *output_buf_len = output_off;
  *error_buf_len = error_off;

 close_pipes_and_exit:
  if (flag_stdin)
    close_pipes(pipe_stdin);
  if (flag_stdout)
    close_pipes(pipe_stdout);
  if (flag_stderr)
    close_pipes(pipe_stderr);

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