| 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 |