OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "content/common/process_watcher.h" | 5 #include "content/common/process_watcher.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <signal.h> | 8 #include <signal.h> |
9 #include <sys/event.h> | 9 #include <sys/event.h> |
10 #include <sys/types.h> | 10 #include <sys/types.h> |
11 #include <sys/wait.h> | 11 #include <sys/wait.h> |
12 | 12 |
13 #include "base/eintr_wrapper.h" | 13 #include "base/eintr_wrapper.h" |
14 #include "base/file_util.h" | 14 #include "base/file_util.h" |
15 #include "base/time.h" | 15 #include "base/time.h" |
16 | 16 |
17 namespace { | 17 namespace { |
18 | 18 |
19 const int kWaitBeforeKillSeconds = 2; | 19 const int kWaitBeforeKillSeconds = 2; |
20 | 20 |
21 // Reap |child| process. This call blocks until completion. | 21 // Reap |child| process. This call blocks until completion. |
22 void BlockingReap(pid_t child) { | 22 void BlockingReap(pid_t child) { |
23 const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0)); | 23 const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0)); |
24 if (result == -1) { | 24 if (result == -1) { |
25 PLOG(ERROR) << "waitpid(" << child << ", NULL, 0)"; | 25 DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)"; |
26 } | 26 } |
27 } | 27 } |
28 | 28 |
29 // Waits for |timeout| seconds for the given |child| to exit and reap it. If | 29 // Waits for |timeout| seconds for the given |child| to exit and reap it. If |
30 // the child doesn't exit within the time specified, kills it. | 30 // the child doesn't exit within the time specified, kills it. |
31 // | 31 // |
32 // This function takes two approaches: first, it tries to use kqueue to | 32 // This function takes two approaches: first, it tries to use kqueue to |
33 // observe when the process exits. kevent can monitor a kqueue with a | 33 // observe when the process exits. kevent can monitor a kqueue with a |
34 // timeout, so this method is preferred to wait for a specified period of | 34 // timeout, so this method is preferred to wait for a specified period of |
35 // time. Once the kqueue indicates the process has exited, waitpid will reap | 35 // time. Once the kqueue indicates the process has exited, waitpid will reap |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that | 70 // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that |
71 // |child| has been reaped. Specifically, even if a kqueue, kevent, or other | 71 // |child| has been reaped. Specifically, even if a kqueue, kevent, or other |
72 // call fails, this function should fall back to the last resort of trying | 72 // call fails, this function should fall back to the last resort of trying |
73 // to kill and reap the process. Not observing this rule will resurrect | 73 // to kill and reap the process. Not observing this rule will resurrect |
74 // zombies. | 74 // zombies. |
75 | 75 |
76 int result; | 76 int result; |
77 | 77 |
78 int kq = HANDLE_EINTR(kqueue()); | 78 int kq = HANDLE_EINTR(kqueue()); |
79 if (kq == -1) { | 79 if (kq == -1) { |
80 PLOG(ERROR) << "kqueue()"; | 80 DPLOG(ERROR) << "kqueue()"; |
81 } else { | 81 } else { |
82 file_util::ScopedFD auto_close_kq(&kq); | 82 file_util::ScopedFD auto_close_kq(&kq); |
83 | 83 |
84 struct kevent change = {0}; | 84 struct kevent change = {0}; |
85 EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); | 85 EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); |
86 result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); | 86 result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); |
87 | 87 |
88 if (result == -1) { | 88 if (result == -1) { |
89 if (errno != ESRCH) { | 89 if (errno != ESRCH) { |
90 PLOG(ERROR) << "kevent (setup " << child << ")"; | 90 DPLOG(ERROR) << "kevent (setup " << child << ")"; |
91 } else { | 91 } else { |
92 // At this point, one of the following has occurred: | 92 // At this point, one of the following has occurred: |
93 // 1. The process has died but has not yet been reaped. | 93 // 1. The process has died but has not yet been reaped. |
94 // 2. The process has died and has already been reaped. | 94 // 2. The process has died and has already been reaped. |
95 // 3. The process is in the process of dying. It's no longer | 95 // 3. The process is in the process of dying. It's no longer |
96 // kqueueable, but it may not be waitable yet either. Mark calls | 96 // kqueueable, but it may not be waitable yet either. Mark calls |
97 // this case the "zombie death race". | 97 // this case the "zombie death race". |
98 | 98 |
99 result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); | 99 result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); |
100 | 100 |
(...skipping 21 matching lines...) Expand all Loading... |
122 result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec); | 122 result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec); |
123 if (result == -1 && errno == EINTR) { | 123 if (result == -1 && errno == EINTR) { |
124 remaining_delta = deadline - base::Time::Now(); | 124 remaining_delta = deadline - base::Time::Now(); |
125 result = 0; | 125 result = 0; |
126 } else { | 126 } else { |
127 break; | 127 break; |
128 } | 128 } |
129 } | 129 } |
130 | 130 |
131 if (result == -1) { | 131 if (result == -1) { |
132 PLOG(ERROR) << "kevent (wait " << child << ")"; | 132 DPLOG(ERROR) << "kevent (wait " << child << ")"; |
133 } else if (result > 1) { | 133 } else if (result > 1) { |
134 LOG(ERROR) << "kevent (wait " << child << "): unexpected result " | 134 DLOG(ERROR) << "kevent (wait " << child << "): unexpected result " |
135 << result; | 135 << result; |
136 } else if (result == 1) { | 136 } else if (result == 1) { |
137 if ((event.fflags & NOTE_EXIT) && | 137 if ((event.fflags & NOTE_EXIT) && |
138 (event.ident == static_cast<uintptr_t>(child))) { | 138 (event.ident == static_cast<uintptr_t>(child))) { |
139 // The process is dead or dying. This won't block for long, if at | 139 // The process is dead or dying. This won't block for long, if at |
140 // all. | 140 // all. |
141 BlockingReap(child); | 141 BlockingReap(child); |
142 return; | 142 return; |
143 } else { | 143 } else { |
144 LOG(ERROR) << "kevent (wait " << child | 144 DLOG(ERROR) << "kevent (wait " << child |
145 << "): unexpected event: fflags=" << event.fflags | 145 << "): unexpected event: fflags=" << event.fflags |
146 << ", ident=" << event.ident; | 146 << ", ident=" << event.ident; |
147 } | 147 } |
148 } | 148 } |
149 } | 149 } |
150 } | 150 } |
151 | 151 |
152 // The child is still alive, or is very freshly dead. Be sure by sending it | 152 // The child is still alive, or is very freshly dead. Be sure by sending it |
153 // a signal. This is safe even if it's freshly dead, because it will be a | 153 // a signal. This is safe even if it's freshly dead, because it will be a |
154 // zombie (or on the way to zombiedom) and kill will return 0 even if the | 154 // zombie (or on the way to zombiedom) and kill will return 0 even if the |
155 // signal is not delivered to a live process. | 155 // signal is not delivered to a live process. |
156 result = kill(child, SIGKILL); | 156 result = kill(child, SIGKILL); |
157 if (result == -1) { | 157 if (result == -1) { |
158 PLOG(ERROR) << "kill(" << child << ", SIGKILL)"; | 158 DPLOG(ERROR) << "kill(" << child << ", SIGKILL)"; |
159 } else { | 159 } else { |
160 // The child is definitely on the way out now. BlockingReap won't need to | 160 // The child is definitely on the way out now. BlockingReap won't need to |
161 // wait for long, if at all. | 161 // wait for long, if at all. |
162 BlockingReap(child); | 162 BlockingReap(child); |
163 } | 163 } |
164 } | 164 } |
165 | 165 |
166 } // namespace | 166 } // namespace |
167 | 167 |
168 void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) { | 168 void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) { |
169 WaitForChildToDie(process, kWaitBeforeKillSeconds); | 169 WaitForChildToDie(process, kWaitBeforeKillSeconds); |
170 } | 170 } |
OLD | NEW |