#include "lib/dbwrap/dbwrap_rbt.h"
#include "libcli/security/dom_sid.h"
#include "mdssvc.h"
-#include "sparql_parser.h"
+#include "rpc_server/mdssvc/sparql_parser.tab.h"
+#include "lib/tevent_glib_glue.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV
-#define SLQ_DEBUG(lvl, _slq, state) do { if (CHECK_DEBUGLVL(lvl)) { \
- const struct sl_query *__slq = _slq; \
- struct timeval_buf start_buf; \
- const char *start; \
- struct timeval_buf last_used_buf; \
- const char *last_used; \
- struct timeval_buf expire_buf; \
- const char *expire; \
- start = timeval_str_buf(&__slq->start_time, false, \
- true, &start_buf); \
- last_used = timeval_str_buf(&__slq->last_used, false, \
- true, &last_used_buf); \
- expire = timeval_str_buf(&__slq->expire_time, false, \
- true, &expire_buf); \
- DEBUG(lvl,("%s slq[0x%jx,0x%jx], start: %s, last_used: %s, " \
- "expires: %s, query: '%s'\n", state, \
- (uintmax_t)__slq->ctx1, (uintmax_t)__slq->ctx2, \
- start, last_used, expire, __slq->query_string)); \
-}} while(0)
-
struct slrpc_cmd {
const char *name;
bool (*function)(struct mds_ctx *mds_ctx,
struct sl_query *slq;
};
+/*
+ * This is a static global because we may be called multiple times and
+ * we only want one mdssvc_ctx per connection to Tracker.
+ *
+ * The client will bind multiple times to the mdssvc RPC service, once
+ * for every tree connect.
+ */
+static struct mdssvc_ctx *mdssvc_ctx = NULL;
+
/*
* If these functions return an error, they hit something like a non
* recoverable talloc error. Most errors are dealt with by returning
- * an errror code in the Spotlight RPC reply.
+ * an error code in the Spotlight RPC reply.
*/
static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
for (i = 0; i < metacount; i++) {
attribute = dalloc_get_object(reqinfo, i);
+ if (attribute == NULL) {
+ return false;
+ }
if (strcmp(attribute, "kMDItemDisplayName") == 0
|| strcmp(attribute, "kMDItemFSName") == 0) {
p = strrchr(path, '/');
}
DEBUG(10, ("connected to Tracker\n"));
- g_main_loop_quit(mds_ctx->gmainloop);
}
static void tracker_cursor_cb_destroy_done(struct tevent_req *subreq);
* we return.
*/
SLQ_DEBUG(10, slq, "closed");
- g_main_loop_quit(slq->mds_ctx->gmainloop);
- req = slq_destroy_send(slq, server_event_context(), &slq);
+ req = slq_destroy_send(slq, global_event_context(), &slq);
if (req == NULL) {
slq->state = SLQ_STATE_ERROR;
return;
DEBUG(1, ("Tracker cursor: %s\n", error->message));
g_error_free(error);
slq->state = SLQ_STATE_ERROR;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
if (!more_results) {
slq->state = SLQ_STATE_DONE;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
if (uri == NULL) {
DEBUG(1, ("error fetching Tracker URI\n"));
slq->state = SLQ_STATE_ERROR;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
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;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
+ /*
+ * We're in a tevent callback which means in the case of
+ * running as external RPC service we're running as root and
+ * not as the user.
+ */
+ if (!become_authenticated_pipe_user(slq->mds_ctx->pipe_session_info)) {
+ DBG_ERR("can't become authenticated user: %d\n",
+ slq->mds_ctx->uid);
+ smb_panic("can't become authenticated user");
+ }
+
if (geteuid() != slq->mds_ctx->uid) {
- DEBUG(0, ("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid));
+ DBG_ERR("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid);
smb_panic("uid mismatch");
}
+ /*
+ * We've changed identity to the authenticated pipe user, so
+ * any function exit below must ensure we switch back
+ */
+
result = sys_stat(path, &sb, false);
if (result != 0) {
+ unbecome_authenticated_pipe_user();
goto done;
}
result = access(path, R_OK);
if (result != 0) {
+ unbecome_authenticated_pipe_user();
goto done;
}
+ unbecome_authenticated_pipe_user();
+
ino64 = sb.st_ex_ino;
if (slq->cnids) {
/*
* set of IDs. Note that we're faking CNIDs by using
* filesystem inode numbers here
*/
- ok = bsearch(&ino64, slq->cnids, slq->cnids_num,
- sizeof(uint64_t), cnid_comp_fn);
+ ok = bsearch(&ino64,
+ slq->cnids,
+ slq->cnids_num,
+ sizeof(uint64_t),
+ cnid_comp_fn);
if (!ok) {
goto done;
}
* we return as part of the result set of a query
*/
result = dalloc_add_copy(slq->query_results->cnids->ca_cnids,
- &ino64, uint64_t);
+ &ino64,
+ uint64_t);
if (result != 0) {
- DEBUG(1, ("dalloc error\n"));
+ DBG_ERR("dalloc error\n");
slq->state = SLQ_STATE_ERROR;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
- ok = add_filemeta(slq->reqinfo, slq->query_results->fm_array,
- path, &sb);
+ ok = add_filemeta(slq->reqinfo,
+ slq->query_results->fm_array,
+ path,
+ &sb);
if (!ok) {
- DEBUG(1, ("add_filemeta error\n"));
+ DBG_ERR("add_filemeta error\n");
slq->state = SLQ_STATE_ERROR;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
if (!ok) {
DEBUG(1, ("inode_map_add error\n"));
slq->state = SLQ_STATE_ERROR;
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
if (slq->query_results->num_results >= MAX_SL_RESULTS) {
slq->state = SLQ_STATE_FULL;
SLQ_DEBUG(10, slq, "full");
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
slq->state = SLQ_STATE_ERROR;
DEBUG(1, ("Tracker query error: %s\n", error->message));
g_error_free(error);
- g_main_loop_quit(slq->mds_ctx->gmainloop);
return;
}
if (slq->state == SLQ_STATE_DONE) {
SLQ_DEBUG(10, slq, "done");
- g_main_loop_quit(slq->mds_ctx->gmainloop);
talloc_free(slq);
return;
}
struct sl_query *slq = NULL;
int result;
char *querystring;
+ char *scope = NULL;
+ char *escaped_scope = NULL;
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
slq->last_used = timeval_current();
slq->start_time = slq->last_used;
slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
- slq->te = tevent_add_timer(server_event_context(), slq,
+ slq->te = tevent_add_timer(global_event_context(), slq,
slq->expire_time, slq_close_timer, slq);
if (slq->te == NULL) {
DEBUG(1, ("tevent_add_timer failed\n"));
goto error;
}
- slq->path_scope = dalloc_get(path_scope, "char *", 0);
- if (slq->path_scope == NULL) {
+ scope = dalloc_get(path_scope, "char *", 0);
+ if (scope == NULL) {
goto error;
}
- slq->path_scope = talloc_strdup(slq, slq->path_scope);
- if (slq->path_scope == NULL) {
+ 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);
+ if (slq->path_scope == NULL) {
+ goto error;
+ }
reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
"DALLOC_CTX", 1, "kMDAttributeArray");
DEBUG(10, ("SPARQL query: \"%s\"\n", slq->sparql_query));
- g_main_context_push_thread_default(mds_ctx->gcontext);
tracker_sparql_connection_query_async(mds_ctx->tracker_con,
slq->sparql_query,
slq->gcancellable,
tracker_query_cb,
slq);
- g_main_context_pop_thread_default(mds_ctx->gcontext);
slq->state = SLQ_STATE_RUNNING;
sl_result = 0;
TALLOC_FREE(slq->te);
slq->last_used = timeval_current();
slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
- slq->te = tevent_add_timer(server_event_context(), slq,
+ slq->te = tevent_add_timer(global_event_context(), slq,
slq->expire_time, slq_close_timer, slq);
if (slq->te == NULL) {
DEBUG(1, ("tevent_add_timer failed\n"));
}
if (slq->state == SLQ_STATE_FULL) {
slq->state = SLQ_STATE_RESULTS;
- g_main_context_push_thread_default(mds_ctx->gcontext);
tracker_sparql_cursor_next_async(
slq->tracker_cursor,
slq->gcancellable,
tracker_cursor_cb,
slq);
- g_main_context_pop_thread_default(mds_ctx->gcontext);
}
break;
goto error;
}
/* For some reason the list of results always starts with a nil entry */
- dalloc_add_copy(fm_array, &nil, sl_nil_t);
+ result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
+ if (result == -1) {
+ goto error;
+ }
reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
if (reqinfo == NULL) {
goto error;
}
- ok = add_filemeta(reqinfo, fm_array,
- elem ? elem->path : NULL,
- elem ? &sb : NULL);
+ ok = add_filemeta(reqinfo, fm_array, elem->path, &sb);
if (!ok) {
goto error;
}
return true;
}
-/**
- * Init callbacks at startup, nothing to do here really
- **/
-bool mds_init(struct messaging_context *msg_ctx)
+static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
{
- return true;
-}
-
-bool mds_shutdown(void)
-{
- return true;
-}
-
-static gboolean gmainloop_timer(gpointer user_data)
-{
- struct mds_ctx *ctx = talloc_get_type_abort(user_data, struct mds_ctx);
-
- DEBUG(10,("%s\n", __func__));
- g_main_loop_quit(ctx->gmainloop);
-
- return G_SOURCE_CONTINUE;
-}
-
-/**
- * Initialise a context per share handle
- **/
-struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
- const struct auth_session_info *session_info,
- const char *path)
-{
- struct mds_ctx *mds_ctx;
+ if (mdssvc_ctx != NULL) {
+ return mdssvc_ctx;
+ }
- mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
- if (mds_ctx == NULL) {
+ mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
+ if (mdssvc_ctx == NULL) {
return NULL;
}
- talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
- mds_ctx->spath = talloc_strdup(mds_ctx, path);
- if (mds_ctx->spath == NULL) {
- goto error;
- }
+ mdssvc_ctx->ev_ctx = ev;
- if (session_info->security_token->num_sids < 1) {
- goto error;
+ 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;
}
- sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
- mds_ctx->uid = session_info->unix_token->uid;
- mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
- if (mds_ctx->ino_path_map == NULL) {
- DEBUG(1,("open inode map db failed\n"));
- goto error;
+ 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");
+ return NULL;
}
- mds_ctx->gcontext = g_main_context_new();
- if (mds_ctx->gcontext == NULL) {
- DEBUG(1,("error from g_main_context_new\n"));
- goto error;
- }
+ return mdssvc_ctx;
+}
- mds_ctx->gmainloop = g_main_loop_new(mds_ctx->gcontext, false);
- if (mds_ctx->gmainloop == NULL) {
- DEBUG(1,("error from g_main_loop_new\n"));
- goto error;
+/**
+ * Init callbacks at startup
+ *
+ * This gets typically called in the main parent smbd which means we can't
+ * initialize our global state here.
+ **/
+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)
+{
+ if (mdssvc_ctx == NULL) {
+ return false;
}
- g_main_context_push_thread_default(mds_ctx->gcontext);
- tracker_sparql_connection_get_async(mds_ctx->gcancellable,
- tracker_con_cb, mds_ctx);
- g_main_context_pop_thread_default(mds_ctx->gcontext);
+ samba_tevent_glib_glue_quit(mdssvc_ctx->glue);
+ TALLOC_FREE(mdssvc_ctx->glue);
- return mds_ctx;
+ TALLOC_FREE(mdssvc_ctx);
-error:
- TALLOC_FREE(mds_ctx);
- return NULL;
+ return true;
}
/**
* Tear down connections and free all resources
**/
-int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
+static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
{
/*
* We need to free query_list before ino_path_map
g_cancellable_cancel(mds_ctx->gcancellable);
g_object_unref(mds_ctx->gcancellable);
}
- if (mds_ctx->gmainloop != NULL) {
- g_main_loop_unref(mds_ctx->gmainloop);
- }
- if (mds_ctx->gcontext != NULL) {
- g_main_context_unref(mds_ctx->gcontext);
- }
ZERO_STRUCTP(mds_ctx);
return 0;
}
-static bool mds_run_gmainloop(struct mds_ctx *mds_ctx, guint timeout)
+/**
+ * Initialise a context per RPC bind
+ *
+ * This ends up being called for every tcon, because the client does a
+ * RPC bind for every tcon, so this is acually a per tcon context.
+ **/
+struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_session_info *session_info,
+ const char *path)
{
- guint timer_id;
- GSource *timer;
+ struct mds_ctx *mds_ctx;
- /*
- * It seems the event processing of the libtracker-sparql
- * async subsystem defers callbacks until *all* events are
- * processes by the async subsystem main processing loop.
- *
- * g_main_context_iteration(may_block=FALSE) can't be used,
- * because a search that produces a few thousand matches
- * generates as many events that must be processed in either
- * g_main_context_iteration() or g_main_loop_run() before
- * callbacks are called.
- *
- * Unfortunately g_main_context_iteration() only processes a
- * small subset of these event (1-30) at a time when run in
- * mds_dispatch(), which happens once a second while the
- * client polls for results.
- *
- * Carefully using the blocking g_main_loop_run() fixes
- * this. It processes events until we exit from the loop at
- * defined exit points. By adding a 1 ms timeout we at least
- * try to get as close as possible to non-blocking behaviour.
- */
+ mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
+ if (mds_ctx == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
- if (!g_main_context_pending(mds_ctx->gcontext)) {
- return true;
+ mds_ctx->mdssvc_ctx = mdssvc_init(ev);
+ if (mds_ctx->mdssvc_ctx == NULL) {
+ goto error;
}
- g_main_context_push_thread_default(mds_ctx->gcontext);
+ mds_ctx->spath = talloc_strdup(mds_ctx, path);
+ if (mds_ctx->spath == NULL) {
+ goto error;
+ }
- timer = g_timeout_source_new(timeout);
- if (timer == NULL) {
- DEBUG(1,("g_timeout_source_new_seconds\n"));
- g_main_context_pop_thread_default(mds_ctx->gcontext);
- return false;
+ mds_ctx->pipe_session_info = session_info;
+
+ if (session_info->security_token->num_sids < 1) {
+ goto error;
}
+ sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
+ mds_ctx->uid = session_info->unix_token->uid;
- timer_id = g_source_attach(timer, mds_ctx->gcontext);
- if (timer_id == 0) {
- DEBUG(1,("g_timeout_add failed\n"));
- g_source_destroy(timer);
- g_main_context_pop_thread_default(mds_ctx->gcontext);
- return false;
+ mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
+ if (mds_ctx->ino_path_map == NULL) {
+ DEBUG(1,("open inode map db failed\n"));
+ goto error;
}
- g_source_set_callback(timer, gmainloop_timer, mds_ctx, NULL);
+ mds_ctx->gcancellable = g_cancellable_new();
+ if (mds_ctx->gcancellable == NULL) {
+ DBG_ERR("error from g_cancellable_new\n");
+ goto error;
+ }
- g_main_loop_run(mds_ctx->gmainloop);
+ tracker_sparql_connection_get_async(mds_ctx->gcancellable,
+ tracker_con_cb, mds_ctx);
- g_source_destroy(timer);
+ return mds_ctx;
- g_main_context_pop_thread_default(mds_ctx->gcontext);
- return true;
+error:
+ TALLOC_FREE(mds_ctx);
+ return NULL;
}
/**
response_blob->length = 0;
- /*
- * Process finished glib events.
- *
- * FIXME: integrate with tevent instead of piggy packing it
- * onto the processing of new requests.
- *
- * mds_dispatch() is called by the client a few times in a row:
- *
- * - first in order to open/start a search query
- *
- * - later in order to fetch results asynchronously, typically
- * once a second. If no results have been retrieved from the
- * search store (Tracker) yet, we return no results.
- * The client asks for more results every second as long
- * as the "Search Window" in the client gui is open.
- *
- * - at some point the query is closed
- *
- * This means we try to iterate through the glib event loop
- * before processing the request in order to get result
- * from tracker which can be returned to the client.
- */
-
- ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS);
- if (!ok) {
- goto cleanup;
- }
-
DEBUG(10, ("share path: %s\n", mds_ctx->spath));
query = dalloc_new(mds_ctx);
goto cleanup;
}
- /*
- * Run g_main_loop a second time in order to dispatch events
- * that may have been queued at the libtracker-sparql level.
- * As we only want to dispatch (write out requests) but not
- * wait for anything, we use a much shorter timeout here.
- */
- ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS / 10);
- if (!ok) {
- goto cleanup;
- }
-
response_blob->length = len;
cleanup: