#define MAX_OUTBUF_SIZE 1024
enum auth_master_request_state {
- AUTH_MASTER_REQUEST_STATE_SENT = 0,
+ AUTH_MASTER_REQUEST_STATE_SUBMITTED = 0,
+ AUTH_MASTER_REQUEST_STATE_SENT,
+ AUTH_MASTER_REQUEST_STATE_REPLIED_MORE,
AUTH_MASTER_REQUEST_STATE_REPLIED,
AUTH_MASTER_REQUEST_STATE_FINISHED,
AUTH_MASTER_REQUEST_STATE_ABORTED,
unsigned int id_counter;
HASH_TABLE(void *, struct auth_master_request *) requests;
struct auth_master_request *requests_head, *requests_tail;
+ struct auth_master_request *requests_unsent;
unsigned int requests_count;
unsigned int timeout_msecs;
unsigned int
auth_master_request_get_timeout_msecs(struct auth_master_request *req);
+void auth_master_request_send(struct auth_master_request *req);
int auth_master_request_got_reply(struct auth_master_request **_req,
const char *reply, const char *const *args);
+
void auth_master_request_fail(struct auth_master_request **_req,
const char *reason);
void auth_master_connection_update_timeout(struct auth_master_connection *conn);
void auth_master_connection_start_timeout(struct auth_master_connection *conn);
+void auth_master_handle_requests(struct auth_master_connection *conn);
+
#endif
if (req->sent)
hash_table_remove(conn->requests, POINTER_CAST(req->id));
+ if (req == conn->requests_unsent)
+ conn->requests_unsent = conn->requests_unsent->next;
DLLIST2_REMOVE(&conn->requests_head, &conn->requests_tail, req);
conn->requests_count--;
ret = auth_master_request_callback(req, &mreply);
if (ret == 0) {
+ req->state = AUTH_MASTER_REQUEST_STATE_REPLIED_MORE;
if (conn->waiting) {
i_assert(conn->ioloop != NULL);
io_loop_stop(conn->ioloop);
auth_master_request_abort(_req);
}
-static void auth_master_request_send(struct auth_master_request *req)
+void auth_master_request_send(struct auth_master_request *req)
{
struct auth_master_connection *conn = req->conn;
const char *id_str = dec2str(req->id);
+ i_assert(req->state == AUTH_MASTER_REQUEST_STATE_SUBMITTED);
+
const struct const_iovec iov[] = {
{ req->cmd, strlen(req->cmd), },
{ "\t", 1 },
unsigned int iovc = N_ELEMENTS(iov);
o_stream_nsendv(conn->conn.output, iov, iovc);
+
+ req->state = AUTH_MASTER_REQUEST_STATE_SENT;
+
+ hash_table_insert(conn->requests, POINTER_CAST(req->id), req);
+ req->sent = TRUE;
+
+ e_debug(req->event, "Sent");
}
#undef auth_master_request
DLLIST2_APPEND(&conn->requests_head, &conn->requests_tail, req);
conn->requests_count++;
+ if (conn->requests_unsent == NULL)
+ conn->requests_unsent = conn->requests_tail;
+
+ auth_master_connection_start_timeout(conn);
+ auth_master_stop_idle(conn);
req->cmd = p_strdup(req->pool, cmd);
if (args_size > 0)
e_debug(req->event, "Created");
+ auth_master_handle_requests(conn);
return req;
}
-int auth_master_request_submit(struct auth_master_request **_req)
-{
- struct auth_master_request *req = *_req;
- struct auth_master_connection *conn = req->conn;
-
- if (req == NULL)
- return -1;
-
- if (!conn->connected) {
- if (auth_master_connect(conn) < 0) {
- // FIXME: handle
- /* we couldn't connect to auth now,
- so we probably can't in future either. */
- auth_master_request_unref(_req);
- return -1;
- }
- // FIXME: allow asynchronous connection
- i_assert(conn->connected);
- connection_input_resume(&conn->conn);
- }
-
- o_stream_cork(conn->conn.output);
- if (!conn->sent_handshake) {
- const struct connection_settings *set = &conn->conn.list->set;
-
- o_stream_nsend_str(conn->conn.output,
- t_strdup_printf("VERSION\t%u\t%u\n",
- set->major_version,
- set->minor_version));
- conn->sent_handshake = TRUE;
- }
-
- auth_master_request_send(req);
- o_stream_uncork(conn->conn.output);
-
- if (o_stream_flush(conn->conn.output) < 0) {
- e_error(conn->conn.event, "write(auth socket) failed: %s",
- o_stream_get_error(conn->conn.output));
- auth_master_disconnect(conn);
- auth_master_request_unref(_req);
- return -1;
- }
-
- hash_table_insert(conn->requests, POINTER_CAST(req->id), req);
- req->sent = TRUE;
-
- auth_master_connection_start_timeout(conn);
- auth_master_stop_idle(conn);
-
- e_debug(req->event, "Submitted");
-
- return 0;
-}
-
static void auth_master_request_stop(struct auth_master_request *req)
{
struct auth_master_connection *conn = req->conn;
io_loop_have_immediate_timeouts(ioloop));
auth_master_request_ref(req);
- req->state = AUTH_MASTER_REQUEST_STATE_SENT;
/* add stop handler */
to = timeout_add_short(100, auth_master_request_stop, req);
conn->waiting = TRUE;
- while (req->state < AUTH_MASTER_REQUEST_STATE_REPLIED)
- io_loop_run(conn->ioloop);
+ if (req->state < AUTH_MASTER_REQUEST_STATE_REPLIED) {
+ if (req->state == AUTH_MASTER_REQUEST_STATE_REPLIED_MORE)
+ req->state = AUTH_MASTER_REQUEST_STATE_SENT;
+ do
+ io_loop_run(ioloop);
+ while (req->state < AUTH_MASTER_REQUEST_STATE_REPLIED_MORE);
+ }
conn->waiting = waiting;
e_debug(req->event, "Finished waiting for request");
return (ret > 0 ? 0 : 1);
}
+static void
+auth_master_handle_output_error(struct auth_master_connection *conn)
+{
+ struct ostream *output = conn->conn.output;
+
+ if (output->stream_errno != EPIPE &&
+ output->stream_errno != ECONNRESET) {
+ e_error(conn->conn.event, "write(%s) failed: %s",
+ o_stream_get_name(output), o_stream_get_error(output));
+ } else {
+ e_error(conn->conn.event, "Remote disconnected");
+ }
+ auth_master_disconnect(conn);
+}
+
+static int
+auth_master_connection_output(struct auth_master_connection *conn)
+{
+ int ret;
+
+ if ((ret = o_stream_flush(conn->conn.output)) <= 0) {
+ if (ret < 0)
+ auth_master_handle_output_error(conn);
+ return ret;
+ }
+
+ if (o_stream_get_buffer_used_size(conn->conn.output) >= MAX_OUTBUF_SIZE)
+ return 1;
+
+ o_stream_cork(conn->conn.output);
+ if (!conn->sent_handshake) {
+ const struct connection_settings *set = &conn->conn.list->set;
+
+ o_stream_nsend_str(conn->conn.output,
+ t_strdup_printf("VERSION\t%u\t%u\n",
+ set->major_version,
+ set->minor_version));
+ conn->sent_handshake = TRUE;
+ }
+
+ e_debug(conn->conn.event, "Sending requests");
+
+ while (conn->requests_unsent != NULL) {
+ auth_master_request_send(conn->requests_unsent);
+ conn->requests_unsent = conn->requests_unsent->next;
+ if (o_stream_get_buffer_used_size(conn->conn.output) >=
+ MAX_OUTBUF_SIZE)
+ break;
+ }
+
+ if (conn->conn.output != NULL &&
+ o_stream_uncork_flush(conn->conn.output) < 0) {
+ auth_master_handle_output_error(conn);
+ return -1;
+ }
+ return 1;
+}
+
static int auth_master_input_line(struct connection *_conn, const char *line)
{
struct auth_master_connection *conn =
i_assert(success);
conn->connected = TRUE;
+
+ o_stream_set_flush_callback(_conn->output,
+ auth_master_connection_output, conn);
+ auth_master_handle_requests(conn);
}
static void auth_master_connect_timeout(struct auth_master_connection *conn)
auth_master_connect_timeout, conn);
return 0;
}
+
+void auth_master_handle_requests(struct auth_master_connection *conn)
+{
+ if (conn->requests_unsent == NULL)
+ return;
+
+ if (!conn->connected) {
+ e_debug(conn->conn.event, "Need to connect");
+
+ (void)auth_master_connect(conn);
+ return;
+ }
+
+ i_assert(conn->conn.output != NULL);
+ o_stream_set_flush_pending(conn->conn.output, TRUE);
+}
+
void auth_master_switch_ioloop_to(struct auth_master_connection *conn,
struct ioloop *ioloop)
{
*fields_r = NULL;
return 0;
}
+ if (auth_master_connect(conn) < 0) {
+ *fields_r = empty_str_array;
+ return -1;
+ }
i_zero(&lookup);
lookup.conn = conn;
req = auth_master_request(conn, "PASS", str_data(args), str_len(args),
auth_lookup_reply_callback, &lookup);
- if (auth_master_request_submit(&req) < 0) {
- *fields_r = empty_str_array;
- event_unref(&lookup.event);
- return -1;
- }
auth_master_request_set_event(req, lookup.event);
(void)auth_master_request_wait(req);
*fields_r = NULL;
return 0;
}
+ if (auth_master_connect(conn) < 0) {
+ *fields_r = empty_str_array;
+ return -1;
+ }
i_zero(&lookup);
lookup.conn = conn;
req = auth_master_request(conn, "USER", str_data(args), str_len(args),
auth_lookup_reply_callback, &lookup);
- if (auth_master_request_submit(&req) < 0) {
- *fields_r = empty_str_array;
- event_unref(&lookup.event);
- return -1;
- }
auth_master_request_set_event(req, lookup.event);
(void)auth_master_request_wait(req);
ctx->req = auth_master_request(conn, "LIST",
str_data(args), str_len(args),
auth_user_list_reply_callback, ctx);
- if (auth_master_request_submit(&ctx->req) < 0)
- ctx->failed = TRUE;
- else
- auth_master_request_set_event(ctx->req, ctx->event);
+ auth_master_request_set_event(ctx->req, ctx->event);
connection_input_halt(&conn->conn);
struct auth_master_request *req;
string_t *args;
+ if (auth_master_connect(conn) < 0)
+ return -1;
+
i_zero(&ctx);
ctx.conn = conn;
req = auth_master_request(conn, "CACHE-FLUSH",
str_data(args), str_len(args),
auth_cache_flush_reply_callback, &ctx);
- if (auth_master_request_submit(&req) < 0)
- ctx.failed = TRUE;
- else {
- auth_master_request_set_event(req, ctx.event);
- (void)auth_master_request_wait(req);
- }
+ auth_master_request_set_event(req, ctx.event);
+ (void)auth_master_request_wait(req);
if (ctx.failed)
e_debug(ctx.event, "Cache flush failed");
typeof(context))), \
(auth_master_request_callback_t *)callback, context)
-int auth_master_request_submit(struct auth_master_request **_req);
-
void auth_master_request_set_event(struct auth_master_request *req,
struct event *event);