tracker-0.16.2/src/libtracker-common/tracker-os-dependant-unix.c

No issues found

  1 /*
  2  * Copyright (C) 2007, Jamie McCracken <jamiemcc@gnome.org>
  3  * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
  4  *
  5  * This library is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU Lesser General Public
  7  * License as published by the Free Software Foundation; either
  8  * version 2.1 of the License, or (at your option) any later version.
  9  *
 10  * This library is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  * Lesser General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU Lesser General Public
 16  * License along with this library; if not, write to the
 17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 18  * Boston, MA  02110-1301, USA.
 19  */
 20 
 21 #include "config.h"
 22 
 23 #include <string.h>
 24 #include <stdlib.h>
 25 #include <errno.h>
 26 #include <unistd.h>
 27 #include <sys/resource.h>
 28 
 29 #if defined (__OpenBSD__)
 30 #include <sys/param.h>
 31 #include <sys/sysctl.h>
 32 #endif
 33 
 34 #include <glib.h>
 35 
 36 #include "tracker-log.h"
 37 #include "tracker-os-dependant.h"
 38 
 39 /* Maximum here is a G_MAXLONG, so if you want to use > 2GB, you have
 40  * to set MEM_LIMIT to RLIM_INFINITY
 41  */
 42 #define MEM_LIMIT_MIN 256 * 1024 * 1024
 43 
 44 #if defined(__OpenBSD__) && !defined(RLIMIT_AS)
 45 #define RLIMIT_AS RLIMIT_DATA
 46 #endif
 47 
 48 #undef DISABLE_MEM_LIMITS
 49 
 50 gboolean
 51 tracker_spawn (gchar **argv,
 52                gint    timeout,
 53                gchar **tmp_stdout,
 54                gchar **tmp_stderr,
 55                gint   *exit_status)
 56 {
 57 	GError      *error = NULL;
 58 	GSpawnFlags  flags;
 59 	gboolean     result;
 60 
 61 	g_return_val_if_fail (argv != NULL, FALSE);
 62 	g_return_val_if_fail (argv[0] != NULL, FALSE);
 63 	g_return_val_if_fail (timeout >= 0, FALSE);
 64 
 65 	flags = G_SPAWN_SEARCH_PATH;
 66 
 67 	if (tmp_stderr == NULL)
 68 		flags |= G_SPAWN_STDERR_TO_DEV_NULL;
 69 
 70 	if (!tmp_stdout) {
 71 		flags = flags | G_SPAWN_STDOUT_TO_DEV_NULL;
 72 	}
 73 
 74 	result = g_spawn_sync (NULL,
 75 	                       argv,
 76 	                       NULL,
 77 	                       flags,
 78 	                       tracker_spawn_child_func,
 79 	                       GINT_TO_POINTER (timeout),
 80 	                       tmp_stdout,
 81 	                       tmp_stderr,
 82 	                       exit_status,
 83 	                       &error);
 84 
 85 	if (error) {
 86 		g_warning ("Could not spawn command:'%s', %s",
 87 		           argv[0],
 88 		           error->message);
 89 		g_error_free (error);
 90 	}
 91 
 92 	return result;
 93 }
 94 
 95 gboolean
 96 tracker_spawn_async_with_channels (const gchar **argv,
 97                                    gint          timeout,
 98                                    GPid         *pid,
 99                                    GIOChannel  **stdin_channel,
100                                    GIOChannel  **stdout_channel,
101                                    GIOChannel  **stderr_channel)
102 {
103 	GError   *error = NULL;
104 	gboolean  result;
105 	gint      tmpstdin, tmpstdout, tmpstderr;
106 
107 	g_return_val_if_fail (argv != NULL, FALSE);
108 	g_return_val_if_fail (argv[0] != NULL, FALSE);
109 	g_return_val_if_fail (timeout >= 0, FALSE);
110 	g_return_val_if_fail (pid != NULL, FALSE);
111 
112 	/* Note: PID must be non-NULL because we're using the
113 	 *  G_SPAWN_DO_NOT_REAP_CHILD option, so an explicit call to
114 	 *  g_spawn_close_pid () will be needed afterwards */
115 
116 	result = g_spawn_async_with_pipes (NULL,
117 	                                   (gchar **) argv,
118 	                                   NULL,
119 	                                   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
120 	                                   tracker_spawn_child_func,
121 	                                   GINT_TO_POINTER (timeout),
122 	                                   pid,
123 	                                   stdin_channel ? &tmpstdin : NULL,
124 	                                   stdout_channel ? &tmpstdout : NULL,
125 	                                   stderr_channel ? &tmpstderr : NULL,
126 	                                   &error);
127 
128 	if (error) {
129 		g_warning ("Could not spawn command:'%s', %s",
130 		           argv[0],
131 		           error->message);
132 		g_error_free (error);
133 	}
134 
135 	if (stdin_channel) {
136 		*stdin_channel = result ? g_io_channel_unix_new (tmpstdin) : NULL;
137 	}
138 
139 	if (stdout_channel) {
140 		*stdout_channel = result ? g_io_channel_unix_new (tmpstdout) : NULL;
141 	}
142 
143 	if (stderr_channel) {
144 		*stderr_channel = result ? g_io_channel_unix_new (tmpstderr) : NULL;
145 	}
146 
147 	return result;
148 }
149 
150 void
151 tracker_spawn_child_func (gpointer user_data)
152 {
153 	struct rlimit cpu_limit;
154 	gint          timeout = GPOINTER_TO_INT (user_data);
155 
156 	if (timeout > 0) {
157 		/* set cpu limit */
158 		getrlimit (RLIMIT_CPU, &cpu_limit);
159 		cpu_limit.rlim_cur = timeout;
160 		cpu_limit.rlim_max = timeout + 1;
161 
162 		if (setrlimit (RLIMIT_CPU, &cpu_limit) != 0) {
163 			g_critical ("Failed to set resource limit for CPU");
164 		}
165 
166 		/* Have this as a precaution in cases where cpu limit has not
167 		 * been reached due to spawned app sleeping.
168 		 */
169 		alarm (timeout + 2);
170 	}
171 
172 	/* Set child's niceness to 19 */
173 	errno = 0;
174 
175 	/* nice() uses attribute "warn_unused_result" and so complains
176 	 * if we do not check its returned value. But it seems that
177 	 * since glibc 2.2.4, nice() can return -1 on a successful call
178 	 * so we have to check value of errno too. Stupid...
179 	 */
180 	if (nice (19) == -1 && errno) {
181 		g_warning ("Failed to set nice value");
182 	}
183 }
184 
185 gchar *
186 tracker_create_permission_string (struct stat finfo)
187 {
188 	gchar *str;
189 	gint   n, bit;
190 
191 	/* Create permissions string */
192 	str = g_strdup ("?rwxrwxrwx");
193 
194 	switch (finfo.st_mode & S_IFMT) {
195 	case S_IFSOCK: str[0] = 's'; break;
196 	case S_IFIFO:  str[0] = 'p'; break;
197 	case S_IFLNK:  str[0] = 'l'; break;
198 	case S_IFCHR:  str[0] = 'c'; break;
199 	case S_IFBLK:  str[0] = 'b'; break;
200 	case S_IFDIR:  str[0] = 'd'; break;
201 	case S_IFREG:  str[0] = '-'; break;
202 	default:
203 		/* By default a regular file */
204 		str[0] = '-';
205 	}
206 
207 	for (bit = 0400, n = 1; bit; bit >>= 1, ++n) {
208 		if (!(finfo.st_mode & bit)) {
209 			str[n] = '-';
210 		}
211 	}
212 
213 	if (finfo.st_mode & S_ISUID) {
214 		str[3] = (finfo.st_mode & S_IXUSR) ? 's' : 'S';
215 	}
216 
217 	if (finfo.st_mode & S_ISGID) {
218 		str[6] = (finfo.st_mode & S_IXGRP) ? 's' : 'S';
219 	}
220 
221 	if (finfo.st_mode & S_ISVTX) {
222 		str[9] = (finfo.st_mode & S_IXOTH) ? 't' : 'T';
223 	}
224 
225 	return str;
226 }
227 
228 #ifndef DISABLE_MEM_LIMITS
229 
230 static glong
231 get_memory_total (void)
232 {
233 #if defined (__OpenBSD__)
234 	glong total = 0;
235 	int64_t physmem;
236 	size_t len;
237 	static gint mib[] = { CTL_HW, HW_PHYSMEM64 };
238 
239 	len = sizeof (physmem);
240 
241 	if (sysctl (mib, G_N_ELEMENTS (mib), &physmem, &len, NULL, 0) == -1) {
242 		g_critical ("Couldn't get memory information: %d", errno);
243 	} else {
244 		total = physmem;
245 	}
246 #elif defined (__sun)
247 	glong total = (glong)sysconf(_SC_PAGESIZE) * (glong)sysconf(_SC_PHYS_PAGES);
248 #else
249 	GError      *error = NULL;
250 	const gchar *filename;
251 	gchar       *contents = NULL;
252 	glong        total = 0;
253 
254 	filename = "/proc/meminfo";
255 
256 	if (!g_file_get_contents (filename,
257 	                          &contents,
258 	                          NULL,
259 	                          &error)) {
260 		g_critical ("Couldn't get memory information:'%s', %s",
261 		            filename,
262 		            error ? error->message : "no error given");
263 		g_clear_error (&error);
264 	} else {
265 		const gchar *start;
266 		gchar *p, *end;
267 
268 		start = "MemTotal:";
269 
270 		p = strstr (contents, start);
271 		if (p) {
272 			p += strlen (start);
273 			end = strstr (p, "kB");
274 
275 			if (end) {
276 				*end = '\0';
277 				total = 1024L * atol (p);
278 			}
279 		}
280 		g_free (contents);
281 	}
282 #endif
283 
284 	return total;
285 }
286 
287 #endif /* DISABLE_MEM_LIMITS */
288 
289 gboolean
290 tracker_memory_setrlimits (void)
291 {
292 #ifndef DISABLE_MEM_LIMITS
293 	struct rlimit rl = { 0 };
294 	glong total;
295 	glong total_halfed;
296 	glong limit;
297 
298 	total = get_memory_total ();
299 
300 	if (!total) {
301 		/* total amount of memory unknown */
302 		return FALSE;
303 	}
304 
305 	total_halfed = total / 2;
306 
307 	/* Clamp memory between 50% of total and MAXLONG (2GB on 32-bit) */
308 	limit = CLAMP (total_halfed, MEM_LIMIT_MIN, G_MAXLONG);
309 
310 	/* We want to limit the max virtual memory
311 	 * most extractors use mmap() so only virtual memory can be
312 	 * effectively limited.
313 	 */
314 	getrlimit (RLIMIT_AS, &rl);
315 	rl.rlim_cur = limit;
316 
317 	if (setrlimit (RLIMIT_AS, &rl) == -1) {
318 		const gchar *str = g_strerror (errno);
319 
320 		g_critical ("Could not set virtual memory limit with setrlimit(RLIMIT_AS), %s",
321 		            str ? str : "no error given");
322 
323 		return FALSE;
324 	} else {
325 		getrlimit (RLIMIT_DATA, &rl);
326 		rl.rlim_cur = limit;
327 
328 		if (setrlimit (RLIMIT_DATA, &rl) == -1) {
329 			const gchar *str = g_strerror (errno);
330 
331 			g_critical ("Could not set heap memory limit with setrlimit(RLIMIT_DATA), %s",
332 			            str ? str : "no error given");
333 
334 			return FALSE;
335 		} else {
336 			gchar *str1, *str2;
337 
338 #if GLIB_CHECK_VERSION (2,30,0)
339 			str1 = g_format_size (total);
340 			str2 = g_format_size (limit);
341 #else
342 			str1 = g_format_size_for_display (total);
343 			str2 = g_format_size_for_display (limit);
344 #endif
345 
346 			g_message ("Setting memory limitations: total is %s, minimum is 256 MB, recommended is ~1 GB", str1);
347 			g_message ("  Virtual/Heap set to %s (50%% of total or MAXLONG)", str2);
348 
349 			g_free (str2);
350 			g_free (str1);
351 		}
352 	}
353 #endif /* DISABLE_MEM_LIMITS */
354 
355 	return TRUE;
356 }