From: Timo Sirainen Date: Wed, 19 Apr 2023 13:38:52 +0000 (+0300) Subject: dict-redis: Implement new init() API X-Git-Tag: 2.4.0~2058 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=29dd388c3b29c8fadeca80db264443ffce5b8ad4;p=thirdparty%2Fdovecot%2Fcore.git dict-redis: Implement new init() API --- diff --git a/src/lib-dict/dict-redis.c b/src/lib-dict/dict-redis.c index 98f3da748a..9a3ca36ae5 100644 --- a/src/lib-dict/dict-redis.c +++ b/src/lib-dict/dict-redis.c @@ -6,10 +6,10 @@ #include "istream.h" #include "ostream.h" #include "connection.h" +#include "settings.h" +#include "settings-parser.h" #include "dict-private.h" -#define REDIS_DEFAULT_PORT 6379 -#define REDIS_DEFAULT_LOOKUP_TIMEOUT_MSECS (1000*30) #define DICT_USERNAME_SEPARATOR '/' enum redis_input_state { @@ -47,8 +47,7 @@ struct redis_dict_reply { struct redis_dict { struct dict dict; - char *password, *key_prefix, *expire_value; - unsigned int timeout_msecs, db_id; + struct dict_redis_settings *set; struct redis_connection conn; struct timeout *to; @@ -68,6 +67,59 @@ struct redis_dict_transaction_context { char *error; }; +struct dict_redis_settings { + pool_t pool; + + const char *dict_redis_socket_path; + const char *dict_redis_host; + in_port_t dict_redis_port; + const char *dict_redis_key_prefix; + const char *dict_redis_password; + unsigned int dict_redis_db_id; + unsigned int dict_redis_expire; + unsigned int dict_redis_request_timeout; + + struct ip_addr dict_redis_ip; +}; + +static bool dict_redis_settings_check(void *_set, pool_t pool, const char **error_r); + +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type(#name, name, struct dict_redis_settings) +static const struct setting_define dict_redis_setting_defines[] = { + DEF(STR, dict_redis_socket_path), + DEF(STR, dict_redis_host), + DEF(IN_PORT, dict_redis_port), + DEF(STR, dict_redis_key_prefix), + DEF(STR, dict_redis_password), + DEF(UINT, dict_redis_db_id), + DEF(TIME, dict_redis_expire), + DEF(TIME_MSECS, dict_redis_request_timeout), + + SETTING_DEFINE_LIST_END +}; +static const struct dict_redis_settings dict_redis_default_settings = { + .dict_redis_socket_path = "", + .dict_redis_host = "127.0.0.1", + .dict_redis_port = 6379, + .dict_redis_key_prefix = "", + .dict_redis_password = "", + .dict_redis_db_id = 0, + .dict_redis_expire = 0, + .dict_redis_request_timeout = 30 * 1000, +}; +const struct setting_parser_info dict_redis_setting_parser_info = { + .name = "dict_redis", + + .defines = dict_redis_setting_defines, + .defaults = &dict_redis_default_settings, + + .struct_size = sizeof(struct dict_redis_settings), + .pool_offset1 = 1 + offsetof(struct dict_redis_settings, pool), + .check_func = dict_redis_settings_check, +}; + static struct connection_list *redis_connections; static void redis_dict_wait(struct dict *_dict); @@ -134,7 +186,8 @@ static void redis_dict_wait_timeout(struct redis_dict *dict) { const char *reason = t_strdup_printf( "redis: Commit timed out in %u.%03u secs", - dict->timeout_msecs/1000, dict->timeout_msecs%1000); + dict->set->dict_redis_request_timeout / 1000, + dict->set->dict_redis_request_timeout % 1000); redis_disconnected(&dict->conn, reason); } @@ -148,7 +201,7 @@ static const char *redis_wait(struct redis_dict *dict) if (dict->to != NULL) dict->to = io_loop_move_timeout(&dict->to); else { - dict->to = timeout_add(dict->timeout_msecs, + dict->to = timeout_add(dict->set->dict_redis_request_timeout, redis_dict_wait_timeout, dict); } connection_switch_ioloop(&dict->conn.conn); @@ -366,73 +419,120 @@ static const char *redis_escape_username(const char *username) return str_c(str); } -static int -redis_dict_init_legacy(struct dict *driver, const char *uri, - const struct dict_legacy_settings *set, - struct dict **dict_r, const char **error_r) +static bool dict_redis_settings_check(void *_set, pool_t pool ATTR_UNUSED, + const char **error_r) { - struct redis_dict *dict; - struct ip_addr ip; - unsigned int secs; - in_port_t port = REDIS_DEFAULT_PORT; - const char *const *args, *value, *unix_path = NULL; - int ret = 0; + struct dict_redis_settings *set = _set; + if (net_addr2ip(set->dict_redis_host, &set->dict_redis_ip) < 0) { + *error_r = "dict_redis_host is not a valid IP"; + return FALSE; + } + return TRUE; +} + +static struct dict * +redis_dict_init_common(const struct dict *dict_driver, struct event *event, + struct dict_redis_settings *set) +{ if (redis_connections == NULL) { redis_connections = connection_list_init(&redis_conn_set, &redis_conn_vfuncs); } - dict = i_new(struct redis_dict, 1); - if (net_addr2ip("127.0.0.1", &ip) < 0) + struct redis_dict *dict = i_new(struct redis_dict, 1); + dict->set = set; + dict->conn.conn.event_parent = event; + + if (set->dict_redis_socket_path[0] != '\0') { + connection_init_client_unix(redis_connections, &dict->conn.conn, + set->dict_redis_socket_path); + } else { + connection_init_client_ip(redis_connections, &dict->conn.conn, + NULL, &set->dict_redis_ip, + set->dict_redis_port); + } + event_set_append_log_prefix(dict->conn.conn.event, "redis: "); + dict->dict = *dict_driver; + dict->conn.last_reply = str_new(default_pool, 256); + dict->conn.dict = dict; + + i_array_init(&dict->input_states, 4); + i_array_init(&dict->replies, 4); + + return &dict->dict; +} + +static int +redis_dict_init(const struct dict *dict_driver, struct event *event, + struct dict **dict_r, const char **error_r) +{ + struct dict_redis_settings *set; + + if (settings_get(event, &dict_redis_setting_parser_info, 0, + &set, error_r) < 0) + return -1; + *dict_r = redis_dict_init_common(dict_driver, event, set); + return 0; +} + +static int +redis_dict_init_legacy(struct dict *dict_driver, const char *uri, + const struct dict_legacy_settings *legacy_set, + struct dict **dict_r, const char **error_r) +{ + pool_t pool = pool_alloconly_create("dict_redis_settings", 128); + struct dict_redis_settings *set = + p_new(pool, struct dict_redis_settings, 1); + *set = dict_redis_default_settings; + set->pool = pool; + if (net_addr2ip(set->dict_redis_host, &set->dict_redis_ip) < 0) i_unreached(); - dict->timeout_msecs = REDIS_DEFAULT_LOOKUP_TIMEOUT_MSECS; - dict->key_prefix = i_strdup(""); - dict->password = i_strdup(""); - args = t_strsplit(uri, ":"); + const char *const *args = t_strsplit(uri, ":"); + const char *value; + int ret = 0; for (; *args != NULL; args++) { if (str_begins(*args, "path=", &value)) { - unix_path = value; + set->dict_redis_socket_path = p_strdup(pool, value); } else if (str_begins(*args, "host=", &value)) { - if (net_addr2ip(value, &ip) < 0) { + if (net_addr2ip(value, &set->dict_redis_ip) < 0) { *error_r = t_strdup_printf("Invalid IP: %s", value); ret = -1; + } else { + set->dict_redis_host = p_strdup(pool, value); } } else if (str_begins(*args, "port=", &value)) { - if (net_str2port(value, &port) < 0) { + if (net_str2port(value, &set->dict_redis_port) < 0) { *error_r = t_strdup_printf("Invalid port: %s", value); ret = -1; } } else if (str_begins(*args, "prefix=", &value)) { - i_free(dict->key_prefix); - dict->key_prefix = i_strdup(value); + set->dict_redis_key_prefix = p_strdup(pool, value); } else if (str_begins(*args, "db=", &value)) { - if (str_to_uint(value, &dict->db_id) < 0) { + if (str_to_uint(value, &set->dict_redis_db_id) < 0) { *error_r = t_strdup_printf( "Invalid db number: %s", value); ret = -1; } } else if (str_begins(*args, "expire_secs=", &value)) { - if (str_to_uint(value, &secs) < 0 || secs == 0) { + if (str_to_uint(value, &set->dict_redis_expire) < 0 || + set->dict_redis_expire == 0) { *error_r = t_strdup_printf( "Invalid expire_secs: %s", value); ret = -1; } - i_free(dict->expire_value); - dict->expire_value = i_strdup(value); } else if (str_begins(*args, "timeout_msecs=", &value)) { - if (str_to_uint(value, &dict->timeout_msecs) < 0) { + if (str_to_uint(value, &set->dict_redis_request_timeout) < 0) { *error_r = t_strdup_printf( "Invalid timeout_msecs: %s", value); ret = -1; } } else if (str_begins(*args, "password=", &value)) { - i_free(dict->password); - dict->password = i_strdup(value); + set->dict_redis_password = p_strdup(pool, value); } else { *error_r = t_strdup_printf("Unknown parameter: %s", *args); @@ -440,30 +540,12 @@ redis_dict_init_legacy(struct dict *driver, const char *uri, } } if (ret < 0) { - i_free(dict->password); - i_free(dict->key_prefix); - i_free(dict); + pool_unref(&pool); return -1; } - dict->conn.conn.event_parent = set->event_parent; - - if (unix_path != NULL) { - connection_init_client_unix(redis_connections, &dict->conn.conn, - unix_path); - } else { - connection_init_client_ip(redis_connections, &dict->conn.conn, - NULL, &ip, port); - } - event_set_append_log_prefix(dict->conn.conn.event, "redis: "); - dict->dict = *driver; - dict->conn.last_reply = str_new(default_pool, 256); - dict->conn.dict = dict; - - i_array_init(&dict->input_states, 4); - i_array_init(&dict->replies, 4); - - *dict_r = &dict->dict; + *dict_r = redis_dict_init_common(dict_driver, legacy_set->event_parent, + set); return 0; } @@ -481,10 +563,8 @@ static void redis_dict_deinit(struct dict *_dict) str_free(&dict->conn.last_reply); array_free(&dict->replies); array_free(&dict->input_states); + settings_free(dict->set); i_free(dict->last_error); - i_free(dict->expire_value); - i_free(dict->key_prefix); - i_free(dict->password); i_free(dict); if (redis_connections->connections == NULL) @@ -505,7 +585,8 @@ static void redis_dict_lookup_timeout(struct redis_dict *dict) { const char *reason = t_strdup_printf( "redis: Lookup timed out in %u.%03u secs", - dict->timeout_msecs/1000, dict->timeout_msecs%1000); + dict->set->dict_redis_request_timeout / 1000, + dict->set->dict_redis_request_timeout % 1000); redis_disconnected(&dict->conn, reason); } @@ -525,8 +606,8 @@ redis_dict_get_full_key(struct redis_dict *dict, const char *username, } else { i_unreached(); } - if (*dict->key_prefix != '\0') - key = t_strconcat(dict->key_prefix, key, NULL); + if (*dict->set->dict_redis_key_prefix != '\0') + key = t_strconcat(dict->set->dict_redis_key_prefix, key, NULL); return key; } @@ -534,11 +615,12 @@ static void redis_dict_auth(struct redis_dict *dict) { const char *cmd; - if (*dict->password == '\0') + if (*dict->set->dict_redis_password == '\0') return; cmd = t_strdup_printf("*2\r\n$4\r\nAUTH\r\n$%zu\r\n%s\r\n", - strlen(dict->password), dict->password); + strlen(dict->set->dict_redis_password), + dict->set->dict_redis_password); o_stream_nsend_str(dict->conn.conn.output, cmd); redis_input_state_add(dict, REDIS_INPUT_STATE_AUTH); } @@ -550,11 +632,11 @@ static void redis_dict_select_db(struct redis_dict *dict) if (dict->db_id_set) return; dict->db_id_set = TRUE; - if (dict->db_id == 0) { + if (dict->set->dict_redis_db_id == 0) { /* 0 is the default */ return; } - db_str = dec2str(dict->db_id); + db_str = dec2str(dict->set->dict_redis_db_id); cmd = t_strdup_printf("*2\r\n$6\r\nSELECT\r\n$%zu\r\n%s\r\n", strlen(db_str), db_str); o_stream_nsend_str(dict->conn.conn.output, cmd); @@ -585,7 +667,7 @@ static int redis_dict_lookup(struct dict *_dict, connection_client_connect(&dict->conn.conn) < 0) { e_error(dict->conn.conn.event, "Couldn't connect"); } else { - to = timeout_add(dict->timeout_msecs, + to = timeout_add(dict->set->dict_redis_request_timeout, redis_dict_lookup_timeout, dict); if (!dict->connected) { /* wait for connection */ @@ -694,7 +776,8 @@ redis_transaction_commit(struct dict_transaction_context *_ctx, bool async, redis_input_state_add(dict, REDIS_INPUT_STATE_EXEC_REPLY); if (async) { if (dict->to == NULL) { - dict->to = timeout_add(dict->timeout_msecs, + dict->to = timeout_add( + dict->set->dict_redis_request_timeout, redis_dict_wait_timeout, dict); } i_free(ctx); @@ -759,12 +842,13 @@ redis_append_expire(struct redis_dict_transaction_context *ctx, string_t *cmd, const char *key) { struct redis_dict *dict = (struct redis_dict *)ctx->ctx.dict; - const char *expire_value = dict->expire_value; + unsigned int expire_secs = dict->set->dict_redis_expire; if (ctx->ctx.set.expire_secs > 0) - expire_value = dec2str(ctx->ctx.set.expire_secs); - if (expire_value == NULL) + expire_secs = ctx->ctx.set.expire_secs; + if (expire_secs == 0) return; + const char *expire_value = dec2str(expire_secs); str_printfa(cmd, "*3\r\n$6\r\nEXPIRE\r\n$%zu\r\n%s\r\n$%zu\r\n%s\r\n", strlen(key), key, strlen(expire_value), expire_value); @@ -851,6 +935,7 @@ struct dict dict_driver_redis = { .name = "redis", .flags = DICT_DRIVER_FLAG_SUPPORT_EXPIRE_SECS, .v = { + .init = redis_dict_init, .init_legacy = redis_dict_init_legacy, .deinit = redis_dict_deinit, .wait = redis_dict_wait,