1 /*
2 * Copyright (C) 2014, Lanedo <martyn@lanedo.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 <stdlib.h>
23 #include <stdio.h>
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <glib/gprintf.h>
28 #include <gio/gio.h>
29
30 #include <libtracker-common/tracker-common.h>
31 #include <libtracker-data/tracker-data.h>
32 #include <libtracker-control/tracker-control.h>
33
34 #include "tracker-reset.h"
35 #include "tracker-daemon.h"
36 #include "tracker-process.h"
37 #include "tracker-config.h"
38 #include "tracker-color.h"
39
40 static gboolean hard_reset;
41 static gboolean soft_reset;
42 static gboolean remove_config;
43 static gchar *filename = NULL;
44
45 #define RESET_OPTIONS_ENABLED() \
46 (hard_reset || \
47 soft_reset || \
48 remove_config || \
49 filename)
50
51 static GOptionEntry entries[] = {
52 { "hard", 'r', 0, G_OPTION_ARG_NONE, &hard_reset,
53 N_("Kill all Tracker processes and remove all databases"),
54 NULL },
55 { "soft", 'e', 0, G_OPTION_ARG_NONE, &soft_reset,
56 N_("Same as --hard but the backup & journal are restored after restart"),
57 NULL },
58 { "config", 'c', 0, G_OPTION_ARG_NONE, &remove_config,
59 N_("Remove all configuration files so they are re-generated on next start"),
60 NULL },
61 { "file", 'f', 0, G_OPTION_ARG_FILENAME, &filename,
62 N_("Erase indexed information about a file, works recursively for directories"),
63 N_("FILE") },
64 { NULL }
65 };
66
67 static void
log_handler(const gchar * domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)68 log_handler (const gchar *domain,
69 GLogLevelFlags log_level,
70 const gchar *message,
71 gpointer user_data)
72 {
73 switch (log_level) {
74 case G_LOG_LEVEL_WARNING:
75 case G_LOG_LEVEL_CRITICAL:
76 case G_LOG_LEVEL_ERROR:
77 case G_LOG_FLAG_RECURSION:
78 case G_LOG_FLAG_FATAL:
79 g_fprintf (stderr, "%s\n", message);
80 fflush (stderr);
81 break;
82 case G_LOG_LEVEL_MESSAGE:
83 case G_LOG_LEVEL_INFO:
84 case G_LOG_LEVEL_DEBUG:
85 case G_LOG_LEVEL_MASK:
86 default:
87 g_fprintf (stdout, "%s\n", message);
88 fflush (stdout);
89 break;
90 }
91 }
92
93 static int
delete_info_recursively(GFile * file)94 delete_info_recursively (GFile *file)
95 {
96 TrackerSparqlConnection *connection;
97 TrackerMinerManager *miner_manager;
98 TrackerSparqlCursor *cursor;
99 gchar *query, *uri;
100 GError *error = NULL;
101
102 connection = tracker_sparql_connection_get (NULL, &error);
103
104 if (error)
105 goto error;
106
107 uri = g_file_get_uri (file);
108
109 /* First, query whether the item exists */
110 query = g_strdup_printf ("SELECT ?u { ?u nie:url '%s' }", uri);
111 cursor = tracker_sparql_connection_query (connection, query,
112 NULL, &error);
113
114 /* If the item doesn't exist, bail out. */
115 if (!cursor || !tracker_sparql_cursor_next (cursor, NULL, &error)) {
116 g_clear_object (&cursor);
117
118 if (error)
119 goto error;
120
121 return EXIT_SUCCESS;
122 }
123
124 g_object_unref (cursor);
125
126 /* Now, delete the element recursively */
127 g_print ("%s\n", _("Deleting…"));
128 query = g_strdup_printf ("DELETE { "
129 " ?f a rdfs:Resource . "
130 " ?ie a rdfs:Resource "
131 "} WHERE {"
132 " ?f nie:url ?url . "
133 " ?ie nie:isStoredAs ?f . "
134 " FILTER (?url = '%s' ||"
135 " STRSTARTS (?url, '%s/'))"
136 "}", uri, uri);
137 g_free (uri);
138
139 tracker_sparql_connection_update (connection, query,
140 G_PRIORITY_DEFAULT, NULL, &error);
141 g_free (query);
142
143 if (error)
144 goto error;
145
146 g_object_unref (connection);
147
148 g_print ("%s\n", _("The indexed data for this file has been deleted "
149 "and will be reindexed again."));
150
151 /* Request reindexing of this data, it was previously in the store. */
152 miner_manager = tracker_miner_manager_new_full (FALSE, NULL);
153 tracker_miner_manager_index_file (miner_manager, file, NULL, &error);
154 g_object_unref (miner_manager);
155
156 if (error)
157 goto error;
158
159 return EXIT_SUCCESS;
160
161 error:
162 g_warning ("%s", error->message);
163 g_error_free (error);
164 return EXIT_FAILURE;
165 }
166
167 static gint
reset_run(void)168 reset_run (void)
169 {
170 GError *error = NULL;
171
172 if (hard_reset && soft_reset) {
173 g_printerr ("%s\n",
174 /* TRANSLATORS: --hard and --soft are commandline arguments */
175 _("You can not use the --hard and --soft arguments together"));
176 return EXIT_FAILURE;
177 }
178
179 if (hard_reset || soft_reset) {
180 gchar response[100] = { 0 };
181
182 g_print (CRIT_BEGIN "%s" CRIT_END "\n%s\n\n%s %s: ",
183 _("CAUTION: This process may irreversibly delete data."),
184 _("Although most content indexed by Tracker can "
185 "be safely reindexed, it can’t be assured that "
186 "this is the case for all data. Be aware that "
187 "you may be incurring in a data loss situation, "
188 "proceed at your own risk."),
189 _("Are you sure you want to proceed?"),
190 /* TRANSLATORS: This is to be displayed on command line output */
191 _("[y|N]"));
192
193 fgets (response, 100, stdin);
194 response[strlen (response) - 1] = '\0';
195
196 /* TRANSLATORS: this is our test for a [y|N] question in the command line.
197 * A partial or full match will be considered an affirmative answer,
198 * it is intentionally lowercase, so please keep it like this.
199 */
200 if (!response[0] || !g_str_has_prefix (_("yes"), response)) {
201 return EXIT_FAILURE;
202 }
203 }
204
205 if (filename) {
206 GFile *file;
207 gint retval;
208
209 file = g_file_new_for_commandline_arg (filename);
210 retval = delete_info_recursively (file);
211 g_object_unref (file);
212 return retval;
213 }
214
215 /* KILL processes first... */
216 if (hard_reset || soft_reset) {
217 tracker_process_stop (TRACKER_PROCESS_TYPE_NONE, TRACKER_PROCESS_TYPE_ALL);
218 }
219
220 if (hard_reset || soft_reset) {
221 guint log_handler_id;
222 GFile *cache_location, *data_location;
223 gchar *dir;
224 TrackerDBManager *db_manager;
225 #ifndef DISABLE_JOURNAL
226 gchar *rotate_to;
227 TrackerDBConfig *db_config;
228 gsize chunk_size;
229 gint chunk_size_mb;
230 TrackerDBJournal *journal_writer;
231 #endif /* DISABLE_JOURNAL */
232
233 dir = g_build_filename (g_get_user_cache_dir (), "tracker", NULL);
234 cache_location = g_file_new_for_path (dir);
235 g_free (dir);
236
237 dir = g_build_filename (g_get_user_data_dir (), "tracker", "data", NULL);
238 data_location = g_file_new_for_path (dir);
239 g_free (dir);
240
241 /* Set log handler for library messages */
242 log_handler_id = g_log_set_handler (NULL,
243 G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,
244 log_handler,
245 NULL);
246
247 g_log_set_default_handler (log_handler, NULL);
248
249 #ifndef DISABLE_JOURNAL
250 db_config = tracker_db_config_new ();
251
252 chunk_size_mb = tracker_db_config_get_journal_chunk_size (db_config);
253 chunk_size = (gsize) ((gsize) chunk_size_mb * (gsize) 1024 * (gsize) 1024);
254 rotate_to = tracker_db_config_get_journal_rotate_destination (db_config);
255
256 /* This call is needed to set the journal's filename */
257 tracker_db_journal_set_rotating ((chunk_size_mb != -1),
258 chunk_size, rotate_to);
259
260 g_free (rotate_to);
261 g_object_unref (db_config);
262
263 #endif /* DISABLE_JOURNAL */
264
265 /* Clean up (select_cache_size and update_cache_size don't matter here) */
266 db_manager = tracker_db_manager_new (TRACKER_DB_MANAGER_REMOVE_ALL,
267 cache_location, data_location,
268 NULL,
269 FALSE,
270 FALSE,
271 100,
272 100,
273 NULL,
274 NULL,
275 NULL,
276 NULL,
277 NULL,
278 &error);
279
280 if (!db_manager) {
281 g_message ("Error initializing database: %s", error->message);
282 g_free (error);
283
284 return EXIT_FAILURE;
285 }
286
287 tracker_db_manager_remove_all (db_manager);
288 #ifndef DISABLE_JOURNAL
289 journal_writer = tracker_db_journal_new (data_location, FALSE, NULL);
290 tracker_db_journal_remove (journal_writer);
291 #endif /* DISABLE_JOURNAL */
292
293 tracker_db_manager_remove_version_file (db_manager);
294 tracker_db_manager_free (db_manager);
295
296 /* Unset log handler */
297 g_log_remove_handler (NULL, log_handler_id);
298
299 if (!remove_config) {
300 return EXIT_SUCCESS;
301 }
302 }
303
304 if (remove_config) {
305 GSList *all, *l;
306
307 g_print ("%s\n", _("Resetting existing configuration…"));
308
309 all = tracker_gsettings_get_all (NULL);
310
311 if (!all) {
312 return EXIT_FAILURE;
313 }
314
315 for (l = all; l; l = l->next) {
316 ComponentGSettings *c = l->data;
317 gchar **keys, **p;
318
319 if (!c) {
320 continue;
321 }
322
323 g_print (" %s\n", c->name);
324
325 keys = g_settings_schema_list_keys (c->schema);
326 for (p = keys; p && *p; p++) {
327 g_print (" %s\n", *p);
328 g_settings_reset (c->settings, *p);
329 }
330
331 if (keys) {
332 g_strfreev (keys);
333 }
334
335 g_settings_apply (c->settings);
336 }
337
338 g_settings_sync ();
339
340 tracker_gsettings_free (all);
341
342 return EXIT_SUCCESS;
343 }
344
345 /* All known options have their own exit points */
346 g_warn_if_reached ();
347
348 return EXIT_FAILURE;
349 }
350
351 static int
reset_run_default(void)352 reset_run_default (void)
353 {
354 GOptionContext *context;
355 gchar *help;
356
357 context = g_option_context_new (NULL);
358 g_option_context_add_main_entries (context, entries, NULL);
359 help = g_option_context_get_help (context, FALSE, NULL);
360 g_option_context_free (context);
361 g_printerr ("%s\n", help);
362 g_free (help);
363
364 return EXIT_FAILURE;
365 }
366
367 static gboolean
reset_options_enabled(void)368 reset_options_enabled (void)
369 {
370 return RESET_OPTIONS_ENABLED ();
371 }
372
373 int
tracker_reset(int argc,const char ** argv)374 tracker_reset (int argc, const char **argv)
375 {
376 GOptionContext *context;
377 GError *error = NULL;
378
379 context = g_option_context_new (NULL);
380 g_option_context_add_main_entries (context, entries, NULL);
381
382 argv[0] = "tracker reset";
383
384 if (!g_option_context_parse (context, &argc, (char***) &argv, &error)) {
385 g_printerr ("%s, %s\n", _("Unrecognized options"), error->message);
386 g_error_free (error);
387 g_option_context_free (context);
388 return EXIT_FAILURE;
389 }
390
391 g_option_context_free (context);
392
393 if (reset_options_enabled ()) {
394 return reset_run ();
395 }
396
397 return reset_run_default ();
398 }
399