s3:utils: Fix 'Usage:' for 'net ads enctypes'
[samba4-gss.git] / source3 / rpc_server / mdssvc / mdssvc_tracker.c
blob54f391e6c345c9df307c8583336b544379d84ead
1 /*
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/>.
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"
28 #undef DBGC_CLASS
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,
38 GAsyncResult *res,
39 gpointer user_data)
41 struct mds_tracker_ctx *ctx = NULL;
42 TrackerSparqlConnection *tracker_con = NULL;
43 GError *error = 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");
53 g_error_free(error);
54 return;
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.
64 if (error) {
65 DBG_ERR("Could not connect to Tracker: %s\n", error->message);
66 g_error_free(error);
67 return;
70 ctx->tracker_con = tracker_con;
72 DBG_DEBUG("connected to Tracker\n");
75 static void tracker_cursor_cb(GObject *object,
76 GAsyncResult *res,
77 gpointer user_data);
79 static void tracker_query_cb(GObject *object,
80 GAsyncResult *res,
81 gpointer user_data)
83 struct sl_tracker_query *tq = NULL;
84 struct sl_query *slq = NULL;
85 TrackerSparqlConnection *conn = NULL;
86 TrackerSparqlCursor *cursor = NULL;
87 GError *error = 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");
99 if (cursor != NULL) {
100 g_object_unref(cursor);
102 g_error_free(error);
103 return;
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;
110 slq = tq->slq;
112 * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
114 if (error) {
115 DBG_ERR("Tracker query error: %s\n", error->message);
116 g_error_free(error);
117 slq->state = SLQ_STATE_ERROR;
118 return;
121 tq->cursor = cursor;
122 slq->state = SLQ_STATE_RESULTS;
124 tracker_sparql_cursor_next_async(tq->cursor,
125 tq->gcancellable,
126 tracker_cursor_cb,
127 tq);
128 tq->async_pending = true;
131 static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri)
133 GFile *f = NULL;
134 char *path = NULL;
135 char *talloc_path = NULL;
137 f = g_file_new_for_uri(uri);
138 if (f == NULL) {
139 return NULL;
142 path = g_file_get_path(f);
143 g_object_unref(f);
145 if (path == NULL) {
146 return NULL;
149 talloc_path = talloc_strdup(mem_ctx, path);
150 g_free(path);
151 if (talloc_path == NULL) {
152 return NULL;
155 return talloc_path;
158 static void tracker_cursor_cb(GObject *object,
159 GAsyncResult *res,
160 gpointer user_data)
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;
167 char *path = NULL;
168 gboolean more_results;
169 bool ok;
171 cursor = TRACKER_SPARQL_CURSOR(object);
172 more_results = tracker_sparql_cursor_next_finish(cursor,
173 res,
174 &error);
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)) {
181 g_error_free(error);
182 g_object_unref(cursor);
183 return;
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;
190 slq = tq->slq;
192 * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
194 if (error) {
195 DBG_ERR("Tracker cursor: %s\n", error->message);
196 g_error_free(error);
197 slq->state = SLQ_STATE_ERROR;
198 return;
201 SLQ_DEBUG(10, slq, "results");
203 if (!more_results) {
204 slq->state = SLQ_STATE_DONE;
206 g_object_unref(tq->cursor);
207 tq->cursor = NULL;
209 g_object_unref(tq->gcancellable);
210 tq->gcancellable = NULL;
211 return;
214 uri = tracker_sparql_cursor_get_string(tq->cursor, 0, NULL);
215 if (uri == NULL) {
216 DBG_ERR("error fetching Tracker URI\n");
217 slq->state = SLQ_STATE_ERROR;
218 return;
221 path = tracker_to_unix_path(slq->query_results, uri);
222 if (path == NULL) {
223 DBG_ERR("error converting Tracker URI to path: %s\n", uri);
224 slq->state = SLQ_STATE_ERROR;
225 return;
228 ok = mds_add_result(slq, path);
229 if (!ok) {
230 DBG_ERR("error adding result for path: %s\n", uri);
231 slq->state = SLQ_STATE_ERROR;
232 return;
235 if (slq->query_results->num_results >= MAX_SL_RESULTS) {
236 slq->state = SLQ_STATE_FULL;
237 SLQ_DEBUG(10, slq, "full");
238 return;
241 slq->state = SLQ_STATE_RESULTS;
242 SLQ_DEBUG(10, slq, "cursor next");
244 tracker_sparql_cursor_next_async(tq->cursor,
245 tq->gcancellable,
246 tracker_cursor_cb,
247 tq);
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) {
257 return true;
260 #if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36)
261 g_type_init();
262 #endif
264 mdssvc_tracker_ctx = talloc_zero(mdssvc_ctx, struct mdssvc_tracker_ctx);
265 if (mdssvc_tracker_ctx == NULL) {
266 return false;
268 mdssvc_tracker_ctx->mdssvc_ctx = mdssvc_ctx;
270 return true;
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.
283 return true;
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");
289 return false;
292 mdssvc_tracker_ctx->glue = samba_tevent_glib_glue_create(
293 mdssvc_tracker_ctx,
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;
300 return false;
303 return true;
306 static bool mdssvc_tracker_shutdown(struct mdssvc_ctx *mdssvc_ctx)
308 if (mdssvc_tracker_ctx == NULL) {
309 return true;
312 if (mdssvc_tracker_ctx->gmain_ctx == NULL) {
313 return true;
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;
321 return true;
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
329 * there.
331 if (ctx->async_pending) {
332 g_cancellable_cancel(ctx->gcancellable);
333 ctx->gcancellable = NULL;
334 return 0;
337 if (ctx->tracker_con == NULL) {
338 return 0;
340 g_object_unref(ctx->tracker_con);
341 ctx->tracker_con = NULL;
343 return 0;
346 static bool mds_tracker_connect(struct mds_ctx *mds_ctx)
348 struct mds_tracker_ctx *ctx = NULL;
349 bool ok;
351 ok = mdssvc_tracker_prepare();
352 if (!ok) {
353 return false;
356 ctx = talloc_zero(mds_ctx, struct mds_tracker_ctx);
357 if (ctx == NULL) {
358 return false;
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");
367 TALLOC_FREE(ctx);
368 return false;
371 tracker_sparql_connection_get_async(ctx->gcancellable,
372 tracker_con_cb,
373 ctx);
374 ctx->async_pending = true;
376 mds_ctx->backend_private = ctx;
378 return true;
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
386 * there.
388 if (tq->async_pending) {
389 g_cancellable_cancel(tq->gcancellable);
390 tq->gcancellable = NULL;
391 return 0;
394 if (tq->cursor == NULL) {
395 return 0;
397 g_object_unref(tq->cursor);
398 tq->cursor = NULL;
399 return 0;
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;
408 bool ok;
410 if (tmds_ctx->tracker_con == NULL) {
411 DBG_ERR("no connection to Tracker\n");
412 return false;
415 tq = talloc_zero(slq, struct sl_tracker_query);
416 if (tq == NULL) {
417 return false;
419 tq->slq = slq;
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");
425 goto error;
428 escaped_scope = g_uri_escape_string(
429 slq->path_scope,
430 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
431 TRUE);
432 if (escaped_scope == NULL) {
433 goto error;
436 tq->path_scope = talloc_strdup(tq, escaped_scope);
437 g_free(escaped_scope);
438 escaped_scope = NULL;
439 if (tq->path_scope == NULL) {
440 goto error;
443 slq->backend_private = tq;
445 ok = map_spotlight_to_sparql_query(slq);
446 if (!ok) {
448 * Two cases:
450 * 1) the query string is "false", the parser returns
451 * an error for that. We're supposed to return -1
452 * here.
454 * 2) the parsing really failed, in that case we're
455 * probably supposed to return -1 too, this needs
456 * verification though
458 goto error;
461 DBG_DEBUG("SPARQL query: \"%s\"\n", tq->sparql_query);
463 tracker_sparql_connection_query_async(tmds_ctx->tracker_con,
464 tq->sparql_query,
465 tq->gcancellable,
466 tracker_query_cb,
467 tq);
468 tq->async_pending = true;
470 slq->state = SLQ_STATE_RUNNING;
471 return true;
472 error:
473 g_object_unref(tq->gcancellable);
474 TALLOC_FREE(tq);
475 slq->backend_private = NULL;
476 return false;
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,
485 tq->gcancellable,
486 tracker_cursor_cb,
487 tq);
488 tq->async_pending = true;
490 return 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,