/* * Copyright (C) 2010, Nokia * Copyright (C) 2014, Lanedo * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include #ifdef __sun #include #endif #include #include #include #include #include #include #include #include "tracker-daemon.h" #include "tracker-config.h" #include "tracker-process.h" #include "tracker-dbus.h" typedef struct { TrackerSparqlConnection *connection; GHashTable *prefixes; GStrv filter; } WatchData; static gboolean parse_watch (const gchar *option_name, const gchar *value, gpointer data, GError **error); static GDBusConnection *connection = NULL; static GDBusProxy *proxy = NULL; static GMainLoop *main_loop; static GHashTable *miners_progress; static GHashTable *miners_status; static gint longest_miner_name_length = 0; static gint paused_length = 0; static gboolean full_namespaces = FALSE; /* Can be turned on if needed, or made cmd line option */ #define OPTION_TERM_ALL "all" #define OPTION_TERM_STORE "store" #define OPTION_TERM_MINERS "miners" /* Note: * Every time a new option is added, make sure it is considered in the * 'STATUS_OPTIONS_ENABLED' macro below */ static gboolean status; static gboolean follow; static gchar *watch = NULL; static gboolean list_common_statuses; static gchar *miner_name; static gchar *pause_reason; static gchar *pause_for_process_reason; static gint resume_cookie = -1; static gboolean list_miners_running; static gboolean list_miners_available; static gboolean pause_details; static gboolean list_processes; static TrackerProcessTypes daemons_to_kill = TRACKER_PROCESS_TYPE_NONE; static TrackerProcessTypes daemons_to_term = TRACKER_PROCESS_TYPE_NONE; static gchar *set_log_verbosity; static gboolean get_log_verbosity; static gboolean start; static gchar *backup; static gchar *restore; #define DAEMON_OPTIONS_ENABLED() \ ((status || follow || watch || list_common_statuses) || \ (miner_name || \ pause_reason || \ pause_for_process_reason || \ resume_cookie != -1 || \ list_miners_running || \ list_miners_available || \ pause_details) || \ (list_processes || \ daemons_to_kill != TRACKER_PROCESS_TYPE_NONE || \ daemons_to_term != TRACKER_PROCESS_TYPE_NONE || \ get_log_verbosity || \ set_log_verbosity || \ start || \ backup || \ restore)); static gboolean term_option_arg_func (const gchar *option_value, const gchar *value, gpointer data, GError **error); /* Make sure our statuses are translated (most from libtracker-miner) */ static const gchar *statuses[8] = { N_("Unavailable"), /* generic */ N_("Initializing"), N_("Processing…"), N_("Fetching…"), /* miner/rss */ N_("Crawling single directory “%s”"), N_("Crawling recursively directory “%s”"), N_("Paused"), N_("Idle") }; static GOptionEntry entries[] = { /* Status */ { "follow", 'f', 0, G_OPTION_ARG_NONE, &follow, N_("Follow status changes as they happen"), NULL }, { "watch", 'w', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_watch, N_("Watch changes to the database in real time (e.g. resources or files being added)"), N_("ONTOLOGY") }, { "list-common-statuses", 0, 0, G_OPTION_ARG_NONE, &list_common_statuses, N_("List common statuses for miners and the store"), NULL }, /* Miners */ { "pause", 0 , 0, G_OPTION_ARG_STRING, &pause_reason, N_("Pause a miner (you must use this with --miner)"), N_("REASON") }, { "pause-for-process", 0 , 0, G_OPTION_ARG_STRING, &pause_for_process_reason, N_("Pause a miner while the calling process is alive or until resumed (you must use this with --miner)"), N_("REASON") }, { "resume", 0 , 0, G_OPTION_ARG_INT, &resume_cookie, N_("Resume a miner (you must use this with --miner)"), N_("COOKIE") }, { "miner", 0 , 0, G_OPTION_ARG_STRING, &miner_name, N_("Miner to use with --resume or --pause (you can use suffixes, e.g. Files or Applications)"), N_("MINER") }, { "list-miners-running", 0, 0, G_OPTION_ARG_NONE, &list_miners_running, N_("List all miners currently running"), NULL }, { "list-miners-available", 0, 0, G_OPTION_ARG_NONE, &list_miners_available, N_("List all miners installed"), NULL }, { "pause-details", 0, 0, G_OPTION_ARG_NONE, &pause_details, N_("List pause reasons"), NULL }, /* Processes */ { "list-processes", 'p', 0, G_OPTION_ARG_NONE, &list_processes, N_("List all Tracker processes") }, { "kill", 'k', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, term_option_arg_func, N_("Use SIGKILL to stop all matching processes, either “store”, “miners” or “all” may be used, no parameter equals “all”"), N_("APPS") }, { "terminate", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, term_option_arg_func, N_("Use SIGTERM to stop all matching processes, either “store”, “miners” or “all” may be used, no parameter equals “all”"), N_("APPS") }, { "start", 's', 0, G_OPTION_ARG_NONE, &start, N_("Starts miners (which indirectly starts tracker-store too)"), NULL }, { "set-log-verbosity", 0, 0, G_OPTION_ARG_STRING, &set_log_verbosity, N_("Sets the logging verbosity to LEVEL (“debug”, “detailed”, “minimal”, “errors”) for all processes"), N_("LEVEL") }, { "get-log-verbosity", 0, 0, G_OPTION_ARG_NONE, &get_log_verbosity, N_("Show logging values in terms of log verbosity for each process"), NULL }, { NULL } }; static gboolean parse_watch (const gchar *option_name, const gchar *value, gpointer data, GError **error) { if (!value) { watch = g_strdup (""); } else { watch = g_strdup (value); } return TRUE; } static gboolean signal_handler (gpointer user_data) { int signo = GPOINTER_TO_INT (user_data); static gboolean in_loop = FALSE; /* Die if we get re-entrant signals handler calls */ if (in_loop) { exit (EXIT_FAILURE); } switch (signo) { case SIGTERM: case SIGINT: in_loop = TRUE; g_main_loop_quit (main_loop); /* Fall through */ default: if (g_strsignal (signo)) { g_print ("\n"); g_print ("Received signal:%d->'%s'\n", signo, g_strsignal (signo)); } break; } return G_SOURCE_CONTINUE; } static void initialize_signal_handler (void) { g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM)); g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT)); } static gboolean miner_get_details (TrackerMinerManager *manager, const gchar *miner, gchar **status, gdouble *progress, gint *remaining_time, GStrv *pause_applications, GStrv *pause_reasons) { if ((status || progress || remaining_time) && !tracker_miner_manager_get_status (manager, miner, status, progress, remaining_time)) { g_printerr (_("Could not get status from miner: %s"), miner); return FALSE; } tracker_miner_manager_is_paused (manager, miner, pause_applications, pause_reasons); if (!(*pause_applications) || !(*pause_reasons)) { /* unable to get pause details, already logged by tracker_miner_manager_is_paused */ return FALSE; } return TRUE; } static void miner_print_state (TrackerMinerManager *manager, const gchar *miner_name, const gchar *status, gdouble progress, gint remaining_time, gboolean is_running, gboolean is_paused) { const gchar *name; time_t now; gchar time_str[64]; size_t len; struct tm *local_time; now = time ((time_t *) NULL); local_time = localtime (&now); len = strftime (time_str, sizeof (time_str) - 1, "%d %b %Y, %H:%M:%S:", local_time); time_str[len] = '\0'; name = tracker_miner_manager_get_display_name (manager, miner_name); if (is_running) { gchar *progress_str = NULL; gchar *remaining_time_str = NULL; if (progress >= 0.0 && progress < 1.0) { progress_str = g_strdup_printf ("%3u%%", (guint)(progress * 100)); } /* Progress > 0.01 here because we want to avoid any message * during crawling, as we don't have the remaining time in that * case and it would just print "unknown time left" */ if (progress > 0.01 && progress < 1.0 && remaining_time >= 0) { /* 0 means that we couldn't properly compute the remaining * time. */ if (remaining_time > 0) { gchar *seconds_str = tracker_seconds_to_string (remaining_time, TRUE); /* Translators: %s is a time string */ remaining_time_str = g_strdup_printf (_("%s remaining"), seconds_str); g_free (seconds_str); } else { remaining_time_str = g_strdup (_("unknown time left")); } } g_print ("%s %s %-*.*s %s%-*.*s%s %s %s %s\n", time_str, progress_str ? progress_str : "✓ ", longest_miner_name_length, longest_miner_name_length, name, is_paused ? "(" : " ", paused_length, paused_length, is_paused ? _("PAUSED") : " ", is_paused ? ")" : " ", status ? "-" : "", status ? _(status) : "", remaining_time_str ? remaining_time_str : ""); g_free (progress_str); g_free (remaining_time_str); } else { g_print ("%s ✗ %-*.*s %-*.*s - %s\n", time_str, longest_miner_name_length, longest_miner_name_length, name, paused_length, paused_length, " ", _("Not running or is a disabled plugin")); } } static void store_print_state (const gchar *status, gdouble progress) { gchar time_str[64]; struct tm *local_time; time_t now; size_t len; now = time ((time_t *) NULL); local_time = localtime (&now); len = strftime (time_str, sizeof (time_str) - 1, "%d %b %Y, %H:%M:%S:", local_time); time_str[len] = '\0'; if (status) { gchar *operation = NULL; gchar *operation_status = NULL; gchar *progress_str; if (strstr (status, "-")) { gchar **status_split; status_split = g_strsplit (status, "-", 2); if (status_split[0] && status_split[1]) { operation = g_strstrip (status_split[0]); operation_status = g_strstrip (status_split[1]); /* Free the array, not the contents */ g_free (status_split); } else { /* Free everything */ g_strfreev (status_split); } } if (progress >= 0.0 && progress < 1.0) { progress_str = g_strdup_printf ("%3u%%", (guint)(progress * 100)); } else { progress_str = g_strdup_printf ("✓ "); } g_print ("%s %s %-*.*s - %s %s%s%s\n", time_str, progress_str ? progress_str : " ", longest_miner_name_length + paused_length, longest_miner_name_length + paused_length, "Store", /*(operation ? _(operation) : _(status)),*/ /*operation ? "-" : "",*/ operation ? _(operation) : _(status), operation_status ? "(" : "", operation_status ? operation_status : "", operation_status ? ")" : ""); g_free (progress_str); g_free (operation); g_free (operation_status); } else { g_print ("%s %s %-*.*s - %s\n", time_str, "✗ ", /* Progress */ longest_miner_name_length + paused_length, longest_miner_name_length + paused_length, "Store", _("Unavailable")); } } static void store_get_and_print_state (void) { GVariant *v_status, *v_progress; const gchar *status = NULL; gdouble progress = -1.0; GError *error = NULL; gchar *owner; owner = g_dbus_proxy_get_name_owner (proxy); if (!owner) { /* Name is not owned yet, store is not running */ store_print_state (NULL, -1); return; } g_free (owner); /* Status */ v_status = g_dbus_proxy_call_sync (proxy, "GetStatus", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!v_status || error) { g_critical ("%s, %s", _("Could not retrieve tracker-store status"), error ? error->message : _("No error given")); g_clear_error (&error); return; } g_variant_get (v_status, "(&s)", &status); /* Progress */ v_progress = g_dbus_proxy_call_sync (proxy, "GetProgress", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); g_variant_get (v_progress, "(d)", &progress); if (progress < 0.0 || error) { g_critical ("%s, %s", _("Could not retrieve tracker-store progress"), error ? error->message : _("No error given")); g_clear_error (&error); return; } /* Print */ store_print_state (status, progress); g_variant_unref (v_progress); g_variant_unref (v_status); } static void manager_miner_progress_cb (TrackerMinerManager *manager, const gchar *miner_name, const gchar *status, gdouble progress, gint remaining_time) { GValue *gvalue; gvalue = g_slice_new0 (GValue); g_value_init (gvalue, G_TYPE_DOUBLE); g_value_set_double (gvalue, progress); miner_print_state (manager, miner_name, status, progress, remaining_time, TRUE, FALSE); g_hash_table_replace (miners_status, g_strdup (miner_name), g_strdup (status)); g_hash_table_replace (miners_progress, g_strdup (miner_name), gvalue); } static void manager_miner_paused_cb (TrackerMinerManager *manager, const gchar *miner_name) { GValue *gvalue; gvalue = g_hash_table_lookup (miners_progress, miner_name); miner_print_state (manager, miner_name, g_hash_table_lookup (miners_status, miner_name), gvalue ? g_value_get_double (gvalue) : 0.0, -1, TRUE, TRUE); } static void manager_miner_resumed_cb (TrackerMinerManager *manager, const gchar *miner_name) { GValue *gvalue; gvalue = g_hash_table_lookup (miners_progress, miner_name); miner_print_state (manager, miner_name, g_hash_table_lookup (miners_status, miner_name), gvalue ? g_value_get_double (gvalue) : 0.0, 0, TRUE, FALSE); } static void miners_progress_destroy_notify (gpointer data) { GValue *value; value = data; g_value_unset (value); g_slice_free (GValue, value); } static gchar * get_shorthand (GHashTable *prefixes, const gchar *namespace) { gchar *hash; hash = strrchr (namespace, '#'); if (hash) { gchar *property; const gchar *prefix; property = hash + 1; *hash = '\0'; prefix = g_hash_table_lookup (prefixes, namespace); return g_strdup_printf ("%s:%s", prefix, property); } return g_strdup (namespace); } static GHashTable * get_prefixes (TrackerSparqlConnection *connection) { TrackerSparqlCursor *cursor; GError *error = NULL; GHashTable *retval; const gchar *query; retval = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); /* FIXME: Would like to get this in the same SPARQL that we * use to get the info, but doesn't seem possible at the * moment with the limited string manipulation features we * support in SPARQL. */ query = "SELECT ?ns ?prefix " "WHERE {" " ?ns a tracker:Namespace ;" " tracker:prefix ?prefix " "}"; cursor = tracker_sparql_connection_query (connection, query, NULL, &error); if (error) { g_printerr ("%s, %s\n", _("Unable to retrieve namespace prefixes"), error->message); g_error_free (error); return retval; } if (!cursor) { g_printerr ("%s\n", _("No namespace prefixes were returned")); return retval; } while (tracker_sparql_cursor_next (cursor, NULL, NULL)) { const gchar *key, *value; key = tracker_sparql_cursor_get_string (cursor, 0, NULL); value = tracker_sparql_cursor_get_string (cursor, 1, NULL); if (!key || !value) { continue; } g_hash_table_insert (retval, g_strndup (key, strlen (key) - 1), g_strdup (value)); } if (cursor) { g_object_unref (cursor); } return retval; } static inline void print_key (GHashTable *prefixes, const gchar *key) { if (G_UNLIKELY (full_namespaces)) { g_print ("'%s'\n", key); } else { gchar *shorthand; shorthand = get_shorthand (prefixes, key); g_print ("'%s'\n", shorthand); g_free (shorthand); } } static void store_progress (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { const gchar *status = NULL; gdouble progress = 0.0; g_variant_get (parameters, "(sd)", &status, &progress); store_print_state (status, progress); } static void store_graph_update_interpret (WatchData *wd, GHashTable *updates, gint subject, gint predicate) { TrackerSparqlCursor *cursor; GError *error = NULL; gchar *query, *key; gboolean ok = TRUE; query = g_strdup_printf ("SELECT tracker:uri (%d) tracker:uri(%d) {}", subject, predicate); cursor = tracker_sparql_connection_query (wd->connection, query, NULL, &error); g_free (query); if (error) { g_critical ("%s, %s", _("Could not run SPARQL query"), error->message); g_clear_error (&error); return; } if (!tracker_sparql_cursor_next (cursor, NULL, &error) || error) { g_critical ("%s, %s", _("Could not call tracker_sparql_cursor_next() on SPARQL query"), error ? error->message : _("No error given")); g_clear_error (&error); return; } /* Key = predicate */ key = g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)); query = g_strdup_printf ("SELECT ?t { <%s> <%s> ?t } ORDER BY DESC(<%s>)", tracker_sparql_cursor_get_string (cursor, 0, NULL), key, key); g_object_unref (cursor); cursor = tracker_sparql_connection_query (wd->connection, query, NULL, &error); g_free (query); if (error) { g_critical ("%s, %s", _("Could not run SPARQL query"), error->message); g_clear_error (&error); return; } while (ok) { const gchar *value; ok = tracker_sparql_cursor_next (cursor, NULL, &error); if (error) { g_critical ("%s, %s", _("Could not call tracker_sparql_cursor_next() on SPARQL query"), error ? error->message : _("No error given")); g_clear_error (&error); break; } value = tracker_sparql_cursor_get_string (cursor, 0, NULL); if (!key || !value) { continue; } /* Don't display nie:plainTextContent */ if (strcmp (key, "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#plainTextContent") == 0) { continue; } if (G_UNLIKELY (full_namespaces)) { if (wd->filter == NULL || tracker_string_in_string_list (key, wd->filter) != -1) { g_hash_table_replace (updates, g_strdup (key), g_strdup (value)); } } else { gchar *shorthand; shorthand = get_shorthand (wd->prefixes, key); if (wd->filter == NULL || tracker_string_in_string_list (shorthand, wd->filter) != -1) { g_hash_table_replace (updates, shorthand, g_strdup (value)); } else { g_free (shorthand); } } } g_free (key); g_object_unref (cursor); } static void store_graph_update_cb (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { WatchData *wd; GHashTable *updates; GVariantIter *iter1, *iter2; gchar *class_name; gint graph = 0, subject = 0, predicate = 0, object = 0; wd = user_data; updates = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_free); g_variant_get (parameters, "(&sa(iiii)a(iiii))", &class_name, &iter1, &iter2); while (g_variant_iter_loop (iter1, "(iiii)", &graph, &subject, &predicate, &object)) { store_graph_update_interpret (wd, updates, subject, predicate); } while (g_variant_iter_loop (iter2, "(iiii)", &graph, &subject, &predicate, &object)) { store_graph_update_interpret (wd, updates, subject, predicate); } /* Print updates sorted and filtered */ GList *keys, *l; keys = g_hash_table_get_keys (updates); keys = g_list_sort (keys, (GCompareFunc) g_strcmp0); if (g_hash_table_size (updates) > 0) { print_key (wd->prefixes, class_name); } for (l = keys; l; l = l->next) { gchar *key = l->data; gchar *value = g_hash_table_lookup (updates, l->data); g_print (" '%s' = '%s'\n", key, value); } g_list_free (keys); g_hash_table_unref (updates); g_variant_iter_free (iter1); g_variant_iter_free (iter2); } static WatchData * watch_data_new (TrackerSparqlConnection *sparql_connection, GHashTable *sparql_prefixes, const gchar *watch_filter) { WatchData *data; data = g_new0 (WatchData, 1); data->connection = g_object_ref (sparql_connection); data->prefixes = g_hash_table_ref (sparql_prefixes); if (watch_filter && strlen (watch_filter) > 0) { data->filter = g_strsplit (watch_filter, ",", -1); } return data; } static void watch_data_free (WatchData *data) { if (!data) { return; } if (data->filter) { g_strfreev (data->filter); } if (data->prefixes) { g_hash_table_unref (data->prefixes); } if (data->connection) { g_object_unref (data->connection); } g_free (data); } static gint miner_pause (const gchar *miner, const gchar *reason, gboolean for_process) { TrackerMinerManager *manager; GError *error = NULL; gchar *str; guint32 cookie; /* Don't auto-start the miners here */ manager = tracker_miner_manager_new_full (FALSE, &error); if (!manager) { g_printerr (_("Could not pause miner, manager could not be created, %s"), error ? error->message : _("No error given")); g_printerr ("\n"); g_clear_error (&error); return EXIT_FAILURE; } str = g_strdup_printf (_("Attempting to pause miner “%s” with reason “%s”"), miner, reason); g_print ("%s\n", str); g_free (str); if (for_process) { if (!tracker_miner_manager_pause_for_process (manager, miner, reason, &cookie)) { g_printerr (_("Could not pause miner: %s"), miner); g_printerr ("\n"); return EXIT_FAILURE; } } else { if (!tracker_miner_manager_pause (manager, miner, reason, &cookie)) { g_printerr (_("Could not pause miner: %s"), miner); g_printerr ("\n"); return EXIT_FAILURE; } } str = g_strdup_printf (_("Cookie is %d"), cookie); g_print (" %s\n", str); g_free (str); if (for_process) { GMainLoop *main_loop; g_print ("%s\n", _("Press Ctrl+C to stop")); main_loop = g_main_loop_new (NULL, FALSE); /* Block until Ctrl+C */ g_main_loop_run (main_loop); g_object_unref (main_loop); } g_object_unref (manager); return EXIT_SUCCESS; } static gint miner_resume (const gchar *miner, gint cookie) { TrackerMinerManager *manager; GError *error = NULL; gchar *str; /* Don't auto-start the miners here */ manager = tracker_miner_manager_new_full (FALSE, &error); if (!manager) { g_printerr (_("Could not resume miner, manager could not be created, %s"), error ? error->message : _("No error given")); g_printerr ("\n"); g_clear_error (&error); return EXIT_FAILURE; } str = g_strdup_printf (_("Attempting to resume miner %s with cookie %d"), miner, cookie); g_print ("%s\n", str); g_free (str); if (!tracker_miner_manager_resume (manager, miner, cookie)) { g_printerr (_("Could not resume miner: %s"), miner); return EXIT_FAILURE; } g_print (" %s\n", _("Done")); g_object_unref (manager); return EXIT_SUCCESS; } static gint miner_list (gboolean available, gboolean running) { TrackerMinerManager *manager; GError *error = NULL; /* Don't auto-start the miners here */ manager = tracker_miner_manager_new_full (FALSE, &error); if (!manager) { g_printerr (_("Could not list miners, manager could not be created, %s"), error ? error->message : _("No error given")); g_printerr ("\n"); g_clear_error (&error); return EXIT_FAILURE; } if (available) { GSList *miners_available; gchar *str; GSList *l; miners_available = tracker_miner_manager_get_available (manager); str = g_strdup_printf (ngettext ("Found %d miner installed", "Found %d miners installed", g_slist_length (miners_available)), g_slist_length (miners_available)); g_print ("%s%s\n", str, g_slist_length (miners_available) > 0 ? ":" : ""); g_free (str); for (l = miners_available; l; l = l->next) { g_print (" %s\n", (gchar*) l->data); } g_slist_foreach (miners_available, (GFunc) g_free, NULL); g_slist_free (miners_available); } if (running) { GSList *miners_running; gchar *str; GSList *l; miners_running = tracker_miner_manager_get_running (manager); str = g_strdup_printf (ngettext ("Found %d miner running", "Found %d miners running", g_slist_length (miners_running)), g_slist_length (miners_running)); g_print ("%s%s\n", str, g_slist_length (miners_running) > 0 ? ":" : ""); g_free (str); for (l = miners_running; l; l = l->next) { g_print (" %s\n", (gchar*) l->data); } g_slist_foreach (miners_running, (GFunc) g_free, NULL); g_slist_free (miners_running); } g_object_unref (manager); return EXIT_SUCCESS; } static gint miner_pause_details (void) { TrackerMinerManager *manager; GError *error = NULL; GSList *miners_running, *l; gint paused_miners = 0; /* Don't auto-start the miners here */ manager = tracker_miner_manager_new_full (FALSE, &error); if (!manager) { g_printerr (_("Could not get pause details, manager could not be created, %s"), error ? error->message : _("No error given")); g_printerr ("\n"); g_clear_error (&error); return EXIT_FAILURE; } miners_running = tracker_miner_manager_get_running (manager); if (!miners_running) { g_print ("%s\n", _("No miners are running")); g_slist_foreach (miners_running, (GFunc) g_free, NULL); g_slist_free (miners_running); g_object_unref (manager); return EXIT_SUCCESS; } for (l = miners_running; l; l = l->next) { const gchar *name; GStrv pause_applications, pause_reasons; gint i; name = tracker_miner_manager_get_display_name (manager, l->data); if (!name) { g_critical ("Could not get name for '%s'", (gchar *) l->data); continue; } tracker_miner_manager_is_paused (manager, l->data, &pause_applications, &pause_reasons); if (!pause_applications || !pause_reasons) { /* unable to get pause details, already logged by tracker_miner_manager_is_paused */ continue; } if (!(*pause_applications) || !(*pause_reasons)) { g_strfreev (pause_applications); g_strfreev (pause_reasons); continue; } paused_miners++; if (paused_miners == 1) { g_print ("%s:\n", _("Miners")); } g_print (" %s:\n", name); for (i = 0; pause_applications[i] != NULL; i++) { g_print (" %s: '%s', %s: '%s'\n", _("Application"), pause_applications[i], _("Reason"), pause_reasons[i]); } g_strfreev (pause_applications); g_strfreev (pause_reasons); } if (paused_miners < 1) { g_print ("%s\n", _("No miners are paused")); } g_slist_foreach (miners_running, (GFunc) g_free, NULL); g_slist_free (miners_running); g_object_unref (manager); return EXIT_SUCCESS; } inline static const gchar * verbosity_to_string (TrackerVerbosity verbosity) { GType type; GEnumClass *enum_class; GEnumValue *enum_value; type = tracker_verbosity_get_type (); enum_class = G_ENUM_CLASS (g_type_class_peek (type)); enum_value = g_enum_get_value (enum_class, verbosity); if (!enum_value) { return "unknown"; } return enum_value->value_nick; } inline static void tracker_gsettings_print_verbosity (GSList *all, gint longest, gboolean miners) { GSList *l; for (l = all; l; l = l->next) { ComponentGSettings *c; TrackerVerbosity v; c = l->data; if (c->is_miner == miners) { continue; } v = g_settings_get_enum (c->settings, "verbosity"); g_print (" %-*.*s: %s\n", longest, longest, c->name, verbosity_to_string (v)); } } static gboolean term_option_arg_func (const gchar *option_value, const gchar *value, gpointer data, GError **error) { TrackerProcessTypes option; if (!value) { value = OPTION_TERM_ALL; } if (strcmp (value, OPTION_TERM_ALL) == 0) { option = TRACKER_PROCESS_TYPE_ALL; } else if (strcmp (value, OPTION_TERM_STORE) == 0) { option = TRACKER_PROCESS_TYPE_STORE; } else if (strcmp (value, OPTION_TERM_MINERS) == 0) { option = TRACKER_PROCESS_TYPE_MINERS; } else { g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, _("Only one of “all”, “store” and “miners” options are allowed")); return FALSE; } if (strcmp (option_value, "-k") == 0 || strcmp (option_value, "--kill") == 0) { daemons_to_kill = option; } else if (strcmp (option_value, "-t") == 0 || strcmp (option_value, "--terminate") == 0) { daemons_to_term = option; } return TRUE; } static gint daemon_run (void) { TrackerMinerManager *manager; /* --follow implies --status */ if (follow) { status = TRUE; } if (watch != NULL) { TrackerSparqlConnection *sparql_connection; GHashTable *sparql_prefixes; GError *error = NULL; guint signal_id; sparql_connection = tracker_sparql_connection_get (NULL, &error); if (!sparql_connection) { g_critical ("%s, %s", _("Could not get SPARQL connection"), error ? error->message : _("No error given")); g_clear_error (&error); return EXIT_FAILURE; } if (!tracker_dbus_get_connection ("org.freedesktop.Tracker1", "/org/freedesktop/Tracker1/Resources", "org.freedesktop.Tracker1.Resources", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, &connection, &proxy)) { g_object_unref (sparql_connection); return EXIT_FAILURE; } sparql_prefixes = get_prefixes (sparql_connection); signal_id = g_dbus_connection_signal_subscribe (connection, TRACKER_DBUS_SERVICE, TRACKER_DBUS_INTERFACE_RESOURCES, "GraphUpdated", TRACKER_DBUS_OBJECT_RESOURCES, NULL, /* TODO: Use class-name here */ G_DBUS_SIGNAL_FLAGS_NONE, store_graph_update_cb, watch_data_new (sparql_connection, sparql_prefixes, watch), (GDestroyNotify) watch_data_free); g_hash_table_unref (sparql_prefixes); g_object_unref (sparql_connection); g_print ("%s\n", _("Now listening for resource updates to the database")); g_print ("%s\n\n", _("All nie:plainTextContent properties are omitted")); g_print ("%s\n", _("Press Ctrl+C to stop")); main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); g_main_loop_unref (main_loop); g_dbus_connection_signal_unsubscribe (connection, signal_id); return EXIT_SUCCESS; } if (list_common_statuses) { gint i; g_print ("%s:\n", _("Common statuses include")); for (i = 0; i < G_N_ELEMENTS (statuses); i++) { g_print (" %s\n", _(statuses[i])); } return EXIT_SUCCESS; } if (status) { GError *error = NULL; GSList *miners_available; GSList *miners_running; GSList *l; /* Don't auto-start the miners here */ manager = tracker_miner_manager_new_full (FALSE, &error); if (!manager) { g_printerr (_("Could not get status, manager could not be created, %s"), error ? error->message : _("No error given")); g_printerr ("\n"); g_clear_error (&error); return EXIT_FAILURE; } miners_available = tracker_miner_manager_get_available (manager); miners_running = tracker_miner_manager_get_running (manager); /* Work out lengths for output spacing */ paused_length = strlen (_("PAUSED")); for (l = miners_available; l; l = l->next) { const gchar *name; name = tracker_miner_manager_get_display_name (manager, l->data); longest_miner_name_length = MAX (longest_miner_name_length, strlen (name)); } /* Display states */ g_print ("%s:\n", _("Store")); if (!tracker_dbus_get_connection ("org.freedesktop.Tracker1", "/org/freedesktop/Tracker1/Status", "org.freedesktop.Tracker1.Status", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, &connection, &proxy)) { return EXIT_FAILURE; } g_dbus_connection_signal_subscribe (connection, "org.freedesktop.Tracker1", "org.freedesktop.Tracker1.Status", "Progress", "/org/freedesktop/Tracker1/Status", NULL, G_DBUS_SIGNAL_FLAGS_NONE, store_progress, NULL, NULL); store_get_and_print_state (); g_print ("\n"); g_print ("%s:\n", _("Miners")); for (l = miners_available; l; l = l->next) { const gchar *name; gboolean is_running; name = tracker_miner_manager_get_display_name (manager, l->data); if (!name) { g_critical (_("Could not get display name for miner “%s”"), (const gchar*) l->data); continue; } is_running = tracker_string_in_gslist (l->data, miners_running); if (is_running) { GStrv pause_applications, pause_reasons; gchar *status = NULL; gdouble progress; gint remaining_time; gboolean is_paused; if (!miner_get_details (manager, l->data, &status, &progress, &remaining_time, &pause_applications, &pause_reasons)) { continue; } is_paused = *pause_applications || *pause_reasons; miner_print_state (manager, l->data, status, progress, remaining_time, TRUE, is_paused); g_strfreev (pause_applications); g_strfreev (pause_reasons); g_free (status); } else { miner_print_state (manager, l->data, NULL, 0.0, -1, FALSE, FALSE); } } g_slist_foreach (miners_available, (GFunc) g_free, NULL); g_slist_free (miners_available); g_slist_foreach (miners_running, (GFunc) g_free, NULL); g_slist_free (miners_running); if (!follow) { /* Do nothing further */ if (proxy) { g_object_unref (proxy); } g_print ("\n"); return EXIT_SUCCESS; } g_print ("%s\n", _("Press Ctrl+C to stop")); g_signal_connect (manager, "miner-progress", G_CALLBACK (manager_miner_progress_cb), NULL); g_signal_connect (manager, "miner-paused", G_CALLBACK (manager_miner_paused_cb), NULL); g_signal_connect (manager, "miner-resumed", G_CALLBACK (manager_miner_resumed_cb), NULL); initialize_signal_handler (); miners_progress = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) miners_progress_destroy_notify); miners_status = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_free); main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); g_main_loop_unref (main_loop); g_hash_table_unref (miners_progress); g_hash_table_unref (miners_status); if (proxy) { g_object_unref (proxy); } if (manager) { g_object_unref (manager); } return EXIT_SUCCESS; } /* Miners */ if (pause_reason && resume_cookie != -1) { g_printerr ("%s\n", _("You can not use miner pause and resume switches together")); return EXIT_FAILURE; } if ((pause_reason || pause_for_process_reason || resume_cookie != -1) && !miner_name) { g_printerr ("%s\n", _("You must provide the miner for pause or resume commands")); return EXIT_FAILURE; } if ((!pause_reason && !pause_for_process_reason && resume_cookie == -1) && miner_name) { g_printerr ("%s\n", _("You must provide a pause or resume command for the miner")); return EXIT_FAILURE; } /* Known actions */ if (list_miners_running || list_miners_available) { return miner_list (list_miners_available, list_miners_running); } if (pause_reason) { return miner_pause (miner_name, pause_reason, FALSE); } if (pause_for_process_reason) { return miner_pause (miner_name, pause_for_process_reason, TRUE); } if (resume_cookie != -1) { return miner_resume (miner_name, resume_cookie); } if (pause_details) { return miner_pause_details (); } /* Processes */ GError *error = NULL; gpointer verbosity_type_enum_class_pointer = NULL; TrackerVerbosity set_log_verbosity_value = TRACKER_VERBOSITY_ERRORS; /* Constraints */ if (daemons_to_kill != TRACKER_PROCESS_TYPE_NONE && daemons_to_term != TRACKER_PROCESS_TYPE_NONE) { g_printerr ("%s\n", _("You can not use the --kill and --terminate arguments together")); return EXIT_FAILURE; } if (get_log_verbosity && set_log_verbosity) { g_printerr ("%s\n", _("You can not use the --get-logging and --set-logging arguments together")); return EXIT_FAILURE; } if (set_log_verbosity) { if (g_ascii_strcasecmp (set_log_verbosity, "debug") == 0) { set_log_verbosity_value = TRACKER_VERBOSITY_DEBUG; } else if (g_ascii_strcasecmp (set_log_verbosity, "detailed") == 0) { set_log_verbosity_value = TRACKER_VERBOSITY_DETAILED; } else if (g_ascii_strcasecmp (set_log_verbosity, "minimal") == 0) { set_log_verbosity_value = TRACKER_VERBOSITY_MINIMAL; } else if (g_ascii_strcasecmp (set_log_verbosity, "errors") == 0) { set_log_verbosity_value = TRACKER_VERBOSITY_ERRORS; } else { g_printerr ("%s\n", _("Invalid log verbosity, try “debug”, “detailed”, “minimal” or “errors”")); return EXIT_FAILURE; } } if (get_log_verbosity || set_log_verbosity) { GType etype; /* Since we don't reference this enum anywhere, we do * it here to make sure it exists when we call * g_type_class_peek(). This wouldn't be necessary if * it was a param in a GObject for example. * * This does mean that we are leaking by 1 reference * here and should clean it up, but it doesn't grow so * this is acceptable. */ etype = tracker_verbosity_get_type (); verbosity_type_enum_class_pointer = g_type_class_ref (etype); } if (list_processes) { GSList *pids, *l; gchar *str; pids = tracker_process_find_all (); str = g_strdup_printf (g_dngettext (NULL, "Found %d PID…", "Found %d PIDs…", g_slist_length (pids)), g_slist_length (pids)); g_print ("%s\n", str); g_free (str); for (l = pids; l; l = l->next) { TrackerProcessData *pd = l->data; str = g_strdup_printf (_("Found process ID %d for “%s”"), pd->pid, pd->cmd); g_print ("%s\n", str); g_free (str); } g_slist_foreach (pids, (GFunc) tracker_process_data_free, NULL); g_slist_free (pids); return EXIT_SUCCESS; } if (daemons_to_kill != TRACKER_PROCESS_TYPE_NONE || daemons_to_term != TRACKER_PROCESS_TYPE_NONE) { exit (tracker_process_stop (daemons_to_term, daemons_to_kill)); } /* Deal with logging changes AFTER the config may have been * reset, this way users can actually use --remove-config with * the --set-logging switch. */ if (get_log_verbosity) { GSList *all; gint longest = 0; all = tracker_gsettings_get_all (&longest); if (!all) { return EXIT_FAILURE; } g_print ("%s:\n", _("Components")); tracker_gsettings_print_verbosity (all, longest, TRUE); g_print ("\n"); /* Miners */ g_print ("%s (%s):\n", _("Miners"), _("Only those with config listed")); tracker_gsettings_print_verbosity (all, longest, FALSE); g_print ("\n"); tracker_gsettings_free (all); return EXIT_SUCCESS; } if (set_log_verbosity) { GSList *all; gchar *str; gint longest = 0; all = tracker_gsettings_get_all (&longest); if (!all) { return EXIT_FAILURE; } str = g_strdup_printf (_("Setting log verbosity for all components to “%s”…"), set_log_verbosity); g_print ("%s\n", str); g_print ("\n"); g_free (str); tracker_gsettings_set_all (all, set_log_verbosity_value); tracker_gsettings_free (all); /* We free to make sure we get new settings and that * they're saved properly. */ all = tracker_gsettings_get_all (&longest); if (!all) { return EXIT_FAILURE; } g_print ("%s:\n", _("Components")); tracker_gsettings_print_verbosity (all, longest, TRUE); g_print ("\n"); /* Miners */ g_print ("%s (%s):\n", _("Miners"), _("Only those with config listed")); tracker_gsettings_print_verbosity (all, longest, FALSE); g_print ("\n"); tracker_gsettings_free (all); return EXIT_SUCCESS; } if (verbosity_type_enum_class_pointer) { g_type_class_unref (verbosity_type_enum_class_pointer); } if (start) { TrackerMinerManager *manager; GSList *miners, *l; g_print ("%s\n", _("Starting miners…")); /* Auto-start the miners here */ manager = tracker_miner_manager_new_full (TRUE, &error); if (!manager) { g_printerr (_("Could not start miners, manager could not be created, %s"), error ? error->message : _("No error given")); g_printerr ("\n"); g_clear_error (&error); return EXIT_FAILURE; } miners = tracker_miner_manager_get_available (manager); /* Get the status of all miners, this will start all * miners not already running. */ for (l = miners; l; l = l->next) { const gchar *display_name; gdouble progress = 0.0; display_name = tracker_miner_manager_get_display_name (manager, l->data); if (!tracker_miner_manager_get_status (manager, l->data, NULL, &progress, NULL)) { g_printerr (" ✗ %s (%s)\n", display_name, _("perhaps a disabled plugin?")); } else { g_print (" ✓ %s\n", display_name); } g_free (l->data); } g_slist_free (miners); g_object_unref (manager); return EXIT_SUCCESS; } /* All known options have their own exit points */ g_warn_if_reached (); return EXIT_FAILURE; } static int daemon_run_default (void) { /* Enable status output in the default run */ status = TRUE; return daemon_run (); } static gboolean daemon_options_enabled (void) { return DAEMON_OPTIONS_ENABLED (); } int tracker_daemon (int argc, const char **argv) { GOptionContext *context; GError *error = NULL; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, NULL); g_option_context_set_summary (context, _("If no arguments are given, the status of the store and data miners is shown")); argv[0] = "tracker daemon"; if (!g_option_context_parse (context, &argc, (char***) &argv, &error)) { g_printerr ("%s, %s\n", _("Unrecognized options"), error->message); g_error_free (error); g_option_context_free (context); return EXIT_FAILURE; } g_option_context_free (context); if (daemon_options_enabled ()) { return daemon_run (); } return daemon_run_default (); }