blob: 9d0ba58c85b9ecce2e6ccf29d36e095ded9156e0 [file] [log] [blame]
[email protected]bf4878d2010-06-16 20:12:011// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]3d5617e2008-08-27 14:36:192// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]05d4b0a2009-01-29 17:51:515#include <dirent.h>
[email protected]c7856632009-01-13 17:38:496#include <errno.h>
[email protected]05d4b0a2009-01-29 17:51:517#include <fcntl.h>
[email protected]8cf7cebd2009-01-05 19:53:308#include <signal.h>
[email protected]05d4b0a2009-01-29 17:51:519#include <stdlib.h>
[email protected]0d83c732008-12-02 16:50:4710#include <sys/resource.h>
11#include <sys/time.h>
[email protected]3d5617e2008-08-27 14:36:1912#include <sys/types.h>
[email protected]43bc28322008-12-05 01:57:2113#include <sys/wait.h>
[email protected]3d5617e2008-08-27 14:36:1914#include <unistd.h>
[email protected]05d4b0a2009-01-29 17:51:5115
[email protected]fa3097a6a52008-12-17 22:41:5016#include <limits>
[email protected]3f04f2b2009-04-30 19:40:0317#include <set>
[email protected]3d5617e2008-08-27 14:36:1918
[email protected]5d91c9e2010-07-28 17:25:2819#include "base/command_line.h"
[email protected]cabe39c2010-02-02 02:28:1620#include "base/compiler_specific.h"
[email protected]58580352010-10-26 04:07:5021#include "base/debug/stack_trace.h"
[email protected]ef73044e2010-03-11 15:25:5422#include "base/dir_reader_posix.h"
[email protected]157c61b2009-05-01 21:37:3123#include "base/eintr_wrapper.h"
[email protected]0d83c732008-12-02 16:50:4724#include "base/logging.h"
[email protected]05d4b0a2009-01-29 17:51:5125#include "base/process_util.h"
[email protected]1fec8402009-03-13 19:11:5926#include "base/scoped_ptr.h"
[email protected]f1633932010-08-17 23:05:2827#include "base/stringprintf.h"
[email protected]44f9c952011-01-02 06:05:3928#include "base/synchronization/waitable_event.h"
[email protected]ce072a72010-12-31 20:02:1629#include "base/threading/platform_thread.h"
[email protected]34b99632011-01-01 01:01:0630#include "base/threading/thread_restrictions.h"
[email protected]43bc28322008-12-05 01:57:2131#include "base/time.h"
[email protected]3d5617e2008-08-27 14:36:1932
[email protected]c0028792010-01-12 00:39:1533#if defined(OS_MACOSX)
[email protected]ef73044e2010-03-11 15:25:5434#include <crt_externs.h>
35#define environ (*_NSGetEnviron())
[email protected]ef73044e2010-03-11 15:25:5436#else
37extern char** environ;
[email protected]c0028792010-01-12 00:39:1538#endif
39
[email protected]aa660752008-11-14 03:39:4640namespace base {
[email protected]3d5617e2008-08-27 14:36:1941
[email protected]d6fc9fd2009-10-27 18:03:4742namespace {
43
[email protected]eaec38c2010-11-29 19:30:2344int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds,
45 bool* success) {
[email protected]d6fc9fd2009-10-27 18:03:4746 // This POSIX version of this function only guarantees that we wait no less
[email protected]2d9bb0222010-04-02 08:24:0147 // than |wait_milliseconds| for the process to exit. The child process may
48 // exit sometime before the timeout has ended but we may still block for up
49 // to 256 milliseconds after the fact.
[email protected]d6fc9fd2009-10-27 18:03:4750 //
51 // waitpid() has no direct support on POSIX for specifying a timeout, you can
52 // either ask it to block indefinitely or return immediately (WNOHANG).
53 // When a child process terminates a SIGCHLD signal is sent to the parent.
54 // Catching this signal would involve installing a signal handler which may
55 // affect other parts of the application and would be difficult to debug.
56 //
57 // Our strategy is to call waitpid() once up front to check if the process
58 // has already exited, otherwise to loop for wait_milliseconds, sleeping for
[email protected]2d9bb0222010-04-02 08:24:0159 // at most 256 milliseconds each time using usleep() and then calling
60 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
61 // we double it every 4 sleep cycles.
[email protected]d6fc9fd2009-10-27 18:03:4762 //
63 // usleep() is speced to exit if a signal is received for which a handler
64 // has been installed. This means that when a SIGCHLD is sent, it will exit
65 // depending on behavior external to this function.
66 //
[email protected]eaec38c2010-11-29 19:30:2367 // This function is used primarily for unit tests, if we want to use it in
68 // the application itself it would probably be best to examine other routes.
69 int status = -1;
70 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
[email protected]2d9bb0222010-04-02 08:24:0171 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
72 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
[email protected]eaec38c2010-11-29 19:30:2373 int64 double_sleep_time = 0;
[email protected]d6fc9fd2009-10-27 18:03:4774
75 // If the process hasn't exited yet, then sleep and try again.
[email protected]2d9bb0222010-04-02 08:24:0176 Time wakeup_time = Time::Now() +
77 TimeDelta::FromMilliseconds(wait_milliseconds);
[email protected]d6fc9fd2009-10-27 18:03:4778 while (ret_pid == 0) {
79 Time now = Time::Now();
80 if (now > wakeup_time)
81 break;
82 // Guaranteed to be non-negative!
83 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
[email protected]2d9bb0222010-04-02 08:24:0184 // Sleep for a bit while we wait for the process to finish.
85 if (sleep_time_usecs > max_sleep_time_usecs)
86 sleep_time_usecs = max_sleep_time_usecs;
[email protected]d6fc9fd2009-10-27 18:03:4787
88 // usleep() will return 0 and set errno to EINTR on receipt of a signal
89 // such as SIGCHLD.
90 usleep(sleep_time_usecs);
[email protected]eaec38c2010-11-29 19:30:2391 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
[email protected]2d9bb0222010-04-02 08:24:0192
93 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
94 (double_sleep_time++ % 4 == 0)) {
95 max_sleep_time_usecs *= 2;
96 }
[email protected]d6fc9fd2009-10-27 18:03:4797 }
98
[email protected]eaec38c2010-11-29 19:30:2399 if (success)
100 *success = (ret_pid != -1);
101
102 return status;
[email protected]d6fc9fd2009-10-27 18:03:47103}
104
[email protected]7e3edc22010-11-22 22:31:00105void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) {
[email protected]7e868c962010-04-30 06:24:37106 LOG(ERROR) << "Received signal " << signal;
[email protected]58580352010-10-26 04:07:50107 debug::StackTrace().PrintBacktrace();
[email protected]7e3edc22010-11-22 22:31:00108
109 // TODO(shess): Port to Linux.
110#if defined(OS_MACOSX)
111 // TODO(shess): Port to 64-bit.
112#if ARCH_CPU_32_BITS
113 char buf[1024];
114 size_t len;
115
116 // NOTE: Even |snprintf()| is not on the approved list for signal
117 // handlers, but buffered I/O is definitely not on the list due to
118 // potential for |malloc()|.
119 len = static_cast<size_t>(
120 snprintf(buf, sizeof(buf),
121 "ax: %x, bx: %x, cx: %x, dx: %x\n",
122 context->uc_mcontext->__ss.__eax,
123 context->uc_mcontext->__ss.__ebx,
124 context->uc_mcontext->__ss.__ecx,
125 context->uc_mcontext->__ss.__edx));
126 write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
127
128 len = static_cast<size_t>(
129 snprintf(buf, sizeof(buf),
130 "di: %x, si: %x, bp: %x, sp: %x, ss: %x, flags: %x\n",
131 context->uc_mcontext->__ss.__edi,
132 context->uc_mcontext->__ss.__esi,
133 context->uc_mcontext->__ss.__ebp,
134 context->uc_mcontext->__ss.__esp,
135 context->uc_mcontext->__ss.__ss,
136 context->uc_mcontext->__ss.__eflags));
137 write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
138
139 len = static_cast<size_t>(
140 snprintf(buf, sizeof(buf),
141 "ip: %x, cs: %x, ds: %x, es: %x, fs: %x, gs: %x\n",
142 context->uc_mcontext->__ss.__eip,
143 context->uc_mcontext->__ss.__cs,
144 context->uc_mcontext->__ss.__ds,
145 context->uc_mcontext->__ss.__es,
146 context->uc_mcontext->__ss.__fs,
147 context->uc_mcontext->__ss.__gs));
148 write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
149#endif // ARCH_CPU_32_BITS
150#endif // defined(OS_MACOSX)
[email protected]d6fc9fd2009-10-27 18:03:47151 _exit(1);
152}
153
[email protected]620a0032010-09-04 02:32:59154void ResetChildSignalHandlersToDefaults() {
155 // The previous signal handlers are likely to be meaningless in the child's
156 // context so we reset them to the defaults for now. http://crbug.com/44953
157 // These signal handlers are setup in browser_main.cc:BrowserMain
158 signal(SIGTERM, SIG_DFL);
159 signal(SIGHUP, SIG_DFL);
160 signal(SIGINT, SIG_DFL);
161}
162
[email protected]fa289832010-03-19 20:30:30163} // anonymous namespace
[email protected]d6fc9fd2009-10-27 18:03:47164
[email protected]43cf3252009-04-01 09:19:37165ProcessId GetCurrentProcId() {
[email protected]3d5617e2008-08-27 14:36:19166 return getpid();
167}
168
[email protected]113ab132008-09-18 20:42:55169ProcessHandle GetCurrentProcessHandle() {
170 return GetCurrentProcId();
171}
172
[email protected]6c6cc802009-04-03 17:01:36173bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
[email protected]5986ed22009-02-06 00:19:17174 // On Posix platforms, process handles are the same as PIDs, so we
175 // don't need to do anything.
[email protected]6c6cc802009-04-03 17:01:36176 *handle = pid;
177 return true;
[email protected]5986ed22009-02-06 00:19:17178}
179
[email protected]5d438dbad2009-04-30 08:59:39180bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
181 // On POSIX permissions are checked for each operation on process,
182 // not when opening a "handle".
183 return OpenProcessHandle(pid, handle);
184}
185
[email protected]7d11f6d52010-10-12 21:44:23186bool OpenProcessHandleWithAccess(ProcessId pid,
187 uint32 access_flags,
188 ProcessHandle* handle) {
189 // On POSIX permissions are checked for each operation on process,
190 // not when opening a "handle".
191 return OpenProcessHandle(pid, handle);
192}
193
[email protected]5986ed22009-02-06 00:19:17194void CloseProcessHandle(ProcessHandle process) {
195 // See OpenProcessHandle, nothing to do.
196 return;
197}
198
[email protected]43cf3252009-04-01 09:19:37199ProcessId GetProcId(ProcessHandle process) {
[email protected]fadb8ea2008-08-27 15:36:37200 return process;
[email protected]3d5617e2008-08-27 14:36:19201}
[email protected]fadb8ea2008-08-27 15:36:37202
[email protected]8cf7cebd2009-01-05 19:53:30203// Attempts to kill the process identified by the given process
204// entry structure. Ignores specified exit_code; posix can't force that.
205// Returns true if this is successful, false otherwise.
[email protected]cd4fd152009-02-09 19:28:41206bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
[email protected]9610ef242009-11-18 02:41:26207 DCHECK_GT(process_id, 1) << " tried to kill invalid process_id";
208 if (process_id <= 1)
[email protected]9fd697a2009-07-07 14:00:59209 return false;
[email protected]f2d2ec22010-08-13 15:13:04210 static unsigned kMaxSleepMs = 1000;
211 unsigned sleep_ms = 4;
[email protected]9fd697a2009-07-07 14:00:59212
[email protected]140a7cd2009-04-28 01:37:23213 bool result = kill(process_id, SIGTERM) == 0;
[email protected]8cf7cebd2009-01-05 19:53:30214
[email protected]140a7cd2009-04-28 01:37:23215 if (result && wait) {
[email protected]8cf7cebd2009-01-05 19:53:30216 int tries = 60;
217 // The process may not end immediately due to pending I/O
[email protected]d24423742009-07-13 23:31:28218 bool exited = false;
[email protected]8cf7cebd2009-01-05 19:53:30219 while (tries-- > 0) {
[email protected]a4dc33f2009-10-20 15:09:55220 pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
[email protected]d24423742009-07-13 23:31:28221 if (pid == process_id) {
222 exited = true;
[email protected]8cf7cebd2009-01-05 19:53:30223 break;
[email protected]d24423742009-07-13 23:31:28224 }
[email protected]bf4878d2010-06-16 20:12:01225 if (pid == -1) {
226 if (errno == ECHILD) {
227 // The wait may fail with ECHILD if another process also waited for
228 // the same pid, causing the process state to get cleaned up.
229 exited = true;
230 break;
231 }
232 DPLOG(ERROR) << "Error waiting for process " << process_id;
233 }
[email protected]140a7cd2009-04-28 01:37:23234
[email protected]f2d2ec22010-08-13 15:13:04235 usleep(sleep_ms * 1000);
236 if (sleep_ms < kMaxSleepMs)
237 sleep_ms *= 2;
[email protected]8cf7cebd2009-01-05 19:53:30238 }
[email protected]140a7cd2009-04-28 01:37:23239
[email protected]443b80e2010-12-14 00:42:23240 // If we're waiting and the child hasn't died by now, force it
241 // with a SIGKILL.
[email protected]32cd5302009-11-20 19:30:59242 if (!exited)
[email protected]d24423742009-07-13 23:31:28243 result = kill(process_id, SIGKILL) == 0;
[email protected]8cf7cebd2009-01-05 19:53:30244 }
[email protected]140a7cd2009-04-28 01:37:23245
[email protected]32cd5302009-11-20 19:30:59246 if (!result)
247 DPLOG(ERROR) << "Unable to terminate process " << process_id;
[email protected]140a7cd2009-04-28 01:37:23248
[email protected]8cf7cebd2009-01-05 19:53:30249 return result;
250}
251
[email protected]61b93f88f2010-09-22 17:28:30252bool KillProcessGroup(ProcessHandle process_group_id) {
253 bool result = kill(-1 * process_group_id, SIGKILL) == 0;
254 if (!result)
255 PLOG(ERROR) << "Unable to terminate process group " << process_group_id;
256 return result;
257}
258
[email protected]05d4b0a2009-01-29 17:51:51259// A class to handle auto-closing of DIR*'s.
260class ScopedDIRClose {
261 public:
262 inline void operator()(DIR* x) const {
263 if (x) {
264 closedir(x);
265 }
266 }
267};
268typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
269
[email protected]aec92f832009-05-06 16:40:12270#if defined(OS_LINUX)
271 static const rlim_t kSystemDefaultMaxFds = 8192;
[email protected]ef73044e2010-03-11 15:25:54272 static const char kFDDir[] = "/proc/self/fd";
[email protected]aec92f832009-05-06 16:40:12273#elif defined(OS_MACOSX)
274 static const rlim_t kSystemDefaultMaxFds = 256;
[email protected]ef73044e2010-03-11 15:25:54275 static const char kFDDir[] = "/dev/fd";
[email protected]a1208912010-02-17 14:15:08276#elif defined(OS_SOLARIS)
277 static const rlim_t kSystemDefaultMaxFds = 8192;
[email protected]ef73044e2010-03-11 15:25:54278 static const char kFDDir[] = "/dev/fd";
[email protected]4a34ce02009-08-31 22:25:00279#elif defined(OS_FREEBSD)
280 static const rlim_t kSystemDefaultMaxFds = 8192;
[email protected]ef73044e2010-03-11 15:25:54281 static const char kFDDir[] = "/dev/fd";
[email protected]8d578822010-01-25 23:54:54282#elif defined(OS_OPENBSD)
283 static const rlim_t kSystemDefaultMaxFds = 256;
[email protected]ef73044e2010-03-11 15:25:54284 static const char kFDDir[] = "/dev/fd";
[email protected]aec92f832009-05-06 16:40:12285#endif
[email protected]ef73044e2010-03-11 15:25:54286
287void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
288 // DANGER: no calls to malloc are allowed from now on:
289 // http://crbug.com/36678
[email protected]3f04f2b2009-04-30 19:40:03290
[email protected]aec92f832009-05-06 16:40:12291 // Get the maximum number of FDs possible.
292 struct rlimit nofile;
293 rlim_t max_fds;
294 if (getrlimit(RLIMIT_NOFILE, &nofile)) {
295 // getrlimit failed. Take a best guess.
296 max_fds = kSystemDefaultMaxFds;
297 DLOG(ERROR) << "getrlimit(RLIMIT_NOFILE) failed: " << errno;
298 } else {
299 max_fds = nofile.rlim_cur;
300 }
301
302 if (max_fds > INT_MAX)
303 max_fds = INT_MAX;
304
[email protected]ef73044e2010-03-11 15:25:54305 DirReaderPosix fd_dir(kFDDir);
[email protected]3f04f2b2009-04-30 19:40:03306
[email protected]ef73044e2010-03-11 15:25:54307 if (!fd_dir.IsValid()) {
[email protected]aec92f832009-05-06 16:40:12308 // Fallback case: Try every possible fd.
309 for (rlim_t i = 0; i < max_fds; ++i) {
[email protected]3f04f2b2009-04-30 19:40:03310 const int fd = static_cast<int>(i);
[email protected]ef73044e2010-03-11 15:25:54311 if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
312 continue;
[email protected]40350b12010-03-30 17:29:27313 InjectiveMultimap::const_iterator j;
314 for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) {
315 if (fd == j->dest)
[email protected]ef73044e2010-03-11 15:25:54316 break;
317 }
[email protected]40350b12010-03-30 17:29:27318 if (j != saved_mapping.end())
[email protected]3f04f2b2009-04-30 19:40:03319 continue;
320
[email protected]cabe39c2010-02-02 02:28:16321 // Since we're just trying to close anything we can find,
322 // ignore any error return values of close().
323 int unused ALLOW_UNUSED = HANDLE_EINTR(close(fd));
[email protected]3f04f2b2009-04-30 19:40:03324 }
325 return;
326 }
[email protected]3f04f2b2009-04-30 19:40:03327
[email protected]ef73044e2010-03-11 15:25:54328 const int dir_fd = fd_dir.fd();
329
330 for ( ; fd_dir.Next(); ) {
[email protected]3f04f2b2009-04-30 19:40:03331 // Skip . and .. entries.
[email protected]ef73044e2010-03-11 15:25:54332 if (fd_dir.name()[0] == '.')
[email protected]3f04f2b2009-04-30 19:40:03333 continue;
334
335 char *endptr;
336 errno = 0;
[email protected]ef73044e2010-03-11 15:25:54337 const long int fd = strtol(fd_dir.name(), &endptr, 10);
338 if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno)
[email protected]3f04f2b2009-04-30 19:40:03339 continue;
[email protected]ef73044e2010-03-11 15:25:54340 if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
341 continue;
342 InjectiveMultimap::const_iterator i;
343 for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) {
344 if (fd == i->dest)
345 break;
346 }
347 if (i != saved_mapping.end())
[email protected]3f04f2b2009-04-30 19:40:03348 continue;
[email protected]699de0b2009-06-07 23:03:08349 if (fd == dir_fd)
350 continue;
[email protected]3f04f2b2009-04-30 19:40:03351
[email protected]aec92f832009-05-06 16:40:12352 // When running under Valgrind, Valgrind opens several FDs for its
353 // own use and will complain if we try to close them. All of
354 // these FDs are >= |max_fds|, so we can check against that here
355 // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758
[email protected]cabe39c2010-02-02 02:28:16356 if (fd < static_cast<int>(max_fds)) {
357 int ret = HANDLE_EINTR(close(fd));
358 DPCHECK(ret == 0);
359 }
[email protected]3f04f2b2009-04-30 19:40:03360 }
361}
362
[email protected]ef73044e2010-03-11 15:25:54363char** AlterEnvironment(const environment_vector& changes,
364 const char* const* const env) {
365 unsigned count = 0;
366 unsigned size = 0;
367
368 // First assume that all of the current environment will be included.
369 for (unsigned i = 0; env[i]; i++) {
370 const char *const pair = env[i];
371 count++;
372 size += strlen(pair) + 1 /* terminating NUL */;
373 }
374
375 for (environment_vector::const_iterator
376 j = changes.begin(); j != changes.end(); j++) {
377 bool found = false;
378 const char *pair;
379
380 for (unsigned i = 0; env[i]; i++) {
381 pair = env[i];
382 const char *const equals = strchr(pair, '=');
383 if (!equals)
384 continue;
385 const unsigned keylen = equals - pair;
386 if (keylen == j->first.size() &&
387 memcmp(pair, j->first.data(), keylen) == 0) {
388 found = true;
389 break;
390 }
391 }
392
393 // if found, we'll either be deleting or replacing this element.
394 if (found) {
395 count--;
396 size -= strlen(pair) + 1;
397 if (j->second.size())
398 found = false;
399 }
400
401 // if !found, then we have a new element to add.
402 if (!found && j->second.size() > 0) {
403 count++;
404 size += j->first.size() + 1 /* '=' */ + j->second.size() + 1 /* NUL */;
405 }
406 }
407
408 count++; // for the final NULL
409 uint8_t *buffer = new uint8_t[sizeof(char*) * count + size];
410 char **const ret = reinterpret_cast<char**>(buffer);
411 unsigned k = 0;
412 char *scratch = reinterpret_cast<char*>(buffer + sizeof(char*) * count);
413
414 for (unsigned i = 0; env[i]; i++) {
415 const char *const pair = env[i];
416 const char *const equals = strchr(pair, '=');
417 if (!equals) {
418 const unsigned len = strlen(pair);
419 ret[k++] = scratch;
420 memcpy(scratch, pair, len + 1);
421 scratch += len + 1;
422 continue;
423 }
424 const unsigned keylen = equals - pair;
425 bool handled = false;
426 for (environment_vector::const_iterator
427 j = changes.begin(); j != changes.end(); j++) {
428 if (j->first.size() == keylen &&
429 memcmp(j->first.data(), pair, keylen) == 0) {
430 if (!j->second.empty()) {
431 ret[k++] = scratch;
432 memcpy(scratch, pair, keylen + 1);
433 scratch += keylen + 1;
434 memcpy(scratch, j->second.c_str(), j->second.size() + 1);
435 scratch += j->second.size() + 1;
436 }
437 handled = true;
438 break;
439 }
440 }
441
442 if (!handled) {
443 const unsigned len = strlen(pair);
444 ret[k++] = scratch;
445 memcpy(scratch, pair, len + 1);
446 scratch += len + 1;
447 }
448 }
449
450 // Now handle new elements
451 for (environment_vector::const_iterator
452 j = changes.begin(); j != changes.end(); j++) {
453 if (j->second.size() == 0)
454 continue;
455
456 bool found = false;
457 for (unsigned i = 0; env[i]; i++) {
458 const char *const pair = env[i];
459 const char *const equals = strchr(pair, '=');
460 if (!equals)
461 continue;
462 const unsigned keylen = equals - pair;
463 if (keylen == j->first.size() &&
464 memcmp(pair, j->first.data(), keylen) == 0) {
465 found = true;
466 break;
467 }
468 }
469
470 if (!found) {
471 ret[k++] = scratch;
472 memcpy(scratch, j->first.data(), j->first.size());
473 scratch += j->first.size();
474 *scratch++ = '=';
475 memcpy(scratch, j->second.c_str(), j->second.size() + 1);
476 scratch += j->second.size() + 1;
477 }
478 }
479
480 ret[k] = NULL;
481 return ret;
482}
483
[email protected]61b93f88f2010-09-22 17:28:30484bool LaunchAppImpl(
[email protected]c0028792010-01-12 00:39:15485 const std::vector<std::string>& argv,
[email protected]ef73044e2010-03-11 15:25:54486 const environment_vector& env_changes,
[email protected]c0028792010-01-12 00:39:15487 const file_handle_mapping_vector& fds_to_remap,
488 bool wait,
[email protected]61b93f88f2010-09-22 17:28:30489 ProcessHandle* process_handle,
490 bool start_new_process_group) {
[email protected]c0028792010-01-12 00:39:15491 pid_t pid;
[email protected]ef73044e2010-03-11 15:25:54492 InjectiveMultimap fd_shuffle1, fd_shuffle2;
493 fd_shuffle1.reserve(fds_to_remap.size());
494 fd_shuffle2.reserve(fds_to_remap.size());
495 scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
496 scoped_array<char*> new_environ(AlterEnvironment(env_changes, environ));
497
[email protected]c0028792010-01-12 00:39:15498 pid = fork();
[email protected]2aea9e092009-08-06 20:03:01499 if (pid < 0)
500 return false;
501
502 if (pid == 0) {
503 // Child process
[email protected]61b93f88f2010-09-22 17:28:30504
505 if (start_new_process_group) {
506 // Instead of inheriting the process group ID of the parent, the child
507 // starts off a new process group with pgid equal to its process ID.
508 if (setpgid(0, 0) < 0)
509 return false;
510 }
[email protected]e9a8c19f2009-09-03 21:27:36511#if defined(OS_MACOSX)
512 RestoreDefaultExceptionHandler();
513#endif
514
[email protected]620a0032010-09-04 02:32:59515 ResetChildSignalHandlersToDefaults();
[email protected]01aa3cd2010-06-02 18:47:55516
[email protected]ef73044e2010-03-11 15:25:54517#if 0
518 // When debugging it can be helpful to check that we really aren't making
519 // any hidden calls to malloc.
520 void *malloc_thunk =
521 reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095);
522 mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
523 memset(reinterpret_cast<void*>(malloc), 0xff, 8);
524#endif
525
526 // DANGER: no calls to malloc are allowed from now on:
527 // http://crbug.com/36678
528
[email protected]e6572302009-08-21 17:05:54529 for (file_handle_mapping_vector::const_iterator
530 it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
[email protected]ef73044e2010-03-11 15:25:54531 fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
532 fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
[email protected]e6572302009-08-21 17:05:54533 }
[email protected]2aea9e092009-08-06 20:03:01534
[email protected]ef73044e2010-03-11 15:25:54535 environ = new_environ.get();
[email protected]2aea9e092009-08-06 20:03:01536
537 // Obscure fork() rule: in the child, if you don't end up doing exec*(),
538 // you call _exit() instead of exit(). This is because _exit() does not
539 // call any previously-registered (in the parent) exit handlers, which
540 // might do things like block waiting for threads that don't even exist
541 // in the child.
[email protected]ef73044e2010-03-11 15:25:54542
543 // fd_shuffle1 is mutated by this call because it cannot malloc.
544 if (!ShuffleFileDescriptors(&fd_shuffle1))
[email protected]2aea9e092009-08-06 20:03:01545 _exit(127);
546
[email protected]ef73044e2010-03-11 15:25:54547 CloseSuperfluousFds(fd_shuffle2);
[email protected]e6572302009-08-21 17:05:54548
[email protected]e6572302009-08-21 17:05:54549 for (size_t i = 0; i < argv.size(); i++)
550 argv_cstr[i] = const_cast<char*>(argv[i].c_str());
551 argv_cstr[argv.size()] = NULL;
[email protected]2aea9e092009-08-06 20:03:01552 execvp(argv_cstr[0], argv_cstr.get());
[email protected]ef73044e2010-03-11 15:25:54553 RAW_LOG(ERROR, "LaunchApp: failed to execvp:");
554 RAW_LOG(ERROR, argv_cstr[0]);
[email protected]2aea9e092009-08-06 20:03:01555 _exit(127);
556 } else {
557 // Parent process
[email protected]cabe39c2010-02-02 02:28:16558 if (wait) {
[email protected]b42c4652010-12-04 03:23:53559 // While this isn't strictly disk IO, waiting for another process to
560 // finish is the sort of thing ThreadRestrictions is trying to prevent.
561 base::ThreadRestrictions::AssertIOAllowed();
[email protected]cabe39c2010-02-02 02:28:16562 pid_t ret = HANDLE_EINTR(waitpid(pid, 0, 0));
563 DPCHECK(ret > 0);
564 }
[email protected]2aea9e092009-08-06 20:03:01565
566 if (process_handle)
567 *process_handle = pid;
568 }
569
570 return true;
571}
572
[email protected]61b93f88f2010-09-22 17:28:30573bool LaunchApp(
574 const std::vector<std::string>& argv,
575 const environment_vector& env_changes,
576 const file_handle_mapping_vector& fds_to_remap,
577 bool wait,
578 ProcessHandle* process_handle) {
579 return LaunchAppImpl(argv, env_changes, fds_to_remap,
580 wait, process_handle, false);
581}
582
583bool LaunchAppInNewProcessGroup(
584 const std::vector<std::string>& argv,
585 const environment_vector& env_changes,
586 const file_handle_mapping_vector& fds_to_remap,
587 bool wait,
588 ProcessHandle* process_handle) {
589 return LaunchAppImpl(argv, env_changes, fds_to_remap, wait,
590 process_handle, true);
591}
592
[email protected]2aea9e092009-08-06 20:03:01593bool LaunchApp(const std::vector<std::string>& argv,
594 const file_handle_mapping_vector& fds_to_remap,
595 bool wait, ProcessHandle* process_handle) {
596 base::environment_vector no_env;
597 return LaunchApp(argv, no_env, fds_to_remap, wait, process_handle);
598}
599
600bool LaunchApp(const CommandLine& cl,
601 bool wait, bool start_hidden,
602 ProcessHandle* process_handle) {
603 file_handle_mapping_vector no_files;
604 return LaunchApp(cl.argv(), no_files, wait, process_handle);
605}
606
[email protected]0b100bc8b2008-10-14 20:49:16607ProcessMetrics::~ProcessMetrics() { }
608
[email protected]c9d40872008-09-24 12:58:37609void EnableTerminationOnHeapCorruption() {
610 // On POSIX, there nothing to do AFAIK.
611}
612
[email protected]d6fc9fd2009-10-27 18:03:47613bool EnableInProcessStackDumping() {
614 // When running in an application, our code typically expects SIGPIPE
615 // to be ignored. Therefore, when testing that same code, it should run
616 // with SIGPIPE ignored as well.
617 struct sigaction action;
618 action.sa_handler = SIG_IGN;
619 action.sa_flags = 0;
620 sigemptyset(&action.sa_mask);
621 bool success = (sigaction(SIGPIPE, &action, NULL) == 0);
622
[email protected]7e3edc22010-11-22 22:31:00623 sig_t handler = reinterpret_cast<sig_t>(&StackDumpSignalHandler);
624 success &= (signal(SIGILL, handler) != SIG_ERR);
625 success &= (signal(SIGABRT, handler) != SIG_ERR);
626 success &= (signal(SIGFPE, handler) != SIG_ERR);
627 success &= (signal(SIGBUS, handler) != SIG_ERR);
628 success &= (signal(SIGSEGV, handler) != SIG_ERR);
629 success &= (signal(SIGSYS, handler) != SIG_ERR);
[email protected]7e868c962010-04-30 06:24:37630
[email protected]d6fc9fd2009-10-27 18:03:47631 return success;
632}
633
[email protected]3d5617e2008-08-27 14:36:19634void RaiseProcessToHighPriority() {
635 // On POSIX, we don't actually do anything here. We could try to nice() or
636 // setpriority() or sched_getscheduler, but these all require extra rights.
637}
638
[email protected]443b80e2010-12-14 00:42:23639TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
640 int status = 0;
[email protected]eaec38c2010-11-29 19:30:23641 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
642 if (result == -1) {
643 PLOG(ERROR) << "waitpid(" << handle << ")";
[email protected]443b80e2010-12-14 00:42:23644 if (exit_code)
645 *exit_code = 0;
646 return TERMINATION_STATUS_NORMAL_TERMINATION;
[email protected]eaec38c2010-11-29 19:30:23647 } else if (result == 0) {
648 // the child hasn't exited yet.
[email protected]443b80e2010-12-14 00:42:23649 if (exit_code)
650 *exit_code = 0;
651 return TERMINATION_STATUS_STILL_RUNNING;
[email protected]9c19aa12009-01-21 13:50:11652 }
653
[email protected]443b80e2010-12-14 00:42:23654 if (exit_code)
655 *exit_code = status;
[email protected]140a7cd2009-04-28 01:37:23656
[email protected]9c19aa12009-01-21 13:50:11657 if (WIFSIGNALED(status)) {
[email protected]2fdc86a2010-01-26 23:08:02658 switch (WTERMSIG(status)) {
[email protected]22b61ba2010-10-19 18:25:47659 case SIGABRT:
[email protected]443b80e2010-12-14 00:42:23660 case SIGBUS:
[email protected]22b61ba2010-10-19 18:25:47661 case SIGFPE:
[email protected]443b80e2010-12-14 00:42:23662 case SIGILL:
663 case SIGSEGV:
664 return TERMINATION_STATUS_PROCESS_CRASHED;
665 case SIGINT:
666 case SIGKILL:
667 case SIGTERM:
668 return TERMINATION_STATUS_PROCESS_WAS_KILLED;
[email protected]9c19aa12009-01-21 13:50:11669 default:
[email protected]443b80e2010-12-14 00:42:23670 break;
[email protected]9c19aa12009-01-21 13:50:11671 }
672 }
673
[email protected]443b80e2010-12-14 00:42:23674 if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
675 return TERMINATION_STATUS_ABNORMAL_TERMINATION;
[email protected]9c19aa12009-01-21 13:50:11676
[email protected]443b80e2010-12-14 00:42:23677 return TERMINATION_STATUS_NORMAL_TERMINATION;
[email protected]9c19aa12009-01-21 13:50:11678}
679
[email protected]c7856632009-01-13 17:38:49680bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
681 int status;
[email protected]157c61b2009-05-01 21:37:31682 if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
683 NOTREACHED();
684 return false;
[email protected]c7856632009-01-13 17:38:49685 }
686
687 if (WIFEXITED(status)) {
688 *exit_code = WEXITSTATUS(status);
689 return true;
690 }
691
692 // If it didn't exit cleanly, it must have been signaled.
693 DCHECK(WIFSIGNALED(status));
694 return false;
695}
696
[email protected]8004e682010-03-16 07:41:22697bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
698 int64 timeout_milliseconds) {
[email protected]eaec38c2010-11-29 19:30:23699 bool waitpid_success = false;
700 int status = WaitpidWithTimeout(handle, timeout_milliseconds,
701 &waitpid_success);
702 if (status == -1)
703 return false;
704 if (!waitpid_success)
[email protected]8004e682010-03-16 07:41:22705 return false;
706 if (!WIFEXITED(status))
707 return false;
708 if (WIFSIGNALED(status)) {
709 *exit_code = -1;
710 return true;
711 }
712 *exit_code = WEXITSTATUS(status);
713 return true;
714}
715
[email protected]743ace42009-06-17 17:23:51716bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) {
[email protected]eaec38c2010-11-29 19:30:23717 bool waitpid_success;
[email protected]45750212009-04-27 23:01:34718 int status;
[email protected]eaec38c2010-11-29 19:30:23719 if (wait_milliseconds == base::kNoTimeout)
720 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
721 else
722 status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success);
723 if (status != -1) {
724 DCHECK(waitpid_success);
725 return WIFEXITED(status);
[email protected]ef0279272009-03-06 09:24:38726 } else {
[email protected]774f2202010-11-29 18:56:19727 return false;
[email protected]eaec38c2010-11-29 19:30:23728 }
[email protected]076bf0b62009-03-04 20:57:58729}
730
[email protected]743ace42009-06-17 17:23:51731bool CrashAwareSleep(ProcessHandle handle, int64 wait_milliseconds) {
[email protected]eaec38c2010-11-29 19:30:23732 bool waitpid_success;
733 int status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success);
734 if (status != -1) {
735 DCHECK(waitpid_success);
[email protected]774f2202010-11-29 18:56:19736 return !(WIFEXITED(status) || WIFSIGNALED(status));
[email protected]eaec38c2010-11-29 19:30:23737 } else {
738 // If waitpid returned with an error, then the process doesn't exist
739 // (which most probably means it didn't exist before our call).
740 return waitpid_success;
[email protected]ef0279272009-03-06 09:24:38741 }
[email protected]43bc28322008-12-05 01:57:21742}
743
[email protected]0d83c732008-12-02 16:50:47744int64 TimeValToMicroseconds(const struct timeval& tv) {
[email protected]2d9bb0222010-04-02 08:24:01745 static const int kMicrosecondsPerSecond = 1000000;
746 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow.
747 ret *= kMicrosecondsPerSecond;
748 ret += tv.tv_usec;
749 return ret;
[email protected]0d83c732008-12-02 16:50:47750}
751
[email protected]3b9f5aa2010-07-28 04:02:34752// Executes the application specified by |cl| and wait for it to exit. Stores
753// the output (stdout) in |output|. If |do_search_path| is set, it searches the
[email protected]f164cea2009-11-05 23:37:40754// path for the application; in that case, |envp| must be null, and it will use
755// the current environment. If |do_search_path| is false, |cl| should fully
756// specify the path of the application, and |envp| will be used as the
757// environment. Redirects stderr to /dev/null. Returns true on success
758// (application launched and exited cleanly, with exit code indicating success).
[email protected]f164cea2009-11-05 23:37:40759static bool GetAppOutputInternal(const CommandLine& cl, char* const envp[],
760 std::string* output, size_t max_output,
[email protected]3b9f5aa2010-07-28 04:02:34761 bool do_search_path) {
[email protected]b42c4652010-12-04 03:23:53762 // Doing a blocking wait for another command to finish counts as IO.
763 base::ThreadRestrictions::AssertIOAllowed();
764
[email protected]c0b210ee2009-04-17 09:57:52765 int pipe_fd[2];
766 pid_t pid;
[email protected]ef73044e2010-03-11 15:25:54767 InjectiveMultimap fd_shuffle1, fd_shuffle2;
768 const std::vector<std::string>& argv = cl.argv();
769 scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
770
771 fd_shuffle1.reserve(3);
772 fd_shuffle2.reserve(3);
[email protected]c0b210ee2009-04-17 09:57:52773
[email protected]f164cea2009-11-05 23:37:40774 // Either |do_search_path| should be false or |envp| should be null, but not
775 // both.
776 DCHECK(!do_search_path ^ !envp);
777
[email protected]c0b210ee2009-04-17 09:57:52778 if (pipe(pipe_fd) < 0)
779 return false;
780
781 switch (pid = fork()) {
782 case -1: // error
783 close(pipe_fd[0]);
784 close(pipe_fd[1]);
785 return false;
786 case 0: // child
787 {
[email protected]e9a8c19f2009-09-03 21:27:36788#if defined(OS_MACOSX)
789 RestoreDefaultExceptionHandler();
790#endif
[email protected]ef73044e2010-03-11 15:25:54791 // DANGER: no calls to malloc are allowed from now on:
792 // http://crbug.com/36678
[email protected]e9a8c19f2009-09-03 21:27:36793
[email protected]afa82472009-07-23 19:11:48794 // Obscure fork() rule: in the child, if you don't end up doing exec*(),
795 // you call _exit() instead of exit(). This is because _exit() does not
796 // call any previously-registered (in the parent) exit handlers, which
797 // might do things like block waiting for threads that don't even exist
798 // in the child.
[email protected]1912cfe2009-04-21 08:09:30799 int dev_null = open("/dev/null", O_WRONLY);
800 if (dev_null < 0)
[email protected]7a97d292009-07-22 18:21:27801 _exit(127);
[email protected]1912cfe2009-04-21 08:09:30802
[email protected]ef73044e2010-03-11 15:25:54803 fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
804 fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
805 fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
[email protected]3b9f5aa2010-07-28 04:02:34806 // Adding another element here? Remeber to increase the argument to
[email protected]ef73044e2010-03-11 15:25:54807 // reserve(), above.
[email protected]3f04f2b2009-04-30 19:40:03808
[email protected]ef73044e2010-03-11 15:25:54809 std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
810 std::back_inserter(fd_shuffle2));
811
812 if (!ShuffleFileDescriptors(&fd_shuffle1))
[email protected]7a97d292009-07-22 18:21:27813 _exit(127);
[email protected]3f04f2b2009-04-30 19:40:03814
[email protected]ef73044e2010-03-11 15:25:54815 CloseSuperfluousFds(fd_shuffle2);
[email protected]c0b210ee2009-04-17 09:57:52816
[email protected]c0b210ee2009-04-17 09:57:52817 for (size_t i = 0; i < argv.size(); i++)
818 argv_cstr[i] = const_cast<char*>(argv[i].c_str());
819 argv_cstr[argv.size()] = NULL;
[email protected]f164cea2009-11-05 23:37:40820 if (do_search_path)
821 execvp(argv_cstr[0], argv_cstr.get());
822 else
823 execve(argv_cstr[0], argv_cstr.get(), envp);
[email protected]7a97d292009-07-22 18:21:27824 _exit(127);
[email protected]c0b210ee2009-04-17 09:57:52825 }
826 default: // parent
827 {
828 // Close our writing end of pipe now. Otherwise later read would not
829 // be able to detect end of child's output (in theory we could still
830 // write to the pipe).
831 close(pipe_fd[1]);
832
[email protected]96878a212010-06-10 18:26:33833 output->clear();
[email protected]c0b210ee2009-04-17 09:57:52834 char buffer[256];
[email protected]983ef7f2010-01-04 16:17:13835 size_t output_buf_left = max_output;
[email protected]f164cea2009-11-05 23:37:40836 ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0|
837 // case in the logic below.
[email protected]c0b210ee2009-04-17 09:57:52838
[email protected]983ef7f2010-01-04 16:17:13839 while (output_buf_left > 0) {
[email protected]f164cea2009-11-05 23:37:40840 bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer,
[email protected]983ef7f2010-01-04 16:17:13841 std::min(output_buf_left, sizeof(buffer))));
[email protected]157c61b2009-05-01 21:37:31842 if (bytes_read <= 0)
[email protected]c0b210ee2009-04-17 09:57:52843 break;
[email protected]96878a212010-06-10 18:26:33844 output->append(buffer, bytes_read);
[email protected]983ef7f2010-01-04 16:17:13845 output_buf_left -= static_cast<size_t>(bytes_read);
[email protected]c0b210ee2009-04-17 09:57:52846 }
[email protected]c0b210ee2009-04-17 09:57:52847 close(pipe_fd[0]);
[email protected]affb1842009-06-10 16:56:31848
[email protected]3b9f5aa2010-07-28 04:02:34849 // Always wait for exit code (even if we know we'll declare success).
850 int exit_code = EXIT_FAILURE;
851 bool success = WaitForExitCode(pid, &exit_code);
[email protected]983ef7f2010-01-04 16:17:13852
[email protected]f164cea2009-11-05 23:37:40853 // If we stopped because we read as much as we wanted, we always declare
854 // success (because the child may exit due to |SIGPIPE|).
[email protected]983ef7f2010-01-04 16:17:13855 if (output_buf_left || bytes_read <= 0) {
[email protected]f164cea2009-11-05 23:37:40856 if (!success || exit_code != EXIT_SUCCESS)
857 return false;
858 }
[email protected]affb1842009-06-10 16:56:31859
[email protected]c0b210ee2009-04-17 09:57:52860 return true;
861 }
862 }
863}
864
[email protected]f164cea2009-11-05 23:37:40865bool GetAppOutput(const CommandLine& cl, std::string* output) {
866 // Run |execve()| with the current environment and store "unlimited" data.
867 return GetAppOutputInternal(cl, NULL, output,
[email protected]3b9f5aa2010-07-28 04:02:34868 std::numeric_limits<std::size_t>::max(), true);
[email protected]f164cea2009-11-05 23:37:40869}
870
[email protected]3b9f5aa2010-07-28 04:02:34871// TODO(viettrungluu): Conceivably, we should have a timeout as well, so we
872// don't hang if what we're calling hangs.
[email protected]f164cea2009-11-05 23:37:40873bool GetAppOutputRestricted(const CommandLine& cl,
874 std::string* output, size_t max_output) {
875 // Run |execve()| with the empty environment.
876 char* const empty_environ = NULL;
[email protected]3b9f5aa2010-07-28 04:02:34877 return GetAppOutputInternal(cl, &empty_environ, output, max_output, false);
[email protected]f164cea2009-11-05 23:37:40878}
879
[email protected]4f260d02010-12-23 18:35:42880bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
[email protected]743ace42009-06-17 17:23:51881 int64 wait_milliseconds,
[email protected]962dd312009-02-05 21:44:13882 const ProcessFilter* filter) {
883 bool result = false;
884
885 // TODO(port): This is inefficient, but works if there are multiple procs.
886 // TODO(port): use waitpid to avoid leaving zombies around
887
888 base::Time end_time = base::Time::Now() +
889 base::TimeDelta::FromMilliseconds(wait_milliseconds);
890 do {
891 NamedProcessIterator iter(executable_name, filter);
892 if (!iter.NextProcessEntry()) {
893 result = true;
894 break;
895 }
[email protected]ce072a72010-12-31 20:02:16896 base::PlatformThread::Sleep(100);
[email protected]962dd312009-02-05 21:44:13897 } while ((base::Time::Now() - end_time) > base::TimeDelta());
898
899 return result;
900}
901
[email protected]4f260d02010-12-23 18:35:42902bool CleanupProcesses(const FilePath::StringType& executable_name,
[email protected]743ace42009-06-17 17:23:51903 int64 wait_milliseconds,
[email protected]962dd312009-02-05 21:44:13904 int exit_code,
905 const ProcessFilter* filter) {
906 bool exited_cleanly =
907 WaitForProcessesToExit(executable_name, wait_milliseconds,
[email protected]743ace42009-06-17 17:23:51908 filter);
[email protected]962dd312009-02-05 21:44:13909 if (!exited_cleanly)
910 KillProcesses(executable_name, exit_code, filter);
911 return exited_cleanly;
912}
913
[email protected]aa660752008-11-14 03:39:46914} // namespace base