auth-worker-server.c revision 5f1d689131a75c39f064cbd4202373e7edf78f18
/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
#include "auth-common.h"
#include "ioloop.h"
#include "array.h"
#include "aqueue.h"
#include "net.h"
#include "istream.h"
#include "ostream.h"
#include "hex-binary.h"
#include "str.h"
#include "eacces-error.h"
#include "auth-request.h"
#include "auth-worker-client.h"
#include "auth-worker-server.h"
#include <unistd.h>
/* Initial lookup timeout */
#define AUTH_WORKER_LOOKUP_TIMEOUT_SECS 60
/* Timeout for multi-line replies, e.g. listing users. This should be a much
higher value, because e.g. doveadm could be doing some long-running commands
for the users. And because of buffering this timeout is for handling
multiple users, not just one. */
#define AUTH_WORKER_ABORT_SECS 60
#define AUTH_WORKER_DELAY_WARN_SECS 3
#define AUTH_WORKER_DELAY_WARN_MIN_INTERVAL_SECS 300
struct auth_worker_request {
unsigned int id;
const char *username;
const char *data;
void *context;
};
struct auth_worker_connection {
int fd;
struct auth_worker_request *request;
unsigned int id_counter;
bool received_error:1;
bool restart:1;
bool shutdown:1;
bool timeout_pending_resume:1;
bool resuming:1;
};
static unsigned int idle_count = 0, auth_workers_with_errors = 0;
static struct aqueue *worker_request_queue;
static time_t auth_worker_last_warn;
static unsigned int auth_workers_throttle_count;
static const char *worker_socket_path;
{
if (idle_count > 1)
else
}
{
}
struct auth_worker_request *request)
{
if (age_secs >= AUTH_WORKER_ABORT_SECS) {
i_error("Aborting auth request that was queued for %d secs, "
"%d left in queue",
"FAIL\t%d", PASSDB_RESULT_INTERNAL_FAILURE),
return FALSE;
}
if (age_secs >= AUTH_WORKER_DELAY_WARN_SECS &&
i_warning("auth workers: Auth request was queued for %d "
"seconds, %d left in queue "
"(see auth_worker_max_count)",
}
idle_count--;
return TRUE;
}
{
do {
if (aqueue_count(worker_request_queue) == 0)
return;
}
{
unsigned char passdb_md5[MD5_RESULTLEN];
unsigned char userdb_md5[MD5_RESULTLEN];
}
static struct auth_worker_connection *auth_worker_create(void)
{
struct auth_worker_connection *conn;
int fd;
return NULL;
if (fd == -1) {
} else {
i_error("net_connect_unix(%s) failed: %m",
}
return NULL;
}
idle_count++;
return conn;
}
{
struct auth_worker_connection *const *conns;
unsigned int idx;
if (conn->received_error) {
}
break;
}
}
idle_count--;
i_error("auth worker: Aborted %s request for %s: %s",
"FAIL\t%d", PASSDB_RESULT_INTERNAL_FAILURE),
}
i_error("close(auth worker) failed: %m");
if (idle_count == 0 && restart) {
conn = auth_worker_create();
}
}
static struct auth_worker_connection *auth_worker_find_free(void)
{
struct auth_worker_connection **conns;
if (idle_count == 0)
return NULL;
return conn;
}
i_unreached();
return NULL;
}
struct auth_worker_request *request,
const char *line)
{
/* multi-line reply, not finished yet */
else {
}
} else {
idle_count++;
}
return FALSE;
}
return TRUE;
}
{
if (conn->received_error)
return TRUE;
if (auth_workers_with_errors == 1) {
/* this is the only failing auth worker connection.
don't create new ones until this one sends SUCCESS. */
return TRUE;
}
/* too many auth workers, reduce them */
else if (auth_workers_throttle_count > 1)
return FALSE;
}
{
if (!conn->received_error)
return;
if (auth_workers_with_errors == 0) {
/* all workers are succeeding now, set the limit back to
original. */
} else if (auth_workers_throttle_count < max_count)
}
{
unsigned int id;
case 0:
return;
case -1:
/* disconnected */
TRUE);
return;
case -2:
/* buffer full */
i_error("BUG: Auth worker sent us more than %d bytes",
(int)AUTH_WORKER_MAX_LINE_LENGTH);
return;
}
continue;
}
continue;
}
if (!auth_worker_error(conn))
return;
continue;
}
continue;
}
continue;
line + 1))
break;
} else {
i_error("BUG: Worker sent reply with id %u, "
} else {
i_error("BUG: Worker sent reply with id %u, "
"none was expected", id);
}
return;
}
}
/* there's still a pending request */
else
}
{
}
struct auth_worker_connection *
{
struct auth_worker_connection *conn;
struct auth_worker_request *request;
if (aqueue_count(worker_request_queue) > 0) {
/* requests are already being queued, no chance of
} else {
/* no free connections, create a new one */
conn = auth_worker_create();
}
}
i_unreached();
} else {
/* reached the limit, queue the request */
}
return conn;
}
{
/* request was just finished, don't try to resume it */
return;
}
if (!conn->timeout_pending_resume) {
}
}
void auth_worker_server_init(void)
{
worker_socket_path = "auth-worker";
}
void auth_worker_server_deinit(void)
{
while (array_count(&connections) > 0) {
}
}