diff options
| -rw-r--r-- | configure.in | 1 | ||||
| -rw-r--r-- | gpgme/Makefile.am | 4 | ||||
| -rw-r--r-- | gpgme/data.c | 68 | ||||
| -rw-r--r-- | gpgme/gpgme.c | 27 | ||||
| -rw-r--r-- | gpgme/io.h | 64 | ||||
| -rw-r--r-- | gpgme/posix-io.c | 284 | ||||
| -rw-r--r-- | gpgme/rungpg.c | 189 | ||||
| -rw-r--r-- | gpgme/util.c | 16 | ||||
| -rw-r--r-- | gpgme/util.h | 7 | ||||
| -rw-r--r-- | gpgme/verify.c | 22 | ||||
| -rw-r--r-- | gpgme/w32-io.c | 239 | ||||
| -rw-r--r-- | gpgme/wait.c | 270 | 
12 files changed, 902 insertions, 289 deletions
| diff --git a/configure.in b/configure.in index 3e23edfd..7363d790 100644 --- a/configure.in +++ b/configure.in @@ -57,6 +57,7 @@ dnl  dnl  dnl Checks for library functions  dnl +AC_CHECK_FUNCS(stpcpy)  dnl diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index b65157a5..6fe3bb1a 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -24,6 +24,7 @@ libgpgme_la_SOURCES = \          key.c key.h \  	keylist.c \          rungpg.c rungpg.h status-table.h \ +	io.h posix-io.c w32-io.c \  	gpgme.c version.c errors.c @@ -36,3 +37,6 @@ status-table.h : rungpg.h + + + diff --git a/gpgme/data.c b/gpgme/data.c index 1037166b..62bd830a 100644 --- a/gpgme/data.c +++ b/gpgme/data.c @@ -38,6 +38,14 @@                           || ((a) >= 'f' && (a) <= 'f') ) +/** + * gpgme_data_new: + * @r_dh: returns the new data object  + *  + * Create a new data object without any content.  + *  + * Return value: An error value or 0 on success + **/  GpgmeError  gpgme_data_new ( GpgmeData *r_dh )  { @@ -104,6 +112,19 @@ gpgme_data_new_from_mem ( GpgmeData *r_dh,      return 0;  } +/** + * gpgme_data_new_from_file: + * @r_dh: returns the new data object + * @fname: filename + * @copy: Flag, whether the file should be copied. + *  + * Create a new data object and initialize it with the content of  + * the file @file.  If @copy is %True the file is immediately read in + * adn closed.  @copy of %False is not yet supportted. + *  + * Return value: An error code or 0 on success. If the error code is + * %GPGME_File_Error, the OS error code is held in %errno. + **/  GpgmeError  gpgme_data_new_from_file ( GpgmeData *r_dh, const char *fname, int copy )  { @@ -210,6 +231,18 @@ _gpgme_data_release_and_return_string ( GpgmeData dh )      return val;  } +/** + * gpgme_data_release_and_get_mem: + * @dh: the data object + * @r_len: returns the length of the memory + *  + * Release the data object @dh and return its content and the length of + * that content.  The caller has to free this data.  @dh maybe NULL in + * which case NULL is returned.  I there is not enough memory for allocating + * the return value, NULL is returned and the object is released. + *  + * Return value: a pointer to an allocated buffer of length @r_len. + **/  char *  gpgme_data_release_and_get_mem ( GpgmeData dh, size_t *r_len )  { @@ -233,6 +266,15 @@ gpgme_data_release_and_get_mem ( GpgmeData dh, size_t *r_len )  } +/** + * gpgme_data_get_type: + * @dh: the data object + *  + * Get the type of the data object. + * Data types are prefixed with %GPGME_DATA_TYPE_ + *  + * Return value: the data type + **/  GpgmeDataType  gpgme_data_get_type ( GpgmeData dh )  { @@ -257,6 +299,16 @@ _gpgme_data_get_mode ( GpgmeData dh )      return dh->mode;  } +/** + * gpgme_data_rewind: + * @dh: the data object  + *  + * Prepare the data object in a way, that a gpgme_data_read() does start + * at the beginning of the data.  This has to be done for all types + * of data objects. + *  + * Return value: An error code or 0 on success + **/  GpgmeError  gpgme_data_rewind ( GpgmeData dh )  { @@ -268,6 +320,22 @@ gpgme_data_rewind ( GpgmeData dh )      return 0;  } +/** + * gpgme_data_read: + * @dh: the data object + * @buffer: A buffer  + * @length: The length of that bufer + * @nread: Returns the number of bytes actually read. + *  + * Copy data from the current read position (which may be set by + * gpgme_data_rewind()) to the supplied @buffer, max. @length bytes + * are copied and the actual number of bytes are returned in @nread. + * If there are no more bytes available %GPGME_EOF is returned and @nread + * is set to 0. + *  + * Return value: An errocodee or 0 on success, EOF is indcated by the + * error code GPGME_EOF. + **/  GpgmeError  gpgme_data_read ( GpgmeData dh, char *buffer, size_t length, size_t *nread )  { diff --git a/gpgme/gpgme.c b/gpgme/gpgme.c index 165f03cb..9a6d7b53 100644 --- a/gpgme/gpgme.c +++ b/gpgme/gpgme.c @@ -95,6 +95,16 @@ _gpgme_release_result ( GpgmeCtx c )  } +/** + * gpgme_get_notation: + * @c: the context  + *  + * If there is notation data available from the last signature check, this + * function may be used to return this notation data as a string.  The string + * is an XML represantaton of that data embedded in a %<notation> container. + *  + * Return value: An XML string or NULL if no notation data is available. + **/  char *  gpgme_get_notation ( GpgmeCtx c )  { @@ -104,6 +114,13 @@ gpgme_get_notation ( GpgmeCtx c )  } +/** + * gpgme_set_armor: + * @c: the contect  + * @yes: boolean value to set or clear that flag + *  + * Enable or disable the use of an ascii armor for all output.   + **/  void  gpgme_set_armor ( GpgmeCtx c, int yes )  { @@ -112,6 +129,14 @@ gpgme_set_armor ( GpgmeCtx c, int yes )      c->use_armor = yes;  } +/** + * gpgme_set_textmode: + * @c: the context  + * @yes: boolean flag whether textmode should be enabled + *  + * Enable or disable the use of the special textmode.  Textmode is for example + * used for MIME (RFC2015) signatures + **/  void  gpgme_set_textmode ( GpgmeCtx c, int yes )  { @@ -136,3 +161,5 @@ gpgme_set_passphrase_cb ( GpgmeCtx c, GpgmePassphraseCb fnc, void *fncval ) + + diff --git a/gpgme/io.h b/gpgme/io.h new file mode 100644 index 00000000..5afac591 --- /dev/null +++ b/gpgme/io.h @@ -0,0 +1,64 @@ +/* io.h - I/O functions  + *	Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef IO_H +#define IO_H + +#include "types.h" + +struct spawn_fd_item_s { +    int fd; +    int dup_to; +}; + + +struct io_select_fd_s { +    int fd; +    int for_read; +    int for_write; +    int signaled; +    void *opaque; +}; + + +/* These function are either defined in posix-io.c or w32-io.c */ + +int _gpgme_io_read ( int fd, void *buffer, size_t count ); +int _gpgme_io_write ( int fd, const void *buffer, size_t count ); +int _gpgme_io_pipe ( int filedes[2] ); +int _gpgme_io_set_nonblocking ( int fd ); +pid_t _gpgme_io_spawn ( const char *path, char **argv, +                        struct spawn_fd_item_s *fd_child_list, +                        struct spawn_fd_item_s *fd_parent_list ); +int _gpgme_io_waitpid ( pid_t pid, int hang, int *r_status, int *r_signal ); +int _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds); + + + + + + + +#endif /* IO_H */ + + + + + diff --git a/gpgme/posix-io.c b/gpgme/posix-io.c new file mode 100644 index 00000000..f2de6cdd --- /dev/null +++ b/gpgme/posix-io.c @@ -0,0 +1,284 @@ +/* posix-io.c - Posix I/O functions + *	Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#ifndef HAVE_DOSISH_SYSTEM + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/wait.h> +#include <signal.h> +#include <fcntl.h> + +#include "io.h" + +#define DEBUG_SELECT_ENABLED 0 + +#if DEBUG_SELECT_ENABLED +# define DEBUG_SELECT(a) fprintf a +#else +# define DEBUG_SELECT(a) do { } while(0) +#endif + + +int +_gpgme_io_read ( int fd, void *buffer, size_t count ) +{ +    int nread; + +    do { +        nread = read (fd, buffer, count); +    } while (nread == -1 && errno == EINTR ); +    return nread; +} + + +int +_gpgme_io_write ( int fd, const void *buffer, size_t count ) +{ +    int nwritten; + +    do { +        nwritten = write (fd, buffer, count); +    } while (nwritten == -1 && errno == EINTR ); +    return nwritten; +} + +int +_gpgme_io_pipe ( int filedes[2] ) +{ +    return pipe ( filedes ); +} + + +int +_gpgme_io_set_nonblocking ( int fd ) +{ +    int flags; + +    flags = fcntl (fd, F_GETFL, 0); +    if (flags == -1) +        return -1; +    flags |= O_NONBLOCK; +    return fcntl (fd, F_SETFL, flags); +} + + +pid_t +_gpgme_io_spawn ( const char *path, char **argv, +                  struct spawn_fd_item_s *fd_child_list, +                  struct spawn_fd_item_s *fd_parent_list ) +{ +    pid_t pid; +    int i; +     +    pid = fork (); +    if (pid == -1)  +        return -1; + +    if ( !pid ) { /* child */ +        int duped_stdin = 0; +        int duped_stderr = 0; + +        /* first close all fds which will not be duped */ +        for (i=0; fd_child_list[i].fd != -1; i++ ) { +            if (fd_child_list[i].dup_to == -1 ) +                close (fd_child_list[i].fd); +        } +        /* and now dup and close the rest */ +        for (i=0; fd_child_list[i].fd != -1; i++ ) { +            if (fd_child_list[i].dup_to != -1 ) { +                if ( dup2 (fd_child_list[i].fd, +                           fd_child_list[i].dup_to ) == -1 ) { +                    fprintf (stderr, "dup2 failed in child: %s\n", +                             strerror (errno)); +                    _exit (8); +                } +                if ( fd_child_list[i].dup_to == 0 ) +                    duped_stdin=1; +                if ( fd_child_list[i].dup_to == 2 ) +                    duped_stderr=1; +                close (fd_child_list[i].fd); +            } +        } + +        if( !duped_stdin || !duped_stderr ) { +            int fd = open ( "/dev/null", O_RDONLY ); +            if ( fd == -1 ) { +                fprintf (stderr,"can't open `/dev/null': %s\n", +                         strerror (errno) ); +                _exit (8); +            } +            /* Make sure that the process has a connected stdin */ +            if ( !duped_stdin ) { +                if ( dup2 ( fd, 0 ) == -1 ) { +                    fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n", +                             strerror (errno) ); +                    _exit (8); +                } +            } +            /* We normally don't want all the normal output */ +            if ( !duped_stderr ) { +                if (!getenv ("GPGME_DEBUG") ) { +                    if ( dup2 ( fd, 2 ) == -1 ) { +                        fprintf (stderr,"dup2(dev/null, 2) failed: %s\n", +                                 strerror (errno) ); +                        _exit (8); +                    } +                } +            } +            close (fd); +        } + +        execv ( path, argv ); +        /* Hmm: in that case we could write a special status code to the +         * status-pipe */ +        fprintf (stderr,"exec of `%s' failed\n", path ); +        _exit (8); +    } /* end child */ +     +    /* .dup_to is not used in the parent list */ +    for (i=0; fd_parent_list[i].fd != -1; i++ ) { +        close (fd_parent_list[i].fd); +    } + +    return pid; +} + + +int +_gpgme_io_waitpid ( pid_t pid, int hang, int *r_status, int *r_signal ) +{ +    int status; + +    *r_status = 0; +    *r_signal = 0; +    if ( waitpid ( pid, &status, hang? 0 : WNOHANG ) == pid ) { +        if ( WIFSIGNALED (status) ) { +            *r_status = 4; /* Need some value here */ +            *r_signal = WTERMSIG (status); +        } +        else if ( WIFEXITED (status) ) { +            *r_status = WEXITSTATUS (status); +        } +        else { +            *r_status = 4; /* oops */ +        } +        return 1; +    } +    return 0; +} + + +/* + * Select on the list of fds. + * Returns: -1 = error + *           0 = timeout or nothing to select + *          >0 = number of signaled fds + */ +int +_gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds ) +{ +    static fd_set readfds; +    static fd_set writefds; +    int any, i, max_fd, n, count; +    struct timeval timeout = { 1, 0 }; /* Use a one second timeout */ +     +    FD_ZERO ( &readfds ); +    FD_ZERO ( &writefds ); +    max_fd = 0; + +    DEBUG_SELECT ((stderr, "gpgme:select on [ ")); +    any = 0; +    for ( i=0; i < nfds; i++ ) { +        if ( fds[i].fd == -1 )  +            continue; +        if ( fds[i].for_read ) { +            assert ( !FD_ISSET ( fds[i].fd, &readfds ) ); +            FD_SET ( fds[i].fd, &readfds ); +            if ( fds[i].fd > max_fd ) +                max_fd = fds[i].fd; +            DEBUG_SELECT ((stderr, "r%d ", fds[i].fd )); +            any = 1; +        } +        else if ( fds[i].for_write ) { +            assert ( !FD_ISSET ( fds[i].fd, &writefds ) ); +            FD_SET ( fds[i].fd, &writefds ); +            if ( fds[i].fd > max_fd ) +                max_fd = fds[i].fd; +            DEBUG_SELECT ((stderr, "w%d ", fds[i].fd )); +            any = 1; +        } +        fds[i].signaled = 0; +    } +    DEBUG_SELECT ((stderr, "]\n" )); +    if ( !any ) +        return 0; + +    do { +        count = select ( max_fd+1, &readfds, &writefds, NULL, &timeout ); +    } while ( count < 0 && errno == EINTR); +    if ( count < 0 ) { +        fprintf (stderr, "_gpgme_io_select failed: %s\n", strerror (errno) ); +        return -1; /* error */ +    } + +#if DEBUG_SELECT_ENABLED +    fprintf (stderr, "gpgme:select OK [ " ); +    for (i=0; i <= max_fd; i++ ) { +        if (FD_ISSET (i, &readfds) ) +            fprintf (stderr, "r%d ", i ); +        if (FD_ISSET (i, &writefds) ) +            fprintf (stderr, "w%d ", i ); +    } +    fprintf (stderr, "]\n" ); +#endif +     +    /* n is used to optimize it a little bit */ +    for ( n=count, i=0; i < nfds && n ; i++ ) { +        if ( fds[i].fd == -1 )  +            ; +        else if ( fds[i].for_read ) { +            if ( FD_ISSET ( fds[i].fd, &readfds ) ) { +                fds[i].signaled = 1; +                n--; +            } +        } +        else if ( fds[i].for_write ) { +            if ( FD_ISSET ( fds[i].fd, &writefds ) ) { +                fds[i].signaled = 1; +                n--; +            } +        } +    } +    return count; +} + + +#endif /*!HAVE_DOSISH_SYSTEM*/ + + + diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index f75b8b14..316f78b5 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -37,6 +37,7 @@  #include "wait.h"  #include "rungpg.h"  #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */ +#include "io.h"  #include "status-table.h" @@ -137,7 +138,7 @@ _gpgme_gpg_new ( GpgObject *r_gpg )      }      /* In any case we need a status pipe - create it right here  and       * don't handle it with our generic GpgmeData mechanism */ -    if (pipe (gpg->status.fd) == -1) { +    if (_gpgme_io_pipe (gpg->status.fd) == -1) {          rc = mk_error (Pipe_Error);          goto leave;      } @@ -202,19 +203,6 @@ kill_gpg ( GpgObject gpg ) -static int -set_nonblocking ( int fd ) -{ -    int flags; - -    flags = fcntl (fd, F_GETFL, 0); -    if (flags == -1) -        return -1; -    flags |= O_NONBLOCK; -    return fcntl (fd, F_SETFL, flags); -} - -  GpgmeError  _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )  { @@ -281,7 +269,7 @@ _gpgme_gpg_set_colon_line_handler ( GpgObject gpg,      if (!gpg->colon.buffer) {          return mk_error (Out_Of_Core);      } -    if (pipe (gpg->colon.fd) == -1) { +    if (_gpgme_io_pipe (gpg->colon.fd) == -1) {          xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;          return mk_error (Pipe_Error);      } @@ -428,7 +416,7 @@ build_argv ( GpgObject gpg )              {                     int fds[2]; -                if (pipe (fds) == -1) { +                if (_gpgme_io_pipe (fds) == -1) {                      xfree (fd_data_map);                      free_argv (argv);                      return mk_error (Pipe_Error); @@ -477,8 +465,9 @@ GpgmeError  _gpgme_gpg_spawn( GpgObject gpg, void *opaque )  {      int rc; -    int i; +    int i, n;      pid_t pid; +    struct spawn_fd_item_s *fd_child_list, *fd_parent_list;      if ( !gpg )          return mk_error (Invalid_Value); @@ -492,88 +481,75 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque )      if ( rc )          return rc; -    fflush (stderr); -    pid = fork (); -    if (pid == -1) { -        return mk_error (Exec_Error); +    n = 4; /* status fd, 2*colon_fd and end of list */ +    for (i=0; gpg->fd_data_map[i].data; i++ )  +        n += 2; +    fd_child_list = xtrycalloc ( n+n, sizeof *fd_child_list ); +    if (!fd_child_list) +        return mk_error (Out_Of_Core); +    fd_parent_list = fd_child_list + n; + +    /* build the fd list for the child */ +    n=0; +    fd_child_list[n].fd = gpg->status.fd[0]; +    fd_child_list[n].dup_to = -1; +    n++; +    if ( gpg->colon.fnc ) { +        fd_child_list[n].fd = gpg->colon.fd[0]; +        fd_child_list[n].dup_to = -1; +        n++; +        fd_child_list[n].fd = gpg->colon.fd[1];  +        fd_child_list[n].dup_to = 1; /* dup to stdout */ +        n++;      } -         -    if ( !pid ) { /* child */ -        int duped_stdin = 0; -        int duped_stderr = 0; - -        close (gpg->status.fd[0]); - -        if (gpg->colon.fnc) { -            /* dup it to stdout */ -            if ( dup2 ( gpg->colon.fd[1], 1 ) == -1 ) { -                fprintf (stderr,"dup2(colon, 1) failed: %s\n", -                         strerror (errno) ); -                _exit (8); -            } -            close (gpg->colon.fd[0]); -            close (gpg->colon.fd[1]); -        } -             -        for (i=0; gpg->fd_data_map[i].data; i++ ) { -            close (gpg->fd_data_map[i].fd); -            gpg->fd_data_map[i].fd = -1; -            if ( gpg->fd_data_map[i].dup_to != -1 ) { -                if ( dup2 (gpg->fd_data_map[i].peer_fd, -                           gpg->fd_data_map[i].dup_to ) == -1 ) { -                    fprintf (stderr, "dup2 failed in child: %s\n", -                             strerror (errno)); -                    _exit (8); -                } -                if ( gpg->fd_data_map[i].dup_to == 0 ) -                    duped_stdin=1; -                if ( gpg->fd_data_map[i].dup_to == 2 ) -                    duped_stderr=1; -                close ( gpg->fd_data_map[i].peer_fd ); -            } +    for (i=0; gpg->fd_data_map[i].data; i++ ) { +        fd_child_list[n].fd = gpg->fd_data_map[i].fd; +        fd_child_list[n].dup_to = -1; +        n++; +        if (gpg->fd_data_map[i].dup_to != -1) { +            fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd; +            fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to; +            n++;          } +    } +    fd_child_list[n].fd = -1; +    fd_child_list[n].dup_to = -1; -        if( !duped_stdin || !duped_stderr ) { -            int fd = open ( "/dev/null", O_RDONLY ); -            if ( fd == -1 ) { -                fprintf (stderr,"can't open `/dev/null': %s\n", -                         strerror (errno) ); -                _exit (8); -            } -            /* Make sure that gpg has a connected stdin */ -            if ( !duped_stdin ) { -                if ( dup2 ( fd, 0 ) == -1 ) { -                    fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n", -                             strerror (errno) ); -                    _exit (8); -                } -            } -            /* We normally don't want all the normal output */ -            if ( !duped_stderr ) { -                if (!getenv ("GPGME_DEBUG") ) { -                    if ( dup2 ( fd, 2 ) == -1 ) { -                        fprintf (stderr,"dup2(dev/null, 2) failed: %s\n", -                                 strerror (errno) ); -                        _exit (8); -                    } -                } -            } -            close (fd); -        } +    /* build the fd list for the parent */ +    n=0; +    if ( gpg->status.fd[1] != -1 ) { +        fd_parent_list[n].fd = gpg->status.fd[1]; +        fd_parent_list[n].dup_to = -1; +        n++; +        gpg->status.fd[1] = -1; +    } +    if ( gpg->colon.fd[1] != -1 ) { +        fd_parent_list[n].fd = gpg->colon.fd[1]; +        fd_parent_list[n].dup_to = -1; +        n++; +        gpg->colon.fd[1] = -1; +    } +    for (i=0; gpg->fd_data_map[i].data; i++ ) { +        fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd; +        fd_parent_list[n].dup_to = -1; +        n++; +        gpg->fd_data_map[i].peer_fd = -1; +    }         +    fd_parent_list[n].fd = -1; +    fd_parent_list[n].dup_to = -1; -        execv ( GPG_PATH, gpg->argv ); -        fprintf (stderr,"exec of gpg failed\n"); -        _exit (8); + +    fflush (stderr); +    pid = _gpgme_io_spawn (GPG_PATH, gpg->argv, fd_child_list, fd_parent_list); +    xfree (fd_child_list); +    if (pid == -1) { +        return mk_error (Exec_Error);      } -    /* parent */ +      gpg->pid = pid;      /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ -    if ( gpg->status.fd[1] != -1 ) { -        close (gpg->status.fd[1]); -        gpg->status.fd[1] = -1; -    }      if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,                                          gpg, pid, gpg->status.fd[0], 1 ) ) {          /* FIXME: kill the child */ @@ -581,9 +557,7 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque )      } -    if ( gpg->colon.fd[1] != -1 ) { -        close (gpg->colon.fd[1]); -        gpg->colon.fd[1] = -1; +    if ( gpg->colon.fnc ) {          assert ( gpg->colon.fd[0] != -1 );          if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler,                                              gpg, pid, gpg->colon.fd[0], 1 ) ) { @@ -594,13 +568,10 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque )      }      for (i=0; gpg->fd_data_map[i].data; i++ ) { -        close (gpg->fd_data_map[i].peer_fd); -        gpg->fd_data_map[i].peer_fd = -1; -                  /* Due to problems with select and write we set outbound pipes           * to non-blocking */          if (!gpg->fd_data_map[i].inbound) { -            set_nonblocking (gpg->fd_data_map[i].fd); +            _gpgme_io_set_nonblocking (gpg->fd_data_map[i].fd);          }          if ( _gpgme_register_pipe_handler ( @@ -633,9 +604,7 @@ gpg_inbound_handler ( void *opaque, pid_t pid, int fd )      assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN ); -    do { -        nread = read (fd, buf, 200 ); -    } while ( nread == -1 && errno == EINTR); +    nread = _gpgme_io_read (fd, buf, 200 );      fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread );      if ( nread < 0 ) {          fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n", @@ -683,12 +652,10 @@ write_mem_data ( GpgmeData dh, int fd )       */ -    do { -        fprintf (stderr, "write_mem_data(%d): about to write %d bytes len=%d rpos=%d\n", +    fprintf (stderr, "write_mem_data(%d): about to write %d bytes len=%d rpos=%d\n",                   fd, (int)nbytes, (int)dh->len, dh->readpos ); -        nwritten = write ( fd, dh->data+dh->readpos, nbytes ); -        fprintf (stderr, "write_mem_data(%d): wrote %d bytes\n", fd, nwritten ); -    } while ( nwritten == -1 && errno == EINTR ); +    nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes ); +    fprintf (stderr, "write_mem_data(%d): wrote %d bytes\n", fd, nwritten );      if (nwritten == -1 && errno == EAGAIN )          return 0;      if ( nwritten < 1 ) { @@ -779,10 +746,8 @@ read_status ( GpgObject gpg )      } -    do {  -        nread = read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos ); -    } while (nread == -1 && errno == EINTR); - +    nread = _gpgme_io_read ( gpg->status.fd[0], +                             buffer+readpos, bufsize-readpos );      if (nread == -1)          return mk_error(Read_Error); @@ -887,10 +852,8 @@ read_colon_line ( GpgObject gpg )      } -    do {  -        nread = read ( gpg->colon.fd[0], buffer+readpos, bufsize-readpos ); -    } while (nread == -1 && errno == EINTR); - +    nread = _gpgme_io_read ( gpg->colon.fd[0], +                             buffer+readpos, bufsize-readpos );      if (nread == -1)          return mk_error(Read_Error); diff --git a/gpgme/util.c b/gpgme/util.c index 2711f6be..02107330 100644 --- a/gpgme/util.c +++ b/gpgme/util.c @@ -61,3 +61,19 @@ _gpgme_free ( void *a ) +/********************************************* + ********** missing string functions ********* + *********************************************/ + +#ifndef HAVE_STPCPY +char * +stpcpy (char *a, const char *b) +{ +    while( *b ) +	*a++ = *b++; +    *a = 0; +     +    return a; +} +#endif + diff --git a/gpgme/util.h b/gpgme/util.h index c7557732..72e3eae3 100644 --- a/gpgme/util.h +++ b/gpgme/util.h @@ -42,6 +42,13 @@ void  _gpgme_free ( void *a );  #define DIMof(type,member)   DIM(((type *)0)->member) + +#ifndef HAVE_STPCPY +char *stpcpy (char *a, const char *b); +#endif + + +  #endif /* UTIL_H */ diff --git a/gpgme/verify.c b/gpgme/verify.c index a9f14ba3..3d97fd6a 100644 --- a/gpgme/verify.c +++ b/gpgme/verify.c @@ -195,6 +195,28 @@ gpgme_op_verify_start ( GpgmeCtx c,  GpgmeData sig, GpgmeData text )  } +/** + * gpgme_op_verify: + * @c: the context + * @sig: the signature data + * @text: the signed text + * @r_stat: returns the status of the signature + *  + * Perform a signature check on the signature given in @sig. Currently it is + * assumed that this is a detached signature for the material given in @text. + * The result of this operation is returned in @r_stat which can take these + * values: + *  GPGME_SIG_STAT_NONE:  No status - should not happen + *  GPGME_SIG_STAT_GOOD:  The signature is valid  + *  GPGME_SIG_STAT_BAD:   The signature is not valid + *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a + *                        missing key + *  GPGME_SIG_STAT_NOSIG: This is not a signature + *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done. + * + * Return value: 0 on success or an errorcode if something not related to + *               the signature itself did go wrong. + **/  GpgmeError  gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,                    GpgmeSigStat *r_stat ) diff --git a/gpgme/w32-io.c b/gpgme/w32-io.c new file mode 100644 index 00000000..36bbe2a6 --- /dev/null +++ b/gpgme/w32-io.c @@ -0,0 +1,239 @@ +/* w32-io.c - W32 API I/O functions + *	Copyright (C) 2000 Werner Koch (dd9jn) + * + * This file is part of GPGME. + * + * GPGME is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#ifdef HAVE_DOSISH_SYSTEM + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <windows.h> + +#include "io.h" + +/*  + * We assume that a HANDLE can be represented by an int which should be true + * for all i386 systems (HANDLE is defined as void *) and these are the only + * systems for which Windows is available. + * Further we assume that -1 denotes an invalid handle. + */ + +#define fd_to_handle(a)  ((HANDLE)(a)) +#define handle_to_fd(a)  ((int)(a)) +#define pid_to_handle(a) ((HANDLE)(a)) +#define handle_to_pid(a) ((pid_t)(a)) + + +int +_gpgme_io_read ( int fd, void *buffer, size_t count ) +{ +    int nread = 0; +    HANDLE h = fd_to_handle (fd); + +    if ( !ReadFile ( h, buffer, count, &nread, NULL) ) { +        fprintf (stderr, "** ReadFile failed: ec=%d\n", (int)GetLastError ()); +        return -1; +    } + +    return nread; +} + + +int +_gpgme_io_write ( int fd, const void *buffer, size_t count ) +{ +    int nwritten; +    HANDLE h = fd_to_handle (fd); + +    if ( !WriteFile ( h, buffer, count, &nwritten, NULL) ) { +        fprintf (stderr, "** WriteFile failed: ec=%d\n", (int)GetLastError ()); +        return -1; +    } + +    return nwritten; +} + +int +_gpgme_io_pipe ( int filedes[2] ) +{ +    HANDLE r, w; +     +    if (!CreatePipe ( &r, &w, NULL, 0)) +        return -1; +    filedes[0] = handle_to_fd (r); +    filedes[1] = handle_to_fd (w); +    return 0 +} + +int +_gpgme_io_set_nonblocking ( int fd ) +{ +    return 0; +} + + +static char * +build_commandline ( char **argv ); +{ +    int i, n = 0; +    char *buf, *p; + +    /* FIXME: we have to quote some things because under Windows the  +     * program parses the commandline and does some unquoting */ +    for (i=0; argv[i]; i++) +        n += strlen (argv[i]) + 1; +    n += 5;                     /* "gpg " */ +    buf = p = xtrymalloc (n); +    if ( !buf ) +        return NULL; +    p = stpcpy (p, "gpg"); +    for (i = 0; argv[i]; i++) +        p = stpcpy (stpcpy (p, " "), argv[i]); + +    return buf; +} + + +pid_t +_gpgme_io_spawn ( const char *path, char **argv, +                  struct spawn_fd_item_s *fd_child_list, +                  struct spawn_fd_item_s *fd_parent_list ) +{ +    SECURITY_ATTRIBUTES sec_attr; +    PROCESS_INFORMATION pi = { +        NULL,      /* returns process handle */ +        0,         /* returns primary thread handle */ +        0,         /* returns pid */ +        0         /* returns tid */ +    }; +    STARTUPINFO si = { +        0, NULL, NULL, NULL, +        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +        NULL, NULL, NULL, NULL +    }; +    char *envblock = NULL; +    int cr_flags = CREATE_DEFAULT_ERROR_MODE +                 | GetPriorityClass (GetCurrentProcess ()); +    int rc; +    HANDLE save_stdout; +    HANDLE outputfd[2], statusfd[2], inputfd[2]; + +    sec_attr.nLength = sizeof (sec_attr); +    sec_attr.bInheritHandle = FALSE; +    sec_attr.lpSecurityDescriptor = NULL; + + +    arg_string = build_commandline ( argv ); +    if (!arg_string ) +        return -1;  + +    si.cb = sizeof (si); +    si.dwFlags = STARTF_USESTDHANDLES; +    si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); +    si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); +    si.hStdError = GetStdHandle (STD_ERROR_HANDLE); +    if (!SetHandleInformation (si.hStdOutput, +                               HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { +        fprintf (stderr, "** SHI 1 failed: ec=%d\n", (int) GetLastError ()); +    } +    if (!SetHandleInformation (si.hStdError, +                               HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { +        fprintf (stderr, "** SHI 2 failed: ec=%d\n", (int) GetLastError ()); +    } +     + +    fputs ("** CreateProcess ...\n", stderr); +    fprintf (stderr, "** args=`%s'\n", arg_string); +    fflush (stderr); +    if ( !CreateProcessA (GPG_PATH, +                          arg_string, +                          &sec_attr,     /* process security attributes */ +                          &sec_attr,     /* thread security attributes */ +                          TRUE,          /* inherit handles */ +                          cr_flags,      /* creation flags */ +                          envblock,      /* environment */ +                          NULL,          /* use current drive/directory */ +                          &si,           /* startup information */ +                          &pi            /* returns process information */ +        ) ) { +        fprintf (stderr, "** CreateProcess failed: ec=%d\n", +                 (int) GetLastError ()); +        fflush (stderr); +        xfree (arg_string); +        return -1; +    } + +    /* .dup_to is not used in the parent list */ +    for (i=0; fd_parent_list[i].fd != -1; i++ ) { +        CloseHandle ( fd_to_handle (fd_parent_list[i].fd) ); +    } + +    fprintf (stderr, "** CreateProcess ready\n"); +    fprintf (stderr, "**   hProcess=%p  hThread=%p\n", +             pi.hProcess, pi.hThread); +    fprintf (stderr, "**   dwProcessID=%d dwThreadId=%d\n", +             (int) pi.dwProcessId, (int) pi.dwThreadId); +    fflush (stderr); + +    return handle_to_pid (pi.hProcess); +} + + +int +_gpgme_io_waitpid ( pid_t pid, int hang, int *r_status, int *r_signal ) +{ +    return 0; +} + + +/* + * Select on the list of fds. + * Returns: -1 = error + *           0 = timeout or nothing to select + *          >0 = number of signaled fds + */ +int +_gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds ) +{ +    return -1; +} + + + + + + +#endif /*HAVE_DOSISH_SYSTEM*/ + + + + + + + + + diff --git a/gpgme/wait.c b/gpgme/wait.c index f54209d6..d08c021a 100644 --- a/gpgme/wait.c +++ b/gpgme/wait.c @@ -33,14 +33,7 @@  #include "context.h"  #include "ops.h"  #include "wait.h" - -#define DEBUG_SELECT_ENABLED 1 - -#if DEBUG_SELECT_ENABLED -# define DEBUG_SELECT(a) fprintf a -#else -# define DEBUG_SELECT(a) do { } while(0) -#endif +#include "io.h"  /* Fixme: implement the following stuff to make the code MT safe.   * To avoid the need to link against a specific threads lib, such @@ -52,55 +45,36 @@   *  */  #define enter_crit()    do { } while (0)  #define leave_crit()    do { } while (0) -#define lock_queue()    do { } while (0) -#define unlock_queue()  do { } while (0) +#define lock_table()    do { } while (0) +#define unlock_table()  do { } while (0) + -struct wait_queue_item_s { -    struct wait_queue_item_s *next; -    volatile int used;  +struct wait_item_s {      volatile int active;      int (*handler)(void*,pid_t,int);      void *handler_value;      pid_t pid; -    int   fd;   -    int   inbound;       /* this is an inbound data handler fd */ - +    int inbound;       /* this is an inbound data handler fd */      int exited;      int exit_status;        int exit_signal; -      GpgmeCtx ctx;  }; +static int fd_table_size; +static struct io_select_fd_s *fd_table; -static struct wait_queue_item_s wait_queue[SIZEOF_WAIT_QUEUE]; - -static int the_big_select ( void ); +static int do_select ( void ); -static void -init_wait_queue (void) -{ -    int i; -    static int initialized = 0; - -    if ( initialized )  /* FIXME: This leads to a race */ -        return; - -    lock_queue (); -    for (i=1; i < SIZEOF_WAIT_QUEUE; i++ ) -        wait_queue[i-1].next = &wait_queue[i]; -    initialized = 1; -    unlock_queue(); -} - -static struct wait_queue_item_s * +static struct wait_item_s *  queue_item_from_context ( GpgmeCtx ctx )  { -    struct wait_queue_item_s *q; +    struct wait_item_s *q; +    int i; -    for (q=wait_queue; q; q = q->next) { -        if ( q->used && q->ctx == ctx ) +    for (i=0; i < fd_table_size; i++ ) { +        if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->ctx == ctx )              return q;      }      return NULL; @@ -108,12 +82,14 @@ queue_item_from_context ( GpgmeCtx ctx )  static void -propagate_term_results ( const struct wait_queue_item_s *first_q ) +propagate_term_results ( const struct wait_item_s *first_q )  { -    struct wait_queue_item_s *q; +    struct wait_item_s *q; +    int i; -    for (q=wait_queue; q; q = q->next) { -        if ( q->used && q != first_q && !q->exited +    for (i=0; i < fd_table_size; i++ ) { +        if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) +             && q != first_q && !q->exited               && q->pid == first_q->pid  ) {              q->exited = first_q->exited;              q->exit_status = first_q->exit_status; @@ -125,11 +101,12 @@ propagate_term_results ( const struct wait_queue_item_s *first_q )  static int  count_active_fds ( pid_t pid )  { -    struct wait_queue_item_s *q; -    int count = 0; +    struct wait_item_s *q; +    int i, count = 0; -    for (q=wait_queue; q; q = q->next) { -        if ( q->used && q->active && q->pid == pid  )  +    for (i=0; i < fd_table_size; i++ ) { +        if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) +             && q->active && q->pid == pid  )               count++;      }      return count; @@ -140,14 +117,15 @@ count_active_fds ( pid_t pid )  static void  remove_process ( pid_t pid )  { -    struct wait_queue_item_s *q; -     -    for (q=wait_queue; q; q = q->next) { -        if ( q->used ) { -            close (q->fd); -            q->handler = NULL; -            q->ctx = NULL; -            q->used = 0; +    struct wait_item_s *q; +    int i; + +    for (i=0; i < fd_table_size; i++ ) { +        if (fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->pid == pid ) { +            xfree (q); +            fd_table[i].opaque = NULL; +            close (fd_table[i].fd); +            fd_table[i].fd = -1;          }      }  } @@ -176,19 +154,16 @@ gpgme_wait ( GpgmeCtx c, int hang )  GpgmeCtx   _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )  { -    struct wait_queue_item_s *q; +    struct wait_item_s *q; -    init_wait_queue ();      do { -        int did_work = the_big_select(); +        int did_work = do_select();          if ( cond && *cond )              hang = 0;          if ( !did_work ) { -            int status; - -            /* We did no read/write - see whether this process is still +            /* We did no read/write - see whether the process is still               * alive */              assert (c); /* !c is not yet implemented */              q = queue_item_from_context ( c ); @@ -196,19 +171,9 @@ _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )              if (q->exited)                  ; -            else if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) { +            else if ( _gpgme_io_waitpid (q->pid, 0, +                                          &q->exit_status, &q->exit_signal)){                  q->exited = 1;      -                if ( WIFSIGNALED (status) ) { -                    q->exit_status = 4; /* Need some value here */ -                    q->exit_signal = WTERMSIG (status); -                } -                else if ( WIFEXITED (status) ) { -                    q->exit_status = WEXITSTATUS (status); -                } -                else { -                    q->exited++; -                    q->exit_status = 4; -                }                  propagate_term_results (q);              } @@ -234,99 +199,34 @@ _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )   * gpgs.  A future version might provide a facility to delegate   * those selects to the GDK select stuff.   * This function must be called only by one thread!! - * FIXME: The data structures and  algorithms are stupid.   * Returns: 0 = nothing to run   *          1 = did run something    */  static int -the_big_select ( void ) +do_select ( void )  { -    static fd_set readfds; -    static fd_set writefds; -    struct wait_queue_item_s *q; -    int max_fd, n; -    struct timeval timeout = { 1, 0 }; /* Use a one second timeout */ -     -    FD_ZERO ( &readfds ); -    FD_ZERO ( &writefds ); -    max_fd = 0; - +    struct wait_item_s *q; +    int i, n; -    DEBUG_SELECT ((stderr, "gpgme:select on [ ")); -    lock_queue (); -    for ( q = wait_queue; q; q = q->next ) { -        if ( q->used && q->active ) { -            if (q->inbound) { -                assert ( !FD_ISSET ( q->fd, &readfds ) ); -                FD_SET ( q->fd, &readfds ); -                DEBUG_SELECT ((stderr, "r%d ", q->fd )); -            } -            else { -                assert ( !FD_ISSET ( q->fd, &writefds ) ); -                FD_SET ( q->fd, &writefds ); -                DEBUG_SELECT ((stderr, "w%d ", q->fd )); -            } -            if ( q->fd > max_fd ) -                max_fd = q->fd; -          } -    } -    unlock_queue (); -    DEBUG_SELECT ((stderr, "]\n" )); - -    n = select ( max_fd+1, &readfds, &writefds, NULL, &timeout ); -    if ( n <= 0 ) { -        if ( n && errno != EINTR ) { -            fprintf (stderr, "the_big_select: select failed: %s\n", -                     strerror (errno) ); -        } -        return 0; -    } - -#if DEBUG_SELECT_ENABLED -    {  -        int i; - -        fprintf (stderr, "gpgme:select OK [ " ); -        for (i=0; i <= max_fd; i++ ) { -            if (FD_ISSET (i, &readfds) ) -                fprintf (stderr, "r%d ", i ); -            if (FD_ISSET (i, &writefds) ) -                fprintf (stderr, "w%d ", i ); -        } -        fprintf (stderr, "]\n" ); -    } -#endif - -    /* something has to be done.  Go over the queue and call -     * the handlers */ - restart: -    while ( n ) { -        lock_queue (); -        for ( q = wait_queue; q; q = q->next ) { -            if ( q->used && q->active && q->inbound  -                 && FD_ISSET (q->fd, &readfds ) ) { -                FD_CLR (q->fd, &readfds ); -                assert (n); -                n--; -                unlock_queue (); -                if ( q->handler (q->handler_value, q->pid, q->fd ) ) -                    q->active = 0; -                goto restart; -            } -            if ( q->used && q->active && !q->inbound -                 && FD_ISSET (q->fd, &writefds ) ) { -                FD_CLR (q->fd, &writefds ); -                assert (n); -                n--; -                unlock_queue (); -                if ( q->handler (q->handler_value, q->pid, q->fd ) ) -                    q->active = 0; -                goto restart; +    n = _gpgme_io_select ( fd_table, fd_table_size ); +    if ( n <= 0 )  +        return 0; /* error or timeout */ + +    for (i=0; i < fd_table_size && n; i++ ) { +        if ( fd_table[i].fd != -1 && fd_table[i].signaled ) { +            q = fd_table[i].opaque; +            assert (n); +            n--; +            if ( q->active && q->handler (q->handler_value, +                                          q->pid, fd_table[i].fd ) ) { +                q->active = 0; +                fd_table[i].for_read = 0; +                fd_table[i].for_write = 0;              }          } -        unlock_queue ();      } +          return 1;  } @@ -342,35 +242,53 @@ _gpgme_register_pipe_handler( void *opaque,                                pid_t pid, int fd, int inbound )  {      GpgmeCtx ctx = opaque; -    struct wait_queue_item_s *q; +    struct wait_item_s *q; +    int i; -    init_wait_queue();      assert (opaque);      assert (handler); -    lock_queue (); -    for ( q = wait_queue; q; q = q->next ) { -        if ( !q->used ) { -            q->used = 1; -            q->active = 0; -            break; -        } -    } -    unlock_queue (); -    if ( !q )  -        return mk_error (Too_Many_Procs); - -    q->fd = fd; +    q = xtrycalloc ( 1, sizeof *q ); +    if ( !q ) +        return mk_error (Out_Of_Core);      q->inbound = inbound;      q->handler = handler;      q->handler_value = handler_value;      q->pid = pid;      q->ctx = ctx; -     -    /* and enable this entry for the next select */ -    q->exited = 0;      q->active = 1; -    return 0; + +    lock_table (); + again:   +    for (i=0; i < fd_table_size; i++ ) { +        if ( fd_table[i].fd == -1 ) { +            fd_table[i].fd = fd; +            fd_table[i].for_read = inbound;     +            fd_table[i].for_write = !inbound;     +            fd_table[i].signaled = 0; +            fd_table[i].opaque = q; +            unlock_table (); +            return 0; +        } +    } +    if ( fd_table_size < 50 ) { +        /* FIXME: We have to wait until there are no other readers of the  +         * table, i.e that the io_select is not active in another thread */ +        struct io_select_fd_s *tmp; + +        tmp = xtryrealloc ( fd_table, (fd_table_size + 10) * sizeof *tmp ); +        if ( tmp ) { +            for (i=0; i < 10; i++ ) +                tmp[fd_table_size+i].fd = -1; +            fd_table_size += i; +            fd_table = tmp; +            goto again; +        } +    } + +    unlock_table (); +    xfree (q); +    return mk_error (Too_Many_Procs);  } | 
