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

Generated by: LCOV version 1.12