LCOV - code coverage report
Current view: top level - src - engine-spawn.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 145 168 86.3 %
Date: 2018-11-15 08:49:49 Functions: 15 15 100.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         108 : close_notify_handler (int fd, void *opaque)
      88             : {
      89         108 :   engine_spawn_t esp = opaque;
      90             :   int i;
      91             : 
      92         108 :   assert (fd != -1);
      93             : 
      94         108 :   if (esp->fd_data_map)
      95             :     {
      96         216 :       for (i = 0; esp->fd_data_map[i].data; i++)
      97             :         {
      98         216 :           if (esp->fd_data_map[i].fd == fd)
      99             :             {
     100          54 :               if (esp->fd_data_map[i].tag)
     101          54 :                 (*esp->io_cbs.remove) (esp->fd_data_map[i].tag);
     102          54 :               esp->fd_data_map[i].fd = -1;
     103          54 :               break;
     104             :             }
     105         162 :           if (esp->fd_data_map[i].peer_fd == fd)
     106             :             {
     107          54 :               esp->fd_data_map[i].peer_fd = -1;
     108          54 :               break;
     109             :             }
     110             :         }
     111             :     }
     112         108 : }
     113             : 
     114             : 
     115             : static gpgme_error_t
     116          54 : add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound)
     117             : {
     118             :   struct datalist_s *a;
     119             : 
     120          54 :   assert (esp);
     121          54 :   assert (data);
     122             : 
     123          54 :   a = malloc (sizeof *a);
     124          54 :   if (!a)
     125           0 :     return gpg_error_from_syserror ();
     126          54 :   a->next = NULL;
     127          54 :   a->data = data;
     128          54 :   a->inbound = inbound;
     129          54 :   a->dup_to = dup_to;
     130          54 :   *esp->argtail = a;
     131          54 :   esp->argtail = &a->next;
     132          54 :   return 0;
     133             : }
     134             : 
     135             : 
     136             : static void
     137          36 : free_fd_data_map (struct fd_data_map_s *fd_data_map)
     138             : {
     139             :   int i;
     140             : 
     141          36 :   if (!fd_data_map)
     142          18 :     return;
     143             : 
     144          72 :   for (i = 0; fd_data_map[i].data; i++)
     145             :     {
     146          54 :       if (fd_data_map[i].fd != -1)
     147           0 :         _gpgme_io_close (fd_data_map[i].fd);
     148          54 :       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          18 :   free (fd_data_map);
     153             : }
     154             : 
     155             : 
     156             : static gpgme_error_t
     157          18 : build_fd_data_map (engine_spawn_t esp)
     158             : {
     159             :   struct datalist_s *a;
     160             :   size_t datac;
     161             :   int fds[2];
     162             : 
     163          72 :   for (datac = 0, a = esp->arglist; a; a = a->next)
     164          54 :     if (a->data)
     165          54 :       datac++;
     166             : 
     167          18 :   free_fd_data_map (esp->fd_data_map);
     168          18 :   esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map);
     169          18 :   if (!esp->fd_data_map)
     170           0 :     return gpg_error_from_syserror ();
     171             : 
     172          72 :   for (datac = 0, a = esp->arglist; a; a = a->next)
     173             :     {
     174          54 :       assert (a->data);
     175             : 
     176          54 :       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          54 :       if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp)
     183          54 :           || _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          54 :       esp->fd_data_map[datac].inbound = a->inbound;
     190          54 :       if (a->inbound)
     191             :         {
     192          36 :           esp->fd_data_map[datac].fd       = fds[0];
     193          36 :           esp->fd_data_map[datac].peer_fd  = fds[1];
     194             :         }
     195             :       else
     196             :         {
     197          18 :           esp->fd_data_map[datac].fd       = fds[1];
     198          18 :           esp->fd_data_map[datac].peer_fd  = fds[0];
     199             :         }
     200          54 :       esp->fd_data_map[datac].data    = a->data;
     201          54 :       esp->fd_data_map[datac].dup_to  = a->dup_to;
     202          54 :       datac++;
     203             :     }
     204             : 
     205          18 :   return 0;
     206             : }
     207             : 
     208             : 
     209             : static gpgme_error_t
     210          54 : 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          54 :   err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag);
     216          54 :   if (err)
     217           0 :     return err;
     218          54 :   if (!dir) /* Fixme: Kludge around poll() problem.  */
     219          18 :     err = _gpgme_io_set_nonblocking (fd);
     220          54 :   return err;
     221             : }
     222             : 
     223             : 
     224             : static gpgme_error_t
     225          18 : 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          18 :   const char *save_argv0 = NULL;
     235             : 
     236          18 :   if (!esp || !file || !argv || !argv[0])
     237           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     238             : 
     239          18 :   spflags = 0;
     240          18 :   if ((flags & GPGME_SPAWN_DETACHED))
     241           0 :     spflags |= IOSPAWN_FLAG_DETACHED;
     242          18 :   if ((flags & GPGME_SPAWN_ALLOW_SET_FG))
     243           0 :     spflags |= IOSPAWN_FLAG_ALLOW_SET_FG;
     244          18 :   if ((flags & GPGME_SPAWN_SHOW_WINDOW))
     245           0 :     spflags |= IOSPAWN_FLAG_SHOW_WINDOW;
     246             : 
     247          18 :   err = build_fd_data_map (esp);
     248          18 :   if (err)
     249           0 :     return err;
     250             : 
     251          18 :   n = 0;
     252          72 :   for (i = 0; esp->fd_data_map[i].data; i++)
     253          54 :     n++;
     254          18 :   fd_list = calloc (n+1, sizeof *fd_list);
     255          18 :   if (!fd_list)
     256           0 :     return gpg_error_from_syserror ();
     257             : 
     258             :   /* Build the fd list for the child.  */
     259          18 :   n = 0;
     260          72 :   for (i = 0; esp->fd_data_map[i].data; i++)
     261             :     {
     262          54 :       fd_list[n].fd = esp->fd_data_map[i].peer_fd;
     263          54 :       fd_list[n].dup_to = esp->fd_data_map[i].dup_to;
     264          54 :       n++;
     265             :     }
     266          18 :   fd_list[n].fd = -1;
     267          18 :   fd_list[n].dup_to = -1;
     268             : 
     269          18 :   if (argv[0] && !*argv[0])
     270             :     {
     271           0 :       save_argv0 = argv[0];
     272           0 :       argv[0] = _gpgme_get_basename (file);
     273             :     }
     274          18 :   status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
     275             :                             fd_list, NULL, NULL, &pid);
     276          18 :   if (save_argv0)
     277           0 :     argv[0] = save_argv0;
     278          18 :   free (fd_list);
     279          18 :   if (status == -1)
     280           0 :     return gpg_error_from_syserror ();
     281             : 
     282          72 :   for (i = 0; esp->fd_data_map[i].data; i++)
     283             :     {
     284         162 :       err = add_io_cb (esp, esp->fd_data_map[i].fd,
     285          54 :                        esp->fd_data_map[i].inbound,
     286          54 :                        esp->fd_data_map[i].inbound
     287             :                        ? _gpgme_data_inbound_handler
     288             :                        : _gpgme_data_outbound_handler,
     289         108 :                        esp->fd_data_map[i].data, &esp->fd_data_map[i].tag);
     290          54 :       if (err)
     291           0 :         return err;  /* FIXME: kill the child */
     292             :     }
     293             : 
     294          18 :   engspawn_io_event (esp, GPGME_EVENT_START, NULL);
     295             : 
     296          18 :   return 0;
     297             : }
     298             : 
     299             : 
     300             : 
     301             : /*
     302             :     Public functions
     303             :  */
     304             : 
     305             : static const char *
     306         117 : engspawn_get_file_name (void)
     307             : {
     308         117 :   return "/nonexistent";
     309             : }
     310             : 
     311             : 
     312             : static char *
     313         117 : engspawn_get_version (const char *file_name)
     314             : {
     315             :   (void)file_name;
     316         117 :   return NULL;
     317             : }
     318             : 
     319             : 
     320             : static const char *
     321         117 : engspawn_get_req_version (void)
     322             : {
     323         117 :   return NULL;
     324             : }
     325             : 
     326             : 
     327             : static gpgme_error_t
     328          18 : 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          18 :   esp = calloc (1, sizeof *esp);
     338          18 :   if (!esp)
     339           0 :     return gpg_error_from_syserror ();
     340             : 
     341          18 :   esp->argtail = &esp->arglist;
     342          18 :   *engine = esp;
     343          18 :   return 0;
     344             : }
     345             : 
     346             : 
     347             : static void
     348          18 : engspawn_release (void *engine)
     349             : {
     350          18 :   engine_spawn_t esp = engine;
     351             : 
     352          18 :   if (!esp)
     353           0 :     return;
     354             : 
     355          18 :   engspawn_cancel (engine);
     356             : 
     357          90 :   while (esp->arglist)
     358             :     {
     359          54 :       struct datalist_s *next = esp->arglist->next;
     360             : 
     361          54 :       free (esp->arglist);
     362          54 :       esp->arglist = next;
     363             :     }
     364             : 
     365          18 :   free (esp);
     366             : }
     367             : 
     368             : 
     369             : static void
     370          18 : engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
     371             : {
     372          18 :   engine_spawn_t esp = engine;
     373             : 
     374          18 :   esp->io_cbs = *io_cbs;
     375          18 : }
     376             : 
     377             : 
     378             : static void
     379          36 : engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data)
     380             : {
     381          36 :   engine_spawn_t esp = engine;
     382             : 
     383          36 :   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          36 :   if (esp->io_cbs.event)
     387          36 :     (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data);
     388          36 : }
     389             : 
     390             : 
     391             : static gpgme_error_t
     392          18 : engspawn_cancel (void *engine)
     393             : {
     394          18 :   engine_spawn_t esp = engine;
     395             : 
     396          18 :   if (!esp)
     397           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     398             : 
     399          18 :   if (esp->fd_data_map)
     400             :     {
     401          18 :       free_fd_data_map (esp->fd_data_map);
     402          18 :       esp->fd_data_map = NULL;
     403             :     }
     404             : 
     405          18 :   return 0;
     406             : }
     407             : 
     408             : 
     409             : static gpgme_error_t
     410          18 : 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          18 :   engine_spawn_t esp = engine;
     417          18 :   gpgme_error_t err = 0;
     418             : 
     419          18 :   if (datain)
     420          18 :     err = add_data (esp, datain, 0, 0);
     421          18 :   if (!err && dataout)
     422          18 :     err = add_data (esp, dataout, 1, 1);
     423          18 :   if (!err && dataerr)
     424          18 :     err = add_data (esp, dataerr, 2, 1);
     425             : 
     426          18 :   if (!err)
     427          18 :     err = engspawn_start (esp, file, argv, flags);
     428             : 
     429          18 :   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