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/files/file_path_watcher.h" | 5 #include "base/files/file_path_watcher.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <string.h> | 8 #include <string.h> |
9 #include <sys/inotify.h> | 9 #include <sys/inotify.h> |
10 #include <sys/ioctl.h> | 10 #include <sys/ioctl.h> |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 // change. Returns kInvalidWatch on failure. | 49 // change. Returns kInvalidWatch on failure. |
50 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); | 50 Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); |
51 | 51 |
52 // Remove |watch|. Returns true on success. | 52 // Remove |watch|. Returns true on success. |
53 bool RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); | 53 bool RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); |
54 | 54 |
55 // Callback for InotifyReaderTask. | 55 // Callback for InotifyReaderTask. |
56 void OnInotifyEvent(const inotify_event* event); | 56 void OnInotifyEvent(const inotify_event* event); |
57 | 57 |
58 private: | 58 private: |
59 friend struct ::base::DefaultLazyInstanceTraits<InotifyReader>; | 59 friend struct DefaultLazyInstanceTraits<InotifyReader>; |
60 | 60 |
61 typedef std::set<FilePathWatcherImpl*> WatcherSet; | 61 typedef std::set<FilePathWatcherImpl*> WatcherSet; |
62 | 62 |
63 InotifyReader(); | 63 InotifyReader(); |
64 ~InotifyReader(); | 64 ~InotifyReader(); |
65 | 65 |
66 // We keep track of which delegates want to be notified on which watches. | 66 // We keep track of which delegates want to be notified on which watches. |
67 base::hash_map<Watch, WatcherSet> watchers_; | 67 hash_map<Watch, WatcherSet> watchers_; |
68 | 68 |
69 // Lock to protect watchers_. | 69 // Lock to protect watchers_. |
70 base::Lock lock_; | 70 Lock lock_; |
71 | 71 |
72 // Separate thread on which we run blocking read for inotify events. | 72 // Separate thread on which we run blocking read for inotify events. |
73 base::Thread thread_; | 73 Thread thread_; |
74 | 74 |
75 // File descriptor returned by inotify_init. | 75 // File descriptor returned by inotify_init. |
76 const int inotify_fd_; | 76 const int inotify_fd_; |
77 | 77 |
78 // Use self-pipe trick to unblock select during shutdown. | 78 // Use self-pipe trick to unblock select during shutdown. |
79 int shutdown_pipe_[2]; | 79 int shutdown_pipe_[2]; |
80 | 80 |
81 // Flag set to true when startup was successful. | 81 // Flag set to true when startup was successful. |
82 bool valid_; | 82 bool valid_; |
83 | 83 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 }; | 152 }; |
153 | 153 |
154 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd, | 154 void InotifyReaderCallback(InotifyReader* reader, int inotify_fd, |
155 int shutdown_fd) { | 155 int shutdown_fd) { |
156 // Make sure the file descriptors are good for use with select(). | 156 // Make sure the file descriptors are good for use with select(). |
157 CHECK_LE(0, inotify_fd); | 157 CHECK_LE(0, inotify_fd); |
158 CHECK_GT(FD_SETSIZE, inotify_fd); | 158 CHECK_GT(FD_SETSIZE, inotify_fd); |
159 CHECK_LE(0, shutdown_fd); | 159 CHECK_LE(0, shutdown_fd); |
160 CHECK_GT(FD_SETSIZE, shutdown_fd); | 160 CHECK_GT(FD_SETSIZE, shutdown_fd); |
161 | 161 |
162 base::debug::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); | 162 debug::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); |
163 | 163 |
164 while (true) { | 164 while (true) { |
165 fd_set rfds; | 165 fd_set rfds; |
166 FD_ZERO(&rfds); | 166 FD_ZERO(&rfds); |
167 FD_SET(inotify_fd, &rfds); | 167 FD_SET(inotify_fd, &rfds); |
168 FD_SET(shutdown_fd, &rfds); | 168 FD_SET(shutdown_fd, &rfds); |
169 | 169 |
170 // Wait until some inotify events are available. | 170 // Wait until some inotify events are available. |
171 int select_result = | 171 int select_result = |
172 HANDLE_EINTR(select(std::max(inotify_fd, shutdown_fd) + 1, | 172 HANDLE_EINTR(select(std::max(inotify_fd, shutdown_fd) + 1, |
(...skipping 30 matching lines...) Expand all Loading... |
203 while (i < bytes_read) { | 203 while (i < bytes_read) { |
204 inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]); | 204 inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]); |
205 size_t event_size = sizeof(inotify_event) + event->len; | 205 size_t event_size = sizeof(inotify_event) + event->len; |
206 DCHECK(i + event_size <= static_cast<size_t>(bytes_read)); | 206 DCHECK(i + event_size <= static_cast<size_t>(bytes_read)); |
207 reader->OnInotifyEvent(event); | 207 reader->OnInotifyEvent(event); |
208 i += event_size; | 208 i += event_size; |
209 } | 209 } |
210 } | 210 } |
211 } | 211 } |
212 | 212 |
213 static base::LazyInstance<InotifyReader>::Leaky g_inotify_reader = | 213 static LazyInstance<InotifyReader>::Leaky g_inotify_reader = |
214 LAZY_INSTANCE_INITIALIZER; | 214 LAZY_INSTANCE_INITIALIZER; |
215 | 215 |
216 InotifyReader::InotifyReader() | 216 InotifyReader::InotifyReader() |
217 : thread_("inotify_reader"), | 217 : thread_("inotify_reader"), |
218 inotify_fd_(inotify_init()), | 218 inotify_fd_(inotify_init()), |
219 valid_(false) { | 219 valid_(false) { |
220 if (inotify_fd_ < 0) | 220 if (inotify_fd_ < 0) |
221 PLOG(ERROR) << "inotify_init() failed"; | 221 PLOG(ERROR) << "inotify_init() failed"; |
222 | 222 |
223 shutdown_pipe_[0] = -1; | 223 shutdown_pipe_[0] = -1; |
224 shutdown_pipe_[1] = -1; | 224 shutdown_pipe_[1] = -1; |
225 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { | 225 if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { |
226 thread_.message_loop()->PostTask( | 226 thread_.message_loop()->PostTask( |
227 FROM_HERE, base::Bind(&InotifyReaderCallback, this, inotify_fd_, | 227 FROM_HERE, Bind(&InotifyReaderCallback, this, inotify_fd_, |
228 shutdown_pipe_[0])); | 228 shutdown_pipe_[0])); |
229 valid_ = true; | 229 valid_ = true; |
230 } | 230 } |
231 } | 231 } |
232 | 232 |
233 InotifyReader::~InotifyReader() { | 233 InotifyReader::~InotifyReader() { |
234 if (valid_) { | 234 if (valid_) { |
235 // Write to the self-pipe so that the select call in InotifyReaderTask | 235 // Write to the self-pipe so that the select call in InotifyReaderTask |
236 // returns. | 236 // returns. |
237 ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1)); | 237 ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1)); |
238 DPCHECK(ret > 0); | 238 DPCHECK(ret > 0); |
239 DCHECK_EQ(ret, 1); | 239 DCHECK_EQ(ret, 1); |
240 thread_.Stop(); | 240 thread_.Stop(); |
241 } | 241 } |
242 if (inotify_fd_ >= 0) | 242 if (inotify_fd_ >= 0) |
243 close(inotify_fd_); | 243 close(inotify_fd_); |
244 if (shutdown_pipe_[0] >= 0) | 244 if (shutdown_pipe_[0] >= 0) |
245 close(shutdown_pipe_[0]); | 245 close(shutdown_pipe_[0]); |
246 if (shutdown_pipe_[1] >= 0) | 246 if (shutdown_pipe_[1] >= 0) |
247 close(shutdown_pipe_[1]); | 247 close(shutdown_pipe_[1]); |
248 } | 248 } |
249 | 249 |
250 InotifyReader::Watch InotifyReader::AddWatch( | 250 InotifyReader::Watch InotifyReader::AddWatch( |
251 const FilePath& path, FilePathWatcherImpl* watcher) { | 251 const FilePath& path, FilePathWatcherImpl* watcher) { |
252 if (!valid_) | 252 if (!valid_) |
253 return kInvalidWatch; | 253 return kInvalidWatch; |
254 | 254 |
255 base::AutoLock auto_lock(lock_); | 255 AutoLock auto_lock(lock_); |
256 | 256 |
257 Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), | 257 Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), |
258 IN_CREATE | IN_DELETE | | 258 IN_CREATE | IN_DELETE | |
259 IN_CLOSE_WRITE | IN_MOVE | | 259 IN_CLOSE_WRITE | IN_MOVE | |
260 IN_ONLYDIR); | 260 IN_ONLYDIR); |
261 | 261 |
262 if (watch == kInvalidWatch) | 262 if (watch == kInvalidWatch) |
263 return kInvalidWatch; | 263 return kInvalidWatch; |
264 | 264 |
265 watchers_[watch].insert(watcher); | 265 watchers_[watch].insert(watcher); |
266 | 266 |
267 return watch; | 267 return watch; |
268 } | 268 } |
269 | 269 |
270 bool InotifyReader::RemoveWatch(Watch watch, | 270 bool InotifyReader::RemoveWatch(Watch watch, |
271 FilePathWatcherImpl* watcher) { | 271 FilePathWatcherImpl* watcher) { |
272 if (!valid_) | 272 if (!valid_) |
273 return false; | 273 return false; |
274 | 274 |
275 base::AutoLock auto_lock(lock_); | 275 AutoLock auto_lock(lock_); |
276 | 276 |
277 watchers_[watch].erase(watcher); | 277 watchers_[watch].erase(watcher); |
278 | 278 |
279 if (watchers_[watch].empty()) { | 279 if (watchers_[watch].empty()) { |
280 watchers_.erase(watch); | 280 watchers_.erase(watch); |
281 return (inotify_rm_watch(inotify_fd_, watch) == 0); | 281 return (inotify_rm_watch(inotify_fd_, watch) == 0); |
282 } | 282 } |
283 | 283 |
284 return true; | 284 return true; |
285 } | 285 } |
286 | 286 |
287 void InotifyReader::OnInotifyEvent(const inotify_event* event) { | 287 void InotifyReader::OnInotifyEvent(const inotify_event* event) { |
288 if (event->mask & IN_IGNORED) | 288 if (event->mask & IN_IGNORED) |
289 return; | 289 return; |
290 | 290 |
291 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); | 291 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); |
292 base::AutoLock auto_lock(lock_); | 292 AutoLock auto_lock(lock_); |
293 | 293 |
294 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); | 294 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); |
295 watcher != watchers_[event->wd].end(); | 295 watcher != watchers_[event->wd].end(); |
296 ++watcher) { | 296 ++watcher) { |
297 (*watcher)->OnFilePathChanged(event->wd, | 297 (*watcher)->OnFilePathChanged(event->wd, |
298 child, | 298 child, |
299 event->mask & (IN_CREATE | IN_MOVED_TO)); | 299 event->mask & (IN_CREATE | IN_MOVED_TO)); |
300 } | 300 } |
301 } | 301 } |
302 | 302 |
303 FilePathWatcherImpl::FilePathWatcherImpl() { | 303 FilePathWatcherImpl::FilePathWatcherImpl() { |
304 } | 304 } |
305 | 305 |
306 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 306 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
307 const FilePath::StringType& child, | 307 const FilePath::StringType& child, |
308 bool created) { | 308 bool created) { |
309 if (!message_loop()->BelongsToCurrentThread()) { | 309 if (!message_loop()->BelongsToCurrentThread()) { |
310 // Switch to message_loop_ to access watches_ safely. | 310 // Switch to message_loop_ to access watches_ safely. |
311 message_loop()->PostTask(FROM_HERE, | 311 message_loop()->PostTask(FROM_HERE, |
312 base::Bind(&FilePathWatcherImpl::OnFilePathChanged, | 312 Bind(&FilePathWatcherImpl::OnFilePathChanged, |
313 this, | 313 this, |
314 fired_watch, | 314 fired_watch, |
315 child, | 315 child, |
316 created)); | 316 created)); |
317 return; | 317 return; |
318 } | 318 } |
319 | 319 |
320 DCHECK(MessageLoopForIO::current()); | 320 DCHECK(MessageLoopForIO::current()); |
321 | 321 |
322 // Find the entry in |watches_| that corresponds to |fired_watch|. | 322 // Find the entry in |watches_| that corresponds to |fired_watch|. |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
369 bool recursive, | 369 bool recursive, |
370 const FilePathWatcher::Callback& callback) { | 370 const FilePathWatcher::Callback& callback) { |
371 DCHECK(target_.empty()); | 371 DCHECK(target_.empty()); |
372 DCHECK(MessageLoopForIO::current()); | 372 DCHECK(MessageLoopForIO::current()); |
373 if (recursive) { | 373 if (recursive) { |
374 // Recursive watch is not supported on this platform. | 374 // Recursive watch is not supported on this platform. |
375 NOTIMPLEMENTED(); | 375 NOTIMPLEMENTED(); |
376 return false; | 376 return false; |
377 } | 377 } |
378 | 378 |
379 set_message_loop(base::MessageLoopProxy::current().get()); | 379 set_message_loop(MessageLoopProxy::current().get()); |
380 callback_ = callback; | 380 callback_ = callback; |
381 target_ = path; | 381 target_ = path; |
382 MessageLoop::current()->AddDestructionObserver(this); | 382 MessageLoop::current()->AddDestructionObserver(this); |
383 | 383 |
384 std::vector<FilePath::StringType> comps; | 384 std::vector<FilePath::StringType> comps; |
385 target_.GetComponents(&comps); | 385 target_.GetComponents(&comps); |
386 DCHECK(!comps.empty()); | 386 DCHECK(!comps.empty()); |
387 std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); | 387 std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); |
388 for (++comp; comp != comps.end(); ++comp) | 388 for (++comp; comp != comps.end(); ++comp) |
389 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); | 389 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); |
390 | 390 |
391 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, | 391 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, |
392 FilePath::StringType())); | 392 FilePath::StringType())); |
393 return UpdateWatches(); | 393 return UpdateWatches(); |
394 } | 394 } |
395 | 395 |
396 void FilePathWatcherImpl::Cancel() { | 396 void FilePathWatcherImpl::Cancel() { |
397 if (callback_.is_null()) { | 397 if (callback_.is_null()) { |
398 // Watch was never called, or the |message_loop_| thread is already gone. | 398 // Watch was never called, or the |message_loop_| thread is already gone. |
399 set_cancelled(); | 399 set_cancelled(); |
400 return; | 400 return; |
401 } | 401 } |
402 | 402 |
403 // Switch to the message_loop_ if necessary so we can access |watches_|. | 403 // Switch to the message_loop_ if necessary so we can access |watches_|. |
404 if (!message_loop()->BelongsToCurrentThread()) { | 404 if (!message_loop()->BelongsToCurrentThread()) { |
405 message_loop()->PostTask(FROM_HERE, | 405 message_loop()->PostTask(FROM_HERE, |
406 base::Bind(&FilePathWatcher::CancelWatch, | 406 Bind(&FilePathWatcher::CancelWatch, |
407 make_scoped_refptr(this))); | 407 make_scoped_refptr(this))); |
408 } else { | 408 } else { |
409 CancelOnMessageLoopThread(); | 409 CancelOnMessageLoopThread(); |
410 } | 410 } |
411 } | 411 } |
412 | 412 |
413 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 413 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
414 if (!is_cancelled()) | 414 if (!is_cancelled()) |
415 set_cancelled(); | 415 set_cancelled(); |
416 | 416 |
(...skipping 24 matching lines...) Expand all Loading... |
441 FilePath path(FILE_PATH_LITERAL("/")); | 441 FilePath path(FILE_PATH_LITERAL("/")); |
442 bool path_valid = true; | 442 bool path_valid = true; |
443 for (WatchVector::iterator watch_entry(watches_.begin()); | 443 for (WatchVector::iterator watch_entry(watches_.begin()); |
444 watch_entry != watches_.end(); ++watch_entry) { | 444 watch_entry != watches_.end(); ++watch_entry) { |
445 InotifyReader::Watch old_watch = watch_entry->watch_; | 445 InotifyReader::Watch old_watch = watch_entry->watch_; |
446 if (path_valid) { | 446 if (path_valid) { |
447 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); | 447 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); |
448 if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && | 448 if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && |
449 file_util::IsLink(path)) { | 449 file_util::IsLink(path)) { |
450 FilePath link; | 450 FilePath link; |
451 if (file_util::ReadSymbolicLink(path, &link)) { | 451 if (ReadSymbolicLink(path, &link)) { |
452 if (!link.IsAbsolute()) | 452 if (!link.IsAbsolute()) |
453 link = path.DirName().Append(link); | 453 link = path.DirName().Append(link); |
454 // Try watching symlink target directory. If the link target is "/", | 454 // Try watching symlink target directory. If the link target is "/", |
455 // then we shouldn't get here in normal situations and if we do, we'd | 455 // then we shouldn't get here in normal situations and if we do, we'd |
456 // watch "/" for changes to a component "/" which is harmless so no | 456 // watch "/" for changes to a component "/" which is harmless so no |
457 // special treatment of this case is required. | 457 // special treatment of this case is required. |
458 watch_entry->watch_ = | 458 watch_entry->watch_ = |
459 g_inotify_reader.Get().AddWatch(link.DirName(), this); | 459 g_inotify_reader.Get().AddWatch(link.DirName(), this); |
460 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) { | 460 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) { |
461 watch_entry->linkname_ = link.BaseName().value(); | 461 watch_entry->linkname_ = link.BaseName().value(); |
(...skipping 22 matching lines...) Expand all Loading... |
484 return true; | 484 return true; |
485 } | 485 } |
486 | 486 |
487 } // namespace | 487 } // namespace |
488 | 488 |
489 FilePathWatcher::FilePathWatcher() { | 489 FilePathWatcher::FilePathWatcher() { |
490 impl_ = new FilePathWatcherImpl(); | 490 impl_ = new FilePathWatcherImpl(); |
491 } | 491 } |
492 | 492 |
493 } // namespace base | 493 } // namespace base |
OLD | NEW |