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, TQT_SIGNAL(timeout()),
this, TQT_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, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
247 if (FAMOpen(&fc) ==0) {
248 available +=
", FAM";
250 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
251 TQSocketNotifier::Read,
this);
252 connect( sn, TQT_SIGNAL(activated(
int)),
253 this, TQT_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, TQT_SIGNAL(activated(
int )),
this, TQT_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, TQT_SIGNAL(activated(
int)),
this, TQT_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(Q_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, TQT_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, TQT_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" 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.