|
Wired Foundation
2.0
A foundation framework for the Wired implementation on Mac OS X
|
00001 /* $Id$ */ 00002 00003 /* 00004 * Copyright (c) 2005-2009 Axel Andersson 00005 * All rights reserved. 00006 * 00007 * Redistribution and use in source and binary forms, with or without 00008 * modification, are permitted provided that the following conditions 00009 * are met: 00010 * 1. Redistributions of source code must retain the above copyright 00011 * notice, this list of conditions and the following disclaimer. 00012 * 2. Redistributions in binary form must reproduce the above copyright 00013 * notice, this list of conditions and the following disclaimer in the 00014 * documentation and/or other materials provided with the distribution. 00015 * 00016 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 00017 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00018 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00019 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 00020 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00021 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00022 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00023 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 00024 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00025 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00026 * POSSIBILITY OF SUCH DAMAGE. 00027 */ 00028 00029 #import <WiredFoundation/WIEventQueue.h> 00030 00031 NSString * const WIEventFileDeleteNotification = @"WIEventFileDeleteNotification"; 00032 NSString * const WIEventFileWriteNotification = @"WIEventFileWriteNotification"; 00033 NSString * const WIEventFileExtendNotification = @"WIEventFileExtendNotification"; 00034 NSString * const WIEventFileAttributeChangeNotification = @"WIEventFileAttributeChangeNotification"; 00035 NSString * const WIEventFileLinkCountChangeNotification = @"WIEventFileLinkCountChangeNotification"; 00036 NSString * const WIEventFileRenameNotification = @"WIEventFileRenameNotification"; 00037 NSString * const WIEventFileRevokeNotification = @"WIEventFileRevokeNotification"; 00038 00039 00040 00041 @interface WIEventFile : WIObject { 00042 NSString *_path; 00043 int _fd; 00044 } 00045 00046 00047 - (id)initWithPath:(NSString *)path fileDescriptor:(int)fd; 00048 - (NSString *)path; 00049 - (int)fileDescriptor; 00050 00051 @end 00052 00053 00054 @implementation WIEventFile 00055 00056 - (id)initWithPath:(NSString *)path fileDescriptor:(int)fd { 00057 self = [super init]; 00058 00059 _path = [path retain]; 00060 _fd = fd; 00061 00062 return self; 00063 } 00064 00065 00066 00067 - (void)dealloc { 00068 [_path release]; 00069 00070 [super dealloc]; 00071 } 00072 00073 00074 00075 #pragma mark - 00076 00077 - (NSString *)path { 00078 return _path; 00079 } 00080 00081 00082 00083 - (int)fileDescriptor { 00084 return _fd; 00085 } 00086 00087 @end 00088 00089 00090 00091 @interface WIEventQueue(Private) 00092 00093 - (void)_processQueue; 00094 00095 @end 00096 00097 00098 @implementation WIEventQueue(Private) 00099 00100 - (void)_processQueue { 00101 NSString *path; 00102 struct timespec ts = { 0, 0 }; 00103 struct kevent ev; 00104 00105 while(kevent(_fd, NULL, 0, &ev, 1, &ts) > 0) { 00106 if(ev.filter == EVFILT_VNODE) { 00107 path = [(NSString *) ev.udata retain]; 00108 00109 if(ev.fflags & NOTE_DELETE) 00110 [_center postNotificationName:WIEventFileDeleteNotification object:path]; 00111 00112 if(ev.fflags & NOTE_WRITE) 00113 [_center postNotificationName:WIEventFileWriteNotification object:path]; 00114 00115 if(ev.fflags & NOTE_EXTEND) 00116 [_center postNotificationName:WIEventFileExtendNotification object:path]; 00117 00118 if(ev.fflags & NOTE_ATTRIB) 00119 [_center postNotificationName:WIEventFileAttributeChangeNotification object:path]; 00120 00121 if(ev.fflags & NOTE_LINK) 00122 [_center postNotificationName:WIEventFileLinkCountChangeNotification object:path]; 00123 00124 if(ev.fflags & NOTE_RENAME) 00125 [_center postNotificationName:WIEventFileRenameNotification object:path]; 00126 00127 if(ev.fflags & NOTE_REVOKE) 00128 [_center postNotificationName:WIEventFileRevokeNotification object:path]; 00129 00130 [path release]; 00131 } 00132 } 00133 } 00134 00135 @end 00136 00137 00138 00139 @implementation WIEventQueue 00140 00141 + (WIEventQueue *)sharedQueue { 00142 static WIEventQueue *sharedQueue; 00143 00144 if(!sharedQueue) 00145 sharedQueue = [[self alloc] init]; 00146 00147 return sharedQueue; 00148 } 00149 00150 00151 00152 - (id)init { 00153 self = [super init]; 00154 00155 _fd = kqueue(); 00156 00157 if(_fd < 0) { 00158 [self release]; 00159 00160 return NULL; 00161 } 00162 00163 _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:_fd]; 00164 [_fileHandle waitForDataInBackgroundAndNotify]; 00165 00166 [[NSNotificationCenter defaultCenter] 00167 addObserver:self 00168 selector:@selector(fileHandleDataAvailable:) 00169 name:NSFileHandleDataAvailableNotification 00170 object:_fileHandle]; 00171 00172 _center = [[NSNotificationCenter alloc] init]; 00173 _files = [[NSMutableDictionary alloc] init]; 00174 00175 return self; 00176 } 00177 00178 00179 00180 - (void)dealloc { 00181 [self removeAllPaths]; 00182 00183 [_center release]; 00184 [_fileHandle release]; 00185 00186 if(close(_fd) < 0) 00187 NSLog(@"*** %@: close(): %s", [self class], strerror(errno)); 00188 00189 [_files release]; 00190 00191 [super dealloc]; 00192 } 00193 00194 00195 00196 #pragma mark - 00197 00198 - (void)fileHandleDataAvailable:(NSNotification *)notification { 00199 [self _processQueue]; 00200 00201 [_fileHandle waitForDataInBackgroundAndNotify]; 00202 } 00203 00204 00205 00206 #pragma mark - 00207 00208 - (NSNotificationCenter *)notificationCenter { 00209 return _center; 00210 } 00211 00212 00213 00214 #pragma mark - 00215 00216 - (void)addPath:(NSString *)path { 00217 [self addPath:path forMode:WIEventFileDelete | 00218 WIEventFileWrite | 00219 WIEventFileRename | 00220 WIEventFileAttributeChange]; 00221 } 00222 00223 00224 00225 - (void)addPath:(NSString *)path forMode:(WIEventMode)mode { 00226 WIEventFile *file; 00227 struct kevent ev; 00228 int fd; 00229 00230 fd = open([path fileSystemRepresentation], O_EVTONLY, 0); 00231 00232 if(fd < 0) { 00233 NSLog(@"*** %@: open(): %s", [self class], strerror(errno)); 00234 00235 return; 00236 } 00237 00238 file = [[WIEventFile alloc] initWithPath:path fileDescriptor:fd]; 00239 00240 EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, mode, 0, path); 00241 00242 if(kevent(_fd, &ev, 1, NULL, 0, NULL) < 0) { 00243 [file release]; 00244 00245 return; 00246 } 00247 00248 [_files setObject:file forKey:path]; 00249 [file release]; 00250 } 00251 00252 00253 00254 - (void)removePath:(NSString *)path { 00255 WIEventFile *file; 00256 00257 file = [_files objectForKey:path]; 00258 00259 if(!file) 00260 return; 00261 00262 if(close([file fileDescriptor]) < 0) 00263 NSLog(@"*** %@: close(): %s", [self class], strerror(errno)); 00264 00265 [_files removeObjectForKey:path]; 00266 } 00267 00268 00269 00270 - (void)removeAllPaths { 00271 NSEnumerator *enumerator; 00272 WIEventFile *file; 00273 00274 enumerator = [_files objectEnumerator]; 00275 00276 while((file = [enumerator nextObject])) { 00277 if(close([file fileDescriptor]) < 0) 00278 NSLog(@"*** %@: close(): %s", [self class], strerror(errno)); 00279 } 00280 00281 [_files removeAllObjects]; 00282 } 00283 00284 @end