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