LCOV - code coverage report
Current view: top level - src - assuan-pipe-connect.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 107 137 78.1 %
Date: 2015-11-05 17:06:03 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* assuan-pipe-connect.c - Establish a pipe connection (client)
       2             :    Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010,
       3             :                  2011 Free Software Foundation, Inc.
       4             : 
       5             :    This file is part of Assuan.
       6             : 
       7             :    Assuan is free software; you can redistribute it and/or modify it
       8             :    under the terms of the GNU Lesser General Public License as
       9             :    published by the Free Software Foundation; either version 2.1 of
      10             :    the License, or (at your option) any later version.
      11             : 
      12             :    Assuan is distributed in the hope that it will be useful, but
      13             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :    Lesser General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU Lesser General Public
      18             :    License along with this program; if not, see <http://www.gnu.org/licenses/>.
      19             :  */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include <stdlib.h>
      26             : #include <stdio.h>
      27             : #include <string.h>
      28             : /* On Windows systems signal.h is not needed and even not supported on
      29             :    WindowsCE. */
      30             : #ifndef HAVE_DOSISH_SYSTEM
      31             : # include <signal.h>
      32             : #endif
      33             : #ifdef HAVE_UNISTD_H
      34             : # include <unistd.h>
      35             : #endif
      36             : #include <errno.h>
      37             : #ifdef HAVE_FCNTL_H
      38             : #include <fcntl.h>
      39             : #endif
      40             : #ifdef HAVE_SYS_TYPES_H
      41             : # include <sys/types.h>
      42             : #endif
      43             : #ifndef HAVE_W32_SYSTEM
      44             : # include <sys/wait.h>
      45             : #else
      46             : # ifdef HAVE_WINSOCK2_H
      47             : #  include <winsock2.h>
      48             : # endif
      49             : # include <windows.h>
      50             : #endif
      51             : 
      52             : #include "assuan-defs.h"
      53             : #include "debug.h"
      54             : 
      55             : /* Hacks for Slowaris.  */
      56             : #ifndef PF_LOCAL
      57             : # ifdef PF_UNIX
      58             : #  define PF_LOCAL PF_UNIX
      59             : # else
      60             : #  define PF_LOCAL AF_UNIX
      61             : # endif
      62             : #endif
      63             : #ifndef AF_LOCAL
      64             : # define AF_LOCAL AF_UNIX
      65             : #endif
      66             : 
      67             : 
      68             : #ifdef _POSIX_OPEN_MAX
      69             : #define MAX_OPEN_FDS _POSIX_OPEN_MAX
      70             : #else
      71             : #define MAX_OPEN_FDS 20
      72             : #endif
      73             : 
      74             : 
      75             : /* This should be called to make sure that SIGPIPE gets ignored.  */
      76             : static void
      77           2 : fix_signals (void)
      78             : {
      79             : #ifndef HAVE_DOSISH_SYSTEM  /* No SIGPIPE for these systems.  */
      80             :   static int fixed_signals;
      81             : 
      82           2 :   if (!fixed_signals)
      83             :     {
      84             :       struct sigaction act;
      85             : 
      86           2 :       sigaction (SIGPIPE, NULL, &act);
      87           2 :       if (act.sa_handler == SIG_DFL)
      88             :         {
      89           2 :           act.sa_handler = SIG_IGN;
      90           2 :           sigemptyset (&act.sa_mask);
      91           2 :           act.sa_flags = 0;
      92           2 :           sigaction (SIGPIPE, &act, NULL);
      93             :         }
      94           2 :       fixed_signals = 1;
      95             :       /* FIXME: This is not MT safe */
      96             :     }
      97             : #endif /*HAVE_DOSISH_SYSTEM*/
      98           2 : }
      99             : 
     100             : 
     101             : /* Helper for pipe_connect. */
     102             : static gpg_error_t
     103           2 : initial_handshake (assuan_context_t ctx)
     104             : {
     105             :   assuan_response_t response;
     106             :   int off;
     107             :   gpg_error_t err;
     108             : 
     109           2 :   err = _assuan_read_from_server (ctx, &response, &off, 0);
     110           2 :   if (err)
     111           0 :     TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
     112             :             "can't connect server: %s", gpg_strerror (err));
     113           2 :   else if (response != ASSUAN_RESPONSE_OK)
     114             :     {
     115           0 :       TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
     116             :               "can't connect server: `%s'", ctx->inbound.line);
     117           0 :       err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED);
     118             :     }
     119             : 
     120           2 :   return err;
     121             : }
     122             : 
     123             : 
     124             : struct at_pipe_fork
     125             : {
     126             :   void (*user_atfork) (void *opaque, int reserved);
     127             :   void *user_atforkvalue;
     128             :   pid_t parent_pid;
     129             : };
     130             : 
     131             : 
     132             : static void
     133           1 : at_pipe_fork_cb (void *opaque, int reserved)
     134             : {
     135           1 :   struct at_pipe_fork *atp = opaque;
     136             : 
     137           1 :   if (atp->user_atfork)
     138           0 :     atp->user_atfork (atp->user_atforkvalue, reserved);
     139             : 
     140             : #ifndef HAVE_W32_SYSTEM
     141             :   {
     142             :     char mypidstr[50];
     143             : 
     144             :     /* We store our parents pid in the environment so that the execed
     145             :        assuan server is able to read the actual pid of the client.
     146             :        The server can't use getppid because it might have been double
     147             :        forked before the assuan server has been initialized. */
     148           1 :     sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid);
     149           1 :     setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
     150             : 
     151             :     /* Make sure that we never pass a connection fd variable when
     152             :        using a simple pipe.  */
     153           1 :     unsetenv ("_assuan_connection_fd");
     154             :   }
     155             : #endif
     156           1 : }
     157             : 
     158             : 
     159             : static gpg_error_t
     160           1 : pipe_connect (assuan_context_t ctx,
     161             :               const char *name, const char **argv,
     162             :               assuan_fd_t *fd_child_list,
     163             :               void (*atfork) (void *opaque, int reserved),
     164             :               void *atforkvalue, unsigned int flags)
     165             : {
     166             :   gpg_error_t rc;
     167             :   assuan_fd_t rp[2];
     168             :   assuan_fd_t wp[2];
     169             :   pid_t pid;
     170             :   int res;
     171             :   struct at_pipe_fork atp;
     172             :   unsigned int spawn_flags;
     173             : 
     174           1 :   atp.user_atfork = atfork;
     175           1 :   atp.user_atforkvalue = atforkvalue;
     176           1 :   atp.parent_pid = getpid ();
     177             : 
     178           1 :   if (!ctx || !name || !argv || !argv[0])
     179           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     180             : 
     181           1 :   if (! ctx->flags.no_fixsignals)
     182           1 :     fix_signals ();
     183             : 
     184           1 :   if (_assuan_pipe (ctx, rp, 1) < 0)
     185           0 :     return _assuan_error (ctx, gpg_err_code_from_syserror ());
     186             : 
     187           1 :   if (_assuan_pipe (ctx, wp, 0) < 0)
     188             :     {
     189           0 :       _assuan_close (ctx, rp[0]);
     190           0 :       _assuan_close_inheritable (ctx, rp[1]);
     191           0 :       return _assuan_error (ctx, gpg_err_code_from_syserror ());
     192             :     }
     193             : 
     194           1 :   spawn_flags = 0;
     195           1 :   if (flags & ASSUAN_PIPE_CONNECT_DETACHED)
     196           0 :     spawn_flags |= ASSUAN_SPAWN_DETACHED;
     197             : 
     198             :   /* FIXME: Use atfork handler that closes child fds on Unix.  */
     199           1 :   res = _assuan_spawn (ctx, &pid, name, argv, wp[0], rp[1],
     200             :                        fd_child_list, at_pipe_fork_cb, &atp, spawn_flags);
     201           1 :   if (res < 0)
     202             :     {
     203           0 :       rc = gpg_err_code_from_syserror ();
     204           0 :       _assuan_close (ctx, rp[0]);
     205           0 :       _assuan_close_inheritable (ctx, rp[1]);
     206           0 :       _assuan_close_inheritable (ctx, wp[0]);
     207           0 :       _assuan_close (ctx, wp[1]);
     208           0 :       return _assuan_error (ctx, rc);
     209             :     }
     210             : 
     211             :   /* Close the stdin/stdout child fds in the parent.  */
     212           1 :   _assuan_close_inheritable (ctx, rp[1]);
     213           1 :   _assuan_close_inheritable (ctx, wp[0]);
     214             : 
     215           1 :   ctx->engine.release = _assuan_client_release;
     216           1 :   ctx->engine.readfnc = _assuan_simple_read;
     217           1 :   ctx->engine.writefnc = _assuan_simple_write;
     218           1 :   ctx->engine.sendfd = NULL;
     219           1 :   ctx->engine.receivefd = NULL;
     220           1 :   ctx->finish_handler = _assuan_client_finish;
     221           1 :   ctx->max_accepts = 1;
     222           1 :   ctx->accept_handler = NULL;
     223           1 :   ctx->inbound.fd  = rp[0];  /* Our inbound is read end of read pipe. */
     224           1 :   ctx->outbound.fd = wp[1];  /* Our outbound is write end of write pipe. */
     225           1 :   ctx->pid = pid;
     226             : 
     227           1 :   rc = initial_handshake (ctx);
     228           1 :   if (rc)
     229           0 :     _assuan_reset (ctx);
     230           1 :   return rc;
     231             : }
     232             : 
     233             : 
     234             : /* FIXME: For socketpair_connect, use spawn function and add atfork
     235             :    handler to do the right thing.  Instead of stdin and stdout, we
     236             :    extend the fd_child_list by fds[1].  */
     237             : 
     238             : #ifndef HAVE_W32_SYSTEM
     239             : struct at_socketpair_fork
     240             : {
     241             :   assuan_fd_t peer_fd;
     242             :   void (*user_atfork) (void *opaque, int reserved);
     243             :   void *user_atforkvalue;
     244             :   pid_t parent_pid;
     245             : };
     246             : 
     247             : 
     248             : static void
     249           1 : at_socketpair_fork_cb (void *opaque, int reserved)
     250             : {
     251           1 :   struct at_socketpair_fork *atp = opaque;
     252             : 
     253           1 :   if (atp->user_atfork)
     254           0 :     atp->user_atfork (atp->user_atforkvalue, reserved);
     255             : 
     256             : #ifndef HAVE_W32_SYSTEM
     257             :   {
     258             :     char mypidstr[50];
     259             : 
     260             :     /* We store our parents pid in the environment so that the execed
     261             :        assuan server is able to read the actual pid of the client.
     262             :        The server can't use getppid because it might have been double
     263             :        forked before the assuan server has been initialized. */
     264           1 :     sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid);
     265           1 :     setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
     266             : 
     267             :     /* Now set the environment variable used to convey the
     268             :        connection's file descriptor.  */
     269           1 :     sprintf (mypidstr, "%d", atp->peer_fd);
     270           1 :     if (setenv ("_assuan_connection_fd", mypidstr, 1))
     271           0 :       _exit (4);
     272             :   }
     273             : #endif
     274           1 : }
     275             : 
     276             : 
     277             : /* This function is similar to pipe_connect but uses a socketpair and
     278             :    sets the I/O up to use sendmsg/recvmsg. */
     279             : static gpg_error_t
     280           1 : socketpair_connect (assuan_context_t ctx,
     281             :                     const char *name, const char **argv,
     282             :                     assuan_fd_t *fd_child_list,
     283             :                     void (*atfork) (void *opaque, int reserved),
     284             :                     void *atforkvalue)
     285             : {
     286             :   gpg_error_t err;
     287             :   int idx;
     288             :   int fds[2];
     289             :   char mypidstr[50];
     290             :   pid_t pid;
     291           1 :   int *child_fds = NULL;
     292           1 :   int child_fds_cnt = 0;
     293             :   struct at_socketpair_fork atp;
     294             :   int rc;
     295             : 
     296           1 :   TRACE_BEG3 (ctx, ASSUAN_LOG_CTX, "socketpair_connect", ctx,
     297             :               "name=%s,atfork=%p,atforkvalue=%p", name ? name : "(null)",
     298             :               atfork, atforkvalue);
     299             : 
     300           1 :   atp.user_atfork = atfork;
     301           1 :   atp.user_atforkvalue = atforkvalue;
     302           1 :   atp.parent_pid = getpid ();
     303             : 
     304           1 :   if (!ctx
     305           1 :       || (name && (!argv || !argv[0]))
     306           1 :       || (!name && !argv))
     307           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     308             : 
     309           1 :   if (! ctx->flags.no_fixsignals)
     310           1 :     fix_signals ();
     311             : 
     312           1 :   sprintf (mypidstr, "%lu", (unsigned long)getpid ());
     313             : 
     314           1 :   if (fd_child_list)
     315           3 :     while (fd_child_list[child_fds_cnt] != ASSUAN_INVALID_FD)
     316           1 :       child_fds_cnt++;
     317           1 :   child_fds = _assuan_malloc (ctx, (child_fds_cnt + 2) * sizeof (int));
     318           1 :   if (! child_fds)
     319           0 :     return TRACE_ERR (gpg_err_code_from_syserror ());
     320           1 :   child_fds[1] = ASSUAN_INVALID_FD;
     321           1 :   if (fd_child_list)
     322           1 :     memcpy (&child_fds[1], fd_child_list, (child_fds_cnt + 1) * sizeof (int));
     323             : 
     324           1 :   if (_assuan_socketpair (ctx, AF_LOCAL, SOCK_STREAM, 0, fds))
     325             :     {
     326           0 :       TRACE_LOG1 ("socketpair failed: %s", strerror (errno));
     327           0 :       _assuan_free (ctx, child_fds);
     328           0 :       return TRACE_ERR (GPG_ERR_ASS_GENERAL);
     329             :     }
     330           1 :   atp.peer_fd = fds[1];
     331           1 :   child_fds[0] = fds[1];
     332             : 
     333           1 :   rc = _assuan_spawn (ctx, &pid, name, argv, ASSUAN_INVALID_FD,
     334             :                       ASSUAN_INVALID_FD, child_fds, at_socketpair_fork_cb,
     335             :                       &atp, 0);
     336           2 :   if (rc < 0)
     337             :     {
     338           0 :       err = gpg_err_code_from_syserror ();
     339           0 :       _assuan_close (ctx, fds[0]);
     340           0 :       _assuan_close (ctx, fds[1]);
     341           0 :       _assuan_free (ctx, child_fds);
     342           0 :       return TRACE_ERR (err);
     343             :     }
     344             : 
     345             :   /* For W32, the user needs to know the server-local names of the
     346             :      inherited handles.  Return them here.  Note that the translation
     347             :      of the peer socketpair fd (fd_child_list[0]) must be done by the
     348             :      wrapper program based on the environment variable
     349             :      _assuan_connection_fd.  */
     350           2 :   if (fd_child_list)
     351             :     {
     352           4 :       for (idx = 0; fd_child_list[idx] != -1; idx++)
     353             :         /* We add 1 to skip over the socketpair end.  */
     354           2 :         fd_child_list[idx] = child_fds[idx + 1];
     355             :     }
     356             : 
     357             :   /* If this is the server child process, exit early.  */
     358           2 :   if (! name && (*argv)[0] == 's')
     359             :     {
     360           1 :       _assuan_free (ctx, child_fds);
     361           1 :       _assuan_close (ctx, fds[0]);
     362           1 :       return 0;
     363             :     }
     364             : 
     365           1 :   _assuan_close (ctx, fds[1]);
     366             : 
     367           1 :   ctx->engine.release = _assuan_client_release;
     368           1 :   ctx->finish_handler = _assuan_client_finish;
     369           1 :   ctx->max_accepts = 1;
     370           1 :   ctx->inbound.fd  = fds[0];
     371           1 :   ctx->outbound.fd = fds[0];
     372           1 :   _assuan_init_uds_io (ctx);
     373             : 
     374           1 :   err = initial_handshake (ctx);
     375           1 :   if (err)
     376           0 :     _assuan_reset (ctx);
     377           1 :   return err;
     378             : }
     379             : #endif /*!HAVE_W32_SYSTEM*/
     380             : 
     381             : 
     382             : /* Connect to a server over a full-duplex socket (i.e. created by
     383             :    socketpair), creating the assuan context and returning it in CTX.
     384             :    The server filename is NAME, the argument vector in ARGV.
     385             :    FD_CHILD_LIST is a -1 terminated list of file descriptors not to
     386             :    close in the child.  ATFORK is called in the child right after the
     387             :    fork; ATFORKVALUE is passed as the first argument and 0 is passed
     388             :    as the second argument. The ATFORK function should only act if the
     389             :    second value is 0.
     390             : 
     391             :    FLAGS is a bit vector and controls how the function acts:
     392             :    Bit 0: If cleared a simple pipe based server is expected and the
     393             :           function behaves similar to `assuan_pipe_connect'.
     394             : 
     395             :           If set a server based on full-duplex pipes is expected. Such
     396             :           pipes are usually created using the `socketpair' function.
     397             :           It also enables features only available with such servers.
     398             : 
     399             :    Bit 7: If set and there is a need to start the server it will be
     400             :           started as a background process.  This flag is useful under
     401             :           W32 systems, so that no new console is created and pops up a
     402             :           console window when starting the server
     403             : 
     404             : 
     405             :    If NAME is NULL, no exec is done but the same process is continued.
     406             :    However all file descriptors are closed and some special
     407             :    environment variables are set. To let the caller detect whether the
     408             :    child or the parent continues, the child returns "client" or
     409             :    "server" in *ARGV (but it is sufficient to check only the first
     410             :    character).  This feature is only available on POSIX platforms.  */
     411             : gpg_error_t
     412           2 : assuan_pipe_connect (assuan_context_t ctx,
     413             :                      const char *name, const char *argv[],
     414             :                      assuan_fd_t *fd_child_list,
     415             :                      void (*atfork) (void *opaque, int reserved),
     416             :                      void *atforkvalue, unsigned int flags)
     417             : {
     418           2 :   TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_pipe_connect", ctx,
     419             :           "name=%s, flags=0x%x", name ? name : "(null)", flags);
     420             : 
     421           2 :   if (flags & ASSUAN_PIPE_CONNECT_FDPASSING)
     422             :     {
     423             : #ifdef HAVE_W32_SYSTEM
     424             :       return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
     425             : #else
     426           1 :       return socketpair_connect (ctx, name, argv, fd_child_list,
     427             :                                  atfork, atforkvalue);
     428             : #endif
     429             :     }
     430             :   else
     431           1 :     return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue,
     432             :                          flags);
     433             : }
     434             : 

Generated by: LCOV version 1.11