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