Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2011 Collabora Ltd.
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it 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 : : * This program 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 : : * Author: Stef Walter <stefw@collabora.co.uk>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include "gcr-gnupg-process.h"
25 : : #include "gcr-util.h"
26 : :
27 : : #include "gcr/gcr-marshal.h"
28 : :
29 : : #include <glib/gi18n-lib.h>
30 : :
31 : : #include <sys/wait.h>
32 : : #include <fcntl.h>
33 : : #include <errno.h>
34 : : #include <string.h>
35 : : #include <unistd.h>
36 : :
37 : : /**
38 : : * GcrGnupgProcessFlags:
39 : : * @GCR_GNUPG_PROCESS_NONE: No flags
40 : : * @GCR_GNUPG_PROCESS_RESPECT_LOCALE: Respect the user's locale when running gnupg.
41 : : * @GCR_GNUPG_PROCESS_WITH_STATUS: Ask the process to send status records.
42 : : * @GCR_GNUPG_PROCESS_WITH_ATTRIBUTES: Ask the process to output attribute data.
43 : : *
44 : : * Flags for running a gnupg process.
45 : : */
46 : :
47 : : enum {
48 : : PROP_0,
49 : : PROP_DIRECTORY,
50 : : PROP_EXECUTABLE,
51 : : PROP_INPUT_STREAM,
52 : : PROP_OUTPUT_STREAM,
53 : : PROP_ATTRIBUTE_STREAM
54 : : };
55 : :
56 : : enum {
57 : : FD_INPUT,
58 : : FD_OUTPUT,
59 : : FD_ERROR,
60 : : FD_STATUS,
61 : : FD_ATTRIBUTE,
62 : : NUM_FDS
63 : : };
64 : :
65 : : enum {
66 : : ERROR_LINE,
67 : : STATUS_RECORD,
68 : : NUM_SIGNALS
69 : : };
70 : :
71 : : static gint signals[NUM_SIGNALS] = { 0, };
72 : :
73 : : typedef struct _GnupgSource {
74 : : GSource source;
75 : : GPollFD polls[NUM_FDS]; /* The various fd's we're listening to */
76 : :
77 : : GcrGnupgProcess *process; /* Pointer back to the process object */
78 : :
79 : : GByteArray *input_buf;
80 : : GString *error_buf;
81 : : GString *status_buf;
82 : :
83 : : GPid child_pid;
84 : : guint child_sig;
85 : :
86 : : GCancellable *cancellable;
87 : : guint cancel_sig;
88 : : } GnupgSource;
89 : :
90 : : struct _GcrGnupgProcessPrivate {
91 : : gchar *directory;
92 : : gchar *executable;
93 : :
94 : : GInputStream *input;
95 : : GOutputStream *output;
96 : : GOutputStream *attributes;
97 : :
98 : : gboolean running;
99 : : gboolean complete;
100 : : GError *error;
101 : : guint source_sig;
102 : :
103 : : GAsyncReadyCallback async_callback;
104 : : gpointer user_data;
105 : : };
106 : :
107 : : /* Forward declarations */
108 : : static void _gcr_gnupg_process_init_async (GAsyncResultIface *iface);
109 : :
110 [ + + + - : 148 : G_DEFINE_TYPE_WITH_CODE (GcrGnupgProcess, _gcr_gnupg_process, G_TYPE_OBJECT,
+ + ]
111 : : G_ADD_PRIVATE (GcrGnupgProcess);
112 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, _gcr_gnupg_process_init_async));
113 : :
114 : : static void
115 : 13 : _gcr_gnupg_process_init (GcrGnupgProcess *self)
116 : : {
117 : 13 : self->pv = _gcr_gnupg_process_get_instance_private (self);
118 : 13 : }
119 : :
120 : : static void
121 : 13 : _gcr_gnupg_process_constructed (GObject *obj)
122 : : {
123 : 13 : GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
124 : :
125 [ + - ]: 13 : if (G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->constructed)
126 : 13 : G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->constructed (obj);
127 : :
128 [ - + ]: 13 : if (!self->pv->executable)
129 : 0 : self->pv->executable = g_strdup (GPG_EXECUTABLE);
130 : 13 : }
131 : :
132 : : static void
133 : 2 : _gcr_gnupg_process_get_property (GObject *obj, guint prop_id, GValue *value,
134 : : GParamSpec *pspec)
135 : : {
136 : 2 : GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
137 : :
138 [ + + - - : 2 : switch (prop_id) {
- - ]
139 : 1 : case PROP_DIRECTORY:
140 : 1 : g_value_set_string (value, self->pv->directory);
141 : 1 : break;
142 : 1 : case PROP_EXECUTABLE:
143 : 1 : g_value_set_string (value, self->pv->executable);
144 : 1 : break;
145 : 0 : case PROP_INPUT_STREAM:
146 : 0 : g_value_set_object (value, _gcr_gnupg_process_get_input_stream (self));
147 : 0 : break;
148 : 0 : case PROP_OUTPUT_STREAM:
149 : 0 : g_value_set_object (value, _gcr_gnupg_process_get_output_stream (self));
150 : 0 : break;
151 : 0 : case PROP_ATTRIBUTE_STREAM:
152 : 0 : g_value_set_object (value, _gcr_gnupg_process_get_attribute_stream (self));
153 : 0 : break;
154 : 0 : default:
155 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
156 : 0 : break;
157 : : }
158 : 2 : }
159 : :
160 : : static void
161 : 26 : _gcr_gnupg_process_set_property (GObject *obj, guint prop_id, const GValue *value,
162 : : GParamSpec *pspec)
163 : : {
164 : 26 : GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
165 : :
166 [ + + - - : 26 : switch (prop_id) {
- - ]
167 : 13 : case PROP_DIRECTORY:
168 [ - + ]: 13 : g_return_if_fail (!self->pv->directory);
169 : 13 : self->pv->directory = g_value_dup_string (value);
170 : 13 : break;
171 : 13 : case PROP_EXECUTABLE:
172 [ - + ]: 13 : g_return_if_fail (!self->pv->executable);
173 : 13 : self->pv->executable = g_value_dup_string (value);
174 : 13 : break;
175 : 0 : case PROP_INPUT_STREAM:
176 : 0 : _gcr_gnupg_process_set_input_stream (self, g_value_get_object (value));
177 : 0 : break;
178 : 0 : case PROP_OUTPUT_STREAM:
179 : 0 : _gcr_gnupg_process_set_output_stream (self, g_value_get_object (value));
180 : 0 : break;
181 : 0 : case PROP_ATTRIBUTE_STREAM:
182 : 0 : _gcr_gnupg_process_set_attribute_stream (self, g_value_get_object (value));
183 : 0 : break;
184 : 0 : default:
185 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
186 : 0 : break;
187 : : }
188 : : }
189 : :
190 : : static void
191 : 13 : _gcr_gnupg_process_dispose (GObject *obj)
192 : : {
193 : 13 : GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
194 : :
195 [ + + ]: 13 : g_clear_object (&self->pv->input);
196 [ + + ]: 13 : g_clear_object (&self->pv->output);
197 [ + + ]: 13 : g_clear_object (&self->pv->attributes);
198 : :
199 : 13 : G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->dispose (obj);
200 : 13 : }
201 : :
202 : : static void
203 : 13 : _gcr_gnupg_process_finalize (GObject *obj)
204 : : {
205 : 13 : GcrGnupgProcess *self = GCR_GNUPG_PROCESS (obj);
206 : :
207 [ - + ]: 13 : g_assert (self->pv->source_sig == 0);
208 [ - + ]: 13 : g_assert (!self->pv->running);
209 : 13 : g_free (self->pv->directory);
210 : 13 : g_free (self->pv->executable);
211 : 13 : g_clear_error (&self->pv->error);
212 : :
213 : 13 : G_OBJECT_CLASS (_gcr_gnupg_process_parent_class)->finalize (obj);
214 : 13 : }
215 : :
216 : : static void
217 : 1 : _gcr_gnupg_process_class_init (GcrGnupgProcessClass *klass)
218 : : {
219 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
220 : :
221 : 1 : gobject_class->constructed = _gcr_gnupg_process_constructed;
222 : 1 : gobject_class->get_property = _gcr_gnupg_process_get_property;
223 : 1 : gobject_class->set_property = _gcr_gnupg_process_set_property;
224 : 1 : gobject_class->dispose = _gcr_gnupg_process_dispose;
225 : 1 : gobject_class->finalize = _gcr_gnupg_process_finalize;
226 : :
227 : : /**
228 : : * GcrGnupgProcess:directory:
229 : : *
230 : : * Directory to run as gnupg home directory, or %NULL for default
231 : : * ~/.gnupg/ directory.
232 : : */
233 : 1 : g_object_class_install_property (gobject_class, PROP_DIRECTORY,
234 : : g_param_spec_string ("directory", "Directory", "Gnupg Directory",
235 : : NULL,
236 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
237 : :
238 : : /**
239 : : * GcrGnupgProcess:executable:
240 : : *
241 : : * Path to the gnupg executable, or %NULL for default.
242 : : */
243 : 1 : g_object_class_install_property (gobject_class, PROP_EXECUTABLE,
244 : : g_param_spec_string ("executable", "Executable", "Gnupg Executable",
245 : : GPG_EXECUTABLE,
246 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
247 : :
248 : : /**
249 : : * GcrGnupgProcess:input-stream:
250 : : *
251 : : * Input for gnupg, or %NULL for no input.
252 : : */
253 : 1 : g_object_class_install_property (gobject_class, PROP_INPUT_STREAM,
254 : : g_param_spec_object ("input-stream", "Input Stream", "Input Stream",
255 : : G_TYPE_INPUT_STREAM,
256 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
257 : :
258 : : /**
259 : : * GcrGnupgProcess:output-stream:
260 : : *
261 : : * Output from gnupg, or %NULL for ignored output.
262 : : */
263 : 1 : g_object_class_install_property (gobject_class, PROP_OUTPUT_STREAM,
264 : : g_param_spec_object ("output-stream", "Output Stream", "Output Stream",
265 : : G_TYPE_OUTPUT_STREAM,
266 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
267 : :
268 : : /**
269 : : * GcrGnupgProcess:attribute-stream:
270 : : *
271 : : * Output of attribute data from gnupg, or %NULL for ignored attributes.
272 : : */
273 : 1 : g_object_class_install_property (gobject_class, PROP_ATTRIBUTE_STREAM,
274 : : g_param_spec_object ("attribute-stream", "Attribute Stream", "Attribute Stream",
275 : : G_TYPE_OUTPUT_STREAM,
276 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277 : :
278 : : /**
279 : : * GcrGnupgProcess::error-line:
280 : : * @line: a line of error output.
281 : : *
282 : : * Signal emitted when a line of error output is available from the
283 : : * gnupg process.
284 : : */
285 : 1 : signals[ERROR_LINE] = g_signal_new ("error-line", GCR_TYPE_GNUPG_PROCESS,
286 : : G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrGnupgProcessClass, error_line),
287 : : NULL, NULL, _gcr_marshal_VOID__STRING,
288 : : G_TYPE_NONE, 1, G_TYPE_STRING);
289 : :
290 : : /**
291 : : * GcrGnupgProcess::status-record:
292 : : * @record: a status record.
293 : : *
294 : : * Signal emitted when a status record is available from the gnupg process.
295 : : */
296 : 1 : signals[STATUS_RECORD] = g_signal_new ("status-record", GCR_TYPE_GNUPG_PROCESS,
297 : : G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrGnupgProcessClass, status_record),
298 : : NULL, NULL, _gcr_marshal_VOID__BOXED,
299 : : G_TYPE_NONE, 1, GCR_TYPE_RECORD);
300 : 1 : }
301 : :
302 : : static gpointer
303 : 0 : _gcr_gnupg_process_get_user_data (GAsyncResult *result)
304 : : {
305 [ # # # # : 0 : g_return_val_if_fail (GCR_IS_GNUPG_PROCESS (result), NULL);
# # # # ]
306 : 0 : return GCR_GNUPG_PROCESS (result)->pv->user_data;
307 : : }
308 : :
309 : : static GObject*
310 : 12 : _gcr_gnupg_process_get_source_object (GAsyncResult *result)
311 : : {
312 [ - + + - : 12 : g_return_val_if_fail (GCR_IS_GNUPG_PROCESS (result), NULL);
+ - - + ]
313 : 12 : return G_OBJECT (g_object_ref (result));
314 : : }
315 : :
316 : : static void
317 : 1 : _gcr_gnupg_process_init_async (GAsyncResultIface *iface)
318 : : {
319 : 1 : iface->get_source_object = _gcr_gnupg_process_get_source_object;
320 : 1 : iface->get_user_data = _gcr_gnupg_process_get_user_data;
321 : 1 : }
322 : :
323 : : /**
324 : : * _gcr_gnupg_process_new:
325 : : * @directory: (nullable): The gnupg home directory
326 : : * @executable: (nullable): The gpg executable
327 : : *
328 : : * Create a new GcrGnupgProcess.
329 : : *
330 : : * The gnupg home directory is where the keyring files live. If directory is
331 : : * %NULL then the default gnupg home directory is used.
332 : : *
333 : : * The executable will default to the compiled in path if a %NULL executable
334 : : * argument is used.
335 : : *
336 : : * Returns: (transfer full): A newly allocated process.
337 : : */
338 : : GcrGnupgProcess*
339 : 13 : _gcr_gnupg_process_new (const gchar *directory, const gchar *executable)
340 : : {
341 : 13 : return g_object_new (GCR_TYPE_GNUPG_PROCESS,
342 : : "directory", directory,
343 : : "executable", executable,
344 : : NULL);
345 : : }
346 : :
347 : : const gchar *
348 : 0 : _gcr_gnupg_process_get_directory (GcrGnupgProcess *self)
349 : : {
350 [ # # ]: 0 : g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
351 : 0 : return self->pv->directory;
352 : : }
353 : :
354 : : GInputStream *
355 : 0 : _gcr_gnupg_process_get_input_stream (GcrGnupgProcess *self)
356 : : {
357 [ # # ]: 0 : g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
358 : 0 : return self->pv->input;
359 : : }
360 : :
361 : : void
362 : 1 : _gcr_gnupg_process_set_input_stream (GcrGnupgProcess *self,
363 : : GInputStream *input)
364 : : {
365 [ - + ]: 1 : g_return_if_fail (GCR_GNUPG_PROCESS (self));
366 [ + - - + ]: 1 : g_return_if_fail (input == NULL || G_INPUT_STREAM (input));
367 : :
368 [ + - ]: 1 : if (input)
369 : 1 : g_object_ref (input);
370 [ - + ]: 1 : if (self->pv->input)
371 : 0 : g_object_unref (self->pv->input);
372 : 1 : self->pv->input = input;
373 : 1 : g_object_notify (G_OBJECT (self), "input-stream");
374 : : }
375 : :
376 : : GOutputStream *
377 : 0 : _gcr_gnupg_process_get_output_stream (GcrGnupgProcess *self)
378 : : {
379 [ # # ]: 0 : g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
380 : 0 : return self->pv->output;
381 : : }
382 : :
383 : : void
384 : 6 : _gcr_gnupg_process_set_output_stream (GcrGnupgProcess *self,
385 : : GOutputStream *output)
386 : : {
387 [ - + ]: 6 : g_return_if_fail (GCR_GNUPG_PROCESS (self));
388 [ + - - + ]: 6 : g_return_if_fail (output == NULL || G_OUTPUT_STREAM (output));
389 : :
390 [ + - ]: 6 : if (output)
391 : 6 : g_object_ref (output);
392 [ - + ]: 6 : if (self->pv->output)
393 : 0 : g_object_unref (self->pv->output);
394 : 6 : self->pv->output = output;
395 : 6 : g_object_notify (G_OBJECT (self), "output-stream");
396 : : }
397 : :
398 : : GOutputStream *
399 : 0 : _gcr_gnupg_process_get_attribute_stream (GcrGnupgProcess *self)
400 : : {
401 [ # # ]: 0 : g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
402 : 0 : return self->pv->attributes;
403 : : }
404 : :
405 : : void
406 : 1 : _gcr_gnupg_process_set_attribute_stream (GcrGnupgProcess *self,
407 : : GOutputStream *output)
408 : : {
409 [ - + ]: 1 : g_return_if_fail (GCR_GNUPG_PROCESS (self));
410 [ + - - + ]: 1 : g_return_if_fail (output == NULL || G_OUTPUT_STREAM (output));
411 : :
412 [ + - ]: 1 : if (output)
413 : 1 : g_object_ref (output);
414 [ - + ]: 1 : if (self->pv->attributes)
415 : 0 : g_object_unref (self->pv->attributes);
416 : 1 : self->pv->attributes = output;
417 : 1 : g_object_notify (G_OBJECT (self), "attribute-stream");
418 : : }
419 : :
420 : : static void
421 : 12 : run_async_ready_callback (GcrGnupgProcess *self)
422 : : {
423 : : GAsyncReadyCallback callback;
424 : : gpointer user_data;
425 : :
426 : 12 : g_debug ("running async callback");
427 : :
428 : : /* Remove these before completing */
429 : 12 : callback = self->pv->async_callback;
430 : 12 : user_data = self->pv->user_data;
431 : 12 : self->pv->async_callback = NULL;
432 : 12 : self->pv->user_data = NULL;
433 : :
434 [ + - ]: 12 : if (callback != NULL)
435 : 12 : (callback) (G_OBJECT (self), G_ASYNC_RESULT (self), user_data);
436 : 12 : }
437 : :
438 : : static gboolean
439 : 1 : on_run_async_ready_callback_later (gpointer user_data)
440 : : {
441 : 1 : run_async_ready_callback (GCR_GNUPG_PROCESS (user_data));
442 : 1 : return FALSE; /* Don't run this callback again */
443 : : }
444 : :
445 : : static void
446 : 1 : run_async_ready_callback_later (GcrGnupgProcess *self)
447 : : {
448 : 1 : g_debug ("running async callback later");
449 : 1 : g_idle_add_full (G_PRIORITY_DEFAULT, on_run_async_ready_callback_later,
450 : : g_object_ref (self), g_object_unref);
451 : 1 : }
452 : :
453 : : static void
454 : 12 : complete_run_process (GcrGnupgProcess *self)
455 : : {
456 [ - + ]: 12 : g_return_if_fail (self->pv->running);
457 [ - + ]: 12 : g_return_if_fail (!self->pv->complete);
458 : :
459 : 12 : self->pv->running = FALSE;
460 : 12 : self->pv->complete = TRUE;
461 : :
462 [ + + ]: 12 : if (self->pv->error == NULL) {
463 : 7 : g_debug ("completed process");
464 : : } else {
465 : 5 : g_debug ("completed process with error: %s",
466 : : self->pv->error->message);
467 : : }
468 : : }
469 : :
470 : : static void
471 : 11 : complete_source_is_done (GnupgSource *gnupg_source)
472 : : {
473 : 11 : GcrGnupgProcess *self = gnupg_source->process;
474 : :
475 : 11 : g_debug ("all fds closed and process exited, completing");
476 : :
477 [ - + ]: 11 : g_assert (gnupg_source->child_sig == 0);
478 : :
479 [ + + ]: 11 : if (gnupg_source->cancel_sig) {
480 : 2 : g_signal_handler_disconnect (gnupg_source->cancellable, gnupg_source->cancel_sig);
481 : 2 : gnupg_source->cancel_sig = 0;
482 : : }
483 : :
484 [ + + ]: 11 : g_clear_object (&gnupg_source->cancellable);
485 : :
486 : 11 : complete_run_process (self);
487 : 11 : run_async_ready_callback (self);
488 : :
489 : : /* All done, the source can go away now */
490 : 11 : g_source_unref ((GSource*)gnupg_source);
491 : 11 : }
492 : :
493 : : static void
494 : 117 : close_fd (int *fd)
495 : : {
496 [ - + ]: 117 : g_assert (fd);
497 [ + + ]: 117 : if (*fd >= 0) {
498 : 39 : g_debug ("closing fd: %d", *fd);
499 : 39 : close (*fd);
500 : : }
501 : 117 : *fd = -1;
502 : 117 : }
503 : :
504 : : static void
505 : 36 : close_poll (GSource *source, GPollFD *poll)
506 : : {
507 : 36 : g_source_remove_poll (source, poll);
508 : 36 : close_fd (&poll->fd);
509 : 36 : poll->revents = 0;
510 : 36 : }
511 : :
512 : : static gboolean
513 : 0 : unused_callback (gpointer data)
514 : : {
515 : : /* Never called */
516 : 0 : g_assert_not_reached ();
517 : : return FALSE;
518 : : }
519 : :
520 : : static gboolean
521 : 44 : on_gnupg_source_prepare (GSource *source, gint *timeout_)
522 : : {
523 : 44 : GnupgSource *gnupg_source = (GnupgSource*)source;
524 : : gint i;
525 : :
526 [ + - ]: 76 : for (i = 0; i < NUM_FDS; ++i) {
527 [ + + ]: 76 : if (gnupg_source->polls[i].fd >= 0)
528 : 44 : return FALSE;
529 : : }
530 : :
531 : : /* If none of the FDs are valid, then process immediately */
532 : 0 : return TRUE;
533 : : }
534 : :
535 : : static gboolean
536 : 44 : on_gnupg_source_check (GSource *source)
537 : : {
538 : 44 : GnupgSource *gnupg_source = (GnupgSource*)source;
539 : : gint i;
540 : :
541 [ + + ]: 141 : for (i = 0; i < NUM_FDS; ++i) {
542 [ + + + + ]: 125 : if (gnupg_source->polls[i].fd >= 0 && gnupg_source->polls[i].revents != 0)
543 : 28 : return TRUE;
544 : : }
545 : 16 : return FALSE;
546 : : }
547 : :
548 : : static void
549 : 11 : on_gnupg_source_finalize (GSource *source)
550 : : {
551 : 11 : GnupgSource *gnupg_source = (GnupgSource*)source;
552 : : gint i;
553 : :
554 [ - + ]: 11 : g_assert (gnupg_source->cancellable == NULL);
555 [ - + ]: 11 : g_assert (gnupg_source->cancel_sig == 0);
556 : :
557 [ + + ]: 66 : for (i = 0; i < NUM_FDS; ++i)
558 : 55 : close_fd (&gnupg_source->polls[i].fd);
559 : :
560 : 11 : g_object_unref (gnupg_source->process);
561 [ + + ]: 11 : if (gnupg_source->input_buf)
562 : 1 : g_byte_array_free (gnupg_source->input_buf, TRUE);
563 : 11 : g_string_free (gnupg_source->error_buf, TRUE);
564 : 11 : g_string_free (gnupg_source->status_buf, TRUE);
565 : :
566 [ - + ]: 11 : g_assert (!gnupg_source->child_pid);
567 [ - + ]: 11 : g_assert (!gnupg_source->child_sig);
568 : 11 : }
569 : :
570 : : static gboolean
571 : 11 : read_output (int fd, GByteArray *buffer)
572 : : {
573 : : guchar block[1024];
574 : : gssize result;
575 : :
576 [ - + ]: 11 : g_return_val_if_fail (fd >= 0, FALSE);
577 : :
578 : : do {
579 : 11 : result = read (fd, block, sizeof (block));
580 [ - + ]: 11 : if (result < 0) {
581 [ # # # # ]: 0 : if (errno == EINTR || errno == EAGAIN)
582 : 0 : continue;
583 : 0 : return FALSE;
584 : : } else {
585 : 11 : g_byte_array_append (buffer, block, result);
586 : : }
587 [ - + ]: 11 : } while (result == sizeof (block));
588 : :
589 : 11 : return TRUE;
590 : : }
591 : :
592 : : static gboolean
593 : 1 : write_input (int fd, GByteArray *buffer)
594 : : {
595 : : gssize result;
596 : :
597 [ - + ]: 1 : g_return_val_if_fail (fd >= 0, FALSE);
598 : :
599 : : for (;;) {
600 : 1 : result = write (fd, buffer->data, buffer->len);
601 [ - + ]: 1 : if (result < 0) {
602 [ # # # # ]: 0 : if (errno == EINTR || errno == EAGAIN)
603 : 0 : continue;
604 : 0 : return FALSE;
605 : : } else {
606 : 1 : g_byte_array_remove_range (buffer, 0, result);
607 : 1 : return TRUE;
608 : : }
609 : : }
610 : : }
611 : :
612 : : static void
613 : 2 : emit_status_for_each_line (const gchar *line, gpointer user_data)
614 : : {
615 : : GcrRecord *record;
616 : :
617 [ + - ]: 2 : if (g_str_has_prefix (line, "[GNUPG:] ")) {
618 : 2 : g_debug ("received status line: %s", line);
619 : 2 : line += 9;
620 : : } else {
621 : 0 : g_message ("gnupg status record was not prefixed appropriately: %s", line);
622 : 0 : return;
623 : : }
624 : :
625 : 2 : record = _gcr_record_parse_spaces (line, -1);
626 [ - + ]: 2 : if (!record) {
627 : 0 : g_message ("couldn't parse status record: %s", line);
628 : 0 : return;
629 : : }
630 : :
631 : 2 : g_signal_emit (GCR_GNUPG_PROCESS (user_data), signals[STATUS_RECORD], 0, record);
632 : 2 : _gcr_record_free (record);
633 : : }
634 : :
635 : : static void
636 : 4 : emit_error_for_each_line (const gchar *line, gpointer user_data)
637 : : {
638 : 4 : g_debug ("received error line: %s", line);
639 : 4 : g_signal_emit (GCR_GNUPG_PROCESS (user_data), signals[ERROR_LINE], 0, line);
640 : 4 : }
641 : :
642 : : static gboolean
643 : 12 : on_gnupg_source_input (GcrGnupgProcess *self,
644 : : GnupgSource *gnupg_source,
645 : : gint fd)
646 : : {
647 : : gssize read;
648 : :
649 [ + + ]: 12 : if (gnupg_source->input_buf == NULL ||
650 [ + - ]: 1 : gnupg_source->input_buf->len == 0) {
651 [ + + ]: 12 : if (self->pv->input == NULL)
652 : 10 : return FALSE;
653 [ + + ]: 2 : if (!gnupg_source->input_buf)
654 : 1 : gnupg_source->input_buf = g_byte_array_new ();
655 : 2 : g_byte_array_set_size (gnupg_source->input_buf, 4096);
656 : 2 : read = g_input_stream_read (self->pv->input,
657 : 2 : gnupg_source->input_buf->data,
658 : 2 : gnupg_source->input_buf->len,
659 : : gnupg_source->cancellable, NULL);
660 : 2 : g_byte_array_set_size (gnupg_source->input_buf, read < 0 ? 0 : read);
661 [ - + ]: 2 : if (read < 0)
662 : 0 : return FALSE;
663 [ + + ]: 2 : if (read == 0)
664 : 1 : return FALSE;
665 : : }
666 : :
667 [ - + ]: 1 : if (!write_input (fd, gnupg_source->input_buf)) {
668 : 0 : g_warning ("couldn't write output data to gnupg process");
669 : 0 : return FALSE;
670 : : }
671 : :
672 : 1 : return TRUE;
673 : : }
674 : :
675 : : static gboolean
676 : 2 : on_gnupg_source_status (GcrGnupgProcess *self,
677 : : GnupgSource *gnupg_source,
678 : : gint fd)
679 : : {
680 : 2 : GByteArray *buffer = g_byte_array_new ();
681 : 2 : gboolean result = TRUE;
682 : :
683 [ - + ]: 2 : if (!read_output (fd, buffer)) {
684 : 0 : g_warning ("couldn't read status data from gnupg process");
685 : 0 : result = FALSE;
686 : : } else {
687 : 2 : g_string_append_len (gnupg_source->status_buf, (gchar*)buffer->data, buffer->len);
688 : 2 : _gcr_util_parse_lines (gnupg_source->status_buf, buffer->len == 0,
689 : : emit_status_for_each_line, self);
690 : : }
691 : :
692 : 2 : g_byte_array_unref (buffer);
693 : 2 : return result;
694 : : }
695 : :
696 : : static gboolean
697 : 1 : on_gnupg_source_attribute (GcrGnupgProcess *self,
698 : : GnupgSource *gnupg_source,
699 : : gint fd)
700 : : {
701 : 1 : GByteArray *buffer = g_byte_array_new ();
702 : 1 : gboolean result = TRUE;
703 : :
704 [ - + ]: 1 : if (!read_output (fd, buffer)) {
705 : 0 : g_warning ("couldn't read attribute data from gnupg process");
706 : 0 : result = FALSE;
707 [ + - ]: 1 : } else if (buffer->len > 0) {
708 : 1 : g_debug ("received %d bytes of attribute data", (gint)buffer->len);
709 [ + - ]: 1 : if (self->pv->attributes != NULL)
710 : 1 : g_output_stream_write_all (self->pv->attributes, buffer->data,
711 : 1 : buffer->len, NULL,
712 : : gnupg_source->cancellable, NULL);
713 : : }
714 : :
715 : 1 : g_byte_array_unref (buffer);
716 : 1 : return result;
717 : : }
718 : :
719 : : static gboolean
720 : 6 : on_gnupg_source_output (GcrGnupgProcess *self,
721 : : GnupgSource *gnupg_source,
722 : : gint fd)
723 : : {
724 : 6 : GByteArray *buffer = g_byte_array_new ();
725 : 6 : gboolean result = TRUE;
726 : :
727 [ - + ]: 6 : if (!read_output (fd, buffer)) {
728 : 0 : g_warning ("couldn't read output data from gnupg process");
729 : 0 : result = FALSE;
730 [ + - ]: 6 : } else if (buffer->len > 0) {
731 : 6 : g_debug ("received %d bytes of output data", (gint)buffer->len);
732 [ + - ]: 6 : if (self->pv->output != NULL)
733 : 6 : g_output_stream_write_all (self->pv->output, buffer->data, buffer->len,
734 : : NULL, gnupg_source->cancellable, NULL);
735 : : }
736 : :
737 : 6 : g_byte_array_unref (buffer);
738 : 6 : return result;
739 : : }
740 : :
741 : : static gboolean
742 : 2 : on_gnupg_source_error (GcrGnupgProcess *self,
743 : : GnupgSource *gnupg_source,
744 : : gint fd,
745 : : gboolean last)
746 : : {
747 : 2 : GByteArray *buffer = g_byte_array_new ();
748 : 2 : gboolean result = TRUE;
749 : :
750 [ - + ]: 2 : if (!read_output (fd, buffer)) {
751 : 0 : g_warning ("couldn't read error data from gnupg process");
752 : 0 : result = FALSE;
753 : : } else {
754 : 2 : g_string_append_len (gnupg_source->error_buf, (gchar*)buffer->data, buffer->len);
755 : 2 : _gcr_util_parse_lines (gnupg_source->error_buf, last,
756 : 2 : emit_error_for_each_line, gnupg_source->process);
757 : : }
758 : :
759 : 2 : g_byte_array_unref (buffer);
760 : 2 : return result;
761 : : }
762 : :
763 : : static gboolean
764 : 28 : on_gnupg_source_dispatch (GSource *source, GSourceFunc unused, gpointer user_data)
765 : : {
766 : 28 : GnupgSource *gnupg_source = (GnupgSource*)source;
767 : 28 : GcrGnupgProcess *self = gnupg_source->process;
768 : : GPollFD *poll;
769 : : guint i;
770 : :
771 : : /* Standard input, no support yet */
772 : 28 : poll = &gnupg_source->polls[FD_INPUT];
773 [ + + ]: 28 : if (poll->fd >= 0) {
774 [ + - ]: 12 : if (poll->revents & G_IO_OUT)
775 [ + + ]: 12 : if (!on_gnupg_source_input (self, gnupg_source, poll->fd))
776 : 11 : poll->revents |= G_IO_HUP;
777 [ + + ]: 12 : if (poll->revents & G_IO_HUP)
778 : 11 : close_poll (source, poll);
779 : 12 : poll->revents = 0;
780 : : }
781 : :
782 : : /* Status output */
783 : 28 : poll = &gnupg_source->polls[FD_STATUS];
784 [ + + ]: 28 : if (poll->fd >= 0) {
785 [ + + ]: 4 : if (poll->revents & G_IO_IN)
786 [ - + ]: 2 : if (!on_gnupg_source_status (self, gnupg_source, poll->fd))
787 : 0 : poll->revents |= G_IO_HUP;
788 [ + + ]: 4 : if (poll->revents & G_IO_HUP)
789 : 2 : close_poll (source, poll);
790 : 4 : poll->revents = 0;
791 : : }
792 : :
793 : : /* Attribute output */
794 : 28 : poll = &gnupg_source->polls[FD_ATTRIBUTE];
795 [ + + ]: 28 : if (poll->fd >= 0) {
796 [ + - ]: 1 : if (poll->revents & G_IO_IN)
797 [ - + ]: 1 : if (!on_gnupg_source_attribute (self, gnupg_source, poll->fd))
798 : 0 : poll->revents |= G_IO_HUP;
799 [ + - ]: 1 : if (poll->revents & G_IO_HUP)
800 : 1 : close_poll (source, poll);
801 : 1 : poll->revents = 0;
802 : : }
803 : :
804 : : /* Standard output */
805 : 28 : poll = &gnupg_source->polls[FD_OUTPUT];
806 [ + - ]: 28 : if (poll->fd >= 0) {
807 [ + + ]: 28 : if (poll->revents & G_IO_IN)
808 [ - + ]: 6 : if (!on_gnupg_source_output (self, gnupg_source, poll->fd))
809 : 0 : poll->revents |= G_IO_HUP;
810 [ + + ]: 28 : if (poll->revents & G_IO_HUP)
811 : 11 : close_poll (source, poll);
812 : 28 : poll->revents = 0;
813 : : }
814 : :
815 : : /* Standard error */
816 : 28 : poll = &gnupg_source->polls[FD_ERROR];
817 [ + - ]: 28 : if (poll->fd >= 0) {
818 [ + + ]: 28 : if (poll->revents & G_IO_IN)
819 [ - + ]: 2 : if (!on_gnupg_source_error (self, gnupg_source, poll->fd,
820 : 2 : (poll->revents & G_IO_HUP) ? TRUE : FALSE))
821 : 0 : poll->revents |= G_IO_HUP;
822 [ + + ]: 28 : if (poll->revents & G_IO_HUP)
823 : 11 : close_poll (source, poll);
824 : 28 : poll->revents = 0;
825 : : }
826 : :
827 [ + + ]: 99 : for (i = 0; i < NUM_FDS; ++i) {
828 [ + + ]: 88 : if (gnupg_source->polls[i].fd >= 0)
829 : 17 : return TRUE;
830 : : }
831 : :
832 : : /* Because we return below */
833 : 11 : self->pv->source_sig = 0;
834 : :
835 [ - + ]: 11 : if (!gnupg_source->child_pid)
836 : 0 : complete_source_is_done (gnupg_source);
837 : :
838 : 11 : return FALSE; /* Disconnect this source */
839 : : }
840 : :
841 : : static GSourceFuncs gnupg_source_funcs = {
842 : : on_gnupg_source_prepare,
843 : : on_gnupg_source_check,
844 : : on_gnupg_source_dispatch,
845 : : on_gnupg_source_finalize,
846 : : };
847 : :
848 : : static void
849 : 11 : on_gnupg_process_child_exited (GPid pid, gint status, gpointer user_data)
850 : : {
851 : 11 : GnupgSource *gnupg_source = user_data;
852 : 11 : GcrGnupgProcess *self = gnupg_source->process;
853 : 11 : GError *error = NULL;
854 : : gint code;
855 : : guint i;
856 : :
857 : 11 : g_debug ("process exited: %d", (int)pid);
858 : :
859 : 11 : g_spawn_close_pid (gnupg_source->child_pid);
860 : 11 : gnupg_source->child_pid = 0;
861 : 11 : gnupg_source->child_sig = 0;
862 : :
863 [ + + ]: 11 : if (WIFEXITED (status)) {
864 : 9 : code = WEXITSTATUS (status);
865 [ + + ]: 9 : if (code != 0) {
866 : 1 : error = g_error_new (G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
867 : : _("Gnupg process exited with code: %d"), code);
868 : : }
869 [ + - ]: 2 : } else if (WIFSIGNALED (status)) {
870 : 2 : code = WTERMSIG (status);
871 : : /* Ignore cases where we've signaled the process because we were cancelled */
872 [ + + ]: 2 : if (!g_error_matches (self->pv->error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
873 : 1 : error = g_error_new (G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
874 : : _("Gnupg process was terminated with signal: %d"), code);
875 : : }
876 : :
877 : : /* Take this as the async result error */
878 [ + + + - ]: 11 : if (error && !self->pv->error) {
879 : 2 : g_debug ("%s", error->message);
880 : 2 : self->pv->error = error;
881 : :
882 : : /* Already have an error, just print out message */
883 [ - + ]: 9 : } else if (error) {
884 : 0 : g_warning ("%s", error->message);
885 : 0 : g_error_free (error);
886 : : }
887 : :
888 [ + + ]: 66 : for (i = 0; i < NUM_FDS; ++i) {
889 [ - + ]: 55 : if (gnupg_source->polls[i].fd >= 0)
890 : 0 : return;
891 : : }
892 : :
893 : 11 : complete_source_is_done (gnupg_source);
894 : : }
895 : :
896 : : static void
897 : 0 : on_gnupg_process_child_setup (gpointer user_data)
898 : : {
899 : 0 : int *child_fds = user_data;
900 : : long val;
901 : : guint i;
902 : :
903 : : /*
904 : : * Clear close-on-exec flag for these file descriptors, so that
905 : : * gnupg can write to them
906 : : */
907 : :
908 [ # # ]: 0 : for (i = 0; i < NUM_FDS; i++) {
909 [ # # ]: 0 : if (child_fds[i] >= 0) {
910 : 0 : val = fcntl (child_fds[i], F_GETFD);
911 : 0 : fcntl (child_fds[i], F_SETFD, val & ~FD_CLOEXEC);
912 : : }
913 : : }
914 : 0 : }
915 : :
916 : : static void
917 : 2 : on_cancellable_cancelled (GCancellable *cancellable, gpointer user_data)
918 : : {
919 : 2 : GnupgSource *gnupg_source = user_data;
920 : :
921 [ - + ]: 2 : g_assert (gnupg_source->process);
922 : :
923 : 2 : g_debug ("process cancelled");
924 : :
925 : : /* Set an error, which is respected when this actually completes. */
926 [ + - ]: 2 : if (gnupg_source->process->pv->error == NULL)
927 : 2 : gnupg_source->process->pv->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
928 : : _("The operation was cancelled"));
929 : :
930 : : /* Try and kill the child process */
931 [ + - ]: 2 : if (gnupg_source->child_pid) {
932 : 2 : g_debug ("sending term signal to process: %d",
933 : : (int)gnupg_source->child_pid);
934 : 2 : kill (gnupg_source->child_pid, SIGTERM);
935 : : }
936 : 2 : }
937 : :
938 : : /**
939 : : * _gcr_gnupg_process_run_async:
940 : : * @self: The process
941 : : * @argv: (array zero-terminated=1): The arguments for the process, not including executable, terminated with %NULL.
942 : : * @envp: (nullable) (array zero-terminated=1): The environment for new process, terminated with %NULL.
943 : : * @flags: Flags for starting the process.
944 : : * @cancellable: (nullable): Cancellation object
945 : : * @callback: Will be called when operation completes.
946 : : * @user_data: (closure): Data passed to callback.
947 : : *
948 : : * Run the gpg process. Only one 'run' operation can run per GcrGnupgProcess
949 : : * object. The GcrGnupgProcess:output_data and GcrGnupgProcess:error_line
950 : : * signals will be emitted when data is received from the gpg process.
951 : : *
952 : : * Unless the %GCR_GNUPG_PROCESS_RESPECT_LOCALE flag is specified, the process
953 : : * will be run in the 'C' locale. If the %GCR_GNUPG_PROCESS_WITH_STATUS or
954 : : * %GCR_GNUPG_PROCESS_WITH_ATTRIBUTES flags are set, then the gpg process
955 : : * will be status and attribute output respectively. The
956 : : * GcrGnupgProcess:status_record and GcrGnupgProcess:attribute_data signals
957 : : * will provide this data.
958 : : */
959 : : void
960 : 12 : _gcr_gnupg_process_run_async (GcrGnupgProcess *self, const gchar **argv, const gchar **envp,
961 : : GcrGnupgProcessFlags flags, GCancellable *cancellable,
962 : : GAsyncReadyCallback callback, gpointer user_data)
963 : : {
964 : 12 : GError *error = NULL;
965 : : GPtrArray *args;
966 : : GPtrArray *envs;
967 : : int child_fds[NUM_FDS];
968 : 12 : int status_fds[2] = { -1, -1 };
969 : 12 : int attribute_fds[2] = { -1, -1 };
970 : 12 : int output_fd = -1;
971 : 12 : int error_fd = -1;
972 : 12 : int input_fd = -1;
973 : : GnupgSource *gnupg_source;
974 : : GSource *source;
975 : : GPid pid;
976 : : guint i;
977 : :
978 [ - + + - : 13 : g_return_if_fail (GCR_IS_GNUPG_PROCESS (self));
+ - - + ]
979 [ - + ]: 12 : g_return_if_fail (argv);
980 [ - + ]: 12 : g_return_if_fail (callback);
981 [ + + - + : 12 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ - + - -
+ ]
982 : :
983 [ - + ]: 12 : g_return_if_fail (self->pv->running == FALSE);
984 [ - + ]: 12 : g_return_if_fail (self->pv->complete == FALSE);
985 [ - + ]: 12 : g_return_if_fail (self->pv->executable);
986 : :
987 : 12 : self->pv->async_callback = callback;
988 : 12 : self->pv->user_data = user_data;
989 : :
990 [ + + ]: 72 : for (i = 0; i < NUM_FDS; i++)
991 : 60 : child_fds[i] = -1;
992 : :
993 : : /* The command needs to be updated with these status and attribute fds */
994 : 12 : args = g_ptr_array_new_with_free_func (g_free);
995 : 12 : g_ptr_array_add (args, g_strdup (self->pv->executable));
996 : :
997 : : /* Spawn/child will close all other attributes, besides thesthose in child_fds */
998 : 12 : child_fds[FD_INPUT] = 0;
999 : 12 : child_fds[FD_OUTPUT] = 1;
1000 : 12 : child_fds[FD_ERROR] = 2;
1001 : :
1002 [ + + ]: 12 : if (flags & GCR_GNUPG_PROCESS_WITH_STATUS) {
1003 [ - + ]: 2 : if (pipe (status_fds) < 0)
1004 : 0 : g_return_if_reached ();
1005 : 2 : child_fds[FD_STATUS] = status_fds[1];
1006 : 2 : g_ptr_array_add (args, g_strdup ("--status-fd"));
1007 : 2 : g_ptr_array_add (args, g_strdup_printf ("%d", child_fds[FD_STATUS]));
1008 : : }
1009 [ + + ]: 12 : if (flags & GCR_GNUPG_PROCESS_WITH_ATTRIBUTES) {
1010 [ - + ]: 1 : if (pipe (attribute_fds) < 0)
1011 : 0 : g_return_if_reached ();
1012 : 1 : child_fds[FD_ATTRIBUTE] = attribute_fds[1];
1013 : 1 : g_ptr_array_add (args, g_strdup ("--attribute-fd"));
1014 : 1 : g_ptr_array_add (args, g_strdup_printf ("%d", child_fds[FD_ATTRIBUTE]));
1015 : : }
1016 : :
1017 [ + + ]: 12 : if (self->pv->directory) {
1018 : 2 : g_ptr_array_add (args, g_strdup ("--homedir"));
1019 : 2 : g_ptr_array_add (args, g_strdup (self->pv->directory));
1020 : : }
1021 : :
1022 : : /* All the remaining arguments */
1023 [ + + ]: 20 : for (i = 0; argv[i] != NULL; i++)
1024 : 8 : g_ptr_array_add (args, g_strdup (argv[i]));
1025 : 12 : g_ptr_array_add (args, NULL);
1026 : :
1027 : 12 : envs = g_ptr_array_new ();
1028 [ + + + + ]: 14 : for (i = 0; envp && envp[i] != NULL; i++) {
1029 [ + - + - ]: 4 : if (flags & GCR_GNUPG_PROCESS_RESPECT_LOCALE ||
1030 : 2 : !g_str_has_prefix (envp[i], "LOCALE="))
1031 : 2 : g_ptr_array_add (envs, (gpointer)envp[i]);
1032 : : }
1033 [ + - ]: 12 : if (!(flags & GCR_GNUPG_PROCESS_RESPECT_LOCALE))
1034 : 12 : g_ptr_array_add (envs, (gpointer)"LOCALE=C");
1035 : 12 : g_ptr_array_add (envs, NULL);
1036 : :
1037 : 12 : gchar *command = g_strjoinv (" ", (gchar**)args->pdata);
1038 : 12 : gchar *environ = g_strjoinv (", ", (gchar**)envs->pdata);
1039 : 12 : g_debug ("running command: %s", command);
1040 : 12 : g_debug ("process environment: %s", environ);
1041 : 12 : g_free (command);
1042 : 12 : g_free (environ);
1043 : :
1044 : 12 : g_spawn_async_with_pipes (self->pv->directory, (gchar**)args->pdata,
1045 : 12 : (gchar**)envs->pdata, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_CLOEXEC_PIPES,
1046 : : on_gnupg_process_child_setup, child_fds,
1047 : : &pid, &input_fd, &output_fd, &error_fd, &error);
1048 : :
1049 : 12 : g_ptr_array_free (args, TRUE);
1050 : 12 : g_ptr_array_free (envs, TRUE);
1051 : :
1052 : : /* Close 'wrong' ends of extra file descriptors */
1053 : 12 : close_fd (&(status_fds[1]));
1054 : 12 : close_fd (&(attribute_fds[1]));
1055 : :
1056 : 12 : self->pv->complete = FALSE;
1057 : 12 : self->pv->running = TRUE;
1058 : :
1059 [ + + ]: 12 : if (error) {
1060 : 1 : close_fd (&(status_fds[0]));
1061 : 1 : close_fd (&(attribute_fds[0]));
1062 [ - + ]: 1 : g_assert (!self->pv->error);
1063 : 1 : self->pv->error = error;
1064 : 1 : complete_run_process (self);
1065 : 1 : run_async_ready_callback_later (self);
1066 : 1 : return;
1067 : : }
1068 : :
1069 : 11 : g_debug ("process started: %d", (int)pid);
1070 : :
1071 : 11 : source = g_source_new (&gnupg_source_funcs, sizeof (GnupgSource));
1072 : :
1073 : : /* Initialize the source */
1074 : 11 : gnupg_source = (GnupgSource*)source;
1075 [ + + ]: 66 : for (i = 0; i < NUM_FDS; i++)
1076 : 55 : gnupg_source->polls[i].fd = -1;
1077 : 11 : gnupg_source->error_buf = g_string_sized_new (128);
1078 : 11 : gnupg_source->status_buf = g_string_sized_new (128);
1079 : 11 : gnupg_source->process = g_object_ref (self);
1080 : 11 : gnupg_source->child_pid = pid;
1081 : :
1082 : 11 : gnupg_source->polls[FD_INPUT].fd = input_fd;
1083 [ + - ]: 11 : if (input_fd >= 0) {
1084 : 11 : gnupg_source->polls[FD_INPUT].events = G_IO_HUP | G_IO_OUT;
1085 : 11 : g_source_add_poll (source, &gnupg_source->polls[FD_INPUT]);
1086 : : }
1087 : 11 : gnupg_source->polls[FD_OUTPUT].fd = output_fd;
1088 [ + - ]: 11 : if (output_fd >= 0) {
1089 : 11 : gnupg_source->polls[FD_OUTPUT].events = G_IO_HUP | G_IO_IN;
1090 : 11 : g_source_add_poll (source, &gnupg_source->polls[FD_OUTPUT]);
1091 : : }
1092 : 11 : gnupg_source->polls[FD_ERROR].fd = error_fd;
1093 [ + - ]: 11 : if (error_fd >= 0) {
1094 : 11 : gnupg_source->polls[FD_ERROR].events = G_IO_HUP | G_IO_IN;
1095 : 11 : g_source_add_poll (source, &gnupg_source->polls[FD_ERROR]);
1096 : : }
1097 : 11 : gnupg_source->polls[FD_STATUS].fd = status_fds[0];
1098 [ + + ]: 11 : if (status_fds[0] >= 0) {
1099 : 2 : gnupg_source->polls[FD_STATUS].events = G_IO_HUP | G_IO_IN;
1100 : 2 : g_source_add_poll (source, &gnupg_source->polls[FD_STATUS]);
1101 : : }
1102 : 11 : gnupg_source->polls[FD_ATTRIBUTE].fd = attribute_fds[0];
1103 [ + + ]: 11 : if (attribute_fds[0] >= 0) {
1104 : 1 : gnupg_source->polls[FD_ATTRIBUTE].events = G_IO_HUP | G_IO_IN;
1105 : 1 : g_source_add_poll (source, &gnupg_source->polls[FD_ATTRIBUTE]);
1106 : : }
1107 : :
1108 [ + + ]: 11 : if (cancellable) {
1109 : 2 : gnupg_source->cancellable = g_object_ref (cancellable);
1110 : 2 : gnupg_source->cancel_sig = g_cancellable_connect (cancellable,
1111 : : G_CALLBACK (on_cancellable_cancelled),
1112 : 2 : g_source_ref (source),
1113 : : (GDestroyNotify)g_source_unref);
1114 : : }
1115 : :
1116 [ - + ]: 11 : g_assert (self->pv->source_sig == 0);
1117 : 11 : g_source_set_callback (source, unused_callback, NULL, NULL);
1118 : 11 : self->pv->source_sig = g_source_attach (source, g_main_context_default ());
1119 : :
1120 : : /* This assumes the outstanding reference to source */
1121 [ - + ]: 11 : g_assert (gnupg_source->child_sig == 0);
1122 : 11 : gnupg_source->child_sig = g_child_watch_add_full (G_PRIORITY_DEFAULT, pid,
1123 : : on_gnupg_process_child_exited,
1124 : 11 : g_source_ref (source),
1125 : : (GDestroyNotify)g_source_unref);
1126 : :
1127 : : /* source is unreffed in complete_if_source_is_done() */
1128 : : }
1129 : :
1130 : : /**
1131 : : * _gcr_gnupg_process_run_finish:
1132 : : * @self: The process
1133 : : * @result: The result passed to the callback
1134 : : * @error: Location to raise an error on failure.
1135 : : *
1136 : : * Get the result of running a gnupg process.
1137 : : *
1138 : : * Return value: Whether the Gnupg process was run or not.
1139 : : */
1140 : : gboolean
1141 : 12 : _gcr_gnupg_process_run_finish (GcrGnupgProcess *self, GAsyncResult *result,
1142 : : GError **error)
1143 : : {
1144 [ - + + - : 12 : g_return_val_if_fail (GCR_IS_GNUPG_PROCESS (self), FALSE);
+ - - + ]
1145 [ + - - + ]: 12 : g_return_val_if_fail (!error || !*error, FALSE);
1146 [ - + ]: 12 : g_return_val_if_fail (G_ASYNC_RESULT (self) == result, FALSE);
1147 [ - + ]: 12 : g_return_val_if_fail (self->pv->complete, FALSE);
1148 : :
1149 : : /* This allows the process to run again... */
1150 : 12 : self->pv->complete = FALSE;
1151 : :
1152 [ - + ]: 12 : g_assert (!self->pv->running);
1153 [ - + ]: 12 : g_assert (!self->pv->async_callback);
1154 [ - + ]: 12 : g_assert (!self->pv->user_data);
1155 : :
1156 [ + + ]: 12 : if (self->pv->error) {
1157 : 5 : g_propagate_error (error, self->pv->error);
1158 : 5 : self->pv->error = NULL;
1159 : 5 : return FALSE;
1160 : : }
1161 : :
1162 : 7 : return TRUE;
1163 : : }
|