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>
68 #include <sys/filio.h>
74 #include <sys/syscall.h>
76 #include <linux/types.h>
79 #define _S390_BITOPS_H
80 #include <sys/inotify.h>
82 #ifndef __NR_inotify_init
84 #define __NR_inotify_init 291
85 #define __NR_inotify_add_watch 292
86 #define __NR_inotify_rm_watch 293
89 #define __NR_inotify_init 275
90 #define __NR_inotify_add_watch 276
91 #define __NR_inotify_rm_watch 277
93 #if defined(__x86_64__)
94 #define __NR_inotify_init 253
95 #define __NR_inotify_add_watch 254
96 #define __NR_inotify_rm_watch 255
101 #define IN_ONLYDIR 0x01000000
104 #ifndef IN_DONT_FOLLOW
105 #define IN_DONT_FOLLOW 0x02000000
109 #define IN_MOVE_SELF 0x00000800
114 #include <sys/utsname.h>
116 #include "kdirwatch.h"
117 #include "kdirwatch_p.h"
120 #define NO_NOTIFY (time_t) 0
122 static KDirWatchPrivate* dwp_self = 0;
126 static int dnotify_signal = 0;
136 void KDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
138 if (!dwp_self)
return;
142 int saved_errno = errno;
144 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
149 if(e && e->dn_fd == si->si_fd)
153 write(dwp_self->mPipe[1], &c, 1);
157 static struct sigaction old_sigio_act;
162 void KDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
168 int saved_errno = errno;
170 dwp_self->rescan_all =
true;
172 write(dwp_self->mPipe[1], &c, 1);
178 if (old_sigio_act.sa_flags & SA_SIGINFO)
180 if (old_sigio_act.sa_sigaction)
181 (*old_sigio_act.sa_sigaction)(sig, si, p);
185 if ((old_sigio_act.sa_handler != SIG_DFL) &&
186 (old_sigio_act.sa_handler != SIG_IGN))
187 (*old_sigio_act.sa_handler)(sig);
225 KDirWatchPrivate::KDirWatchPrivate()
226 : rescan_timer(0,
"KDirWatchPrivate::rescan_timer")
228 timer =
new TQTimer(
this,
"KDirWatchPrivate::timer");
229 connect (timer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(slotRescan()));
235 TDEConfigGroup config(TDEGlobal::config(), TQCString(
"DirWatch"));
236 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
237 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
239 TQString available(
"Stat");
243 connect(&rescan_timer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(slotRescan()));
247 if (FAMOpen(&fc) ==0) {
248 available +=
", FAM";
250 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
251 TQSocketNotifier::Read,
this);
252 connect( sn, TQ_SIGNAL(activated(
int)),
253 this, TQ_SLOT(famEventReceived()) );
256 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" << endl;
262 supports_inotify =
true;
264 m_inotify_fd = inotify_init();
266 if ( m_inotify_fd <= 0 ) {
267 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" << endl;
268 supports_inotify =
false;
273 int major, minor, patch;
275 supports_inotify =
false;
276 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
277 supports_inotify =
false;
278 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
279 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" << endl;
280 supports_inotify =
false;
284 if ( supports_inotify ) {
285 available +=
", Inotify";
286 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
288 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
289 connect( mSn, TQ_SIGNAL(activated(
int )),
this, TQ_SLOT( slotActivated() ) );
297 supports_dnotify = !supports_inotify;
300 supports_dnotify =
true;
304 int major, minor, patch;
306 supports_dnotify =
false;
307 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
308 supports_dnotify =
false;
309 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
310 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" << endl;
311 supports_dnotify =
false;
314 if( supports_dnotify ) {
315 available +=
", DNotify";
318 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
319 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
320 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
321 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
322 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
323 connect(mSn, TQ_SIGNAL(activated(
int)),
this, TQ_SLOT(slotActivated()));
325 if ( dnotify_signal == 0 )
327 dnotify_signal = SIGRTMIN + 8;
329 struct sigaction act;
330 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
331 sigemptyset(&act.sa_mask);
332 act.sa_flags = SA_SIGINFO;
334 act.sa_flags |= SA_RESTART;
336 sigaction(dnotify_signal, &act, NULL);
338 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
339 sigaction(SIGIO, &act, &old_sigio_act);
349 kdDebug(7001) <<
"Available methods: " << available << endl;
353 KDirWatchPrivate::~KDirWatchPrivate()
363 kdDebug(7001) <<
"KDirWatch deleted (FAM closed)" << endl;
367 if ( supports_inotify )
368 ::close( m_inotify_fd );
378 void KDirWatchPrivate::slotActivated()
381 if ( supports_dnotify )
383 char dummy_buf[4096];
384 read(mPipe[0], &dummy_buf, 4096);
386 if (!rescan_timer.isActive())
387 rescan_timer.start(m_PollInterval,
true );
394 if ( !supports_inotify )
400 assert( m_inotify_fd > -1 );
401 ioctl( m_inotify_fd, FIONREAD, &pending );
403 while ( pending > 0 ) {
405 if ( pending > (
int)
sizeof( buf ) )
406 pending =
sizeof( buf );
408 pending = read( m_inotify_fd, buf, pending);
410 while ( pending > 0 ) {
411 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
412 pending -=
sizeof(
struct inotify_event ) + event->len;
413 offset +=
sizeof(
struct inotify_event ) + event->len;
417 path = TQFile::decodeName( TQCString( event->name, event->len ) );
419 if ( path.length() && isNoisyFile( path.latin1() ) )
422 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path << endl;
427 for ( EntryMap::Iterator it = m_mapEntries.begin();
428 it != m_mapEntries.end(); ++it ) {
430 if ( e->wd == event->wd ) {
433 if ( 1 || e->isDir) {
434 if( event->mask & IN_DELETE_SELF) {
435 kdDebug(7001) <<
"-->got deleteself signal for " << e->path << endl;
436 e->m_status = NonExistent;
438 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
440 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
442 if ( event->mask & IN_IGNORED ) {
445 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
446 Entry *sub_entry = e->m_entries.first();
447 for(;sub_entry; sub_entry = e->m_entries.next())
448 if (sub_entry->path == e->path.path() +
"/" + path)
break;
451 removeEntry(0,e->path.path(), sub_entry);
452 KDE_struct_stat stat_buf;
453 TQCString tpath = TQFile::encodeName(path);
454 KDE_stat(tpath, &stat_buf);
461 if(!useINotify(sub_entry))
463 sub_entry->dirty =
true;
468 if (!rescan_timer.isActive())
469 rescan_timer.start(m_PollInterval,
true );
484 void KDirWatchPrivate::Entry::propagate_dirty()
486 for (TQPtrListIterator<Entry> sub_entry (m_entries);
487 sub_entry.current(); ++sub_entry)
489 if (!sub_entry.current()->dirty)
491 sub_entry.current()->dirty =
true;
492 sub_entry.current()->propagate_dirty();
501 void KDirWatchPrivate::Entry::addClient(
KDirWatch* instance)
503 Client* client = m_clients.first();
504 for(;client; client = m_clients.next())
505 if (client->instance == instance)
break;
513 client->instance = instance;
515 client->watchingStopped = instance->
isStopped();
516 client->pending = NoChange;
518 m_clients.append(client);
521 void KDirWatchPrivate::Entry::removeClient(
KDirWatch* instance)
523 Client* client = m_clients.first();
524 for(;client; client = m_clients.next())
525 if (client->instance == instance)
break;
529 if (client->count == 0) {
530 m_clients.removeRef(client);
537 int KDirWatchPrivate::Entry::clients()
540 Client* client = m_clients.first();
541 for(;client; client = m_clients.next())
542 clients += client->count;
548 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(
const KURL& _path)
551 if (TQDir::isRelativePath(_path.path())) {
555 TQString path = _path.path();
557 if ( path.length() > 1 && path.right(1) ==
"/" )
558 path.truncate( path.length() - 1 );
560 EntryMap::Iterator it = m_mapEntries.find( _path );
561 if ( it == m_mapEntries.end() )
568 void KDirWatchPrivate::useFreq(Entry* e,
int newFreq)
573 if (e->freq < freq) {
575 if (timer->isActive()) timer->changeInterval(freq);
576 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" << endl;
583 bool KDirWatchPrivate::useFAM(Entry* e)
585 if (!use_fam)
return false;
595 if (e->m_status == NonExistent) {
597 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
600 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path.path()),
603 e->m_mode = UnknownMode;
607 kdDebug(7001) <<
" Setup FAM (Req "
608 << FAMREQUEST_GETREQNUM(&(e->fr))
609 <<
") for " << e->path.path() << endl;
613 if (e->m_status == NonExistent) {
615 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
618 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path.path()),
621 e->m_mode = UnknownMode;
626 kdDebug(7001) <<
" Setup FAM (Req "
627 << FAMREQUEST_GETREQNUM(&(e->fr))
628 <<
") for " << e->path.path() << endl;
643 bool KDirWatchPrivate::useDNotify(Entry* e)
647 if (!supports_dnotify)
return false;
649 e->m_mode = DNotifyMode;
652 if (e->m_status == Normal) {
653 int fd = KDE_open(TQFile::encodeName(e->path.path()).data(), O_RDONLY);
666 int fd2 = fcntl(fd, F_DUPFD, 128);
673 e->m_mode = UnknownMode;
677 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
679 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
680 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
682 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
683 fcntl(fd, F_NOTIFY, mask) < 0) {
685 kdDebug(7001) <<
"Not using Linux Directory Notifications."
687 supports_dnotify =
false;
689 e->m_mode = UnknownMode;
693 fd_Entry.replace(fd, e);
696 kdDebug(7001) <<
" Setup DNotify (fd " << fd
697 <<
") for " << e->path.path() << endl;
700 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
706 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
715 bool KDirWatchPrivate::useINotify( Entry* e )
719 if (!supports_inotify)
return false;
721 e->m_mode = INotifyMode;
723 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
725 mask |= IN_MODIFY|IN_ATTRIB;
730 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
731 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
734 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
735 TQFile::encodeName( e->path.path() ), mask) ) > 0 )
738 if ( e->m_status == NonExistent ) {
740 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
742 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
750 bool KDirWatchPrivate::useStat(Entry* e)
752 if ( e->path.path().startsWith(
"/media/") || e->path.path().startsWith(
"/run/") || (e->path.path() ==
"/media")
754 useFreq(e, m_nfsPollInterval);
756 useFreq(e, m_PollInterval);
758 if (e->m_mode != StatMode) {
759 e->m_mode = StatMode;
762 if ( statEntries == 1 ) {
765 kdDebug(7001) <<
" Started Polling Timer, freq " << freq << endl;
769 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
770 <<
") for " << e->path.path() << endl;
781 void KDirWatchPrivate::addEntry(
KDirWatch* instance,
const KURL& _path,
782 Entry* sub_entry,
bool isDir)
784 TQString path = _path.path();
785 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
788 if ( path.length() > 1 && path.right(1) ==
"/" ) {
789 path.truncate( path.length() - 1 );
792 EntryMap::Iterator it = m_mapEntries.find( _path );
793 if ( it != m_mapEntries.end() )
796 (*it).m_entries.append(sub_entry);
797 kdDebug(7001) <<
"Added already watched Entry " << path
798 <<
" (for " << sub_entry->path <<
")" << endl;
803 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
804 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
806 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
807 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
808 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
810 e->m_mode = UnknownMode;
811 fd_Entry.remove(e->dn_fd);
822 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
823 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
825 mask |= IN_MODIFY|IN_ATTRIB;
829 inotify_rm_watch (m_inotify_fd, e->wd);
830 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path.path() ), mask);
837 (*it).addClient(instance);
838 kdDebug(7001) <<
"Added already watched Entry " << path
839 <<
" (now " << (*it).clients() <<
" clients)"
840 << TQString(TQString(
" [%1]").arg(instance->name())) << endl;
847 KDE_struct_stat stat_buf;
848 TQCString tpath = TQFile::encodeName(path);
849 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
852 m_mapEntries.insert( _path, newEntry );
854 Entry* e = &(m_mapEntries[_path]);
857 e->isDir = S_ISDIR(stat_buf.st_mode);
859 if (e->isDir && !isDir)
860 kdWarning() <<
"KDirWatch: " << path <<
" is a directory. Use addDir!" << endl;
861 else if (!e->isDir && isDir)
862 kdWarning() <<
"KDirWatch: " << path <<
" is a file. Use addFile!" << endl;
864 e->m_ctime = stat_buf.st_ctime;
865 e->m_mtime = stat_buf.st_mtime;
866 e->m_status = Normal;
867 e->m_nlink = stat_buf.st_nlink;
871 e->m_ctime = invalid_ctime;
872 e->m_mtime = invalid_mtime;
873 e->m_status = NonExistent;
879 e->m_entries.append(sub_entry);
881 e->addClient(instance);
883 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
884 << (e->m_status == NonExistent ?
" NotExisting" :
"")
885 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
886 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
891 e->m_mode = UnknownMode;
894 if ( isNoisyFile( tpath ) ) {
899 if (useFAM(e))
return;
903 if (useINotify(e))
return;
907 if (useDNotify(e))
return;
914 void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
915 const KURL& _path, Entry* sub_entry )
917 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry << endl;
918 Entry* e = entry(_path);
920 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" << endl;
925 e->m_entries.removeRef(sub_entry);
927 e->removeClient(instance);
929 if (e->m_clients.count() || e->m_entries.count()) {
930 kdDebug(7001) <<
"removeEntry: unwatched " << e->path.path() <<
" " << _path << endl;
936 if (removeList.findRef(e)==-1)
937 removeList.append(e);
943 if (e->m_mode == FAMMode) {
944 if ( e->m_status == Normal) {
945 FAMCancelMonitor(&fc, &(e->fr) );
946 kdDebug(7001) <<
"Cancelled FAM (Req "
947 << FAMREQUEST_GETREQNUM(&(e->fr))
948 <<
") for " << e->path.path() << endl;
952 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
954 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
960 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) << endl;
961 if (e->m_mode == INotifyMode) {
962 if ( e->m_status == Normal ) {
963 (void) inotify_rm_watch( m_inotify_fd, e->wd );
964 kdDebug(7001) <<
"Cancelled INotify (fd " <<
965 m_inotify_fd <<
", " << e->wd <<
966 ") for " << e->path.path() << endl;
970 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
972 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
978 if (e->m_mode == DNotifyMode) {
980 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
984 if ( e->m_status == Normal) {
987 fd_Entry.remove(e->dn_fd);
989 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
990 <<
") for " << e->path.path() << endl;
996 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
1002 if (e->m_mode == StatMode) {
1004 if ( statEntries == 0 ) {
1006 kdDebug(7001) <<
" Stopped Polling Timer" << endl;
1010 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path.path()
1011 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
1012 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
1014 m_mapEntries.remove( e->path );
1021 void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
1023 TQPtrList<Entry> list;
1024 int minfreq = 3600000;
1027 EntryMap::Iterator it = m_mapEntries.begin();
1028 for( ; it != m_mapEntries.end(); ++it ) {
1029 Client* c = (*it).m_clients.first();
1030 for(;c;c=(*it).m_clients.next())
1031 if (c->instance == instance)
break;
1034 list.append(&(*it));
1036 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1037 minfreq = (*it).freq;
1040 for(Entry* e=list.first();e;e=list.next())
1041 removeEntry(instance, e->path, 0);
1043 if (minfreq > freq) {
1046 if (timer->isActive()) timer->changeInterval(freq);
1047 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" << endl;
1052 bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
1054 int stillWatching = 0;
1055 Client* c = e->m_clients.first();
1056 for(;c;c=e->m_clients.next()) {
1057 if (!instance || instance == c->instance)
1058 c->watchingStopped =
true;
1059 else if (!c->watchingStopped)
1060 stillWatching += c->count;
1063 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path.path()
1064 <<
" (now " << stillWatching <<
" watchers)" << endl;
1066 if (stillWatching == 0) {
1068 e->m_ctime = invalid_ctime;
1069 e->m_mtime = invalid_mtime;
1070 e->m_status = NonExistent;
1077 bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
1080 int wasWatching = 0, newWatching = 0;
1081 Client* c = e->m_clients.first();
1082 for(;c;c=e->m_clients.next()) {
1083 if (!c->watchingStopped)
1084 wasWatching += c->count;
1085 else if (!instance || instance == c->instance) {
1086 c->watchingStopped =
false;
1087 newWatching += c->count;
1090 if (newWatching == 0)
1093 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path.path()
1094 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1099 if (wasWatching == 0) {
1101 KDE_struct_stat stat_buf;
1102 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1104 e->m_ctime = stat_buf.st_ctime;
1105 e->m_mtime = stat_buf.st_mtime;
1106 e->m_status = Normal;
1107 e->m_nlink = stat_buf.st_nlink;
1110 e->m_ctime = invalid_ctime;
1111 e->m_mtime = invalid_mtime;
1112 e->m_status = NonExistent;
1125 void KDirWatchPrivate::stopScan(
KDirWatch* instance)
1127 EntryMap::Iterator it = m_mapEntries.begin();
1128 for( ; it != m_mapEntries.end(); ++it )
1129 stopEntryScan(instance, &(*it));
1133 void KDirWatchPrivate::startScan(
KDirWatch* instance,
1134 bool notify,
bool skippedToo )
1137 resetList(instance,skippedToo);
1139 EntryMap::Iterator it = m_mapEntries.begin();
1140 for( ; it != m_mapEntries.end(); ++it )
1141 restartEntryScan(instance, &(*it), notify);
1148 void KDirWatchPrivate::resetList(
KDirWatch* ,
1151 EntryMap::Iterator it = m_mapEntries.begin();
1152 for( ; it != m_mapEntries.end(); ++it ) {
1154 Client* c = (*it).m_clients.first();
1155 for(;c;c=(*it).m_clients.next())
1156 if (!c->watchingStopped || skippedToo)
1157 c->pending = NoChange;
1163 int KDirWatchPrivate::scanEntry(Entry* e)
1166 if (e->m_mode == FAMMode) {
1168 if(!e->dirty)
return NoChange;
1171 if (e->isDir)
return Changed;
1175 if (e->m_mode == UnknownMode)
return NoChange;
1177 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1178 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1180 if(!e->dirty)
return NoChange;
1181 kdDebug(7001) <<
"scanning " << e->path.path() <<
" " << e->m_status <<
" " << e->m_ctime <<
" " << e->m_mtime << endl;
1186 if (e->m_mode == StatMode) {
1191 e->msecLeft -= freq;
1192 if (e->msecLeft>0)
return NoChange;
1193 e->msecLeft += e->freq;
1196 KDE_struct_stat stat_buf;
1197 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1200 if (e->m_status == NonExistent) {
1203 e->m_ctime = stat_buf.st_ctime;
1204 e->m_mtime = stat_buf.st_mtime;
1205 e->m_status = Normal;
1206 e->m_nlink = stat_buf.st_nlink;
1210 if ( (e->m_ctime != invalid_ctime) &&
1211 ((stat_buf.st_ctime != e->m_ctime) ||
1212 (stat_buf.st_mtime != e->m_mtime) ||
1213 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1214 e->m_ctime = stat_buf.st_ctime;
1215 e->m_mtime = stat_buf.st_mtime;
1216 e->m_nlink = stat_buf.st_nlink;
1225 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1227 e->m_status = NonExistent;
1231 e->m_ctime = invalid_ctime;
1232 e->m_mtime = invalid_mtime;
1234 e->m_status = NonExistent;
1243 void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const KURL &fileName)
1245 TQString path = e->path.path();
1246 if (!fileName.isEmpty()) {
1247 if (!TQDir::isRelativePath(fileName.path()))
1248 path = fileName.path();
1251 path +=
"/" + fileName.path();
1252 #elif defined(TQ_WS_WIN)
1254 path += TQDir::currentDirPath().left(2) +
"/" + fileName.path();
1258 TQPtrListIterator<Client> cit( e->m_clients );
1259 for ( ; cit.current(); ++cit )
1261 Client* c = cit.current();
1263 if (c->instance==0 || c->count==0)
continue;
1265 if (c->watchingStopped) {
1267 if (event == Changed)
1268 c->pending |= event;
1269 else if (event == Created || event == Deleted)
1274 if (event == NoChange || event == Changed)
1275 event |= c->pending;
1276 c->pending = NoChange;
1277 if (event == NoChange)
continue;
1279 if (event & Deleted) {
1280 c->instance->setDeleted(path);
1285 if (event & Created) {
1286 c->instance->setCreated(path);
1290 if (event & Changed) {
1291 c->instance->setDirty(path);
1292 c->instance->setDirty(e->path);
1298 void KDirWatchPrivate::slotRemoveDelayed()
1301 delayRemove =
false;
1302 for(e=removeList.first();e;e=removeList.next())
1303 removeEntry(0, e->path, 0);
1310 void KDirWatchPrivate::slotRescan()
1312 EntryMap::Iterator it;
1317 bool timerRunning = timer->isActive();
1318 if ( timerRunning ) {
1326 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1327 TQPtrList<Entry> dList, cList;
1333 it = m_mapEntries.begin();
1334 for( ; it != m_mapEntries.end(); ++it ) {
1342 it = m_mapEntries.begin();
1343 for( ; it != m_mapEntries.end(); ++it ) {
1344 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty ) {
1345 (*it).propagate_dirty();
1350 it = m_mapEntries.begin();
1351 for( ; it != m_mapEntries.end(); ++it ) {
1353 if (!(*it).isValid())
continue;
1355 int ev = scanEntry( &(*it) );
1359 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1360 cList.append( &(*it) );
1361 if (! useINotify( &(*it) )) {
1368 if ((*it).m_mode == DNotifyMode) {
1369 if ((*it).isDir && (ev == Deleted)) {
1370 dList.append( &(*it) );
1374 ::close((*it).dn_fd);
1375 fd_Entry.remove((*it).dn_fd);
1380 else if ((*it).isDir && (ev == Created)) {
1382 if ( (*it).dn_fd == 0) {
1383 cList.append( &(*it) );
1384 if (! useDNotify( &(*it) )) {
1393 if ( ev != NoChange ) {
1395 EntryMap::Iterator it2;
1396 it2 = m_mapEntries.begin();
1397 for( ; it2 != m_mapEntries.end(); ++it2 ) {
1398 if ((*it).path.url() == (*it2).path.url()) {
1399 emitEvent( &(*it2), ev);
1406 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1409 for(e=dList.first();e;e=dList.next()) {
1410 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1414 for(e=cList.first();e;e=cList.next()) {
1415 removeEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e);
1419 if ( timerRunning ) {
1423 TQTimer::singleShot(0,
this, TQ_SLOT(slotRemoveDelayed()));
1426 bool KDirWatchPrivate::isNoisyFile(
const char * filename )
1429 if ( *filename ==
'.') {
1430 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1431 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1434 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1441 void KDirWatchPrivate::famEventReceived()
1447 while(use_fam && FAMPending(&fc)) {
1448 if (FAMNextEvent(&fc, &fe) == -1) {
1449 kdWarning(7001) <<
"FAM connection problem, switching to polling."
1455 EntryMap::Iterator it;
1456 it = m_mapEntries.begin();
1457 for( ; it != m_mapEntries.end(); ++it )
1458 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1460 if (useINotify( &(*it) ))
continue;
1463 if (useDNotify( &(*it) ))
continue;
1472 TQTimer::singleShot(0,
this, TQ_SLOT(slotRemoveDelayed()));
1475 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1478 if ((fe->code == FAMExists) ||
1479 (fe->code == FAMEndExist) ||
1480 (fe->code == FAMAcknowledge))
return;
1482 if ( isNoisyFile( fe->filename ) )
1486 EntryMap::Iterator it = m_mapEntries.begin();
1487 for( ; it != m_mapEntries.end(); ++it )
1488 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1489 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1497 kdDebug(7001) <<
"Processing FAM event ("
1498 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1499 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1500 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1501 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1502 (fe->code == FAMCreated) ?
"FAMCreated" :
1503 (fe->code == FAMMoved) ?
"FAMMoved" :
1504 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1505 (fe->code == FAMExists) ?
"FAMExists" :
1506 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1507 <<
", " << fe->filename
1508 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1518 if (e->m_status == NonExistent) {
1519 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path.path() << endl;
1525 if (!rescan_timer.isActive())
1526 rescan_timer.start(m_PollInterval,
true);
1534 if (!TQDir::isRelativePath(fe->filename))
1538 e->m_status = NonExistent;
1539 FAMCancelMonitor(&fc, &(e->fr) );
1540 kdDebug(7001) <<
"Cancelled FAMReq "
1541 << FAMREQUEST_GETREQNUM(&(e->fr))
1542 <<
" for " << e->path.path() << endl;
1544 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1550 Entry *sub_entry = e->m_entries.first();
1551 for(;sub_entry; sub_entry = e->m_entries.next())
1552 if (sub_entry->path.path() == e->path.path() +
"/" + fe->filename)
break;
1553 if (sub_entry && sub_entry->isDir) {
1554 KURL path = e->path;
1555 removeEntry(0,e->path,sub_entry);
1556 sub_entry->m_status = Normal;
1557 if (!useFAM(sub_entry))
1560 if (!useINotify(sub_entry ))
1575 void KDirWatchPrivate::famEventReceived() {}
1579 void KDirWatchPrivate::statistics()
1581 EntryMap::Iterator it;
1583 kdDebug(7001) <<
"Entries watched:" << endl;
1584 if (m_mapEntries.count()==0) {
1585 kdDebug(7001) <<
" None." << endl;
1588 it = m_mapEntries.begin();
1589 for( ; it != m_mapEntries.end(); ++it ) {
1591 kdDebug(7001) <<
" " << e->path.path() <<
" ("
1592 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1593 << (e->isDir ?
"Dir":
"File") <<
", using "
1594 << ((e->m_mode == FAMMode) ?
"FAM" :
1595 (e->m_mode == INotifyMode) ?
"INotify" :
1596 (e->m_mode == DNotifyMode) ?
"DNotify" :
1597 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1600 Client* c = e->m_clients.first();
1601 for(;c; c = e->m_clients.next()) {
1603 if (c->watchingStopped) {
1604 if (c->pending & Deleted) pending +=
"deleted ";
1605 if (c->pending & Created) pending +=
"created ";
1606 if (c->pending & Changed) pending +=
"changed ";
1607 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1608 pending =
", stopped" + pending;
1610 kdDebug(7001) <<
" by " << c->instance->name()
1611 <<
" (" << c->count <<
" times)"
1614 if (e->m_entries.count()>0) {
1615 kdDebug(7001) <<
" dependent entries:" << endl;
1616 Entry* d = e->m_entries.first();
1617 for(;d; d = e->m_entries.next()) {
1618 kdDebug(7001) <<
" " << d << endl;
1619 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " << endl;
1631 static KStaticDeleter<KDirWatch> sd_dw;
1637 sd_dw.setObject( s_pSelf,
new KDirWatch );
1645 return s_pSelf != 0;
1649 : TQObject(parent,name)
1652 static int nameCounter = 0;
1655 setName(TQString(TQString(
"KDirWatch-%1").arg(nameCounter)).ascii());
1659 dwp_self =
new KDirWatchPrivate;
1668 d->removeEntries(
this);
1681 if (watchFiles || recursive) {
1682 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1684 if (d) d->addEntry(
this, _path, 0,
true);
1690 if (watchFiles || recursive) {
1691 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1693 if (d) d->addEntry(
this, _url, 0,
true);
1698 if (d) d->addEntry(
this, _path, 0,
false);
1703 KDirWatchPrivate::Entry* e = d->entry(_path);
1706 return TQDateTime();
1709 result.setTime_t(e->m_ctime);
1715 if (d) d->removeEntry(
this, _path, 0);
1720 if (d) d->removeEntry(
this, _url, 0);
1725 if (d) d->removeEntry(
this, _path, 0);
1731 KDirWatchPrivate::Entry *e = d->entry(_path);
1732 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1740 KDirWatchPrivate::Entry *e = d->entry(_path);
1743 return d->restartEntryScan(
this, e,
false);
1750 if (d) d->stopScan(
this);
1757 if (d) d->startScan(
this, notify, skippedToo);
1763 KDirWatchPrivate::Entry* e = d->entry(_path);
1767 KDirWatchPrivate::Client* c = e->m_clients.first();
1768 for(;c;c=e->m_clients.next())
1769 if (c->instance ==
this)
return true;
1777 kdDebug(7001) <<
"KDirWatch not used" << endl;
1780 dwp_self->statistics();
1786 kdDebug(7001) << name() <<
" emitting created " << _file << endl;
1792 kdDebug(7001) << name() <<
" emitting dirty " << _file << endl;
1793 emit
dirty( _file );
1798 kdDebug(7001) << name() <<
" emitting dirty " << _url << endl;
1804 kdDebug(7001) << name() <<
" emitting deleted " << _file << endl;
1812 return KDirWatch::FAM;
1815 if (d->supports_inotify)
1816 return KDirWatch::INotify;
1819 if (d->supports_dnotify)
1820 return KDirWatch::DNotify;
1822 return KDirWatch::Stat;
1826 #include "kdirwatch.moc"
1827 #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.