51 #include <tqintdict.h> 52 #include <tqptrlist.h> 53 #include <tqsocketnotifier.h> 54 #include <tqstringlist.h> 57 #include <tdeapplication.h> 59 #include <tdeconfig.h> 60 #include <tdeglobal.h> 61 #include <kstaticdeleter.h> 66 #include <sys/ioctl.h> 71 #include <sys/syscall.h> 73 #include <sys/filio.h> 75 #include <linux/types.h> 78 #define _S390_BITOPS_H 79 #include <sys/inotify.h> 81 #ifndef __NR_inotify_init 83 #define __NR_inotify_init 291 84 #define __NR_inotify_add_watch 292 85 #define __NR_inotify_rm_watch 293 88 #define __NR_inotify_init 275 89 #define __NR_inotify_add_watch 276 90 #define __NR_inotify_rm_watch 277 92 #if defined(__x86_64__) 93 #define __NR_inotify_init 253 94 #define __NR_inotify_add_watch 254 95 #define __NR_inotify_rm_watch 255 100 #define IN_ONLYDIR 0x01000000 103 #ifndef IN_DONT_FOLLOW 104 #define IN_DONT_FOLLOW 0x02000000 108 #define IN_MOVE_SELF 0x00000800 113 #include <sys/utsname.h> 115 #include "kdirwatch.h" 116 #include "kdirwatch_p.h" 119 #define NO_NOTIFY (time_t) 0 121 static KDirWatchPrivate* dwp_self = 0;
125 static int dnotify_signal = 0;
135 void KDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
137 if (!dwp_self)
return;
141 int saved_errno = errno;
143 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
148 if(e && e->dn_fd == si->si_fd)
152 write(dwp_self->mPipe[1], &c, 1);
156 static struct sigaction old_sigio_act;
161 void KDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
167 int saved_errno = errno;
169 dwp_self->rescan_all =
true;
171 write(dwp_self->mPipe[1], &c, 1);
177 if (old_sigio_act.sa_flags & SA_SIGINFO)
179 if (old_sigio_act.sa_sigaction)
180 (*old_sigio_act.sa_sigaction)(sig, si, p);
184 if ((old_sigio_act.sa_handler != SIG_DFL) &&
185 (old_sigio_act.sa_handler != SIG_IGN))
186 (*old_sigio_act.sa_handler)(sig);
224 KDirWatchPrivate::KDirWatchPrivate()
225 : rescan_timer(0,
"KDirWatchPrivate::rescan_timer")
227 timer =
new TQTimer(
this,
"KDirWatchPrivate::timer");
228 connect (timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
234 TDEConfigGroup config(TDEGlobal::config(), TQCString(
"DirWatch"));
235 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
236 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
238 TQString available(
"Stat");
242 connect(&rescan_timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
246 if (FAMOpen(&fc) ==0) {
247 available +=
", FAM";
249 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
250 TQSocketNotifier::Read,
this);
251 connect( sn, TQT_SIGNAL(activated(
int)),
252 this, TQT_SLOT(famEventReceived()) );
255 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" << endl;
261 supports_inotify =
true;
263 m_inotify_fd = inotify_init();
265 if ( m_inotify_fd <= 0 ) {
266 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" << endl;
267 supports_inotify =
false;
272 int major, minor, patch;
274 supports_inotify =
false;
275 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
276 supports_inotify =
false;
277 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
278 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" << endl;
279 supports_inotify =
false;
283 if ( supports_inotify ) {
284 available +=
", Inotify";
285 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
287 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
288 connect( mSn, TQT_SIGNAL(activated(
int )),
this, TQT_SLOT( slotActivated() ) );
296 supports_dnotify = !supports_inotify;
299 supports_dnotify =
true;
303 int major, minor, patch;
305 supports_dnotify =
false;
306 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
307 supports_dnotify =
false;
308 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
309 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" << endl;
310 supports_dnotify =
false;
313 if( supports_dnotify ) {
314 available +=
", DNotify";
317 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
318 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
319 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
320 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
321 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
322 connect(mSn, TQT_SIGNAL(activated(
int)),
this, TQT_SLOT(slotActivated()));
324 if ( dnotify_signal == 0 )
326 dnotify_signal = SIGRTMIN + 8;
328 struct sigaction act;
329 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
330 sigemptyset(&act.sa_mask);
331 act.sa_flags = SA_SIGINFO;
333 act.sa_flags |= SA_RESTART;
335 sigaction(dnotify_signal, &act, NULL);
337 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
338 sigaction(SIGIO, &act, &old_sigio_act);
348 kdDebug(7001) <<
"Available methods: " << available << endl;
352 KDirWatchPrivate::~KDirWatchPrivate()
362 kdDebug(7001) <<
"KDirWatch deleted (FAM closed)" << endl;
366 if ( supports_inotify )
367 ::close( m_inotify_fd );
377 void KDirWatchPrivate::slotActivated()
380 if ( supports_dnotify )
382 char dummy_buf[4096];
383 read(mPipe[0], &dummy_buf, 4096);
385 if (!rescan_timer.isActive())
386 rescan_timer.start(m_PollInterval,
true );
393 if ( !supports_inotify )
399 assert( m_inotify_fd > -1 );
400 ioctl( m_inotify_fd, FIONREAD, &pending );
402 while ( pending > 0 ) {
404 if ( pending > (
int)
sizeof( buf ) )
405 pending =
sizeof( buf );
407 pending = read( m_inotify_fd, buf, pending);
409 while ( pending > 0 ) {
410 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
411 pending -=
sizeof(
struct inotify_event ) + event->len;
412 offset +=
sizeof(
struct inotify_event ) + event->len;
416 path = TQFile::decodeName( TQCString( event->name, event->len ) );
418 if ( path.length() && isNoisyFile( path.latin1() ) )
421 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path << endl;
426 for ( EntryMap::Iterator it = m_mapEntries.begin();
427 it != m_mapEntries.end(); ++it ) {
429 if ( e->wd == event->wd ) {
432 if ( 1 || e->isDir) {
433 if( event->mask & IN_DELETE_SELF) {
434 kdDebug(7001) <<
"-->got deleteself signal for " << e->path << endl;
435 e->m_status = NonExistent;
437 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
439 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
441 if ( event->mask & IN_IGNORED ) {
444 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
445 Entry *sub_entry = e->m_entries.first();
446 for(;sub_entry; sub_entry = e->m_entries.next())
447 if (sub_entry->path == e->path.path() +
"/" + path)
break;
450 removeEntry(0,e->path.path(), sub_entry);
451 KDE_struct_stat stat_buf;
452 TQCString tpath = TQFile::encodeName(path);
453 KDE_stat(tpath, &stat_buf);
460 if(!useINotify(sub_entry))
462 sub_entry->dirty =
true;
467 if (!rescan_timer.isActive())
468 rescan_timer.start(m_PollInterval,
true );
483 void KDirWatchPrivate::Entry::propagate_dirty()
485 for (TQPtrListIterator<Entry> sub_entry (m_entries);
486 sub_entry.current(); ++sub_entry)
488 if (!sub_entry.current()->dirty)
490 sub_entry.current()->dirty =
true;
491 sub_entry.current()->propagate_dirty();
500 void KDirWatchPrivate::Entry::addClient(
KDirWatch* instance)
502 Client* client = m_clients.first();
503 for(;client; client = m_clients.next())
504 if (client->instance == instance)
break;
512 client->instance = instance;
514 client->watchingStopped = instance->
isStopped();
515 client->pending = NoChange;
517 m_clients.append(client);
520 void KDirWatchPrivate::Entry::removeClient(
KDirWatch* instance)
522 Client* client = m_clients.first();
523 for(;client; client = m_clients.next())
524 if (client->instance == instance)
break;
528 if (client->count == 0) {
529 m_clients.removeRef(client);
536 int KDirWatchPrivate::Entry::clients()
539 Client* client = m_clients.first();
540 for(;client; client = m_clients.next())
541 clients += client->count;
547 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(
const KURL& _path)
550 if (TQDir::isRelativePath(_path.path())) {
554 TQString path = _path.path();
556 if ( path.length() > 1 && path.right(1) ==
"/" )
557 path.truncate( path.length() - 1 );
559 EntryMap::Iterator it = m_mapEntries.find( _path );
560 if ( it == m_mapEntries.end() )
567 void KDirWatchPrivate::useFreq(Entry* e,
int newFreq)
572 if (e->freq < freq) {
574 if (timer->isActive()) timer->changeInterval(freq);
575 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" << endl;
582 bool KDirWatchPrivate::useFAM(Entry* e)
584 if (!use_fam)
return false;
594 if (e->m_status == NonExistent) {
596 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
599 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path.path()),
602 e->m_mode = UnknownMode;
606 kdDebug(7001) <<
" Setup FAM (Req " 607 << FAMREQUEST_GETREQNUM(&(e->fr))
608 <<
") for " << e->path.path() << endl;
612 if (e->m_status == NonExistent) {
614 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
617 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path.path()),
620 e->m_mode = UnknownMode;
625 kdDebug(7001) <<
" Setup FAM (Req " 626 << FAMREQUEST_GETREQNUM(&(e->fr))
627 <<
") for " << e->path.path() << endl;
642 bool KDirWatchPrivate::useDNotify(Entry* e)
646 if (!supports_dnotify)
return false;
648 e->m_mode = DNotifyMode;
651 if (e->m_status == Normal) {
652 int fd = KDE_open(TQFile::encodeName(e->path.path()).data(), O_RDONLY);
665 int fd2 = fcntl(fd, F_DUPFD, 128);
672 e->m_mode = UnknownMode;
676 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
678 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
679 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
681 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
682 fcntl(fd, F_NOTIFY, mask) < 0) {
684 kdDebug(7001) <<
"Not using Linux Directory Notifications." 686 supports_dnotify =
false;
688 e->m_mode = UnknownMode;
692 fd_Entry.replace(fd, e);
695 kdDebug(7001) <<
" Setup DNotify (fd " << fd
696 <<
") for " << e->path.path() << endl;
699 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
705 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
714 bool KDirWatchPrivate::useINotify( Entry* e )
718 if (!supports_inotify)
return false;
720 e->m_mode = INotifyMode;
722 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
724 mask |= IN_MODIFY|IN_ATTRIB;
729 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
730 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
733 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
734 TQFile::encodeName( e->path.path() ), mask) ) > 0 )
737 if ( e->m_status == NonExistent ) {
739 addEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e,
true);
741 addEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e,
true);
749 bool KDirWatchPrivate::useStat(Entry* e)
751 if ( e->path.path().startsWith(
"/media/") || e->path.path().startsWith(
"/run/") || (e->path.path() ==
"/media")
753 useFreq(e, m_nfsPollInterval);
755 useFreq(e, m_PollInterval);
757 if (e->m_mode != StatMode) {
758 e->m_mode = StatMode;
761 if ( statEntries == 1 ) {
764 kdDebug(7001) <<
" Started Polling Timer, freq " << freq << endl;
768 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
769 <<
") for " << e->path.path() << endl;
780 void KDirWatchPrivate::addEntry(
KDirWatch* instance,
const KURL& _path,
781 Entry* sub_entry,
bool isDir)
783 TQString path = _path.path();
784 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
787 if ( path.length() > 1 && path.right(1) ==
"/" ) {
788 path.truncate( path.length() - 1 );
791 EntryMap::Iterator it = m_mapEntries.find( _path );
792 if ( it != m_mapEntries.end() )
795 (*it).m_entries.append(sub_entry);
796 kdDebug(7001) <<
"Added already watched Entry " << path
797 <<
" (for " << sub_entry->path <<
")" << endl;
802 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
803 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
805 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
806 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
807 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
809 e->m_mode = UnknownMode;
810 fd_Entry.remove(e->dn_fd);
821 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
822 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
824 mask |= IN_MODIFY|IN_ATTRIB;
828 inotify_rm_watch (m_inotify_fd, e->wd);
829 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path.path() ), mask);
836 (*it).addClient(instance);
837 kdDebug(7001) <<
"Added already watched Entry " << path
838 <<
" (now " << (*it).clients() <<
" clients)" 839 << TQString(TQString(
" [%1]").arg(instance->name())) << endl;
846 KDE_struct_stat stat_buf;
847 TQCString tpath = TQFile::encodeName(path);
848 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
851 m_mapEntries.insert( _path, newEntry );
853 Entry* e = &(m_mapEntries[_path]);
856 e->isDir = S_ISDIR(stat_buf.st_mode);
858 if (e->isDir && !isDir)
859 kdWarning() <<
"KDirWatch: " << path <<
" is a directory. Use addDir!" << endl;
860 else if (!e->isDir && isDir)
861 kdWarning() <<
"KDirWatch: " << path <<
" is a file. Use addFile!" << endl;
863 e->m_ctime = stat_buf.st_ctime;
864 e->m_mtime = stat_buf.st_mtime;
865 e->m_status = Normal;
866 e->m_nlink = stat_buf.st_nlink;
870 e->m_ctime = invalid_ctime;
871 e->m_mtime = invalid_mtime;
872 e->m_status = NonExistent;
878 e->m_entries.append(sub_entry);
880 e->addClient(instance);
882 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
883 << (e->m_status == NonExistent ?
" NotExisting" :
"")
884 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
885 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
890 e->m_mode = UnknownMode;
893 if ( isNoisyFile( tpath ) ) {
898 if (useFAM(e))
return;
902 if (useINotify(e))
return;
906 if (useDNotify(e))
return;
913 void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
914 const KURL& _path, Entry* sub_entry )
916 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry << endl;
917 Entry* e = entry(_path);
919 kdDebug(7001) <<
"KDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" << endl;
924 e->m_entries.removeRef(sub_entry);
926 e->removeClient(instance);
928 if (e->m_clients.count() || e->m_entries.count()) {
929 kdDebug(7001) <<
"removeEntry: unwatched " << e->path.path() <<
" " << _path << endl;
935 if (removeList.findRef(e)==-1)
936 removeList.append(e);
942 if (e->m_mode == FAMMode) {
943 if ( e->m_status == Normal) {
944 FAMCancelMonitor(&fc, &(e->fr) );
945 kdDebug(7001) <<
"Cancelled FAM (Req " 946 << FAMREQUEST_GETREQNUM(&(e->fr))
947 <<
") for " << e->path.path() << endl;
951 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
953 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
959 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) << endl;
960 if (e->m_mode == INotifyMode) {
961 if ( e->m_status == Normal ) {
962 (void) inotify_rm_watch( m_inotify_fd, e->wd );
963 kdDebug(7001) <<
"Cancelled INotify (fd " <<
964 m_inotify_fd <<
", " << e->wd <<
965 ") for " << e->path.path() << endl;
969 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
971 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
977 if (e->m_mode == DNotifyMode) {
979 removeEntry(0, TQFileInfo(e->path.path()).dirPath(
true), e);
983 if ( e->m_status == Normal) {
986 fd_Entry.remove(e->dn_fd);
988 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
989 <<
") for " << e->path.path() << endl;
995 removeEntry(0, TQDir::cleanDirPath(e->path.path()+
"/.."), e);
1001 if (e->m_mode == StatMode) {
1003 if ( statEntries == 0 ) {
1005 kdDebug(7001) <<
" Stopped Polling Timer" << endl;
1009 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path.path()
1010 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path.path())) : TQString(
""))
1011 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
1013 m_mapEntries.remove( e->path );
1020 void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
1022 TQPtrList<Entry> list;
1023 int minfreq = 3600000;
1026 EntryMap::Iterator it = m_mapEntries.begin();
1027 for( ; it != m_mapEntries.end(); ++it ) {
1028 Client* c = (*it).m_clients.first();
1029 for(;c;c=(*it).m_clients.next())
1030 if (c->instance == instance)
break;
1033 list.append(&(*it));
1035 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1036 minfreq = (*it).freq;
1039 for(Entry* e=list.first();e;e=list.next())
1040 removeEntry(instance, e->path, 0);
1042 if (minfreq > freq) {
1045 if (timer->isActive()) timer->changeInterval(freq);
1046 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" << endl;
1051 bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
1053 int stillWatching = 0;
1054 Client* c = e->m_clients.first();
1055 for(;c;c=e->m_clients.next()) {
1056 if (!instance || instance == c->instance)
1057 c->watchingStopped =
true;
1058 else if (!c->watchingStopped)
1059 stillWatching += c->count;
1062 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path.path()
1063 <<
" (now " << stillWatching <<
" watchers)" << endl;
1065 if (stillWatching == 0) {
1067 e->m_ctime = invalid_ctime;
1068 e->m_mtime = invalid_mtime;
1069 e->m_status = NonExistent;
1076 bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
1079 int wasWatching = 0, newWatching = 0;
1080 Client* c = e->m_clients.first();
1081 for(;c;c=e->m_clients.next()) {
1082 if (!c->watchingStopped)
1083 wasWatching += c->count;
1084 else if (!instance || instance == c->instance) {
1085 c->watchingStopped =
false;
1086 newWatching += c->count;
1089 if (newWatching == 0)
1092 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path.path()
1093 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1098 if (wasWatching == 0) {
1100 KDE_struct_stat stat_buf;
1101 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1103 e->m_ctime = stat_buf.st_ctime;
1104 e->m_mtime = stat_buf.st_mtime;
1105 e->m_status = Normal;
1106 e->m_nlink = stat_buf.st_nlink;
1109 e->m_ctime = invalid_ctime;
1110 e->m_mtime = invalid_mtime;
1111 e->m_status = NonExistent;
1124 void KDirWatchPrivate::stopScan(
KDirWatch* instance)
1126 EntryMap::Iterator it = m_mapEntries.begin();
1127 for( ; it != m_mapEntries.end(); ++it )
1128 stopEntryScan(instance, &(*it));
1132 void KDirWatchPrivate::startScan(
KDirWatch* instance,
1133 bool notify,
bool skippedToo )
1136 resetList(instance,skippedToo);
1138 EntryMap::Iterator it = m_mapEntries.begin();
1139 for( ; it != m_mapEntries.end(); ++it )
1140 restartEntryScan(instance, &(*it), notify);
1147 void KDirWatchPrivate::resetList(
KDirWatch* ,
1150 EntryMap::Iterator it = m_mapEntries.begin();
1151 for( ; it != m_mapEntries.end(); ++it ) {
1153 Client* c = (*it).m_clients.first();
1154 for(;c;c=(*it).m_clients.next())
1155 if (!c->watchingStopped || skippedToo)
1156 c->pending = NoChange;
1162 int KDirWatchPrivate::scanEntry(Entry* e)
1165 if (e->m_mode == FAMMode) {
1167 if(!e->dirty)
return NoChange;
1170 if (e->isDir)
return Changed;
1174 if (e->m_mode == UnknownMode)
return NoChange;
1176 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY ) 1177 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1179 if(!e->dirty)
return NoChange;
1180 kdDebug(7001) <<
"scanning " << e->path.path() <<
" " << e->m_status <<
" " << e->m_ctime <<
" " << e->m_mtime << endl;
1185 if (e->m_mode == StatMode) {
1190 e->msecLeft -= freq;
1191 if (e->msecLeft>0)
return NoChange;
1192 e->msecLeft += e->freq;
1195 KDE_struct_stat stat_buf;
1196 bool exists = (KDE_stat(TQFile::encodeName(e->path.path()), &stat_buf) == 0);
1199 if (e->m_status == NonExistent) {
1202 e->m_ctime = stat_buf.st_ctime;
1203 e->m_mtime = stat_buf.st_mtime;
1204 e->m_status = Normal;
1205 e->m_nlink = stat_buf.st_nlink;
1209 if ( (e->m_ctime != invalid_ctime) &&
1210 ((stat_buf.st_ctime != e->m_ctime) ||
1211 (stat_buf.st_mtime != e->m_mtime) ||
1212 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1213 e->m_ctime = stat_buf.st_ctime;
1214 e->m_mtime = stat_buf.st_mtime;
1215 e->m_nlink = stat_buf.st_nlink;
1224 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1226 e->m_status = NonExistent;
1230 e->m_ctime = invalid_ctime;
1231 e->m_mtime = invalid_mtime;
1233 e->m_status = NonExistent;
1242 void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const KURL &fileName)
1244 TQString path = e->path.path();
1245 if (!fileName.isEmpty()) {
1246 if (!TQDir::isRelativePath(fileName.path()))
1247 path = fileName.path();
1250 path +=
"/" + fileName.path();
1251 #elif defined(Q_WS_WIN) 1253 path += TQDir::currentDirPath().left(2) +
"/" + fileName.path();
1257 TQPtrListIterator<Client> cit( e->m_clients );
1258 for ( ; cit.current(); ++cit )
1260 Client* c = cit.current();
1262 if (c->instance==0 || c->count==0)
continue;
1264 if (c->watchingStopped) {
1266 if (event == Changed)
1267 c->pending |= event;
1268 else if (event == Created || event == Deleted)
1273 if (event == NoChange || event == Changed)
1274 event |= c->pending;
1275 c->pending = NoChange;
1276 if (event == NoChange)
continue;
1278 if (event & Deleted) {
1279 c->instance->setDeleted(path);
1284 if (event & Created) {
1285 c->instance->setCreated(path);
1289 if (event & Changed) {
1290 c->instance->setDirty(path);
1291 c->instance->setDirty(e->path);
1297 void KDirWatchPrivate::slotRemoveDelayed()
1300 delayRemove =
false;
1301 for(e=removeList.first();e;e=removeList.next())
1302 removeEntry(0, e->path, 0);
1309 void KDirWatchPrivate::slotRescan()
1311 EntryMap::Iterator it;
1316 bool timerRunning = timer->isActive();
1317 if ( timerRunning ) {
1325 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) 1326 TQPtrList<Entry> dList, cList;
1332 it = m_mapEntries.begin();
1333 for( ; it != m_mapEntries.end(); ++it ) {
1341 it = m_mapEntries.begin();
1342 for( ; it != m_mapEntries.end(); ++it ) {
1343 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty ) {
1344 (*it).propagate_dirty();
1349 it = m_mapEntries.begin();
1350 for( ; it != m_mapEntries.end(); ++it ) {
1352 if (!(*it).isValid())
continue;
1354 int ev = scanEntry( &(*it) );
1358 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1359 cList.append( &(*it) );
1360 if (! useINotify( &(*it) )) {
1367 if ((*it).m_mode == DNotifyMode) {
1368 if ((*it).isDir && (ev == Deleted)) {
1369 dList.append( &(*it) );
1373 ::close((*it).dn_fd);
1374 fd_Entry.remove((*it).dn_fd);
1379 else if ((*it).isDir && (ev == Created)) {
1381 if ( (*it).dn_fd == 0) {
1382 cList.append( &(*it) );
1383 if (! useDNotify( &(*it) )) {
1392 if ( ev != NoChange ) {
1394 EntryMap::Iterator it2;
1395 it2 = m_mapEntries.begin();
1396 for( ; it2 != m_mapEntries.end(); ++it2 ) {
1397 if ((*it).path.url() == (*it2).path.url()) {
1398 emitEvent( &(*it2), ev);
1405 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) 1408 for(e=dList.first();e;e=dList.next()) {
1409 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1413 for(e=cList.first();e;e=cList.next()) {
1414 removeEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e);
1418 if ( timerRunning ) {
1422 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1425 bool KDirWatchPrivate::isNoisyFile(
const char * filename )
1428 if ( *filename ==
'.') {
1429 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1430 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1433 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1440 void KDirWatchPrivate::famEventReceived()
1446 while(use_fam && FAMPending(&fc)) {
1447 if (FAMNextEvent(&fc, &fe) == -1) {
1448 kdWarning(7001) <<
"FAM connection problem, switching to polling." 1454 EntryMap::Iterator it;
1455 it = m_mapEntries.begin();
1456 for( ; it != m_mapEntries.end(); ++it )
1457 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1459 if (useINotify( &(*it) ))
continue;
1462 if (useDNotify( &(*it) ))
continue;
1471 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1474 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1477 if ((fe->code == FAMExists) ||
1478 (fe->code == FAMEndExist) ||
1479 (fe->code == FAMAcknowledge))
return;
1481 if ( isNoisyFile( fe->filename ) )
1485 EntryMap::Iterator it = m_mapEntries.begin();
1486 for( ; it != m_mapEntries.end(); ++it )
1487 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1488 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1496 kdDebug(7001) <<
"Processing FAM event (" 1497 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1498 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1499 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1500 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1501 (fe->code == FAMCreated) ?
"FAMCreated" :
1502 (fe->code == FAMMoved) ?
"FAMMoved" :
1503 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1504 (fe->code == FAMExists) ?
"FAMExists" :
1505 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1506 <<
", " << fe->filename
1507 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1517 if (e->m_status == NonExistent) {
1518 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path.path() << endl;
1524 if (!rescan_timer.isActive())
1525 rescan_timer.start(m_PollInterval,
true);
1533 if (!TQDir::isRelativePath(fe->filename))
1537 e->m_status = NonExistent;
1538 FAMCancelMonitor(&fc, &(e->fr) );
1539 kdDebug(7001) <<
"Cancelled FAMReq " 1540 << FAMREQUEST_GETREQNUM(&(e->fr))
1541 <<
" for " << e->path.path() << endl;
1543 addEntry(0, TQDir::cleanDirPath( e->path.path()+
"/.."), e,
true);
1549 Entry *sub_entry = e->m_entries.first();
1550 for(;sub_entry; sub_entry = e->m_entries.next())
1551 if (sub_entry->path.path() == e->path.path() +
"/" + fe->filename)
break;
1552 if (sub_entry && sub_entry->isDir) {
1553 KURL path = e->path;
1554 removeEntry(0,e->path,sub_entry);
1555 sub_entry->m_status = Normal;
1556 if (!useFAM(sub_entry))
1558 if (!useINotify(sub_entry ))
1570 void KDirWatchPrivate::famEventReceived() {}
1574 void KDirWatchPrivate::statistics()
1576 EntryMap::Iterator it;
1578 kdDebug(7001) <<
"Entries watched:" << endl;
1579 if (m_mapEntries.count()==0) {
1580 kdDebug(7001) <<
" None." << endl;
1583 it = m_mapEntries.begin();
1584 for( ; it != m_mapEntries.end(); ++it ) {
1586 kdDebug(7001) <<
" " << e->path.path() <<
" (" 1587 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1588 << (e->isDir ?
"Dir":
"File") <<
", using " 1589 << ((e->m_mode == FAMMode) ?
"FAM" :
1590 (e->m_mode == INotifyMode) ?
"INotify" :
1591 (e->m_mode == DNotifyMode) ?
"DNotify" :
1592 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1595 Client* c = e->m_clients.first();
1596 for(;c; c = e->m_clients.next()) {
1598 if (c->watchingStopped) {
1599 if (c->pending & Deleted) pending +=
"deleted ";
1600 if (c->pending & Created) pending +=
"created ";
1601 if (c->pending & Changed) pending +=
"changed ";
1602 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1603 pending =
", stopped" + pending;
1605 kdDebug(7001) <<
" by " << c->instance->name()
1606 <<
" (" << c->count <<
" times)" 1609 if (e->m_entries.count()>0) {
1610 kdDebug(7001) <<
" dependent entries:" << endl;
1611 Entry* d = e->m_entries.first();
1612 for(;d; d = e->m_entries.next()) {
1613 kdDebug(7001) <<
" " << d << endl;
1614 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " << endl;
1626 static KStaticDeleter<KDirWatch> sd_dw;
1632 sd_dw.setObject( s_pSelf,
new KDirWatch );
1640 return s_pSelf != 0;
1644 : TQObject(parent,name)
1647 static int nameCounter = 0;
1650 setName(TQString(TQString(
"KDirWatch-%1").arg(nameCounter)).ascii());
1654 dwp_self =
new KDirWatchPrivate;
1663 d->removeEntries(
this);
1676 if (watchFiles || recursive) {
1677 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1679 if (d) d->addEntry(
this, _path, 0,
true);
1685 if (watchFiles || recursive) {
1686 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in TDE 3.x" << endl;
1688 if (d) d->addEntry(
this, _url, 0,
true);
1693 if (d) d->addEntry(
this, _path, 0,
false);
1698 KDirWatchPrivate::Entry* e = d->entry(_path);
1701 return TQDateTime();
1704 result.setTime_t(e->m_ctime);
1710 if (d) d->removeEntry(
this, _path, 0);
1715 if (d) d->removeEntry(
this, _url, 0);
1720 if (d) d->removeEntry(
this, _path, 0);
1726 KDirWatchPrivate::Entry *e = d->entry(_path);
1727 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1735 KDirWatchPrivate::Entry *e = d->entry(_path);
1738 return d->restartEntryScan(
this, e,
false);
1745 if (d) d->stopScan(
this);
1752 if (d) d->startScan(
this, notify, skippedToo);
1758 KDirWatchPrivate::Entry* e = d->entry(_path);
1762 KDirWatchPrivate::Client* c = e->m_clients.first();
1763 for(;c;c=e->m_clients.next())
1764 if (c->instance ==
this)
return true;
1772 kdDebug(7001) <<
"KDirWatch not used" << endl;
1775 dwp_self->statistics();
1781 kdDebug(7001) << name() <<
" emitting created " << _file << endl;
1787 kdDebug(7001) << name() <<
" emitting dirty " << _file << endl;
1788 emit
dirty( _file );
1793 kdDebug(7001) << name() <<
" emitting dirty " << _url << endl;
1799 kdDebug(7001) << name() <<
" emitting deleted " << _file << endl;
1807 return KDirWatch::FAM;
1810 if (d->supports_inotify)
1811 return KDirWatch::INotify;
1814 if (d->supports_dnotify)
1815 return KDirWatch::DNotify;
1817 return KDirWatch::Stat;
1821 #include "kdirwatch.moc" 1822 #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.