#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 {
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;
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);
{
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);
}
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);
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);
}
}
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;
}
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)
{
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);
}
} 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;
}
{
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);
}
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);
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 */
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);
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);
.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,