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 }