2 Unix SMB/CIFS implementation.
3 Main metadata server / Spotlight routines / Tracker backend
5 Copyright (C) Ralph Boehme 2019
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "lib/util/time_basic.h"
24 #include "mdssvc_tracker.h"
25 #include "lib/tevent_glib_glue.h"
26 #include "rpc_server/mdssvc/sparql_parser.tab.h"
29 #define DBGC_CLASS DBGC_RPC_SRV
31 static struct mdssvc_tracker_ctx
*mdssvc_tracker_ctx
;
33 /************************************************
34 * Tracker async callbacks
35 ************************************************/
37 static void tracker_con_cb(GObject
*object
,
41 struct mds_tracker_ctx
*ctx
= NULL
;
42 TrackerSparqlConnection
*tracker_con
= NULL
;
45 tracker_con
= tracker_sparql_connection_get_finish(res
, &error
);
46 if (error
&& g_error_matches(error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
48 * If the async request was cancelled, user_data will already be
49 * talloc_free'd, so we must be carefully checking for
50 * G_IO_ERROR_CANCELLED before using user_data.
52 DBG_ERR("Tracker connection cancelled\n");
57 * Ok, we're not cancelled, we can now safely use user_data.
59 ctx
= talloc_get_type_abort(user_data
, struct mds_tracker_ctx
);
60 ctx
->async_pending
= false;
62 * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
65 DBG_ERR("Could not connect to Tracker: %s\n", error
->message
);
70 ctx
->tracker_con
= tracker_con
;
72 DBG_DEBUG("connected to Tracker\n");
75 static void tracker_cursor_cb(GObject
*object
,
79 static void tracker_query_cb(GObject
*object
,
83 struct sl_tracker_query
*tq
= NULL
;
84 struct sl_query
*slq
= NULL
;
85 TrackerSparqlConnection
*conn
= NULL
;
86 TrackerSparqlCursor
*cursor
= NULL
;
89 conn
= TRACKER_SPARQL_CONNECTION(object
);
91 cursor
= tracker_sparql_connection_query_finish(conn
, res
, &error
);
93 * If the async request was cancelled, user_data will already be
94 * talloc_free'd, so we must be carefully checking for
95 * G_IO_ERROR_CANCELLED before using user_data.
97 if (error
&& g_error_matches(error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
98 DBG_ERR("Tracker query cancelled\n");
100 g_object_unref(cursor
);
106 * Ok, we're not cancelled, we can now safely use user_data.
108 tq
= talloc_get_type_abort(user_data
, struct sl_tracker_query
);
109 tq
->async_pending
= false;
112 * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
115 DBG_ERR("Tracker query error: %s\n", error
->message
);
117 slq
->state
= SLQ_STATE_ERROR
;
122 slq
->state
= SLQ_STATE_RESULTS
;
124 tracker_sparql_cursor_next_async(tq
->cursor
,
128 tq
->async_pending
= true;
131 static char *tracker_to_unix_path(TALLOC_CTX
*mem_ctx
, const char *uri
)
135 char *talloc_path
= NULL
;
137 f
= g_file_new_for_uri(uri
);
142 path
= g_file_get_path(f
);
149 talloc_path
= talloc_strdup(mem_ctx
, path
);
151 if (talloc_path
== NULL
) {
158 static void tracker_cursor_cb(GObject
*object
,
162 TrackerSparqlCursor
*cursor
= NULL
;
163 struct sl_tracker_query
*tq
= NULL
;
164 struct sl_query
*slq
= NULL
;
165 const gchar
*uri
= NULL
;
166 GError
*error
= NULL
;
168 gboolean more_results
;
171 cursor
= TRACKER_SPARQL_CURSOR(object
);
172 more_results
= tracker_sparql_cursor_next_finish(cursor
,
176 * If the async request was cancelled, user_data will already be
177 * talloc_free'd, so we must be carefully checking for
178 * G_IO_ERROR_CANCELLED before using user_data.
180 if (error
&& g_error_matches(error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
182 g_object_unref(cursor
);
186 * Ok, we're not cancelled, we can now safely use user_data.
188 tq
= talloc_get_type_abort(user_data
, struct sl_tracker_query
);
189 tq
->async_pending
= false;
192 * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
195 DBG_ERR("Tracker cursor: %s\n", error
->message
);
197 slq
->state
= SLQ_STATE_ERROR
;
201 SLQ_DEBUG(10, slq
, "results");
204 slq
->state
= SLQ_STATE_DONE
;
206 g_object_unref(tq
->cursor
);
209 g_object_unref(tq
->gcancellable
);
210 tq
->gcancellable
= NULL
;
214 uri
= tracker_sparql_cursor_get_string(tq
->cursor
, 0, NULL
);
216 DBG_ERR("error fetching Tracker URI\n");
217 slq
->state
= SLQ_STATE_ERROR
;
221 path
= tracker_to_unix_path(slq
->query_results
, uri
);
223 DBG_ERR("error converting Tracker URI to path: %s\n", uri
);
224 slq
->state
= SLQ_STATE_ERROR
;
228 ok
= mds_add_result(slq
, path
);
230 DBG_ERR("error adding result for path: %s\n", uri
);
231 slq
->state
= SLQ_STATE_ERROR
;
235 if (slq
->query_results
->num_results
>= MAX_SL_RESULTS
) {
236 slq
->state
= SLQ_STATE_FULL
;
237 SLQ_DEBUG(10, slq
, "full");
241 slq
->state
= SLQ_STATE_RESULTS
;
242 SLQ_DEBUG(10, slq
, "cursor next");
244 tracker_sparql_cursor_next_async(tq
->cursor
,
248 tq
->async_pending
= true;
252 * This gets called once, even if the backend is not configured by the user
254 static bool mdssvc_tracker_init(struct mdssvc_ctx
*mdssvc_ctx
)
256 if (mdssvc_tracker_ctx
!= NULL
) {
260 #if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36)
264 mdssvc_tracker_ctx
= talloc_zero(mdssvc_ctx
, struct mdssvc_tracker_ctx
);
265 if (mdssvc_tracker_ctx
== NULL
) {
268 mdssvc_tracker_ctx
->mdssvc_ctx
= mdssvc_ctx
;
274 * This gets called per mdscmd_open / tcon. This runs initialisation code that
275 * should only run if the tracker backend is actually used.
277 static bool mdssvc_tracker_prepare(void)
279 if (mdssvc_tracker_ctx
->gmain_ctx
!= NULL
) {
281 * Assuming everything is setup if gmain_ctx is.
286 mdssvc_tracker_ctx
->gmain_ctx
= g_main_context_new();
287 if (mdssvc_tracker_ctx
->gmain_ctx
== NULL
) {
288 DBG_ERR("error from g_main_context_new\n");
292 mdssvc_tracker_ctx
->glue
= samba_tevent_glib_glue_create(
294 mdssvc_tracker_ctx
->mdssvc_ctx
->ev_ctx
,
295 mdssvc_tracker_ctx
->gmain_ctx
);
296 if (mdssvc_tracker_ctx
->glue
== NULL
) {
297 DBG_ERR("samba_tevent_glib_glue_create failed\n");
298 g_object_unref(mdssvc_tracker_ctx
->gmain_ctx
);
299 mdssvc_tracker_ctx
->gmain_ctx
= NULL
;
306 static bool mdssvc_tracker_shutdown(struct mdssvc_ctx
*mdssvc_ctx
)
308 if (mdssvc_tracker_ctx
== NULL
) {
312 if (mdssvc_tracker_ctx
->gmain_ctx
== NULL
) {
316 samba_tevent_glib_glue_quit(mdssvc_tracker_ctx
->glue
);
317 TALLOC_FREE(mdssvc_tracker_ctx
->glue
);
319 g_object_unref(mdssvc_tracker_ctx
->gmain_ctx
);
320 mdssvc_tracker_ctx
->gmain_ctx
= NULL
;
324 static int mds_tracker_ctx_destructor(struct mds_tracker_ctx
*ctx
)
327 * Don't g_object_unref() the connection if there's an async request
328 * pending, it's used in the async callback and will be unreferenced
331 if (ctx
->async_pending
) {
332 g_cancellable_cancel(ctx
->gcancellable
);
333 ctx
->gcancellable
= NULL
;
337 if (ctx
->tracker_con
== NULL
) {
340 g_object_unref(ctx
->tracker_con
);
341 ctx
->tracker_con
= NULL
;
346 static bool mds_tracker_connect(struct mds_ctx
*mds_ctx
)
348 struct mds_tracker_ctx
*ctx
= NULL
;
351 ok
= mdssvc_tracker_prepare();
356 ctx
= talloc_zero(mds_ctx
, struct mds_tracker_ctx
);
360 talloc_set_destructor(ctx
, mds_tracker_ctx_destructor
);
362 ctx
->mds_ctx
= mds_ctx
;
364 ctx
->gcancellable
= g_cancellable_new();
365 if (ctx
->gcancellable
== NULL
) {
366 DBG_ERR("error from g_cancellable_new\n");
371 tracker_sparql_connection_get_async(ctx
->gcancellable
,
374 ctx
->async_pending
= true;
376 mds_ctx
->backend_private
= ctx
;
381 static int tq_destructor(struct sl_tracker_query
*tq
)
384 * Don't g_object_unref() the cursor if there's an async request
385 * pending, it's used in the async callback and will be unreferenced
388 if (tq
->async_pending
) {
389 g_cancellable_cancel(tq
->gcancellable
);
390 tq
->gcancellable
= NULL
;
394 if (tq
->cursor
== NULL
) {
397 g_object_unref(tq
->cursor
);
402 static bool mds_tracker_search_start(struct sl_query
*slq
)
404 struct mds_tracker_ctx
*tmds_ctx
= talloc_get_type_abort(
405 slq
->mds_ctx
->backend_private
, struct mds_tracker_ctx
);
406 struct sl_tracker_query
*tq
= NULL
;
407 char *escaped_scope
= NULL
;
410 if (tmds_ctx
->tracker_con
== NULL
) {
411 DBG_ERR("no connection to Tracker\n");
415 tq
= talloc_zero(slq
, struct sl_tracker_query
);
420 talloc_set_destructor(tq
, tq_destructor
);
422 tq
->gcancellable
= g_cancellable_new();
423 if (tq
->gcancellable
== NULL
) {
424 DBG_ERR("g_cancellable_new() failed\n");
428 escaped_scope
= g_uri_escape_string(
430 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
,
432 if (escaped_scope
== NULL
) {
436 tq
->path_scope
= talloc_strdup(tq
, escaped_scope
);
437 g_free(escaped_scope
);
438 escaped_scope
= NULL
;
439 if (tq
->path_scope
== NULL
) {
443 slq
->backend_private
= tq
;
445 ok
= map_spotlight_to_sparql_query(slq
);
450 * 1) the query string is "false", the parser returns
451 * an error for that. We're supposed to return -1
454 * 2) the parsing really failed, in that case we're
455 * probably supposed to return -1 too, this needs
456 * verification though
461 DBG_DEBUG("SPARQL query: \"%s\"\n", tq
->sparql_query
);
463 tracker_sparql_connection_query_async(tmds_ctx
->tracker_con
,
468 tq
->async_pending
= true;
470 slq
->state
= SLQ_STATE_RUNNING
;
473 g_object_unref(tq
->gcancellable
);
475 slq
->backend_private
= NULL
;
479 static bool mds_tracker_search_cont(struct sl_query
*slq
)
481 struct sl_tracker_query
*tq
= talloc_get_type_abort(
482 slq
->backend_private
, struct sl_tracker_query
);
484 tracker_sparql_cursor_next_async(tq
->cursor
,
488 tq
->async_pending
= true;
493 struct mdssvc_backend mdsscv_backend_tracker
= {
494 .init
= mdssvc_tracker_init
,
495 .shutdown
= mdssvc_tracker_shutdown
,
496 .connect
= mds_tracker_connect
,
497 .search_start
= mds_tracker_search_start
,
498 .search_cont
= mds_tracker_search_cont
,