tracker-0.16.2/src/tracker-control/tracker-control-status.c

No issues found

  1 /*
  2  * Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU General Public License
  6  * as published by the Free Software Foundation; either version 2
  7  * of the License, or (at your option) any later version.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU General Public License
 15  * along with this program; if not, write to the Free Software
 16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 17  * 02110-1301, USA.
 18  */
 19 
 20 #include "config.h"
 21 
 22 #include <glib/gi18n.h>
 23 
 24 #include <libtracker-common/tracker-common.h>
 25 #include <libtracker-miner/tracker-miner.h>
 26 
 27 #include "tracker-control.h"
 28 
 29 static GDBusConnection *connection = NULL;
 30 static GDBusProxy *proxy = NULL;
 31 static GMainLoop *main_loop;
 32 static GHashTable *miners_progress;
 33 static GHashTable *miners_status;
 34 static gint longest_miner_name_length = 0;
 35 static gint paused_length = 0;
 36 
 37 /* Note:
 38  * Every time a new option is added, make sure it is considered in the
 39  * 'STATUS_OPTIONS_ENABLED' macro below
 40  */
 41 static gboolean status;
 42 static gboolean follow;
 43 static gboolean list_common_statuses;
 44 
 45 #define STATUS_OPTIONS_ENABLED() \
 46 	(status || follow || list_common_statuses)
 47 
 48 /* Make sure our statuses are translated (most from libtracker-miner) */
 49 static const gchar *statuses[8] = {
 50 	N_("Unavailable"), /* generic */
 51 	N_("Initializing"),
 52 	N_("Processing…"),
 53 	N_("Fetching…"), /* miner/rss */
 54 	N_("Crawling single directory '%s'"),
 55 	N_("Crawling recursively directory '%s'"),
 56 	N_("Paused"),
 57 	N_("Idle")
 58 };
 59 
 60 static GOptionEntry entries[] = {
 61 	{ "status", 'S', 0, G_OPTION_ARG_NONE, &status,
 62 	  N_("Show current status"),
 63 	  NULL
 64 	},
 65 	{ "follow", 'F', 0, G_OPTION_ARG_NONE, &follow,
 66 	  N_("Follow status changes as they happen"),
 67 	  NULL
 68 	},
 69 	{ "list-common-statuses", 'C', 0, G_OPTION_ARG_NONE, &list_common_statuses,
 70 	  N_("List common statuses for miners and the store"),
 71 	  NULL
 72 	},
 73 	{ NULL }
 74 };
 75 
 76 gboolean
 77 tracker_control_status_options_enabled (void)
 78 {
 79 	return STATUS_OPTIONS_ENABLED ();
 80 }
 81 
 82 static void
 83 signal_handler (int signo)
 84 {
 85 	static gboolean in_loop = FALSE;
 86 
 87 	/* Die if we get re-entrant signals handler calls */
 88 	if (in_loop) {
 89 		exit (EXIT_FAILURE);
 90 	}
 91 
 92 	switch (signo) {
 93 	case SIGTERM:
 94 	case SIGINT:
 95 		in_loop = TRUE;
 96 		g_main_loop_quit (main_loop);
 97 
 98 		/* Fall through */
 99 	default:
100 		if (g_strsignal (signo)) {
101 			g_print ("\n");
102 			g_print ("Received signal:%d->'%s'\n",
103 			         signo,
104 			         g_strsignal (signo));
105 		}
106 		break;
107 	}
108 }
109 
110 static void
111 initialize_signal_handler (void)
112 {
113 	struct sigaction act;
114 	sigset_t empty_mask;
115 
116 	sigemptyset (&empty_mask);
117 	act.sa_handler = signal_handler;
118 	act.sa_mask = empty_mask;
119 	act.sa_flags = 0;
120 
121 	sigaction (SIGTERM, &act, NULL);
122 	sigaction (SIGINT, &act, NULL);
123 	sigaction (SIGHUP, &act, NULL);
124 }
125 
126 static gboolean
127 miner_get_details (TrackerMinerManager  *manager,
128                    const gchar          *miner,
129                    gchar               **status,
130                    gdouble              *progress,
131                    gint                 *remaining_time,
132                    GStrv                *pause_applications,
133                    GStrv                *pause_reasons)
134 {
135 	if ((status || progress || remaining_time) &&
136 	    !tracker_miner_manager_get_status (manager,
137 	                                       miner,
138 	                                       status,
139 	                                       progress,
140 	                                       remaining_time)) {
141 		g_printerr (_("Could not get status from miner: %s"), miner);
142 		return FALSE;
143 	}
144 
145 	tracker_miner_manager_is_paused (manager, miner,
146 	                                 pause_applications,
147 	                                 pause_reasons);
148 
149 	if (!(*pause_applications) || !(*pause_reasons)) {
150 		/* unable to get pause details,
151 		   already logged by tracker_miner_manager_is_paused */
152 		return FALSE;
153 	}
154 
155 	return TRUE;
156 }
157 
158 static void
159 miner_print_state (TrackerMinerManager *manager,
160                    const gchar         *miner_name,
161                    const gchar         *status,
162                    gdouble              progress,
163                    gint                 remaining_time,
164                    gboolean             is_running,
165                    gboolean             is_paused)
166 {
167 	const gchar *name;
168 	time_t now;
169 	gchar time_str[64];
170 	size_t len;
171 	struct tm *local_time;
172 
173 	now = time ((time_t *) NULL);
174 	local_time = localtime (&now);
175 	len = strftime (time_str,
176 	                sizeof (time_str) - 1,
177 	                "%d %b %Y, %H:%M:%S:",
178 	                local_time);
179 	time_str[len] = '\0';
180 
181 	name = tracker_miner_manager_get_display_name (manager, miner_name);
182 
183 	if (is_running) {
184 		gchar *progress_str = NULL;
185 		gchar *remaining_time_str = NULL;
186 
187 		if (progress >= 0.0 && progress < 1.0) {
188 			progress_str = g_strdup_printf ("%3u%%", (guint)(progress * 100));
189 		}
190 
191 		/* Progress > 0.01 here because we want to avoid any message
192 		 * during crawling, as we don't have the remaining time in that
193 		 * case and it would just print "unknown time left" */
194 		if (progress > 0.01 &&
195 		    progress < 1.0 &&
196 		    remaining_time >= 0) {
197 			/* 0 means that we couldn't properly compute the remaining
198 			 * time. */
199 			if (remaining_time > 0) {
200 				gchar *seconds_str = tracker_seconds_to_string (remaining_time, TRUE);
201 
202 				/* Translators: %s is a time string */
203 				remaining_time_str = g_strdup_printf (_("%s remaining"), seconds_str);
204 				g_free (seconds_str);
205 			} else {
206 				remaining_time_str = g_strdup (_("unknown time left"));
207 			}
208 		}
209 
210 		g_print ("%s  %s  %-*.*s %s%-*.*s%s %s %s %s\n",
211 		         time_str,
212 		         progress_str ? progress_str : "✓   ",
213 		         longest_miner_name_length,
214 		         longest_miner_name_length,
215 		         name,
216 		         is_paused ? "(" : " ",
217 		         paused_length,
218 		         paused_length,
219 		         is_paused ? _("PAUSED") : " ",
220 		         is_paused ? ")" : " ",
221 		         status ? "-" : "",
222 		         status ? _(status) : "",
223 		         remaining_time_str ? remaining_time_str : "");
224 
225 		g_free (progress_str);
226 		g_free (remaining_time_str);
227 	} else {
228 		g_print ("%s  ✗     %-*.*s  %-*.*s  - %s\n",
229 		         time_str,
230 		         longest_miner_name_length,
231 		         longest_miner_name_length,
232 		         name,
233 		         paused_length,
234 		         paused_length,
235 		         " ",
236 		         _("Not running or is a disabled plugin"));
237 	}
238 }
239 
240 static void
241 store_print_state (const gchar *status,
242                    gdouble      progress)
243 {
244 	gchar time_str[64];
245 	struct tm *local_time;
246 	time_t now;
247 	size_t len;
248 
249 	now = time ((time_t *) NULL);
250 	local_time = localtime (&now);
251 	len = strftime (time_str,
252 	                sizeof (time_str) - 1,
253 	                "%d %b %Y, %H:%M:%S:",
254 	                local_time);
255 	time_str[len] = '\0';
256 
257 	if (status) {
258 		gchar *operation = NULL;
259 		gchar *operation_status = NULL;
260 		gchar *progress_str;
261 
262 		if (strstr (status, "-")) {
263 			gchar **status_split;
264 
265 			status_split = g_strsplit (status, "-", 2);
266 			if (status_split[0] && status_split[1]) {
267 				operation = g_strstrip (status_split[0]);
268 				operation_status = g_strstrip (status_split[1]);
269 				/* Free the array, not the contents */
270 				g_free (status_split);
271 			} else {
272 				/* Free everything */
273 				g_strfreev (status_split);
274 			}
275 		}
276 
277 		if (progress >= 0.0 && progress < 1.0) {
278 			progress_str = g_strdup_printf ("%3u%%", (guint)(progress * 100));
279 		} else {
280 			progress_str = g_strdup_printf ("✓   ");
281 		}
282 
283 		g_print ("%s  %s  %-*.*s    - %s %s%s%s\n",
284 		         time_str,
285 		         progress_str ? progress_str : "    ",
286 		         longest_miner_name_length + paused_length,
287 		         longest_miner_name_length + paused_length,
288 		         "Store",
289 		         /*(operation ? _(operation) : _(status)),*/
290 		         /*operation ? "-" : "",*/
291 		         operation ? _(operation) : _(status),
292 		         operation_status ? "(" : "",
293 		         operation_status ? operation_status : "",
294 		         operation_status ? ")" : "");
295 
296 		g_free (progress_str);
297 		g_free (operation);
298 		g_free (operation_status);
299 	} else {
300 		g_print ("%s  %s %-*.*s    - %s\n",
301 		         time_str,
302 		         "✗    ", /* Progress */
303 		         longest_miner_name_length + paused_length,
304 		         longest_miner_name_length + paused_length,
305 		         "Store",
306 		         _("Unavailable"));
307 	}
308 }
309 
310 static void
311 store_get_and_print_state (void)
312 {
313 	GVariant *v_status, *v_progress;
314 	const gchar *status = NULL;
315 	gdouble progress = -1.0;
316 	GError *error = NULL;
317 	gchar *owner;
318 
319 	owner = g_dbus_proxy_get_name_owner (proxy);
320 	if (!owner) {
321 		/* Name is not owned yet, store is not running */
322 		store_print_state (NULL, -1);
323 		return;
324 	}
325 	g_free (owner);
326 
327 	/* Status */
328 	v_status = g_dbus_proxy_call_sync (proxy,
329 	                                   "GetStatus",
330 	                                   NULL,
331 	                                   G_DBUS_CALL_FLAGS_NONE,
332 	                                   -1,
333 	                                   NULL,
334 	                                   &error);
335 
336 	if (!v_status || error) {
337 		g_critical ("Could not retrieve tracker-store status: %s",
338 		            error ? error->message : "no error given");
339 		g_clear_error (&error);
340 		return;
341 	}
342 
343 	g_variant_get (v_status, "(&s)", &status);
344 
345 	/* Progress */
346 	v_progress = g_dbus_proxy_call_sync (proxy,
347 	                                     "GetProgress",
348 	                                     NULL,
349 	                                     G_DBUS_CALL_FLAGS_NONE,
350 	                                     -1,
351 	                                     NULL,
352 	                                     &error);
353 
354 	g_variant_get (v_progress, "(d)", &progress);
355 
356 	if (progress < 0.0 || error) {
357 		g_critical ("Could not retrieve tracker-store progress: %s",
358 		            error ? error->message : "no error given");
359 		g_clear_error (&error);
360 		return;
361 	}
362 
363 	/* Print */
364 	store_print_state (status, progress);
365 
366 	g_variant_unref (v_progress);
367 	g_variant_unref (v_status);
368 }
369 
370 static void
371 manager_miner_progress_cb (TrackerMinerManager *manager,
372                            const gchar         *miner_name,
373                            const gchar         *status,
374                            gdouble              progress,
375                            gint                 remaining_time)
376 {
377 	GValue *gvalue;
378 
379 	gvalue = g_slice_new0 (GValue);
380 
381 	g_value_init (gvalue, G_TYPE_DOUBLE);
382 	g_value_set_double (gvalue, progress);
383 
384 	miner_print_state (manager, miner_name, status, progress, remaining_time, TRUE, FALSE);
385 
386 	g_hash_table_replace (miners_status,
387 	                      g_strdup (miner_name),
388 	                      g_strdup (status));
389 	g_hash_table_replace (miners_progress,
390 	                      g_strdup (miner_name),
391 	                      gvalue);
392 }
393 
394 static void
395 manager_miner_paused_cb (TrackerMinerManager *manager,
396                          const gchar         *miner_name)
397 {
398 	GValue *gvalue;
399 
400 	gvalue = g_hash_table_lookup (miners_progress, miner_name);
401 
402 	miner_print_state (manager, miner_name,
403 	                   g_hash_table_lookup (miners_status, miner_name),
404 	                   gvalue ? g_value_get_double (gvalue) : 0.0,
405 	                   -1,
406 	                   TRUE,
407 	                   TRUE);
408 }
409 
410 static void
411 manager_miner_resumed_cb (TrackerMinerManager *manager,
412                           const gchar         *miner_name)
413 {
414 	GValue *gvalue;
415 
416 	gvalue = g_hash_table_lookup (miners_progress, miner_name);
417 
418 	miner_print_state (manager, miner_name,
419 	                   g_hash_table_lookup (miners_status, miner_name),
420 	                   gvalue ? g_value_get_double (gvalue) : 0.0,
421 	                   0,
422 	                   TRUE,
423 	                   FALSE);
424 }
425 
426 static void
427 miners_progress_destroy_notify (gpointer data)
428 {
429 	GValue *value;
430 
431 	value = data;
432 	g_value_unset (value);
433 	g_slice_free (GValue, value);
434 }
435 
436 static void
437 store_progress (GDBusConnection *connection,
438                 const gchar     *sender_name,
439                 const gchar     *object_path,
440                 const gchar     *interface_name,
441                 const gchar     *signal_name,
442                 GVariant        *parameters,
443                 gpointer         user_data)
444 {
445 	const gchar *status = NULL;
446 	gdouble progress = 0.0;
447 
448 	g_variant_get (parameters, "(sd)", &status, &progress);
449 	store_print_state (status, progress);
450 }
451 
452 static gboolean
453 store_init (void)
454 {
455 	GError *error = NULL;
456 
457 	if (connection && proxy) {
458 		return TRUE;
459 	}
460 
461 	connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
462 
463 	if (!connection) {
464 		g_critical ("Could not connect to the D-Bus session bus, %s",
465 		            error ? error->message : "no error given.");
466 		g_clear_error (&error);
467 		return FALSE;
468 	}
469 
470 	proxy = g_dbus_proxy_new_sync (connection,
471 	                               G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
472 	                               NULL,
473 	                               "org.freedesktop.Tracker1",
474 	                               "/org/freedesktop/Tracker1/Status",
475 	                               "org.freedesktop.Tracker1.Status",
476 	                               NULL,
477 	                               &error);
478 
479 	if (error) {
480 		g_critical ("Could not create proxy on the D-Bus session bus, %s",
481 		            error ? error->message : "no error given.");
482 		g_clear_error (&error);
483 		return FALSE;
484 	}
485 
486 	g_dbus_connection_signal_subscribe (connection,
487 	                                    "org.freedesktop.Tracker1",
488 	                                    "org.freedesktop.Tracker1.Status",
489 	                                    "Progress",
490 	                                    "/org/freedesktop/Tracker1/Status",
491 	                                    NULL,
492 	                                    G_DBUS_SIGNAL_FLAGS_NONE,
493 	                                    store_progress,
494 	                                    NULL,
495 	                                    NULL);
496 
497 	return TRUE;
498 }
499 
500 void
501 tracker_control_status_run_default (void)
502 {
503 	/* Enable status output in the default run */
504 	status = TRUE;
505 
506 	tracker_control_status_run ();
507 }
508 
509 gint
510 tracker_control_status_run (void)
511 {
512 	TrackerMinerManager *manager;
513 
514 	/* --follow implies --status */
515 	if (follow) {
516 		status = TRUE;
517 	}
518 
519 	if (list_common_statuses) {
520 		gint i;
521 
522 		g_print ("%s:\n", _("Common statuses include"));
523 
524 		for (i = 0; i < G_N_ELEMENTS (statuses); i++) {
525 			g_print ("  %s\n", _(statuses[i]));
526 		}
527 
528 		return EXIT_SUCCESS;
529 	}
530 
531 	if (status) {
532 		GError *error = NULL;
533 		GSList *miners_available;
534 		GSList *miners_running;
535 		GSList *l;
536 
537 		/* Don't auto-start the miners here */
538 		manager = tracker_miner_manager_new_full (FALSE, &error);
539 		if (!manager) {
540 			g_printerr (_("Could not get status, manager could not be created, %s"),
541 			            error ? error->message : "unknown error");
542 			g_printerr ("\n");
543 			g_clear_error (&error);
544 			return EXIT_FAILURE;
545 		}
546 
547 		miners_available = tracker_miner_manager_get_available (manager);
548 		miners_running = tracker_miner_manager_get_running (manager);
549 
550 		/* Work out lengths for output spacing */
551 		paused_length = strlen (_("PAUSED"));
552 
553 		for (l = miners_available; l; l = l->next) {
554 			const gchar *name;
555 
556 			name = tracker_miner_manager_get_display_name (manager, l->data);
557 			longest_miner_name_length = MAX (longest_miner_name_length, strlen (name));
558 		}
559 
560 		/* Display states */
561 		g_print ("%s:\n", _("Store"));
562 		store_init ();
563 		store_get_and_print_state ();
564 
565 		g_print ("\n");
566 
567 		g_print ("%s:\n", _("Miners"));
568 
569 		for (l = miners_available; l; l = l->next) {
570 			const gchar *name;
571 			gboolean is_running;
572 
573 			name = tracker_miner_manager_get_display_name (manager, l->data);
574 			if (!name) {
575 				g_critical ("Could not get name for '%s'", (gchar *) l->data);
576 				continue;
577 			}
578 
579 			is_running = tracker_string_in_gslist (l->data, miners_running);
580 
581 			if (is_running) {
582 				GStrv pause_applications, pause_reasons;
583 				gchar *status = NULL;
584 				gdouble progress;
585 				gint remaining_time;
586 				gboolean is_paused;
587 
588 				if (!miner_get_details (manager,
589 				                        l->data,
590 				                        &status,
591 				                        &progress,
592 				                        &remaining_time,
593 				                        &pause_applications,
594 				                        &pause_reasons)) {
595 					continue;
596 				}
597 
598 				is_paused = *pause_applications || *pause_reasons;
599 
600 				miner_print_state (manager,
601 				                   l->data,
602 				                   status,
603 				                   progress,
604 				                   remaining_time,
605 				                   TRUE,
606 				                   is_paused);
607 
608 				g_strfreev (pause_applications);
609 				g_strfreev (pause_reasons);
610 				g_free (status);
611 			} else {
612 				miner_print_state (manager, l->data, NULL, 0.0, -1, FALSE, FALSE);
613 			}
614 		}
615 
616 		g_slist_foreach (miners_available, (GFunc) g_free, NULL);
617 		g_slist_free (miners_available);
618 
619 		g_slist_foreach (miners_running, (GFunc) g_free, NULL);
620 		g_slist_free (miners_running);
621 
622 		if (!follow) {
623 			/* Do nothing further */
624 			if (proxy) {
625 				g_object_unref (proxy);
626 			}
627 			g_print ("\n");
628 			return EXIT_SUCCESS;
629 		}
630 
631 		g_print ("Press Ctrl+C to end follow of Tracker state\n");
632 
633 		g_signal_connect (manager, "miner-progress",
634 		                  G_CALLBACK (manager_miner_progress_cb), NULL);
635 		g_signal_connect (manager, "miner-paused",
636 		                  G_CALLBACK (manager_miner_paused_cb), NULL);
637 		g_signal_connect (manager, "miner-resumed",
638 		                  G_CALLBACK (manager_miner_resumed_cb), NULL);
639 
640 		initialize_signal_handler ();
641 
642 		miners_progress = g_hash_table_new_full (g_str_hash,
643 		                                         g_str_equal,
644 		                                         (GDestroyNotify) g_free,
645 		                                         (GDestroyNotify) miners_progress_destroy_notify);
646 		miners_status = g_hash_table_new_full (g_str_hash,
647 		                                       g_str_equal,
648 		                                       (GDestroyNotify) g_free,
649 		                                       (GDestroyNotify) g_free);
650 
651 		main_loop = g_main_loop_new (NULL, FALSE);
652 		g_main_loop_run (main_loop);
653 		g_main_loop_unref (main_loop);
654 
655 		g_hash_table_unref (miners_progress);
656 		g_hash_table_unref (miners_status);
657 
658 		if (proxy) {
659 			g_object_unref (proxy);
660 		}
661 
662 		if (manager) {
663 			g_object_unref (manager);
664 		}
665 
666 		return EXIT_SUCCESS;
667 	}
668 
669 	/* All known options have their own exit points */
670 	g_warn_if_reached ();
671 
672 	return EXIT_FAILURE;
673 }
674 
675 GOptionGroup *
676 tracker_control_status_get_option_group (void)
677 {
678 	GOptionGroup *group;
679 
680 	/* Status options */
681 	group = g_option_group_new ("status",
682 	                            _("Status options"),
683 	                            _("Show status options"),
684 	                            NULL,
685 	                            NULL);
686 	g_option_group_add_entries (group, entries);
687 
688 	return group;
689 }