1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "array.h"
6 #include "str.h"
7 #include "str-sanitize.h"
8 #include "safe-mkstemp.h"
9 #include "istream-private.h"
10 #include "ostream-dot.h"
11 #include "istream-dot.h"
12 #include "ostream.h"
13 #include "iostream-pump.h"
14 #include "iostream-temp.h"
15 #include "lib-signals.h"
16
17 #include "program-client-private.h"
18
19 #include <unistd.h>
20
21 #define MAX_OUTPUT_BUFFER_SIZE 16384
22 #define MAX_OUTPUT_MEMORY_BUFFER (1024*128)
23
program_client_set_label(struct program_client * pclient,const char * label)24 void program_client_set_label(struct program_client *pclient,
25 const char *label)
26 {
27 event_set_append_log_prefix(pclient->event,
28 t_strconcat("program ", label, ": ", NULL));
29 }
30
31 static void
program_client_callback(struct program_client * pclient,int result,void * context)32 program_client_callback(struct program_client *pclient, int result,
33 void *context)
34 {
35 program_client_callback_t *callback = pclient->callback;
36
37 pclient->callback = NULL;
38 if (pclient->destroying || callback == NULL)
39 return;
40 callback(result, context);
41 }
42
43 static void
program_client_timeout(struct program_client * pclient)44 program_client_timeout(struct program_client *pclient)
45 {
46 e_error(pclient->event,
47 "Execution timed out (> %u msecs)",
48 pclient->set.input_idle_timeout_msecs);
49 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_RUN_TIMEOUT);
50 }
51
52 static void
program_client_connect_timeout(struct program_client * pclient)53 program_client_connect_timeout(struct program_client *pclient)
54 {
55 e_error(pclient->event,
56 "Connection timed out (> %u msecs)",
57 pclient->set.client_connect_timeout_msecs);
58 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT);
59 }
60
61 static int
program_client_connect(struct program_client * pclient)62 program_client_connect(struct program_client *pclient)
63 {
64 e_debug(pclient->event, "Establishing connection");
65
66 if (pclient->set.client_connect_timeout_msecs != 0) {
67 pclient->to = timeout_add(
68 pclient->set.client_connect_timeout_msecs,
69 program_client_connect_timeout, pclient);
70 }
71
72 return pclient->connect(pclient);
73 }
74
75 static int
program_client_close_output(struct program_client * pclient)76 program_client_close_output(struct program_client *pclient)
77 {
78 int ret;
79
80 o_stream_destroy(&pclient->program_output);
81 o_stream_destroy(&pclient->raw_program_output);
82 if ((ret = pclient->close_output(pclient)) < 0)
83 return -1;
84
85 return ret;
86 }
87
88 static void
program_client_disconnect_extra_fds(struct program_client * pclient)89 program_client_disconnect_extra_fds(struct program_client *pclient)
90 {
91 struct program_client_extra_fd *efds;
92 unsigned int i, count;
93
94 if (!array_is_created(&pclient->extra_fds))
95 return;
96
97 efds = array_get_modifiable(&pclient->extra_fds, &count);
98 for(i = 0; i < count; i++) {
99 i_stream_unref(&efds[i].input);
100 io_remove(&efds[i].io);
101 if (efds[i].parent_fd != -1)
102 i_close_fd(&efds[i].parent_fd);
103 }
104
105 array_clear(&pclient->extra_fds);
106 }
107
108 static void
program_client_do_disconnect(struct program_client * pclient)109 program_client_do_disconnect(struct program_client *pclient)
110 {
111 i_stream_destroy(&pclient->program_input);
112 o_stream_destroy(&pclient->program_output);
113 i_stream_destroy(&pclient->raw_program_input);
114 o_stream_destroy(&pclient->raw_program_output);
115
116 timeout_remove(&pclient->to);
117 io_remove(&pclient->io);
118 iostream_pump_destroy(&pclient->pump_in);
119 iostream_pump_destroy(&pclient->pump_out);
120
121 if (pclient->fd_out == pclient->fd_in)
122 pclient->fd_in = -1;
123 i_close_fd(&pclient->fd_in);
124 i_close_fd(&pclient->fd_out);
125
126 program_client_disconnect_extra_fds(pclient);
127
128 if (!pclient->disconnected)
129 e_debug(pclient->event, "Disconnected");
130 pclient->disconnected = TRUE;
131 }
132
program_client_disconnected(struct program_client * pclient)133 void program_client_disconnected(struct program_client *pclient)
134 {
135 program_client_do_disconnect(pclient);
136
137 if (pclient->other_error &&
138 pclient->error == PROGRAM_CLIENT_ERROR_NONE) {
139 pclient->error = PROGRAM_CLIENT_ERROR_OTHER;
140 }
141
142 program_client_callback(pclient,
143 (pclient->error != PROGRAM_CLIENT_ERROR_NONE ?
144 PROGRAM_CLIENT_EXIT_STATUS_INTERNAL_FAILURE :
145 pclient->exit_status),
146 pclient->context);
147 }
148
149 static void
program_client_disconnect(struct program_client * pclient,bool force)150 program_client_disconnect(struct program_client *pclient, bool force)
151 {
152 if (pclient->disconnected)
153 return;
154
155 program_client_do_disconnect(pclient);
156 pclient->disconnect(pclient, force);
157 }
158
program_client_fail(struct program_client * pclient,enum program_client_error error)159 void program_client_fail(struct program_client *pclient,
160 enum program_client_error error)
161 {
162 if (pclient->error != PROGRAM_CLIENT_ERROR_NONE)
163 return;
164
165 e_debug(pclient->event, "Failed to run program");
166
167 pclient->error = error;
168 program_client_disconnect(pclient, TRUE);
169 }
170
171 static bool
program_client_input_pending(struct program_client * pclient)172 program_client_input_pending(struct program_client *pclient)
173 {
174 struct program_client_extra_fd *efds = NULL;
175 unsigned int count, i;
176
177 if (pclient->pump_in != NULL || pclient->pump_out != NULL)
178 return TRUE;
179
180 if (pclient->program_output != NULL &&
181 !pclient->program_output->closed &&
182 o_stream_get_buffer_used_size(pclient->program_output) > 0) {
183 return TRUE;
184 }
185 if (pclient->program_input != NULL &&
186 !pclient->program_input->closed &&
187 i_stream_have_bytes_left(pclient->program_input)) {
188 return TRUE;
189 }
190
191 if (array_is_created(&pclient->extra_fds)) {
192 efds = array_get_modifiable(&pclient->extra_fds, &count);
193 for(i = 0; i < count; i++) {
194 if (efds[i].input != NULL &&
195 !efds[i].input->closed &&
196 i_stream_have_bytes_left(efds[i].input)) {
197 return TRUE;
198 }
199 }
200 }
201
202 return FALSE;
203 }
204
205 static void
program_client_output_finished(struct program_client * pclient)206 program_client_output_finished(struct program_client *pclient)
207 {
208 e_debug(pclient->event, "Finished input to program");
209
210 /* check whether program i/o is finished */
211 if (!program_client_input_pending(pclient)) {
212 /* finished */
213 program_client_disconnect(pclient, FALSE);
214 /* close output towards program, so that it reads EOF */
215 } else if (program_client_close_output(pclient) < 0) {
216 program_client_fail(pclient,
217 PROGRAM_CLIENT_ERROR_OTHER);
218 }
219 }
220
221 static int
program_client_output_finish(struct program_client * pclient)222 program_client_output_finish(struct program_client *pclient)
223 {
224 struct ostream *output = pclient->program_output;
225 int ret = 0;
226
227 /* flush the output */
228 if ((ret=o_stream_finish(output)) < 0) {
229 e_error(pclient->event,
230 "write(%s) failed: %s",
231 o_stream_get_name(output),
232 o_stream_get_error(output));
233 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
234 return -1;
235 }
236 if (ret > 0)
237 program_client_output_finished(pclient);
238 return ret;
239 }
240
241 static void
program_client_output_pump_finished(enum iostream_pump_status status,struct program_client * pclient)242 program_client_output_pump_finished(enum iostream_pump_status status,
243 struct program_client *pclient)
244 {
245 struct istream *input = pclient->input;
246 struct ostream *output = pclient->program_output;
247
248 i_assert(input != NULL);
249 i_assert(output != NULL);
250
251 switch (status) {
252 case IOSTREAM_PUMP_STATUS_INPUT_EOF:
253 break;
254 case IOSTREAM_PUMP_STATUS_INPUT_ERROR:
255 e_error(pclient->event,
256 "read(%s) failed: %s",
257 i_stream_get_name(input),
258 i_stream_get_error(input));
259 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
260 return;
261 case IOSTREAM_PUMP_STATUS_OUTPUT_ERROR:
262 e_error(pclient->event,
263 "write(%s) failed: %s",
264 o_stream_get_name(output),
265 o_stream_get_error(output));
266 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
267 return;
268 }
269
270 iostream_pump_destroy(&pclient->pump_out);
271
272 e_debug(pclient->event, "Finished streaming payload to program");
273
274 o_stream_set_flush_callback(pclient->program_output,
275 program_client_output_finish, pclient);
276 o_stream_set_flush_pending(pclient->program_output, TRUE);
277 }
278
279 static void
program_client_input_finished(struct program_client * pclient)280 program_client_input_finished(struct program_client *pclient)
281 {
282 e_debug(pclient->event, "Finished output from program");
283
284 /* check whether program i/o is finished */
285 if (program_client_input_pending(pclient))
286 return;
287
288 /* finished */
289 program_client_disconnect(pclient, FALSE);
290 }
291
292 static void
program_client_input_finish(struct program_client * pclient)293 program_client_input_finish(struct program_client *pclient)
294 {
295 struct istream *input = pclient->program_input;
296 const unsigned char *data;
297 size_t size;
298 int ret;
299
300 /* read (the remainder of) the raw program input */
301 while ((ret=i_stream_read_more(input, &data, &size)) > 0)
302 i_stream_skip(input, size);
303 if (ret == 0)
304 return;
305 if (ret < 0) {
306 if (input->stream_errno != 0) {
307 e_error(pclient->event,
308 "read(%s) failed: %s",
309 i_stream_get_name(input),
310 i_stream_get_error(input));
311 program_client_fail(pclient,
312 PROGRAM_CLIENT_ERROR_IO);
313 return;
314 }
315 }
316
317 if (pclient->program_input != pclient->raw_program_input) {
318 /* return to raw program input */
319 i_stream_unref(&pclient->program_input);
320 pclient->program_input = pclient->raw_program_input;
321 i_stream_ref(pclient->program_input);
322
323 io_remove(&pclient->io);
324 pclient->io = io_add_istream(pclient->program_input,
325 program_client_input_finish,
326 pclient);
327 io_set_pending(pclient->io);
328 }
329
330 program_client_input_finished(pclient);
331 }
332
333 static void
program_client_input_pump_finished(enum iostream_pump_status status,struct program_client * pclient)334 program_client_input_pump_finished(enum iostream_pump_status status,
335 struct program_client *pclient)
336 {
337 struct istream *input = pclient->program_input;
338 struct ostream *output = pclient->output;
339
340 i_assert(input != NULL);
341 i_assert(output != NULL);
342
343 switch (status) {
344 case IOSTREAM_PUMP_STATUS_INPUT_EOF:
345 break;
346 case IOSTREAM_PUMP_STATUS_INPUT_ERROR:
347 e_error(pclient->event,
348 "read(%s) failed: %s",
349 i_stream_get_name(input),
350 i_stream_get_error(input));
351 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
352 return;
353 case IOSTREAM_PUMP_STATUS_OUTPUT_ERROR:
354 e_error(pclient->event,
355 "write(%s) failed: %s",
356 o_stream_get_name(output),
357 o_stream_get_error(output));
358 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
359 return;
360 }
361
362 iostream_pump_destroy(&pclient->pump_in);
363
364 e_debug(pclient->event, "Finished streaming payload from program");
365
366 if (pclient->program_input != pclient->raw_program_input) {
367 /* return to raw program input */
368 i_stream_unref(&pclient->program_input);
369 pclient->program_input = pclient->raw_program_input;
370 i_stream_ref(pclient->program_input);
371 }
372
373 i_assert(pclient->io == NULL);
374 pclient->io = io_add_istream(pclient->program_input,
375 program_client_input_finish, pclient);
376 io_set_pending(pclient->io);
377 }
378
379 static void
program_client_extra_fd_input(struct program_client_extra_fd * efd)380 program_client_extra_fd_input(struct program_client_extra_fd *efd)
381 {
382 struct program_client *pclient = efd->pclient;
383
384 i_assert(efd->callback != NULL);
385 efd->callback(efd->context, efd->input);
386
387 if (efd->input->closed || !i_stream_have_bytes_left(efd->input)) {
388 if (!program_client_input_pending(pclient))
389 program_client_disconnect(pclient, FALSE);
390 }
391 }
392
program_client_connected(struct program_client * pclient)393 void program_client_connected(struct program_client *pclient)
394 {
395 e_debug(pclient->event, "Connected to program");
396
397 /* finish creating program input */
398 if (pclient->raw_program_input != NULL) {
399 struct istream *input = pclient->raw_program_input;
400
401 /* initialize dot input stream if required */
402 if (pclient->set.use_dotstream)
403 input = i_stream_create_dot(input, FALSE);
404 else
405 i_stream_ref(input);
406 pclient->program_input = input;
407 }
408 /* finish creating program output */
409 if (pclient->raw_program_output != NULL) {
410 struct ostream *output = pclient->raw_program_output;
411
412 /* initialize dot output stream if required */
413 if (pclient->set.use_dotstream)
414 output = o_stream_create_dot(output, FALSE);
415 else
416 o_stream_ref(output);
417 pclient->program_output = output;
418 }
419
420 pclient->start_time = ioloop_timeval;
421 timeout_remove(&pclient->to);
422 if (pclient->set.input_idle_timeout_msecs != 0) {
423 pclient->to =
424 timeout_add(pclient->set.input_idle_timeout_msecs,
425 program_client_timeout, pclient);
426 }
427
428 /* run program input */
429 if (pclient->program_input == NULL) {
430 /* nothing */
431 } else if (pclient->output == NULL) {
432 i_assert(pclient->io == NULL);
433 pclient->io = io_add_istream(pclient->program_input,
434 program_client_input_finish,
435 pclient);
436 io_set_pending(pclient->io);
437 } else {
438 pclient->pump_in =
439 iostream_pump_create(pclient->program_input,
440 pclient->output);
441 iostream_pump_set_completion_callback(pclient->pump_in,
442 program_client_input_pump_finished, pclient);
443 iostream_pump_start(pclient->pump_in);
444 }
445
446 /* run program output */
447 if (pclient->program_output == NULL) {
448 /* nothing */
449 } else if (pclient->input == NULL) {
450 o_stream_set_flush_callback(pclient->program_output,
451 program_client_output_finish, pclient);
452 o_stream_set_flush_pending(pclient->program_output, TRUE);
453 } else {
454 pclient->pump_out =
455 iostream_pump_create(pclient->input,
456 pclient->program_output);
457 iostream_pump_set_completion_callback(pclient->pump_out,
458 program_client_output_pump_finished, pclient);
459 iostream_pump_start(pclient->pump_out);
460 }
461 }
462
program_client_init(struct program_client * pclient,pool_t pool,const char * initial_label,const char * const * args,const struct program_client_settings * set)463 void program_client_init(struct program_client *pclient, pool_t pool,
464 const char *initial_label, const char *const *args,
465 const struct program_client_settings *set)
466 {
467 pclient->pool = pool;
468 if (args != NULL)
469 pclient->args = p_strarray_dup(pool, args);
470 pclient->fd_in = -1;
471 pclient->fd_out = -1;
472
473 if (set == NULL)
474 pclient->event = event_create(NULL);
475 else {
476 pclient->set = *set;
477 pclient->debug = set->debug;
478 pclient->set.dns_client_socket_path =
479 p_strdup(pool, set->dns_client_socket_path);
480 pclient->set.home = p_strdup(pool, set->home);
481
482 pclient->event = event_create(set->event);
483 event_set_forced_debug(pclient->event, set->debug);
484 }
485
486 program_client_set_label(pclient, initial_label);
487
488 e_debug(pclient->event, "Created");
489 }
490
program_client_set_input(struct program_client * pclient,struct istream * input)491 void program_client_set_input(struct program_client *pclient,
492 struct istream *input)
493 {
494 i_stream_unref(&pclient->input);
495 if (input != NULL)
496 i_stream_ref(input);
497 pclient->input = input;
498 }
499
program_client_set_output(struct program_client * pclient,struct ostream * output)500 void program_client_set_output(struct program_client *pclient,
501 struct ostream *output)
502 {
503 o_stream_unref(&pclient->output);
504 if (output != NULL)
505 o_stream_ref(output);
506 pclient->output = output;
507 pclient->output_seekable = FALSE;
508 }
509
program_client_set_output_seekable(struct program_client * pclient,const char * temp_prefix)510 void program_client_set_output_seekable(struct program_client *pclient,
511 const char *temp_prefix)
512 {
513 o_stream_unref(&pclient->output);
514 pclient->output = iostream_temp_create_sized(temp_prefix, 0,
515 "(program client seekable output)",
516 MAX_OUTPUT_MEMORY_BUFFER);
517 pclient->output_seekable = TRUE;
518 }
519
520 struct istream *
program_client_get_output_seekable(struct program_client * pclient)521 program_client_get_output_seekable(struct program_client *pclient)
522 {
523 i_assert(pclient->output_seekable);
524 return iostream_temp_finish(&pclient->output, IO_BLOCK_SIZE);
525 }
526
527 #undef program_client_set_extra_fd
program_client_set_extra_fd(struct program_client * pclient,int fd,program_client_fd_callback_t * callback,void * context)528 void program_client_set_extra_fd(struct program_client *pclient, int fd,
529 program_client_fd_callback_t *callback,
530 void *context)
531 {
532 struct program_client_extra_fd *efds;
533 struct program_client_extra_fd *efd = NULL;
534 unsigned int i, count;
535 i_assert(fd > 1);
536
537 if (!array_is_created(&pclient->extra_fds))
538 p_array_init(&pclient->extra_fds, pclient->pool, 2);
539
540 efds = array_get_modifiable(&pclient->extra_fds, &count);
541 for(i = 0; i < count; i++) {
542 if (efds[i].child_fd == fd) {
543 efd = &efds[i];
544 break;
545 }
546 }
547
548 if (efd == NULL) {
549 efd = array_append_space(&pclient->extra_fds);
550 efd->pclient = pclient;
551 efd->child_fd = fd;
552 efd->parent_fd = -1;
553 }
554 efd->callback = callback;
555 efd->context = context;
556 }
557
program_client_set_env(struct program_client * pclient,const char * name,const char * value)558 void program_client_set_env(struct program_client *pclient, const char *name,
559 const char *value)
560 {
561 const char *env;
562
563 if (!array_is_created(&pclient->envs))
564 p_array_init(&pclient->envs, pclient->pool, 16);
565
566 env = p_strdup_printf(pclient->pool, "%s=%s", name, value);
567 array_push_back(&pclient->envs, &env);
568
569 e_debug(pclient->event, "Pass environment: %s",
570 str_sanitize(env, 256));
571 }
572
program_client_init_streams(struct program_client * pclient)573 void program_client_init_streams(struct program_client *pclient)
574 {
575 /* Create streams for normal program I/O */
576 if (pclient->fd_out >= 0) {
577 struct ostream *program_output;
578
579 program_output = o_stream_create_fd(pclient->fd_out,
580 MAX_OUTPUT_BUFFER_SIZE);
581 o_stream_set_name(program_output, "program stdin");
582 o_stream_set_no_error_handling(program_output, TRUE);
583 pclient->raw_program_output = program_output;
584 }
585 if (pclient->fd_in >= 0) {
586 struct istream *program_input;
587
588 program_input = i_stream_create_fd(pclient->fd_in, SIZE_MAX);
589 i_stream_set_name(program_input, "program stdout");
590 pclient->raw_program_input = program_input;
591 }
592
593 /* Create streams for additional output through side-channel fds */
594 if (array_is_created(&pclient->extra_fds)) {
595 struct program_client_extra_fd *efds = NULL;
596 unsigned int count, i;
597
598 efds = array_get_modifiable(&pclient->extra_fds, &count);
599 for(i = 0; i < count; i++) {
600 i_assert(efds[i].parent_fd >= 0);
601 efds[i].input = i_stream_create_fd
602 (efds[i].parent_fd, SIZE_MAX);
603 i_stream_set_name(efds[i].input,
604 t_strdup_printf("program output fd=%d",
605 efds[i].child_fd));
606 efds[i].io = io_add(efds[i].parent_fd, IO_READ,
607 program_client_extra_fd_input,
608 &efds[i]);
609 }
610 }
611 }
612
program_client_destroy(struct program_client ** _pclient)613 void program_client_destroy(struct program_client **_pclient)
614 {
615 struct program_client *pclient = *_pclient;
616
617 *_pclient = NULL;
618
619 e_debug(pclient->event, "Destroy");
620
621 pclient->destroying = TRUE;
622 pclient->callback = NULL;
623
624 program_client_disconnect(pclient, TRUE);
625
626 i_assert(pclient->callback == NULL);
627
628 i_stream_unref(&pclient->input);
629 o_stream_unref(&pclient->output);
630
631 i_stream_unref(&pclient->program_input);
632 o_stream_unref(&pclient->program_output);
633 i_stream_unref(&pclient->raw_program_input);
634 o_stream_unref(&pclient->raw_program_output);
635
636 if (pclient->destroy != NULL)
637 pclient->destroy(pclient);
638
639 event_unref(&pclient->event);
640
641 pool_unref(&pclient->pool);
642 }
643
program_client_switch_ioloop(struct program_client * pclient)644 void program_client_switch_ioloop(struct program_client *pclient)
645 {
646 if (pclient->input != NULL)
647 i_stream_switch_ioloop(pclient->input);
648 if (pclient->program_input != NULL)
649 i_stream_switch_ioloop(pclient->program_input);
650 if (pclient->output != NULL)
651 o_stream_switch_ioloop(pclient->output);
652 if (pclient->program_output != NULL)
653 o_stream_switch_ioloop(pclient->program_output);
654 if (pclient->to != NULL)
655 pclient->to = io_loop_move_timeout(&pclient->to);
656 if (pclient->pump_in != NULL)
657 iostream_pump_switch_ioloop(pclient->pump_in);
658 if (pclient->pump_out != NULL)
659 iostream_pump_switch_ioloop(pclient->pump_out);
660 if (pclient->io != NULL)
661 pclient->io = io_loop_move_io(&pclient->io);
662 pclient->switch_ioloop(pclient);
663 }
664
program_client_create(const char * uri,const char * const * args,const struct program_client_settings * set,bool noreply,struct program_client ** pc_r,const char ** error_r)665 int program_client_create(const char *uri, const char *const *args,
666 const struct program_client_settings *set,
667 bool noreply, struct program_client **pc_r,
668 const char **error_r)
669 {
670 if (str_begins(uri, "exec:")) {
671 *pc_r = program_client_local_create(uri+5, args, set);
672 return 0;
673 } else if (str_begins(uri, "unix:")) {
674 *pc_r = program_client_unix_create(uri+5, args, set, noreply);
675 return 0;
676 } else if (str_begins(uri, "tcp:")) {
677 const char *host;
678 in_port_t port;
679
680 if (net_str2hostport(uri+4, 0, &host, &port) < 0 ||
681 port == 0) {
682 *error_r = t_strdup_printf(
683 "Invalid tcp syntax, "
684 "must be host:port in '%s'", uri+4);
685 return -1;
686 }
687 *pc_r = program_client_net_create(host, port, args, set,
688 noreply);
689 return 0;
690 } else {
691 *error_r = t_strdup_printf(
692 "Unsupported program client scheme '%s'",
693 t_strcut(uri, ':'));
694 return -1;
695 }
696 }
697
698 static void
program_client_run_callback(int result,int * context)699 program_client_run_callback(int result, int *context)
700 {
701 *context = result;
702 io_loop_stop(current_ioloop);
703 }
704
program_client_run(struct program_client * pclient)705 int program_client_run(struct program_client *pclient)
706 {
707 int ret = -2;
708 struct ioloop *prev_ioloop = current_ioloop;
709 struct ioloop *ioloop = io_loop_create();
710
711 program_client_switch_ioloop(pclient);
712
713 program_client_run_async(pclient, program_client_run_callback, &ret);
714
715 if (ret == -2) {
716 io_loop_run(ioloop);
717 }
718
719 io_loop_set_current(prev_ioloop);
720 program_client_switch_ioloop(pclient);
721 io_loop_set_current(ioloop);
722 io_loop_destroy(&ioloop);
723
724 if (pclient->error != PROGRAM_CLIENT_ERROR_NONE)
725 return -1;
726
727 return pclient->exit_status;
728 }
729
730 #undef program_client_run_async
program_client_run_async(struct program_client * pclient,program_client_callback_t * callback,void * context)731 void program_client_run_async(struct program_client *pclient,
732 program_client_callback_t *callback,
733 void *context)
734 {
735 i_assert(callback != NULL);
736
737 pclient->disconnected = FALSE;
738 pclient->exit_status = PROGRAM_CLIENT_EXIT_STATUS_SUCCESS;
739 pclient->error = PROGRAM_CLIENT_ERROR_NONE;
740
741 pclient->callback = callback;
742 pclient->context = context;
743 if (program_client_connect(pclient) < 0)
744 program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
745 }
746