Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Main metadata server / Spotlight routines / Tracker backend
4 :
5 : Copyright (C) Ralph Boehme 2019
6 :
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.
11 :
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.
16 :
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/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "lib/util/time_basic.h"
23 : #include "mdssvc.h"
24 : #include "mdssvc_tracker.h"
25 : #include "lib/tevent_glib_glue.h"
26 : #include "rpc_server/mdssvc/sparql_parser.tab.h"
27 :
28 : #undef DBGC_CLASS
29 : #define DBGC_CLASS DBGC_RPC_SRV
30 :
31 : static struct mdssvc_tracker_ctx *mdssvc_tracker_ctx;
32 :
33 : /************************************************
34 : * Tracker async callbacks
35 : ************************************************/
36 :
37 0 : static void tracker_con_cb(GObject *object,
38 : GAsyncResult *res,
39 : gpointer user_data)
40 : {
41 0 : struct mds_tracker_ctx *ctx = NULL;
42 0 : TrackerSparqlConnection *tracker_con = NULL;
43 0 : GError *error = NULL;
44 :
45 0 : tracker_con = tracker_sparql_connection_get_finish(res, &error);
46 0 : if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
47 : /*
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.
51 : */
52 0 : DBG_ERR("Tracker connection cancelled\n");
53 0 : g_error_free(error);
54 0 : return;
55 : }
56 : /*
57 : * Ok, we're not canclled, we can now safely use user_data.
58 : */
59 0 : ctx = talloc_get_type_abort(user_data, struct mds_tracker_ctx);
60 0 : ctx->async_pending = false;
61 : /*
62 : * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
63 : */
64 0 : if (error) {
65 0 : DBG_ERR("Could not connect to Tracker: %s\n", error->message);
66 0 : g_error_free(error);
67 0 : return;
68 : }
69 :
70 0 : ctx->tracker_con = tracker_con;
71 :
72 0 : DBG_DEBUG("connected to Tracker\n");
73 : }
74 :
75 : static void tracker_cursor_cb(GObject *object,
76 : GAsyncResult *res,
77 : gpointer user_data);
78 :
79 0 : static void tracker_query_cb(GObject *object,
80 : GAsyncResult *res,
81 : gpointer user_data)
82 : {
83 0 : struct sl_tracker_query *tq = NULL;
84 0 : struct sl_query *slq = NULL;
85 0 : TrackerSparqlConnection *conn = NULL;
86 0 : TrackerSparqlCursor *cursor = NULL;
87 0 : GError *error = NULL;
88 :
89 0 : conn = TRACKER_SPARQL_CONNECTION(object);
90 :
91 0 : cursor = tracker_sparql_connection_query_finish(conn, res, &error);
92 : /*
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.
96 : */
97 0 : if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
98 0 : DBG_ERR("Tracker query cancelled\n");
99 0 : if (cursor != NULL) {
100 0 : g_object_unref(cursor);
101 : }
102 0 : g_error_free(error);
103 0 : return;
104 : }
105 : /*
106 : * Ok, we're not cancelled, we can now safely use user_data.
107 : */
108 0 : tq = talloc_get_type_abort(user_data, struct sl_tracker_query);
109 0 : tq->async_pending = false;
110 0 : slq = tq->slq;
111 : /*
112 : * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
113 : */
114 0 : if (error) {
115 0 : DBG_ERR("Tracker query error: %s\n", error->message);
116 0 : g_error_free(error);
117 0 : slq->state = SLQ_STATE_ERROR;
118 0 : return;
119 : }
120 :
121 0 : tq->cursor = cursor;
122 0 : slq->state = SLQ_STATE_RESULTS;
123 :
124 0 : tracker_sparql_cursor_next_async(tq->cursor,
125 : tq->gcancellable,
126 : tracker_cursor_cb,
127 : tq);
128 0 : tq->async_pending = true;
129 : }
130 :
131 0 : static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri)
132 : {
133 0 : GFile *f = NULL;
134 0 : char *path = NULL;
135 0 : char *talloc_path = NULL;
136 :
137 0 : f = g_file_new_for_uri(uri);
138 0 : if (f == NULL) {
139 0 : return NULL;
140 : }
141 :
142 0 : path = g_file_get_path(f);
143 0 : g_object_unref(f);
144 :
145 0 : if (path == NULL) {
146 0 : return NULL;
147 : }
148 :
149 0 : talloc_path = talloc_strdup(mem_ctx, path);
150 0 : g_free(path);
151 0 : if (talloc_path == NULL) {
152 0 : return NULL;
153 : }
154 :
155 0 : return talloc_path;
156 : }
157 :
158 0 : static void tracker_cursor_cb(GObject *object,
159 : GAsyncResult *res,
160 : gpointer user_data)
161 : {
162 0 : TrackerSparqlCursor *cursor = NULL;
163 0 : struct sl_tracker_query *tq = NULL;
164 0 : struct sl_query *slq = NULL;
165 0 : const gchar *uri = NULL;
166 0 : GError *error = NULL;
167 0 : char *path = NULL;
168 : gboolean more_results;
169 : bool ok;
170 :
171 0 : cursor = TRACKER_SPARQL_CURSOR(object);
172 0 : more_results = tracker_sparql_cursor_next_finish(cursor,
173 : res,
174 : &error);
175 : /*
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.
179 : */
180 0 : if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
181 0 : g_error_free(error);
182 0 : g_object_unref(cursor);
183 0 : return;
184 : }
185 : /*
186 : * Ok, we're not canclled, we can now safely use user_data.
187 : */
188 0 : tq = talloc_get_type_abort(user_data, struct sl_tracker_query);
189 0 : tq->async_pending = false;
190 0 : slq = tq->slq;
191 : /*
192 : * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
193 : */
194 0 : if (error) {
195 0 : DBG_ERR("Tracker cursor: %s\n", error->message);
196 0 : g_error_free(error);
197 0 : slq->state = SLQ_STATE_ERROR;
198 0 : return;
199 : }
200 :
201 0 : SLQ_DEBUG(10, slq, "results");
202 :
203 0 : if (!more_results) {
204 0 : slq->state = SLQ_STATE_DONE;
205 :
206 0 : g_object_unref(tq->cursor);
207 0 : tq->cursor = NULL;
208 :
209 0 : g_object_unref(tq->gcancellable);
210 0 : tq->gcancellable = NULL;
211 0 : return;
212 : }
213 :
214 0 : uri = tracker_sparql_cursor_get_string(tq->cursor, 0, NULL);
215 0 : if (uri == NULL) {
216 0 : DBG_ERR("error fetching Tracker URI\n");
217 0 : slq->state = SLQ_STATE_ERROR;
218 0 : return;
219 : }
220 :
221 0 : path = tracker_to_unix_path(slq->query_results, uri);
222 0 : if (path == NULL) {
223 0 : DBG_ERR("error converting Tracker URI to path: %s\n", uri);
224 0 : slq->state = SLQ_STATE_ERROR;
225 0 : return;
226 : }
227 :
228 0 : ok = mds_add_result(slq, path);
229 0 : if (!ok) {
230 0 : DBG_ERR("error adding result for path: %s\n", uri);
231 0 : slq->state = SLQ_STATE_ERROR;
232 0 : return;
233 : }
234 :
235 0 : if (slq->query_results->num_results >= MAX_SL_RESULTS) {
236 0 : slq->state = SLQ_STATE_FULL;
237 0 : SLQ_DEBUG(10, slq, "full");
238 0 : return;
239 : }
240 :
241 0 : slq->state = SLQ_STATE_RESULTS;
242 0 : SLQ_DEBUG(10, slq, "cursor next");
243 :
244 0 : tracker_sparql_cursor_next_async(tq->cursor,
245 : tq->gcancellable,
246 : tracker_cursor_cb,
247 : tq);
248 0 : tq->async_pending = true;
249 : }
250 :
251 : /*
252 : * This gets called once, even if the backend is not configured by the user
253 : */
254 0 : static bool mdssvc_tracker_init(struct mdssvc_ctx *mdssvc_ctx)
255 : {
256 0 : if (mdssvc_tracker_ctx != NULL) {
257 0 : return true;
258 : }
259 :
260 : #if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36)
261 : g_type_init();
262 : #endif
263 :
264 0 : mdssvc_tracker_ctx = talloc_zero(mdssvc_ctx, struct mdssvc_tracker_ctx);
265 0 : if (mdssvc_tracker_ctx == NULL) {
266 0 : return false;
267 : }
268 0 : mdssvc_tracker_ctx->mdssvc_ctx = mdssvc_ctx;
269 :
270 0 : return true;
271 : }
272 :
273 : /*
274 : * This gets called per mdscmd_open / tcon. This runs initialisation code that
275 : * should only run if the tracker backend is actually used.
276 : */
277 0 : static bool mdssvc_tracker_prepare(void)
278 : {
279 0 : if (mdssvc_tracker_ctx->gmain_ctx != NULL) {
280 : /*
281 : * Assuming everything is setup if gmain_ctx is.
282 : */
283 0 : return true;
284 : }
285 :
286 0 : mdssvc_tracker_ctx->gmain_ctx = g_main_context_new();
287 0 : if (mdssvc_tracker_ctx->gmain_ctx == NULL) {
288 0 : DBG_ERR("error from g_main_context_new\n");
289 0 : TALLOC_FREE(mdssvc_tracker_ctx);
290 0 : return false;
291 : }
292 :
293 0 : mdssvc_tracker_ctx->glue = samba_tevent_glib_glue_create(
294 : mdssvc_tracker_ctx,
295 0 : mdssvc_tracker_ctx->mdssvc_ctx->ev_ctx,
296 0 : mdssvc_tracker_ctx->gmain_ctx);
297 0 : if (mdssvc_tracker_ctx->glue == NULL) {
298 0 : DBG_ERR("samba_tevent_glib_glue_create failed\n");
299 0 : g_object_unref(mdssvc_tracker_ctx->gmain_ctx);
300 0 : TALLOC_FREE(mdssvc_tracker_ctx);
301 0 : return false;
302 : }
303 :
304 0 : return true;
305 : }
306 :
307 0 : static bool mdssvc_tracker_shutdown(struct mdssvc_ctx *mdssvc_ctx)
308 : {
309 0 : samba_tevent_glib_glue_quit(mdssvc_tracker_ctx->glue);
310 0 : TALLOC_FREE(mdssvc_tracker_ctx->glue);
311 :
312 0 : g_object_unref(mdssvc_tracker_ctx->gmain_ctx);
313 0 : return true;
314 : }
315 :
316 0 : static int mds_tracker_ctx_destructor(struct mds_tracker_ctx *ctx)
317 : {
318 : /*
319 : * Don't g_object_unref() the connection if there's an async request
320 : * pending, it's used in the async callback and will be unreferenced
321 : * there.
322 : */
323 0 : if (ctx->async_pending) {
324 0 : g_cancellable_cancel(ctx->gcancellable);
325 0 : ctx->gcancellable = NULL;
326 0 : return 0;
327 : }
328 :
329 0 : if (ctx->tracker_con == NULL) {
330 0 : return 0;
331 : }
332 0 : g_object_unref(ctx->tracker_con);
333 0 : ctx->tracker_con = NULL;
334 :
335 0 : return 0;
336 : }
337 :
338 0 : static bool mds_tracker_connect(struct mds_ctx *mds_ctx)
339 : {
340 0 : struct mds_tracker_ctx *ctx = NULL;
341 : bool ok;
342 :
343 0 : ok = mdssvc_tracker_prepare();
344 0 : if (!ok) {
345 0 : return false;
346 : }
347 :
348 0 : ctx = talloc_zero(mds_ctx, struct mds_tracker_ctx);
349 0 : if (ctx == NULL) {
350 0 : return false;
351 : }
352 0 : talloc_set_destructor(ctx, mds_tracker_ctx_destructor);
353 :
354 0 : ctx->mds_ctx = mds_ctx;
355 :
356 0 : ctx->gcancellable = g_cancellable_new();
357 0 : if (ctx->gcancellable == NULL) {
358 0 : DBG_ERR("error from g_cancellable_new\n");
359 0 : TALLOC_FREE(ctx);
360 0 : return false;
361 : }
362 :
363 0 : tracker_sparql_connection_get_async(ctx->gcancellable,
364 : tracker_con_cb,
365 : ctx);
366 0 : ctx->async_pending = true;
367 :
368 0 : mds_ctx->backend_private = ctx;
369 :
370 0 : return true;
371 : }
372 :
373 0 : static int tq_destructor(struct sl_tracker_query *tq)
374 : {
375 : /*
376 : * Don't g_object_unref() the cursor if there's an async request
377 : * pending, it's used in the async callback and will be unreferenced
378 : * there.
379 : */
380 0 : if (tq->async_pending) {
381 0 : g_cancellable_cancel(tq->gcancellable);
382 0 : tq->gcancellable = NULL;
383 0 : return 0;
384 : }
385 :
386 0 : if (tq->cursor == NULL) {
387 0 : return 0;
388 : }
389 0 : g_object_unref(tq->cursor);
390 0 : tq->cursor = NULL;
391 0 : return 0;
392 : }
393 :
394 0 : static bool mds_tracker_search_start(struct sl_query *slq)
395 : {
396 0 : struct mds_tracker_ctx *tmds_ctx = talloc_get_type_abort(
397 : slq->mds_ctx->backend_private, struct mds_tracker_ctx);
398 0 : struct sl_tracker_query *tq = NULL;
399 0 : char *escaped_scope = NULL;
400 : bool ok;
401 :
402 0 : if (tmds_ctx->tracker_con == NULL) {
403 0 : DBG_ERR("no connection to Tracker\n");
404 0 : return false;
405 : }
406 :
407 0 : tq = talloc_zero(slq, struct sl_tracker_query);
408 0 : if (tq == NULL) {
409 0 : return false;
410 : }
411 0 : tq->slq = slq;
412 0 : talloc_set_destructor(tq, tq_destructor);
413 :
414 0 : tq->gcancellable = g_cancellable_new();
415 0 : if (tq->gcancellable == NULL) {
416 0 : DBG_ERR("g_cancellable_new() failed\n");
417 0 : goto error;
418 : }
419 :
420 0 : escaped_scope = g_uri_escape_string(
421 : slq->path_scope,
422 : G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
423 : TRUE);
424 0 : if (escaped_scope == NULL) {
425 0 : goto error;
426 : }
427 :
428 0 : tq->path_scope = talloc_strdup(tq, escaped_scope);
429 0 : g_free(escaped_scope);
430 0 : escaped_scope = NULL;
431 0 : if (tq->path_scope == NULL) {
432 0 : goto error;
433 : }
434 :
435 0 : slq->backend_private = tq;
436 :
437 0 : ok = map_spotlight_to_sparql_query(slq);
438 0 : if (!ok) {
439 : /*
440 : * Two cases:
441 : *
442 : * 1) the query string is "false", the parser returns
443 : * an error for that. We're supposed to return -1
444 : * here.
445 : *
446 : * 2) the parsing really failed, in that case we're
447 : * probably supposed to return -1 too, this needs
448 : * verification though
449 : */
450 0 : goto error;
451 : }
452 :
453 0 : DBG_DEBUG("SPARQL query: \"%s\"\n", tq->sparql_query);
454 :
455 0 : tracker_sparql_connection_query_async(tmds_ctx->tracker_con,
456 0 : tq->sparql_query,
457 : tq->gcancellable,
458 : tracker_query_cb,
459 : tq);
460 0 : tq->async_pending = true;
461 :
462 0 : slq->state = SLQ_STATE_RUNNING;
463 0 : return true;
464 0 : error:
465 0 : g_object_unref(tq->gcancellable);
466 0 : TALLOC_FREE(tq);
467 0 : slq->backend_private = NULL;
468 0 : return false;
469 : }
470 :
471 0 : static bool mds_tracker_search_cont(struct sl_query *slq)
472 : {
473 0 : struct sl_tracker_query *tq = talloc_get_type_abort(
474 : slq->backend_private, struct sl_tracker_query);
475 :
476 0 : tracker_sparql_cursor_next_async(tq->cursor,
477 : tq->gcancellable,
478 : tracker_cursor_cb,
479 : tq);
480 0 : tq->async_pending = true;
481 :
482 0 : return true;
483 : }
484 :
485 : struct mdssvc_backend mdsscv_backend_tracker = {
486 : .init = mdssvc_tracker_init,
487 : .shutdown = mdssvc_tracker_shutdown,
488 : .connect = mds_tracker_connect,
489 : .search_start = mds_tracker_search_start,
490 : .search_cont = mds_tracker_search_cont,
491 : };
|