bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_callback(struct program_client *pclient, int result, void
f207fb0f2fd3113aa9a9f7911fb12d94dce19dffAki Tuomi /* do not call callback when destroying */
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi program_client_callback_t *callback = pclient->callback;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_seekable_fd_callback(const char **path_r, void *context)
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi struct program_client *pclient = (struct program_client *)context;
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("safe_mkstemp(%s) failed: %m", str_c(path));
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* we just want the fd, unlink it */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* shouldn't happen.. */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_timeout(struct program_client *pclient)
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_error("program `%s' execution timed out (> %u msecs)",
17541ea25593c656060199715051db2c1eef221dAki Tuomi pclient->path, pclient->set.input_idle_timeout_msecs);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_RUN_TIMEOUT);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_connect_timeout(struct program_client *pclient)
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_error("program `%s' socket connection timed out (> %u msecs)",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient->path, pclient->set.client_connect_timeout_msecs);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_connect(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (pclient->set.client_connect_timeout_msecs != 0) {
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi pclient->to = timeout_add(pclient->set.client_connect_timeout_msecs,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_close_output(struct program_client *pclient)
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek o_stream_destroy(&pclient->program_output);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_disconnect_extra_fds(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi efds = array_get_modifiable(&pclient->extra_fds, &count);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < count; i++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (efds[i].parent_fd != -1 && close(efds[i].parent_fd) < 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("close(fd=%d) failed: %m", efds[i].parent_fd);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomivoid program_client_disconnected(struct program_client *pclient)
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek o_stream_destroy(&pclient->program_output);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (pclient->fd_in != -1 && close(pclient->fd_in) < 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (pclient->fd_out != -1 && pclient->fd_out != pclient->fd_in
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_error("close(%s/out) failed: %m", pclient->path);
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if (pclient->other_error && pclient->error == PROGRAM_CLIENT_ERROR_NONE) {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomivoid program_client_disconnect(struct program_client *pclient, bool force)
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomi if ((ret = program_client_close_output(pclient)) < 0)
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_fail(struct program_client *pclient,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomibool program_client_input_pending(struct program_client *pclient)
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch i_stream_have_bytes_left(pclient->program_input)) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi efds = array_get_modifiable(&pclient->extra_fds, &count);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < count; i++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_program_output(struct program_client *pclient)
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* flush the output first, before writing more */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* initialize dot stream if required */
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi pclient->dot_output = o_stream_create_dot(output, FALSE);
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* transfer provided input stream to output towards program */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* finish and flush program output */
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* check whether program i/o is finished */
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* finished */
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* close output towards program, so that it reads EOF */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi } else if (program_client_close_output(pclient) < 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_program_input(struct program_client *pclient)
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* initialize seekable output if required */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (pclient->output_seekable && pclient->seekable_output == NULL) {
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi input = i_stream_create_seekable(input_list, MAX_OUTPUT_MEMORY_BUFFER,
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* initialize dot stream if required */
a371ea8bd48d45548cd7aa16d4f5aeb38ba48c91Aki Tuomi pclient->dot_input = i_stream_create_dot(input, FALSE);
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* transfer input from program to provided output stream */
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* read (the remainder of) the outer stream */
d9b6d24bfa1354757fe9bc68205c0323e40abce3Stephan Bosch while ((ret=i_stream_read_more(input, &data, &size)) > 0)
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* flush output stream to make sure all is sent */
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* check whether program i/o is finished */
4427c6add036d046c291cbbc82e496c5135ccf04Stephan Bosch /* finished */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_extra_fd_input(struct program_client_extra_fd *efd)
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch if (efd->input->closed || !i_stream_have_bytes_left(efd->input)) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_connected(struct program_client *pclient)
17541ea25593c656060199715051db2c1eef221dAki Tuomi timeout_add(pclient->set.input_idle_timeout_msecs,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* run output */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi (ret = program_client_program_output(pclient)) == 0) {
03c6532fe7aa3ddae23c99ff6bec78d8dd2e8165Aki Tuomi o_stream_set_flush_callback(pclient->program_output,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_init(struct program_client *pclient, pool_t pool,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi const char *path,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi const char *const *args,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_set_input(struct program_client *pclient,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_set_output(struct program_client *pclient,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_set_output_seekable(struct program_client *pclient,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistruct istream *program_client_get_output_seekable(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_set_extra_fd(struct program_client *pclient, int fd,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi p_array_init(&pclient->extra_fds, pclient->pool, 2);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi efds = array_get_modifiable(&pclient->extra_fds, &count);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < count; i++) {
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_set_env(struct program_client *pclient, const char *name,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi const char *value)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi env = p_strdup_printf(pclient->pool, "%s=%s", name, value);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_init_streams(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Create streams for normal program I/O */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi o_stream_set_name(pclient->program_output, "program stdin");
e4e8301f2b8ee44abcd901db4e27f150de7c4739Timo Sirainen o_stream_set_no_error_handling(pclient->program_output, TRUE);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_stream_set_name(pclient->program_input, "program stdout");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Create streams for additional output through side-channel fds */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi efds = array_get_modifiable(&pclient->extra_fds, &count);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < count; i++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_destroy(struct program_client **_pclient)
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek o_stream_unref(&pclient->program_output);
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek i_stream_unref(&pclient->seekable_output);
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomivoid program_client_switch_ioloop(struct program_client *pclient)
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainenint program_client_create(const char *uri, const char *const *args,
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen const char **error_r)
80521bcdd28b22818480a6e6e1ae84230e19c1baAki Tuomi if (net_str2hostport(uri+4, 0, &host, &port) < 0 || port == 0) {
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen "Invalid tcp syntax, must be host:port in '%s'", uri+4);
4a197212360f75bfc89254bfd5bc4a31151fe4b4Timo Sirainen "Unsupported program client scheme '%s'",
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomivoid program_client_run_callback(int result, int *context)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_run(struct program_client *pclient)
98bc2ecdbfd4f2f20c3a5e96ae445072fbe22223Aki Tuomi program_client_run_async(pclient, program_client_run_callback, &ret);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_run_async(struct program_client *pclient,
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_SUCCESS;