LCOV - code coverage report
Current view: top level - src - engine-spawn.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 168 3.6 %
Date: 2018-11-14 16:53:58 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           0 :   if ((flags & GPGME_SPAWN_SHOW_WINDOW))
     245           0 :     spflags |= IOSPAWN_FLAG_SHOW_WINDOW;
     246             : 
     247           0 :   err = build_fd_data_map (esp);
     248           0 :   if (err)
     249           0 :     return err;
     250             : 
     251           0 :   n = 0;
     252           0 :   for (i = 0; esp->fd_data_map[i].data; i++)
     253           0 :     n++;
     254           0 :   fd_list = calloc (n+1, sizeof *fd_list);
     255           0 :   if (!fd_list)
     256           0 :     return gpg_error_from_syserror ();
     257             : 
     258             :   /* Build the fd list for the child.  */
     259           0 :   n = 0;
     260           0 :   for (i = 0; esp->fd_data_map[i].data; i++)
     261             :     {
     262           0 :       fd_list[n].fd = esp->fd_data_map[i].peer_fd;
     263           0 :       fd_list[n].dup_to = esp->fd_data_map[i].dup_to;
     264           0 :       n++;
     265             :     }
     266           0 :   fd_list[n].fd = -1;
     267           0 :   fd_list[n].dup_to = -1;
     268             : 
     269           0 :   if (argv[0] && !*argv[0])
     270             :     {
     271           0 :       save_argv0 = argv[0];
     272           0 :       argv[0] = _gpgme_get_basename (file);
     273             :     }
     274           0 :   status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
     275             :                             fd_list, NULL, NULL, &pid);
     276           0 :   if (save_argv0)
     277           0 :     argv[0] = save_argv0;
     278           0 :   free (fd_list);
     279           0 :   if (status == -1)
     280           0 :     return gpg_error_from_syserror ();
     281             : 
     282           0 :   for (i = 0; esp->fd_data_map[i].data; i++)
     283             :     {
     284           0 :       err = add_io_cb (esp, esp->fd_data_map[i].fd,
     285           0 :                        esp->fd_data_map[i].inbound,
     286           0 :                        esp->fd_data_map[i].inbound
     287             :                        ? _gpgme_data_inbound_handler
     288             :                        : _gpgme_data_outbound_handler,
     289           0 :                        esp->fd_data_map[i].data, &esp->fd_data_map[i].tag);
     290           0 :       if (err)
     291           0 :         return err;  /* FIXME: kill the child */
     292             :     }
     293             : 
     294           0 :   engspawn_io_event (esp, GPGME_EVENT_START, NULL);
     295             : 
     296           0 :   return 0;
     297             : }
     298             : 
     299             : 
     300             : 
     301             : /*
     302             :     Public functions
     303             :  */
     304             : 
     305             : static const char *
     306          98 : engspawn_get_file_name (void)
     307             : {
     308          98 :   return "/nonexistent";
     309             : }
     310             : 
     311             : 
     312             : static char *
     313          98 : engspawn_get_version (const char *file_name)
     314             : {
     315             :   (void)file_name;
     316          98 :   return NULL;
     317             : }
     318             : 
     319             : 
     320             : static const char *
     321          98 : engspawn_get_req_version (void)
     322             : {
     323          98 :   return NULL;
     324             : }
     325             : 
     326             : 
     327             : static gpgme_error_t
     328           0 : engspawn_new (void **engine, const char *file_name, const char *home_dir,
     329             :               const char *version)
     330             : {
     331             :   engine_spawn_t esp;
     332             : 
     333             :   (void)file_name;
     334             :   (void)home_dir;
     335             :   (void)version;
     336             : 
     337           0 :   esp = calloc (1, sizeof *esp);
     338           0 :   if (!esp)
     339           0 :     return gpg_error_from_syserror ();
     340             : 
     341           0 :   esp->argtail = &esp->arglist;
     342           0 :   *engine = esp;
     343           0 :   return 0;
     344             : }
     345             : 
     346             : 
     347             : static void
     348           0 : engspawn_release (void *engine)
     349             : {
     350           0 :   engine_spawn_t esp = engine;
     351             : 
     352           0 :   if (!esp)
     353           0 :     return;
     354             : 
     355           0 :   engspawn_cancel (engine);
     356             : 
     357           0 :   while (esp->arglist)
     358             :     {
     359           0 :       struct datalist_s *next = esp->arglist->next;
     360             : 
     361           0 :       free (esp->arglist);
     362           0 :       esp->arglist = next;
     363             :     }
     364             : 
     365           0 :   free (esp);
     366             : }
     367             : 
     368             : 
     369             : static void
     370           0 : engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
     371             : {
     372           0 :   engine_spawn_t esp = engine;
     373             : 
     374           0 :   esp->io_cbs = *io_cbs;
     375           0 : }
     376             : 
     377             : 
     378             : static void
     379           0 : engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data)
     380             : {
     381           0 :   engine_spawn_t esp = engine;
     382             : 
     383           0 :   TRACE3 (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp,
     384             :           "event %p, type %d, type_data %p",
     385             :           esp->io_cbs.event, type, type_data);
     386           0 :   if (esp->io_cbs.event)
     387           0 :     (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data);
     388           0 : }
     389             : 
     390             : 
     391             : static gpgme_error_t
     392           0 : engspawn_cancel (void *engine)
     393             : {
     394           0 :   engine_spawn_t esp = engine;
     395             : 
     396           0 :   if (!esp)
     397           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     398             : 
     399           0 :   if (esp->fd_data_map)
     400             :     {
     401           0 :       free_fd_data_map (esp->fd_data_map);
     402           0 :       esp->fd_data_map = NULL;
     403             :     }
     404             : 
     405           0 :   return 0;
     406             : }
     407             : 
     408             : 
     409             : static gpgme_error_t
     410           0 : engspawn_op_spawn (void *engine,
     411             :                    const char *file, const char *argv[],
     412             :                    gpgme_data_t datain,
     413             :                    gpgme_data_t dataout, gpgme_data_t dataerr,
     414             :                    unsigned int flags)
     415             : {
     416           0 :   engine_spawn_t esp = engine;
     417           0 :   gpgme_error_t err = 0;
     418             : 
     419           0 :   if (datain)
     420           0 :     err = add_data (esp, datain, 0, 0);
     421           0 :   if (!err && dataout)
     422           0 :     err = add_data (esp, dataout, 1, 1);
     423           0 :   if (!err && dataerr)
     424           0 :     err = add_data (esp, dataerr, 2, 1);
     425             : 
     426           0 :   if (!err)
     427           0 :     err = engspawn_start (esp, file, argv, flags);
     428             : 
     429           0 :   return err;
     430             : }
     431             : 
     432             : 
     433             : 
     434             : struct engine_ops _gpgme_engine_ops_spawn =
     435             :   {
     436             :     /* Static functions.  */
     437             :     engspawn_get_file_name,
     438             :     NULL,               /* get_home_dir */
     439             :     engspawn_get_version,
     440             :     engspawn_get_req_version,
     441             :     engspawn_new,
     442             : 
     443             :     /* Member functions.  */
     444             :     engspawn_release,
     445             :     NULL,               /* reset */
     446             :     NULL,               /* set_status_cb */
     447             :     NULL,               /* set_status_handler */
     448             :     NULL,               /* set_command_handler */
     449             :     NULL,               /* set_colon_line_handler */
     450             :     NULL,               /* set_locale */
     451             :     NULL,               /* set_protocol */
     452             :     NULL,               /* set_engine_flags */
     453             :     NULL,               /* decrypt */
     454             :     NULL,               /* delete */
     455             :     NULL,               /* edit */
     456             :     NULL,               /* encrypt */
     457             :     NULL,               /* encrypt_sign */
     458             :     NULL,               /* export */
     459             :     NULL,               /* export_ext */
     460             :     NULL,               /* genkey */
     461             :     NULL,               /* import */
     462             :     NULL,               /* keylist */
     463             :     NULL,               /* keylist_ext */
     464             :     NULL,               /* keylist_data */
     465             :     NULL,               /* keysign */
     466             :     NULL,               /* tofu_policy */
     467             :     NULL,               /* sign */
     468             :     NULL,               /* trustlist */
     469             :     NULL,               /* verify */
     470             :     NULL,               /* getauditlog */
     471             :     NULL,               /* opassuan_transact */
     472             :     NULL,               /* conf_load */
     473             :     NULL,               /* conf_save */
     474             :     NULL,               /* conf_dir */
     475             :     NULL,               /* query_swdb */
     476             :     engspawn_set_io_cbs,
     477             :     engspawn_io_event,  /* io_event */
     478             :     engspawn_cancel,    /* cancel */
     479             :     NULL,               /* cancel_op */
     480             :     NULL,               /* passwd */
     481             :     NULL,               /* set_pinentry_mode */
     482             :     engspawn_op_spawn   /* opspawn */
     483             :   };

Generated by: LCOV version 1.13