Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(457)

Side by Side Diff: base/process_util_posix.cc

Issue 2810014: Add and alternative GetAppOutput() to process_util that takes a timeout.... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/process_util.h ('k') | base/process_util_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 <dirent.h> 5 #include <dirent.h>
6 #include <errno.h> 6 #include <errno.h>
7 #include <fcntl.h> 7 #include <fcntl.h>
8 #include <poll.h>
8 #include <signal.h> 9 #include <signal.h>
9 #include <stdlib.h> 10 #include <stdlib.h>
10 #include <sys/resource.h> 11 #include <sys/resource.h>
11 #include <sys/time.h> 12 #include <sys/time.h>
12 #include <sys/types.h> 13 #include <sys/types.h>
13 #include <sys/wait.h> 14 #include <sys/wait.h>
14 #include <unistd.h> 15 #include <unistd.h>
15 16
16 #include <limits> 17 #include <limits>
17 #include <set> 18 #include <set>
(...skipping 712 matching lines...) Expand 10 before | Expand all | Expand 10 after
730 } 731 }
731 732
732 int64 TimeValToMicroseconds(const struct timeval& tv) { 733 int64 TimeValToMicroseconds(const struct timeval& tv) {
733 static const int kMicrosecondsPerSecond = 1000000; 734 static const int kMicrosecondsPerSecond = 1000000;
734 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow. 735 int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow.
735 ret *= kMicrosecondsPerSecond; 736 ret *= kMicrosecondsPerSecond;
736 ret += tv.tv_usec; 737 ret += tv.tv_usec;
737 return ret; 738 return ret;
738 } 739 }
739 740
740 // Executes the application specified by |cl| and wait for it to exit. Stores 741 // Executes the application specified by |cl| and waits for it to exit, but
741 // the output (stdout) in |output|. If |do_search_path| is set, it searches the 742 // at most |timeout_milliseconds| (use kNoTimeout for no timeout). Stores
743 // the output (stdout) in |output|, and whether or not the timeout
744 // triggered in |timed_out|. If |do_search_path| is set, it searches the
742 // path for the application; in that case, |envp| must be null, and it will use 745 // path for the application; in that case, |envp| must be null, and it will use
743 // the current environment. If |do_search_path| is false, |cl| should fully 746 // the current environment. If |do_search_path| is false, |cl| should fully
744 // specify the path of the application, and |envp| will be used as the 747 // specify the path of the application, and |envp| will be used as the
745 // environment. Redirects stderr to /dev/null. Returns true on success 748 // environment. Redirects stderr to /dev/null. Returns true on success
746 // (application launched and exited cleanly, with exit code indicating success). 749 // (application launched and exited cleanly, with exit code indicating success).
747 static bool GetAppOutputInternal(const CommandLine& cl, char* const envp[], 750 static bool GetAppOutputInternal(const CommandLine& cl, char* const envp[],
748 std::string* output, size_t max_output, 751 std::string* output, size_t max_output,
749 bool do_search_path) { 752 bool do_search_path, bool* timed_out,
753 int timeout_milliseconds) {
750 int pipe_fd[2]; 754 int pipe_fd[2];
751 pid_t pid; 755 pid_t pid;
752 InjectiveMultimap fd_shuffle1, fd_shuffle2; 756 InjectiveMultimap fd_shuffle1, fd_shuffle2;
753 const std::vector<std::string>& argv = cl.argv(); 757 const std::vector<std::string>& argv = cl.argv();
754 scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); 758 scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
755 759
756 fd_shuffle1.reserve(3); 760 fd_shuffle1.reserve(3);
757 fd_shuffle2.reserve(3); 761 fd_shuffle2.reserve(3);
758 762
759 // Either |do_search_path| should be false or |envp| should be null, but not 763 // Either |do_search_path| should be false or |envp| should be null, but not
(...skipping 21 matching lines...) Expand all
781 // call any previously-registered (in the parent) exit handlers, which 785 // call any previously-registered (in the parent) exit handlers, which
782 // might do things like block waiting for threads that don't even exist 786 // might do things like block waiting for threads that don't even exist
783 // in the child. 787 // in the child.
784 int dev_null = open("/dev/null", O_WRONLY); 788 int dev_null = open("/dev/null", O_WRONLY);
785 if (dev_null < 0) 789 if (dev_null < 0)
786 _exit(127); 790 _exit(127);
787 791
788 fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true)); 792 fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
789 fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true)); 793 fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
790 fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true)); 794 fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
791 // Adding another element here? Remeber to increase the argument to 795 // Adding another element here? Remember to increase the argument to
792 // reserve(), above. 796 // reserve(), above.
793 797
794 std::copy(fd_shuffle1.begin(), fd_shuffle1.end(), 798 std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
795 std::back_inserter(fd_shuffle2)); 799 std::back_inserter(fd_shuffle2));
796 800
797 if (!ShuffleFileDescriptors(&fd_shuffle1)) 801 if (!ShuffleFileDescriptors(&fd_shuffle1))
798 _exit(127); 802 _exit(127);
799 803
800 CloseSuperfluousFds(fd_shuffle2); 804 CloseSuperfluousFds(fd_shuffle2);
801 805
(...skipping 12 matching lines...) Expand all
814 // be able to detect end of child's output (in theory we could still 818 // be able to detect end of child's output (in theory we could still
815 // write to the pipe). 819 // write to the pipe).
816 close(pipe_fd[1]); 820 close(pipe_fd[1]);
817 821
818 output->clear(); 822 output->clear();
819 char buffer[256]; 823 char buffer[256];
820 size_t output_buf_left = max_output; 824 size_t output_buf_left = max_output;
821 ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0| 825 ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0|
822 // case in the logic below. 826 // case in the logic below.
823 827
828 *timed_out = false;
829 const Time timeout_time = Time::Now() +
830 TimeDelta::FromMilliseconds(timeout_milliseconds);
831 int remaining_ms = timeout_milliseconds;
824 while (output_buf_left > 0) { 832 while (output_buf_left > 0) {
833 struct pollfd poll_struct = { pipe_fd[0], POLLIN, 0 };
834 const int poll_ret_code = poll(&poll_struct, 1, remaining_ms);
835
836 // poll() should only fail due to interrupt.
837 DCHECK(poll_ret_code != -1 || errno == EINTR);
838 if (poll_ret_code == 0) {
839 *timed_out = true;
840 break;
841 }
842
825 bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer, 843 bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer,
826 std::min(output_buf_left, sizeof(buffer)))); 844 std::min(output_buf_left, sizeof(buffer))));
827 if (bytes_read <= 0) 845 if (bytes_read <= 0)
828 break; 846 break;
829 output->append(buffer, bytes_read); 847 output->append(buffer, bytes_read);
830 output_buf_left -= static_cast<size_t>(bytes_read); 848 output_buf_left -= static_cast<size_t>(bytes_read);
849
850 // Update remaining_ms if we're not using an infinite timeout.
851 if (timeout_milliseconds >= 0) {
852 remaining_ms = (timeout_time - Time::Now()).InMilliseconds();
853 if (remaining_ms < 0) {
854 *timed_out = true;
855 break;
856 }
857 }
831 } 858 }
832 close(pipe_fd[0]); 859 close(pipe_fd[0]);
833 860
834 // Always wait for exit code (even if we know we'll declare success). 861 bool success = false;
835 int exit_code = EXIT_FAILURE; 862 int exit_code = PROCESS_END_PROCESS_WAS_HUNG;
836 bool success = WaitForExitCode(pid, &exit_code); 863 if (*timed_out) {
864 KillProcess(pid, exit_code, true);
865 // This waits up to 60 seconds for process to actually be dead
866 // which means this may overrun the timeout.
867 LOG(ERROR) << "Process "<< pid << " killed by timeout (waited "
868 << timeout_milliseconds << " ms).";
869 } else {
870 success = WaitForExitCode(pid, &exit_code);
871 }
837 872
838 // If we stopped because we read as much as we wanted, we always declare 873 // If we stopped because we read as much as we wanted, we always declare
839 // success (because the child may exit due to |SIGPIPE|). 874 // success (because the child may exit due to |SIGPIPE|).
840 if (output_buf_left || bytes_read <= 0) { 875 if (output_buf_left || bytes_read <= 0) {
841 if (!success || exit_code != EXIT_SUCCESS) 876 if (!success || exit_code != EXIT_SUCCESS)
842 return false; 877 return false;
843 } 878 }
844 879
845 return true; 880 return true;
846 } 881 }
847 } 882 }
848 } 883 }
849 884
850 bool GetAppOutput(const CommandLine& cl, std::string* output) { 885 bool GetAppOutput(const CommandLine& cl, std::string* output) {
851 // Run |execve()| with the current environment and store "unlimited" data. 886 // Run |execve()| with the current environment and store "unlimited" data.
887 bool timed_out;
852 return GetAppOutputInternal(cl, NULL, output, 888 return GetAppOutputInternal(cl, NULL, output,
853 std::numeric_limits<std::size_t>::max(), true); 889 std::numeric_limits<std::size_t>::max(), true,
890 &timed_out, base::kNoTimeout);
854 } 891 }
855 892
856 // TODO(viettrungluu): Conceivably, we should have a timeout as well, so we 893 bool GetAppOutputWithTimeout(const CommandLine& cl, std::string* output,
857 // don't hang if what we're calling hangs. 894 bool* timed_out, int timeout_milliseconds) {
895 return GetAppOutputInternal(cl, NULL, output,
896 std::numeric_limits<std::size_t>::max(), true,
897 timed_out, timeout_milliseconds);
898 }
899
858 bool GetAppOutputRestricted(const CommandLine& cl, 900 bool GetAppOutputRestricted(const CommandLine& cl,
859 std::string* output, size_t max_output) { 901 std::string* output, size_t max_output) {
860 // Run |execve()| with the empty environment. 902 // Run |execve()| with the empty environment.
861 char* const empty_environ = NULL; 903 char* const empty_environ = NULL;
862 return GetAppOutputInternal(cl, &empty_environ, output, max_output, false); 904 bool timed_out;
905 return GetAppOutputInternal(cl, &empty_environ, output, max_output, false,
906 &timed_out, base::kNoTimeout);
863 } 907 }
864 908
865 bool WaitForProcessesToExit(const std::wstring& executable_name, 909 bool WaitForProcessesToExit(const std::wstring& executable_name,
866 int64 wait_milliseconds, 910 int64 wait_milliseconds,
867 const ProcessFilter* filter) { 911 const ProcessFilter* filter) {
868 bool result = false; 912 bool result = false;
869 913
870 // TODO(port): This is inefficient, but works if there are multiple procs. 914 // TODO(port): This is inefficient, but works if there are multiple procs.
871 // TODO(port): use waitpid to avoid leaving zombies around 915 // TODO(port): use waitpid to avoid leaving zombies around
872 916
(...skipping 17 matching lines...) Expand all
890 const ProcessFilter* filter) { 934 const ProcessFilter* filter) {
891 bool exited_cleanly = 935 bool exited_cleanly =
892 WaitForProcessesToExit(executable_name, wait_milliseconds, 936 WaitForProcessesToExit(executable_name, wait_milliseconds,
893 filter); 937 filter);
894 if (!exited_cleanly) 938 if (!exited_cleanly)
895 KillProcesses(executable_name, exit_code, filter); 939 KillProcesses(executable_name, exit_code, filter);
896 return exited_cleanly; 940 return exited_cleanly;
897 } 941 }
898 942
899 } // namespace base 943 } // namespace base
OLDNEW
« no previous file with comments | « base/process_util.h ('k') | base/process_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698