1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source3/rpc_server/mdssvc/mdssvc_tracker.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

500 lines
12 KiB
C
Raw Normal View History

/*
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 cancelled, 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 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 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");
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);
mdssvc_tracker_ctx->gmain_ctx = NULL;
return false;
}
return true;
}
static bool mdssvc_tracker_shutdown(struct mdssvc_ctx *mdssvc_ctx)
{
if (mdssvc_tracker_ctx == NULL) {
return true;
}
if (mdssvc_tracker_ctx->gmain_ctx == NULL) {
return true;
}
samba_tevent_glib_glue_quit(mdssvc_tracker_ctx->glue);
TALLOC_FREE(mdssvc_tracker_ctx->glue);
g_object_unref(mdssvc_tracker_ctx->gmain_ctx);
mdssvc_tracker_ctx->gmain_ctx = NULL;
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,
};