*
* Copyright 2026 Alexander Vanhee
*
* 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include <dbus/dbus.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <systemd/sd-event.h>
#include <unistd.h>
#include "search-index.h"
#define BUS_NAME "io.github.kolunmi.Bazaar.SearchProvider"
#define OBJECT_PATH "/io/github/kolunmi/Bazaar/SearchProvider"
#define DAEMON_IFACE "io.github.kolunmi.Bazaar.Daemon"
#define SEARCH_IFACE "org.gnome.Shell.SearchProvider2"
#define MAX_SEARCH_RESULTS 25
#define ACTIVATE_TIMEOUT_MS 2000
#define UPDATE_CHECK_INTERVAL_USEC (60ULL * 60ULL * 1000000ULL) /* 1 hour */
#define IDLE_EXIT_TIMEOUT_USEC (5ULL * 1000000ULL)
#define _cleanup_(x) __attribute__ ((cleanup (x)))
static sd_event *event = NULL;
static DBusConnection *bus = NULL;
static pid_t child_pid = -1;
static SearchIndex *g_index = NULL;
static char *g_index_path = NULL;
static sd_event_source *update_timer_source = NULL;
static sd_event_source *idle_timer_source = NULL;
static sd_event_source *dispatch_source = NULL;
static void log_msg (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
static void *malloc_or_bail (size_t n_bytes);
static char *build_index_path (void);
static void ensure_index_loaded (void);
static int strv_count_local (char **strv);
static void strv_free_local (char **strv);
static void strv_freep (char ***strv);
static void generic_freep (void *p);
static int auto_update_enabled (void);
static void cancel_idle_timer (void);
static void arm_idle_timer (void);
static int on_idle_timeout (sd_event_source *s, uint64_t usec, void *userdata);
static int on_child_exit (sd_event_source *s, const siginfo_t *si, void *userdata);
static void launch_app (char **extra_args, int n_extra_args);
static void run_update_worker_once (void);
static int on_update_worker_exit (sd_event_source *s, const siginfo_t *si, void *userdata);
static int on_update_timer (sd_event_source *s, uint64_t usec, void *userdata);
static int schedule_next_update_check (void);
static void build_and_send_search_reply (DBusConnection *connection, DBusMessage *call, char **terms);
static DBusHandlerResult handle_get_result_set (DBusConnection *connection, DBusMessage *message);
static DBusHandlerResult handle_get_result_metas (DBusConnection *connection, DBusMessage *message);
static DBusHandlerResult handle_activate_result (DBusConnection *connection, DBusMessage *message);
static DBusHandlerResult handle_launch_search (DBusConnection *connection, DBusMessage *message);
static DBusHandlerResult handle_relaunch (DBusConnection *connection, DBusMessage *message);
static DBusHandlerResult handle_quit (DBusConnection *connection, DBusMessage *message);
static int on_signal (sd_event_source *s, const struct signalfd_siginfo *si, void *userdata);
static int forward_to_running_daemon (DBusConnection *forward_bus, int argc, char *argv[]);
static char **read_strv_arg (DBusMessageIter *iter);
static void append_meta (DBusMessageIter *dict_iter, const char *key, const char *value);
static void send_empty_reply (DBusConnection *connection, DBusMessage *message);
static DBusHandlerResult daemon_message_handler (DBusConnection *connection, DBusMessage *message, void *user_data);
static int on_watch_io (sd_event_source *s, int fd, uint32_t revents, void *userdata);
static dbus_bool_t add_watch (DBusWatch *watch, void *data);
static void remove_watch (DBusWatch *watch, void *data);
static void watch_toggled (DBusWatch *watch, void *data);
static int on_dbus_timeout (sd_event_source *s, uint64_t usec, void *userdata);
static dbus_bool_t add_timeout (DBusTimeout *timeout, void *data);
static void remove_timeout (DBusTimeout *timeout, void *data);
static void timeout_toggled (DBusTimeout *timeout, void *data);
static int on_dispatch (sd_event_source *s, void *userdata);
static void dispatch_status_cb (DBusConnection *connection, DBusDispatchStatus new_status, void *data);
static const DBusObjectPathVTable daemon_vtable = {
.message_function = daemon_message_handler,
};
int
main (int argc, char *argv[])
{
sigset_t mask = { 0 };
DBusError err = DBUS_ERROR_INIT;
int r = 0;
int no_window = 0;
int name_reply = 0;
_cleanup_ (generic_freep) char **fwd_args = NULL;
int n_fwd_args = 0;
int i = 0;
sigemptyset (&mask);
sigaddset (&mask, SIGTERM);
sigaddset (&mask, SIGINT);
sigaddset (&mask, SIGCHLD);
sigprocmask (SIG_BLOCK, &mask, NULL);
r = sd_event_default (&event);
if (r < 0)
return 1;
sd_event_add_signal (event, NULL, SIGTERM, on_signal, NULL);
sd_event_add_signal (event, NULL, SIGINT, on_signal, NULL);
bus = dbus_bus_get_private (DBUS_BUS_SESSION, &err);
if (bus == NULL)
{
log_msg ("Failed to connect to session bus: %s", err.message);
dbus_error_free (&err);
return 1;
}
dbus_connection_set_exit_on_disconnect (bus, FALSE);
dbus_connection_set_watch_functions (bus, add_watch, remove_watch, watch_toggled, NULL, NULL);
dbus_connection_set_timeout_functions (bus, add_timeout, remove_timeout, timeout_toggled, NULL, NULL);
sd_event_add_defer (event, &dispatch_source, on_dispatch, NULL);
sd_event_source_set_enabled (dispatch_source, SD_EVENT_OFF);
dbus_connection_set_dispatch_status_function (bus, dispatch_status_cb, NULL, NULL);
name_reply = dbus_bus_request_name (bus, BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
if (dbus_error_is_set (&err))
{
log_msg ("Failed to acquire bus name %s: %s", BUS_NAME, err.message);
dbus_error_free (&err);
return 1;
}
if (name_reply == DBUS_REQUEST_NAME_REPLY_EXISTS)
{
log_msg ("App already running, forwarding");
forward_to_running_daemon (bus, argc, argv);
dbus_connection_close (bus);
dbus_connection_unref (bus);
sd_event_unref (event);
return 0;
}
if (name_reply != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
log_msg ("Failed to acquire bus name %s: unexpected reply %d", BUS_NAME, name_reply);
return 1;
}
if (!dbus_connection_register_object_path (bus, OBJECT_PATH, &daemon_vtable, NULL))
log_msg ("Failed to register daemon object");
ensure_index_loaded ();
schedule_next_update_check ();
fwd_args = malloc_or_bail (sizeof (char *) * (size_t) (argc > 0 ? argc : 1));
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "--no-window") == 0)
{
no_window = 1;
continue;
}
fwd_args[n_fwd_args] = argv[i];
n_fwd_args++;
}
if (no_window)
log_msg ("staying headless");
else
launch_app (fwd_args, n_fwd_args);
arm_idle_timer ();
while (dbus_connection_get_dispatch_status (bus) == DBUS_DISPATCH_DATA_REMAINS)
dbus_connection_dispatch (bus);
sd_event_loop (event);
cancel_idle_timer ();
dbus_connection_flush (bus);
dbus_connection_close (bus);
dbus_connection_unref (bus);
if (update_timer_source != NULL)
sd_event_source_unref (update_timer_source);
if (dispatch_source != NULL)
sd_event_source_unref (dispatch_source);
search_index_close (g_index);
free (g_index_path);
sd_event_unref (event);
return 0;
}
static DBusHandlerResult
daemon_message_handler (DBusConnection *connection, DBusMessage *message, void *user_data)
{
if (dbus_message_is_method_call (message, SEARCH_IFACE, "GetInitialResultSet"))
return handle_get_result_set (connection, message);
if (dbus_message_is_method_call (message, SEARCH_IFACE, "GetSubsearchResultSet"))
return handle_get_result_set (connection, message);
if (dbus_message_is_method_call (message, SEARCH_IFACE, "GetResultMetas"))
return handle_get_result_metas (connection, message);
if (dbus_message_is_method_call (message, SEARCH_IFACE, "ActivateResult"))
return handle_activate_result (connection, message);
if (dbus_message_is_method_call (message, SEARCH_IFACE, "LaunchSearch"))
return handle_launch_search (connection, message);
if (dbus_message_is_method_call (message, DAEMON_IFACE, "Relaunch"))
return handle_relaunch (connection, message);
if (dbus_message_is_method_call (message, DAEMON_IFACE, "Quit"))
return handle_quit (connection, message);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static int
on_watch_io (sd_event_source *s, int fd, uint32_t revents, void *userdata)
{
DBusWatch *watch = userdata;
unsigned int flags = 0;
if (revents & EPOLLIN)
flags |= DBUS_WATCH_READABLE;
if (revents & EPOLLOUT)
flags |= DBUS_WATCH_WRITABLE;
if (revents & EPOLLHUP)
flags |= DBUS_WATCH_HANGUP;
if (revents & EPOLLERR)
flags |= DBUS_WATCH_ERROR;
dbus_watch_handle (watch, flags);
if (dbus_connection_get_dispatch_status (bus) == DBUS_DISPATCH_DATA_REMAINS
&& dispatch_source != NULL)
sd_event_source_set_enabled (dispatch_source, SD_EVENT_ON);
return 0;
}
static dbus_bool_t
add_watch (DBusWatch *watch, void *data)
{
sd_event_source *s = NULL;
unsigned int flags = 0;
uint32_t events = 0;
int r = 0;
if (!dbus_watch_get_enabled (watch))
return TRUE;
flags = dbus_watch_get_flags (watch);
if (flags & DBUS_WATCH_READABLE)
events |= EPOLLIN;
if (flags & DBUS_WATCH_WRITABLE)
events |= EPOLLOUT;
r = sd_event_add_io (event, &s, dbus_watch_get_unix_fd (watch), events, on_watch_io, watch);
if (r < 0)
return FALSE;
dbus_watch_set_data (watch, s, NULL);
return TRUE;
}
static void
remove_watch (DBusWatch *watch, void *data)
{
sd_event_source *s = NULL;
s = dbus_watch_get_data (watch);
if (s != NULL)
sd_event_source_unref (s);
dbus_watch_set_data (watch, NULL, NULL);
}
static void
watch_toggled (DBusWatch *watch, void *data)
{
sd_event_source *s = NULL;
s = dbus_watch_get_data (watch);
if (s == NULL)
{
if (dbus_watch_get_enabled (watch))
add_watch (watch, data);
return;
}
sd_event_source_set_enabled (s, dbus_watch_get_enabled (watch) ? SD_EVENT_ON : SD_EVENT_OFF);
}
static int
on_dbus_timeout (sd_event_source *s, uint64_t usec, void *userdata)
{
dbus_timeout_handle ((DBusTimeout *) userdata);
return 0;
}
static dbus_bool_t
add_timeout (DBusTimeout *timeout, void *data)
{
sd_event_source *s = NULL;
uint64_t now = 0;
int r = 0;
if (!dbus_timeout_get_enabled (timeout))
return TRUE;
sd_event_now (event, CLOCK_MONOTONIC, &now);
r = sd_event_add_time (event, &s, CLOCK_MONOTONIC,
now + (uint64_t) dbus_timeout_get_interval (timeout) * 1000,
0, on_dbus_timeout, timeout);
if (r < 0)
return FALSE;
dbus_timeout_set_data (timeout, s, NULL);
return TRUE;
}
static void
remove_timeout (DBusTimeout *timeout, void *data)
{
sd_event_source *s = NULL;
s = dbus_timeout_get_data (timeout);
if (s != NULL)
sd_event_source_unref (s);
dbus_timeout_set_data (timeout, NULL, NULL);
}
static void
timeout_toggled (DBusTimeout *timeout, void *data)
{
remove_timeout (timeout, data);
if (dbus_timeout_get_enabled (timeout))
add_timeout (timeout, data);
}
static int
on_dispatch (sd_event_source *s, void *userdata)
{
while (dbus_connection_dispatch (bus) == DBUS_DISPATCH_DATA_REMAINS)
;
sd_event_source_set_enabled (s, SD_EVENT_OFF);
return 0;
}
static void
dispatch_status_cb (DBusConnection *connection, DBusDispatchStatus new_status, void *data)
{
if (dispatch_source == NULL)
return;
sd_event_source_set_enabled (dispatch_source,
new_status == DBUS_DISPATCH_DATA_REMAINS ? SD_EVENT_ON : SD_EVENT_OFF);
}
static int
auto_update_enabled (void)
{
FILE *fp = NULL;
char buf[8] = { 0 };
int enabled = 0;
fp = popen ("gsettings get io.github.kolunmi.Bazaar auto-update", "r");
if (fp == NULL)
return 0;
if (fgets (buf, sizeof (buf), fp) != NULL)
enabled = (strncmp (buf, "true", 4) == 0);
pclose (fp);
return enabled;
}
static void
cancel_idle_timer (void)
{
if (idle_timer_source != NULL)
{
sd_event_source_unref (idle_timer_source);
idle_timer_source = NULL;
}
}
static void
arm_idle_timer (void)
{
uint64_t now = 0;
cancel_idle_timer ();
if (child_pid > 0)
return;
if (sd_event_now (event, CLOCK_BOOTTIME, &now) < 0)
return;
sd_event_add_time (event, &idle_timer_source, CLOCK_BOOTTIME,
now + IDLE_EXIT_TIMEOUT_USEC, 0,
on_idle_timeout, NULL);
}
static int
on_idle_timeout (sd_event_source *s,
uint64_t usec,
void *userdata)
{
idle_timer_source = NULL;
if (child_pid > 0)
return 0;
if (auto_update_enabled ())
{
log_msg ("Idle, but auto update is on so staying alive");
return 0;
}
log_msg ("Idle timeout reached, closing down");
sd_event_exit (event, 0);
return 0;
}
static int
forward_to_running_daemon (DBusConnection *forward_bus, int argc, char *argv[])
{
DBusMessage *m = NULL;
DBusMessage *reply = NULL;
DBusMessageIter iter;
DBusMessageIter sub;
DBusError err = DBUS_ERROR_INIT;
int i = 0;
int start = argc > 0 ? 1 : 0;
m = dbus_message_new_method_call (BUS_NAME, OBJECT_PATH, DAEMON_IFACE, "Relaunch");
if (m == NULL)
return -1;
dbus_message_iter_init_append (m, &iter);
dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub);
for (i = start; i < argc; i++)
dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &argv[i]);
dbus_message_iter_close_container (&iter, &sub);
reply = dbus_connection_send_with_reply_and_block (forward_bus, m, ACTIVATE_TIMEOUT_MS, &err);
dbus_message_unref (m);
if (reply == NULL)
{
log_msg ("Failed to forward relaunch request: %s", err.message);
dbus_error_free (&err);
return -1;
}
dbus_message_unref (reply);
return 0;
}
static DBusHandlerResult
handle_relaunch (DBusConnection *connection, DBusMessage *message)
{
DBusMessageIter iter;
_cleanup_ (strv_freep) char **terms = NULL;
dbus_message_iter_init (message, &iter);
terms = read_strv_arg (&iter);
log_msg ("Relaunch requested via D-Bus");
arm_idle_timer ();
launch_app (terms, strv_count_local (terms));
send_empty_reply (connection, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_quit (DBusConnection *connection, DBusMessage *message)
{
log_msg ("Quit requested");
send_empty_reply (connection, message);
if (child_pid > 0)
kill (child_pid, SIGTERM);
sd_event_exit (event, 0);
return DBUS_HANDLER_RESULT_HANDLED;
}
static void
log_msg (const char *fmt, ...)
{
va_list args;
printf ("(bazaar-daemon): ");
va_start (args, fmt);
vprintf (fmt, args);
va_end (args);
printf ("\n");
fflush (stdout);
}
static void *
malloc_or_bail (size_t n_bytes)
{
void *p = NULL;
p = malloc (n_bytes);
if (p == NULL)
{
perror ("malloc");
_exit (127);
}
return p;
}
static char *
build_index_path (void)
{
const char *cache_home = NULL;
const char *home = NULL;
char buf[4096] = { 0 };
cache_home = getenv ("XDG_CACHE_HOME");
if (cache_home != NULL && *cache_home != '\0')
{
snprintf (buf, sizeof (buf),
"%s/io.github.kolunmi.Bazaar/core/search-index",
cache_home);
return strdup (buf);
}
home = getenv ("HOME");
if (home != NULL && *home != '\0')
{
snprintf (buf, sizeof (buf),
"%s/.cache/io.github.kolunmi.Bazaar/core/search-index",
home);
return strdup (buf);
}
return strdup ("/tmp/search-index");
}
static void
ensure_index_loaded (void)
{
if (g_index_path == NULL)
g_index_path = build_index_path ();
if (g_index == NULL)
{
g_index = search_index_open (g_index_path);
if (g_index == NULL)
log_msg ("Search index not yet available at %s", g_index_path);
return;
}
if (search_index_reload_if_stale (&g_index))
log_msg ("Search index reloaded ");
}
static int
strv_count_local (char **strv)
{
int n = 0;
if (strv == NULL)
return 0;
while (strv[n] != NULL)
n++;
return n;
}
static void
strv_free_local (char **strv)
{
char **p = NULL;
if (strv == NULL)
return;
for (p = strv; *p != NULL; p++)
free (*p);
free (strv);
}
static void
strv_freep (char ***strv)
{
strv_free_local (*strv);
}
static void
generic_freep (void *p)
{
free (*(void **) p);
}
static int
on_child_exit (sd_event_source *s,
const siginfo_t *si,
void *userdata)
{
log_msg ("Application exited (status %d)", si->si_status);
if (si->si_pid == (pid_t) child_pid)
child_pid = -1;
sd_event_source_unref (s);
ensure_index_loaded ();
arm_idle_timer ();
return 0;
}
static void
launch_app (char **extra_args,
int n_extra_args)
{
pid_t pid = -1;
_cleanup_ (generic_freep) char **argv = NULL;
int i = 0;
sigset_t mask = { 0 };
argv = malloc_or_bail (sizeof (char *) * (size_t) (n_extra_args + 2));
argv[0] = (char *) BAZAAR_BIN_PATH;
for (i = 0; i < n_extra_args; i++)
argv[i + 1] = extra_args[i];
argv[n_extra_args + 1] = NULL;
log_msg ("Application starting");
cancel_idle_timer ();
pid = fork ();
if (pid == 0)
{
sigemptyset (&mask);
sigprocmask (SIG_SETMASK, &mask, NULL);
execvp (argv[0], argv);
perror ("execvp");
_exit (127);
}
if (pid < 0)
{
log_msg ("Failed to spawn application: %s", strerror (errno));
arm_idle_timer ();
return;
}
if (child_pid <= 0)
child_pid = pid;
sd_event_add_child (event, NULL, pid, WEXITED, on_child_exit, NULL);
}
static void
run_update_worker_once (void)
{
pid_t pid = -1;
char *argv[3] = { NULL };
sigset_t mask = { 0 };
argv[0] = (char *) BAZAAR_BIN_PATH;
argv[1] = (char *) UPDATE_WORKER_CLI_OPTION;
argv[2] = NULL;
pid = fork ();
if (pid == 0)
{
sigemptyset (&mask);
sigprocmask (SIG_SETMASK, &mask, NULL);
execvp (argv[0], argv);
perror ("execvp");
_exit (127);
}
if (pid < 0)
{
log_msg ("Failed to spawn update worker: %s", strerror (errno));
return;
}
sd_event_add_child (event, NULL, pid, WEXITED, on_update_worker_exit, NULL);
}
static int
on_update_worker_exit (sd_event_source *s,
const siginfo_t *si,
void *userdata)
{
sd_event_source_unref (s);
return 0;
}
static int
on_update_timer (sd_event_source *s,
uint64_t usec,
void *userdata)
{
run_update_worker_once ();
schedule_next_update_check ();
return 0;
}
static int
schedule_next_update_check (void)
{
uint64_t now = 0;
int r = 0;
if (update_timer_source != NULL)
{
sd_event_source_unref (update_timer_source);
update_timer_source = NULL;
}
r = sd_event_now (event, CLOCK_BOOTTIME, &now);
if (r < 0)
return r;
r = sd_event_add_time (event, &update_timer_source, CLOCK_BOOTTIME,
now + UPDATE_CHECK_INTERVAL_USEC, 0,
on_update_timer, NULL);
if (r < 0)
log_msg ("failed to schedule update: %s", strerror (-r));
return r;
}
static char **
read_strv_arg (DBusMessageIter *iter)
{
DBusMessageIter sub;
char **result = NULL;
int n = 0;
int cap = 8;
if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_ARRAY)
return NULL;
result = malloc_or_bail (sizeof (char *) * (size_t) cap);
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub) == DBUS_TYPE_STRING)
{
const char *s = NULL;
dbus_message_iter_get_basic (&sub, &s);
if (n + 1 >= cap)
{
cap *= 2;
result = realloc (result, sizeof (char *) * (size_t) cap);
}
result[n++] = strdup (s);
dbus_message_iter_next (&sub);
}
result[n] = NULL;
return result;
}
static void
append_meta (DBusMessageIter *dict_iter, const char *key, const char *value)
{
DBusMessageIter entry_iter;
DBusMessageIter variant_iter;
dbus_message_iter_open_container (dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
dbus_message_iter_append_basic (&entry_iter, DBUS_TYPE_STRING, &key);
dbus_message_iter_open_container (&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
dbus_message_iter_append_basic (&variant_iter, DBUS_TYPE_STRING, &value);
dbus_message_iter_close_container (&entry_iter, &variant_iter);
dbus_message_iter_close_container (dict_iter, &entry_iter);
}
static void
send_empty_reply (DBusConnection *connection, DBusMessage *message)
{
DBusMessage *reply = NULL;
reply = dbus_message_new_method_return (message);
if (reply == NULL)
return;
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
}
static void
build_and_send_search_reply (DBusConnection *connection,
DBusMessage *call,
char **terms)
{
DBusMessage *reply = NULL;
DBusMessageIter iter;
DBusMessageIter sub;
SearchIndexMatch matches[MAX_SEARCH_RESULTS] = { 0 };
size_t n_matches = 0;
size_t i = 0;
ensure_index_loaded ();
n_matches = search_index_query (
g_index, (const char *const *) terms,
strv_count_local (terms),
matches, MAX_SEARCH_RESULTS);
reply = dbus_message_new_method_return (call);
if (reply == NULL)
return;
dbus_message_iter_init_append (reply, &iter);
dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub);
for (i = 0; i < n_matches; i++)
dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &matches[i].entry->id);
dbus_message_iter_close_container (&iter, &sub);
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
}
static DBusHandlerResult
handle_get_result_set (DBusConnection *connection, DBusMessage *message)
{
DBusMessageIter iter;
_cleanup_ (strv_freep) char **previous = NULL;
_cleanup_ (strv_freep) char **terms = NULL;
const char *sig = NULL;
arm_idle_timer ();
sig = dbus_message_get_signature (message);
dbus_message_iter_init (message, &iter);
if (sig != NULL && strcmp (sig, "asas") == 0)
{
previous = read_strv_arg (&iter);
dbus_message_iter_next (&iter);
}
terms = read_strv_arg (&iter);
build_and_send_search_reply (connection, message, terms);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_get_result_metas (DBusConnection *connection, DBusMessage *message)
{
DBusMessageIter iter;
DBusMessage *reply = NULL;
DBusMessageIter reply_iter;
DBusMessageIter array_iter;
_cleanup_ (strv_freep) char **ids = NULL;
char **p = NULL;
arm_idle_timer ();
dbus_message_iter_init (message, &iter);
ids = read_strv_arg (&iter);
ensure_index_loaded ();
reply = dbus_message_new_method_return (message);
if (reply == NULL)
return DBUS_HANDLER_RESULT_HANDLED;
dbus_message_iter_init_append (reply, &reply_iter);
dbus_message_iter_open_container (&reply_iter, DBUS_TYPE_ARRAY, "a{sv}", &array_iter);
for (p = ids; p != NULL && *p != NULL; p++)
{
const SearchIndexEntry *e = NULL;
DBusMessageIter dict_iter;
e = search_index_find (g_index, *p);
if (e == NULL)
continue;
dbus_message_iter_open_container (&array_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
append_meta (&dict_iter, "id", e->id);
append_meta (&dict_iter, "name", e->title);
if (e->description != NULL)
append_meta (&dict_iter, "description", e->description);
if (e->icon_path != NULL)
append_meta (&dict_iter, "gicon", e->icon_path);
dbus_message_iter_close_container (&array_iter, &dict_iter);
}
dbus_message_iter_close_container (&reply_iter, &array_iter);
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_activate_result (DBusConnection *connection, DBusMessage *message)
{
DBusMessageIter iter;
const char *id = NULL;
_cleanup_ (generic_freep) char *uri_arg = NULL;
char *args[1] = { NULL };
arm_idle_timer ();
dbus_message_iter_init (message, &iter);
dbus_message_iter_get_basic (&iter, &id);
uri_arg = malloc_or_bail (strlen ("appstream:") + strlen (id) + 1);
sprintf (uri_arg, "appstream:%s", id);
args[0] = uri_arg;
launch_app (args, 1);
send_empty_reply (connection, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_launch_search (DBusConnection *connection, DBusMessage *message)
{
DBusMessageIter iter;
_cleanup_ (strv_freep) char **terms = NULL;
_cleanup_ (generic_freep) char *joined = NULL;
_cleanup_ (generic_freep) char *arg = NULL;
char *args[1] = { NULL };
dbus_uint32_t timestamp = 0;
size_t len = 0;
int n_terms = 0;
int i = 0;
arm_idle_timer ();
dbus_message_iter_init (message, &iter);
terms = read_strv_arg (&iter);
dbus_message_iter_next (&iter);
dbus_message_iter_get_basic (&iter, ×tamp);
n_terms = strv_count_local (terms);
if (n_terms == 0)
{
launch_app (NULL, 0);
send_empty_reply (connection, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
for (i = 0; i < n_terms; i++)
len += strlen (terms[i]) + 1;
joined = malloc_or_bail (len);
joined[0] = '\0';
for (i = 0; i < n_terms; i++)
{
strcat (joined, terms[i]);
if (i + 1 < n_terms)
strcat (joined, " ");
}
arg = malloc_or_bail (strlen ("--search-for=") + strlen (joined) + 1);
sprintf (arg, "--search-for=%s", joined);
args[0] = arg;
launch_app (args, 1);
send_empty_reply (connection, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static int
on_signal (sd_event_source *s,
const struct signalfd_siginfo *si,
void *userdata)
{
log_msg ("Daemon shutting down");
if (child_pid > 0)
kill (child_pid, SIGTERM);
sd_event_exit (event, 0);
return 0;
}