LCOV - code coverage report
Current view: top level - src - engine-spawn.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 166 3.6 %
Date: 2016-12-01 18:45:36 Functions: 3 15 20.0 %

          Line data    Source code
       1             : /* engine-spawn.c - Run an arbitrary program
       2             :    Copyright (C) 2014 g10 Code GmbH
       3             : 
       4             :    This file is part of GPGME.
       5             : 
       6             :    GPGME is free software; you can redistribute it and/or modify it
       7             :    under the terms of the GNU Lesser General Public License as
       8             :    published by the Free Software Foundation; either version 2.1 of
       9             :    the License, or (at your option) any later version.
      10             : 
      11             :    GPGME is distributed in the hope that it will be useful, but
      12             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :    Lesser General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU Lesser General Public
      17             :    License along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #if HAVE_CONFIG_H
      21             : #include <config.h>
      22             : #endif
      23             : #include <stdio.h>
      24             : #include <stdlib.h>
      25             : #include <string.h>
      26             : #include <assert.h>
      27             : #include <errno.h>
      28             : #ifdef HAVE_UNISTD_H
      29             : # include <unistd.h>
      30             : #endif
      31             : #ifdef HAVE_LOCALE_H
      32             : #include <locale.h>
      33             : #endif
      34             : 
      35             : #include "gpgme.h"
      36             : #include "util.h"
      37             : #include "ops.h"
      38             : #include "wait.h"
      39             : #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
      40             : #include "priv-io.h"
      41             : #include "sema.h"
      42             : #include "debug.h"
      43             : 
      44             : #include "engine-backend.h"
      45             : 
      46             : 
      47             : /* This type is used to build a list of data sources/sinks.  */
      48             : struct datalist_s
      49             : {
      50             :   struct datalist_s *next;
      51             :   gpgme_data_t data;  /* The data object. */
      52             :   int inbound;        /* True if this is used for reading from the peer.  */
      53             :   int dup_to;         /* The fd used by the peer.  */
      54             : };
      55             : 
      56             : 
      57             : struct fd_data_map_s
      58             : {
      59             :   gpgme_data_t data;
      60             :   int inbound;  /* True if this is used for reading from the peer. */
      61             :   int dup_to;   /* Dup the fd to that one.  */
      62             :   int fd;       /* The fd to use.  */
      63             :   int peer_fd;  /* The other side of the pipe. */
      64             :   void *tag;    /* Tag used by the I/O callback.  */
      65             : };
      66             : 
      67             : 
      68             : struct engine_spawn
      69             : {
      70             :   struct datalist_s *arglist;
      71             :   struct datalist_s **argtail;
      72             : 
      73             :   struct fd_data_map_s *fd_data_map;
      74             : 
      75             :   struct gpgme_io_cbs io_cbs;
      76             : };
      77             : typedef struct engine_spawn *engine_spawn_t;
      78             : 
      79             : 
      80             : static void engspawn_io_event (void *engine,
      81             :                                gpgme_event_io_t type, void *type_data);
      82             : static gpgme_error_t engspawn_cancel (void *engine);
      83             : 
      84             : 
      85             : 
      86             : static void
      87           0 : close_notify_handler (int fd, void *opaque)
      88             : {
      89           0 :   engine_spawn_t esp = opaque;
      90             :   int i;
      91             : 
      92           0 :   assert (fd != -1);
      93             : 
      94           0 :   if (esp->fd_data_map)
      95             :     {
      96           0 :       for (i = 0; esp->fd_data_map[i].data; i++)
      97             :         {
      98           0 :           if (esp->fd_data_map[i].fd == fd)
      99             :             {
     100           0 :               if (esp->fd_data_map[i].tag)
     101           0 :                 (*esp->io_cbs.remove) (esp->fd_data_map[i].tag);
     102           0 :               esp->fd_data_map[i].fd = -1;
     103           0 :               break;
     104             :             }
     105           0 :           if (esp->fd_data_map[i].peer_fd == fd)
     106             :             {
     107           0 :               esp->fd_data_map[i].peer_fd = -1;
     108           0 :               break;
     109             :             }
     110             :         }
     111             :     }
     112           0 : }
     113             : 
     114             : 
     115             : static gpgme_error_t
     116           0 : add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound)
     117             : {
     118             :   struct datalist_s *a;
     119             : 
     120           0 :   assert (esp);
     121           0 :   assert (data);
     122             : 
     123           0 :   a = malloc (sizeof *a);
     124           0 :   if (!a)
     125           0 :     return gpg_error_from_syserror ();
     126           0 :   a->next = NULL;
     127           0 :   a->data = data;
     128           0 :   a->inbound = inbound;
     129           0 :   a->dup_to = dup_to;
     130           0 :   *esp->argtail = a;
     131           0 :   esp->argtail = &a->next;
     132           0 :   return 0;
     133             : }
     134             : 
     135             : 
     136             : static void
     137           0 : free_fd_data_map (struct fd_data_map_s *fd_data_map)
     138             : {
     139             :   int i;
     140             : 
     141           0 :   if (!fd_data_map)
     142           0 :     return;
     143             : 
     144           0 :   for (i = 0; fd_data_map[i].data; i++)
     145             :     {
     146           0 :       if (fd_data_map[i].fd != -1)
     147           0 :         _gpgme_io_close (fd_data_map[i].fd);
     148           0 :       if (fd_data_map[i].peer_fd != -1)
     149           0 :         _gpgme_io_close (fd_data_map[i].peer_fd);
     150             :       /* Don't release data because this is only a reference.  */
     151             :     }
     152           0 :   free (fd_data_map);
     153             : }
     154             : 
     155             : 
     156             : static gpgme_error_t
     157           0 : build_fd_data_map (engine_spawn_t esp)
     158             : {
     159             :   struct datalist_s *a;
     160             :   size_t datac;
     161             :   int fds[2];
     162             : 
     163           0 :   for (datac = 0, a = esp->arglist; a; a = a->next)
     164           0 :     if (a->data)
     165           0 :       datac++;
     166             : 
     167           0 :   free_fd_data_map (esp->fd_data_map);
     168           0 :   esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map);
     169           0 :   if (!esp->fd_data_map)
     170           0 :     return gpg_error_from_syserror ();
     171             : 
     172           0 :   for (datac = 0, a = esp->arglist; a; a = a->next)
     173             :     {
     174           0 :       assert (a->data);
     175             : 
     176           0 :       if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1)
     177             :         {
     178           0 :           free (esp->fd_data_map);
     179           0 :           esp->fd_data_map = NULL;
     180           0 :           return gpg_error_from_syserror ();
     181             :         }
     182           0 :       if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp)
     183           0 :           || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp))
     184             :         {
     185             :           /* FIXME: Need error cleanup.  */
     186           0 :           return gpg_error (GPG_ERR_GENERAL);
     187             :         }
     188             : 
     189           0 :       esp->fd_data_map[datac].inbound = a->inbound;
     190           0 :       if (a->inbound)
     191             :         {
     192           0 :           esp->fd_data_map[datac].fd       = fds[0];
     193           0 :           esp->fd_data_map[datac].peer_fd  = fds[1];
     194             :         }
     195             :       else
     196             :         {
     197           0 :           esp->fd_data_map[datac].fd       = fds[1];
     198           0 :           esp->fd_data_map[datac].peer_fd  = fds[0];
     199             :         }
     200           0 :       esp->fd_data_map[datac].data    = a->data;
     201           0 :       esp->fd_data_map[datac].dup_to  = a->dup_to;
     202           0 :       datac++;
     203             :     }
     204             : 
     205           0 :   return 0;
     206             : }
     207             : 
     208             : 
     209             : static gpgme_error_t
     210           0 : add_io_cb (engine_spawn_t esp, int fd, int dir, gpgme_io_cb_t handler,
     211             :            void *data, void **tag)
     212             : {
     213             :   gpgme_error_t err;
     214             : 
     215           0 :   err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag);
     216           0 :   if (err)
     217           0 :     return err;
     218           0 :   if (!dir) /* Fixme: Kludge around poll() problem.  */
     219           0 :     err = _gpgme_io_set_nonblocking (fd);
     220           0 :   return err;
     221             : }
     222             : 
     223             : 
     224             : static gpgme_error_t
     225           0 : engspawn_start (engine_spawn_t esp, const char *file, const char *argv[],
     226             :                 unsigned int flags)
     227             : {
     228             :   gpgme_error_t err;
     229             :   int i, n;
     230             :   int status;
     231             :   struct spawn_fd_item_s *fd_list;
     232             :   pid_t pid;
     233             :   unsigned int spflags;
     234           0 :   const char *save_argv0 = NULL;
     235             : 
     236           0 :   if (!esp || !file || !argv || !argv[0])
     237           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     238             : 
     239           0 :   spflags = 0;
     240           0 :   if ((flags & GPGME_SPAWN_DETACHED))
     241           0 :     spflags |= IOSPAWN_FLAG_DETACHED;
     242           0 :   if ((flags & GPGME_SPAWN_ALLOW_SET_FG))
     243           0 :     spflags |= IOSPAWN_FLAG_ALLOW_SET_FG;
     244             : 
     245             : 
     246           0 :   err = build_fd_data_map (esp);
     247           0 :   if (err)
     248           0 :     return err;
     249             : 
     250           0 :   n = 0;
     251           0 :   for (i = 0; esp->fd_data_map[i].data; i++)
     252           0 :     n++;
     253           0 :   fd_list = calloc (n+1, sizeof *fd_list);
     254           0 :   if (!fd_list)
     255           0 :     return gpg_error_from_syserror ();
     256             : 
     257             :   /* Build the fd list for the child.  */
     258           0 :   n = 0;
     259           0 :   for (i = 0; esp->fd_data_map[i].data; i++)
     260             :     {
     261           0 :       fd_list[n].fd = esp->fd_data_map[i].peer_fd;
     262           0 :       fd_list[n].dup_to = esp->fd_data_map[i].dup_to;
     263           0 :       n++;
     264             :     }
     265           0 :   fd_list[n].fd = -1;
     266           0 :   fd_list[n].dup_to = -1;
     267             : 
     268           0 :   if (argv[0] && !*argv[0])
     269             :     {
     270           0 :       save_argv0 = argv[0];
     271           0 :       argv[0] = _gpgme_get_basename (file);
     272             :     }
     273           0 :   status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
     274             :                             fd_list, NULL, NULL, &pid);
     275           0 :   if (save_argv0)
     276           0 :     argv[0] = save_argv0;
     277           0 :   free (fd_list);
     278           0 :   if (status == -1)
     279           0 :     return gpg_error_from_syserror ();
     280             : 
     281           0 :   for (i = 0; esp->fd_data_map[i].data; i++)
     282             :     {
     283           0 :       err = add_io_cb (esp, esp->fd_data_map[i].fd,
     284           0 :                        esp->fd_data_map[i].inbound,
     285           0 :                        esp->fd_data_map[i].inbound
     286             :                        ? _gpgme_data_inbound_handler
     287             :                        : _gpgme_data_outbound_handler,
     288           0 :                        esp->fd_data_map[i].data, &esp->fd_data_map[i].tag);
     289           0 :       if (err)
     290           0 :         return err;  /* FIXME: kill the child */
     291             :     }
     292             : 
     293           0 :   engspawn_io_event (esp, GPGME_EVENT_START, NULL);
     294             : 
     295           0 :   return 0;
     296             : }
     297             : 
     298             : 
     299             : 
     300             : /*
     301             :     Public functions
     302             :  */
     303             : 
     304             : static const char *
     305          79 : engspawn_get_file_name (void)
     306             : {
     307          79 :   return "/nonexistent";
     308             : }
     309             : 
     310             : 
     311             : static char *
     312          79 : engspawn_get_version (const char *file_name)
     313             : {
     314             :   (void)file_name;
     315          79 :   return NULL;
     316             : }
     317             : 
     318             : 
     319             : static const char *
     320          79 : engspawn_get_req_version (void)
     321             : {
     322          79 :   return NULL;
     323             : }
     324             : 
     325             : 
     326             : static gpgme_error_t
     327           0 : engspawn_new (void **engine, const char *file_name, const char *home_dir,
     328             :               const char *version)
     329             : {
     330             :   engine_spawn_t esp;
     331             : 
     332             :   (void)file_name;
     333             :   (void)home_dir;
     334             :   (void)version;
     335             : 
     336           0 :   esp = calloc (1, sizeof *esp);
     337           0 :   if (!esp)
     338           0 :     return gpg_error_from_syserror ();
     339             : 
     340           0 :   esp->argtail = &esp->arglist;
     341           0 :   *engine = esp;
     342           0 :   return 0;
     343             : }
     344             : 
     345             : 
     346             : static void
     347           0 : engspawn_release (void *engine)
     348             : {
     349           0 :   engine_spawn_t esp = engine;
     350             : 
     351           0 :   if (!esp)
     352           0 :     return;
     353             : 
     354           0 :   engspawn_cancel (engine);
     355             : 
     356           0 :   while (esp->arglist)
     357             :     {
     358           0 :       struct datalist_s *next = esp->arglist->next;
     359             : 
     360           0 :       free (esp->arglist);
     361           0 :       esp->arglist = next;
     362             :     }
     363             : 
     364           0 :   free (esp);
     365             : }
     366             : 
     367             : 
     368             : static void
     369           0 : engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
     370             : {
     371           0 :   engine_spawn_t esp = engine;
     372             : 
     373           0 :   esp->io_cbs = *io_cbs;
     374           0 : }
     375             : 
     376             : 
     377             : static void
     378           0 : engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data)
     379             : {
     380           0 :   engine_spawn_t esp = engine;
     381             : 
     382           0 :   TRACE3 (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp,
     383             :           "event %p, type %d, type_data %p",
     384             :           esp->io_cbs.event, type, type_data);
     385           0 :   if (esp->io_cbs.event)
     386           0 :     (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data);
     387           0 : }
     388             : 
     389             : 
     390             : static gpgme_error_t
     391           0 : engspawn_cancel (void *engine)
     392             : {
     393           0 :   engine_spawn_t esp = engine;
     394             : 
     395           0 :   if (!esp)
     396           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     397             : 
     398           0 :   if (esp->fd_data_map)
     399             :     {
     400           0 :       free_fd_data_map (esp->fd_data_map);
     401           0 :       esp->fd_data_map = NULL;
     402             :     }
     403             : 
     404           0 :   return 0;
     405             : }
     406             : 
     407             : 
     408             : static gpgme_error_t
     409           0 : engspawn_op_spawn (void *engine,
     410             :                    const char *file, const char *argv[],
     411             :                    gpgme_data_t datain,
     412             :                    gpgme_data_t dataout, gpgme_data_t dataerr,
     413             :                    unsigned int flags)
     414             : {
     415           0 :   engine_spawn_t esp = engine;
     416           0 :   gpgme_error_t err = 0;
     417             : 
     418           0 :   if (datain)
     419           0 :     err = add_data (esp, datain, 0, 0);
     420           0 :   if (!err && dataout)
     421           0 :     err = add_data (esp, dataout, 1, 1);
     422           0 :   if (!err && dataerr)
     423           0 :     err = add_data (esp, dataerr, 2, 1);
     424             : 
     425           0 :   if (!err)
     426           0 :     err = engspawn_start (esp, file, argv, flags);
     427             : 
     428           0 :   return err;
     429             : }
     430             : 
     431             : 
     432             : 
     433             : struct engine_ops _gpgme_engine_ops_spawn =
     434             :   {
     435             :     /* Static functions.  */
     436             :     engspawn_get_file_name,
     437             :     NULL,               /* get_home_dir */
     438             :     engspawn_get_version,
     439             :     engspawn_get_req_version,
     440             :     engspawn_new,
     441             : 
     442             :     /* Member functions.  */
     443             :     engspawn_release,
     444             :     NULL,               /* reset */
     445             :     NULL,               /* set_status_cb */
     446             :     NULL,               /* set_status_handler */
     447             :     NULL,               /* set_command_handler */
     448             :     NULL,               /* set_colon_line_handler */
     449             :     NULL,               /* set_locale */
     450             :     NULL,               /* set_protocol */
     451             :     NULL,               /* decrypt */
     452             :     NULL,               /* decrypt_verify */
     453             :     NULL,               /* delete */
     454             :     NULL,               /* edit */
     455             :     NULL,               /* encrypt */
     456             :     NULL,               /* encrypt_sign */
     457             :     NULL,               /* export */
     458             :     NULL,               /* export_ext */
     459             :     NULL,               /* genkey */
     460             :     NULL,               /* import */
     461             :     NULL,               /* keylist */
     462             :     NULL,               /* keylist_ext */
     463             :     NULL,               /* keysign */
     464             :     NULL,               /* tofu_policy */
     465             :     NULL,               /* sign */
     466             :     NULL,               /* trustlist */
     467             :     NULL,               /* verify */
     468             :     NULL,               /* getauditlog */
     469             :     NULL,               /* opassuan_transact */
     470             :     NULL,               /* conf_load */
     471             :     NULL,               /* conf_save */
     472             :     NULL,               /* query_swdb */
     473             :     engspawn_set_io_cbs,
     474             :     engspawn_io_event,  /* io_event */
     475             :     engspawn_cancel,    /* cancel */
     476             :     NULL,               /* cancel_op */
     477             :     NULL,               /* passwd */
     478             :     NULL,               /* set_pinentry_mode */
     479             :     engspawn_op_spawn   /* opspawn */
     480             :   };

Generated by: LCOV version 1.11