aboutsummaryrefslogtreecommitdiff
path: root/src/3rdParty/efsw/WatcherKqueue.cpp
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2022-11-15 17:23:46 +0800
committerLi Jin <dragon-fly@qq.com>2022-11-15 17:52:09 +0800
commit94f8330613877b3582d32bd11abd83a97b4399ad (patch)
tree5359de314be1ebde17f8d1e48632a97d18f9e50f /src/3rdParty/efsw/WatcherKqueue.cpp
parent60f8f00a022ac08701792b2897b72d8c99b50f52 (diff)
downloadyuescript-94f8330613877b3582d32bd11abd83a97b4399ad.tar.gz
yuescript-94f8330613877b3582d32bd11abd83a97b4399ad.tar.bz2
yuescript-94f8330613877b3582d32bd11abd83a97b4399ad.zip
adding -w option to Yuescript tool.
Diffstat (limited to 'src/3rdParty/efsw/WatcherKqueue.cpp')
-rwxr-xr-xsrc/3rdParty/efsw/WatcherKqueue.cpp569
1 files changed, 569 insertions, 0 deletions
diff --git a/src/3rdParty/efsw/WatcherKqueue.cpp b/src/3rdParty/efsw/WatcherKqueue.cpp
new file mode 100755
index 0000000..441948a
--- /dev/null
+++ b/src/3rdParty/efsw/WatcherKqueue.cpp
@@ -0,0 +1,569 @@
1#include <efsw/WatcherKqueue.hpp>
2
3#if EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE || EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
4
5#include <cstdio>
6#include <cstdlib>
7#include <cstring>
8#include <dirent.h>
9#include <efsw/Debug.hpp>
10#include <efsw/FileSystem.hpp>
11#include <efsw/FileWatcherKqueue.hpp>
12#include <efsw/String.hpp>
13#include <efsw/System.hpp>
14#include <efsw/WatcherGeneric.hpp>
15#include <errno.h>
16#include <fcntl.h>
17#include <sys/stat.h>
18#include <unistd.h>
19
20#define KEVENT_RESERVE_VALUE ( 10 )
21
22#ifndef O_EVTONLY
23#define O_EVTONLY ( O_RDONLY | O_NONBLOCK )
24#endif
25
26namespace efsw {
27
28int comparator( const void* ke1, const void* ke2 ) {
29 const KEvent* kev1 = reinterpret_cast<const KEvent*>( ke1 );
30 const KEvent* kev2 = reinterpret_cast<const KEvent*>( ke2 );
31
32 if ( NULL != kev2->udata ) {
33 FileInfo* fi1 = reinterpret_cast<FileInfo*>( kev1->udata );
34 FileInfo* fi2 = reinterpret_cast<FileInfo*>( kev2->udata );
35
36 return strcmp( fi1->Filepath.c_str(), fi2->Filepath.c_str() );
37 }
38
39 return 1;
40}
41
42WatcherKqueue::WatcherKqueue( WatchID watchid, const std::string& dirname,
43 FileWatchListener* listener, bool recursive,
44 FileWatcherKqueue* watcher, WatcherKqueue* parent ) :
45 Watcher( watchid, dirname, listener, recursive ),
46 mLastWatchID( 0 ),
47 mChangeListCount( 0 ),
48 mKqueue( kqueue() ),
49 mWatcher( watcher ),
50 mParent( parent ),
51 mInitOK( true ),
52 mErrno( 0 ) {
53 if ( -1 == mKqueue ) {
54 efDEBUG(
55 "kqueue() returned invalid descriptor for directory %s. File descriptors count: %ld\n",
56 Directory.c_str(), mWatcher->mFileDescriptorCount );
57
58 mInitOK = false;
59 mErrno = errno;
60 } else {
61 mWatcher->addFD();
62 }
63}
64
65WatcherKqueue::~WatcherKqueue() {
66 // Remove the childs watchers ( sub-folders watches )
67 removeAll();
68
69 for ( size_t i = 0; i < mChangeListCount; i++ ) {
70 if ( NULL != mChangeList[i].udata ) {
71 FileInfo* fi = reinterpret_cast<FileInfo*>( mChangeList[i].udata );
72
73 efSAFE_DELETE( fi );
74 }
75 }
76
77 close( mKqueue );
78
79 mWatcher->removeFD();
80}
81
82void WatcherKqueue::addAll() {
83 if ( -1 == mKqueue ) {
84 return;
85 }
86
87 // scan directory and call addFile(name, false) on each file
88 FileSystem::dirAddSlashAtEnd( Directory );
89
90 efDEBUG( "addAll(): Added folder: %s\n", Directory.c_str() );
91
92 // add base dir
93 int fd = open( Directory.c_str(), O_EVTONLY );
94
95 if ( -1 == fd ) {
96 efDEBUG( "addAll(): Couldn't open folder: %s\n", Directory.c_str() );
97
98 if ( EACCES != errno ) {
99 mInitOK = false;
100 }
101
102 mErrno = errno;
103
104 return;
105 }
106
107 mDirSnap.setDirectoryInfo( Directory );
108 mDirSnap.scan();
109
110 mChangeList.resize( KEVENT_RESERVE_VALUE );
111
112 // Creates the kevent for the folder
113 EV_SET( &mChangeList[0], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
114 NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME, 0, 0 );
115
116 mWatcher->addFD();
117
118 // Get the files and directories from the directory
119 FileInfoMap files = FileSystem::filesInfoFromPath( Directory );
120
121 for ( FileInfoMap::iterator it = files.begin(); it != files.end(); it++ ) {
122 FileInfo& fi = it->second;
123
124 if ( fi.isRegularFile() ) {
125 // Add the regular files kevent
126 addFile( fi.Filepath, false );
127 } else if ( Recursive && fi.isDirectory() && fi.isReadable() ) {
128 // Create another watcher for the subfolders ( if recursive )
129 WatchID id = addWatch( fi.Filepath, Listener, Recursive, this );
130
131 // If the watcher is not adding the watcher means that the directory was created
132 if ( id > 0 && !mWatcher->isAddingWatcher() ) {
133 handleFolderAction( fi.Filepath, Actions::Add );
134 }
135 }
136 }
137}
138
139void WatcherKqueue::removeAll() {
140 efDEBUG( "removeAll(): Removing all child watchers\n" );
141
142 std::list<WatchID> erase;
143
144 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ ) {
145 efDEBUG( "removeAll(): Removed child watcher %s\n", it->second->Directory.c_str() );
146
147 erase.push_back( it->second->ID );
148 }
149
150 for ( std::list<WatchID>::iterator eit = erase.begin(); eit != erase.end(); eit++ ) {
151 removeWatch( *eit );
152 }
153}
154
155void WatcherKqueue::addFile( const std::string& name, bool emitEvents ) {
156 efDEBUG( "addFile(): Added: %s\n", name.c_str() );
157
158 // Open the file to get the file descriptor
159 int fd = open( name.c_str(), O_EVTONLY );
160
161 if ( fd == -1 ) {
162 efDEBUG( "addFile(): Could open file descriptor for %s. File descriptor count: %ld\n",
163 name.c_str(), mWatcher->mFileDescriptorCount );
164
165 Errors::Log::createLastError( Errors::FileNotReadable, name );
166
167 if ( EACCES != errno ) {
168 mInitOK = false;
169 }
170
171 mErrno = errno;
172
173 return;
174 }
175
176 mWatcher->addFD();
177
178 // increase the file kevent file count
179 mChangeListCount++;
180
181 if ( mChangeListCount + KEVENT_RESERVE_VALUE > mChangeList.size() &&
182 mChangeListCount % KEVENT_RESERVE_VALUE == 0 ) {
183 size_t reserve_size = mChangeList.size() + KEVENT_RESERVE_VALUE;
184 mChangeList.resize( reserve_size );
185 efDEBUG( "addFile(): Reserverd more KEvents space for %s, space reserved %ld, list actual "
186 "size %ld.\n",
187 Directory.c_str(), reserve_size, mChangeListCount );
188 }
189
190 // create entry
191 FileInfo* entry = new FileInfo( name );
192
193 // set the event data at the end of the list
194 EV_SET( &mChangeList[mChangeListCount], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
195 NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME, 0, (void*)entry );
196
197 // qsort sort the list by name
198 qsort( &mChangeList[1], mChangeListCount, sizeof( KEvent ), comparator );
199
200 // handle action
201 if ( emitEvents ) {
202 handleAction( name, Actions::Add );
203 }
204}
205
206void WatcherKqueue::removeFile( const std::string& name, bool emitEvents ) {
207 efDEBUG( "removeFile(): Trying to remove file: %s\n", name.c_str() );
208
209 // bsearch
210 KEvent target;
211
212 // Create a temporary file info to search the kevent ( searching the directory )
213 FileInfo tempEntry( name );
214
215 target.udata = &tempEntry;
216
217 // Search the kevent
218 KEvent* ke = (KEvent*)bsearch( &target, &mChangeList[0], mChangeListCount + 1, sizeof( KEvent ),
219 comparator );
220
221 // Trying to remove a non-existing file?
222 if ( !ke ) {
223 Errors::Log::createLastError( Errors::FileNotFound, name );
224 efDEBUG( "File not removed\n" );
225 return;
226 }
227
228 efDEBUG( "File removed\n" );
229
230 // handle action
231 if ( emitEvents ) {
232 handleAction( name, Actions::Delete );
233 }
234
235 // Delete the user data ( FileInfo ) from the kevent closed
236 FileInfo* del = reinterpret_cast<FileInfo*>( ke->udata );
237
238 efSAFE_DELETE( del );
239
240 // close the file descriptor from the kevent
241 close( ke->ident );
242
243 mWatcher->removeFD();
244
245 memset( ke, 0, sizeof( KEvent ) );
246
247 // move end to current
248 memcpy( ke, &mChangeList[mChangeListCount], sizeof( KEvent ) );
249 memset( &mChangeList[mChangeListCount], 0, sizeof( KEvent ) );
250 --mChangeListCount;
251}
252
253void WatcherKqueue::rescan() {
254 efDEBUG( "rescan(): Rescanning: %s\n", Directory.c_str() );
255
256 DirectorySnapshotDiff Diff = mDirSnap.scan();
257
258 if ( Diff.DirChanged ) {
259 sendDirChanged();
260 }
261
262 if ( Diff.changed() ) {
263 FileInfoList::iterator it;
264 MovedList::iterator mit;
265
266 /// Files
267 DiffIterator( FilesCreated ) {
268 addFile( ( *it ).Filepath );
269 }
270
271 DiffIterator( FilesModified ) {
272 handleAction( ( *it ).Filepath, Actions::Modified );
273 }
274
275 DiffIterator( FilesDeleted ) {
276 removeFile( ( *it ).Filepath );
277 }
278
279 DiffMovedIterator( FilesMoved ) {
280 handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first );
281 removeFile( Directory + ( *mit ).first, false );
282 addFile( ( *mit ).second.Filepath, false );
283 }
284
285 /// Directories
286 DiffIterator( DirsCreated ) {
287 handleFolderAction( ( *it ).Filepath, Actions::Add );
288 addWatch( ( *it ).Filepath, Listener, Recursive, this );
289 }
290
291 DiffIterator( DirsModified ) {
292 handleFolderAction( ( *it ).Filepath, Actions::Modified );
293 }
294
295 DiffIterator( DirsDeleted ) {
296 handleFolderAction( ( *it ).Filepath, Actions::Delete );
297
298 Watcher* watch = findWatcher( ( *it ).Filepath );
299
300 if ( NULL != watch ) {
301 removeWatch( watch->ID );
302 }
303 }
304
305 DiffMovedIterator( DirsMoved ) {
306 moveDirectory( Directory + ( *mit ).first, ( *mit ).second.Filepath );
307 }
308 }
309}
310
311WatchID WatcherKqueue::watchingDirectory( std::string dir ) {
312 Watcher* watch = findWatcher( dir );
313
314 if ( NULL != watch ) {
315 return watch->ID;
316 }
317
318 return Errors::FileNotFound;
319}
320
321void WatcherKqueue::handleAction( const std::string& filename, efsw::Action action,
322 const std::string& oldFilename ) {
323 Listener->handleFileAction( ID, Directory, FileSystem::fileNameFromPath( filename ), action,
324 FileSystem::fileNameFromPath( oldFilename ) );
325}
326
327void WatcherKqueue::handleFolderAction( std::string filename, efsw::Action action,
328 const std::string& oldFilename ) {
329 FileSystem::dirRemoveSlashAtEnd( filename );
330
331 handleAction( filename, action, oldFilename );
332}
333
334void WatcherKqueue::sendDirChanged() {
335 if ( NULL != mParent ) {
336 Listener->handleFileAction( mParent->ID, mParent->Directory,
337 FileSystem::fileNameFromPath( Directory ), Actions::Modified );
338 }
339}
340
341void WatcherKqueue::watch() {
342 if ( -1 == mKqueue ) {
343 return;
344 }
345
346 int nev = 0;
347 KEvent event;
348
349 // First iterate the childs, to get the events from the deepest folder, to the watcher childs
350 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
351 it->second->watch();
352 }
353
354 bool needScan = false;
355
356 // Then we get the the events of the current folder
357 while ( ( nev = kevent( mKqueue, &mChangeList[0], mChangeListCount + 1, &event, 1,
358 &mWatcher->mTimeOut ) ) != 0 ) {
359 // An error ocurred?
360 if ( nev == -1 ) {
361 efDEBUG( "watch(): Error on directory %s\n", Directory.c_str() );
362 perror( "kevent" );
363 break;
364 } else {
365 FileInfo* entry = NULL;
366
367 // If udate == NULL means that it is the fisrt element of the change list, the folder.
368 // otherwise it is an event of some file inside the folder
369 if ( ( entry = reinterpret_cast<FileInfo*>( event.udata ) ) != NULL ) {
370 efDEBUG( "watch(): File: %s ", entry->Filepath.c_str() );
371
372 // If the event flag is delete... the file was deleted
373 if ( event.fflags & NOTE_DELETE ) {
374 efDEBUG( "deleted\n" );
375
376 mDirSnap.removeFile( entry->Filepath );
377
378 removeFile( entry->Filepath );
379 } else if ( event.fflags & NOTE_EXTEND || event.fflags & NOTE_WRITE ||
380 event.fflags & NOTE_ATTRIB ) {
381 // The file was modified
382 efDEBUG( "modified\n" );
383
384 FileInfo fi( entry->Filepath );
385
386 if ( fi != *entry ) {
387 *entry = fi;
388
389 mDirSnap.updateFile( entry->Filepath );
390
391 handleAction( entry->Filepath, efsw::Actions::Modified );
392 }
393 } else if ( event.fflags & NOTE_RENAME ) {
394 efDEBUG( "moved\n" );
395
396 needScan = true;
397 }
398 } else {
399 needScan = true;
400 }
401 }
402 }
403
404 if ( needScan ) {
405 rescan();
406 }
407}
408
409Watcher* WatcherKqueue::findWatcher( const std::string path ) {
410 WatchMap::iterator it = mWatches.begin();
411
412 for ( ; it != mWatches.end(); it++ ) {
413 if ( it->second->Directory == path ) {
414 return it->second;
415 }
416 }
417
418 return NULL;
419}
420
421void WatcherKqueue::moveDirectory( std::string oldPath, std::string newPath, bool emitEvents ) {
422 // Update the directory path if it's a watcher
423 std::string opath2( oldPath );
424 FileSystem::dirAddSlashAtEnd( opath2 );
425
426 Watcher* watch = findWatcher( opath2 );
427
428 if ( NULL != watch ) {
429 watch->Directory = opath2;
430 }
431
432 if ( emitEvents ) {
433 handleFolderAction( newPath, efsw::Actions::Moved, oldPath );
434 }
435}
436
437WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher,
438 bool recursive, WatcherKqueue* parent ) {
439 static long s_fc = 0;
440 static bool s_ug = false;
441
442 std::string dir( directory );
443
444 FileSystem::dirAddSlashAtEnd( dir );
445
446 // This should never happen here
447 if ( !FileSystem::isDirectory( dir ) ) {
448 return Errors::Log::createLastError( Errors::FileNotFound, dir );
449 } else if ( pathInWatches( dir ) || pathInParent( dir ) ) {
450 return Errors::Log::createLastError( Errors::FileRepeated, directory );
451 } else if ( NULL != parent && FileSystem::isRemoteFS( dir ) ) {
452 return Errors::Log::createLastError( Errors::FileRemote, dir );
453 }
454
455 std::string curPath;
456 std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
457
458 if ( "" != link ) {
459 /// Avoid adding symlinks directories if it's now enabled
460 if ( NULL != parent && !mWatcher->mFileWatcher->followSymlinks() ) {
461 return Errors::Log::createLastError( Errors::FileOutOfScope, dir );
462 }
463
464 if ( pathInWatches( link ) || pathInParent( link ) ) {
465 return Errors::Log::createLastError( Errors::FileRepeated, link );
466 } else if ( !mWatcher->linkAllowed( curPath, link ) ) {
467 return Errors::Log::createLastError( Errors::FileOutOfScope, link );
468 } else {
469 dir = link;
470 }
471 }
472
473 if ( mWatcher->availablesFD() ) {
474 WatcherKqueue* watch =
475 new WatcherKqueue( ++mLastWatchID, dir, watcher, recursive, mWatcher, parent );
476
477 mWatches.insert( std::make_pair( mLastWatchID, watch ) );
478
479 watch->addAll();
480
481 s_fc++;
482
483 // if failed to open the directory... erase the watcher
484 if ( !watch->initOK() ) {
485 int le = watch->lastErrno();
486
487 mWatches.erase( watch->ID );
488
489 efSAFE_DELETE( watch );
490
491 mLastWatchID--;
492
493 // Probably the folder has too many files, create a generic watcher
494 if ( EACCES != le ) {
495 WatcherGeneric* watch =
496 new WatcherGeneric( ++mLastWatchID, dir, watcher, mWatcher, recursive );
497
498 mWatches.insert( std::make_pair( mLastWatchID, watch ) );
499 } else {
500 return Errors::Log::createLastError( Errors::Unspecified, link );
501 }
502 }
503 } else {
504 if ( !s_ug ) {
505 efDEBUG( "Started using WatcherGeneric, reached file descriptors limit: %ld. Folders "
506 "added: %ld\n",
507 mWatcher->mFileDescriptorCount, s_fc );
508 s_ug = true;
509 }
510
511 WatcherGeneric* watch =
512 new WatcherGeneric( ++mLastWatchID, dir, watcher, mWatcher, recursive );
513
514 mWatches.insert( std::make_pair( mLastWatchID, watch ) );
515 }
516
517 return mLastWatchID;
518}
519
520bool WatcherKqueue::initOK() {
521 return mInitOK;
522}
523
524void WatcherKqueue::removeWatch( WatchID watchid ) {
525 WatchMap::iterator iter = mWatches.find( watchid );
526
527 if ( iter == mWatches.end() )
528 return;
529
530 Watcher* watch = iter->second;
531
532 mWatches.erase( iter );
533
534 efSAFE_DELETE( watch );
535}
536
537bool WatcherKqueue::pathInWatches( const std::string& path ) {
538 return NULL != findWatcher( path );
539}
540
541bool WatcherKqueue::pathInParent( const std::string& path ) {
542 WatcherKqueue* pNext = mParent;
543
544 while ( NULL != pNext ) {
545 if ( pNext->pathInWatches( path ) ) {
546 return true;
547 }
548
549 pNext = pNext->mParent;
550 }
551
552 if ( mWatcher->pathInWatches( path ) ) {
553 return true;
554 }
555
556 if ( path == Directory ) {
557 return true;
558 }
559
560 return false;
561}
562
563int WatcherKqueue::lastErrno() {
564 return mErrno;
565}
566
567} // namespace efsw
568
569#endif