changeset 19721:9d2fa1afc222

auth: If auth_stats=yes, send statistics to stats process.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 08 Feb 2016 16:20:46 +0200
parents c34f24c83729
children 69cf950c6e6f
files src/auth/Makefile.am src/auth/auth-request-stats.c src/auth/auth-request-stats.h src/auth/auth-request.c src/auth/auth-request.h src/auth/auth-settings.c src/auth/auth-settings.h src/auth/auth-stats.c src/auth/auth-stats.h src/auth/main.c src/auth/passdb-cache.c
diffstat 11 files changed, 270 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/Makefile.am	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/Makefile.am	Mon Feb 08 16:20:46 2016 +0200
@@ -29,6 +29,7 @@
 	-I$(top_srcdir)/src/lib-dns \
 	-I$(top_srcdir)/src/lib-sql \
 	-I$(top_srcdir)/src/lib-settings \
+	-I$(top_srcdir)/src/lib-stats \
 	-I$(top_srcdir)/src/lib-ntlm \
 	-I$(top_srcdir)/src/lib-otp \
 	-I$(top_srcdir)/src/lib-master \
@@ -70,8 +71,10 @@
 	auth-penalty.c \
 	auth-request.c \
 	auth-request-handler.c \
+	auth-request-stats.c \
 	auth-request-var-expand.c \
 	auth-settings.c \
+	auth-stats.c \
 	auth-fields.c \
 	auth-token.c \
 	auth-worker-client.c \
@@ -141,6 +144,7 @@
 	auth-request-handler.h \
 	auth-request-var-expand.h \
 	auth-settings.h \
+	auth-stats.h \
 	auth-fields.h \
 	auth-token.h \
 	auth-worker-client.h \
@@ -195,6 +199,14 @@
 checkpassword_reply_sources = \
 	checkpassword-reply.c
 
+stats_moduledir = $(moduledir)/stats
+stats_module_LTLIBRARIES = libstats_auth.la
+
+libstats_auth_la_LDFLAGS = -module -avoid-version
+libstats_auth_la_LIBADD = auth-stats.lo $(LIBDOVECOT)
+libstats_auth_la_DEPENDENCIES = auth-stats.lo
+libstats_auth_la_SOURCES =
+
 test_programs = \
 	test-auth-cache \
 	test-auth-request-var-expand \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-request-stats.c	Mon Feb 08 16:20:46 2016 +0200
@@ -0,0 +1,77 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "auth-common.h"
+#include "str.h"
+#include "strescape.h"
+#include "buffer.h"
+#include "base64.h"
+#include "stats.h"
+#include "stats-connection.h"
+#include "auth-stats.h"
+#include "auth-request.h"
+#include "auth-request-stats.h"
+
+#define USER_STATS_SOCKET_NAME "stats-user"
+
+static struct stats_connection *auth_stats_conn = NULL;
+static struct stats_item *auth_stats_item;
+
+struct auth_stats *auth_request_stats_get(struct auth_request *request)
+{
+	if (request->stats == NULL)
+		request->stats = stats_alloc(request->pool);
+	return stats_fill_ptr(request->stats, auth_stats_item);
+}
+
+void auth_request_stats_add_tempfail(struct auth_request *request)
+{
+	struct auth_stats *stats = auth_request_stats_get(request);
+
+	stats->auth_db_tempfail_count++;
+}
+
+void auth_request_stats_send(struct auth_request *request)
+{
+	string_t *str;
+	buffer_t *buf;
+
+	/* we'll send stats only when the request is finished. this reduces
+	   memory usage and is a bit simpler. auth requests are typically
+	   pretty short lived anyway. */
+	i_assert(!request->stats_sent);
+	request->stats_sent = TRUE;
+
+	if (request->stats == NULL) {
+		/* nothing happened in this request - don't send it */
+		return;
+	}
+	if (!request->set->stats)
+		return;
+
+	buf = buffer_create_dynamic(pool_datastack_create(), 128);
+	stats_export(buf, request->stats);
+
+	str = t_str_new(256);
+	str_append(str, "ADD-USER\t");
+	if (request->user != NULL)
+		str_append_tabescaped(str, request->user);
+	str_append_c(str, '\t');
+	str_append_tabescaped(str, request->service);
+	str_append_c(str, '\t');
+	base64_encode(buf->data, buf->used, str);
+
+	str_append_c(str, '\n');
+	stats_connection_send(auth_stats_conn, str);
+}
+
+void auth_request_stats_init(void)
+{
+	auth_stats_conn = stats_connection_create(USER_STATS_SOCKET_NAME);
+	auth_stats_item = stats_register(&auth_stats_vfuncs);
+}
+
+void auth_request_stats_deinit(void)
+{
+	stats_connection_unref(&auth_stats_conn);
+	stats_unregister(&auth_stats_item);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-request-stats.h	Mon Feb 08 16:20:46 2016 +0200
@@ -0,0 +1,15 @@
+#ifndef AUTH_REQUEST_STATS_H
+#define AUTH_REQUEST_STATS_H
+
+#include "auth-stats.h"
+
+struct auth_request;
+
+struct auth_stats *auth_request_stats_get(struct auth_request *request);
+void auth_request_stats_add_tempfail(struct auth_request *request);
+void auth_request_stats_send(struct auth_request *request);
+
+void auth_request_stats_init(void);
+void auth_request_stats_deinit(void);
+
+#endif
--- a/src/auth/auth-request.c	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/auth-request.c	Mon Feb 08 16:20:46 2016 +0200
@@ -15,6 +15,7 @@
 #include "auth-cache.h"
 #include "auth-request.h"
 #include "auth-request-handler.h"
+#include "auth-request-stats.h"
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
 #include "passdb.h"
@@ -121,6 +122,8 @@
 void auth_request_success(struct auth_request *request,
 			  const void *data, size_t data_size)
 {
+	struct auth_stats *stats;
+
 	i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
 
 	if (request->failed || !request->passdb_success) {
@@ -137,6 +140,11 @@
 		return;
 	}
 
+	stats = auth_request_stats_get(request);
+	stats->auth_success_count++;
+	if (request->master_user != NULL)
+		stats->auth_master_success_count++;
+
 	auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
 	auth_request_refresh_last_access(request);
 	auth_request_handler_reply(request, AUTH_CLIENT_RESULT_SUCCESS,
@@ -145,8 +153,13 @@
 
 void auth_request_fail(struct auth_request *request)
 {
+	struct auth_stats *stats;
+
 	i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
 
+	stats = auth_request_stats_get(request);
+	stats->auth_failure_count++;
+
 	auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
 	auth_request_refresh_last_access(request);
 	auth_request_handler_reply(request, AUTH_CLIENT_RESULT_FAILURE, "", 0);
@@ -172,6 +185,7 @@
 	if (--request->refcount > 0)
 		return;
 
+	auth_request_stats_send(request);
 	auth_request_state_count[request->state]--;
 	auth_refresh_proctitle();
 
@@ -706,6 +720,7 @@
 		   expired record. */
 		const char *cache_key = passdb->cache_key;
 
+		auth_request_stats_add_tempfail(request);
 		if (passdb_cache_verify_plain(request, cache_key,
 					      request->mech_password,
 					      &result, TRUE)) {
@@ -871,6 +886,7 @@
 		   expired record. */
 		const char *cache_key = passdb->cache_key;
 
+		auth_request_stats_add_tempfail(request);
 		if (passdb_cache_lookup_credentials(request, cache_key,
 						    &cache_cred, &cache_scheme,
 						    &result, TRUE)) {
@@ -1004,6 +1020,7 @@
 					   enum userdb_result *result_r,
 					   bool use_expired)
 {
+	struct auth_stats *stats = auth_request_stats_get(request);
 	const char *value;
 	struct auth_cache_node *node;
 	bool expired, neg_expired;
@@ -1011,11 +1028,13 @@
 	value = auth_cache_lookup(passdb_cache, request, key, &node,
 				  &expired, &neg_expired);
 	if (value == NULL || (expired && !use_expired)) {
+		stats->auth_cache_miss_count++;
 		auth_request_log_debug(request, AUTH_SUBSYS_DB,
 				       value == NULL ? "userdb cache miss" :
 				       "userdb cache expired");
 		return FALSE;
 	}
+	stats->auth_cache_hit_count++;
 	auth_request_log_debug(request, AUTH_SUBSYS_DB,
 			       "userdb cache hit: %s", value);
 
@@ -1051,6 +1070,7 @@
 		result_rule = request->userdb->result_success;
 		break;
 	case USERDB_RESULT_INTERNAL_FAILURE:
+		auth_request_stats_add_tempfail(request);
 		result_rule = request->userdb->result_internalfail;
 		break;
 	case USERDB_RESULT_USER_UNKNOWN:
--- a/src/auth/auth-request.h	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/auth-request.h	Mon Feb 08 16:20:46 2016 +0200
@@ -61,6 +61,8 @@
         struct auth_passdb *passdb;
         struct auth_userdb *userdb;
 
+	struct stats *stats;
+
 	/* passdb lookups have a handler, userdb lookups don't */
 	struct auth_request_handler *handler;
         struct auth_master_connection *master;
@@ -138,6 +140,7 @@
 	/* userdb_* fields have been set by the passdb lookup, userdb prefetch
 	   will work. */
 	unsigned int userdb_prefetch_set:1;
+	unsigned int stats_sent:1;
 
 	/* ... mechanism specific data ... */
 };
--- a/src/auth/auth-settings.c	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/auth-settings.c	Mon Feb 08 16:20:46 2016 +0200
@@ -230,6 +230,7 @@
 	DEF(SET_STR, proxy_self),
 	DEF(SET_TIME, failure_delay),
 
+	DEF(SET_BOOL, stats),
 	DEF(SET_BOOL, verbose),
 	DEF(SET_BOOL, debug),
 	DEF(SET_BOOL, debug_passwords),
@@ -269,6 +270,7 @@
 	.proxy_self = "",
 	.failure_delay = 2,
 
+	.stats = FALSE,
 	.verbose = FALSE,
 	.debug = FALSE,
 	.debug_passwords = FALSE,
--- a/src/auth/auth-settings.h	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/auth-settings.h	Mon Feb 08 16:20:46 2016 +0200
@@ -51,6 +51,7 @@
 	const char *proxy_self;
 	unsigned int failure_delay;
 
+	bool stats;
 	bool verbose, debug, debug_passwords;
 	const char *verbose_passwords;
 	bool ssl_require_client_cert;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-stats.c	Mon Feb 08 16:20:46 2016 +0200
@@ -0,0 +1,116 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "stats.h"
+#include "stats-parser.h"
+#include "auth-stats.h"
+
+static struct stats_parser_field auth_stats_fields[] = {
+#define E(parsename, name, type) { parsename, offsetof(struct auth_stats, name), sizeof(((struct auth_stats *)0)->name), type }
+#define EN(parsename, name) E(parsename, name, STATS_PARSER_TYPE_UINT)
+	EN("auth_successes", auth_success_count),
+	EN("auth_master_successes", auth_master_success_count),
+	EN("auth_failures", auth_failure_count),
+	EN("auth_db_tempfails", auth_db_tempfail_count),
+
+	EN("auth_cache_hits", auth_cache_hit_count),
+	EN("auth_cache_misses", auth_cache_miss_count)
+};
+
+static size_t auth_stats_alloc_size(void)
+{
+	return sizeof(struct auth_stats);
+}
+
+static unsigned int auth_stats_field_count(void)
+{
+	return N_ELEMENTS(auth_stats_fields);
+}
+
+static const char *auth_stats_field_name(unsigned int n)
+{
+	i_assert(n < N_ELEMENTS(auth_stats_fields));
+
+	return auth_stats_fields[n].name;
+}
+
+static void
+auth_stats_field_value(string_t *str, const struct stats *stats,
+		       unsigned int n)
+{
+	i_assert(n < N_ELEMENTS(auth_stats_fields));
+
+	stats_parser_value(str, &auth_stats_fields[n], stats);
+}
+
+static bool
+auth_stats_diff(const struct stats *stats1, const struct stats *stats2,
+		struct stats *diff_stats_r, const char **error_r)
+{
+	return stats_parser_diff(auth_stats_fields, N_ELEMENTS(auth_stats_fields),
+				 stats1, stats2, diff_stats_r, error_r);
+}
+
+static void auth_stats_add(struct stats *dest, const struct stats *src)
+{
+	stats_parser_add(auth_stats_fields, N_ELEMENTS(auth_stats_fields),
+			 dest, src);
+}
+
+static bool
+auth_stats_have_changed(const struct stats *_prev, const struct stats *_cur)
+{
+	return memcmp(_prev, _cur, sizeof(struct auth_stats)) != 0;
+}
+
+static void auth_stats_export(buffer_t *buf, const struct stats *_stats)
+{
+	const struct auth_stats *stats = (const struct auth_stats *)_stats;
+
+	buffer_append(buf, stats, sizeof(*stats));
+}
+
+static bool
+auth_stats_import(const unsigned char *data, size_t size, size_t *pos_r,
+		  struct stats *_stats, const char **error_r)
+{
+	struct auth_stats *stats = (struct auth_stats *)_stats;
+
+	if (size < sizeof(*stats)) {
+		*error_r = "auth_stats too small";
+		return FALSE;
+	}
+	memcpy(stats, data, sizeof(*stats));
+	*pos_r = sizeof(*stats);
+	return TRUE;
+}
+
+const struct stats_vfuncs auth_stats_vfuncs = {
+	"auth",
+	auth_stats_alloc_size,
+	auth_stats_field_count,
+	auth_stats_field_name,
+	auth_stats_field_value,
+	auth_stats_diff,
+	auth_stats_add,
+	auth_stats_have_changed,
+	auth_stats_export,
+	auth_stats_import
+};
+
+/* for the stats_auth plugin: */
+void stats_auth_init(void);
+void stats_auth_deinit(void);
+
+static struct stats_item *auth_stats_item;
+
+void stats_auth_init(void)
+{
+	auth_stats_item = stats_register(&auth_stats_vfuncs);
+}
+
+void stats_auth_deinit(void)
+{
+	stats_unregister(&auth_stats_item);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-stats.h	Mon Feb 08 16:20:46 2016 +0200
@@ -0,0 +1,16 @@
+#ifndef AUTH_STATS_H
+#define AUTH_STATS_H
+
+struct auth_stats {
+	uint32_t auth_success_count;
+	uint32_t auth_master_success_count;
+	uint32_t auth_failure_count;
+	uint32_t auth_db_tempfail_count;
+
+	uint32_t auth_cache_hit_count;
+	uint32_t auth_cache_miss_count;
+};
+
+extern const struct stats_vfuncs auth_stats_vfuncs;
+
+#endif
--- a/src/auth/main.c	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/main.c	Mon Feb 08 16:20:46 2016 +0200
@@ -24,6 +24,7 @@
 #include "auth-penalty.h"
 #include "auth-token.h"
 #include "auth-request-handler.h"
+#include "auth-request-stats.h"
 #include "auth-worker-server.h"
 #include "auth-worker-client.h"
 #include "auth-master-connection.h"
@@ -198,6 +199,7 @@
 
 	if (!worker)
 		auth_penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH);
+	auth_request_stats_init();
 	mech_init(global_auth_settings);
 	mech_reg = mech_register_init(global_auth_settings);
 	dict_drivers_register_builtin();
@@ -293,6 +295,8 @@
 	passdbs_deinit();
 	passdb_cache_deinit();
         password_schemes_deinit();
+	auth_request_stats_deinit();
+
 	sql_drivers_deinit();
 	random_deinit();
 	child_wait_deinit();
--- a/src/auth/passdb-cache.c	Fri Feb 05 11:36:32 2016 -0700
+++ b/src/auth/passdb-cache.c	Mon Feb 08 16:20:46 2016 +0200
@@ -2,11 +2,11 @@
 
 #include "auth-common.h"
 #include "restrict-process-size.h"
+#include "auth-request-stats.h"
 #include "password-scheme.h"
 #include "passdb.h"
 #include "passdb-cache.h"
 
-
 struct auth_cache *passdb_cache = NULL;
 
 static void
@@ -28,6 +28,7 @@
 		    bool use_expired, struct auth_cache_node **node_r,
 		    const char **value_r, bool *neg_expired_r)
 {
+	struct auth_stats *stats = auth_request_stats_get(request);
 	const char *value;
 	bool expired;
 
@@ -35,11 +36,13 @@
 	value = auth_cache_lookup(passdb_cache, request, key, node_r,
 				  &expired, neg_expired_r);
 	if (value == NULL || (expired && !use_expired)) {
+		stats->auth_cache_miss_count++;
 		auth_request_log_debug(request, AUTH_SUBSYS_DB,
 				       value == NULL ? "cache miss" :
 				       "cache expired");
 		return FALSE;
 	}
+	stats->auth_cache_hit_count++;
 	passdb_cache_log_hit(request, value);
 
 	*value_r = value;