aboutsummaryrefslogtreecommitdiffstats
path: root/src/controller.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2017-01-09 17:15:36 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2017-01-09 17:15:36 +0000
commitb6d181c683ed32a67f704f1b022c78651cd88082 (patch)
tree1feb069841ad7b34c8e50e6a07ab7e1ea2b82cf2 /src/controller.c
parent851c1b67b70e21f392115be2e6ddfbefb5d49b8f (diff)
downloadrspamd-b6d181c683ed32a67f704f1b022c78651cd88082.tar.gz
rspamd-b6d181c683ed32a67f704f1b022c78651cd88082.zip
[Feature] Multiple improvements in the maps
- Allow to create maps if directory is writable - Show all backends for all maps - Index backend istead of map - Improve locking logic to retry on failure
Diffstat (limited to 'src/controller.c')
-rw-r--r--src/controller.c170
1 files changed, 104 insertions, 66 deletions
diff --git a/src/controller.c b/src/controller.c
index 27d7c407b..8bdefd249 100644
--- a/src/controller.c
+++ b/src/controller.c
@@ -849,6 +849,32 @@ rspamd_controller_handle_actions (struct rspamd_http_connection_entry *conn_ent,
return 0;
}
+
+static gboolean
+rspamd_controller_can_edit_map (struct rspamd_map_backend *bk)
+{
+ gchar *fpath;
+
+ if (access (bk->uri, W_OK) == 0) {
+ return TRUE;
+ }
+ else if (access (bk->uri, R_OK) == -1 && errno == ENOENT) {
+ fpath = g_path_get_dirname (bk->uri);
+
+ if (fpath) {
+ if (access (fpath, W_OK) == 0) {
+ g_free (fpath);
+
+ return TRUE;
+ }
+
+ g_free (fpath);
+ }
+ }
+
+ return FALSE;
+}
+
/*
* Maps command handler:
* request: /maps
@@ -867,9 +893,10 @@ rspamd_controller_handle_maps (struct rspamd_http_connection_entry *conn_ent,
struct rspamd_http_message *msg)
{
struct rspamd_controller_session *session = conn_ent->ud;
- GList *cur, *tmp = NULL;
+ GList *cur;
struct rspamd_map *map;
struct rspamd_map_backend *bk;
+ guint i;
gboolean editable;
ucl_object_t *obj, *top;
@@ -878,44 +905,38 @@ rspamd_controller_handle_maps (struct rspamd_http_connection_entry *conn_ent,
}
top = ucl_object_typed_new (UCL_ARRAY);
+
/* Iterate over all maps */
cur = session->ctx->cfg->maps;
while (cur) {
map = cur->data;
- bk = g_ptr_array_index (map->backends, 0);
- if (bk->protocol == MAP_PROTO_FILE) {
- if (access (bk->uri, R_OK) == 0) {
- tmp = g_list_prepend (tmp, map);
- }
- }
- cur = g_list_next (cur);
- }
- /* Iterate over selected maps */
- cur = tmp;
- while (cur) {
- map = cur->data;
- bk = g_ptr_array_index (map->backends, 0);
- editable = (access (bk->uri, W_OK) == 0);
+ PTR_ARRAY_FOREACH (map->backends, i, bk) {
+ bk = g_ptr_array_index (map->backends, 0);
- obj = ucl_object_typed_new (UCL_OBJECT);
- ucl_object_insert_key (obj, ucl_object_fromint (map->id),
- "map", 0, false);
- if (map->description) {
- ucl_object_insert_key (obj, ucl_object_fromstring (map->description),
- "description", 0, false);
- }
- ucl_object_insert_key (obj, ucl_object_fromstring (bk->uri),
- "uri", 0, false);
- ucl_object_insert_key (obj, ucl_object_frombool (editable),
- "editable", 0, false);
- ucl_array_append (top, obj);
+ if (bk->protocol == MAP_PROTO_FILE) {
+ editable = rspamd_controller_can_edit_map (bk);
- cur = g_list_next (cur);
- }
+ if (!editable && access (bk->uri, R_OK) == -1) {
+ /* Skip unreadable and non-existing maps */
+ continue;
+ }
- if (tmp) {
- g_list_free (tmp);
+ obj = ucl_object_typed_new (UCL_OBJECT);
+ ucl_object_insert_key (obj, ucl_object_fromint (bk->id),
+ "map", 0, false);
+ if (map->description) {
+ ucl_object_insert_key (obj, ucl_object_fromstring (map->description),
+ "description", 0, false);
+ }
+ ucl_object_insert_key (obj, ucl_object_fromstring (bk->uri),
+ "uri", 0, false);
+ ucl_object_insert_key (obj, ucl_object_frombool (editable),
+ "editable", 0, false);
+ ucl_array_append (top, obj);
+ }
+ }
+ cur = g_list_next (cur);
}
rspamd_controller_send_ucl (conn_ent, top);
@@ -941,7 +962,7 @@ rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
const rspamd_ftok_t *idstr;
struct stat st;
gint fd;
- gulong id;
+ gulong id, i;
gboolean found = FALSE;
struct rspamd_http_message *reply;
@@ -965,13 +986,16 @@ rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
/* Now let's be sure that we have map defined in configuration */
cur = session->ctx->cfg->maps;
- while (cur) {
+ while (cur && !found) {
map = cur->data;
- bk = g_ptr_array_index (map->backends, 0);
- if (map->id == id && bk->protocol == MAP_PROTO_FILE) {
- found = TRUE;
- break;
+
+ PTR_ARRAY_FOREACH (map->backends, i, bk) {
+ if (bk->id == id && bk->protocol == MAP_PROTO_FILE) {
+ found = TRUE;
+ break;
+ }
}
+
cur = g_list_next (cur);
}
@@ -981,28 +1005,28 @@ rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
return 0;
}
- bk = g_ptr_array_index (map->backends, 0);
-
if (stat (bk->uri, &st) == -1 || (fd = open (bk->uri, O_RDONLY)) == -1) {
- msg_err_session ("cannot open map %s: %s", bk->uri, strerror (errno));
- rspamd_controller_send_error (conn_ent, 500, "Map open error");
- return 0;
+ reply = rspamd_http_new_message (HTTP_RESPONSE);
+ reply->date = time (NULL);
+ reply->code = 200;
}
+ else {
+
+ reply = rspamd_http_new_message (HTTP_RESPONSE);
+ reply->date = time (NULL);
+ reply->code = 200;
- reply = rspamd_http_new_message (HTTP_RESPONSE);
- reply->date = time (NULL);
- reply->code = 200;
+ if (!rspamd_http_message_set_body_from_fd (reply, fd)) {
+ close (fd);
+ rspamd_http_message_unref (reply);
+ msg_err_session ("cannot read map %s: %s", bk->uri, strerror (errno));
+ rspamd_controller_send_error (conn_ent, 500, "Map read error");
+ return 0;
+ }
- if (!rspamd_http_message_set_body_from_fd (reply, fd)) {
close (fd);
- rspamd_http_message_unref (reply);
- msg_err_session ("cannot read map %s: %s", bk->uri, strerror (errno));
- rspamd_controller_send_error (conn_ent, 500, "Map read error");
- return 0;
}
- close (fd);
-
rspamd_http_connection_reset (conn_ent->conn);
rspamd_http_connection_write_message (conn_ent->conn, reply, NULL,
"text/plain", conn_ent, conn_ent->conn->fd,
@@ -2165,7 +2189,7 @@ rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
struct rspamd_map_backend *bk;
struct rspamd_controller_worker_ctx *ctx;
const rspamd_ftok_t *idstr;
- gulong id;
+ gulong id, i, ntries = 0;
gboolean found = FALSE;
gint fd;
@@ -2199,12 +2223,14 @@ rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
/* Now let's be sure that we have map defined in configuration */
cur = ctx->cfg->maps;
- while (cur) {
+ while (cur && !found) {
map = cur->data;
- bk = g_ptr_array_index (map->backends, 0);
- if (map->id == id && bk->protocol == MAP_PROTO_FILE) {
- found = TRUE;
- break;
+
+ PTR_ARRAY_FOREACH (map->backends, i, bk) {
+ if (bk->id == id && bk->protocol == MAP_PROTO_FILE) {
+ found = TRUE;
+ break;
+ }
}
cur = g_list_next (cur);
}
@@ -2215,19 +2241,30 @@ rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
return 0;
}
- bk = g_ptr_array_index (map->backends, 0);
- if (g_atomic_int_compare_and_exchange (map->locked, 0, 1)) {
- msg_info_session ("map locked: %s", bk->uri);
- rspamd_controller_send_error (conn_ent, 404, "Map is locked");
- return 0;
+ while (g_atomic_int_compare_and_exchange (map->locked, 0, 1)) {
+ struct timespec sleep_ts = {
+ .tv_sec = 0,
+ .tv_nsec = 100000000ULL,
+ };
+
+ if (ntries > 5) {
+ msg_info_session ("map locked: %s", bk->uri);
+ rspamd_controller_send_error (conn_ent, 404, "Map is locked");
+ return 0;
+ }
+
+ ntries ++;
+ nanosleep (&sleep_ts, NULL);
}
/* Set lock */
- fd = open (bk->uri, O_WRONLY | O_TRUNC);
+ fd = open (bk->uri, O_WRONLY | O_TRUNC | O_CREAT, 00644);
+
if (fd == -1) {
g_atomic_int_set (map->locked, 0);
msg_info_session ("map %s open error: %s", bk->uri, strerror (errno));
- rspamd_controller_send_error (conn_ent, 404, "Map id not found");
+ rspamd_controller_send_error (conn_ent, 404, "Cannot open map: %s",
+ strerror (errno));
return 0;
}
@@ -2235,7 +2272,8 @@ rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
msg_info_session ("map %s write error: %s", bk->uri, strerror (errno));
close (fd);
g_atomic_int_set (map->locked, 0);
- rspamd_controller_send_error (conn_ent, 500, "Map write error");
+ rspamd_controller_send_error (conn_ent, 500, "Map write error: %s",
+ strerror (errno));
return 0;
}