summaryrefslogtreecommitdiff
path: root/source3/rpc_server
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2019-03-14 07:38:20 +0100
committerJeremy Allison <jra@samba.org>2019-08-08 20:24:31 +0000
commit1ef96e09071458322845b4323fa59cf4e7475783 (patch)
treec0f2f557612e8f51c67fe0ab567bbcf2a5852b1c /source3/rpc_server
parentf80f8c5f99f6b5949d14ee62261a7dd448e9b0fe (diff)
downloadsamba-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.c357
-rw-r--r--source3/rpc_server/mdssvc/mdssvc.h27
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_tracker.c491
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_tracker.h62
-rw-r--r--source3/rpc_server/mdssvc/sparql_parser.y9
-rw-r--r--source3/rpc_server/mdssvc/sparql_parser_test.c11
-rw-r--r--source3/rpc_server/wscript_build3
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,