51 #include <tqintdict.h>
52 #include <tqptrlist.h>
53 #include <tqsocketnotifier.h>
54 #include <tqstringlist.h>
57 #include <kapplication.h>
61 #include <kstaticdeleter.h>
65 #include <sys/ioctl.h>
70 #include <sys/syscall.h>
71 #include <linux/types.h>
73 #define _S390_BITOPS_H
74 #include <sys/inotify.h>
76 #ifndef __NR_inotify_init
78 #define __NR_inotify_init 291
79 #define __NR_inotify_add_watch 292
80 #define __NR_inotify_rm_watch 293
83 #define __NR_inotify_init 275
84 #define __NR_inotify_add_watch 276
85 #define __NR_inotify_rm_watch 277
87 #if defined(__x86_64__)
88 #define __NR_inotify_init 253
89 #define __NR_inotify_add_watch 254
90 #define __NR_inotify_rm_watch 255
95 #define IN_ONLYDIR 0x01000000
98 #ifndef IN_DONT_FOLLOW
99 #define IN_DONT_FOLLOW 0x02000000
103 #define IN_MOVE_SELF 0x00000800
108 #include <sys/utsname.h>
110 #include "kdirwatch.h"
111 #include "kdirwatch_p.h"
114 #define NO_NOTIFY (time_t) 0
116 static KDirWatchPrivate* dwp_self = 0;
120 static int dnotify_signal = 0;
130 void KDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
132 if (!dwp_self)
return;
136 int saved_errno = errno;
138 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
143 if(e && e->dn_fd == si->si_fd)
147 write(dwp_self->mPipe[1], &c, 1);
151 static struct sigaction old_sigio_act;
156 void KDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
162 int saved_errno = errno;
164 dwp_self->rescan_all =
true;
166 write(dwp_self->mPipe[1], &c, 1);
172 if (old_sigio_act.sa_flags & SA_SIGINFO)
174 if (old_sigio_act.sa_sigaction)
175 (*old_sigio_act.sa_sigaction)(sig, si, p);
179 if ((old_sigio_act.sa_handler != SIG_DFL) &&
180 (old_sigio_act.sa_handler != SIG_IGN))
181 (*old_sigio_act.sa_handler)(sig);
219 KDirWatchPrivate::KDirWatchPrivate()
220 : rescan_timer(0,
"KDirWatchPrivate::rescan_timer")
222 timer =
new TQTimer(
this,
"KDirWatchPrivate::timer");
223 connect (timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
229 KConfigGroup config(KGlobal::config(), TQCString(
"DirWatch"));
230 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
231 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
233 TQString available(
"Stat");
237 connect(&rescan_timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
241 if (FAMOpen(&fc) ==0) {
242 available +=
", FAM";
244 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
245 TQSocketNotifier::Read,
this);
246 connect( sn, TQT_SIGNAL(activated(
int)),
247 this, TQT_SLOT(famEventReceived()) );
250 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" << endl;
256 supports_inotify =
true;
258 m_inotify_fd = inotify_init();
260 if ( m_inotify_fd <= 0 ) {
261 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" << endl;
262 supports_inotify =
false;
267 int major, minor, patch;
269 supports_inotify =
false;
270 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
271 supports_inotify =
false;
272 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
273 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" << endl;
274 supports_inotify =
false;
278 if ( supports_inotify ) {
279 available +=
", Inotify";
280 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
282 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
283 connect( mSn, TQT_SIGNAL(activated(
int )),
this, TQT_SLOT( slotActivated() ) );
291 supports_dnotify = !supports_inotify;
294 supports_dnotify =
true;
298 int major, minor, patch;
300 supports_dnotify =
false;
301 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
302 supports_dnotify =
false;
303 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
304 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" << endl;
305 supports_dnotify =
false;
308 if( supports_dnotify ) {
309 available +=
", DNotify";
312 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
313 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
314 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
315 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
316 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
317 connect(mSn, TQT_SIGNAL(activated(
int)),
this, TQT_SLOT(slotActivated()));
319 if ( dnotify_signal == 0 )
321 dnotify_signal = SIGRTMIN + 8;
323 struct sigaction act;
324 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
325 sigemptyset(&act.sa_mask);
326 act.sa_flags = SA_SIGINFO;
328 act.sa_flags |= SA_RESTART;
330 sigaction(dnotify_signal, &act, NULL);
332 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
333 sigaction(SIGIO, &act, &old_sigio_act);
343 kdDebug(7001) <<
"Available methods: " << available << endl;
347 KDirWatchPrivate::~KDirWatchPrivate()
357 kdDebug(7001) <<
"KDirWatch deleted (FAM closed)" << endl;
361 if ( supports_inotify )
362 ::close( m_inotify_fd );
372 void KDirWatchPrivate::slotActivated()
375 if ( supports_dnotify )
377 char dummy_buf[4096];
378 read(mPipe[0], &dummy_buf, 4096);
380 if (!rescan_timer.isActive())
381 rescan_timer.start(m_PollInterval,
true );
388 if ( !supports_inotify )
394 assert( m_inotify_fd > -1 );
395 ioctl( m_inotify_fd, FIONREAD, &pending );
397 while ( pending > 0 ) {
399 if ( pending > (
int)
sizeof( buf ) )
400 pending =
sizeof( buf );
402 pending = read( m_inotify_fd, buf, pending);
404 while ( pending > 0 ) {
405 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
406 pending -=
sizeof(
struct inotify_event ) + event->len;
407 offset +=
sizeof(
struct inotify_event ) + event->len;
411 path = TQFile::decodeName( TQCString( event->name, event->len ) );
413 if ( path.length() && isNoisyFile( path.latin1() ) )
416 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path << endl;
421 for ( EntryMap::Iterator it = m_mapEntries.begin();
422 it != m_mapEntries.end(); ++it ) {
424 if ( e->wd == event->wd ) {
427 if ( 1 || e->isDir) {
428 if( event->mask & IN_DELETE_SELF) {
429 kdDebug(7001) <<
"-->got deleteself signal for " << e->path << endl;
430 e->m_status = NonExistent;
432 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
434 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
436 if ( event->mask & IN_IGNORED ) {
439 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
440 Entry *sub_entry = e->m_entries.first();
441 for(;sub_entry; sub_entry = e->m_entries.next())
442 if (sub_entry->path == e->path +
"/" + path)
break;
445 removeEntry(0,e->path, sub_entry);
446 KDE_struct_stat stat_buf;
447 TQCString tpath = TQFile::encodeName(path);
448 KDE_stat(tpath, &stat_buf);
455 if(!useINotify(sub_entry))
457 sub_entry->dirty =
true;
462 if (!rescan_timer.isActive())
463 rescan_timer.start(m_PollInterval,
true );
478 void KDirWatchPrivate::Entry::propagate_dirty()
480 for (TQPtrListIterator<Entry> sub_entry (m_entries);
481 sub_entry.current(); ++sub_entry)
483 if (!sub_entry.current()->dirty)
485 sub_entry.current()->dirty =
true;
486 sub_entry.current()->propagate_dirty();
495 void KDirWatchPrivate::Entry::addClient(
KDirWatch* instance)
497 Client* client = m_clients.first();
498 for(;client; client = m_clients.next())
499 if (client->instance == instance)
break;
507 client->instance = instance;
509 client->watchingStopped = instance->
isStopped();
510 client->pending = NoChange;
512 m_clients.append(client);
515 void KDirWatchPrivate::Entry::removeClient(
KDirWatch* instance)
517 Client* client = m_clients.first();
518 for(;client; client = m_clients.next())
519 if (client->instance == instance)
break;
523 if (client->count == 0) {
524 m_clients.removeRef(client);
531 int KDirWatchPrivate::Entry::clients()
534 Client* client = m_clients.first();
535 for(;client; client = m_clients.next())
536 clients += client->count;
542 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(
const TQString& _path)
545 if (TQDir::isRelativePath(_path)) {
549 TQString path = _path;
551 if ( path.length() > 1 && path.right(1) ==
"/" )
552 path.truncate( path.length() - 1 );
554 EntryMap::Iterator it = m_mapEntries.find( path );
555 if ( it == m_mapEntries.end() )
562 void KDirWatchPrivate::useFreq(Entry* e,
int newFreq)
567 if (e->freq < freq) {
569 if (timer->isActive()) timer->changeInterval(freq);
570 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" << endl;
577 bool KDirWatchPrivate::useFAM(Entry* e)
579 if (!use_fam)
return false;
589 if (e->m_status == NonExistent) {
591 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
594 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
597 e->m_mode = UnknownMode;
601 kdDebug(7001) <<
" Setup FAM (Req "
602 << FAMREQUEST_GETREQNUM(&(e->fr))
603 <<
") for " << e->path << endl;
607 if (e->m_status == NonExistent) {
609 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
612 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
615 e->m_mode = UnknownMode;
620 kdDebug(7001) <<
" Setup FAM (Req "
621 << FAMREQUEST_GETREQNUM(&(e->fr))
622 <<
") for " << e->path << endl;
637 bool KDirWatchPrivate::useDNotify(Entry* e)
641 if (!supports_dnotify)
return false;
643 e->m_mode = DNotifyMode;
646 if (e->m_status == Normal) {
647 int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
660 int fd2 = fcntl(fd, F_DUPFD, 128);
667 e->m_mode = UnknownMode;
671 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
673 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
674 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
676 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
677 fcntl(fd, F_NOTIFY, mask) < 0) {
679 kdDebug(7001) <<
"Not using Linux Directory Notifications."
681 supports_dnotify =
false;
683 e->m_mode = UnknownMode;
687 fd_Entry.replace(fd, e);
690 kdDebug(7001) <<
" Setup DNotify (fd " << fd
691 <<
") for " << e->path << endl;
694 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
700 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
709 bool KDirWatchPrivate::useINotify( Entry* e )
713 if (!supports_inotify)
return false;
715 e->m_mode = INotifyMode;
717 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
719 mask |= IN_MODIFY|IN_ATTRIB;
724 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
725 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
728 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
729 TQFile::encodeName( e->path ), mask) ) > 0 )
732 if ( e->m_status == NonExistent ) {
734 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
736 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
744 bool KDirWatchPrivate::useStat(Entry* e)
746 if ( e->path.startsWith(
"/media/") || (e->path ==
"/media")
748 useFreq(e, m_nfsPollInterval);
750 useFreq(e, m_PollInterval);
752 if (e->m_mode != StatMode) {
753 e->m_mode = StatMode;
756 if ( statEntries == 1 ) {
759 kdDebug(7001) <<
" Started Polling Timer, freq " << freq << endl;
763 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
764 <<
") for " << e->path << endl;
775 void KDirWatchPrivate::addEntry(
KDirWatch* instance,
const TQString& _path,
776 Entry* sub_entry,
bool isDir)
778 TQString path = _path;
779 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
782 if ( path.length() > 1 && path.right(1) ==
"/" )
783 path.truncate( path.length() - 1 );
785 EntryMap::Iterator it = m_mapEntries.find( path );
786 if ( it != m_mapEntries.end() )
789 (*it).m_entries.append(sub_entry);
790 kdDebug(7001) <<
"Added already watched Entry " << path
791 <<
" (for " << sub_entry->path <<
")" << endl;
796 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
797 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
799 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
800 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
801 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
803 e->m_mode = UnknownMode;
804 fd_Entry.remove(e->dn_fd);
815 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
816 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
818 mask |= IN_MODIFY|IN_ATTRIB;
822 inotify_rm_watch (m_inotify_fd, e->wd);
823 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
830 (*it).addClient(instance);
831 kdDebug(7001) <<
"Added already watched Entry " << path
832 <<
" (now " << (*it).clients() <<
" clients)"
833 << TQString(TQString(
" [%1]").arg(instance->name())) << endl;
840 KDE_struct_stat stat_buf;
841 TQCString tpath = TQFile::encodeName(path);
842 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
845 m_mapEntries.insert( path, newEntry );
847 Entry* e = &(m_mapEntries[path]);
850 e->isDir = S_ISDIR(stat_buf.st_mode);
852 if (e->isDir && !isDir)
853 kdWarning() <<
"KDirWatch: " << path <<
" is a directory. Use addDir!" << endl;
854 else if (!e->isDir && isDir)
855 kdWarning() <<
"KDirWatch: " << path <<
" is a file. Use addFile!" << endl;
857 e->m_ctime = stat_buf.st_ctime;
858 e->m_status = Normal;
859 e->m_nlink = stat_buf.st_nlink;
863 e->m_ctime = invalid_ctime;
864 e->m_status = NonExistent;
870 e->m_entries.append(sub_entry);
872 e->addClient(instance);
874 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
875 << (e->m_status == NonExistent ?
" NotExisting" :
"")
876 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
877 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
882 e->m_mode = UnknownMode;
885 if ( isNoisyFile( tpath ) )
889 if (useFAM(e))
return;
893 if (useINotify(e))
return;
897 if (useDNotify(e))
return;
904 void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
905 const TQString& _path, Entry* sub_entry )
907 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry << endl;
908 Entry* e = entry(_path);
910 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" << endl;
915 e->m_entries.removeRef(sub_entry);
917 e->removeClient(instance);
919 if (e->m_clients.count() || e->m_entries.count()) {
920 kdDebug(7001) <<
"removeEntry: unwatched " << e->path <<
" " << _path << endl;
926 if (removeList.findRef(e)==-1)
927 removeList.append(e);
933 if (e->m_mode == FAMMode) {
934 if ( e->m_status == Normal) {
935 FAMCancelMonitor(&fc, &(e->fr) );
936 kdDebug(7001) <<
"Cancelled FAM (Req "
937 << FAMREQUEST_GETREQNUM(&(e->fr))
938 <<
") for " << e->path << endl;
942 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
944 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
950 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) << endl;
951 if (e->m_mode == INotifyMode) {
952 if ( e->m_status == Normal ) {
953 (void) inotify_rm_watch( m_inotify_fd, e->wd );
954 kdDebug(7001) <<
"Cancelled INotify (fd " <<
955 m_inotify_fd <<
", " << e->wd <<
956 ") for " << e->path << endl;
960 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
962 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
968 if (e->m_mode == DNotifyMode) {
970 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
974 if ( e->m_status == Normal) {
977 fd_Entry.remove(e->dn_fd);
979 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
980 <<
") for " << e->path << endl;
986 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
992 if (e->m_mode == StatMode) {
994 if ( statEntries == 0 ) {
996 kdDebug(7001) <<
" Stopped Polling Timer" << endl;
1000 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
1001 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
1002 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
1004 m_mapEntries.remove( e->path );
1011 void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
1013 TQPtrList<Entry> list;
1014 int minfreq = 3600000;
1017 EntryMap::Iterator it = m_mapEntries.begin();
1018 for( ; it != m_mapEntries.end(); ++it ) {
1019 Client* c = (*it).m_clients.first();
1020 for(;c;c=(*it).m_clients.next())
1021 if (c->instance == instance)
break;
1024 list.append(&(*it));
1026 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1027 minfreq = (*it).freq;
1030 for(Entry* e=list.first();e;e=list.next())
1031 removeEntry(instance, e->path, 0);
1033 if (minfreq > freq) {
1036 if (timer->isActive()) timer->changeInterval(freq);
1037 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" << endl;
1042 bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
1044 int stillWatching = 0;
1045 Client* c = e->m_clients.first();
1046 for(;c;c=e->m_clients.next()) {
1047 if (!instance || instance == c->instance)
1048 c->watchingStopped =
true;
1049 else if (!c->watchingStopped)
1050 stillWatching += c->count;
1053 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path
1054 <<
" (now " << stillWatching <<
" watchers)" << endl;
1056 if (stillWatching == 0) {
1058 e->m_ctime = invalid_ctime;
1059 e->m_status = NonExistent;
1066 bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
1069 int wasWatching = 0, newWatching = 0;
1070 Client* c = e->m_clients.first();
1071 for(;c;c=e->m_clients.next()) {
1072 if (!c->watchingStopped)
1073 wasWatching += c->count;
1074 else if (!instance || instance == c->instance) {
1075 c->watchingStopped =
false;
1076 newWatching += c->count;
1079 if (newWatching == 0)
1082 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path
1083 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1088 if (wasWatching == 0) {
1090 KDE_struct_stat stat_buf;
1091 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1093 e->m_ctime = stat_buf.st_ctime;
1094 e->m_status = Normal;
1095 e->m_nlink = stat_buf.st_nlink;
1098 e->m_ctime = invalid_ctime;
1099 e->m_status = NonExistent;
1112 void KDirWatchPrivate::stopScan(
KDirWatch* instance)
1114 EntryMap::Iterator it = m_mapEntries.begin();
1115 for( ; it != m_mapEntries.end(); ++it )
1116 stopEntryScan(instance, &(*it));
1120 void KDirWatchPrivate::startScan(
KDirWatch* instance,
1121 bool notify,
bool skippedToo )
1124 resetList(instance,skippedToo);
1126 EntryMap::Iterator it = m_mapEntries.begin();
1127 for( ; it != m_mapEntries.end(); ++it )
1128 restartEntryScan(instance, &(*it), notify);
1135 void KDirWatchPrivate::resetList(
KDirWatch* ,
1138 EntryMap::Iterator it = m_mapEntries.begin();
1139 for( ; it != m_mapEntries.end(); ++it ) {
1141 Client* c = (*it).m_clients.first();
1142 for(;c;c=(*it).m_clients.next())
1143 if (!c->watchingStopped || skippedToo)
1144 c->pending = NoChange;
1150 int KDirWatchPrivate::scanEntry(Entry* e)
1153 if (e->m_mode == FAMMode) {
1155 if(!e->dirty)
return NoChange;
1158 if (e->isDir)
return Changed;
1162 if (e->m_mode == UnknownMode)
return NoChange;
1164 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1165 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1167 if(!e->dirty)
return NoChange;
1168 kdDebug(7001) <<
"scanning " << e->path <<
" " << e->m_status <<
" " << e->m_ctime << endl;
1173 if (e->m_mode == StatMode) {
1178 e->msecLeft -= freq;
1179 if (e->msecLeft>0)
return NoChange;
1180 e->msecLeft += e->freq;
1183 KDE_struct_stat stat_buf;
1184 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1187 if (e->m_status == NonExistent) {
1190 e->m_ctime = stat_buf.st_ctime;
1191 e->m_status = Normal;
1192 e->m_nlink = stat_buf.st_nlink;
1196 if ( (e->m_ctime != invalid_ctime) &&
1197 ((stat_buf.st_ctime != e->m_ctime) ||
1198 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1199 e->m_ctime = stat_buf.st_ctime;
1200 e->m_nlink = stat_buf.st_nlink;
1209 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1211 e->m_status = NonExistent;
1215 e->m_ctime = invalid_ctime;
1217 e->m_status = NonExistent;
1226 void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const TQString &fileName)
1228 TQString path = e->path;
1229 if (!fileName.isEmpty()) {
1230 if (!TQDir::isRelativePath(fileName))
1234 path +=
"/" + fileName;
1235 #elif defined(Q_WS_WIN)
1237 path += TQDir::currentDirPath().left(2) +
"/" + fileName;
1241 TQPtrListIterator<Client> cit( e->m_clients );
1242 for ( ; cit.current(); ++cit )
1244 Client* c = cit.current();
1246 if (c->instance==0 || c->count==0)
continue;
1248 if (c->watchingStopped) {
1250 if (event == Changed)
1251 c->pending |= event;
1252 else if (event == Created || event == Deleted)
1257 if (event == NoChange || event == Changed)
1258 event |= c->pending;
1259 c->pending = NoChange;
1260 if (event == NoChange)
continue;
1262 if (event & Deleted) {
1263 c->instance->setDeleted(path);
1268 if (event & Created) {
1269 c->instance->setCreated(path);
1273 if (event & Changed)
1274 c->instance->setDirty(path);
1279 void KDirWatchPrivate::slotRemoveDelayed()
1282 delayRemove =
false;
1283 for(e=removeList.first();e;e=removeList.next())
1284 removeEntry(0, e->path, 0);
1291 void KDirWatchPrivate::slotRescan()
1293 EntryMap::Iterator it;
1298 bool timerRunning = timer->isActive();
1306 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1307 TQPtrList<Entry> dList, cList;
1313 it = m_mapEntries.begin();
1314 for( ; it != m_mapEntries.end(); ++it )
1321 it = m_mapEntries.begin();
1322 for( ; it != m_mapEntries.end(); ++it )
1323 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
1324 (*it).propagate_dirty();
1327 it = m_mapEntries.begin();
1328 for( ; it != m_mapEntries.end(); ++it ) {
1330 if (!(*it).isValid())
continue;
1332 int ev = scanEntry( &(*it) );
1336 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1337 cList.append( &(*it) );
1338 if (! useINotify( &(*it) )) {
1345 if ((*it).m_mode == DNotifyMode) {
1346 if ((*it).isDir && (ev == Deleted)) {
1347 dList.append( &(*it) );
1351 ::close((*it).dn_fd);
1352 fd_Entry.remove((*it).dn_fd);
1357 else if ((*it).isDir && (ev == Created)) {
1359 if ( (*it).dn_fd == 0) {
1360 cList.append( &(*it) );
1361 if (! useDNotify( &(*it) )) {
1370 if ( ev != NoChange )
1371 emitEvent( &(*it), ev);
1375 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1378 for(e=dList.first();e;e=dList.next())
1379 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1382 for(e=cList.first();e;e=cList.next())
1383 removeEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e);
1389 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1392 bool KDirWatchPrivate::isNoisyFile(
const char * filename )
1395 if ( *filename ==
'.') {
1396 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1397 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1400 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1407 void KDirWatchPrivate::famEventReceived()
1413 while(use_fam && FAMPending(&fc)) {
1414 if (FAMNextEvent(&fc, &fe) == -1) {
1415 kdWarning(7001) <<
"FAM connection problem, switching to polling."
1421 EntryMap::Iterator it;
1422 it = m_mapEntries.begin();
1423 for( ; it != m_mapEntries.end(); ++it )
1424 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1426 if (useINotify( &(*it) ))
continue;
1429 if (useDNotify( &(*it) ))
continue;
1438 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1441 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1444 if ((fe->code == FAMExists) ||
1445 (fe->code == FAMEndExist) ||
1446 (fe->code == FAMAcknowledge))
return;
1448 if ( isNoisyFile( fe->filename ) )
1452 EntryMap::Iterator it = m_mapEntries.begin();
1453 for( ; it != m_mapEntries.end(); ++it )
1454 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1455 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1463 kdDebug(7001) <<
"Processing FAM event ("
1464 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1465 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1466 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1467 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1468 (fe->code == FAMCreated) ?
"FAMCreated" :
1469 (fe->code == FAMMoved) ?
"FAMMoved" :
1470 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1471 (fe->code == FAMExists) ?
"FAMExists" :
1472 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1473 <<
", " << fe->filename
1474 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1484 if (e->m_status == NonExistent) {
1485 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path << endl;
1491 if (!rescan_timer.isActive())
1492 rescan_timer.start(m_PollInterval,
true);
1500 if (!TQDir::isRelativePath(fe->filename))
1504 e->m_status = NonExistent;
1505 FAMCancelMonitor(&fc, &(e->fr) );
1506 kdDebug(7001) <<
"Cancelled FAMReq "
1507 << FAMREQUEST_GETREQNUM(&(e->fr))
1508 <<
" for " << e->path << endl;
1510 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1516 Entry *sub_entry = e->m_entries.first();
1517 for(;sub_entry; sub_entry = e->m_entries.next())
1518 if (sub_entry->path == e->path +
"/" + fe->filename)
break;
1519 if (sub_entry && sub_entry->isDir) {
1520 TQString path = e->path;
1521 removeEntry(0,e->path,sub_entry);
1522 sub_entry->m_status = Normal;
1523 if (!useFAM(sub_entry))
1525 if (!useINotify(sub_entry ))
1537 void KDirWatchPrivate::famEventReceived() {}
1541 void KDirWatchPrivate::statistics()
1543 EntryMap::Iterator it;
1545 kdDebug(7001) <<
"Entries watched:" << endl;
1546 if (m_mapEntries.count()==0) {
1547 kdDebug(7001) <<
" None." << endl;
1550 it = m_mapEntries.begin();
1551 for( ; it != m_mapEntries.end(); ++it ) {
1553 kdDebug(7001) <<
" " << e->path <<
" ("
1554 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1555 << (e->isDir ?
"Dir":
"File") <<
", using "
1556 << ((e->m_mode == FAMMode) ?
"FAM" :
1557 (e->m_mode == INotifyMode) ?
"INotify" :
1558 (e->m_mode == DNotifyMode) ?
"DNotify" :
1559 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1562 Client* c = e->m_clients.first();
1563 for(;c; c = e->m_clients.next()) {
1565 if (c->watchingStopped) {
1566 if (c->pending & Deleted) pending +=
"deleted ";
1567 if (c->pending & Created) pending +=
"created ";
1568 if (c->pending & Changed) pending +=
"changed ";
1569 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1570 pending =
", stopped" + pending;
1572 kdDebug(7001) <<
" by " << c->instance->name()
1573 <<
" (" << c->count <<
" times)"
1576 if (e->m_entries.count()>0) {
1577 kdDebug(7001) <<
" dependent entries:" << endl;
1578 Entry* d = e->m_entries.first();
1579 for(;d; d = e->m_entries.next()) {
1580 kdDebug(7001) <<
" " << d << endl;
1581 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " << endl;
1593 static KStaticDeleter<KDirWatch> sd_dw;
1599 sd_dw.setObject( s_pSelf,
new KDirWatch );
1607 return s_pSelf != 0;
1611 : TQObject(parent,name)
1614 static int nameCounter = 0;
1617 setName(TQString(TQString(
"KDirWatch-%1").arg(nameCounter)).ascii());
1621 dwp_self =
new KDirWatchPrivate;
1630 d->removeEntries(
this);
1642 bool watchFiles,
bool recursive)
1644 if (watchFiles || recursive) {
1645 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
1647 if (d) d->addEntry(
this, _path, 0,
true);
1652 if (d) d->addEntry(
this, _path, 0,
false);
1657 KDirWatchPrivate::Entry* e = d->entry(_path);
1660 return TQDateTime();
1663 result.setTime_t(e->m_ctime);
1669 if (d) d->removeEntry(
this, _path, 0);
1674 if (d) d->removeEntry(
this, _path, 0);
1680 KDirWatchPrivate::Entry *e = d->entry(_path);
1681 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1689 KDirWatchPrivate::Entry *e = d->entry(_path);
1692 return d->restartEntryScan(
this, e,
false);
1699 if (d) d->stopScan(
this);
1706 if (d) d->startScan(
this, notify, skippedToo);
1712 KDirWatchPrivate::Entry* e = d->entry(_path);
1716 KDirWatchPrivate::Client* c = e->m_clients.first();
1717 for(;c;c=e->m_clients.next())
1718 if (c->instance ==
this)
return true;
1726 kdDebug(7001) <<
"KDirWatch not used" << endl;
1729 dwp_self->statistics();
1735 kdDebug(7001) << name() <<
" emitting created " << _file << endl;
1741 kdDebug(7001) << name() <<
" emitting dirty " << _file << endl;
1742 emit
dirty( _file );
1747 kdDebug(7001) << name() <<
" emitting deleted " << _file << endl;
1755 return KDirWatch::FAM;
1758 if (d->supports_inotify)
1759 return KDirWatch::INotify;
1762 if (d->supports_dnotify)
1763 return KDirWatch::DNotify;
1765 return KDirWatch::Stat;
1769 #include "kdirwatch.moc"
1770 #include "kdirwatch_p.moc"