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 }