tracker-0.16.2/src/tracker-control/tracker-control-general.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 <errno.h>
  23 
  24 #include <glib.h>
  25 #include <glib/gi18n.h>
  26 
  27 #ifdef __sun
  28 #include <procfs.h>
  29 #endif
  30 
  31 #include <libtracker-common/tracker-common.h>
  32 #include <libtracker-data/tracker-data.h>
  33 #include <libtracker-miner/tracker-miner.h>
  34 
  35 #include "tracker-control.h"
  36 
  37 #define OPTION_TERM_ALL "all"
  38 #define OPTION_TERM_STORE "store"
  39 #define OPTION_TERM_MINERS "miners"
  40 
  41 typedef enum {
  42 	TERM_NONE,
  43 	TERM_ALL,
  44 	TERM_STORE,
  45 	TERM_MINERS
  46 } TermOption;
  47 
  48 /* Note:
  49  * Every time a new option is added, make sure it is considered in the
  50  * 'GENERAL_OPTIONS_ENABLED' macro below
  51  */
  52 static gboolean list_processes;
  53 static TermOption kill_option = TERM_NONE;
  54 static TermOption terminate_option = TERM_NONE;
  55 static gboolean hard_reset;
  56 static gboolean soft_reset;
  57 static gboolean remove_config;
  58 static gchar *set_log_verbosity;
  59 static gboolean get_log_verbosity;
  60 static gboolean start;
  61 static gchar *backup;
  62 static gchar *restore;
  63 
  64 #define GENERAL_OPTIONS_ENABLED() \
  65 	(list_processes || \
  66 	 kill_option != TERM_NONE || \
  67 	 terminate_option != TERM_NONE || \
  68 	 hard_reset || \
  69 	 soft_reset || \
  70 	 remove_config || \
  71 	 get_log_verbosity || \
  72 	 set_log_verbosity || \
  73 	 start || \
  74 	 backup || \
  75 	 restore)
  76 
  77 static gboolean term_option_arg_func (const gchar  *option_value,
  78                                       const gchar  *value,
  79                                       gpointer      data,
  80                                       GError      **error);
  81 
  82 static GOptionEntry entries[] = {
  83 	{ "list-processes", 'p', 0, G_OPTION_ARG_NONE, &list_processes,
  84 	  N_("List all Tracker processes") },
  85 	{ "kill", 'k', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, term_option_arg_func,
  86 	  N_("Use SIGKILL to stop all matching processes, either \"store\", \"miners\" or \"all\" may be used, no parameter equals \"all\""),
  87 	  N_("APPS") },
  88 	{ "terminate", 't', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, term_option_arg_func,
  89 	  N_("Use SIGTERM to stop all matching processes, either \"store\", \"miners\" or \"all\" may be used, no parameter equals \"all\""),
  90 	  N_("APPS") },
  91 	{ "hard-reset", 'r', 0, G_OPTION_ARG_NONE, &hard_reset,
  92 	  N_("Kill all Tracker processes and remove all databases"),
  93 	  NULL },
  94 	{ "soft-reset", 'e', 0, G_OPTION_ARG_NONE, &soft_reset,
  95 	  N_("Same as --hard-reset but the backup & journal are restored after restart"),
  96 	  NULL },
  97 	{ "remove-config", 'c', 0, G_OPTION_ARG_NONE, &remove_config,
  98 	  N_("Remove all configuration files so they are re-generated on next start"),
  99 	  NULL },
 100 	{ "set-log-verbosity", 0, 0, G_OPTION_ARG_STRING, &set_log_verbosity,
 101 	  N_("Sets the logging verbosity to LEVEL ('debug', 'detailed', 'minimal', 'errors') for all processes"),
 102 	  N_("LEVEL") },
 103 	{ "get-log-verbosity", 0, 0, G_OPTION_ARG_NONE, &get_log_verbosity,
 104 	  N_("Show logging values in terms of log verbosity for each process"),
 105 	  NULL },
 106 	{ "start", 's', 0, G_OPTION_ARG_NONE, &start,
 107 	  N_("Starts miners (which indirectly starts tracker-store too)"),
 108 	  NULL },
 109 	{ "backup", 'b', 0, G_OPTION_ARG_FILENAME, &backup,
 110 	  N_("Backup databases to the file provided"),
 111 	  N_("FILE") },
 112 	{ "restore", 'o', 0, G_OPTION_ARG_FILENAME, &restore,
 113 	  N_("Restore databases from the file provided"),
 114 	  N_("FILE") },
 115 	{ NULL }
 116 };
 117 
 118 gboolean
 119 tracker_control_general_options_enabled (void)
 120 {
 121 	return GENERAL_OPTIONS_ENABLED ();
 122 }
 123 
 124 static GSList *
 125 get_pids (void)
 126 {
 127 	GError *error = NULL;
 128 	GDir *dir;
 129 	GSList *pids = NULL;
 130 	const gchar *name;
 131 
 132 	dir = g_dir_open ("/proc", 0, &error);
 133 	if (error) {
 134 		g_printerr ("%s, %s\n",
 135 		            _("Could not open /proc"),
 136 		            error ? error->message : _("no error given"));
 137 		g_clear_error (&error);
 138 		return NULL;
 139 	}
 140 
 141 	while ((name = g_dir_read_name (dir)) != NULL) {
 142 		gchar c;
 143 		gboolean is_pid = TRUE;
 144 
 145 		for (c = *name; c && c != ':' && is_pid; c++) {
 146 			is_pid &= g_ascii_isdigit (c);
 147 		}
 148 
 149 		if (!is_pid) {
 150 			continue;
 151 		}
 152 
 153 		pids = g_slist_prepend (pids, g_strdup (name));
 154 	}
 155 
 156 	g_dir_close (dir);
 157 
 158 	return g_slist_reverse (pids);
 159 }
 160 
 161 static void
 162 log_handler (const gchar    *domain,
 163              GLogLevelFlags  log_level,
 164              const gchar    *message,
 165              gpointer        user_data)
 166 {
 167 	switch (log_level) {
 168 	case G_LOG_LEVEL_WARNING:
 169 	case G_LOG_LEVEL_CRITICAL:
 170 	case G_LOG_LEVEL_ERROR:
 171 	case G_LOG_FLAG_RECURSION:
 172 	case G_LOG_FLAG_FATAL:
 173 		g_fprintf (stderr, "%s\n", message);
 174 		fflush (stderr);
 175 		break;
 176 	case G_LOG_LEVEL_MESSAGE:
 177 	case G_LOG_LEVEL_INFO:
 178 	case G_LOG_LEVEL_DEBUG:
 179 	case G_LOG_LEVEL_MASK:
 180 	default:
 181 		g_fprintf (stdout, "%s\n", message);
 182 		fflush (stdout);
 183 		break;
 184 	}
 185 }
 186 
 187 static gboolean
 188 crawler_check_file_cb (TrackerCrawler *crawler,
 189                        GFile          *file,
 190                        gpointer        user_data)
 191 {
 192 	const gchar **suffix;
 193 	gchar *path;
 194 	gboolean should_remove;
 195 
 196 	suffix = user_data;
 197 	path = g_file_get_path (file);
 198 
 199 	if (suffix) {
 200 		should_remove = g_str_has_suffix (path, *suffix);
 201 	} else {
 202 		should_remove = TRUE;
 203 	}
 204 
 205 	if (!should_remove) {
 206 		g_free (path);
 207 		return FALSE;
 208 	}
 209 
 210 	/* Remove file */
 211 	if (g_unlink (path) == 0) {
 212 		g_print ("  %s\n", path);
 213 	}
 214 
 215 	g_free (path);
 216 
 217 	return should_remove;
 218 }
 219 
 220 static void
 221 crawler_finished_cb (TrackerCrawler *crawler,
 222                      gboolean        was_interrupted,
 223                      gpointer        user_data)
 224 {
 225 	g_main_loop_quit (user_data);
 226 }
 227 
 228 typedef struct {
 229 	gchar *name;
 230 	GSettings *settings;
 231 	gboolean is_miner;
 232 } ComponentGSettings;
 233 
 234 inline static const gchar *
 235 verbosity_to_string (TrackerVerbosity verbosity)
 236 {
 237         GType type;
 238         GEnumClass *enum_class;
 239         GEnumValue *enum_value;
 240 
 241         type = tracker_verbosity_get_type ();
 242         enum_class = G_ENUM_CLASS (g_type_class_peek (type));
 243         enum_value = g_enum_get_value (enum_class, verbosity);
 244 
 245         if (!enum_value) {
 246                 return "unknown";
 247         }
 248 
 249         return enum_value->value_nick;
 250 }
 251 
 252 inline static void
 253 tracker_gsettings_print_verbosity (GSList   *all,
 254                                    gint      longest,
 255                                    gboolean  miners)
 256 {
 257 	GSList *l;
 258 
 259 	for (l = all; l; l = l->next) {
 260 		ComponentGSettings *c;
 261 		TrackerVerbosity v;
 262 
 263 		c = l->data;
 264 
 265 		if (c->is_miner == miners) {
 266 			continue;
 267 		}
 268 
 269 		v = g_settings_get_enum (c->settings, "verbosity");
 270 
 271 		g_print ("  %-*.*s: %s\n",
 272 		         longest,
 273 		         longest,
 274 		         c->name,
 275 		         verbosity_to_string (v));
 276 	}
 277 }
 278 
 279 static gboolean
 280 tracker_gsettings_set_all (GSList           *all,
 281                            TrackerVerbosity  verbosity)
 282 {
 283 	GSList *l;
 284 	gboolean success = TRUE;
 285 
 286 	for (l = all; l && success; l = l->next) {
 287 		ComponentGSettings *c = l->data;
 288 
 289 		if (!c) {
 290 			continue;
 291 		}
 292 
 293 		success &= g_settings_set_enum (c->settings, "verbosity", verbosity);
 294 		g_settings_apply (c->settings);
 295 	}
 296 
 297 	g_settings_sync ();
 298 
 299 	return success;
 300 }
 301 
 302 static GSList *
 303 tracker_gsettings_get_all (gint *longest_name_length)
 304 {
 305 	typedef struct {
 306 		const gchar *schema;
 307 		const gchar *path;
 308 	} SchemaWithPath;
 309 
 310 	TrackerMinerManager *manager;
 311 	GError *error = NULL;
 312 	GSettings *settings;
 313 	GSList *all = NULL;
 314 	GSList *l;
 315 	GSList *miners_available;
 316 	GSList *valid_schemas = NULL;
 317 	const gchar * const *schema;
 318 	gint len = 0;
 319 	SchemaWithPath components[] = {
 320 		{ "Store", "store" },
 321 		{ "Extract", "extract" },
 322 		{ "Writeback", "writeback" },
 323 		{ 0 }
 324 	};
 325 	SchemaWithPath *swp;
 326 
 327 	/* Don't auto-start the miners here */
 328 	manager = tracker_miner_manager_new_full (FALSE, &error);
 329 	if (!manager) {
 330 		g_printerr (_("Could not get GSettings for miners, manager could not be created, %s"),
 331 		            error ? error->message : "unknown error");
 332 		g_printerr ("\n");
 333 		g_clear_error (&error);
 334 		return NULL;
 335 	}
 336 
 337 	miners_available = tracker_miner_manager_get_available (manager);
 338 
 339 	/* Get valid schemas so we don't try to load invalid ones */
 340 	for (schema = g_settings_list_schemas (); schema && *schema; schema++) {
 341 		if (!g_str_has_prefix (*schema, "org.freedesktop.Tracker.")) {
 342 			continue;
 343 		}
 344 
 345 		valid_schemas = g_slist_prepend (valid_schemas, g_strdup (*schema));
 346 	}
 347 
 348 	/* Store / General */
 349 	for (swp = components; swp && swp->schema; swp++) {
 350 		gchar *schema;
 351 		gchar *path;
 352 
 353 		schema = g_strdup_printf ("org.freedesktop.Tracker.%s", swp->schema);
 354 		path = g_strdup_printf ("/org/freedesktop/tracker/%s/", swp->path);
 355 
 356 		/* If miner doesn't have a schema, no point in getting config */
 357 		if (!tracker_string_in_gslist (schema, valid_schemas)) {
 358 			g_free (path);
 359 			g_free (schema);
 360 			continue;
 361 		}
 362 
 363 		len = MAX (len, strlen (swp->schema));
 364 
 365 		settings = g_settings_new_with_path (schema, path);
 366 		if (settings) {
 367 			ComponentGSettings *c = g_slice_new (ComponentGSettings);
 368 
 369 			c->name = g_strdup (swp->schema);
 370 			c->settings = settings;
 371 			c->is_miner = FALSE;
 372 
 373 			all = g_slist_prepend (all, c);
 374 		}
 375 	}
 376 
 377 	/* Miners */
 378 	for (l = miners_available; l; l = l->next) {
 379 		const gchar *name;
 380 		gchar *schema;
 381 		gchar *name_lowercase;
 382 		gchar *path;
 383 		gchar *miner;
 384 
 385 		miner = l->data;
 386 		if (!miner) {
 387 			continue;
 388 		}
 389 
 390 		name = g_utf8_strrchr (miner, -1, '.');
 391 		if (!name) {
 392 			continue;
 393 		}
 394 
 395 		name++;
 396 		name_lowercase = g_utf8_strdown (name, -1);
 397 
 398 		schema = g_strdup_printf ("org.freedesktop.Tracker.Miner.%s", name);
 399 		path = g_strdup_printf ("/org/freedesktop/tracker/miner/%s/", name_lowercase);
 400 		g_free (name_lowercase);
 401 
 402 		/* If miner doesn't have a schema, no point in getting config */
 403 		if (!tracker_string_in_gslist (schema, valid_schemas)) {
 404 			g_free (path);
 405 			g_free (schema);
 406 			continue;
 407 		}
 408 
 409 		settings = g_settings_new_with_path (schema, path);
 410 		g_free (path);
 411 		g_free (schema);
 412 
 413 		if (settings) {
 414 			ComponentGSettings *c = g_slice_new (ComponentGSettings);
 415 
 416 			c->name = g_strdup (name);
 417 			c->settings = settings;
 418 			c->is_miner = TRUE;
 419 
 420 			all = g_slist_prepend (all, c);
 421 			len = MAX (len, strlen (name));
 422 		}
 423 	}
 424 
 425 	g_slist_foreach (valid_schemas, (GFunc) g_free, NULL);
 426 	g_slist_free (valid_schemas);
 427 	g_slist_foreach (miners_available, (GFunc) g_free, NULL);
 428 	g_slist_free (miners_available);
 429 	g_object_unref (manager);
 430 
 431 	if (longest_name_length) {
 432 		*longest_name_length = len;
 433 	}
 434 
 435 	return g_slist_reverse (all);
 436 }
 437 
 438 static void
 439 tracker_gsettings_free (GSList *all)
 440 {
 441 	GSList *l;
 442 
 443 	/* Clean up */
 444 	for (l = all; l; l = l->next) {
 445 		ComponentGSettings *c = l->data;
 446 
 447 		g_free (c->name);
 448 		g_object_unref (c->settings);
 449 		g_slice_free (ComponentGSettings, c);
 450 	}
 451 }
 452 
 453 static gboolean
 454 term_option_arg_func (const gchar  *option_value,
 455                       const gchar  *value,
 456                       gpointer      data,
 457                       GError      **error)
 458 {
 459 	TermOption option;
 460 
 461 	if (!value) {
 462 		value = OPTION_TERM_ALL;
 463 	}
 464 
 465 	if (strcmp (value, OPTION_TERM_ALL) == 0) {
 466 		option = TERM_ALL;
 467 	} else if (strcmp (value, OPTION_TERM_STORE) == 0) {
 468 		option = TERM_STORE;
 469 	} else if (strcmp (value, OPTION_TERM_MINERS) == 0) {
 470 		option = TERM_MINERS;
 471 	} else {
 472 		g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
 473 		                     "Only one of 'all', 'store' and 'miners' are allowed");
 474 		return FALSE;
 475 	}
 476 
 477 	if (strcmp (option_value, "-k") == 0 ||
 478 	    strcmp (option_value, "--kill") == 0) {
 479 		kill_option = option;
 480 	} else if (strcmp (option_value, "-t") == 0 ||
 481 	           strcmp (option_value, "--terminate") == 0) {
 482 		terminate_option = option;
 483 	}
 484 
 485 	return TRUE;
 486 }
 487 
 488 static gboolean
 489 has_valid_uri_scheme (const gchar *uri)
 490 {
 491 	const gchar *s;
 492 
 493 	s = uri;
 494 
 495 	if (!g_ascii_isalpha (*s)) {
 496 		return FALSE;
 497 	}
 498 
 499 	do {
 500 		s++;
 501 	} while (g_ascii_isalnum (*s) || *s == '+' || *s == '.' || *s == '-');
 502 
 503 	return (*s == ':');
 504 }
 505 
 506 static gchar *
 507 get_uri_from_arg (const gchar *arg)
 508 {
 509 	gchar *uri;
 510 
 511 	/* support both, URIs and local file paths */
 512 	if (has_valid_uri_scheme (arg)) {
 513 		uri = g_strdup (arg);
 514 	} else {
 515 		GFile *file;
 516 
 517 		file = g_file_new_for_commandline_arg (arg);
 518 		uri = g_file_get_uri (file);
 519 		g_object_unref (file);
 520 	}
 521 
 522 	return uri;
 523 }
 524 
 525 static inline guint32
 526 get_uid_for_pid (const gchar  *pid_as_string,
 527                  gchar       **filename)
 528 {
 529 	GFile *f;
 530 	GFileInfo *info;
 531 	GError *error = NULL;
 532 	gchar *fn;
 533 	guint uid;
 534 
 535 #ifdef __sun /* Solaris */
 536 	fn = g_build_filename ("/proc", pid_as_string, "psinfo", NULL);
 537 #else
 538 	fn = g_build_filename ("/proc", pid_as_string, "cmdline", NULL);
 539 #endif
 540 
 541 	f = g_file_new_for_path (fn);
 542 	info = g_file_query_info (f,
 543 	                          G_FILE_ATTRIBUTE_UNIX_UID,
 544 	                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
 545 	                          NULL,
 546 	                          &error);
 547 
 548 	if (error) {
 549 		g_printerr ("Could not stat() file:'%s', %s", fn, error->message);
 550 		g_error_free (error);
 551 		uid = 0;
 552 	} else {
 553 		uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
 554 		g_object_unref (info);
 555 	}
 556 
 557 	if (filename) {
 558 		*filename = fn;
 559 	} else {
 560 		g_free (fn);
 561 	}
 562 
 563 	g_object_unref (f);
 564 
 565 	return uid;
 566 }
 567 
 568 void
 569 tracker_control_general_run_default (void)
 570 {
 571 	/* Enable list processes in the default run */
 572 	list_processes = TRUE;
 573 
 574 	tracker_control_general_run ();
 575 }
 576 
 577 gint
 578 tracker_control_general_run (void)
 579 {
 580 	GError *error = NULL;
 581 	GSList *pids;
 582 	GSList *l;
 583 	gchar *str;
 584 	gpointer verbosity_type_enum_class_pointer = NULL;
 585 	TrackerVerbosity set_log_verbosity_value = TRACKER_VERBOSITY_ERRORS;
 586 
 587 	/* Constraints */
 588 
 589 	if (kill_option != TERM_NONE && terminate_option != TERM_NONE) {
 590 		g_printerr ("%s\n",
 591 		            _("You can not use the --kill and --terminate arguments together"));
 592 		return EXIT_FAILURE;
 593 	}
 594 
 595 	if ((hard_reset || soft_reset) && terminate_option != TERM_NONE) {
 596 		g_printerr ("%s\n",
 597 		            _("You can not use the --terminate with --hard-reset or --soft-reset, --kill is implied"));
 598 		return EXIT_FAILURE;
 599 	}
 600 
 601 	if (hard_reset && soft_reset) {
 602 		g_printerr ("%s\n",
 603 		            _("You can not use the --hard-reset and --soft-reset arguments together"));
 604 		return EXIT_FAILURE;
 605 	}
 606 
 607 	if (get_log_verbosity && set_log_verbosity) {
 608 		g_printerr ("%s\n",
 609 		            _("You can not use the --get-logging and --set-logging arguments together"));
 610 		return EXIT_FAILURE;
 611 	}
 612 
 613 	if (set_log_verbosity) {
 614 		if (g_ascii_strcasecmp (set_log_verbosity, "debug") == 0) {
 615 			set_log_verbosity_value = TRACKER_VERBOSITY_DEBUG;
 616 		} else if (g_ascii_strcasecmp (set_log_verbosity, "detailed") == 0) {
 617 			set_log_verbosity_value = TRACKER_VERBOSITY_DETAILED;
 618 		} else if (g_ascii_strcasecmp (set_log_verbosity, "minimal") == 0) {
 619 			set_log_verbosity_value = TRACKER_VERBOSITY_MINIMAL;
 620 		} else if (g_ascii_strcasecmp (set_log_verbosity, "errors") == 0) {
 621 			set_log_verbosity_value = TRACKER_VERBOSITY_ERRORS;
 622 		} else {
 623 			g_printerr ("%s\n",
 624 			            _("Invalid log verbosity, try 'debug', 'detailed', 'minimal' or 'errors'"));
 625 			return EXIT_FAILURE;
 626 		}
 627 	}
 628 
 629 	if (hard_reset || soft_reset) {
 630 		/* Imply --kill */
 631 		kill_option = TERM_ALL;
 632 	}
 633 
 634 	if (get_log_verbosity || set_log_verbosity) {
 635 		GType etype;
 636 
 637 		/* Since we don't reference this enum anywhere, we do
 638 		 * it here to make sure it exists when we call
 639 		 * g_type_class_peek(). This wouldn't be necessary if
 640 		 * it was a param in a GObject for example.
 641 		 *
 642 		 * This does mean that we are leaking by 1 reference
 643 		 * here and should clean it up, but it doesn't grow so
 644 		 * this is acceptable.
 645 		 */
 646 		etype = tracker_verbosity_get_type ();
 647 		verbosity_type_enum_class_pointer = g_type_class_ref (etype);
 648 	}
 649 
 650 	/* Unless we are stopping processes or listing processes,
 651 	 * don't iterate them.
 652 	 */
 653 	if (kill_option != TERM_NONE ||
 654 	    terminate_option != TERM_NONE ||
 655 	    list_processes) {
 656 		guint32 own_pid;
 657 		guint32 own_uid;
 658 		gchar *own_pid_str;
 659 
 660 		pids = get_pids ();
 661 		str = g_strdup_printf (g_dngettext (NULL,
 662 		                                    "Found %d PID…",
 663 		                                    "Found %d PIDs…",
 664 		                                    g_slist_length (pids)),
 665 		                       g_slist_length (pids));
 666 		g_print ("%s\n", str);
 667 		g_free (str);
 668 
 669 		/* Establish own uid/pid */
 670 		own_pid = (guint32) getpid ();
 671 		own_pid_str = g_strdup_printf ("%d", own_pid);
 672 		own_uid = get_uid_for_pid (own_pid_str, NULL);
 673 		g_free (own_pid_str);
 674 
 675 		for (l = pids; l; l = l->next) {
 676 			GError *error = NULL;
 677 			gchar *filename;
 678 #ifdef __sun /* Solaris */
 679 			psinfo_t psinfo = { 0 };
 680 #endif
 681 			gchar *contents = NULL;
 682 
 683 			gchar **strv;
 684 			guint uid;
 685 
 686 			uid = get_uid_for_pid (l->data, &filename);
 687 
 688 			/* Stat the file and make sure current user == file owner */
 689 			if (uid != own_uid) {
 690 				continue;
 691 			}
 692 
 693 			/* Get contents to determine basename */
 694 			if (!g_file_get_contents (filename, &contents, NULL, &error)) {
 695 				str = g_strdup_printf (_("Could not open '%s'"), filename);
 696 				g_printerr ("%s, %s\n",
 697 				            str,
 698 				            error ? error->message : _("no error given"));
 699 				g_free (str);
 700 				g_clear_error (&error);
 701 				g_free (contents);
 702 				g_free (filename);
 703 
 704 				continue;
 705 			}
 706 #ifdef __sun /* Solaris */
 707 			memcpy (&psinfo, contents, sizeof (psinfo));
 708 
 709 			/* won't work with paths containing spaces :( */
 710 			strv = g_strsplit (psinfo.pr_psargs, " ", 2);
 711 #else
 712 			strv = g_strsplit (contents, "^@", 2);
 713 #endif
 714 			if (strv && strv[0]) {
 715 				gchar *basename;
 716 
 717 				basename = g_path_get_basename (strv[0]);
 718 
 719 				if ((g_str_has_prefix (basename, "tracker") == TRUE ||
 720 				     g_str_has_prefix (basename, "lt-tracker") == TRUE) &&
 721 				    g_str_has_suffix (basename, "-control") == FALSE &&
 722 				    g_str_has_suffix (basename, "-status-icon") == FALSE) {
 723 					pid_t pid;
 724 
 725 					pid = atoi (l->data);
 726 					str = g_strdup_printf (_("Found process ID %d for '%s'"), pid, basename);
 727 					g_print ("%s\n", str);
 728 					g_free (str);
 729 
 730 					if (terminate_option != TERM_NONE) {
 731 						if ((terminate_option == TERM_STORE &&
 732 						     !g_str_has_suffix (basename, "tracker-store")) ||
 733 						    (terminate_option == TERM_MINERS &&
 734 						     !strstr (basename, "tracker-miner"))) {
 735 							continue;
 736 						}
 737 
 738 						if (kill (pid, SIGTERM) == -1) {
 739 							const gchar *errstr = g_strerror (errno);
 740 
 741 							str = g_strdup_printf (_("Could not terminate process %d"), pid);
 742 							g_printerr ("  %s, %s\n",
 743 							            str,
 744 							            errstr ? errstr : _("no error given"));
 745 							g_free (str);
 746 						} else {
 747 							str = g_strdup_printf (_("Terminated process %d"), pid);
 748 							g_print ("  %s\n", str);
 749 							g_free (str);
 750 						}
 751 					} else if (kill_option != TERM_NONE) {
 752 						if ((kill_option == TERM_STORE &&
 753 						     !g_str_has_suffix (basename, "tracker-store")) ||
 754 						    (kill_option == TERM_MINERS &&
 755 						     !strstr (basename, "tracker-miner"))) {
 756 							continue;
 757 						}
 758 
 759 						if (kill (pid, SIGKILL) == -1) {
 760 							const gchar *errstr = g_strerror (errno);
 761 
 762 							str = g_strdup_printf (_("Could not kill process %d"), pid);
 763 							g_printerr ("  %s, %s\n",
 764 							            str,
 765 							            errstr ? errstr : _("no error given"));
 766 							g_free (str);
 767 						} else {
 768 							str = g_strdup_printf (_("Killed process %d"), pid);
 769 							g_print ("  %s\n", str);
 770 							g_free (str);
 771 						}
 772 					}
 773 				}
 774 
 775 				g_free (basename);
 776 			}
 777 
 778 			g_strfreev (strv);
 779 			g_free (contents);
 780 			g_free (filename);
 781 		}
 782 
 783 		g_slist_foreach (pids, (GFunc) g_free, NULL);
 784 		g_slist_free (pids);
 785 
 786 		/* If we just wanted to list processes, all done */
 787 		if (list_processes &&
 788 		    terminate_option == TERM_NONE &&
 789 		    kill_option == TERM_NONE) {
 790 			return EXIT_SUCCESS;
 791 		}
 792 	}
 793 
 794 	if (hard_reset || soft_reset) {
 795 		guint log_handler_id;
 796 #ifndef DISABLE_JOURNAL
 797 		gchar *rotate_to;
 798 		TrackerDBConfig *db_config;
 799 		gsize chunk_size;
 800 		gint chunk_size_mb;
 801 #endif /* DISABLE_JOURNAL */
 802 
 803 		/* Set log handler for library messages */
 804 		log_handler_id = g_log_set_handler (NULL,
 805 		                                    G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,
 806 		                                    log_handler,
 807 		                                    NULL);
 808 
 809 		g_log_set_default_handler (log_handler, NULL);
 810 
 811 #ifndef DISABLE_JOURNAL
 812 		db_config = tracker_db_config_new ();
 813 
 814 		chunk_size_mb = tracker_db_config_get_journal_chunk_size (db_config);
 815 		chunk_size = (gsize) ((gsize) chunk_size_mb * (gsize) 1024 * (gsize) 1024);
 816 		rotate_to = tracker_db_config_get_journal_rotate_destination (db_config);
 817 
 818 		/* This call is needed to set the journal's filename */
 819 		tracker_db_journal_set_rotating ((chunk_size_mb != -1),
 820 		                                 chunk_size, rotate_to);
 821 
 822 		g_free (rotate_to);
 823 		g_object_unref (db_config);
 824 
 825 #endif /* DISABLE_JOURNAL */
 826 
 827 		/* Clean up (select_cache_size and update_cache_size don't matter here) */
 828 		if (!tracker_db_manager_init (TRACKER_DB_MANAGER_REMOVE_ALL,
 829 		                              NULL,
 830 		                              FALSE,
 831 		                              FALSE,
 832 		                              100,
 833 		                              100,
 834 		                              NULL,
 835 		                              NULL,
 836 		                              NULL,
 837 		                              &error)) {
 838 
 839 			g_message ("Error initializing database: %s", error->message);
 840 			g_free (error);
 841 
 842 			return EXIT_FAILURE;
 843 		}
 844 #ifndef DISABLE_JOURNAL
 845 		tracker_db_journal_init (NULL, FALSE, NULL);
 846 #endif /* DISABLE_JOURNAL */
 847 
 848 		tracker_db_manager_remove_all (hard_reset);
 849 		tracker_db_manager_shutdown ();
 850 #ifndef DISABLE_JOURNAL
 851 		tracker_db_journal_shutdown (NULL);
 852 #endif /* DISABLE_JOURNAL */
 853 
 854 		/* Unset log handler */
 855 		g_log_remove_handler (NULL, log_handler_id);
 856 	}
 857 
 858 	if (remove_config) {
 859 		GMainLoop *main_loop;
 860 		GFile *file;
 861 		TrackerCrawler *crawler;
 862 		const gchar *suffix = ".cfg";
 863 		const gchar *home_conf_dir;
 864 		gchar *path;
 865 		GSList *all, *l;
 866 
 867 		crawler = tracker_crawler_new ();
 868 		main_loop = g_main_loop_new (NULL, FALSE);
 869 
 870 		g_signal_connect (crawler, "check-file",
 871 		                  G_CALLBACK (crawler_check_file_cb),
 872 		                  &suffix);
 873 		g_signal_connect (crawler, "finished",
 874 		                  G_CALLBACK (crawler_finished_cb),
 875 		                  main_loop);
 876 
 877 		/* Go through service files */
 878 
 879 		/* Check the default XDG_DATA_HOME location */
 880 		home_conf_dir = g_getenv ("XDG_CONFIG_HOME");
 881 
 882 		if (home_conf_dir && tracker_path_has_write_access_or_was_created (home_conf_dir)) {
 883 			path = g_build_path (G_DIR_SEPARATOR_S, home_conf_dir, "tracker", NULL);
 884 		} else {
 885 			home_conf_dir = g_getenv ("HOME");
 886 
 887 			if (!home_conf_dir || !tracker_path_has_write_access_or_was_created (home_conf_dir)) {
 888 				home_conf_dir = g_get_home_dir ();
 889 			}
 890 			path = g_build_path (G_DIR_SEPARATOR_S, home_conf_dir, ".config", "tracker", NULL);
 891 		}
 892 
 893 		file = g_file_new_for_path (path);
 894 		g_free (path);
 895 
 896 		g_print ("%s\n", _("Removing configuration files…"));
 897 
 898 		tracker_crawler_start (crawler, file, FALSE);
 899 		g_object_unref (file);
 900 
 901 		g_main_loop_run (main_loop);
 902 		g_object_unref (crawler);
 903 
 904 		g_print ("%s\n", _("Resetting existing configuration…"));
 905 
 906 		all = tracker_gsettings_get_all (NULL);
 907 
 908 		if (!all) {
 909 			return EXIT_FAILURE;
 910 		}
 911 
 912 		for (l = all; l; l = l->next) {
 913 			ComponentGSettings *c = l->data;
 914 			gchar **keys, **p;
 915 
 916 			if (!c) {
 917 				continue;
 918 			}
 919 
 920 			g_print ("  %s\n", c->name);
 921 
 922 			keys = g_settings_list_keys (c->settings);
 923 			for (p = keys; p && *p; p++) {
 924 				g_print ("    %s\n", *p);
 925 				g_settings_reset (c->settings, *p);
 926 			}
 927 
 928 			if (keys) {
 929 				g_strfreev (keys);
 930 			}
 931 
 932 			g_settings_apply (c->settings);
 933 		}
 934 
 935 		g_settings_sync ();
 936 
 937 		tracker_gsettings_free (all);
 938 	}
 939 
 940 	/* Deal with logging changes AFTER the config may have been
 941 	 * reset, this way users can actually use --remove-config with
 942 	 * the --set-logging switch.
 943 	 */
 944 	if (get_log_verbosity) {
 945 		GSList *all;
 946 		gint longest = 0;
 947 
 948 		all = tracker_gsettings_get_all (&longest);
 949 
 950 		if (!all) {
 951 			return EXIT_FAILURE;
 952 		}
 953 
 954 		g_print ("%s:\n", _("Components"));
 955 		tracker_gsettings_print_verbosity (all, longest, TRUE);
 956 		g_print ("\n");
 957 
 958 		/* Miners */
 959 		g_print ("%s (%s):\n",
 960 		         _("Miners"),
 961 		         _("Only those with config listed"));
 962 		tracker_gsettings_print_verbosity (all, longest, FALSE);
 963 		g_print ("\n");
 964 
 965 		tracker_gsettings_free (all);
 966 	}
 967 
 968 	if (set_log_verbosity) {
 969 		GSList *all;
 970 		gchar *str;
 971 		gint longest = 0;
 972 
 973 		all = tracker_gsettings_get_all (&longest);
 974 
 975 		if (!all) {
 976 			return EXIT_FAILURE;
 977 		}
 978 
 979 		str = g_strdup_printf (_("Setting log verbosity for all components to '%s'…"), set_log_verbosity);
 980 		g_print ("%s\n", str);
 981 		g_print ("\n");
 982 		g_free (str);
 983 
 984 		tracker_gsettings_set_all (all, set_log_verbosity_value);
 985 		tracker_gsettings_free (all);
 986 
 987 		/* We free to make sure we get new settings and that
 988 		 * they're saved properly.
 989 		 */
 990 		all = tracker_gsettings_get_all (&longest);
 991 
 992 		if (!all) {
 993 			return EXIT_FAILURE;
 994 		}
 995 
 996 		g_print ("%s:\n", _("Components"));
 997 		tracker_gsettings_print_verbosity (all, longest, TRUE);
 998 		g_print ("\n");
 999 
1000 		/* Miners */
1001 		g_print ("%s (%s):\n",
1002 		         _("Miners"),
1003 		         _("Only those with config listed"));
1004 		tracker_gsettings_print_verbosity (all, longest, FALSE);
1005 		g_print ("\n");
1006 
1007 		tracker_gsettings_free (all);
1008 	}
1009 
1010 	if (verbosity_type_enum_class_pointer) {
1011 		g_type_class_unref (verbosity_type_enum_class_pointer);
1012 	}
1013 
1014 	if (start) {
1015 		TrackerMinerManager *manager;
1016 		GSList *miners, *l;
1017 
1018 		if (hard_reset || soft_reset) {
1019 			g_print ("%s\n", _("Waiting one second before starting miners…"));
1020 
1021 			/* Give a second's grace to avoid race conditions */
1022 			g_usleep (G_USEC_PER_SEC);
1023 		}
1024 
1025 		g_print ("%s\n", _("Starting miners…"));
1026 
1027 
1028 		/* Auto-start the miners here */
1029 		manager = tracker_miner_manager_new_full (TRUE, &error);
1030 		if (!manager) {
1031 			g_printerr (_("Could not start miners, manager could not be created, %s"),
1032 			            error ? error->message : "unknown error");
1033 			g_printerr ("\n");
1034 			g_clear_error (&error);
1035 			return EXIT_FAILURE;
1036 		}
1037 
1038 		miners = tracker_miner_manager_get_available (manager);
1039 
1040 		/* Get the status of all miners, this will start all
1041 		 * miners not already running.
1042 		 */
1043 		for (l = miners; l; l = l->next) {
1044 			const gchar *display_name;
1045 			gdouble progress = 0.0;
1046 
1047 			display_name = tracker_miner_manager_get_display_name (manager, l->data);
1048 
1049 			if (!tracker_miner_manager_get_status (manager,
1050 			                                       l->data,
1051 			                                       NULL,
1052 			                                       &progress,
1053 			                                       NULL)) {
1054 				g_printerr ("  ✗ %s (%s)\n",
1055 				            display_name,
1056 				            _("perhaps a disabled plugin?"));
1057 			} else {
1058 				g_print ("  ✓ %s\n",
1059 				         display_name);
1060 			}
1061 
1062 			g_free (l->data);
1063 		}
1064 
1065 		g_slist_free (miners);
1066 		g_object_unref (manager);
1067 	}
1068 
1069 	if (backup) {
1070 		GDBusConnection *connection;
1071 		GDBusProxy *proxy;
1072 		GError *error = NULL;
1073 		GVariant *v;
1074 		gchar *uri;
1075 
1076 		uri = get_uri_from_arg (backup);
1077 
1078 		g_print ("%s\n", _("Backing up database"));
1079 		g_print ("  %s\n", uri);
1080 
1081 		connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1082 
1083 		if (!connection) {
1084 			g_critical ("Could not connect to the D-Bus session bus, %s",
1085 			            error ? error->message : "no error given.");
1086 			g_clear_error (&error);
1087 			g_free (uri);
1088 
1089 			return EXIT_FAILURE;
1090 		}
1091 
1092 		proxy = g_dbus_proxy_new_sync (connection,
1093 		                               G_DBUS_PROXY_FLAGS_NONE,
1094 		                               NULL,
1095 		                               "org.freedesktop.Tracker1",
1096 		                               "/org/freedesktop/Tracker1/Backup",
1097 		                               "org.freedesktop.Tracker1.Backup",
1098 		                               NULL,
1099 		                               &error);
1100 
1101 		if (error) {
1102 			g_critical ("Could not create proxy on the D-Bus session bus, %s",
1103 			            error ? error->message : "no error given.");
1104 			g_clear_error (&error);
1105 			g_free (uri);
1106 
1107 			return EXIT_FAILURE;
1108 		}
1109 
1110 		/* Backup/Restore can take some time */
1111 		g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);
1112 
1113 		v = g_dbus_proxy_call_sync (proxy,
1114 		                            "Save",
1115 		                            g_variant_new ("(s)", uri),
1116 		                            G_DBUS_CALL_FLAGS_NONE,
1117 		                            -1,
1118 		                            NULL,
1119 		                            &error);
1120 
1121 		if (proxy) {
1122 			g_object_unref (proxy);
1123 		}
1124 
1125 		if (error) {
1126 			g_critical ("Could not backup database, %s",
1127 			            error ? error->message : "no error given.");
1128 			g_clear_error (&error);
1129 			g_free (uri);
1130 
1131 			return EXIT_FAILURE;
1132 		}
1133 
1134 		if (v) {
1135 			g_variant_unref (v);
1136 		}
1137 
1138 		g_free (uri);
1139 	}
1140 
1141 	if (restore) {
1142 		GDBusConnection *connection;
1143 		GDBusProxy *proxy;
1144 		GError *error = NULL;
1145 		GVariant *v;
1146 		gchar *uri;
1147 
1148 		uri = get_uri_from_arg (restore);
1149 
1150 		g_print ("%s\n", _("Restoring database from backup"));
1151 		g_print ("  %s\n", uri);
1152 
1153 		connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1154 
1155 		if (!connection) {
1156 			g_critical ("Could not connect to the D-Bus session bus, %s",
1157 			            error ? error->message : "no error given.");
1158 			g_clear_error (&error);
1159 			g_free (uri);
1160 
1161 			return EXIT_FAILURE;
1162 		}
1163 
1164 		proxy = g_dbus_proxy_new_sync (connection,
1165 		                               G_DBUS_PROXY_FLAGS_NONE,
1166 		                               NULL,
1167 		                               "org.freedesktop.Tracker1",
1168 		                               "/org/freedesktop/Tracker1/Backup",
1169 		                               "org.freedesktop.Tracker1.Backup",
1170 		                               NULL,
1171 		                               &error);
1172 
1173 		if (error) {
1174 			g_critical ("Could not create proxy on the D-Bus session bus, %s",
1175 			            error ? error->message : "no error given.");
1176 			g_clear_error (&error);
1177 			g_free (uri);
1178 
1179 			return EXIT_FAILURE;
1180 		}
1181 
1182 		/* Backup/Restore can take some time */
1183 		g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);
1184 
1185 		v = g_dbus_proxy_call_sync (proxy,
1186 		                            "Restore",
1187 		                            g_variant_new ("(s)", uri),
1188 		                            G_DBUS_CALL_FLAGS_NONE,
1189 		                            -1,
1190 		                            NULL,
1191 		                            &error);
1192 
1193 		if (proxy) {
1194 			g_object_unref (proxy);
1195 		}
1196 
1197 		if (error) {
1198 			g_critical ("Could not restore database, %s",
1199 			            error ? error->message : "no error given.");
1200 			g_clear_error (&error);
1201 			g_free (uri);
1202 
1203 			return EXIT_FAILURE;
1204 		}
1205 
1206 		if (v) {
1207 			g_variant_unref (v);
1208 		}
1209 
1210 		g_free (uri);
1211 	}
1212 
1213 	return EXIT_SUCCESS;
1214 }
1215 
1216 GOptionGroup *
1217 tracker_control_general_get_option_group (void)
1218 {
1219 	GOptionGroup *group;
1220 
1221 	/* Status options */
1222 	group = g_option_group_new ("general",
1223 	                            _("General options"),
1224 	                            _("Show general options"),
1225 	                            NULL,
1226 	                            NULL);
1227 	g_option_group_add_entries (group, entries);
1228 
1229 	return group;
1230 }