51 #include <tqintdict.h> 52 #include <tqptrlist.h> 53 #include <tqsocketnotifier.h> 54 #include <tqstringlist.h> 57 #include <tdeapplication.h> 59 #include <tdeconfig.h> 60 #include <tdeglobal.h> 61 #include <kstaticdeleter.h> 66 #include <sys/ioctl.h> 71 #include <sys/syscall.h> 72 #include <linux/types.h> 74 #define _S390_BITOPS_H 75 #include <sys/inotify.h> 77 #ifndef __NR_inotify_init 79 #define __NR_inotify_init 291 80 #define __NR_inotify_add_watch 292 81 #define __NR_inotify_rm_watch 293 84 #define __NR_inotify_init 275 85 #define __NR_inotify_add_watch 276 86 #define __NR_inotify_rm_watch 277 88 #if defined(__x86_64__) 89 #define __NR_inotify_init 253 90 #define __NR_inotify_add_watch 254 91 #define __NR_inotify_rm_watch 255 96 #define IN_ONLYDIR 0x01000000 99 #ifndef IN_DONT_FOLLOW 100 #define IN_DONT_FOLLOW 0x02000000 104 #define IN_MOVE_SELF 0x00000800 109 #include <sys/utsname.h> 111 #include "kdirwatch.h" 112 #include "kdirwatch_p.h" 115 #define NO_NOTIFY (time_t) 0 117 static KDirWatchPrivate* dwp_self = 0;
121 static int dnotify_signal = 0;
131 void KDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
133 if (!dwp_self)
return;
137 int saved_errno = errno;
139 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
144 if(e && e->dn_fd == si->si_fd)
148 write(dwp_self->mPipe[1], &c, 1);
152 static struct sigaction old_sigio_act;
157 void KDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
163 int saved_errno = errno;
165 dwp_self->rescan_all =
true;
167 write(dwp_self->mPipe[1], &c, 1);
173 if (old_sigio_act.sa_flags & SA_SIGINFO)
175 if (old_sigio_act.sa_sigaction)
176 (*old_sigio_act.sa_sigaction)(sig, si, p);
180 if ((old_sigio_act.sa_handler != SIG_DFL) &&
181 (old_sigio_act.sa_handler != SIG_IGN))
182 (*old_sigio_act.sa_handler)(sig);
220 KDirWatchPrivate::KDirWatchPrivate()
221 : rescan_timer(0,
"KDirWatchPrivate::rescan_timer")
223 timer =
new TQTimer(
this,
"KDirWatchPrivate::timer");
224 connect (timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
230 TDEConfigGroup config(TDEGlobal::config(), TQCString(
"DirWatch"));
231 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
232 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
234 TQString available(
"Stat");
238 connect(&rescan_timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
242 if (FAMOpen(&fc) ==0) {
243 available +=
", FAM";
245 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
246 TQSocketNotifier::Read,
this);
247 connect( sn, TQT_SIGNAL(activated(
int)),
248 this, TQT_SLOT(famEventReceived()) );
251 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" << endl;
257 supports_inotify =
true;
259 m_inotify_fd = inotify_init();
261 if ( m_inotify_fd <= 0 ) {
262 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" << endl;
263 supports_inotify =
false;
268 int major, minor, patch;
270 supports_inotify =
false;
271 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
272 supports_inotify =
false;
273 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
274 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" << endl;
275 supports_inotify =
false;
279 if ( supports_inotify ) {
280 available +=
", Inotify";
281 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
283 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
284 connect( mSn, TQT_SIGNAL(activated(
int )),
this, TQT_SLOT( slotActivated() ) );
292 supports_dnotify = !supports_inotify;
295 supports_dnotify =
true;
299 int major, minor, patch;
301 supports_dnotify =
false;
302 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
303 supports_dnotify =
false;
304 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
305 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" << endl;
306 supports_dnotify =
false;
309 if( supports_dnotify ) {
310 available +=
", DNotify";
313 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
314 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
315 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
316 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
317 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
318 connect(mSn, TQT_SIGNAL(activated(
int)),
this, TQT_SLOT(slotActivated()));
320 if ( dnotify_signal == 0 )
322 dnotify_signal = SIGRTMIN + 8;
324 struct sigaction act;
325 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
326 sigemptyset(&act.sa_mask);
327 act.sa_flags = SA_SIGINFO;
329 act.sa_flags |= SA_RESTART;
331 sigaction(dnotify_signal, &act, NULL);
333 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
334 sigaction(SIGIO, &act, &old_sigio_act);
344 kdDebug(7001) <<
"Available methods: " << available << endl;
348 KDirWatchPrivate::~KDirWatchPrivate()
358 kdDebug(7001) <<
"KDirWatch deleted (FAM closed)" << endl;
362 if ( supports_inotify )
363 ::close( m_inotify_fd );
373 void KDirWatchPrivate::slotActivated()
376 if ( supports_dnotify )
378 char dummy_buf[4096];
379 read(mPipe[0], &dummy_buf, 4096);
381 if (!rescan_timer.isActive())
382 rescan_timer.start(m_PollInterval,
true );
389 if ( !supports_inotify )
395 assert( m_inotify_fd > -1 );
396 ioctl( m_inotify_fd, FIONREAD, &pending );
398 while ( pending > 0 ) {
400 if ( pending > (
int)
sizeof( buf ) )
401 pending =
sizeof( buf );
403 pending = read( m_inotify_fd, buf, pending);
405 while ( pending > 0 ) {
406 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
407 pending -=
sizeof(
struct inotify_event ) + event->len;
408 offset +=
sizeof(
struct inotify_event ) + event->len;
412 path = TQFile::decodeName( TQCString( event->name, event->len ) );
414 if ( path.length() && isNoisyFile( path.latin1() ) )
417 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path << endl;
422 for ( EntryMap::Iterator it = m_mapEntries.begin();
423 it != m_mapEntries.end(); ++it ) {
425 if ( e->wd == event->wd ) {
428 if ( 1 || e->isDir) {
429 if( event->mask & IN_DELETE_SELF) {
430 kdDebug(7001) <<
"-->got deleteself signal for " << e->path << endl;
431 e->m_status = NonExistent;
433 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
435 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
437 if ( event->mask & IN_IGNORED ) {
440 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
441 Entry *sub_entry = e->m_entries.first();
442 for(;sub_entry; sub_entry = e->m_entries.next())
443 if (sub_entry->path == e->path.path() +
"/" + path)
break;
446 removeEntry(0,e->path.path(), sub_entry);
447 KDE_struct_stat stat_buf;
448 TQCString tpath = TQFile::encodeName(path);
449 KDE_stat(tpath, &stat_buf);
456 if(!useINotify(sub_entry))
458 sub_entry->dirty =
true;
463 if (!rescan_timer.isActive())
464 rescan_timer.start(m_PollInterval,
true );
479 void KDirWatchPrivate::Entry::propagate_dirty()
481 for (TQPtrListIterator<Entry> sub_entry (m_entries);
482 sub_entry.current(); ++sub_entry)
484 if (!sub_entry.current()->dirty)
486 sub_entry.current()->dirty =
true;
487 sub_entry.current()->propagate_dirty();
496 void KDirWatchPrivate::Entry::addClient(
KDirWatch* instance)
498 Client* client = m_clients.first();
499 for(;client; client = m_clients.next())
500 if (client->instance == instance)
break;
508 client->instance = instance;
510 client->watchingStopped = instance->
isStopped();
511 client->pending = NoChange;
513 m_clients.append(client);
516 void KDirWatchPrivate::Entry::removeClient(
KDirWatch* instance)
518 Client* client = m_clients.first();
519 for(;client; client = m_clients.next())
520 if (client->instance == instance)
break;
524 if (client->count == 0) {
525 m_clients.removeRef(client);
532 int KDirWatchPrivate::Entry::clients()
535 Client* client = m_clients.first();
536 for(;client; client = m_clients.next())
537 clients += client->count;
543 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(
const KURL& _path)
546 if (TQDir::isRelativePath(_path.path())) {
550 TQString path = _path.path();
552 if ( path.length() > 1 && path.right(1) ==
"/" )
553 path.truncate( path.length() - 1 );
555 EntryMap::Iterator it = m_mapEntries.find( _path );
556 if ( it == m_mapEntries.end() )
563 void KDirWatchPrivate::useFreq(Entry* e,
int newFreq)
568 if (e->freq < freq) {
570 if (timer->isActive()) timer->changeInterval(freq);
571 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" << endl;
578 bool KDirWatchPrivate::useFAM(Entry* e)
580 if (!use_fam)
return false;
590 if (e->m_status == NonExistent) {
592 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
595 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path.path()),
598 e->m_mode = UnknownMode;
602 kdDebug(7001) <<
" Setup FAM (Req " 603 << FAMREQUEST_GETREQNUM(&(e->fr))
604 <<
") for " << e->path.path() << endl;
608 if (e->m_status == NonExistent) {
610 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
613 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path.path()),
616 e->m_mode = UnknownMode;
621 kdDebug(7001) <<
" Setup FAM (Req " 622 << FAMREQUEST_GETREQNUM(&(e->fr))
623 <<
") for " << e->path.path() << endl;
638 bool KDirWatchPrivate::useDNotify(Entry* e)
642 if (!supports_dnotify)
return false;
644 e->m_mode = DNotifyMode;
647 if (e->m_status == Normal) {
648 int fd = KDE_open(TQFile::encodeName(e->path.path()).data(), O_RDONLY);
661 int fd2 = fcntl(fd, F_DUPFD, 128);
668 e->m_mode = UnknownMode;
672 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
674 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
675 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
677 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
678 fcntl(fd, F_NOTIFY, mask) < 0) {
680 kdDebug(7001) <<
"Not using Linux Directory Notifications." 682 supports_dnotify =
false;
684 e->m_mode = UnknownMode;
688 fd_Entry.replace(fd, e);
691 kdDebug(7001) <<
" Setup DNotify (fd " << fd
692 <<
") for " << e->path.path() << endl;
695 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
701 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
710 bool KDirWatchPrivate::useINotify( Entry* e )
714 if (!supports_inotify)
return false;
716 e->m_mode = INotifyMode;
718 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
720 mask |= IN_MODIFY|IN_ATTRIB;
725 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
726 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
729 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
730 TQFile::encodeName( e->path.path() ), mask) ) > 0 )
733 if ( e->m_status == NonExistent ) {
735 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
737 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
745 bool KDirWatchPrivate::useStat(Entry* e)
747 if ( e->path.path().startsWith(
"/media/") || e->path.path().startsWith(
"/run/") || (e->path.path() ==
"/media")
749 useFreq(e, m_nfsPollInterval);
751 useFreq(e, m_PollInterval);
753 if (e->m_mode != StatMode) {
754 e->m_mode = StatMode;
757 if ( statEntries == 1 ) {
760 kdDebug(7001) <<
" Started Polling Timer, freq " << freq << endl;
764 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
765 <<
") for " << e->path.path() << endl;
776 void KDirWatchPrivate::addEntry(
KDirWatch* instance,
const KURL& _path,
777 Entry* sub_entry,
bool isDir)
779 TQString path = _path.path();
780 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
783 if ( path.length() > 1 && path.right(1) ==
"/" ) {
784 path.truncate( path.length() - 1 );
787 EntryMap::Iterator it = m_mapEntries.find( _path );
788 if ( it != m_mapEntries.end() )
791 (*it).m_entries.append(sub_entry);
792 kdDebug(7001) <<
"Added already watched Entry " << path
793 <<
" (for " << sub_entry->path <<
")" << endl;
798 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
799 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
801 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
802 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
803 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
805 e->m_mode = UnknownMode;
806 fd_Entry.remove(e->dn_fd);
817 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
818 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
820 mask |= IN_MODIFY|IN_ATTRIB;
824 inotify_rm_watch (m_inotify_fd, e->wd);
825 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path.path() ), mask);
832 (*it).addClient(instance);
833 kdDebug(7001) <<
"Added already watched Entry " << path
834 <<
" (now " << (*it).clients() <<
" clients)" 835 << TQString(TQString(
" [%1]").arg(instance->name())) << endl;
842 KDE_struct_stat stat_buf;
843 TQCString tpath = TQFile::encodeName(path);
844 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
847 m_mapEntries.insert( _path, newEntry );
849 Entry* e = &(m_mapEntries[_path]);
852 e->isDir = S_ISDIR(stat_buf.st_mode);
854 if (e->isDir && !isDir)
855 kdWarning() <<
"KDirWatch: " << path <<
" is a directory. Use addDir!" << endl;
856 else if (!e->isDir && isDir)
857 kdWarning() <<
"KDirWatch: " << path <<
" is a file. Use addFile!" << endl;
859 e->m_ctime = stat_buf.st_ctime;
860 e->m_mtime = stat_buf.st_mtime;
861 e->m_status = Normal;
862 e->m_nlink = stat_buf.st_nlink;
866 e->m_ctime = invalid_ctime;
867 e->m_mtime = invalid_mtime;
868 e->m_status = NonExistent;
874 e->m_entries.append(sub_entry);
876 e->addClient(instance);
878 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
879 << (e->m_status == NonExistent ?
" NotExisting" :
"")
880 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
881 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
886 e->m_mode = UnknownMode;
889 if ( isNoisyFile( tpath ) ) {
894 if (useFAM(e))
return;
898 if (useINotify(e))
return;
902 if (useDNotify(e))
return;
909 void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
910 const KURL& _path, Entry* sub_entry )
912 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry << endl;
913 Entry* e = entry(_path);
915 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" << endl;
920 e->m_entries.removeRef(sub_entry);
922 e->removeClient(instance);
924 if (e->m_clients.count() || e->m_entries.count()) {
925 kdDebug(7001) <<
"removeEntry: unwatched " << e->path.path() <<
" " << _path << endl;
931 if (removeList.findRef(e)==-1)
932 removeList.append(e);
938 if (e->m_mode == FAMMode) {
939 if ( e->m_status == Normal) {
940 FAMCancelMonitor(&fc, &(e->fr) );
941 kdDebug(7001) <<
"Cancelled FAM (Req " 942 << FAMREQUEST_GETREQNUM(&(e->fr))
943 <<
") for " << e->path.path() << endl;
947 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
949 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
955 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) << endl;
956 if (e->m_mode == INotifyMode) {
957 if ( e->m_status == Normal ) {
958 (void) inotify_rm_watch( m_inotify_fd, e->wd );
959 kdDebug(7001) <<
"Cancelled INotify (fd " <<
960 m_inotify_fd <<
", " << e->wd <<
961 ") for " << e->path.path() << endl;
965 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
967 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
973 if (e->m_mode == DNotifyMode) {
975 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
979 if ( e->m_status == Normal) {
982 fd_Entry.remove(e->dn_fd);
984 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
985 <<
") for " << e->path.path() << endl;
991 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
997 if (e->m_mode == StatMode) {
999 if ( statEntries == 0 ) {
1001 kdDebug(7001) <<
" Stopped Polling Timer" << endl;
1005 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path.path()
1006 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
1007 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
1009 m_mapEntries.remove( e->path );
1016 void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
1018 TQPtrList<Entry> list;
1019 int minfreq = 3600000;
1022 EntryMap::Iterator it = m_mapEntries.begin();
1023 for( ; it != m_mapEntries.end(); ++it ) {
1024 Client* c = (*it).m_clients.first();
1025 for(;c;c=(*it).m_clients.next())
1026 if (c->instance == instance)
break;
1029 list.append(&(*it));
1031 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1032 minfreq = (*it).freq;
1035 for(Entry* e=list.first();e;e=list.next())
1036 removeEntry(instance, e->path, 0);
1038 if (minfreq > freq) {
1041 if (timer->isActive()) timer->changeInterval(freq);
1042 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" << endl;
1047 bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
1049 int stillWatching = 0;
1050 Client* c = e->m_clients.first();
1051 for(;c;c=e->m_clients.next()) {
1052 if (!instance || instance == c->instance)
1053 c->watchingStopped =
true;
1054 else if (!c->watchingStopped)
1055 stillWatching += c->count;
1058 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path.path()
1059 <<
" (now " << stillWatching <<
" watchers)" << endl;
1061 if (stillWatching == 0) {
1063 e->m_ctime = invalid_ctime;
1064 e->m_mtime = invalid_mtime;
1065 e->m_status = NonExistent;
1072 bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
1075 int wasWatching = 0, newWatching = 0;
1076 Client* c = e->m_clients.first();
1077 for(;c;c=e->m_clients.next()) {
1078 if (!c->watchingStopped)
1079 wasWatching += c->count;
1080 else if (!instance || instance == c->instance) {
1081 c->watchingStopped =
false;
1082 newWatching += c->count;
1085 if (newWatching == 0)
1088 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path.path()
1089 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1094 if (wasWatching == 0) {
1096 KDE_struct_stat stat_buf;
1097 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1099 e->m_ctime = stat_buf.st_ctime;
1100 e->m_mtime = stat_buf.st_mtime;
1101 e->m_status = Normal;
1102 e->m_nlink = stat_buf.st_nlink;
1105 e->m_ctime = invalid_ctime;
1106 e->m_mtime = invalid_mtime;
1107 e->m_status = NonExistent;
1120 void KDirWatchPrivate::stopScan(
KDirWatch* instance)
1122 EntryMap::Iterator it = m_mapEntries.begin();
1123 for( ; it != m_mapEntries.end(); ++it )
1124 stopEntryScan(instance, &(*it));
1128 void KDirWatchPrivate::startScan(
KDirWatch* instance,
1129 bool notify,
bool skippedToo )
1132 resetList(instance,skippedToo);
1134 EntryMap::Iterator it = m_mapEntries.begin();
1135 for( ; it != m_mapEntries.end(); ++it )
1136 restartEntryScan(instance, &(*it), notify);
1143 void KDirWatchPrivate::resetList(
KDirWatch* ,
1146 EntryMap::Iterator it = m_mapEntries.begin();
1147 for( ; it != m_mapEntries.end(); ++it ) {
1149 Client* c = (*it).m_clients.first();
1150 for(;c;c=(*it).m_clients.next())
1151 if (!c->watchingStopped || skippedToo)
1152 c->pending = NoChange;
1158 int KDirWatchPrivate::scanEntry(Entry* e)
1161 if (e->m_mode == FAMMode) {
1163 if(!e->dirty)
return NoChange;
1166 if (e->isDir)
return Changed;
1170 if (e->m_mode == UnknownMode)
return NoChange;
1172 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY ) 1173 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1175 if(!e->dirty)
return NoChange;
1176 kdDebug(7001) <<
"scanning " << e->path.path() <<
" " << e->m_status <<
" " << e->m_ctime <<
" " << e->m_mtime << endl;
1181 if (e->m_mode == StatMode) {
1186 e->msecLeft -= freq;
1187 if (e->msecLeft>0)
return NoChange;
1188 e->msecLeft += e->freq;
1191 KDE_struct_stat stat_buf;
1192 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1195 if (e->m_status == NonExistent) {
1198 e->m_ctime = stat_buf.st_ctime;
1199 e->m_mtime = stat_buf.st_mtime;
1200 e->m_status = Normal;
1201 e->m_nlink = stat_buf.st_nlink;
1205 if ( (e->m_ctime != invalid_ctime) &&
1206 ((stat_buf.st_ctime != e->m_ctime) ||
1207 (stat_buf.st_mtime != e->m_mtime) ||
1208 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1209 e->m_ctime = stat_buf.st_ctime;
1210 e->m_mtime = stat_buf.st_mtime;
1211 e->m_nlink = stat_buf.st_nlink;
1220 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1222 e->m_status = NonExistent;
1226 e->m_ctime = invalid_ctime;
1227 e->m_mtime = invalid_mtime;
1229 e->m_status = NonExistent;
1238 void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const KURL &fileName)
1240 TQString path = e->path.path();
1241 if (!fileName.isEmpty()) {
1242 if (!TQDir::isRelativePath(fileName.path()))
1243 path = fileName.path();
1246 path +=
"/" + fileName.path();
1247 #elif defined(Q_WS_WIN) 1249 path += TQDir::currentDirPath().left(2) +
"/" + fileName.path();
1253 TQPtrListIterator<Client> cit( e->m_clients );
1254 for ( ; cit.current(); ++cit )
1256 Client* c = cit.current();
1258 if (c->instance==0 || c->count==0)
continue;
1260 if (c->watchingStopped) {
1262 if (event == Changed)
1263 c->pending |= event;
1264 else if (event == Created || event == Deleted)
1269 if (event == NoChange || event == Changed)
1270 event |= c->pending;
1271 c->pending = NoChange;
1272 if (event == NoChange)
continue;
1274 if (event & Deleted) {
1275 c->instance->setDeleted(path);
1280 if (event & Created) {
1281 c->instance->setCreated(path);
1285 if (event & Changed) {
1286 c->instance->setDirty(path);
1287 c->instance->setDirty(e->path);
1293 void KDirWatchPrivate::slotRemoveDelayed()
1296 delayRemove =
false;
1297 for(e=removeList.first();e;e=removeList.next())
1298 removeEntry(0, e->path, 0);
1305 void KDirWatchPrivate::slotRescan()
1307 EntryMap::Iterator it;
1312 bool timerRunning = timer->isActive();
1313 if ( timerRunning ) {
1321 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) 1322 TQPtrList<Entry> dList, cList;
1328 it = m_mapEntries.begin();
1329 for( ; it != m_mapEntries.end(); ++it ) {
1337 it = m_mapEntries.begin();
1338 for( ; it != m_mapEntries.end(); ++it ) {
1339 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty ) {
1340 (*it).propagate_dirty();
1345 it = m_mapEntries.begin();
1346 for( ; it != m_mapEntries.end(); ++it ) {
1348 if (!(*it).isValid())
continue;
1350 int ev = scanEntry( &(*it) );
1354 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1355 cList.append( &(*it) );
1356 if (! useINotify( &(*it) )) {
1363 if ((*it).m_mode == DNotifyMode) {
1364 if ((*it).isDir && (ev == Deleted)) {
1365 dList.append( &(*it) );
1369 ::close((*it).dn_fd);
1370 fd_Entry.remove((*it).dn_fd);
1375 else if ((*it).isDir && (ev == Created)) {
1377 if ( (*it).dn_fd == 0) {
1378 cList.append( &(*it) );
1379 if (! useDNotify( &(*it) )) {
1388 if ( ev != NoChange ) {
1390 EntryMap::Iterator it2;
1391 it2 = m_mapEntries.begin();
1392 for( ; it2 != m_mapEntries.end(); ++it2 ) {
1393 if ((*it).path.url() == (*it2).path.url()) {
1394 emitEvent( &(*it2), ev);
1401 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) 1404 for(e=dList.first();e;e=dList.next()) {
1405 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1409 for(e=cList.first();e;e=cList.next()) {
1410 removeEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e);
1414 if ( timerRunning ) {
1418 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1421 bool KDirWatchPrivate::isNoisyFile(
const char * filename )
1424 if ( *filename ==
'.') {
1425 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1426 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1429 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1436 void KDirWatchPrivate::famEventReceived()
1442 while(use_fam && FAMPending(&fc)) {
1443 if (FAMNextEvent(&fc, &fe) == -1) {
1444 kdWarning(7001) <<
"FAM connection problem, switching to polling." 1450 EntryMap::Iterator it;
1451 it = m_mapEntries.begin();
1452 for( ; it != m_mapEntries.end(); ++it )
1453 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1455 if (useINotify( &(*it) ))
continue;
1458 if (useDNotify( &(*it) ))
continue;
1467 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1470 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1473 if ((fe->code == FAMExists) ||
1474 (fe->code == FAMEndExist) ||
1475 (fe->code == FAMAcknowledge))
return;
1477 if ( isNoisyFile( fe->filename ) )
1481 EntryMap::Iterator it = m_mapEntries.begin();
1482 for( ; it != m_mapEntries.end(); ++it )
1483 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1484 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1492 kdDebug(7001) <<
"Processing FAM event (" 1493 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1494 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1495 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1496 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1497 (fe->code == FAMCreated) ?
"FAMCreated" :
1498 (fe->code == FAMMoved) ?
"FAMMoved" :
1499 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1500 (fe->code == FAMExists) ?
"FAMExists" :
1501 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1502 <<
", " << fe->filename
1503 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1513 if (e->m_status == NonExistent) {
1514 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path.path() << endl;
1520 if (!rescan_timer.isActive())
1521 rescan_timer.start(m_PollInterval,
true);
1529 if (!TQDir::isRelativePath(fe->filename))
1533 e->m_status = NonExistent;
1534 FAMCancelMonitor(&fc, &(e->fr) );
1535 kdDebug(7001) <<
"Cancelled FAMReq " 1536 << FAMREQUEST_GETREQNUM(&(e->fr))
1537 <<
" for " << e->path.path() << endl;
1539 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1545 Entry *sub_entry = e->m_entries.first();
1546 for(;sub_entry; sub_entry = e->m_entries.next())
1547 if (sub_entry->path.path() == e->path.path() +
"/" + fe->filename)
break;
1548 if (sub_entry && sub_entry->isDir) {
1549 KURL path = e->path;
1550 removeEntry(0,e->path,sub_entry);
1551 sub_entry->m_status = Normal;
1552 if (!useFAM(sub_entry))
1554 if (!useINotify(sub_entry ))
1566 void KDirWatchPrivate::famEventReceived() {}
1570 void KDirWatchPrivate::statistics()
1572 EntryMap::Iterator it;
1574 kdDebug(7001) <<
"Entries watched:" << endl;
1575 if (m_mapEntries.count()==0) {
1576 kdDebug(7001) <<
" None." << endl;
1579 it = m_mapEntries.begin();
1580 for( ; it != m_mapEntries.end(); ++it ) {
1582 kdDebug(7001) <<
" " << e->path.path() <<
" (" 1583 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1584 << (e->isDir ?
"Dir":
"File") <<
", using " 1585 << ((e->m_mode == FAMMode) ?
"FAM" :
1586 (e->m_mode == INotifyMode) ?
"INotify" :
1587 (e->m_mode == DNotifyMode) ?
"DNotify" :
1588 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1591 Client* c = e->m_clients.first();
1592 for(;c; c = e->m_clients.next()) {
1594 if (c->watchingStopped) {
1595 if (c->pending & Deleted) pending +=
"deleted ";
1596 if (c->pending & Created) pending +=
"created ";
1597 if (c->pending & Changed) pending +=
"changed ";
1598 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1599 pending =
", stopped" + pending;
1601 kdDebug(7001) <<
" by " << c->instance->name()
1602 <<
" (" << c->count <<
" times)" 1605 if (e->m_entries.count()>0) {
1606 kdDebug(7001) <<
" dependent entries:" << endl;
1607 Entry* d = e->m_entries.first();
1608 for(;d; d = e->m_entries.next()) {
1609 kdDebug(7001) <<
" " << d << endl;
1610 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " << endl;
1622 static KStaticDeleter<KDirWatch> sd_dw;
1628 sd_dw.setObject( s_pSelf,
new KDirWatch );
1636 return s_pSelf != 0;
1640 : TQObject(parent,name)
1643 static int nameCounter = 0;
1646 setName(TQString(TQString(
"KDirWatch-%1").arg(nameCounter)).ascii());
1650 dwp_self =
new KDirWatchPrivate;
1659 d->removeEntries(
this);
1672 if (watchFiles || recursive) {
1673 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1675 if (d) d->addEntry(
this, _path, 0,
true);
1681 if (watchFiles || recursive) {
1682 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1684 if (d) d->addEntry(
this, _url, 0,
true);
1689 if (d) d->addEntry(
this, _path, 0,
false);
1694 KDirWatchPrivate::Entry* e = d->entry(_path);
1697 return TQDateTime();
1700 result.setTime_t(e->m_ctime);
1706 if (d) d->removeEntry(
this, _path, 0);
1711 if (d) d->removeEntry(
this, _url, 0);
1716 if (d) d->removeEntry(
this, _path, 0);
1722 KDirWatchPrivate::Entry *e = d->entry(_path);
1723 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1731 KDirWatchPrivate::Entry *e = d->entry(_path);
1734 return d->restartEntryScan(
this, e,
false);
1741 if (d) d->stopScan(
this);
1748 if (d) d->startScan(
this, notify, skippedToo);
1754 KDirWatchPrivate::Entry* e = d->entry(_path);
1758 KDirWatchPrivate::Client* c = e->m_clients.first();
1759 for(;c;c=e->m_clients.next())
1760 if (c->instance ==
this)
return true;
1768 kdDebug(7001) <<
"KDirWatch not used" << endl;
1771 dwp_self->statistics();
1777 kdDebug(7001) << name() <<
" emitting created " << _file << endl;
1783 kdDebug(7001) << name() <<
" emitting dirty " << _file << endl;
1784 emit
dirty( _file );
1789 kdDebug(7001) << name() <<
" emitting dirty " << _url << endl;
1795 kdDebug(7001) << name() <<
" emitting deleted " << _file << endl;
1803 return KDirWatch::FAM;
1806 if (d->supports_inotify)
1807 return KDirWatch::INotify;
1810 if (d->supports_dnotify)
1811 return KDirWatch::DNotify;
1813 return KDirWatch::Stat;
1817 #include "kdirwatch.moc" 1818 #include "kdirwatch_p.moc" KDirWatch(TQObject *parent=0, const char *name=0)
Constructor.
static KDirWatch * self()
The KDirWatch instance usually globally used in an application.
void stopScan()
Stops scanning of all directories in internal list.
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
void setDirty(const TQString &path)
Emits dirty().
TQDateTime ctime(const TQString &path)
Returns the time the directory/file was last changed.
Method internalMethod()
Returns the preferred internal method to watch for changes.
void addFile(const TQString &file)
Adds a file to be watched.
void removeDir(const TQString &path)
Removes a directory from the list of scanned directories.
void removeFile(const TQString &file)
Removes a file from the list of watched files.
void created(const TQString &path)
Emitted when a file or directory is created.
void setDeleted(const TQString &path)
Emits deleted().
bool contains(const TQString &path) const
Check if a directory is being watched by this KDirWatch instance.
void deleted(const TQString &path)
Emitted when a file or directory is deleted.
static void statistics()
Dump statistic information about all KDirWatch instances.
void addDir(const TQString &path, bool watchFiles=false, bool recursive=false)
Adds a directory to be watched.
void dirty(const TQString &path)
Emitted when a watched object is changed.
bool restartDirScan(const TQString &path)
Restarts scanning for specified path.
void setCreated(const TQString &path)
Emits created().
TDEIO_EXPORT bool probably_slow_mounted(const TQString &filename)
Checks if the path belongs to a filesystem that is probably slow.
bool stopDirScan(const TQString &path)
Stops scanning the specified path.
static bool exists()
Returns true if there is an instance of KDirWatch.
Watch directories and files for changes.
bool isStopped()
Is scanning stopped? After creation of a KDirWatch instance, this is false.