+/**
+ * Cancel a query
+ **/
+static void slq_cancel(slq_t *slq)
+{
+ slq->slq_state = SLQ_STATE_CANCEL_PENDING;
+ slq_remove(slq);
+ slq_cancelled_add(slq);
+}
+
+/**
+ * talloc destructor cb
+ **/
+static int slq_free_cb(slq_t *slq)
+{
+ if (slq->tracker_cursor) {
+ g_object_unref(slq->tracker_cursor);
+ }
+ return 0;
+}
+
+/**
+ * Free all cancelled queries
+ **/
+static void slq_cancelled_cleanup(void)
+{
+ struct list_head *p;
+ slq_t *q = NULL;
+
+ list_for_each(p, &sl_cancelled_queries) {
+ q = list_entry(p, slq_t, slq_list);
+ if (q->slq_state == SLQ_STATE_CANCELLED) {
+ LOG(log_debug, logtype_sl,
+ "ctx1: %" PRIx64 ", ctx2: %" PRIx64 ": cancelled",
+ q->slq_ctx1, q->slq_ctx2);
+ list_del(p);
+ talloc_free(q);
+ } else {
+ LOG(log_debug, logtype_sl,
+ "ctx1: %" PRIx64 ", ctx2: %" PRIx64 ": %s",
+ q->slq_ctx1, q->slq_ctx2, slq_state_names[q->slq_state].state_name);
+ }
+ }
+
+ return;
+}
+
+static void slq_dump(void)
+{
+ struct list_head *p;
+ slq_t *q = NULL;
+ int i = 0;
+
+ list_for_each(p, &sl_queries) {
+ q = list_entry(p, slq_t, slq_list);
+ LOG(log_debug, logtype_sl,
+ "query[%d]: ctx1: %" PRIx64 ", ctx2: %" PRIx64 ", state: %s",
+ i++, q->slq_ctx1, q->slq_ctx2,
+ slq_state_names[q->slq_state].state_name);
+ }
+
+ return;
+}
+
+/************************************************
+ * Tracker async callbacks
+ ************************************************/
+
+static void tracker_con_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ struct sl_ctx *sl_ctx = user_data;
+ GError *error = NULL;
+
+ sl_ctx->tracker_con = tracker_sparql_connection_get_finish(res,
+ &error);
+ if (error) {
+ LOG(log_error, logtype_sl, "Could not connect to Tracker: %s",
+ error->message);
+ sl_ctx->tracker_con = NULL;
+ g_error_free(error);
+ return;
+ }
+
+ LOG(log_info, logtype_sl, "connected to Tracker");
+}
+
+static void tracker_cursor_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ slq_t *slq = user_data;
+ gboolean more_results;
+ const gchar *uri;
+ char *path;
+ int result;
+ struct stat sb;
+ uint64_t uint64var;
+ bool ok;
+ cnid_t did, id;
+
+ LOG(log_debug, logtype_sl,
+ "cursor cb[%d]: ctx1: %" PRIx64 ", ctx2: %" PRIx64,
+ slq->query_results->num_results, slq->slq_ctx1, slq->slq_ctx2);
+
+ more_results = tracker_sparql_cursor_next_finish(slq->tracker_cursor,
+ res,
+ &error);
+
+ if (slq->slq_state == SLQ_STATE_CANCEL_PENDING) {
+ LOG(log_debug, logtype_sl,
+ "cursor cb: ctx1: %" PRIx64 ", ctx2: %" PRIx64 ": cancelled",
+ slq->slq_ctx1, slq->slq_ctx2);
+ slq->slq_state = SLQ_STATE_CANCELLED;
+ return;
+ }
+
+ if (error) {
+ LOG(log_error, logtype_sl, "Tracker cursor: %s", error->message);
+ g_error_free(error);
+ slq->slq_state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ if (!more_results) {
+ LOG(log_debug, logtype_sl, "tracker_cursor_cb: done");
+ slq->slq_state = SLQ_STATE_DONE;
+ return;
+ }
+
+ uri = tracker_sparql_cursor_get_string(slq->tracker_cursor, 0, NULL);
+ if (uri == NULL) {
+ /*
+ * Not sure how this could happen if
+ * tracker_sparql_cursor_next_finish() returns true, but I've
+ * seen it.
+ */
+ LOG(log_debug, logtype_sl, "no URI for result");
+ return;
+ }
+
+ LOG(log_debug, logtype_sl, "URI: %s", uri);
+
+ path = tracker_to_unix_path(slq->query_results, uri);
+ if (path == NULL) {
+ LOG(log_error, logtype_sl, "error converting Tracker URI: %s", uri);
+ slq->slq_state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ result = access(path, R_OK);
+ if (result != 0) {
+ goto exit;
+ }
+
+ id = cnid_for_path(slq->slq_vol->v_cdb, slq->slq_vol->v_path, path, &did);
+ if (id == CNID_INVALID) {
+ LOG(log_error, logtype_sl, "cnid_for_path error: %s", path);
+ goto exit;
+ }
+ uint64var = ntohl(id);
+
+ if (slq->slq_cnids) {
+ ok = bsearch(&uint64var, slq->slq_cnids, slq->slq_cnids_num,
+ sizeof(uint64_t), cnid_comp_fn);
+ if (!ok) {
+ goto exit;
+ }
+ }
+
+ dalloc_add_copy(slq->query_results->cnids->ca_cnids,
+ &uint64var, uint64_t);
+ ok = add_filemeta(slq->slq_reqinfo, slq->query_results->fm_array,
+ path, &sb);
+ if (!ok) {
+ LOG(log_error, logtype_sl, "add_filemeta error");
+ slq->slq_state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ slq->query_results->num_results++;
+
+exit:
+ if (slq->query_results->num_results < MAX_SL_RESULTS) {
+ LOG(log_debug, logtype_sl,
+ "cursor cb[%d]: ctx1: %" PRIx64 ", ctx2: %" PRIx64 ": requesting more results",
+ slq->query_results->num_results - 1, slq->slq_ctx1, slq->slq_ctx2);
+
+ slq->slq_state = SLQ_STATE_RESULTS;
+
+ tracker_sparql_cursor_next_async(slq->tracker_cursor,
+ slq->slq_obj->sl_ctx->cancellable,
+ tracker_cursor_cb,
+ slq);
+ } else {
+ LOG(log_debug, logtype_sl,
+ "cursor cb[%d]: ctx1: %" PRIx64 ", ctx2: %" PRIx64 ": full",
+ slq->query_results->num_results - 1, slq->slq_ctx1, slq->slq_ctx2);
+
+ slq->slq_state = SLQ_STATE_FULL;
+ }
+}
+
+static void tracker_query_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ slq_t *slq = user_data;
+
+ LOG(log_debug, logtype_sl,
+ "query cb: ctx1: %" PRIx64 ", ctx2: %" PRIx64,
+ slq->slq_ctx1, slq->slq_ctx2);
+
+ slq->tracker_cursor = tracker_sparql_connection_query_finish(
+ TRACKER_SPARQL_CONNECTION(object),
+ res,
+ &error);
+
+ if (slq->slq_state == SLQ_STATE_CANCEL_PENDING) {
+ slq->slq_state = SLQ_STATE_CANCELLED;
+ return;
+ }
+
+ if (error) {
+ slq->slq_state = SLQ_STATE_ERROR;
+ LOG(log_error, logtype_sl, "Tracker query error: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ slq->slq_state = SLQ_STATE_RESULTS;
+
+ tracker_sparql_cursor_next_async(slq->tracker_cursor,
+ slq->slq_obj->sl_ctx->cancellable,
+ tracker_cursor_cb,
+ slq);
+}
+
+/*******************************************************************************