bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file
1c0dd964c40f9457af090128a4a5e96f23197020Aki Tuomi#define PROGRAM_CLIENT_VERSION_STRING "VERSION\tscript\t" \
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_net_connect_again(struct program_client *pclient);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi * Script client input stream
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_istream_destroy(struct iostream_private *stream)
d9683a6493b2298ecd309522df315f8e7188e869Stephan Boschprogram_client_istream_parse_result(struct program_client_istream *scstream,
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch struct istream_private *stream = &scstream->istream;
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch scstream->client->exit_code = PROGRAM_CLIENT_EXIT_SUCCESS;
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch scstream->client->exit_code = PROGRAM_CLIENT_EXIT_FAILURE;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiprogram_client_istream_read(struct istream_private *stream)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi stream->buffer = i_stream_get_data(stream->parent, &pos);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* retain/hide potential return code at end of buffer */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi reserved = (stream->buffer[pos - 1] == '\n' && pos > 1 ? 2 : 1);
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen if ((ret = i_stream_read_memarea(stream->parent)) == -2) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (ret == 0 || (ret < 0 && !stream->parent->eof))
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Check return code at EOF */
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch program_client_istream_parse_result(scstream, pos);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* retain/hide potential return code at end of buffer */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Parent EOF and not more data to return; EOF here as well */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi } while (ret == 0);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid ATTR_NORETURN program_client_istream_sync(struct istream_private *stream ATTR_UNUSED)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi i_panic("program_client_istream sync() not implemented");
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_istream_stat(struct istream_private *stream, bool exact)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Stat the original stream */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomistruct istream *program_client_istream_create(struct program_client *program_client,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream = i_new(struct program_client_istream, 1);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream->istream.iostream.destroy = program_client_istream_destroy;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream->istream.read = program_client_istream_read;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream->istream.sync = program_client_istream_sync;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream->istream.stat = program_client_istream_stat;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi scstream->istream.istream.blocking = input->blocking;
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen return i_stream_create(&scstream->istream, input, -1, 0);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi * Program client
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomivoid program_client_remote_connected(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_IO);
a0c57666729eacf4fe1429ae6fa7e8af61f94eddAki Tuomiint program_client_unix_connect(struct program_client *pclient);
a0c57666729eacf4fe1429ae6fa7e8af61f94eddAki Tuomivoid program_client_unix_reconnect(struct program_client *pclient)
9f29dfcbf3812c4f88ffb1c99b5a56b2e8091ea4Aki Tuomiint program_client_unix_connect(struct program_client *pclient)
92c8109f71ed1b801806fba307e3032306bbcbafTimo Sirainen if ((fd = net_connect_unix(pclient->path)) < 0) {
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch pclient->fd_in = (prclient->noreply && pclient->output == NULL &&
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi io_add(fd, IO_WRITE, program_client_remote_connected, pclient);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_net_connect_timeout(struct program_client *pclient)
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi i_error("connect(%s) failed: timeout in %u milliseconds",
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* set error to timeout here */
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->error = PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT;
211c638d81d382517d196ad47565e0d85012c927klemens/* see if connect succeeded or not, if it did, then proceed
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi normally, otherwise try reconnect to next address */
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_net_connected(struct program_client *pclient)
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi if ((errno = net_geterror(pclient->fd_out)) != 0) {
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* disconnect and try again */
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_net_connect_real(struct program_client *pclient)
1b6f7fc2c404852c0f23ca4a0bf3a4f6186e7d5bAki Tuomi if (net_ipport2str(prclient->ips, prclient->port, &str) < 0)
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi i_debug("Trying to connect %s (timeout %u msecs)",
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* try to connect */
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch if ((fd = net_connect_ip(prclient->ips, prclient->port,
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch pclient->fd_in = (prclient->noreply && pclient->output == NULL &&
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->io = io_add(fd, IO_WRITE, program_client_net_connected, pclient);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi if (pclient->set.client_connect_timeout_msecs != 0) {
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->to = timeout_add(pclient->set.client_connect_timeout_msecs,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_net_connect_again(struct program_client *pclient)
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi i_error("program-client-net: %s: No addresses left to try",
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_net_connect_resolved(const struct dns_lookup_result *result,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi i_error("program-client-net: Cannot resolve '%s': %s",
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi program_client_fail(pclient, PROGRAM_CLIENT_ERROR_OTHER);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* reduce timeout */
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi if (pclient->set.client_connect_timeout_msecs > 0) {
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi if (pclient->set.client_connect_timeout_msecs <= result->msecs) {
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* we ran out of time */
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->set.client_connect_timeout_msecs -= result->msecs;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* then connect */
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch prclient->ips = p_memdup(pclient->pool, result->ips,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomiint program_client_net_connect_init(struct program_client *pclient)
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi } else if (net_addr2ip(pclient->path, &ip) == 0) {
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch prclient->ips = p_new(pclient->pool, struct ip_addr, 1);
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch prclient->hostname = p_strdup(pclient->pool, pclient->path);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi if (pclient->set.dns_client_socket_path != NULL) {
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi /* guess we do it here then.. */
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi "Cannot resolve '%s': %s",
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_remote_close_output(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi int fd_out = pclient->fd_out, fd_in = pclient->fd_in;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Shutdown output; program stdin will get EOF */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (shutdown(fd_out, SHUT_WR) < 0 && errno != ENOTCONN) {
097dbdf543bc5d1689c5570f5faaec1e864e3a87Aki Tuomivoid program_client_remote_disconnect(struct program_client *pclient, bool force)
3e1b133686cac6670f6f93479525200772c5e233Stephan Bosch /* nothing */
3e1b133686cac6670f6f93479525200772c5e233Stephan Bosch } else if (pclient->error == PROGRAM_CLIENT_ERROR_NONE &&
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Skip any remaining program output and parse the exit code */
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch /* Check for error and EOF. Since we're disconnected, always
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch mark an internal error when not all input is read. This is
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch generally unlikely to occur. */
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch if (pclient->program_input->stream_errno != 0 ||
23a7a07d4d7cab538509ceef21a280dcc243362fStephan Bosch i_stream_have_bytes_left(pclient->program_input))
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_INTERNAL_FAILURE;
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_SUCCESS;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomivoid program_client_remote_switch_ioloop(struct program_client *pclient)
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch prclient->to_retry = io_loop_move_timeout(&prclient->to_retry);
9f29dfcbf3812c4f88ffb1c99b5a56b2e8091ea4Aki Tuomiprogram_client_unix_create(const char *socket_path, const char *const *args,
9f29dfcbf3812c4f88ffb1c99b5a56b2e8091ea4Aki Tuomi pool = pool_alloconly_create("program client unix", 1024);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient = p_new(pool, struct program_client_remote, 1);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_init(&pclient->client, pool, socket_path, args, set);
9f29dfcbf3812c4f88ffb1c99b5a56b2e8091ea4Aki Tuomi pclient->client.connect = program_client_unix_connect;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient->client.close_output = program_client_remote_close_output;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient->client.disconnect = program_client_remote_disconnect;
204afc1f4f37a4f1cb53ff44b993a661cc45bf5dAki Tuomi pclient->client.switch_ioloop = program_client_remote_switch_ioloop;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomiprogram_client_net_create(const char *host, in_port_t port,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi const char *const *args,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pool = pool_alloconly_create("program client net", 1024);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient = p_new(pool, struct program_client_remote, 1);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi program_client_init(&pclient->client, pool, host, args, set);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->client.connect = program_client_net_connect_init;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->client.close_output = program_client_remote_close_output;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->client.disconnect = program_client_remote_disconnect;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomiprogram_client_net_create_ips(const struct ip_addr *ips, size_t ips_count,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi const char *const *args,
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pool = pool_alloconly_create("program client net", 1024);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient = p_new(pool, struct program_client_remote, 1);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi program_client_init(&pclient->client, pool, net_ip2addr(ips), args, set);
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->client.connect = program_client_net_connect_init;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->client.close_output = program_client_remote_close_output;
366f669495b4b507084e43a56c0a9d201f120b7dAki Tuomi pclient->client.disconnect = program_client_remote_disconnect;