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"