50 #include <tqintdict.h>
51 #include <tqptrlist.h>
52 #include <tqsocketnotifier.h>
53 #include <tqstringlist.h>
56 #include <tdeapplication.h>
58 #include <tdeconfig.h>
59 #include <tdeglobal.h>
60 #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 TDEConfigGroup config(TDEGlobal::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.path()+
"/.."), e,
true);
434 addEntry(0, TQFileInfo(e->path.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() +
"/" + path)
break;
445 removeEntry(0,e->path.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 KURL& _path)
545 if (TQDir::isRelativePath(_path.path())) {
549 TQString path = _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.path()+
"/.."), e,
true);
594 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path.path()),
597 e->m_mode = UnknownMode;
601 kdDebug(7001) <<
" Setup FAM (Req "
602 << FAMREQUEST_GETREQNUM(&(e->fr))
603 <<
") for " << e->path.path() << endl;
607 if (e->m_status == NonExistent) {
609 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
612 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path.path()),
615 e->m_mode = UnknownMode;
620 kdDebug(7001) <<
" Setup FAM (Req "
621 << FAMREQUEST_GETREQNUM(&(e->fr))
622 <<
") for " << e->path.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.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.path() << endl;
694 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
700 addEntry(0, TQFileInfo(e->path.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.path() ), mask) ) > 0 )
732 if ( e->m_status == NonExistent ) {
734 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
736 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
744 bool KDirWatchPrivate::useStat(Entry* e)
746 if ( e->path.path().startsWith(
"/media/") || e->path.path().startsWith(
"/run/") || (e->path.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.path() << endl;
775 void KDirWatchPrivate::addEntry(
KDirWatch* instance,
const KURL& _path,
776 Entry* sub_entry,
bool isDir)
778 TQString path = _path.path();
779 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
782 if ( path.length() > 1 && path.right(1) ==
"/" ) {
783 path.truncate( path.length() - 1 );
786 EntryMap::Iterator it = m_mapEntries.find( _path );
787 if ( it != m_mapEntries.end() )
790 (*it).m_entries.append(sub_entry);
791 kdDebug(7001) <<
"Added already watched Entry " << path
792 <<
" (for " << sub_entry->path <<
")" << endl;
797 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
798 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
800 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
801 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
802 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
804 e->m_mode = UnknownMode;
805 fd_Entry.remove(e->dn_fd);
816 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
817 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
819 mask |= IN_MODIFY|IN_ATTRIB;
823 inotify_rm_watch (m_inotify_fd, e->wd);
824 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path.path() ), mask);
831 (*it).addClient(instance);
832 kdDebug(7001) <<
"Added already watched Entry " << path
833 <<
" (now " << (*it).clients() <<
" clients)"
834 << TQString(TQString(
" [%1]").arg(instance->name())) << endl;
841 KDE_struct_stat stat_buf;
842 TQCString tpath = TQFile::encodeName(path);
843 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
846 m_mapEntries.insert( _path, newEntry );
848 Entry* e = &(m_mapEntries[_path]);
851 e->isDir = S_ISDIR(stat_buf.st_mode);
853 if (e->isDir && !isDir)
854 kdWarning() <<
"KDirWatch: " << path <<
" is a directory. Use addDir!" << endl;
855 else if (!e->isDir && isDir)
856 kdWarning() <<
"KDirWatch: " << path <<
" is a file. Use addFile!" << endl;
858 e->m_ctime = stat_buf.st_ctime;
859 e->m_mtime = stat_buf.st_mtime;
860 e->m_status = Normal;
861 e->m_nlink = stat_buf.st_nlink;
865 e->m_ctime = invalid_ctime;
866 e->m_mtime = invalid_mtime;
867 e->m_status = NonExistent;
873 e->m_entries.append(sub_entry);
875 e->addClient(instance);
877 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
878 << (e->m_status == NonExistent ?
" NotExisting" :
"")
879 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
880 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
885 e->m_mode = UnknownMode;
888 if ( isNoisyFile( tpath ) ) {
893 if (useFAM(e))
return;
897 if (useINotify(e))
return;
901 if (useDNotify(e))
return;
908 void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
909 const KURL& _path, Entry* sub_entry )
911 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry << endl;
912 Entry* e = entry(_path);
914 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" << endl;
919 e->m_entries.removeRef(sub_entry);
921 e->removeClient(instance);
923 if (e->m_clients.count() || e->m_entries.count()) {
924 kdDebug(7001) <<
"removeEntry: unwatched " << e->path.path() <<
" " << _path << endl;
930 if (removeList.findRef(e)==-1)
931 removeList.append(e);
937 if (e->m_mode == FAMMode) {
938 if ( e->m_status == Normal) {
939 FAMCancelMonitor(&fc, &(e->fr) );
940 kdDebug(7001) <<
"Cancelled FAM (Req "
941 << FAMREQUEST_GETREQNUM(&(e->fr))
942 <<
") for " << e->path.path() << endl;
946 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
948 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
954 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) << endl;
955 if (e->m_mode == INotifyMode) {
956 if ( e->m_status == Normal ) {
957 (void) inotify_rm_watch( m_inotify_fd, e->wd );
958 kdDebug(7001) <<
"Cancelled INotify (fd " <<
959 m_inotify_fd <<
", " << e->wd <<
960 ") for " << e->path.path() << endl;
964 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
966 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
972 if (e->m_mode == DNotifyMode) {
974 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
978 if ( e->m_status == Normal) {
981 fd_Entry.remove(e->dn_fd);
983 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
984 <<
") for " << e->path.path() << endl;
990 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
996 if (e->m_mode == StatMode) {
998 if ( statEntries == 0 ) {
1000 kdDebug(7001) <<
" Stopped Polling Timer" << endl;
1004 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path.path()
1005 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
1006 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
1008 m_mapEntries.remove( e->path );
1015 void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
1017 TQPtrList<Entry> list;
1018 int minfreq = 3600000;
1021 EntryMap::Iterator it = m_mapEntries.begin();
1022 for( ; it != m_mapEntries.end(); ++it ) {
1023 Client* c = (*it).m_clients.first();
1024 for(;c;c=(*it).m_clients.next())
1025 if (c->instance == instance)
break;
1028 list.append(&(*it));
1030 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1031 minfreq = (*it).freq;
1034 for(Entry* e=list.first();e;e=list.next())
1035 removeEntry(instance, e->path, 0);
1037 if (minfreq > freq) {
1040 if (timer->isActive()) timer->changeInterval(freq);
1041 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" << endl;
1046 bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
1048 int stillWatching = 0;
1049 Client* c = e->m_clients.first();
1050 for(;c;c=e->m_clients.next()) {
1051 if (!instance || instance == c->instance)
1052 c->watchingStopped =
true;
1053 else if (!c->watchingStopped)
1054 stillWatching += c->count;
1057 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path.path()
1058 <<
" (now " << stillWatching <<
" watchers)" << endl;
1060 if (stillWatching == 0) {
1062 e->m_ctime = invalid_ctime;
1063 e->m_mtime = invalid_mtime;
1064 e->m_status = NonExistent;
1071 bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
1074 int wasWatching = 0, newWatching = 0;
1075 Client* c = e->m_clients.first();
1076 for(;c;c=e->m_clients.next()) {
1077 if (!c->watchingStopped)
1078 wasWatching += c->count;
1079 else if (!instance || instance == c->instance) {
1080 c->watchingStopped =
false;
1081 newWatching += c->count;
1084 if (newWatching == 0)
1087 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path.path()
1088 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1093 if (wasWatching == 0) {
1095 KDE_struct_stat stat_buf;
1096 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1098 e->m_ctime = stat_buf.st_ctime;
1099 e->m_mtime = stat_buf.st_mtime;
1100 e->m_status = Normal;
1101 e->m_nlink = stat_buf.st_nlink;
1104 e->m_ctime = invalid_ctime;
1105 e->m_mtime = invalid_mtime;
1106 e->m_status = NonExistent;
1119 void KDirWatchPrivate::stopScan(
KDirWatch* instance)
1121 EntryMap::Iterator it = m_mapEntries.begin();
1122 for( ; it != m_mapEntries.end(); ++it )
1123 stopEntryScan(instance, &(*it));
1127 void KDirWatchPrivate::startScan(
KDirWatch* instance,
1128 bool notify,
bool skippedToo )
1131 resetList(instance,skippedToo);
1133 EntryMap::Iterator it = m_mapEntries.begin();
1134 for( ; it != m_mapEntries.end(); ++it )
1135 restartEntryScan(instance, &(*it), notify);
1142 void KDirWatchPrivate::resetList(
KDirWatch* ,
1145 EntryMap::Iterator it = m_mapEntries.begin();
1146 for( ; it != m_mapEntries.end(); ++it ) {
1148 Client* c = (*it).m_clients.first();
1149 for(;c;c=(*it).m_clients.next())
1150 if (!c->watchingStopped || skippedToo)
1151 c->pending = NoChange;
1157 int KDirWatchPrivate::scanEntry(Entry* e)
1160 if (e->m_mode == FAMMode) {
1162 if(!e->dirty)
return NoChange;
1165 if (e->isDir)
return Changed;
1169 if (e->m_mode == UnknownMode)
return NoChange;
1171 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1172 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1174 if(!e->dirty)
return NoChange;
1175 kdDebug(7001) <<
"scanning " << e->path.path() <<
" " << e->m_status <<
" " << e->m_ctime <<
" " << e->m_mtime << endl;
1180 if (e->m_mode == StatMode) {
1185 e->msecLeft -= freq;
1186 if (e->msecLeft>0)
return NoChange;
1187 e->msecLeft += e->freq;
1190 KDE_struct_stat stat_buf;
1191 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1194 if (e->m_status == NonExistent) {
1197 e->m_ctime = stat_buf.st_ctime;
1198 e->m_mtime = stat_buf.st_mtime;
1199 e->m_status = Normal;
1200 e->m_nlink = stat_buf.st_nlink;
1204 if ( (e->m_ctime != invalid_ctime) &&
1205 ((stat_buf.st_ctime != e->m_ctime) ||
1206 (stat_buf.st_mtime != e->m_mtime) ||
1207 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1208 e->m_ctime = stat_buf.st_ctime;
1209 e->m_mtime = stat_buf.st_mtime;
1210 e->m_nlink = stat_buf.st_nlink;
1219 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1221 e->m_status = NonExistent;
1225 e->m_ctime = invalid_ctime;
1226 e->m_mtime = invalid_mtime;
1228 e->m_status = NonExistent;
1237 void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const KURL &fileName)
1239 TQString path = e->path.path();
1240 if (!fileName.isEmpty()) {
1241 if (!TQDir::isRelativePath(fileName.path()))
1242 path = fileName.path();
1245 path +=
"/" + fileName.path();
1246 #elif defined(Q_WS_WIN)
1248 path += TQDir::currentDirPath().left(2) +
"/" + fileName.path();
1252 TQPtrListIterator<Client> cit( e->m_clients );
1253 for ( ; cit.current(); ++cit )
1255 Client* c = cit.current();
1257 if (c->instance==0 || c->count==0)
continue;
1259 if (c->watchingStopped) {
1261 if (event == Changed)
1262 c->pending |= event;
1263 else if (event == Created || event == Deleted)
1268 if (event == NoChange || event == Changed)
1269 event |= c->pending;
1270 c->pending = NoChange;
1271 if (event == NoChange)
continue;
1273 if (event & Deleted) {
1274 c->instance->setDeleted(path);
1279 if (event & Created) {
1280 c->instance->setCreated(path);
1284 if (event & Changed) {
1285 c->instance->setDirty(path);
1286 c->instance->setDirty(e->path);
1292 void KDirWatchPrivate::slotRemoveDelayed()
1295 delayRemove =
false;
1296 for(e=removeList.first();e;e=removeList.next())
1297 removeEntry(0, e->path, 0);
1304 void KDirWatchPrivate::slotRescan()
1306 EntryMap::Iterator it;
1311 bool timerRunning = timer->isActive();
1312 if ( timerRunning ) {
1320 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1321 TQPtrList<Entry> dList, cList;
1327 it = m_mapEntries.begin();
1328 for( ; it != m_mapEntries.end(); ++it ) {
1336 it = m_mapEntries.begin();
1337 for( ; it != m_mapEntries.end(); ++it ) {
1338 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty ) {
1339 (*it).propagate_dirty();
1344 it = m_mapEntries.begin();
1345 for( ; it != m_mapEntries.end(); ++it ) {
1347 if (!(*it).isValid())
continue;
1349 int ev = scanEntry( &(*it) );
1353 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1354 cList.append( &(*it) );
1355 if (! useINotify( &(*it) )) {
1362 if ((*it).m_mode == DNotifyMode) {
1363 if ((*it).isDir && (ev == Deleted)) {
1364 dList.append( &(*it) );
1368 ::close((*it).dn_fd);
1369 fd_Entry.remove((*it).dn_fd);
1374 else if ((*it).isDir && (ev == Created)) {
1376 if ( (*it).dn_fd == 0) {
1377 cList.append( &(*it) );
1378 if (! useDNotify( &(*it) )) {
1387 if ( ev != NoChange ) {
1389 EntryMap::Iterator it2;
1390 it2 = m_mapEntries.begin();
1391 for( ; it2 != m_mapEntries.end(); ++it2 ) {
1392 if ((*it).path.url() == (*it2).path.url()) {
1393 emitEvent( &(*it2), ev);
1400 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1403 for(e=dList.first();e;e=dList.next()) {
1404 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1408 for(e=cList.first();e;e=cList.next()) {
1409 removeEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e);
1413 if ( timerRunning ) {
1417 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1420 bool KDirWatchPrivate::isNoisyFile(
const char * filename )
1423 if ( *filename ==
'.') {
1424 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1425 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1428 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1435 void KDirWatchPrivate::famEventReceived()
1441 while(use_fam && FAMPending(&fc)) {
1442 if (FAMNextEvent(&fc, &fe) == -1) {
1443 kdWarning(7001) <<
"FAM connection problem, switching to polling."
1449 EntryMap::Iterator it;
1450 it = m_mapEntries.begin();
1451 for( ; it != m_mapEntries.end(); ++it )
1452 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1454 if (useINotify( &(*it) ))
continue;
1457 if (useDNotify( &(*it) ))
continue;
1466 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1469 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1472 if ((fe->code == FAMExists) ||
1473 (fe->code == FAMEndExist) ||
1474 (fe->code == FAMAcknowledge))
return;
1476 if ( isNoisyFile( fe->filename ) )
1480 EntryMap::Iterator it = m_mapEntries.begin();
1481 for( ; it != m_mapEntries.end(); ++it )
1482 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1483 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1491 kdDebug(7001) <<
"Processing FAM event ("
1492 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1493 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1494 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1495 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1496 (fe->code == FAMCreated) ?
"FAMCreated" :
1497 (fe->code == FAMMoved) ?
"FAMMoved" :
1498 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1499 (fe->code == FAMExists) ?
"FAMExists" :
1500 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1501 <<
", " << fe->filename
1502 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1512 if (e->m_status == NonExistent) {
1513 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path.path() << endl;
1519 if (!rescan_timer.isActive())
1520 rescan_timer.start(m_PollInterval,
true);
1528 if (!TQDir::isRelativePath(fe->filename))
1532 e->m_status = NonExistent;
1533 FAMCancelMonitor(&fc, &(e->fr) );
1534 kdDebug(7001) <<
"Cancelled FAMReq "
1535 << FAMREQUEST_GETREQNUM(&(e->fr))
1536 <<
" for " << e->path.path() << endl;
1538 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1544 Entry *sub_entry = e->m_entries.first();
1545 for(;sub_entry; sub_entry = e->m_entries.next())
1546 if (sub_entry->path.path() == e->path.path() +
"/" + fe->filename)
break;
1547 if (sub_entry && sub_entry->isDir) {
1548 KURL path = e->path;
1549 removeEntry(0,e->path,sub_entry);
1550 sub_entry->m_status = Normal;
1551 if (!useFAM(sub_entry))
1554 if (!useINotify(sub_entry ))
1569 void KDirWatchPrivate::famEventReceived() {}
1573 void KDirWatchPrivate::statistics()
1575 EntryMap::Iterator it;
1577 kdDebug(7001) <<
"Entries watched:" << endl;
1578 if (m_mapEntries.count()==0) {
1579 kdDebug(7001) <<
" None." << endl;
1582 it = m_mapEntries.begin();
1583 for( ; it != m_mapEntries.end(); ++it ) {
1585 kdDebug(7001) <<
" " << e->path.path() <<
" ("
1586 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1587 << (e->isDir ?
"Dir":
"File") <<
", using "
1588 << ((e->m_mode == FAMMode) ?
"FAM" :
1589 (e->m_mode == INotifyMode) ?
"INotify" :
1590 (e->m_mode == DNotifyMode) ?
"DNotify" :
1591 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1594 Client* c = e->m_clients.first();
1595 for(;c; c = e->m_clients.next()) {
1597 if (c->watchingStopped) {
1598 if (c->pending & Deleted) pending +=
"deleted ";
1599 if (c->pending & Created) pending +=
"created ";
1600 if (c->pending & Changed) pending +=
"changed ";
1601 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1602 pending =
", stopped" + pending;
1604 kdDebug(7001) <<
" by " << c->instance->name()
1605 <<
" (" << c->count <<
" times)"
1608 if (e->m_entries.count()>0) {
1609 kdDebug(7001) <<
" dependent entries:" << endl;
1610 Entry* d = e->m_entries.first();
1611 for(;d; d = e->m_entries.next()) {
1612 kdDebug(7001) <<
" " << d << endl;
1613 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " << endl;
1625 static KStaticDeleter<KDirWatch> sd_dw;
1631 sd_dw.setObject( s_pSelf,
new KDirWatch );
1639 return s_pSelf != 0;
1643 : TQObject(parent,name)
1646 static int nameCounter = 0;
1649 setName(TQString(TQString(
"KDirWatch-%1").arg(nameCounter)).ascii());
1653 dwp_self =
new KDirWatchPrivate;
1662 d->removeEntries(
this);
1675 if (watchFiles || recursive) {
1676 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1678 if (d) d->addEntry(
this, _path, 0,
true);
1684 if (watchFiles || recursive) {
1685 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1687 if (d) d->addEntry(
this, _url, 0,
true);
1692 if (d) d->addEntry(
this, _path, 0,
false);
1697 KDirWatchPrivate::Entry* e = d->entry(_path);
1700 return TQDateTime();
1703 result.setTime_t(e->m_ctime);
1709 if (d) d->removeEntry(
this, _path, 0);
1714 if (d) d->removeEntry(
this, _url, 0);
1719 if (d) d->removeEntry(
this, _path, 0);
1725 KDirWatchPrivate::Entry *e = d->entry(_path);
1726 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1734 KDirWatchPrivate::Entry *e = d->entry(_path);
1737 return d->restartEntryScan(
this, e,
false);
1744 if (d) d->stopScan(
this);
1751 if (d) d->startScan(
this, notify, skippedToo);
1757 KDirWatchPrivate::Entry* e = d->entry(_path);
1761 KDirWatchPrivate::Client* c = e->m_clients.first();
1762 for(;c;c=e->m_clients.next())
1763 if (c->instance ==
this)
return true;
1771 kdDebug(7001) <<
"KDirWatch not used" << endl;
1774 dwp_self->statistics();
1780 kdDebug(7001) << name() <<
" emitting created " << _file << endl;
1786 kdDebug(7001) << name() <<
" emitting dirty " << _file << endl;
1787 emit
dirty( _file );
1792 kdDebug(7001) << name() <<
" emitting dirty " << _url << endl;
1798 kdDebug(7001) << name() <<
" emitting deleted " << _file << endl;
1806 return KDirWatch::FAM;
1809 if (d->supports_inotify)
1810 return KDirWatch::INotify;
1813 if (d->supports_dnotify)
1814 return KDirWatch::DNotify;
1816 return KDirWatch::Stat;
1820 #include "kdirwatch.moc"
1821 #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.