1 /* engine-spawn.c - Run an arbitrary program
2 * Copyright (C) 2014 g10 Code GmbH
4 * This file is part of GPGME.
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.
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.
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
40 #include "context.h" /*temp hack until we have GpmeData methods to do I/O */
45 #include "engine-backend.h"
48 /* This type is used to build a list of data sources/sinks. */
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. */
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. */
71 struct datalist_s *arglist;
72 struct datalist_s **argtail;
74 struct fd_data_map_s *fd_data_map;
76 struct gpgme_io_cbs io_cbs;
78 typedef struct engine_spawn *engine_spawn_t;
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);
88 close_notify_handler (int fd, void *opaque)
90 engine_spawn_t esp = opaque;
97 for (i = 0; esp->fd_data_map[i].data; i++)
99 if (esp->fd_data_map[i].fd == fd)
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;
106 if (esp->fd_data_map[i].peer_fd == fd)
108 esp->fd_data_map[i].peer_fd = -1;
117 add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound)
119 struct datalist_s *a;
124 a = malloc (sizeof *a);
126 return gpg_error_from_syserror ();
129 a->inbound = inbound;
132 esp->argtail = &a->next;
138 free_fd_data_map (struct fd_data_map_s *fd_data_map)
145 for (i = 0; fd_data_map[i].data; i++)
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. */
158 build_fd_data_map (engine_spawn_t esp)
160 struct datalist_s *a;
164 for (datac = 0, a = esp->arglist; a; a = a->next)
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 ();
173 for (datac = 0, a = esp->arglist; a; a = a->next)
177 if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1)
179 free (esp->fd_data_map);
180 esp->fd_data_map = NULL;
181 return gpg_error_from_syserror ();
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))
186 /* FIXME: Need error cleanup. */
187 return gpg_error (GPG_ERR_GENERAL);
190 esp->fd_data_map[datac].inbound = a->inbound;
193 esp->fd_data_map[datac].fd = fds[0];
194 esp->fd_data_map[datac].peer_fd = fds[1];
198 esp->fd_data_map[datac].fd = fds[1];
199 esp->fd_data_map[datac].peer_fd = fds[0];
201 esp->fd_data_map[datac].data = a->data;
202 esp->fd_data_map[datac].dup_to = a->dup_to;
211 add_io_cb (engine_spawn_t esp, int fd, int dir, gpgme_io_cb_t handler,
212 void *data, void **tag)
216 err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag);
219 if (!dir) /* Fixme: Kludge around poll() problem. */
220 err = _gpgme_io_set_nonblocking (fd);
226 engspawn_start (engine_spawn_t esp, const char *file, const char *argv[],
232 struct spawn_fd_item_s *fd_list;
234 unsigned int spflags;
235 const char *save_argv0 = NULL;
237 if (!esp || !file || !argv || !argv[0])
238 return gpg_error (GPG_ERR_INV_VALUE);
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;
248 err = build_fd_data_map (esp);
253 for (i = 0; esp->fd_data_map[i].data; i++)
255 fd_list = calloc (n+1, sizeof *fd_list);
257 return gpg_error_from_syserror ();
259 /* Build the fd list for the child. */
261 for (i = 0; esp->fd_data_map[i].data; i++)
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;
268 fd_list[n].dup_to = -1;
270 if (argv[0] && !*argv[0])
272 save_argv0 = argv[0];
273 argv[0] = _gpgme_get_basename (file);
275 status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
276 fd_list, NULL, NULL, &pid);
278 argv[0] = save_argv0;
281 return gpg_error_from_syserror ();
283 for (i = 0; esp->fd_data_map[i].data; i++)
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);
292 return err; /* FIXME: kill the child */
295 engspawn_io_event (esp, GPGME_EVENT_START, NULL);
307 engspawn_get_file_name (void)
309 return "/nonexistent";
314 engspawn_get_version (const char *file_name)
322 engspawn_get_req_version (void)
329 engspawn_new (void **engine, const char *file_name, const char *home_dir,
338 esp = calloc (1, sizeof *esp);
340 return gpg_error_from_syserror ();
342 esp->argtail = &esp->arglist;
349 engspawn_release (void *engine)
351 engine_spawn_t esp = engine;
356 engspawn_cancel (engine);
360 struct datalist_s *next = esp->arglist->next;
371 engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
373 engine_spawn_t esp = engine;
375 esp->io_cbs = *io_cbs;
380 engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data)
382 engine_spawn_t esp = engine;
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);
393 engspawn_cancel (void *engine)
395 engine_spawn_t esp = engine;
398 return gpg_error (GPG_ERR_INV_VALUE);
400 if (esp->fd_data_map)
402 free_fd_data_map (esp->fd_data_map);
403 esp->fd_data_map = NULL;
411 engspawn_op_spawn (void *engine,
412 const char *file, const char *argv[],
414 gpgme_data_t dataout, gpgme_data_t dataerr,
417 engine_spawn_t esp = engine;
418 gpgme_error_t err = 0;
421 err = add_data (esp, datain, 0, 0);
423 err = add_data (esp, dataout, 1, 1);
425 err = add_data (esp, dataerr, 2, 1);
428 err = engspawn_start (esp, file, argv, flags);
435 struct engine_ops _gpgme_engine_ops_spawn =
437 /* Static functions. */
438 engspawn_get_file_name,
439 NULL, /* get_home_dir */
440 engspawn_get_version,
441 engspawn_get_req_version,
444 /* Member functions. */
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 */
458 NULL, /* encrypt_sign */
460 NULL, /* export_ext */
464 NULL, /* keylist_ext */
465 NULL, /* keylist_data */
467 NULL, /* tofu_policy */
469 NULL, /* trustlist */
471 NULL, /* getauditlog */
472 NULL, /* opassuan_transact */
473 NULL, /* conf_load */
474 NULL, /* conf_save */
476 NULL, /* query_swdb */
478 engspawn_io_event, /* io_event */
479 engspawn_cancel, /* cancel */
480 NULL, /* cancel_op */
482 NULL, /* set_pinentry_mode */
483 engspawn_op_spawn /* opspawn */