1 /*
2  * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19 
20 #include "config-miners.h"
21 
22 #include <sys/time.h>
23 #include <sys/resource.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31 
32 #include <glib/gstdio.h>
33 
34 #include "tracker-log.h"
35 #include "tracker-file-utils.h"
36 
37 static gboolean  initialized;
38 static FILE     *fd;
39 static gint      verbosity;
40 static guint     log_handler_id;
41 static gboolean  use_log_files;
42 static GMutex    mutex;
43 
44 static inline void
log_output(const gchar * domain,GLogLevelFlags log_level,const gchar * message)45 log_output (const gchar    *domain,
46             GLogLevelFlags  log_level,
47             const gchar    *message)
48 {
49 	time_t        now;
50 	gchar         time_str[64];
51 	gchar        *output;
52 	struct tm    *local_time;
53 	const gchar  *log_level_str;
54 	static gsize  size = 0;
55 
56 	g_return_if_fail (initialized == TRUE);
57 	g_return_if_fail (message != NULL && message[0] != '\0');
58 
59 	/* Ensure file logging is thread safe */
60 	g_mutex_lock (&mutex);
61 
62 	/* Check log size, 10MiB limit */
63 	if (size > (10 << 20) && fd) {
64 		rewind (fd);
65 
66 		if (ftruncate (fileno (fd), 0) != 0) {
67 			/* FIXME: What should we do if this fails? */
68 		}
69 
70 		size = 0;
71 	}
72 
73 	now = time ((time_t *) NULL);
74 	local_time = localtime (&now);
75 	strftime (time_str, 64, "%d %b %Y, %H:%M:%S:", local_time);
76 
77 	switch (log_level) {
78 	case G_LOG_LEVEL_WARNING:
79 		log_level_str = "-Warning **";
80 		break;
81 
82 	case G_LOG_LEVEL_CRITICAL:
83 		log_level_str = "-Critical **";
84 		break;
85 
86 	case G_LOG_LEVEL_ERROR:
87 		log_level_str = "-Error **";
88 		break;
89 	case G_LOG_FLAG_RECURSION:
90 	case G_LOG_FLAG_FATAL:
91 	case G_LOG_LEVEL_MESSAGE:
92 	case G_LOG_LEVEL_INFO:
93 	case G_LOG_LEVEL_DEBUG:
94 	case G_LOG_LEVEL_MASK:
95 	default:
96 		log_level_str = NULL;
97 		break;
98 	}
99 
100 	output = g_strdup_printf ("%s%s %s%s: %s",
101 	                          log_level_str ? "\n" : "",
102 	                          time_str,
103 	                          domain,
104 	                          log_level_str ? log_level_str : "",
105 	                          message);
106 
107 	if (G_UNLIKELY (fd == NULL)) {
108 		FILE *f;
109 
110 		if (log_level == G_LOG_LEVEL_WARNING ||
111 		    log_level == G_LOG_LEVEL_CRITICAL ||
112 		    log_level == G_LOG_LEVEL_ERROR) {
113 			f = stderr;
114 		} else {
115 			f = stdout;
116 		}
117 
118 		g_fprintf (f, "%s\n", output);
119 		fflush (f);
120 	} else {
121 		size += g_fprintf (fd, "%s\n", output);
122 		fflush (fd);
123 	}
124 
125 	g_free (output);
126 
127 	g_mutex_unlock (&mutex);
128 }
129 
130 static void
tracker_log_handler(const gchar * domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)131 tracker_log_handler (const gchar    *domain,
132                      GLogLevelFlags  log_level,
133                      const gchar    *message,
134                      gpointer        user_data)
135 {
136 	/* Unless enabled, we don't log to file by default */
137 	if (use_log_files) {
138 		log_output (domain, log_level, message);
139 	}
140 
141 	/* Now show the message through stdout/stderr as usual */
142 	g_log_default_handler (domain, log_level, message, user_data);
143 }
144 
145 static void
hide_log_handler(const gchar * domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)146 hide_log_handler (const gchar    *domain,
147                   GLogLevelFlags  log_level,
148                   const gchar    *message,
149                   gpointer        user_data)
150 {
151 	/* do nothing */
152 }
153 
154 gboolean
tracker_log_init(gint this_verbosity,gchar ** used_filename)155 tracker_log_init (gint    this_verbosity,
156                   gchar **used_filename)
157 {
158 	const gchar *env_use_log_files;
159 	const gchar *env_verbosity;
160 	GLogLevelFlags hide_levels = 0;
161 
162 	if (initialized) {
163 		return TRUE;
164 	}
165 
166 	env_use_log_files = g_getenv ("TRACKER_USE_LOG_FILES");
167 	if (env_use_log_files != NULL) {
168 		/* When set we use:
169 		 *   ~/.local/share/Tracker/
170 		 * Otherwise, we either of the following:
171 		 *   ~/.xsession-errors
172 		 *   ~/.cache/gdm/session.log
173 		 *   systemd journal
174 		 * Depending on the system.
175 		 */
176 		use_log_files = TRUE;
177 	}
178 
179 	env_verbosity = g_getenv ("TRACKER_VERBOSITY");
180 	if (env_verbosity != NULL) {
181 		this_verbosity = atoi (env_verbosity);
182 	} else {
183 		gchar *verbosity_string;
184 
185 		/* make sure libtracker-sparql uses the same verbosity setting */
186 
187 		verbosity_string = g_strdup_printf ("%d", this_verbosity);
188 		g_setenv ("TRACKER_VERBOSITY", verbosity_string, FALSE);
189 		g_free (verbosity_string);
190 	}
191 
192 	/* If we have debug enabled, we imply G_MESSAGES_DEBUG or we
193 	 * see nothing, this came in since GLib 2.32.
194 	 */
195 	if (this_verbosity > 1) {
196 		g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
197 	}
198 
199 	if (use_log_files) {
200 		gchar *basename;
201 		gchar *filename;
202 
203 		basename = g_strdup_printf ("%s.log", g_get_application_name ());
204 		filename = g_build_filename (g_get_user_data_dir (),
205 		                             "tracker",
206 		                             basename,
207 		                             NULL);
208 		g_free (basename);
209 
210 		/* Open file */
211 		fd = g_fopen (filename, "a");
212 		if (!fd) {
213 			const gchar *error_string;
214 
215 			error_string = g_strerror (errno);
216 			g_fprintf (stderr,
217 			           "Could not open log:'%s', %s\n",
218 			           filename,
219 			           error_string);
220 			g_fprintf (stderr,
221 			           "All logging will go to stderr\n");
222 
223 			use_log_files = TRUE;
224 		}
225 
226 		if (used_filename) {
227 			*used_filename = filename;
228 		} else {
229 			g_free (filename);
230 		}
231 	} else {
232 		*used_filename = NULL;
233 	}
234 
235 	verbosity = CLAMP (this_verbosity, 0, 3);
236 
237 	g_mutex_init (&mutex);
238 
239 	switch (this_verbosity) {
240 		/* Log level 3: EVERYTHING */
241 	case 3:
242 		break;
243 
244 		/* Log level 2: CRITICAL/ERROR/WARNING/INFO/MESSAGE only */
245 	case 2:
246 		hide_levels = G_LOG_LEVEL_DEBUG;
247 		break;
248 
249 		/* Log level 1: CRITICAL/ERROR/WARNING/INFO only */
250 	case 1:
251 		hide_levels = G_LOG_LEVEL_DEBUG |
252 		              G_LOG_LEVEL_MESSAGE;
253 		break;
254 
255 		/* Log level 0: CRITICAL/ERROR/WARNING only (default) */
256 	default:
257 	case 0:
258 		hide_levels = G_LOG_LEVEL_DEBUG |
259 		              G_LOG_LEVEL_MESSAGE |
260 		              G_LOG_LEVEL_INFO;
261 		break;
262 	}
263 
264 	if (hide_levels) {
265 		/* Hide log levels according to configuration */
266 		log_handler_id = g_log_set_handler (G_LOG_DOMAIN,
267 			                            hide_levels,
268 			                            hide_log_handler,
269 			                            NULL);
270 	}
271 
272 	/* Set log handler function for the rest */
273 	g_log_set_default_handler (tracker_log_handler, NULL);
274 
275 	initialized = TRUE;
276 
277 	/* log binary name and version */
278 	g_message ("Starting %s %s", g_get_application_name (), PACKAGE_VERSION);
279 
280 	return TRUE;
281 }
282 
283 void
tracker_log_shutdown(void)284 tracker_log_shutdown (void)
285 {
286 	if (!initialized) {
287 		return;
288 	}
289 
290 	g_message ("Stopping %s %s", g_get_application_name (), PACKAGE_VERSION);
291 
292 	/* Reset default log handler */
293 	g_log_set_default_handler (g_log_default_handler, NULL);
294 
295 	if (log_handler_id) {
296 		g_log_remove_handler (G_LOG_DOMAIN, log_handler_id);
297 		log_handler_id = 0;
298 	}
299 
300 	if (use_log_files && fd != NULL) {
301 		fclose (fd);
302 	}
303 
304 	g_mutex_clear (&mutex);
305 
306 	initialized = FALSE;
307 }
308