OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/file_path_component_watcher.h" |
| 6 |
| 7 #include <CoreFoundation/CoreFoundation.h> |
| 8 #include <fcntl.h> |
| 9 #include <sys/event.h> |
| 10 #include <sys/param.h> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/file_util.h" |
| 14 #include "base/mac/scoped_cftyperef.h" |
| 15 |
| 16 namespace base { |
| 17 |
| 18 namespace { |
| 19 |
| 20 // Mac-specific file watcher implementation based on kqueue. |
| 21 class FilePathComponentWatcherImpl |
| 22 : public base::FilePathComponentWatcher::PlatformDelegate { |
| 23 public: |
| 24 FilePathComponentWatcherImpl() {} |
| 25 virtual ~FilePathComponentWatcherImpl() {} |
| 26 |
| 27 void OnFilePathChanged(CFFileDescriptorRef fd); |
| 28 |
| 29 // FilePathComponentWatcher::PlatformDelegate overrides. |
| 30 virtual bool Watch(const FilePath& path, |
| 31 FilePathComponentWatcher::Delegate* delegate); |
| 32 virtual void Cancel(); |
| 33 |
| 34 private: |
| 35 base::mac::ScopedCFTypeRef<CFFileDescriptorRef> kqueue_fd_; |
| 36 std::vector<int> file_descriptors_; |
| 37 scoped_refptr<FilePathComponentWatcher::Delegate> delegate_; |
| 38 FilePath path_; |
| 39 |
| 40 DISALLOW_COPY_AND_ASSIGN(FilePathComponentWatcherImpl); |
| 41 }; |
| 42 |
| 43 void CloseFile(int fd) { |
| 44 if(close(fd) != 0) { |
| 45 PLOG(ERROR) << "close"; |
| 46 } |
| 47 } |
| 48 |
| 49 void CloseFileDescriptors(std::vector<int>& descriptors) { |
| 50 std::for_each(descriptors.begin(), descriptors.end(), CloseFile); |
| 51 descriptors.clear(); |
| 52 } |
| 53 |
| 54 bool OpenFileDescriptorsForPath(FilePath path, std::vector<int>* descriptors) { |
| 55 FilePath normalized_path; |
| 56 if (!file_util::NormalizeFilePath(path, &normalized_path)) { |
| 57 return false; |
| 58 } |
| 59 |
| 60 std::vector<FilePath::StringType> components; |
| 61 normalized_path.GetComponents(&components); |
| 62 |
| 63 if (components.size() < 1) { |
| 64 return false; |
| 65 } |
| 66 |
| 67 FilePath built_path(components[0]); |
| 68 std::vector<int> local_descriptors; |
| 69 for(std::vector<FilePath::StringType>::iterator i = components.begin() + 1; |
| 70 i != components.end(); ++i) { |
| 71 built_path = built_path.Append(*i); |
| 72 int fd = open(built_path.value().c_str(), O_RDONLY); |
| 73 if (fd == -1) { |
| 74 PLOG(ERROR) << "open " << built_path.value(); |
| 75 CloseFileDescriptors(*descriptors); |
| 76 descriptors->clear(); |
| 77 return false; |
| 78 } else { |
| 79 descriptors->push_back(fd); |
| 80 } |
| 81 } |
| 82 return true; |
| 83 } |
| 84 |
| 85 void PathComponentWatcherTrampoline(CFFileDescriptorRef fd, |
| 86 CFOptionFlags callBackTypes, |
| 87 void *info) { |
| 88 FilePathComponentWatcherImpl *impl = |
| 89 reinterpret_cast<FilePathComponentWatcherImpl*>(info); |
| 90 impl->OnFilePathChanged(fd); |
| 91 } |
| 92 |
| 93 // FilePathComponentWatcherImpl implementation: |
| 94 |
| 95 void FilePathComponentWatcherImpl::OnFilePathChanged(CFFileDescriptorRef fd) { |
| 96 int native_fd = CFFileDescriptorGetNativeDescriptor(fd); |
| 97 struct kevent kev; |
| 98 struct timespec timeout = {0, 0}; |
| 99 int status = kevent(native_fd, NULL, 0, &kev, 1, &timeout); |
| 100 if (status < 0) { |
| 101 PLOG(ERROR) << "kevent"; |
| 102 if (delegate_) { |
| 103 delegate_->OnError(); |
| 104 } |
| 105 } else if (delegate_) { |
| 106 char path[MAXPATHLEN]; |
| 107 FilePath new_path; |
| 108 int path_fd = file_descriptors_[file_descriptors_.size() - 1]; |
| 109 if (fcntl(path_fd, F_GETPATH, path) != -1) { |
| 110 new_path = FilePath(path); |
| 111 } |
| 112 delegate_->OnFilePathComponentsChanged(path_, new_path); |
| 113 } |
| 114 Cancel(); |
| 115 } |
| 116 |
| 117 bool FilePathComponentWatcherImpl::Watch( |
| 118 const FilePath& path, FilePathComponentWatcher::Delegate* delegate) { |
| 119 delegate_ = delegate; |
| 120 path_ = path; |
| 121 int kq = kqueue(); |
| 122 if (kq == -1) { |
| 123 PLOG(ERROR) << "kqueue"; |
| 124 return false; |
| 125 } |
| 126 if (!OpenFileDescriptorsForPath(path, &file_descriptors_)) { |
| 127 LOG(ERROR) << "FileDescriptorsForPath: " << path.value(); |
| 128 return false; |
| 129 } |
| 130 |
| 131 size_t count = file_descriptors_.size(); |
| 132 |
| 133 scoped_ptr<struct kevent> changes(reinterpret_cast<struct kevent*>( |
| 134 calloc(count, sizeof(struct kevent)))); |
| 135 |
| 136 for(size_t i = 0; i != count; ++i) { |
| 137 EV_SET(&(changes.get())[i], file_descriptors_[i], EVFILT_VNODE, |
| 138 (EV_ADD | EV_CLEAR | EV_ENABLE), |
| 139 (NOTE_RENAME | NOTE_DELETE | NOTE_ATTRIB | NOTE_REVOKE), 0, NULL); |
| 140 } |
| 141 |
| 142 if (HANDLE_EINTR(kevent(kq, changes.get(), count, NULL, 0, NULL)) == -1) { |
| 143 PLOG(ERROR) << "kevent"; |
| 144 return false; |
| 145 } |
| 146 |
| 147 CFFileDescriptorContext context; |
| 148 memset(&context, 0, sizeof(context)); |
| 149 context.info = this; |
| 150 |
| 151 kqueue_fd_.reset( |
| 152 CFFileDescriptorCreate(kCFAllocatorDefault, kq, true, |
| 153 PathComponentWatcherTrampoline, &context)); |
| 154 CFFileDescriptorEnableCallBacks(kqueue_fd_, kCFFileDescriptorReadCallBack); |
| 155 base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> source( |
| 156 CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, kqueue_fd_, 0)); |
| 157 CFRunLoopAddSource(CFRunLoopGetMain(), |
| 158 source, |
| 159 kCFRunLoopDefaultMode); |
| 160 |
| 161 return true; |
| 162 } |
| 163 |
| 164 void FilePathComponentWatcherImpl::Cancel() { |
| 165 if (kqueue_fd_.get()) { |
| 166 CFFileDescriptorInvalidate(kqueue_fd_); |
| 167 kqueue_fd_.reset(); |
| 168 } |
| 169 CloseFileDescriptors(file_descriptors_); |
| 170 } |
| 171 |
| 172 } // namespace |
| 173 |
| 174 FilePathComponentWatcher::FilePathComponentWatcher() { |
| 175 impl_ = new FilePathComponentWatcherImpl(); |
| 176 } |
| 177 |
| 178 } // namespace base |
OLD | NEW |