diff options
author | Ralph Boehme <slow@samba.org> | 2019-03-14 07:38:20 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2019-08-08 20:24:31 +0000 |
commit | 1ef96e09071458322845b4323fa59cf4e7475783 (patch) | |
tree | c0f2f557612e8f51c67fe0ab567bbcf2a5852b1c /source3/rpc_server | |
parent | f80f8c5f99f6b5949d14ee62261a7dd448e9b0fe (diff) | |
download | samba-1ef96e09071458322845b4323fa59cf4e7475783.tar.gz |
s3-mdssvc: factor out Tracker backend logic
This moves all Tracker backend logic into a modularized component.
This should not result in any change in behaviour, it just paves the way
for adding additional backends. Currently the only available backend is
Gnome Tracker.
slq_destroy_send/recv is not needed anymore as the problem is solved now by
correctly checking if an async Tracker request was cancelled and we got
G_IO_ERROR_CANCELLED in tracker_con_cb() or tracker_query_cb() and avoid using
user_data in that the case.
Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3/rpc_server')
-rw-r--r-- | source3/rpc_server/mdssvc/mdssvc.c | 357 | ||||
-rw-r--r-- | source3/rpc_server/mdssvc/mdssvc.h | 27 | ||||
-rw-r--r-- | source3/rpc_server/mdssvc/mdssvc_tracker.c | 491 | ||||
-rw-r--r-- | source3/rpc_server/mdssvc/mdssvc_tracker.h | 62 | ||||
-rw-r--r-- | source3/rpc_server/mdssvc/sparql_parser.y | 9 | ||||
-rw-r--r-- | source3/rpc_server/mdssvc/sparql_parser_test.c | 11 | ||||
-rw-r--r-- | source3/rpc_server/wscript_build | 3 |
7 files changed, 623 insertions, 337 deletions
diff --git a/source3/rpc_server/mdssvc/mdssvc.c b/source3/rpc_server/mdssvc/mdssvc.c index 85da89d4ef0..42eb960bb26 100644 --- a/source3/rpc_server/mdssvc/mdssvc.c +++ b/source3/rpc_server/mdssvc/mdssvc.c @@ -27,8 +27,7 @@ #include "lib/dbwrap/dbwrap_rbt.h" #include "libcli/security/dom_sid.h" #include "mdssvc.h" -#include "rpc_server/mdssvc/sparql_parser.tab.h" -#include "lib/tevent_glib_glue.h" +#include "mdssvc_tracker.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_SRV @@ -74,29 +73,6 @@ static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx, static bool slrpc_close_query(struct mds_ctx *mds_ctx, const DALLOC_CTX *query, DALLOC_CTX *reply); -static struct tevent_req *slq_destroy_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct sl_query **slq) -{ - struct tevent_req *req; - struct slq_destroy_state *state; - - req = tevent_req_create(mem_ctx, &state, - struct slq_destroy_state); - if (req == NULL) { - return NULL; - } - state->slq = talloc_move(state, slq); - tevent_req_done(req); - - return tevent_req_post(req, ev); -} - -static void slq_destroy_recv(struct tevent_req *req) -{ - tevent_req_received(req); -} - /************************************************ * Misc utility functions ************************************************/ @@ -268,33 +244,6 @@ char *mds_dalloc_dump(DALLOC_CTX *dd, int nestinglevel) return logstring; } -static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri) -{ - GFile *f; - char *path; - char *talloc_path; - - f = g_file_new_for_uri(uri); - if (f == NULL) { - return NULL; - } - - path = g_file_get_path(f); - g_object_unref(f); - - if (path == NULL) { - return NULL; - } - - talloc_path = talloc_strdup(mem_ctx, path); - g_free(path); - if (talloc_path == NULL) { - return NULL; - } - - return talloc_path; -} - /** * Add requested metadata for a query result element * @@ -583,16 +532,7 @@ static int slq_destructor_cb(struct sl_query *slq) slq->mds_ctx = NULL; } - if (slq->tracker_cursor != NULL) { - g_object_unref(slq->tracker_cursor); - slq->tracker_cursor = NULL; - } - - if (slq->gcancellable != NULL) { - g_cancellable_cancel(slq->gcancellable); - g_object_unref(slq->gcancellable); - slq->gcancellable = NULL; - } + TALLOC_FREE(slq->backend_private); return 0; } @@ -701,98 +641,12 @@ static bool inode_map_add(struct sl_query *slq, uint64_t ino, const char *path) return true; } -/************************************************ - * Tracker async callbacks - ************************************************/ - -static void tracker_con_cb(GObject *object, - GAsyncResult *res, - gpointer user_data) +bool mds_add_result(struct sl_query *slq, const char *path) { - struct mds_ctx *mds_ctx = talloc_get_type_abort(user_data, struct mds_ctx); - GError *error = NULL; - - mds_ctx->tracker_con = tracker_sparql_connection_get_finish(res, - &error); - if (error) { - DEBUG(1, ("Could not connect to Tracker: %s\n", - error->message)); - g_error_free(error); - } - - DEBUG(10, ("connected to Tracker\n")); -} - -static void tracker_cursor_cb_destroy_done(struct tevent_req *subreq); - -static void tracker_cursor_cb(GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GError *error = NULL; - struct sl_query *slq = talloc_get_type_abort(user_data, struct sl_query); - gboolean more_results; - const gchar *uri; - char *path; - int result; struct stat_ex sb; uint64_t ino64; + int result; bool ok; - struct tevent_req *req; - - SLQ_DEBUG(10, slq, "tracker_cursor_cb"); - - more_results = tracker_sparql_cursor_next_finish(slq->tracker_cursor, - res, - &error); - - if (slq->state == SLQ_STATE_DONE) { - /* - * The query was closed in slrpc_close_query(), so we - * don't care for results or errors from - * tracker_sparql_cursor_next_finish(), we just go - * ahead and schedule deallocation of the slq handle. - * - * We have to shedule the deallocation via tevent, - * because we have to unref the cursor glib object and - * we can't do it here, because it's still used after - * we return. - */ - SLQ_DEBUG(10, slq, "closed"); - - req = slq_destroy_send(slq, global_event_context(), &slq); - if (req == NULL) { - slq->state = SLQ_STATE_ERROR; - return; - } - tevent_req_set_callback(req, tracker_cursor_cb_destroy_done, NULL); - return; - } - - if (error) { - DEBUG(1, ("Tracker cursor: %s\n", error->message)); - g_error_free(error); - slq->state = SLQ_STATE_ERROR; - return; - } - - if (!more_results) { - slq->state = SLQ_STATE_DONE; - return; - } - - uri = tracker_sparql_cursor_get_string(slq->tracker_cursor, 0, NULL); - if (uri == NULL) { - DEBUG(1, ("error fetching Tracker URI\n")); - slq->state = SLQ_STATE_ERROR; - return; - } - path = tracker_to_unix_path(slq->query_results, uri); - if (path == NULL) { - DEBUG(1, ("error converting Tracker URI to path: %s\n", uri)); - slq->state = SLQ_STATE_ERROR; - return; - } /* * We're in a tevent callback which means in the case of @@ -818,12 +672,12 @@ static void tracker_cursor_cb(GObject *object, result = sys_stat(path, &sb, false); if (result != 0) { unbecome_authenticated_pipe_user(); - goto done; + return true; } result = access(path, R_OK); if (result != 0) { unbecome_authenticated_pipe_user(); - goto done; + return true; } unbecome_authenticated_pipe_user(); @@ -841,7 +695,7 @@ static void tracker_cursor_cb(GObject *object, sizeof(uint64_t), cnid_comp_fn); if (!ok) { - goto done; + return false; } } @@ -855,7 +709,7 @@ static void tracker_cursor_cb(GObject *object, if (result != 0) { DBG_ERR("dalloc error\n"); slq->state = SLQ_STATE_ERROR; - return; + return false; } ok = add_filemeta(slq->reqinfo, slq->query_results->fm_array, @@ -864,73 +718,18 @@ static void tracker_cursor_cb(GObject *object, if (!ok) { DBG_ERR("add_filemeta error\n"); slq->state = SLQ_STATE_ERROR; - return; + return false; } ok = inode_map_add(slq, ino64, path); if (!ok) { DEBUG(1, ("inode_map_add error\n")); slq->state = SLQ_STATE_ERROR; - return; + return false; } slq->query_results->num_results++; - -done: - if (slq->query_results->num_results >= MAX_SL_RESULTS) { - slq->state = SLQ_STATE_FULL; - SLQ_DEBUG(10, slq, "full"); - return; - } - - slq->state = SLQ_STATE_RESULTS; - SLQ_DEBUG(10, slq, "cursor next"); - tracker_sparql_cursor_next_async(slq->tracker_cursor, - slq->gcancellable, - tracker_cursor_cb, - slq); -} - -static void tracker_cursor_cb_destroy_done(struct tevent_req *req) -{ - slq_destroy_recv(req); - TALLOC_FREE(req); - - DEBUG(10, ("%s\n", __func__)); -} - -static void tracker_query_cb(GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GError *error = NULL; - struct sl_query *slq = talloc_get_type_abort(user_data, struct sl_query); - - SLQ_DEBUG(10, slq, "tracker_query_cb"); - - slq->tracker_cursor = tracker_sparql_connection_query_finish( - TRACKER_SPARQL_CONNECTION(object), - res, - &error); - if (error) { - slq->state = SLQ_STATE_ERROR; - DEBUG(1, ("Tracker query error: %s\n", error->message)); - g_error_free(error); - return; - } - - if (slq->state == SLQ_STATE_DONE) { - SLQ_DEBUG(10, slq, "done"); - talloc_free(slq); - return; - } - - slq->state = SLQ_STATE_RESULTS; - - tracker_sparql_cursor_next_async(slq->tracker_cursor, - slq->gcancellable, - tracker_cursor_cb, - slq); + return true; } /*********************************************************** @@ -1141,18 +940,12 @@ static bool slrpc_open_query(struct mds_ctx *mds_ctx, int result; char *querystring; char *scope = NULL; - char *escaped_scope = NULL; array = dalloc_zero(reply, sl_array_t); if (array == NULL) { return false; } - if (mds_ctx->tracker_con == NULL) { - DEBUG(1, ("no connection to Tracker\n")); - goto error; - } - /* Allocate and initialize query object */ slq = talloc_zero(mds_ctx, struct sl_query); if (slq == NULL) { @@ -1177,12 +970,6 @@ static bool slrpc_open_query(struct mds_ctx *mds_ctx, goto error; } - slq->gcancellable = g_cancellable_new(); - if (slq->gcancellable == NULL) { - DEBUG(1,("error from g_cancellable_new\n")); - goto error; - } - querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1, "kMDQueryString"); @@ -1225,15 +1012,7 @@ static bool slrpc_open_query(struct mds_ctx *mds_ctx, goto error; } - escaped_scope = g_uri_escape_string(scope, - G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, - TRUE); - if (escaped_scope == NULL) { - goto error; - } - - slq->path_scope = talloc_strdup(slq, escaped_scope); - g_free(escaped_scope); + slq->path_scope = talloc_strdup(slq, scope); if (slq->path_scope == NULL) { goto error; } @@ -1267,32 +1046,12 @@ static bool slrpc_open_query(struct mds_ctx *mds_ctx, DLIST_ADD(mds_ctx->query_list, slq); - ok = map_spotlight_to_sparql_query(slq); + ok = mds_ctx->mdssvc_ctx->backend->search_start(slq); if (!ok) { - /* - * Two cases: - * - * 1) the query string is "false", the parser returns - * an error for that. We're supposed to return -1 - * here. - * - * 2) the parsing really failed, in that case we're - * probably supposed to return -1 too, this needs - * verification though - */ - SLQ_DEBUG(10, slq, "map failed"); + DBG_ERR("backend search_start failed\n"); goto error; } - DEBUG(10, ("SPARQL query: \"%s\"\n", slq->sparql_query)); - - tracker_sparql_connection_query_async(mds_ctx->tracker_con, - slq->sparql_query, - slq->gcancellable, - tracker_query_cb, - slq); - slq->state = SLQ_STATE_RUNNING; - sl_result = 0; result = dalloc_add_copy(array, &sl_result, uint64_t); if (result != 0) { @@ -1383,11 +1142,7 @@ static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx, } if (slq->state == SLQ_STATE_FULL) { slq->state = SLQ_STATE_RESULTS; - tracker_sparql_cursor_next_async( - slq->tracker_cursor, - slq->gcancellable, - tracker_cursor_cb, - slq); + slq->mds_ctx->mdssvc_ctx->backend->search_cont(slq); } break; @@ -1748,35 +1503,8 @@ static bool slrpc_close_query(struct mds_ctx *mds_ctx, goto done; } - switch (slq->state) { - case SLQ_STATE_RUNNING: - case SLQ_STATE_RESULTS: - DEBUG(10, ("close: requesting query close\n")); - /* - * Mark the query is done so the cursor callback can - * act accordingly by stopping to request more results - * and sheduling query resource deallocation via - * tevent. - */ - slq->state = SLQ_STATE_DONE; - break; - - case SLQ_STATE_FULL: - case SLQ_STATE_DONE: - DEBUG(10, ("close: query was done or result queue was full\n")); - /* - * We can directly deallocate the query because there - * are no pending Tracker async calls in flight in - * these query states. - */ - TALLOC_FREE(slq); - break; - - default: - DEBUG(1, ("close: unexpected state: %d\n", slq->state)); - break; - } - + SLQ_DEBUG(10, slq, "close"); + TALLOC_FREE(slq); done: sl_res = 0; @@ -1793,6 +1521,8 @@ done: static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev) { + bool ok; + if (mdssvc_ctx != NULL) { return mdssvc_ctx; } @@ -1804,17 +1534,12 @@ static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev) mdssvc_ctx->ev_ctx = ev; - mdssvc_ctx->gmain_ctx = g_main_context_default(); - if (mdssvc_ctx->gmain_ctx == NULL) { - DBG_ERR("error from g_main_context_new\n"); - return NULL; - } + mdssvc_ctx->backend = &mdsscv_backend_tracker; - mdssvc_ctx->glue = samba_tevent_glib_glue_create(ev, - mdssvc_ctx->ev_ctx, - mdssvc_ctx->gmain_ctx); - if (mdssvc_ctx->glue == NULL) { - DBG_ERR("samba_tevent_glib_glue_create failed\n"); + ok = mdsscv_backend_tracker.init(mdssvc_ctx); + if (!ok) { + DBG_ERR("backend init failed\n"); + TALLOC_FREE(mdssvc_ctx); return NULL; } @@ -1829,24 +1554,26 @@ static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev) **/ bool mds_init(struct messaging_context *msg_ctx) { -#if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36) - g_type_init(); -#endif return true; } bool mds_shutdown(void) { + bool ok; + if (mdssvc_ctx == NULL) { return false; } - samba_tevent_glib_glue_quit(mdssvc_ctx->glue); - TALLOC_FREE(mdssvc_ctx->glue); + ok = mdsscv_backend_tracker.shutdown(mdssvc_ctx); + if (!ok) { + goto fail; + } + ok = true; +fail: TALLOC_FREE(mdssvc_ctx); - - return true; + return ok; } /** @@ -1866,14 +1593,6 @@ static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx) } TALLOC_FREE(mds_ctx->ino_path_map); - if (mds_ctx->tracker_con != NULL) { - g_object_unref(mds_ctx->tracker_con); - } - if (mds_ctx->gcancellable != NULL) { - g_cancellable_cancel(mds_ctx->gcancellable); - g_object_unref(mds_ctx->gcancellable); - } - ZERO_STRUCTP(mds_ctx); return 0; @@ -1891,6 +1610,7 @@ struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx, const char *path) { struct mds_ctx *mds_ctx; + bool ok; mds_ctx = talloc_zero(mem_ctx, struct mds_ctx); if (mds_ctx == NULL) { @@ -1922,15 +1642,12 @@ struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx, goto error; } - mds_ctx->gcancellable = g_cancellable_new(); - if (mds_ctx->gcancellable == NULL) { - DBG_ERR("error from g_cancellable_new\n"); + ok = mds_ctx->mdssvc_ctx->backend->connect(mds_ctx); + if (!ok) { + DBG_ERR("backend connect failed\n"); goto error; } - tracker_sparql_connection_get_async(mds_ctx->gcancellable, - tracker_con_cb, mds_ctx); - return mds_ctx; error: diff --git a/source3/rpc_server/mdssvc/mdssvc.h b/source3/rpc_server/mdssvc/mdssvc.h index 2260a38e6d2..1be07f4a40a 100644 --- a/source3/rpc_server/mdssvc/mdssvc.h +++ b/source3/rpc_server/mdssvc/mdssvc.h @@ -33,12 +33,6 @@ */ #undef TRUE #undef FALSE -/* allow building with --picky-developer */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#include <gio/gio.h> -#include <tracker-sparql.h> -#pragma GCC diagnostic pop #define MAX_SL_FRAGMENT_SIZE 0xFFFFF #define MAX_SL_RESULTS 100 @@ -84,6 +78,7 @@ typedef enum { struct sl_query { struct sl_query *prev, *next; /* list pointers */ struct mds_ctx *mds_ctx; /* context handle */ + void *backend_private; /* search backend private data */ slq_state_t state; /* query state */ struct timeval start_time; /* Query start time */ struct timeval last_used; /* Time of last result fetch */ @@ -94,12 +89,9 @@ struct sl_query { uint64_t ctx2; /* client context 2 */ sl_array_t *reqinfo; /* array with requested metadata */ const char *query_string; /* the Spotlight query string */ - const char *sparql_query; /* the SPARQL query string */ uint64_t *cnids; /* restrict query to these CNIDs */ size_t cnids_num; /* Size of slq_cnids array */ const char *path_scope; /* path to directory to search */ - GCancellable *gcancellable; - TrackerSparqlCursor *tracker_cursor; /* Tracker SPARQL query result cursor */ struct sl_rslts *query_results; /* query results */ TALLOC_CTX *entries_ctx; /* talloc parent of the search results */ }; @@ -119,23 +111,31 @@ struct sl_inode_path_map { /* Per process state */ struct mdssvc_ctx { struct tevent_context *ev_ctx; - GMainContext *gmain_ctx; - struct tevent_glib_glue *glue; + struct mdssvc_backend *backend; + void *backend_private; }; /* Per tree connect state */ struct mds_ctx { struct mdssvc_ctx *mdssvc_ctx; + void *backend_private; struct auth_session_info *pipe_session_info; struct dom_sid sid; uid_t uid; const char *spath; - GCancellable *gcancellable; - TrackerSparqlConnection *tracker_con; struct sl_query *query_list; /* list of active queries */ struct db_context *ino_path_map; /* dbwrap rbt for storing inode->path mappings */ }; +struct mdssvc_backend { + bool (*init)(struct mdssvc_ctx *mdssvc_ctx); + bool (*connect)(struct mds_ctx *mds_ctx); + bool (*search_map)(struct sl_query *slq); + bool (*search_start)(struct sl_query *slq); + bool (*search_cont)(struct sl_query *slq); + bool (*shutdown)(struct mdssvc_ctx *mdssvc_ctx); +}; + /****************************************************************************** * Function declarations ******************************************************************************/ @@ -153,5 +153,6 @@ extern bool mds_dispatch(struct mds_ctx *query_ctx, struct mdssvc_blob *request_blob, struct mdssvc_blob *response_blob); extern char *mds_dalloc_dump(DALLOC_CTX *dd, int nestinglevel); +bool mds_add_result(struct sl_query *slq, const char *path); #endif /* _MDSSVC_H */ diff --git a/source3/rpc_server/mdssvc/mdssvc_tracker.c b/source3/rpc_server/mdssvc/mdssvc_tracker.c new file mode 100644 index 00000000000..fab8bd22324 --- /dev/null +++ b/source3/rpc_server/mdssvc/mdssvc_tracker.c @@ -0,0 +1,491 @@ +/* + Unix SMB/CIFS implementation. + Main metadata server / Spotlight routines / Tracker backend + + Copyright (C) Ralph Boehme 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util/time_basic.h" +#include "mdssvc.h" +#include "mdssvc_tracker.h" +#include "lib/tevent_glib_glue.h" +#include "rpc_server/mdssvc/sparql_parser.tab.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +static struct mdssvc_tracker_ctx *mdssvc_tracker_ctx; + +/************************************************ + * Tracker async callbacks + ************************************************/ + +static void tracker_con_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct mds_tracker_ctx *ctx = NULL; + TrackerSparqlConnection *tracker_con = NULL; + GError *error = NULL; + + tracker_con = tracker_sparql_connection_get_finish(res, &error); + if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + /* + * If the async request was cancelled, user_data will already be + * talloc_free'd, so we must be carefully checking for + * G_IO_ERROR_CANCELLED before using user_data. + */ + DBG_ERR("Tracker connection cancelled\n"); + g_error_free(error); + return; + } + /* + * Ok, we're not canclled, we can now safely use user_data. + */ + ctx = talloc_get_type_abort(user_data, struct mds_tracker_ctx); + ctx->async_pending = false; + /* + * Check error again, above we only checked for G_IO_ERROR_CANCELLED. + */ + if (error) { + DBG_ERR("Could not connect to Tracker: %s\n", error->message); + g_error_free(error); + return; + } + + ctx->tracker_con = tracker_con; + + DBG_DEBUG("connected to Tracker\n"); +} + +static void tracker_cursor_cb(GObject *object, + GAsyncResult *res, + gpointer user_data); + +static void tracker_query_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct sl_tracker_query *tq = NULL; + struct sl_query *slq = NULL; + TrackerSparqlConnection *conn = NULL; + TrackerSparqlCursor *cursor = NULL; + GError *error = NULL; + + conn = TRACKER_SPARQL_CONNECTION(object); + + cursor = tracker_sparql_connection_query_finish(conn, res, &error); + /* + * If the async request was cancelled, user_data will already be + * talloc_free'd, so we must be carefully checking for + * G_IO_ERROR_CANCELLED before using user_data. + */ + if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + DBG_ERR("Tracker query cancelled\n"); + if (cursor != NULL) { + g_object_unref(cursor); + } + g_error_free(error); + return; + } + /* + * Ok, we're not cancelled, we can now safely use user_data. + */ + tq = talloc_get_type_abort(user_data, struct sl_tracker_query); + tq->async_pending = false; + slq = tq->slq; + /* + * Check error again, above we only checked for G_IO_ERROR_CANCELLED. + */ + if (error) { + DBG_ERR("Tracker query error: %s\n", error->message); + g_error_free(error); + slq->state = SLQ_STATE_ERROR; + return; + } + + tq->cursor = cursor; + slq->state = SLQ_STATE_RESULTS; + + tracker_sparql_cursor_next_async(tq->cursor, + tq->gcancellable, + tracker_cursor_cb, + tq); + tq->async_pending = true; +} + +static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri) +{ + GFile *f = NULL; + char *path = NULL; + char *talloc_path = NULL; + + f = g_file_new_for_uri(uri); + if (f == NULL) { + return NULL; + } + + path = g_file_get_path(f); + g_object_unref(f); + + if (path == NULL) { + return NULL; + } + + talloc_path = talloc_strdup(mem_ctx, path); + g_free(path); + if (talloc_path == NULL) { + return NULL; + } + + return talloc_path; +} + +static void tracker_cursor_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlCursor *cursor = NULL; + struct sl_tracker_query *tq = NULL; + struct sl_query *slq = NULL; + const gchar *uri = NULL; + GError *error = NULL; + char *path = NULL; + gboolean more_results; + bool ok; + + cursor = TRACKER_SPARQL_CURSOR(object); + more_results = tracker_sparql_cursor_next_finish(cursor, + res, + &error); + /* + * If the async request was cancelled, user_data will already be + * talloc_free'd, so we must be carefully checking for + * G_IO_ERROR_CANCELLED before using user_data. + */ + if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free(error); + g_object_unref(cursor); + return; + } + /* + * Ok, we're not canclled, we can now safely use user_data. + */ + tq = talloc_get_type_abort(user_data, struct sl_tracker_query); + tq->async_pending = false; + slq = tq->slq; + /* + * Check error again, above we only checked for G_IO_ERROR_CANCELLED. + */ + if (error) { + DBG_ERR("Tracker cursor: %s\n", error->message); + g_error_free(error); + slq->state = SLQ_STATE_ERROR; + return; + } + + SLQ_DEBUG(10, slq, "results"); + + if (!more_results) { + slq->state = SLQ_STATE_DONE; + + g_object_unref(tq->cursor); + tq->cursor = NULL; + + g_object_unref(tq->gcancellable); + tq->gcancellable = NULL; + return; + } + + uri = tracker_sparql_cursor_get_string(tq->cursor, 0, NULL); + if (uri == NULL) { + DBG_ERR("error fetching Tracker URI\n"); + slq->state = SLQ_STATE_ERROR; + return; + } + + path = tracker_to_unix_path(slq->query_results, uri); + if (path == NULL) { + DBG_ERR("error converting Tracker URI to path: %s\n", uri); + slq->state = SLQ_STATE_ERROR; + return; + } + + ok = mds_add_result(slq, path); + if (!ok) { + DBG_ERR("error adding result for path: %s\n", uri); + slq->state = SLQ_STATE_ERROR; + return; + } + + if (slq->query_results->num_results >= MAX_SL_RESULTS) { + slq->state = SLQ_STATE_FULL; + SLQ_DEBUG(10, slq, "full"); + return; + } + + slq->state = SLQ_STATE_RESULTS; + SLQ_DEBUG(10, slq, "cursor next"); + + tracker_sparql_cursor_next_async(tq->cursor, + tq->gcancellable, + tracker_cursor_cb, + tq); + tq->async_pending = true; +} + +/* + * This gets called once, even if the backend is not configured by the user + */ +static bool mdssvc_tracker_init(struct mdssvc_ctx *mdssvc_ctx) +{ + if (mdssvc_tracker_ctx != NULL) { + return true; + } + +#if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36) + g_type_init(); +#endif + + mdssvc_tracker_ctx = talloc_zero(mdssvc_ctx, struct mdssvc_tracker_ctx); + if (mdssvc_tracker_ctx == NULL) { + return false; + } + mdssvc_tracker_ctx->mdssvc_ctx = mdssvc_ctx; + + return true; +} + +/* + * This gets called per mdscmd_open / tcon. This runs initialisation code that + * should only run if the tracker backend is actually used. + */ +static bool mdssvc_tracker_prepare(void) +{ + if (mdssvc_tracker_ctx->gmain_ctx != NULL) { + /* + * Assuming everything is setup if gmain_ctx is. + */ + return true; + } + + mdssvc_tracker_ctx->gmain_ctx = g_main_context_new(); + if (mdssvc_tracker_ctx->gmain_ctx == NULL) { + DBG_ERR("error from g_main_context_new\n"); + TALLOC_FREE(mdssvc_tracker_ctx); + return false; + } + + mdssvc_tracker_ctx->glue = samba_tevent_glib_glue_create( + mdssvc_tracker_ctx, + mdssvc_tracker_ctx->mdssvc_ctx->ev_ctx, + mdssvc_tracker_ctx->gmain_ctx); + if (mdssvc_tracker_ctx->glue == NULL) { + DBG_ERR("samba_tevent_glib_glue_create failed\n"); + g_object_unref(mdssvc_tracker_ctx->gmain_ctx); + TALLOC_FREE(mdssvc_tracker_ctx); + return false; + } + + return true; +} + +static bool mdssvc_tracker_shutdown(struct mdssvc_ctx *mdssvc_ctx) +{ + samba_tevent_glib_glue_quit(mdssvc_tracker_ctx->glue); + TALLOC_FREE(mdssvc_tracker_ctx->glue); + + g_object_unref(mdssvc_tracker_ctx->gmain_ctx); + return true; +} + +static int mds_tracker_ctx_destructor(struct mds_tracker_ctx *ctx) +{ + /* + * Don't g_object_unref() the connection if there's an async request + * pending, it's used in the async callback and will be unreferenced + * there. + */ + if (ctx->async_pending) { + g_cancellable_cancel(ctx->gcancellable); + ctx->gcancellable = NULL; + return 0; + } + + if (ctx->tracker_con == NULL) { + return 0; + } + g_object_unref(ctx->tracker_con); + ctx->tracker_con = NULL; + + return 0; +} + +static bool mds_tracker_connect(struct mds_ctx *mds_ctx) +{ + struct mds_tracker_ctx *ctx = NULL; + bool ok; + + ok = mdssvc_tracker_prepare(); + if (!ok) { + return false; + } + + ctx = talloc_zero(mds_ctx, struct mds_tracker_ctx); + if (ctx == NULL) { + return false; + } + talloc_set_destructor(ctx, mds_tracker_ctx_destructor); + + ctx->mds_ctx = mds_ctx; + + ctx->gcancellable = g_cancellable_new(); + if (ctx->gcancellable == NULL) { + DBG_ERR("error from g_cancellable_new\n"); + TALLOC_FREE(ctx); + return false; + } + + tracker_sparql_connection_get_async(ctx->gcancellable, + tracker_con_cb, + ctx); + ctx->async_pending = true; + + mds_ctx->backend_private = ctx; + + return true; +} + +static int tq_destructor(struct sl_tracker_query *tq) +{ + /* + * Don't g_object_unref() the cursor if there's an async request + * pending, it's used in the async callback and will be unreferenced + * there. + */ + if (tq->async_pending) { + g_cancellable_cancel(tq->gcancellable); + tq->gcancellable = NULL; + return 0; + } + + if (tq->cursor == NULL) { + return 0; + } + g_object_unref(tq->cursor); + tq->cursor = NULL; + return 0; +} + +static bool mds_tracker_search_start(struct sl_query *slq) +{ + struct mds_tracker_ctx *tmds_ctx = talloc_get_type_abort( + slq->mds_ctx->backend_private, struct mds_tracker_ctx); + struct sl_tracker_query *tq = NULL; + char *escaped_scope = NULL; + bool ok; + + if (tmds_ctx->tracker_con == NULL) { + DBG_ERR("no connection to Tracker\n"); + return false; + } + + tq = talloc_zero(slq, struct sl_tracker_query); + if (tq == NULL) { + return false; + } + tq->slq = slq; + talloc_set_destructor(tq, tq_destructor); + + tq->gcancellable = g_cancellable_new(); + if (tq->gcancellable == NULL) { + DBG_ERR("g_cancellable_new() failed\n"); + goto error; + } + + escaped_scope = g_uri_escape_string( + slq->path_scope, + G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, + TRUE); + if (escaped_scope == NULL) { + goto error; + } + + tq->path_scope = talloc_strdup(tq, escaped_scope); + g_free(escaped_scope); + escaped_scope = NULL; + if (tq->path_scope == NULL) { + goto error; + } + + slq->backend_private = tq; + + ok = map_spotlight_to_sparql_query(slq); + if (!ok) { + /* + * Two cases: + * + * 1) the query string is "false", the parser returns + * an error for that. We're supposed to return -1 + * here. + * + * 2) the parsing really failed, in that case we're + * probably supposed to return -1 too, this needs + * verification though + */ + goto error; + } + + DBG_DEBUG("SPARQL query: \"%s\"\n", tq->sparql_query); + + tracker_sparql_connection_query_async(tmds_ctx->tracker_con, + tq->sparql_query, + tq->gcancellable, + tracker_query_cb, + tq); + tq->async_pending = true; + + slq->state = SLQ_STATE_RUNNING; + return true; +error: + g_object_unref(tq->gcancellable); + TALLOC_FREE(tq); + slq->backend_private = NULL; + return false; +} + +static bool mds_tracker_search_cont(struct sl_query *slq) +{ + struct sl_tracker_query *tq = talloc_get_type_abort( + slq->backend_private, struct sl_tracker_query); + + tracker_sparql_cursor_next_async(tq->cursor, + tq->gcancellable, + tracker_cursor_cb, + tq); + tq->async_pending = true; + + return true; +} + +struct mdssvc_backend mdsscv_backend_tracker = { + .init = mdssvc_tracker_init, + .shutdown = mdssvc_tracker_shutdown, + .connect = mds_tracker_connect, + .search_start = mds_tracker_search_start, + .search_cont = mds_tracker_search_cont, +}; diff --git a/source3/rpc_server/mdssvc/mdssvc_tracker.h b/source3/rpc_server/mdssvc/mdssvc_tracker.h new file mode 100644 index 00000000000..54a4a331c95 --- /dev/null +++ b/source3/rpc_server/mdssvc/mdssvc_tracker.h @@ -0,0 +1,62 @@ +/* + Unix SMB/CIFS implementation. + Main metadata server / Spotlight routines / Tracker backend + + Copyright (C) Ralph Boehme 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* allow building with --enable-developer */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#include <gio/gio.h> +#include <tracker-sparql.h> +#pragma GCC diagnostic pop + +/* Global */ +struct mdssvc_tracker_ctx { + struct mdssvc_ctx *mdssvc_ctx; + GMainContext *gmain_ctx; + struct tevent_glib_glue *glue; +}; + +/* Per tree connect state */ +struct mds_tracker_ctx { + struct mds_ctx *mds_ctx; + GCancellable *gcancellable; + bool async_pending; + TrackerSparqlConnection *tracker_con; +}; + +/* Per query */ +struct sl_tracker_query { + struct sl_query *slq; + const char *path_scope; + const char *sparql_query; + + /* + * Notes on the lifetime of cursor: we hold a reference on the object + * and have to call g_object_unref(cursor) at the right place. This is + * either done in the talloc destructor on a struct sl_tracker_query + * talloc object when there are no tracker glib async requests + * running. Or in the glib callback after cancelling the glib async + * request. + */ + TrackerSparqlCursor *cursor; + GCancellable *gcancellable; + bool async_pending; +}; + +extern struct mdssvc_backend mdsscv_backend_tracker; diff --git a/source3/rpc_server/mdssvc/sparql_parser.y b/source3/rpc_server/mdssvc/sparql_parser.y index c0ffca226f5..b059361670c 100644 --- a/source3/rpc_server/mdssvc/sparql_parser.y +++ b/source3/rpc_server/mdssvc/sparql_parser.y @@ -21,6 +21,7 @@ %{ #include "includes.h" #include "rpc_server/mdssvc/mdssvc.h" + #include "rpc_server/mdssvc/mdssvc_tracker.h" #include "rpc_server/mdssvc/sparql_parser.tab.h" #include "rpc_server/mdssvc/sparql_mapping.h" @@ -446,6 +447,8 @@ int mdsyywrap(void) **/ bool map_spotlight_to_sparql_query(struct sl_query *slq) { + struct sl_tracker_query *tq = talloc_get_type_abort( + slq->backend_private, struct sl_tracker_query); struct sparql_parser_state s = { .frame = talloc_stackframe(), .var = 'a', @@ -467,12 +470,12 @@ bool map_spotlight_to_sparql_query(struct sl_query *slq) return false; } - slq->sparql_query = talloc_asprintf(slq, + tq->sparql_query = talloc_asprintf(slq, "SELECT ?url WHERE { %s . ?obj nie:url ?url . " "FILTER(tracker:uri-is-descendant('file://%s/', ?url)) }", - s.result, slq->path_scope); + s.result, tq->path_scope); TALLOC_FREE(s.frame); - if (slq->sparql_query == NULL) { + if (tq->sparql_query == NULL) { return false; } diff --git a/source3/rpc_server/mdssvc/sparql_parser_test.c b/source3/rpc_server/mdssvc/sparql_parser_test.c index 92cf3963680..0a0f62523ab 100644 --- a/source3/rpc_server/mdssvc/sparql_parser_test.c +++ b/source3/rpc_server/mdssvc/sparql_parser_test.c @@ -1,6 +1,7 @@ #include "includes.h" #include "mdssvc.h" #include "rpc_server/mdssvc/sparql_parser.tab.h" +#include "rpc_server/mdssvc/mdssvc_tracker.h" /* * Examples: @@ -13,6 +14,7 @@ int main(int argc, char **argv) { + struct sl_tracker_query *tq = NULL; bool ok; struct sl_query *slq; @@ -30,8 +32,15 @@ int main(int argc, char **argv) slq->query_string = argv[1]; slq->path_scope = "/foo/bar"; + tq = talloc_zero(slq, struct sl_tracker_query); + if (tq == NULL) { + printf("talloc error\n"); + return 1; + } + slq->backend_private = tq; + ok = map_spotlight_to_sparql_query(slq); - printf("%s\n", ok ? slq->sparql_query : "*mapping failed*"); + printf("%s\n", ok ? tq->sparql_query : "*mapping failed*"); talloc_free(slq); return ok ? 0 : 1; diff --git a/source3/rpc_server/wscript_build b/source3/rpc_server/wscript_build index f6ae0add80f..7907cbb78b0 100644 --- a/source3/rpc_server/wscript_build +++ b/source3/rpc_server/wscript_build @@ -149,6 +149,9 @@ rpc_mdssvc_sources = ''' ../../librpc/gen_ndr/srv_mdssvc.c ''' +if bld.CONFIG_SET('HAVE_TRACKER'): + rpc_mdssvc_sources += ' mdssvc/mdssvc_tracker.c' + bld.SAMBA3_MODULE('rpc_mdssvc_module', subsystem='rpc', allow_undefined_symbols=True, |