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 : };
|