39 #include <tqintdict.h>
40 #include <tqptrlist.h>
41 #include <tqsocketnotifier.h>
42 #include <tqstringlist.h>
45 #include <tdeapplication.h>
47 #include <tdeconfig.h>
48 #include <tdeglobal.h>
49 #include <kstaticdeleter.h>
53 #include <sys/ioctl.h>
58 #include <sys/syscall.h>
59 #include <linux/types.h>
61 #define _S390_BITOPS_H
62 #include <sys/inotify.h>
64 #ifndef __NR_inotify_init
66 #define __NR_inotify_init 291
67 #define __NR_inotify_add_watch 292
68 #define __NR_inotify_rm_watch 293
71 #define __NR_inotify_init 275
72 #define __NR_inotify_add_watch 276
73 #define __NR_inotify_rm_watch 277
75 #if defined(__x86_64__)
76 #define __NR_inotify_init 253
77 #define __NR_inotify_add_watch 254
78 #define __NR_inotify_rm_watch 255
83 #define IN_ONLYDIR 0x01000000
86 #ifndef IN_DONT_FOLLOW
87 #define IN_DONT_FOLLOW 0x02000000
91 #define IN_MOVE_SELF 0x00000800
96 #include <sys/utsname.h>
98 #include "ksimpledirwatch.h"
99 #include "ksimpledirwatch_p.h"
101 #define NO_NOTIFY (time_t) 0
103 static KSimpleDirWatchPrivate* dwp_self = 0;
107 static int dnotify_signal = 0;
117 void KSimpleDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
119 if (!dwp_self)
return;
123 int saved_errno = errno;
125 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
130 if(e && e->dn_fd == si->si_fd)
134 write(dwp_self->mPipe[1], &c, 1);
138 static struct sigaction old_sigio_act;
143 void KSimpleDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
149 int saved_errno = errno;
151 dwp_self->rescan_all =
true;
153 write(dwp_self->mPipe[1], &c, 1);
159 if (old_sigio_act.sa_flags & SA_SIGINFO)
161 if (old_sigio_act.sa_sigaction)
162 (*old_sigio_act.sa_sigaction)(sig, si, p);
166 if ((old_sigio_act.sa_handler != SIG_DFL) &&
167 (old_sigio_act.sa_handler != SIG_IGN))
168 (*old_sigio_act.sa_handler)(sig);
206 KSimpleDirWatchPrivate::KSimpleDirWatchPrivate()
207 : rescan_timer(0,
"KSimpleDirWatchPrivate::rescan_timer")
209 timer =
new TQTimer(
this,
"KSimpleDirWatchPrivate::timer");
210 connect (timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
217 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
218 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
220 TQString available(
"Stat");
224 connect(&rescan_timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
228 if (FAMOpen(&fc) ==0) {
229 available +=
", FAM";
231 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
232 TQSocketNotifier::Read,
this);
233 connect( sn, TQT_SIGNAL(activated(
int)),
234 this, TQT_SLOT(famEventReceived()) );
237 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" <<
endl;
243 supports_inotify =
true;
245 m_inotify_fd = inotify_init();
247 if ( m_inotify_fd <= 0 ) {
248 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" <<
endl;
249 supports_inotify =
false;
254 int major, minor, patch;
256 supports_inotify =
false;
257 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
258 supports_inotify =
false;
259 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
260 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" <<
endl;
261 supports_inotify =
false;
265 if ( supports_inotify ) {
266 available +=
", Inotify";
267 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
269 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
270 connect( mSn, TQT_SIGNAL(activated(
int )),
this, TQT_SLOT( slotActivated() ) );
278 supports_dnotify = !supports_inotify;
281 supports_dnotify =
true;
285 int major, minor, patch;
287 supports_dnotify =
false;
288 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
289 supports_dnotify =
false;
290 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
291 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" <<
endl;
292 supports_dnotify =
false;
295 if( supports_dnotify ) {
296 available +=
", DNotify";
299 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
300 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
301 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
302 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
303 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
304 connect(mSn, TQT_SIGNAL(activated(
int)),
this, TQT_SLOT(slotActivated()));
306 if ( dnotify_signal == 0 )
308 dnotify_signal = SIGRTMIN + 8;
310 struct sigaction act;
311 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_handler;
312 sigemptyset(&act.sa_mask);
313 act.sa_flags = SA_SIGINFO;
315 act.sa_flags |= SA_RESTART;
317 sigaction(dnotify_signal, &act, NULL);
319 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_sigio_handler;
320 sigaction(SIGIO, &act, &old_sigio_act);
330 kdDebug(7001) <<
"Available methods: " << available <<
endl;
334 KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
344 kdDebug(7001) <<
"KSimpleDirWatch deleted (FAM closed)" <<
endl;
348 if ( supports_inotify )
349 ::close( m_inotify_fd );
359 void KSimpleDirWatchPrivate::slotActivated()
362 if ( supports_dnotify )
364 char dummy_buf[4096];
365 read(mPipe[0], &dummy_buf, 4096);
367 if (!rescan_timer.isActive())
368 rescan_timer.start(m_PollInterval,
true );
375 if ( !supports_inotify )
381 assert( m_inotify_fd > -1 );
382 ioctl( m_inotify_fd, FIONREAD, &pending );
384 while ( pending > 0 ) {
386 if ( pending > (
int)
sizeof( buf ) )
387 pending =
sizeof( buf );
389 pending = read( m_inotify_fd, buf, pending);
391 while ( pending > 0 ) {
392 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
393 pending -=
sizeof(
struct inotify_event ) +
event->len;
394 offset +=
sizeof(
struct inotify_event ) +
event->len;
398 path = TQFile::decodeName( TQCString(
event->name,
event->len ) );
400 if ( path.length() && isNoisyFile( path.latin1() ) )
403 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path <<
endl;
408 for ( EntryMap::Iterator it = m_mapEntries.begin();
409 it != m_mapEntries.end(); ++it ) {
411 if ( e->wd ==
event->wd ) {
414 if ( 1 || e->isDir) {
415 if(
event->mask & IN_DELETE_SELF) {
416 kdDebug(7001) <<
"-->got deleteself signal for " << e->path <<
endl;
417 e->m_status = NonExistent;
419 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
421 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
423 if (
event->mask & IN_IGNORED ) {
426 if (
event->mask & (IN_CREATE|IN_MOVED_TO) ) {
427 Entry *sub_entry = e->m_entries.first();
428 for(;sub_entry; sub_entry = e->m_entries.next())
429 if (sub_entry->path == e->path +
"/" + path)
break;
432 removeEntry(0,e->path, sub_entry);
433 KDE_struct_stat stat_buf;
434 TQCString tpath = TQFile::encodeName(path);
435 KDE_stat(tpath, &stat_buf);
442 if(!useINotify(sub_entry))
444 sub_entry->dirty =
true;
449 if (!rescan_timer.isActive())
450 rescan_timer.start(m_PollInterval,
true );
465 void KSimpleDirWatchPrivate::Entry::propagate_dirty()
467 for (TQPtrListIterator<Entry> sub_entry (m_entries);
468 sub_entry.current(); ++sub_entry)
470 if (!sub_entry.current()->dirty)
472 sub_entry.current()->dirty =
true;
473 sub_entry.current()->propagate_dirty();
482 void KSimpleDirWatchPrivate::Entry::addClient(
KSimpleDirWatch* instance)
484 Client* client = m_clients.first();
485 for(;client; client = m_clients.next())
486 if (client->instance == instance)
break;
494 client->instance = instance;
496 client->watchingStopped = instance->
isStopped();
497 client->pending = NoChange;
499 m_clients.append(client);
502 void KSimpleDirWatchPrivate::Entry::removeClient(
KSimpleDirWatch* instance)
504 Client* client = m_clients.first();
505 for(;client; client = m_clients.next())
506 if (client->instance == instance)
break;
510 if (client->count == 0) {
511 m_clients.removeRef(client);
518 int KSimpleDirWatchPrivate::Entry::clients()
521 Client* client = m_clients.first();
522 for(;client; client = m_clients.next())
523 clients += client->count;
529 KSimpleDirWatchPrivate::Entry* KSimpleDirWatchPrivate::entry(
const TQString& _path)
532 if (TQDir::isRelativePath(_path)) {
536 TQString path = _path;
538 if ( path.length() > 1 && path.right(1) ==
"/" )
539 path.truncate( path.length() - 1 );
541 EntryMap::Iterator it = m_mapEntries.find( path );
542 if ( it == m_mapEntries.end() )
549 void KSimpleDirWatchPrivate::useFreq(Entry* e,
int newFreq)
554 if (e->freq < freq) {
556 if (timer->isActive()) timer->changeInterval(freq);
557 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" <<
endl;
564 bool KSimpleDirWatchPrivate::useFAM(Entry* e)
566 if (!use_fam)
return false;
576 if (e->m_status == NonExistent) {
578 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
581 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
584 e->m_mode = UnknownMode;
588 kdDebug(7001) <<
" Setup FAM (Req "
589 << FAMREQUEST_GETREQNUM(&(e->fr))
590 <<
") for " << e->path <<
endl;
594 if (e->m_status == NonExistent) {
596 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
599 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
602 e->m_mode = UnknownMode;
607 kdDebug(7001) <<
" Setup FAM (Req "
608 << FAMREQUEST_GETREQNUM(&(e->fr))
609 <<
") for " << e->path <<
endl;
624 bool KSimpleDirWatchPrivate::useDNotify(Entry* e)
628 if (!supports_dnotify)
return false;
630 e->m_mode = DNotifyMode;
633 if (e->m_status == Normal) {
634 int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
647 int fd2 = fcntl(fd, F_DUPFD, 128);
654 e->m_mode = UnknownMode;
658 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
660 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
661 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
663 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
664 fcntl(fd, F_NOTIFY, mask) < 0) {
666 kdDebug(7001) <<
"Not using Linux Directory Notifications."
668 supports_dnotify =
false;
670 e->m_mode = UnknownMode;
674 fd_Entry.replace(fd, e);
677 kdDebug(7001) <<
" Setup DNotify (fd " << fd
678 <<
") for " << e->path <<
endl;
681 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
687 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
696 bool KSimpleDirWatchPrivate::useINotify( Entry* e )
700 if (!supports_inotify)
return false;
702 e->m_mode = INotifyMode;
704 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
706 mask |= IN_MODIFY|IN_ATTRIB;
711 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
712 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
715 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
716 TQFile::encodeName( e->path ), mask) ) > 0 )
719 if ( e->m_status == NonExistent ) {
721 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
723 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
731 bool KSimpleDirWatchPrivate::useStat(Entry* e)
733 useFreq(e, m_PollInterval);
735 if (e->m_mode != StatMode) {
736 e->m_mode = StatMode;
739 if ( statEntries == 1 ) {
742 kdDebug(7001) <<
" Started Polling Timer, freq " << freq <<
endl;
746 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
747 <<
") for " << e->path <<
endl;
758 void KSimpleDirWatchPrivate::addEntry(
KSimpleDirWatch* instance,
const TQString& _path,
759 Entry* sub_entry,
bool isDir)
761 TQString path = _path;
762 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
765 if ( path.length() > 1 && path.right(1) ==
"/" )
766 path.truncate( path.length() - 1 );
768 EntryMap::Iterator it = m_mapEntries.find( path );
769 if ( it != m_mapEntries.end() )
772 (*it).m_entries.append(sub_entry);
773 kdDebug(7001) <<
"Added already watched Entry " << path
774 <<
" (for " << sub_entry->path <<
")" <<
endl;
779 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
780 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
782 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
783 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
784 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
786 e->m_mode = UnknownMode;
787 fd_Entry.remove(e->dn_fd);
798 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
799 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
801 mask |= IN_MODIFY|IN_ATTRIB;
805 inotify_rm_watch (m_inotify_fd, e->wd);
806 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
813 (*it).addClient(instance);
814 kdDebug(7001) <<
"Added already watched Entry " << path
815 <<
" (now " << (*it).clients() <<
" clients)"
816 << TQString(TQString(
" [%1]").arg(instance->name())) <<
endl;
823 KDE_struct_stat stat_buf;
824 TQCString tpath = TQFile::encodeName(path);
825 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
828 m_mapEntries.insert( path, newEntry );
830 Entry* e = &(m_mapEntries[path]);
833 e->isDir = S_ISDIR(stat_buf.st_mode);
835 if (e->isDir && !isDir)
836 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a directory. Use addDir!" <<
endl;
837 else if (!e->isDir && isDir)
838 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a file. Use addFile!" <<
endl;
840 e->m_ctime = stat_buf.st_ctime;
841 e->m_status = Normal;
842 e->m_nlink = stat_buf.st_nlink;
846 e->m_ctime = invalid_ctime;
847 e->m_status = NonExistent;
853 e->m_entries.append(sub_entry);
855 e->addClient(instance);
857 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
858 << (e->m_status == NonExistent ?
" NotExisting" :
"")
859 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
860 << (instance ? TQString(TQString(
" [%1]").arg(instance->
name())) : TQString(
""))
865 e->m_mode = UnknownMode;
868 if ( isNoisyFile( tpath ) )
872 if (useFAM(e))
return;
876 if (useINotify(e))
return;
880 if (useDNotify(e))
return;
888 const TQString& _path, Entry* sub_entry )
890 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry <<
endl;
891 Entry* e = entry(_path);
893 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" <<
endl;
898 e->m_entries.removeRef(sub_entry);
900 e->removeClient(instance);
902 if (e->m_clients.count() || e->m_entries.count()) {
903 kdDebug(7001) <<
"removeEntry: unwatched " << e->path <<
" " << _path <<
endl;
909 if (removeList.findRef(e)==-1)
910 removeList.append(e);
916 if (e->m_mode == FAMMode) {
917 if ( e->m_status == Normal) {
918 FAMCancelMonitor(&fc, &(e->fr) );
919 kdDebug(7001) <<
"Cancelled FAM (Req "
920 << FAMREQUEST_GETREQNUM(&(e->fr))
921 <<
") for " << e->path <<
endl;
925 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
927 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
933 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) <<
endl;
934 if (e->m_mode == INotifyMode) {
935 if ( e->m_status == Normal ) {
936 (void) inotify_rm_watch( m_inotify_fd, e->wd );
937 kdDebug(7001) <<
"Cancelled INotify (fd " <<
938 m_inotify_fd <<
", " << e->wd <<
939 ") for " << e->path <<
endl;
943 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
945 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
951 if (e->m_mode == DNotifyMode) {
953 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
957 if ( e->m_status == Normal) {
960 fd_Entry.remove(e->dn_fd);
962 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
963 <<
") for " << e->path <<
endl;
969 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
975 if (e->m_mode == StatMode) {
977 if ( statEntries == 0 ) {
983 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
984 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
985 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
987 m_mapEntries.remove( e->path );
994 void KSimpleDirWatchPrivate::removeEntries(
KSimpleDirWatch* instance )
996 TQPtrList<Entry> list;
997 int minfreq = 3600000;
1000 EntryMap::Iterator it = m_mapEntries.begin();
1001 for( ; it != m_mapEntries.end(); ++it ) {
1002 Client* c = (*it).m_clients.first();
1003 for(;c;c=(*it).m_clients.next())
1004 if (c->instance == instance)
break;
1007 list.append(&(*it));
1009 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1010 minfreq = (*it).freq;
1013 for(Entry* e=list.first();e;e=list.next())
1014 removeEntry(instance, e->path, 0);
1016 if (minfreq > freq) {
1019 if (timer->isActive()) timer->changeInterval(freq);
1020 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" <<
endl;
1025 bool KSimpleDirWatchPrivate::stopEntryScan(
KSimpleDirWatch* instance, Entry* e)
1027 int stillWatching = 0;
1028 Client* c = e->m_clients.first();
1029 for(;c;c=e->m_clients.next()) {
1030 if (!instance || instance == c->instance)
1031 c->watchingStopped =
true;
1032 else if (!c->watchingStopped)
1033 stillWatching += c->count;
1036 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path
1037 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
1039 if (stillWatching == 0) {
1041 e->m_ctime = invalid_ctime;
1042 e->m_status = NonExistent;
1049 bool KSimpleDirWatchPrivate::restartEntryScan(
KSimpleDirWatch* instance, Entry* e,
1052 int wasWatching = 0, newWatching = 0;
1053 Client* c = e->m_clients.first();
1054 for(;c;c=e->m_clients.next()) {
1055 if (!c->watchingStopped)
1056 wasWatching += c->count;
1057 else if (!instance || instance == c->instance) {
1058 c->watchingStopped =
false;
1059 newWatching += c->count;
1062 if (newWatching == 0)
1065 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path
1066 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1071 if (wasWatching == 0) {
1073 KDE_struct_stat stat_buf;
1074 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1076 e->m_ctime = stat_buf.st_ctime;
1077 e->m_status = Normal;
1078 e->m_nlink = stat_buf.st_nlink;
1081 e->m_ctime = invalid_ctime;
1082 e->m_status = NonExistent;
1097 EntryMap::Iterator it = m_mapEntries.begin();
1098 for( ; it != m_mapEntries.end(); ++it )
1099 stopEntryScan(instance, &(*it));
1104 bool notify,
bool skippedToo )
1107 resetList(instance,skippedToo);
1109 EntryMap::Iterator it = m_mapEntries.begin();
1110 for( ; it != m_mapEntries.end(); ++it )
1111 restartEntryScan(instance, &(*it), notify);
1121 EntryMap::Iterator it = m_mapEntries.begin();
1122 for( ; it != m_mapEntries.end(); ++it ) {
1124 Client* c = (*it).m_clients.first();
1125 for(;c;c=(*it).m_clients.next())
1126 if (!c->watchingStopped || skippedToo)
1127 c->pending = NoChange;
1133 int KSimpleDirWatchPrivate::scanEntry(Entry* e)
1136 if (e->m_mode == FAMMode) {
1138 if(!e->dirty)
return NoChange;
1144 if (e->m_mode == UnknownMode)
return NoChange;
1146 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1147 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1149 if(!e->dirty)
return NoChange;
1150 kdDebug(7001) <<
"scanning " << e->path <<
" " << e->m_status <<
" " << e->m_ctime <<
endl;
1155 if (e->m_mode == StatMode) {
1160 e->msecLeft -= freq;
1161 if (e->msecLeft>0)
return NoChange;
1162 e->msecLeft += e->freq;
1165 KDE_struct_stat stat_buf;
1166 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1169 if (e->m_status == NonExistent) {
1170 e->m_ctime = stat_buf.st_ctime;
1171 e->m_status = Normal;
1172 e->m_nlink = stat_buf.st_nlink;
1176 if ( (e->m_ctime != invalid_ctime) &&
1177 ((stat_buf.st_ctime != e->m_ctime) ||
1178 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1179 e->m_ctime = stat_buf.st_ctime;
1180 e->m_nlink = stat_buf.st_nlink;
1189 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1191 e->m_status = NonExistent;
1195 e->m_ctime = invalid_ctime;
1197 e->m_status = NonExistent;
1206 void KSimpleDirWatchPrivate::emitEvent(Entry* e,
int event,
const TQString &fileName)
1208 TQString path = e->path;
1209 if (!fileName.isEmpty()) {
1210 if (!TQDir::isRelativePath(fileName))
1214 path +=
"/" + fileName;
1215 #elif defined(Q_WS_WIN)
1217 path += TQDir::currentDirPath().left(2) +
"/" + fileName;
1221 TQPtrListIterator<Client> cit( e->m_clients );
1222 for ( ; cit.current(); ++cit )
1224 Client* c = cit.current();
1226 if (c->instance==0 || c->count==0)
continue;
1228 if (c->watchingStopped) {
1230 if (event == Changed)
1231 c->pending |= event;
1232 else if (event == Created || event == Deleted)
1237 if (event == NoChange || event == Changed)
1238 event |= c->pending;
1239 c->pending = NoChange;
1240 if (event == NoChange)
continue;
1242 if (event & Deleted) {
1243 c->instance->setDeleted(path);
1248 if (event & Created) {
1249 c->instance->setCreated(path);
1253 if (event & Changed)
1254 c->instance->setDirty(path);
1259 void KSimpleDirWatchPrivate::slotRemoveDelayed()
1262 delayRemove =
false;
1263 for(e=removeList.first();e;e=removeList.next())
1264 removeEntry(0, e->path, 0);
1271 void KSimpleDirWatchPrivate::slotRescan()
1273 EntryMap::Iterator it;
1278 bool timerRunning = timer->isActive();
1286 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1287 TQPtrList<Entry> dList, cList;
1293 it = m_mapEntries.begin();
1294 for( ; it != m_mapEntries.end(); ++it )
1301 it = m_mapEntries.begin();
1302 for( ; it != m_mapEntries.end(); ++it )
1303 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
1304 (*it).propagate_dirty();
1307 it = m_mapEntries.begin();
1308 for( ; it != m_mapEntries.end(); ++it ) {
1310 if (!(*it).isValid())
continue;
1312 int ev = scanEntry( &(*it) );
1316 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1317 cList.append( &(*it) );
1318 if (! useINotify( &(*it) )) {
1325 if ((*it).m_mode == DNotifyMode) {
1326 if ((*it).isDir && (ev == Deleted)) {
1327 dList.append( &(*it) );
1331 ::close((*it).dn_fd);
1332 fd_Entry.remove((*it).dn_fd);
1337 else if ((*it).isDir && (ev == Created)) {
1339 if ( (*it).dn_fd == 0) {
1340 cList.append( &(*it) );
1341 if (! useDNotify( &(*it) )) {
1350 if ( ev != NoChange )
1351 emitEvent( &(*it), ev);
1355 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1358 for(e=dList.first();e;e=dList.next())
1359 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1362 for(e=cList.first();e;e=cList.next())
1363 removeEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e);
1369 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1372 bool KSimpleDirWatchPrivate::isNoisyFile(
const char * filename )
1375 if ( *filename ==
'.') {
1376 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1377 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1380 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1387 void KSimpleDirWatchPrivate::famEventReceived()
1393 while(use_fam && FAMPending(&fc)) {
1394 if (FAMNextEvent(&fc, &fe) == -1) {
1395 kdWarning(7001) <<
"FAM connection problem, switching to polling."
1401 EntryMap::Iterator it;
1402 it = m_mapEntries.begin();
1403 for( ; it != m_mapEntries.end(); ++it )
1404 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1406 if (useINotify( &(*it) ))
continue;
1409 if (useDNotify( &(*it) ))
continue;
1418 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1421 void KSimpleDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1424 if ((fe->code == FAMExists) ||
1425 (fe->code == FAMEndExist) ||
1426 (fe->code == FAMAcknowledge))
return;
1428 if ( isNoisyFile( fe->filename ) )
1432 EntryMap::Iterator it = m_mapEntries.begin();
1433 for( ; it != m_mapEntries.end(); ++it )
1434 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1435 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1443 kdDebug(7001) <<
"Processing FAM event ("
1444 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1445 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1446 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1447 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1448 (fe->code == FAMCreated) ?
"FAMCreated" :
1449 (fe->code == FAMMoved) ?
"FAMMoved" :
1450 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1451 (fe->code == FAMExists) ?
"FAMExists" :
1452 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1453 <<
", " << fe->filename
1454 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1464 if (e->m_status == NonExistent) {
1465 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path <<
endl;
1471 if (!rescan_timer.isActive())
1472 rescan_timer.start(m_PollInterval,
true);
1480 if (!TQDir::isRelativePath(fe->filename))
1484 e->m_status = NonExistent;
1485 FAMCancelMonitor(&fc, &(e->fr) );
1486 kdDebug(7001) <<
"Cancelled FAMReq "
1487 << FAMREQUEST_GETREQNUM(&(e->fr))
1488 <<
" for " << e->path <<
endl;
1490 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1496 Entry *sub_entry = e->m_entries.first();
1497 for(;sub_entry; sub_entry = e->m_entries.next())
1498 if (sub_entry->path == e->path +
"/" + fe->filename)
break;
1499 if (sub_entry && sub_entry->isDir) {
1500 TQString path = e->path;
1501 removeEntry(0,e->path,sub_entry);
1502 sub_entry->m_status = Normal;
1503 if (!useFAM(sub_entry))
1506 if (!useINotify(sub_entry ))
1521 void KSimpleDirWatchPrivate::famEventReceived() {}
1525 void KSimpleDirWatchPrivate::statistics()
1527 EntryMap::Iterator it;
1530 if (m_mapEntries.count()==0) {
1534 it = m_mapEntries.begin();
1535 for( ; it != m_mapEntries.end(); ++it ) {
1537 kdDebug(7001) <<
" " << e->path <<
" ("
1538 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1539 << (e->isDir ?
"Dir":
"File") <<
", using "
1540 << ((e->m_mode == FAMMode) ?
"FAM" :
1541 (e->m_mode == INotifyMode) ?
"INotify" :
1542 (e->m_mode == DNotifyMode) ?
"DNotify" :
1543 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1546 Client* c = e->m_clients.first();
1547 for(;c; c = e->m_clients.next()) {
1549 if (c->watchingStopped) {
1550 if (c->pending & Deleted) pending +=
"deleted ";
1551 if (c->pending & Created) pending +=
"created ";
1552 if (c->pending & Changed) pending +=
"changed ";
1553 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1554 pending =
", stopped" + pending;
1556 kdDebug(7001) <<
" by " << c->instance->name()
1557 <<
" (" << c->count <<
" times)"
1560 if (e->m_entries.count()>0) {
1562 Entry* d = e->m_entries.first();
1563 for(;d; d = e->m_entries.next()) {
1565 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " <<
endl;
1591 return s_pSelf != 0;
1595 : TQObject(parent,name)
1598 static int nameCounter = 0;
1601 setName(TQString(TQString(
"KSimpleDirWatch-%1").arg(nameCounter)).ascii());
1605 dwp_self =
new KSimpleDirWatchPrivate;
1614 d->removeEntries(
this);
1626 bool watchFiles,
bool recursive)
1628 if (watchFiles || recursive) {
1629 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in KDE 3.x" <<
endl;
1631 if (d) d->addEntry(
this, _path, 0,
true);
1636 if (d) d->addEntry(
this, _path, 0,
false);
1641 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1644 return TQDateTime();
1647 result.setTime_t(e->m_ctime);
1653 if (d) d->removeEntry(
this, _path, 0);
1658 if (d) d->removeEntry(
this, _path, 0);
1664 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1665 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1673 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1676 return d->restartEntryScan(
this, e,
false);
1683 if (d) d->stopScan(
this);
1690 if (d) d->startScan(
this, notify, skippedToo);
1696 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1700 KSimpleDirWatchPrivate::Client* c = e->m_clients.first();
1701 for(;c;c=e->m_clients.next())
1702 if (c->instance ==
this)
return true;
1710 kdDebug(7001) <<
"KSimpleDirWatch not used" <<
endl;
1713 dwp_self->statistics();
1719 kdDebug(7001) << name() <<
" emitting created " << _file <<
endl;
1725 kdDebug(7001) << name() <<
" emitting dirty " << _file <<
endl;
1726 emit
dirty( _file );
1731 kdDebug(7001) << name() <<
" emitting deleted " << _file <<
endl;
1739 return KSimpleDirWatch::FAM;
1742 if (d->supports_inotify)
1743 return KSimpleDirWatch::INotify;
1746 if (d->supports_dnotify)
1747 return KSimpleDirWatch::DNotify;
1749 return KSimpleDirWatch::Stat;
1753 #include "ksimpledirwatch.moc"
1754 #include "ksimpledirwatch_p.moc"
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
int event(const TQString &message, const TQString &text=TQString::null) KDE_DEPRECATED
static void statistics()
Dump statistic information about all KSimpleDirWatch instances.
Method internalMethod()
Returns the preferred internal method to watch for changes.
void setDeleted(const TQString &path)
Emits deleted().
Little helper class to clean up static objects that are held as pointer.
void addDir(const TQString &path, bool watchFiles=false, bool recursive=false)
Adds a directory to be watched.
TQDateTime ctime(const TQString &path)
Returns the time the directory/file was last changed.
KSimpleDirWatch(TQObject *parent=0, const char *name=0)
Constructor.
kdbgstream kdDebug(int area=0)
void removeFile(const TQString &file)
Removes a file from the list of watched files.
~KSimpleDirWatch()
Destructor.
KDE_DEPRECATED type * setObject(type *obj, bool isArray=false)
Sets the object to delete and registers the object to be deleted to TDEGlobal.
void dirty(const TQString &path)
Emitted when a watched object is changed.
void stopScan()
Stops scanning of all directories in internal list.
static bool exists()
Returns true if there is an instance of KSimpleDirWatch.
A TDEConfigBase derived class for one specific group in a TDEConfig object.
bool restartDirScan(const TQString &path)
Restarts scanning for specified path.
void created(const TQString &path)
Emitted when a file or directory is created.
kdbgstream kdWarning(int area=0)
void setCreated(const TQString &path)
Emits created().
const char * name(StdAction id)
void setDirty(const TQString &path)
Emits dirty().
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.
bool stopDirScan(const TQString &path)
Stops scanning the specified path.
TDEAction * close(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
KSimpleDirWatch is a basic copy of KDirWatch but with the TDEIO linking requirement removed...
static TDEConfig * config()
Returns the general config object.
kndbgstream & endl(kndbgstream &s)
Does nothing.
static KSimpleDirWatch * self()
The KSimpleDirWatch instance usually globally used in an application.
bool contains(const TQString &path) const
Check if a directory is being watched by this KSimpleDirWatch instance.
bool isStopped()
Is scanning stopped? After creation of a KSimpleDirWatch instance, this is false. ...
kdbgstream & endl(kdbgstream &s)
void deleted(const TQString &path)
Emitted when a file or directory is deleted.