OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/process/launch.h" | 5 #include "base/process/launch.h" |
6 | 6 |
7 #include <dirent.h> | 7 #include <dirent.h> |
8 #include <errno.h> | 8 #include <errno.h> |
9 #include <fcntl.h> | 9 #include <fcntl.h> |
10 #include <sched.h> | 10 #include <sched.h> |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 // glibc's sigaction() will prevent access to sa_restorer, so we need to roll | 145 // glibc's sigaction() will prevent access to sa_restorer, so we need to roll |
146 // our own. | 146 // our own. |
147 int sys_rt_sigaction(int sig, const struct kernel_sigaction* act, | 147 int sys_rt_sigaction(int sig, const struct kernel_sigaction* act, |
148 struct kernel_sigaction* oact) { | 148 struct kernel_sigaction* oact) { |
149 return syscall(SYS_rt_sigaction, sig, act, oact, sizeof(kernel_sigset_t)); | 149 return syscall(SYS_rt_sigaction, sig, act, oact, sizeof(kernel_sigset_t)); |
150 } | 150 } |
151 | 151 |
152 // This function is intended to be used in between fork() and execve() and will | 152 // This function is intended to be used in between fork() and execve() and will |
153 // reset all signal handlers to the default. | 153 // reset all signal handlers to the default. |
154 // The motivation for going through all of them is that sa_restorer can leak | 154 // The motivation for going through all of them is that sa_restorer can leak |
155 // from parents and help defeat ASLR on buggy kernels. We reset it to NULL. | 155 // from parents and help defeat ASLR on buggy kernels. We reset it to null. |
156 // See crbug.com/177956. | 156 // See crbug.com/177956. |
157 void ResetChildSignalHandlersToDefaults(void) { | 157 void ResetChildSignalHandlersToDefaults(void) { |
158 for (int signum = 1; ; ++signum) { | 158 for (int signum = 1; ; ++signum) { |
159 struct kernel_sigaction act = {0}; | 159 struct kernel_sigaction act = {0}; |
160 int sigaction_get_ret = sys_rt_sigaction(signum, NULL, &act); | 160 int sigaction_get_ret = sys_rt_sigaction(signum, nullptr, &act); |
161 if (sigaction_get_ret && errno == EINVAL) { | 161 if (sigaction_get_ret && errno == EINVAL) { |
162 #if !defined(NDEBUG) | 162 #if !defined(NDEBUG) |
163 // Linux supports 32 real-time signals from 33 to 64. | 163 // Linux supports 32 real-time signals from 33 to 64. |
164 // If the number of signals in the Linux kernel changes, someone should | 164 // If the number of signals in the Linux kernel changes, someone should |
165 // look at this code. | 165 // look at this code. |
166 const int kNumberOfSignals = 64; | 166 const int kNumberOfSignals = 64; |
167 RAW_CHECK(signum == kNumberOfSignals + 1); | 167 RAW_CHECK(signum == kNumberOfSignals + 1); |
168 #endif // !defined(NDEBUG) | 168 #endif // !defined(NDEBUG) |
169 break; | 169 break; |
170 } | 170 } |
171 // All other failures are fatal. | 171 // All other failures are fatal. |
172 if (sigaction_get_ret) { | 172 if (sigaction_get_ret) { |
173 RAW_LOG(FATAL, "sigaction (get) failed."); | 173 RAW_LOG(FATAL, "sigaction (get) failed."); |
174 } | 174 } |
175 | 175 |
176 // The kernel won't allow to re-set SIGKILL or SIGSTOP. | 176 // The kernel won't allow to re-set SIGKILL or SIGSTOP. |
177 if (signum != SIGSTOP && signum != SIGKILL) { | 177 if (signum != SIGSTOP && signum != SIGKILL) { |
178 act.k_sa_handler = reinterpret_cast<void*>(SIG_DFL); | 178 act.k_sa_handler = reinterpret_cast<void*>(SIG_DFL); |
179 act.k_sa_restorer = NULL; | 179 act.k_sa_restorer = nullptr; |
180 if (sys_rt_sigaction(signum, &act, NULL)) { | 180 if (sys_rt_sigaction(signum, &act, nullptr)) { |
181 RAW_LOG(FATAL, "sigaction (set) failed."); | 181 RAW_LOG(FATAL, "sigaction (set) failed."); |
182 } | 182 } |
183 } | 183 } |
184 #if !defined(NDEBUG) | 184 #if !defined(NDEBUG) |
185 // Now ask the kernel again and check that no restorer will leak. | 185 // Now ask the kernel again and check that no restorer will leak. |
186 if (sys_rt_sigaction(signum, NULL, &act) || act.k_sa_restorer) { | 186 if (sys_rt_sigaction(signum, nullptr, &act) || act.k_sa_restorer) { |
187 RAW_LOG(FATAL, "Cound not fix sa_restorer."); | 187 RAW_LOG(FATAL, "Cound not fix sa_restorer."); |
188 } | 188 } |
189 #endif // !defined(NDEBUG) | 189 #endif // !defined(NDEBUG) |
190 } | 190 } |
191 } | 191 } |
192 #endif // !defined(OS_LINUX) || | 192 #endif // !defined(OS_LINUX) || |
193 // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__)) | 193 // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__)) |
194 } // anonymous namespace | 194 } // anonymous namespace |
195 | 195 |
196 // Functor for |ScopedDIR| (below). | 196 // Functor for |ScopedDIR| (below). |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 | 298 |
299 InjectiveMultimap fd_shuffle1; | 299 InjectiveMultimap fd_shuffle1; |
300 InjectiveMultimap fd_shuffle2; | 300 InjectiveMultimap fd_shuffle2; |
301 fd_shuffle1.reserve(fd_shuffle_size); | 301 fd_shuffle1.reserve(fd_shuffle_size); |
302 fd_shuffle2.reserve(fd_shuffle_size); | 302 fd_shuffle2.reserve(fd_shuffle_size); |
303 | 303 |
304 std::unique_ptr<char* []> argv_cstr(new char*[argv.size() + 1]); | 304 std::unique_ptr<char* []> argv_cstr(new char*[argv.size() + 1]); |
305 for (size_t i = 0; i < argv.size(); i++) { | 305 for (size_t i = 0; i < argv.size(); i++) { |
306 argv_cstr[i] = const_cast<char*>(argv[i].c_str()); | 306 argv_cstr[i] = const_cast<char*>(argv[i].c_str()); |
307 } | 307 } |
308 argv_cstr[argv.size()] = NULL; | 308 argv_cstr[argv.size()] = nullptr; |
309 | 309 |
310 std::unique_ptr<char* []> new_environ; | 310 std::unique_ptr<char* []> new_environ; |
311 char* const empty_environ = NULL; | 311 char* const empty_environ = nullptr; |
312 char* const* old_environ = GetEnvironment(); | 312 char* const* old_environ = GetEnvironment(); |
313 if (options.clear_environ) | 313 if (options.clear_environ) |
314 old_environ = &empty_environ; | 314 old_environ = &empty_environ; |
315 if (!options.environ.empty()) | 315 if (!options.environ.empty()) |
316 new_environ = AlterEnvironment(old_environ, options.environ); | 316 new_environ = AlterEnvironment(old_environ, options.environ); |
317 | 317 |
318 sigset_t full_sigset; | 318 sigset_t full_sigset; |
319 sigfillset(&full_sigset); | 319 sigfillset(&full_sigset); |
320 const sigset_t orig_sigmask = SetSignalMask(full_sigset); | 320 const sigset_t orig_sigmask = SetSignalMask(full_sigset); |
321 | 321 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095); | 423 reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095); |
424 mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); | 424 mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); |
425 memset(reinterpret_cast<void*>(malloc), 0xff, 8); | 425 memset(reinterpret_cast<void*>(malloc), 0xff, 8); |
426 #endif // 0 | 426 #endif // 0 |
427 | 427 |
428 #if defined(OS_CHROMEOS) | 428 #if defined(OS_CHROMEOS) |
429 if (options.ctrl_terminal_fd >= 0) { | 429 if (options.ctrl_terminal_fd >= 0) { |
430 // Set process' controlling terminal. | 430 // Set process' controlling terminal. |
431 if (HANDLE_EINTR(setsid()) != -1) { | 431 if (HANDLE_EINTR(setsid()) != -1) { |
432 if (HANDLE_EINTR( | 432 if (HANDLE_EINTR( |
433 ioctl(options.ctrl_terminal_fd, TIOCSCTTY, NULL)) == -1) { | 433 ioctl(options.ctrl_terminal_fd, TIOCSCTTY, nullptr)) == -1) { |
434 RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set"); | 434 RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set"); |
435 } | 435 } |
436 } else { | 436 } else { |
437 RAW_LOG(WARNING, "setsid failed, ctrl terminal not set"); | 437 RAW_LOG(WARNING, "setsid failed, ctrl terminal not set"); |
438 } | 438 } |
439 } | 439 } |
440 #endif // defined(OS_CHROMEOS) | 440 #endif // defined(OS_CHROMEOS) |
441 | 441 |
442 if (options.fds_to_remap) { | 442 if (options.fds_to_remap) { |
443 // Cannot use STL iterators here, since debug iterators use locks. | 443 // Cannot use STL iterators here, since debug iterators use locks. |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
504 } | 504 } |
505 | 505 |
506 return Process(pid); | 506 return Process(pid); |
507 } | 507 } |
508 | 508 |
509 void RaiseProcessToHighPriority() { | 509 void RaiseProcessToHighPriority() { |
510 // On POSIX, we don't actually do anything here. We could try to nice() or | 510 // On POSIX, we don't actually do anything here. We could try to nice() or |
511 // setpriority() or sched_getscheduler, but these all require extra rights. | 511 // setpriority() or sched_getscheduler, but these all require extra rights. |
512 } | 512 } |
513 | 513 |
514 // Return value used by GetAppOutputInternal to encapsulate the various exit | |
515 // scenarios from the function. | |
516 enum GetAppOutputInternalResult { | |
517 EXECUTE_FAILURE, | |
518 EXECUTE_SUCCESS, | |
519 GOT_MAX_OUTPUT, | |
520 }; | |
521 | |
522 // Executes the application specified by |argv| and wait for it to exit. Stores | 514 // Executes the application specified by |argv| and wait for it to exit. Stores |
523 // the output (stdout) in |output|. If |do_search_path| is set, it searches the | 515 // the output (stdout) in |output|. If |do_search_path| is set, it searches the |
524 // path for the application; in that case, |envp| must be null, and it will use | 516 // path for the application; in that case, |envp| must be null, and it will use |
525 // the current environment. If |do_search_path| is false, |argv[0]| should fully | 517 // the current environment. If |do_search_path| is false, |argv[0]| should fully |
526 // specify the path of the application, and |envp| will be used as the | 518 // specify the path of the application, and |envp| will be used as the |
527 // environment. If |include_stderr| is true, includes stderr otherwise redirects | 519 // environment. If |include_stderr| is true, includes stderr otherwise redirects |
528 // it to /dev/null. | 520 // it to /dev/null. |
529 // If we successfully start the application and get all requested output, we | 521 // The return value of the function indicates success or failure. In the case of |
530 // return GOT_MAX_OUTPUT, or if there is a problem starting or exiting | 522 // success, the application exit code will be returned in |*exit_code|, which |
531 // the application we return RUN_FAILURE. Otherwise we return EXECUTE_SUCCESS. | 523 // should be checked to determine if the application ran successfully. |
532 // The GOT_MAX_OUTPUT return value exists so a caller that asks for limited | 524 static bool GetAppOutputInternal( |
533 // output can treat this as a success, despite having an exit code of SIG_PIPE | |
534 // due to us closing the output pipe. | |
535 // In the case of EXECUTE_SUCCESS, the application exit code will be returned | |
536 // in |*exit_code|, which should be checked to determine if the application | |
537 // ran successfully. | |
538 static GetAppOutputInternalResult GetAppOutputInternal( | |
539 const std::vector<std::string>& argv, | 525 const std::vector<std::string>& argv, |
540 char* const envp[], | 526 char* const envp[], |
541 bool include_stderr, | 527 bool include_stderr, |
542 std::string* output, | 528 std::string* output, |
543 size_t max_output, | |
544 bool do_search_path, | 529 bool do_search_path, |
545 int* exit_code) { | 530 int* exit_code) { |
546 // Doing a blocking wait for another command to finish counts as IO. | 531 // Doing a blocking wait for another command to finish counts as IO. |
547 base::ThreadRestrictions::AssertIOAllowed(); | 532 base::ThreadRestrictions::AssertIOAllowed(); |
548 // exit_code must be supplied so calling function can determine success. | 533 // exit_code must be supplied so calling function can determine success. |
549 DCHECK(exit_code); | 534 DCHECK(exit_code); |
550 *exit_code = EXIT_FAILURE; | 535 *exit_code = EXIT_FAILURE; |
551 | 536 |
552 int pipe_fd[2]; | 537 int pipe_fd[2]; |
553 pid_t pid; | 538 pid_t pid; |
554 InjectiveMultimap fd_shuffle1, fd_shuffle2; | 539 InjectiveMultimap fd_shuffle1, fd_shuffle2; |
555 std::unique_ptr<char* []> argv_cstr(new char*[argv.size() + 1]); | 540 std::unique_ptr<char* []> argv_cstr(new char*[argv.size() + 1]); |
556 | 541 |
557 fd_shuffle1.reserve(3); | 542 fd_shuffle1.reserve(3); |
558 fd_shuffle2.reserve(3); | 543 fd_shuffle2.reserve(3); |
559 | 544 |
560 // Either |do_search_path| should be false or |envp| should be null, but not | 545 // Either |do_search_path| should be false or |envp| should be null, but not |
561 // both. | 546 // both. |
562 DCHECK(!do_search_path ^ !envp); | 547 DCHECK(!do_search_path ^ !envp); |
563 | 548 |
564 if (pipe(pipe_fd) < 0) | 549 if (pipe(pipe_fd) < 0) |
565 return EXECUTE_FAILURE; | 550 return false; |
566 | 551 |
567 switch (pid = fork()) { | 552 switch (pid = fork()) { |
568 case -1: // error | 553 case -1: // error |
569 close(pipe_fd[0]); | 554 close(pipe_fd[0]); |
570 close(pipe_fd[1]); | 555 close(pipe_fd[1]); |
571 return EXECUTE_FAILURE; | 556 return false; |
572 case 0: // child | 557 case 0: // child |
573 { | 558 { |
574 // DANGER: no calls to malloc or locks are allowed from now on: | 559 // DANGER: no calls to malloc or locks are allowed from now on: |
575 // http://crbug.com/36678 | 560 // http://crbug.com/36678 |
576 | 561 |
577 #if defined(OS_MACOSX) | 562 #if defined(OS_MACOSX) |
578 RestoreDefaultExceptionHandler(); | 563 RestoreDefaultExceptionHandler(); |
579 #endif | 564 #endif |
580 | 565 |
581 // Obscure fork() rule: in the child, if you don't end up doing exec*(), | 566 // Obscure fork() rule: in the child, if you don't end up doing exec*(), |
(...skipping 16 matching lines...) Expand all Loading... |
598 for (size_t i = 0; i < fd_shuffle1.size(); ++i) | 583 for (size_t i = 0; i < fd_shuffle1.size(); ++i) |
599 fd_shuffle2.push_back(fd_shuffle1[i]); | 584 fd_shuffle2.push_back(fd_shuffle1[i]); |
600 | 585 |
601 if (!ShuffleFileDescriptors(&fd_shuffle1)) | 586 if (!ShuffleFileDescriptors(&fd_shuffle1)) |
602 _exit(127); | 587 _exit(127); |
603 | 588 |
604 CloseSuperfluousFds(fd_shuffle2); | 589 CloseSuperfluousFds(fd_shuffle2); |
605 | 590 |
606 for (size_t i = 0; i < argv.size(); i++) | 591 for (size_t i = 0; i < argv.size(); i++) |
607 argv_cstr[i] = const_cast<char*>(argv[i].c_str()); | 592 argv_cstr[i] = const_cast<char*>(argv[i].c_str()); |
608 argv_cstr[argv.size()] = NULL; | 593 argv_cstr[argv.size()] = nullptr; |
609 if (do_search_path) | 594 if (do_search_path) |
610 execvp(argv_cstr[0], argv_cstr.get()); | 595 execvp(argv_cstr[0], argv_cstr.get()); |
611 else | 596 else |
612 execve(argv_cstr[0], argv_cstr.get(), envp); | 597 execve(argv_cstr[0], argv_cstr.get(), envp); |
613 _exit(127); | 598 _exit(127); |
614 } | 599 } |
615 default: // parent | 600 default: // parent |
616 { | 601 { |
617 // Close our writing end of pipe now. Otherwise later read would not | 602 // Close our writing end of pipe now. Otherwise later read would not |
618 // be able to detect end of child's output (in theory we could still | 603 // be able to detect end of child's output (in theory we could still |
619 // write to the pipe). | 604 // write to the pipe). |
620 close(pipe_fd[1]); | 605 close(pipe_fd[1]); |
621 | 606 |
622 output->clear(); | 607 output->clear(); |
623 char buffer[256]; | |
624 size_t output_buf_left = max_output; | |
625 ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0| | |
626 // case in the logic below. | |
627 | 608 |
628 while (output_buf_left > 0) { | 609 while (true) { |
629 bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer, | 610 char buffer[256]; |
630 std::min(output_buf_left, sizeof(buffer)))); | 611 ssize_t bytes_read = |
| 612 HANDLE_EINTR(read(pipe_fd[0], buffer, sizeof(buffer))); |
631 if (bytes_read <= 0) | 613 if (bytes_read <= 0) |
632 break; | 614 break; |
633 output->append(buffer, bytes_read); | 615 output->append(buffer, bytes_read); |
634 output_buf_left -= static_cast<size_t>(bytes_read); | |
635 } | 616 } |
636 close(pipe_fd[0]); | 617 close(pipe_fd[0]); |
637 | 618 |
638 // Always wait for exit code (even if we know we'll declare | 619 // Always wait for exit code (even if we know we'll declare |
639 // GOT_MAX_OUTPUT). | 620 // GOT_MAX_OUTPUT). |
640 Process process(pid); | 621 Process process(pid); |
641 bool success = process.WaitForExit(exit_code); | 622 return process.WaitForExit(exit_code); |
642 | |
643 // If we stopped because we read as much as we wanted, we return | |
644 // GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|). | |
645 if (!output_buf_left && bytes_read > 0) | |
646 return GOT_MAX_OUTPUT; | |
647 else if (success) | |
648 return EXECUTE_SUCCESS; | |
649 return EXECUTE_FAILURE; | |
650 } | 623 } |
651 } | 624 } |
652 } | 625 } |
653 | 626 |
654 bool GetAppOutput(const CommandLine& cl, std::string* output) { | 627 bool GetAppOutput(const CommandLine& cl, std::string* output) { |
655 return GetAppOutput(cl.argv(), output); | 628 return GetAppOutput(cl.argv(), output); |
656 } | 629 } |
657 | 630 |
658 bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) { | 631 bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) { |
659 // Run |execve()| with the current environment and store "unlimited" data. | 632 // Run |execve()| with the current environment. |
660 int exit_code; | 633 int exit_code; |
661 GetAppOutputInternalResult result = GetAppOutputInternal( | 634 bool result = |
662 argv, NULL, false, output, std::numeric_limits<std::size_t>::max(), true, | 635 GetAppOutputInternal(argv, nullptr, false, output, true, &exit_code); |
663 &exit_code); | 636 return result && exit_code == EXIT_SUCCESS; |
664 return result == EXECUTE_SUCCESS && exit_code == EXIT_SUCCESS; | |
665 } | 637 } |
666 | 638 |
667 bool GetAppOutputAndError(const CommandLine& cl, std::string* output) { | 639 bool GetAppOutputAndError(const CommandLine& cl, std::string* output) { |
668 // Run |execve()| with the current environment and store "unlimited" data. | 640 // Run |execve()| with the current environment. |
669 int exit_code; | 641 int exit_code; |
670 GetAppOutputInternalResult result = GetAppOutputInternal( | 642 bool result = |
671 cl.argv(), NULL, true, output, std::numeric_limits<std::size_t>::max(), | 643 GetAppOutputInternal(cl.argv(), nullptr, true, output, true, &exit_code); |
672 true, &exit_code); | 644 return result && exit_code == EXIT_SUCCESS; |
673 return result == EXECUTE_SUCCESS && exit_code == EXIT_SUCCESS; | |
674 } | |
675 | |
676 // TODO(viettrungluu): Conceivably, we should have a timeout as well, so we | |
677 // don't hang if what we're calling hangs. | |
678 bool GetAppOutputRestricted(const CommandLine& cl, | |
679 std::string* output, size_t max_output) { | |
680 // Run |execve()| with the empty environment. | |
681 char* const empty_environ = NULL; | |
682 int exit_code; | |
683 GetAppOutputInternalResult result = GetAppOutputInternal( | |
684 cl.argv(), &empty_environ, false, output, max_output, false, &exit_code); | |
685 return result == GOT_MAX_OUTPUT || (result == EXECUTE_SUCCESS && | |
686 exit_code == EXIT_SUCCESS); | |
687 } | 645 } |
688 | 646 |
689 bool GetAppOutputWithExitCode(const CommandLine& cl, | 647 bool GetAppOutputWithExitCode(const CommandLine& cl, |
690 std::string* output, | 648 std::string* output, |
691 int* exit_code) { | 649 int* exit_code) { |
692 // Run |execve()| with the current environment and store "unlimited" data. | 650 // Run |execve()| with the current environment. |
693 GetAppOutputInternalResult result = GetAppOutputInternal( | 651 return GetAppOutputInternal(cl.argv(), nullptr, false, output, true, |
694 cl.argv(), NULL, false, output, std::numeric_limits<std::size_t>::max(), | 652 exit_code); |
695 true, exit_code); | |
696 return result == EXECUTE_SUCCESS; | |
697 } | 653 } |
698 | 654 |
699 #endif // !defined(OS_NACL_NONSFI) | 655 #endif // !defined(OS_NACL_NONSFI) |
700 | 656 |
701 #if defined(OS_LINUX) || defined(OS_NACL_NONSFI) | 657 #if defined(OS_LINUX) || defined(OS_NACL_NONSFI) |
702 namespace { | 658 namespace { |
703 | 659 |
704 bool IsRunningOnValgrind() { | 660 bool IsRunningOnValgrind() { |
705 return RUNNING_ON_VALGRIND; | 661 return RUNNING_ON_VALGRIND; |
706 } | 662 } |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
783 jmp_buf env; | 739 jmp_buf env; |
784 if (setjmp(env) == 0) { | 740 if (setjmp(env) == 0) { |
785 return CloneAndLongjmpInChild(flags, ptid, ctid, &env); | 741 return CloneAndLongjmpInChild(flags, ptid, ctid, &env); |
786 } | 742 } |
787 | 743 |
788 return 0; | 744 return 0; |
789 } | 745 } |
790 #endif // defined(OS_LINUX) || defined(OS_NACL_NONSFI) | 746 #endif // defined(OS_LINUX) || defined(OS_NACL_NONSFI) |
791 | 747 |
792 } // namespace base | 748 } // namespace base |
OLD | NEW |