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"
Watch directories and files for changes.
void created(const TQString &path)
Emitted when a file or directory is created.
void stopScan()
Stops scanning of all directories in internal list.
TQDateTime ctime(const TQString &path)
Returns the time the directory/file was last changed.
bool restartDirScan(const TQString &path)
Restarts scanning for specified path.
bool contains(const TQString &path) const
Check if a directory is being watched by this KDirWatch instance.
void removeDir(const TQString &path)
Removes a directory from the list of scanned directories.
static void statistics()
Dump statistic information about all KDirWatch instances.
static KDirWatch * self()
The KDirWatch instance usually globally used in an application.
Method internalMethod()
Returns the preferred internal method to watch for changes.
void setDeleted(const TQString &path)
Emits deleted().
bool stopDirScan(const TQString &path)
Stops scanning the specified path.
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
void setDirty(const TQString &path)
Emits dirty().
static bool exists()
Returns true if there is an instance of KDirWatch.
void addFile(const TQString &file)
Adds a file to be watched.
bool isStopped()
Is scanning stopped? After creation of a KDirWatch instance, this is false.
void removeFile(const TQString &file)
Removes a file from the list of watched files.
void deleted(const TQString &path)
Emitted when a file or directory is deleted.
void addDir(const TQString &path, bool watchFiles=false, bool recursive=false)
Adds a directory to be watched.
KDirWatch(TQObject *parent=0, const char *name=0)
Constructor.
void dirty(const TQString &path)
Emitted when a watched object is changed.
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.