]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: If auth_stats=yes, send statistics to stats process.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 8 Feb 2016 14:20:46 +0000 (16:20 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 8 Feb 2016 14:21:20 +0000 (16:21 +0200)
src/auth/Makefile.am
src/auth/auth-request-stats.c [new file with mode: 0644]
src/auth/auth-request-stats.h [new file with mode: 0644]
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 [new file with mode: 0644]
src/auth/auth-stats.h [new file with mode: 0644]
src/auth/main.c
src/auth/passdb-cache.c

index bfc03abacd251959a501f94fb726afabebbf9767..b92947ffa6443c97e8e1f4328af9fbd6c151e998 100644 (file)
@@ -29,6 +29,7 @@ AM_CPPFLAGS = \
        -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_SOURCES = \
        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 @@ headers = \
        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_DEPENDENCIES = $(LIBDOVECOT_DEPS)
 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 \
diff --git a/src/auth/auth-request-stats.c b/src/auth/auth-request-stats.c
new file mode 100644 (file)
index 0000000..cfb8e81
--- /dev/null
@@ -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);
+}
diff --git a/src/auth/auth-request-stats.h b/src/auth/auth-request-stats.h
new file mode 100644 (file)
index 0000000..5095c2e
--- /dev/null
@@ -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
index 38197b2d5b9bfb2d0db26c4ca7abc26b4bf46924..2b8c7e78ff7510581058165fb9cc7c6dc4c998ad 100644 (file)
@@ -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 @@ struct auth *auth_request_get_auth(struct auth_request *request)
 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 @@ void auth_request_success(struct auth_request *request,
                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_success(struct auth_request *request,
 
 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 @@ void auth_request_unref(struct auth_request **_request)
        if (--request->refcount > 0)
                return;
 
+       auth_request_stats_send(request);
        auth_request_state_count[request->state]--;
        auth_refresh_proctitle();
 
@@ -706,6 +720,7 @@ void auth_request_verify_plain_callback(enum passdb_result result,
                   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 @@ void auth_request_lookup_credentials_callback(enum passdb_result result,
                   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 @@ static bool auth_request_lookup_user_cache(struct auth_request *request,
                                           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 @@ static bool auth_request_lookup_user_cache(struct auth_request *request,
        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 @@ void auth_request_userdb_callback(enum userdb_result result,
                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:
index e9dc0cdb3e32a3ef3d55af6d468a7416a0f06b51..cf63c09545269575d074ff9cf25ba3325fb05c92 100644 (file)
@@ -61,6 +61,8 @@ struct auth_request {
         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 @@ struct auth_request {
        /* 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 ... */
 };
index be5a835b2d94ab380714b799176cf69730c47bd2..7eed12f5dbe92cc333409a48ab32df9f372b7b23 100644 (file)
@@ -230,6 +230,7 @@ static const struct setting_define auth_setting_defines[] = {
        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 @@ static const struct auth_settings auth_default_settings = {
        .proxy_self = "",
        .failure_delay = 2,
 
+       .stats = FALSE,
        .verbose = FALSE,
        .debug = FALSE,
        .debug_passwords = FALSE,
index 30e77aa6a76910e1765a2bb0365eeb0a0e47e1d8..c39f05151bb1a197c2a5b843f8ee6221680982ad 100644 (file)
@@ -51,6 +51,7 @@ struct auth_settings {
        const char *proxy_self;
        unsigned int failure_delay;
 
+       bool stats;
        bool verbose, debug, debug_passwords;
        const char *verbose_passwords;
        bool ssl_require_client_cert;
diff --git a/src/auth/auth-stats.c b/src/auth/auth-stats.c
new file mode 100644 (file)
index 0000000..5205f03
--- /dev/null
@@ -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);
+}
diff --git a/src/auth/auth-stats.h b/src/auth/auth-stats.h
new file mode 100644 (file)
index 0000000..a3431bd
--- /dev/null
@@ -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
index 80d068665cb6f4f8d12e3285eb019dcf2a29c637..5a87c575ceda1f6fb34f9ba084a2915ba53faf80 100644 (file)
@@ -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 @@ static void main_preinit(void)
 
        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 @@ static void main_deinit(void)
        passdbs_deinit();
        passdb_cache_deinit();
         password_schemes_deinit();
+       auth_request_stats_deinit();
+
        sql_drivers_deinit();
        random_deinit();
        child_wait_deinit();
index 2fc9d9227e60b0fbd013bb33631eab56a0fed134..fb211420d44dc2fe622a2430a73f201c94cb8b23 100644 (file)
@@ -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 @@ passdb_cache_lookup(struct auth_request *request, const char *key,
                    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 @@ passdb_cache_lookup(struct auth_request *request, const char *key,
        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;