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.startsWith(
"/run/") || (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"
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.
KIO_EXPORT bool probably_slow_mounted(const TQString &filename)
Checks if the path belongs to a filesystem that is probably slow.
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().
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.