s3-mdssvc: factor out Tracker backend logic
authorRalph Boehme <slow@samba.org>
Thu, 14 Mar 2019 06:38:20 +0000 (07:38 +0100)
committerJeremy Allison <jra@samba.org>
Thu, 8 Aug 2019 20:24:31 +0000 (20:24 +0000)
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>
source3/rpc_server/mdssvc/mdssvc.c
source3/rpc_server/mdssvc/mdssvc.h
source3/rpc_server/mdssvc/mdssvc_tracker.c [new file with mode: 0644]
source3/rpc_server/mdssvc/mdssvc_tracker.h [new file with mode: 0644]
source3/rpc_server/mdssvc/sparql_parser.y
source3/rpc_server/mdssvc/sparql_parser_test.c
source3/rpc_server/wscript_build

index 85da89d4ef0711d1e39c00837647a81e28848cbe..42eb960bb26872b90163286d5cf6b74b31b5f441 100644 (file)
@@ -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:
index 2260a38e6d21bed1c1e5581e9e00253657611384..1be07f4a40a56a949ca84f48e39fb374b41cbb46 100644 (file)
  */
 #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 (file)
index 0000000..fab8bd2
--- /dev/null
@@ -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 (file)
index 0000000..54a4a33
--- /dev/null
@@ -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;
index c0ffca226f555fd67ae61c7255a90cd1e7cc71eb..b059361670c18039a7c423c1c6d5681fe7e96c85 100644 (file)
@@ -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;
        }
 
index 92cf396368093a79cfacd4f0d79395bc84806d4a..0a0f62523ab67330ccad7831207c152b38ea5727 100644 (file)
@@ -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;
index f6ae0add80f1714386a1ddb2e04001399ff85818..7907cbb78b048cf349e75acd2ff55f155aa3dfbb 100644 (file)
@@ -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,