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