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