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;
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(
""))
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;
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)
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"
KSimpleDirWatch is a basic copy of KDirWatch but with the TDEIO linking requirement removed.
TQDateTime ctime(const TQString &path)
Returns the time the directory/file was last changed.
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 restartDirScan(const TQString &path)
Restarts scanning for specified path.
void setDeleted(const TQString &path)
Emits deleted().
void created(const TQString &path)
Emitted when a file or directory is created.
void setCreated(const TQString &path)
Emits created().
void deleted(const TQString &path)
Emitted when a file or directory is deleted.
static void statistics()
Dump statistic information about all KSimpleDirWatch instances.
KSimpleDirWatch(TQObject *parent=0, const char *name=0)
Constructor.
void dirty(const TQString &path)
Emitted when a watched object is changed.
bool stopDirScan(const TQString &path)
Stops scanning the specified path.
bool contains(const TQString &path) const
Check if a directory is being watched by this KSimpleDirWatch instance.
void setDirty(const TQString &path)
Emits dirty().
void addDir(const TQString &path, bool watchFiles=false, bool recursive=false)
Adds a directory to be watched.
void removeFile(const TQString &file)
Removes a file from the list of watched files.
static bool exists()
Returns true if there is an instance of KSimpleDirWatch.
void stopScan()
Stops scanning of all directories in internal list.
static KSimpleDirWatch * self()
The KSimpleDirWatch instance usually globally used in an application.
~KSimpleDirWatch()
Destructor.
Method internalMethod()
Returns the preferred internal method to watch for changes.
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
Little helper class to clean up static objects that are held as pointer.
A TDEConfigBase derived class for one specific group in a TDEConfig object.
static TDEConfig * config()
Returns the general config object.
kndbgstream & endl(kndbgstream &s)
Does nothing.
kdbgstream kdWarning(int area=0)
Returns a warning stream.
kdbgstream kdDebug(int area=0)
Returns a debug stream.
kdbgstream & endl(kdbgstream &s)
Prints an "\n".
int event(const TQString &message, const TQString &text=TQString::null) KDE_DEPRECATED
TDEInstance * instance()
Shortcut to KNotifyClient::Instance::current() :)
TDEAction * close(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
const char * name(StdAction id)