40 #include <tqintdict.h>
41 #include <tqptrlist.h>
42 #include <tqsocketnotifier.h>
43 #include <tqstringlist.h>
46 #include <tdeapplication.h>
48 #include <tdeconfig.h>
49 #include <tdeglobal.h>
50 #include <kstaticdeleter.h>
54 #include <sys/ioctl.h>
59 #include <sys/syscall.h>
60 #include <linux/types.h>
62 #define _S390_BITOPS_H
63 #include <sys/inotify.h>
65 #ifndef __NR_inotify_init
67 #define __NR_inotify_init 291
68 #define __NR_inotify_add_watch 292
69 #define __NR_inotify_rm_watch 293
72 #define __NR_inotify_init 275
73 #define __NR_inotify_add_watch 276
74 #define __NR_inotify_rm_watch 277
76 #if defined(__x86_64__)
77 #define __NR_inotify_init 253
78 #define __NR_inotify_add_watch 254
79 #define __NR_inotify_rm_watch 255
84 #define IN_ONLYDIR 0x01000000
87 #ifndef IN_DONT_FOLLOW
88 #define IN_DONT_FOLLOW 0x02000000
92 #define IN_MOVE_SELF 0x00000800
97 #include <sys/utsname.h>
99 #include "ksimpledirwatch.h"
100 #include "ksimpledirwatch_p.h"
102 #define NO_NOTIFY (time_t) 0
104 static KSimpleDirWatchPrivate* dwp_self = 0;
108 static int dnotify_signal = 0;
118 void KSimpleDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
120 if (!dwp_self)
return;
124 int saved_errno = errno;
126 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
131 if(e && e->dn_fd == si->si_fd)
135 write(dwp_self->mPipe[1], &c, 1);
139 static struct sigaction old_sigio_act;
144 void KSimpleDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
150 int saved_errno = errno;
152 dwp_self->rescan_all =
true;
154 write(dwp_self->mPipe[1], &c, 1);
160 if (old_sigio_act.sa_flags & SA_SIGINFO)
162 if (old_sigio_act.sa_sigaction)
163 (*old_sigio_act.sa_sigaction)(sig, si, p);
167 if ((old_sigio_act.sa_handler != SIG_DFL) &&
168 (old_sigio_act.sa_handler != SIG_IGN))
169 (*old_sigio_act.sa_handler)(sig);
207 KSimpleDirWatchPrivate::KSimpleDirWatchPrivate()
208 : rescan_timer(0,
"KSimpleDirWatchPrivate::rescan_timer")
210 timer =
new TQTimer(
this,
"KSimpleDirWatchPrivate::timer");
211 connect (timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
218 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
219 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
221 TQString available(
"Stat");
225 connect(&rescan_timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
229 if (FAMOpen(&fc) ==0) {
230 available +=
", FAM";
232 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
233 TQSocketNotifier::Read,
this);
234 connect( sn, TQT_SIGNAL(activated(
int)),
235 this, TQT_SLOT(famEventReceived()) );
238 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" <<
endl;
244 supports_inotify =
true;
246 m_inotify_fd = inotify_init();
248 if ( m_inotify_fd <= 0 ) {
249 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" <<
endl;
250 supports_inotify =
false;
255 int major, minor, patch;
257 supports_inotify =
false;
258 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
259 supports_inotify =
false;
260 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
261 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" <<
endl;
262 supports_inotify =
false;
266 if ( supports_inotify ) {
267 available +=
", Inotify";
268 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
270 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
271 connect( mSn, TQT_SIGNAL(activated(
int )),
this, TQT_SLOT( slotActivated() ) );
279 supports_dnotify = !supports_inotify;
282 supports_dnotify =
true;
286 int major, minor, patch;
288 supports_dnotify =
false;
289 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
290 supports_dnotify =
false;
291 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
292 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" <<
endl;
293 supports_dnotify =
false;
296 if( supports_dnotify ) {
297 available +=
", DNotify";
300 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
301 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
302 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
303 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
304 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
305 connect(mSn, TQT_SIGNAL(activated(
int)),
this, TQT_SLOT(slotActivated()));
307 if ( dnotify_signal == 0 )
309 dnotify_signal = SIGRTMIN + 8;
311 struct sigaction act;
312 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_handler;
313 sigemptyset(&act.sa_mask);
314 act.sa_flags = SA_SIGINFO;
316 act.sa_flags |= SA_RESTART;
318 sigaction(dnotify_signal, &act, NULL);
320 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_sigio_handler;
321 sigaction(SIGIO, &act, &old_sigio_act);
331 kdDebug(7001) <<
"Available methods: " << available <<
endl;
335 KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
345 kdDebug(7001) <<
"KSimpleDirWatch deleted (FAM closed)" <<
endl;
349 if ( supports_inotify )
350 ::close( m_inotify_fd );
360 void KSimpleDirWatchPrivate::slotActivated()
363 if ( supports_dnotify )
365 char dummy_buf[4096];
366 read(mPipe[0], &dummy_buf, 4096);
368 if (!rescan_timer.isActive())
369 rescan_timer.start(m_PollInterval,
true );
376 if ( !supports_inotify )
382 assert( m_inotify_fd > -1 );
383 ioctl( m_inotify_fd, FIONREAD, &pending );
385 while ( pending > 0 ) {
387 if ( pending > (
int)
sizeof( buf ) )
388 pending =
sizeof( buf );
390 pending = read( m_inotify_fd, buf, pending);
392 while ( pending > 0 ) {
393 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
394 pending -=
sizeof(
struct inotify_event ) +
event->len;
395 offset +=
sizeof(
struct inotify_event ) +
event->len;
399 path = TQFile::decodeName( TQCString(
event->name,
event->len ) );
401 if ( path.length() && isNoisyFile( path.latin1() ) )
404 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path <<
endl;
409 for ( EntryMap::Iterator it = m_mapEntries.begin();
410 it != m_mapEntries.end(); ++it ) {
412 if ( e->wd ==
event->wd ) {
415 if ( 1 || e->isDir) {
416 if(
event->mask & IN_DELETE_SELF) {
417 kdDebug(7001) <<
"-->got deleteself signal for " << e->path <<
endl;
418 e->m_status = NonExistent;
420 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
422 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
424 if (
event->mask & IN_IGNORED ) {
427 if (
event->mask & (IN_CREATE|IN_MOVED_TO) ) {
428 Entry *sub_entry = e->m_entries.first();
429 for(;sub_entry; sub_entry = e->m_entries.next())
430 if (sub_entry->path == e->path +
"/" + path)
break;
433 removeEntry(0,e->path, sub_entry);
434 KDE_struct_stat stat_buf;
435 TQCString tpath = TQFile::encodeName(path);
436 KDE_stat(tpath, &stat_buf);
443 if(!useINotify(sub_entry))
445 sub_entry->dirty =
true;
450 if (!rescan_timer.isActive())
451 rescan_timer.start(m_PollInterval,
true );
466 void KSimpleDirWatchPrivate::Entry::propagate_dirty()
468 for (TQPtrListIterator<Entry> sub_entry (m_entries);
469 sub_entry.current(); ++sub_entry)
471 if (!sub_entry.current()->dirty)
473 sub_entry.current()->dirty =
true;
474 sub_entry.current()->propagate_dirty();
483 void KSimpleDirWatchPrivate::Entry::addClient(
KSimpleDirWatch* instance)
485 Client* client = m_clients.first();
486 for(;client; client = m_clients.next())
487 if (client->instance == instance)
break;
495 client->instance = instance;
497 client->watchingStopped = instance->
isStopped();
498 client->pending = NoChange;
500 m_clients.append(client);
503 void KSimpleDirWatchPrivate::Entry::removeClient(
KSimpleDirWatch* instance)
505 Client* client = m_clients.first();
506 for(;client; client = m_clients.next())
507 if (client->instance == instance)
break;
511 if (client->count == 0) {
512 m_clients.removeRef(client);
519 int KSimpleDirWatchPrivate::Entry::clients()
522 Client* client = m_clients.first();
523 for(;client; client = m_clients.next())
524 clients += client->count;
530 KSimpleDirWatchPrivate::Entry* KSimpleDirWatchPrivate::entry(
const TQString& _path)
533 if (TQDir::isRelativePath(_path)) {
537 TQString path = _path;
539 if ( path.length() > 1 && path.right(1) ==
"/" )
540 path.truncate( path.length() - 1 );
542 EntryMap::Iterator it = m_mapEntries.find( path );
543 if ( it == m_mapEntries.end() )
550 void KSimpleDirWatchPrivate::useFreq(Entry* e,
int newFreq)
555 if (e->freq < freq) {
557 if (timer->isActive()) timer->changeInterval(freq);
558 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" <<
endl;
565 bool KSimpleDirWatchPrivate::useFAM(Entry* e)
567 if (!use_fam)
return false;
577 if (e->m_status == NonExistent) {
579 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
582 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
585 e->m_mode = UnknownMode;
589 kdDebug(7001) <<
" Setup FAM (Req "
590 << FAMREQUEST_GETREQNUM(&(e->fr))
591 <<
") for " << e->path <<
endl;
595 if (e->m_status == NonExistent) {
597 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
600 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
603 e->m_mode = UnknownMode;
608 kdDebug(7001) <<
" Setup FAM (Req "
609 << FAMREQUEST_GETREQNUM(&(e->fr))
610 <<
") for " << e->path <<
endl;
625 bool KSimpleDirWatchPrivate::useDNotify(Entry* e)
629 if (!supports_dnotify)
return false;
631 e->m_mode = DNotifyMode;
634 if (e->m_status == Normal) {
635 int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
648 int fd2 = fcntl(fd, F_DUPFD, 128);
655 e->m_mode = UnknownMode;
659 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
661 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
662 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
664 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
665 fcntl(fd, F_NOTIFY, mask) < 0) {
667 kdDebug(7001) <<
"Not using Linux Directory Notifications."
669 supports_dnotify =
false;
671 e->m_mode = UnknownMode;
675 fd_Entry.replace(fd, e);
678 kdDebug(7001) <<
" Setup DNotify (fd " << fd
679 <<
") for " << e->path <<
endl;
682 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
688 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
697 bool KSimpleDirWatchPrivate::useINotify( Entry* e )
701 if (!supports_inotify)
return false;
703 e->m_mode = INotifyMode;
705 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
707 mask |= IN_MODIFY|IN_ATTRIB;
712 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
713 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
716 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
717 TQFile::encodeName( e->path ), mask) ) > 0 )
720 if ( e->m_status == NonExistent ) {
722 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
724 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
732 bool KSimpleDirWatchPrivate::useStat(Entry* e)
734 useFreq(e, m_PollInterval);
736 if (e->m_mode != StatMode) {
737 e->m_mode = StatMode;
740 if ( statEntries == 1 ) {
743 kdDebug(7001) <<
" Started Polling Timer, freq " << freq <<
endl;
747 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
748 <<
") for " << e->path <<
endl;
759 void KSimpleDirWatchPrivate::addEntry(
KSimpleDirWatch* instance,
const TQString& _path,
760 Entry* sub_entry,
bool isDir)
762 TQString path = _path;
763 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
766 if ( path.length() > 1 && path.right(1) ==
"/" )
767 path.truncate( path.length() - 1 );
769 EntryMap::Iterator it = m_mapEntries.find( path );
770 if ( it != m_mapEntries.end() )
773 (*it).m_entries.append(sub_entry);
774 kdDebug(7001) <<
"Added already watched Entry " << path
775 <<
" (for " << sub_entry->path <<
")" <<
endl;
780 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
781 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
783 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
784 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
785 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
787 e->m_mode = UnknownMode;
788 fd_Entry.remove(e->dn_fd);
799 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
800 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
802 mask |= IN_MODIFY|IN_ATTRIB;
806 inotify_rm_watch (m_inotify_fd, e->wd);
807 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
814 (*it).addClient(instance);
815 kdDebug(7001) <<
"Added already watched Entry " << path
816 <<
" (now " << (*it).clients() <<
" clients)"
817 << TQString(TQString(
" [%1]").arg(instance->name())) <<
endl;
824 KDE_struct_stat stat_buf;
825 TQCString tpath = TQFile::encodeName(path);
826 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
829 m_mapEntries.insert( path, newEntry );
831 Entry* e = &(m_mapEntries[path]);
834 e->isDir = S_ISDIR(stat_buf.st_mode);
836 if (e->isDir && !isDir)
837 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a directory. Use addDir!" <<
endl;
838 else if (!e->isDir && isDir)
839 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a file. Use addFile!" <<
endl;
841 e->m_ctime = stat_buf.st_ctime;
842 e->m_status = Normal;
843 e->m_nlink = stat_buf.st_nlink;
847 e->m_ctime = invalid_ctime;
848 e->m_status = NonExistent;
854 e->m_entries.append(sub_entry);
856 e->addClient(instance);
858 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
859 << (e->m_status == NonExistent ?
" NotExisting" :
"")
860 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
861 << (instance ? TQString(TQString(
" [%1]").arg(instance->
name())) : TQString(
""))
866 e->m_mode = UnknownMode;
869 if ( isNoisyFile( tpath ) )
873 if (useFAM(e))
return;
877 if (useINotify(e))
return;
881 if (useDNotify(e))
return;
889 const TQString& _path, Entry* sub_entry )
891 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry <<
endl;
892 Entry* e = entry(_path);
894 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" <<
endl;
899 e->m_entries.removeRef(sub_entry);
901 e->removeClient(instance);
903 if (e->m_clients.count() || e->m_entries.count()) {
904 kdDebug(7001) <<
"removeEntry: unwatched " << e->path <<
" " << _path <<
endl;
910 if (removeList.findRef(e)==-1)
911 removeList.append(e);
917 if (e->m_mode == FAMMode) {
918 if ( e->m_status == Normal) {
919 FAMCancelMonitor(&fc, &(e->fr) );
920 kdDebug(7001) <<
"Cancelled FAM (Req "
921 << FAMREQUEST_GETREQNUM(&(e->fr))
922 <<
") for " << e->path <<
endl;
926 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
928 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
934 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) <<
endl;
935 if (e->m_mode == INotifyMode) {
936 if ( e->m_status == Normal ) {
937 (void) inotify_rm_watch( m_inotify_fd, e->wd );
938 kdDebug(7001) <<
"Cancelled INotify (fd " <<
939 m_inotify_fd <<
", " << e->wd <<
940 ") for " << e->path <<
endl;
944 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
946 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
952 if (e->m_mode == DNotifyMode) {
954 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
958 if ( e->m_status == Normal) {
961 fd_Entry.remove(e->dn_fd);
963 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
964 <<
") for " << e->path <<
endl;
970 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
976 if (e->m_mode == StatMode) {
978 if ( statEntries == 0 ) {
984 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
985 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
986 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
988 m_mapEntries.remove( e->path );
995 void KSimpleDirWatchPrivate::removeEntries(
KSimpleDirWatch* instance )
997 TQPtrList<Entry> list;
998 int minfreq = 3600000;
1001 EntryMap::Iterator it = m_mapEntries.begin();
1002 for( ; it != m_mapEntries.end(); ++it ) {
1003 Client* c = (*it).m_clients.first();
1004 for(;c;c=(*it).m_clients.next())
1005 if (c->instance == instance)
break;
1008 list.append(&(*it));
1010 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1011 minfreq = (*it).freq;
1014 for(Entry* e=list.first();e;e=list.next())
1015 removeEntry(instance, e->path, 0);
1017 if (minfreq > freq) {
1020 if (timer->isActive()) timer->changeInterval(freq);
1021 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" <<
endl;
1026 bool KSimpleDirWatchPrivate::stopEntryScan(
KSimpleDirWatch* instance, Entry* e)
1028 int stillWatching = 0;
1029 Client* c = e->m_clients.first();
1030 for(;c;c=e->m_clients.next()) {
1031 if (!instance || instance == c->instance)
1032 c->watchingStopped =
true;
1033 else if (!c->watchingStopped)
1034 stillWatching += c->count;
1037 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path
1038 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
1040 if (stillWatching == 0) {
1042 e->m_ctime = invalid_ctime;
1043 e->m_status = NonExistent;
1050 bool KSimpleDirWatchPrivate::restartEntryScan(
KSimpleDirWatch* instance, Entry* e,
1053 int wasWatching = 0, newWatching = 0;
1054 Client* c = e->m_clients.first();
1055 for(;c;c=e->m_clients.next()) {
1056 if (!c->watchingStopped)
1057 wasWatching += c->count;
1058 else if (!instance || instance == c->instance) {
1059 c->watchingStopped =
false;
1060 newWatching += c->count;
1063 if (newWatching == 0)
1066 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path
1067 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1072 if (wasWatching == 0) {
1074 KDE_struct_stat stat_buf;
1075 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1077 e->m_ctime = stat_buf.st_ctime;
1078 e->m_status = Normal;
1079 e->m_nlink = stat_buf.st_nlink;
1082 e->m_ctime = invalid_ctime;
1083 e->m_status = NonExistent;
1098 EntryMap::Iterator it = m_mapEntries.begin();
1099 for( ; it != m_mapEntries.end(); ++it )
1100 stopEntryScan(instance, &(*it));
1105 bool notify,
bool skippedToo )
1108 resetList(instance,skippedToo);
1110 EntryMap::Iterator it = m_mapEntries.begin();
1111 for( ; it != m_mapEntries.end(); ++it )
1112 restartEntryScan(instance, &(*it), notify);
1122 EntryMap::Iterator it = m_mapEntries.begin();
1123 for( ; it != m_mapEntries.end(); ++it ) {
1125 Client* c = (*it).m_clients.first();
1126 for(;c;c=(*it).m_clients.next())
1127 if (!c->watchingStopped || skippedToo)
1128 c->pending = NoChange;
1134 int KSimpleDirWatchPrivate::scanEntry(Entry* e)
1137 if (e->m_mode == FAMMode) {
1139 if(!e->dirty)
return NoChange;
1145 if (e->m_mode == UnknownMode)
return NoChange;
1147 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1148 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1150 if(!e->dirty)
return NoChange;
1151 kdDebug(7001) <<
"scanning " << e->path <<
" " << e->m_status <<
" " << e->m_ctime <<
endl;
1156 if (e->m_mode == StatMode) {
1161 e->msecLeft -= freq;
1162 if (e->msecLeft>0)
return NoChange;
1163 e->msecLeft += e->freq;
1166 KDE_struct_stat stat_buf;
1167 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1170 if (e->m_status == NonExistent) {
1171 e->m_ctime = stat_buf.st_ctime;
1172 e->m_status = Normal;
1173 e->m_nlink = stat_buf.st_nlink;
1177 if ( (e->m_ctime != invalid_ctime) &&
1178 ((stat_buf.st_ctime != e->m_ctime) ||
1179 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1180 e->m_ctime = stat_buf.st_ctime;
1181 e->m_nlink = stat_buf.st_nlink;
1190 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1192 e->m_status = NonExistent;
1196 e->m_ctime = invalid_ctime;
1198 e->m_status = NonExistent;
1207 void KSimpleDirWatchPrivate::emitEvent(Entry* e,
int event,
const TQString &fileName)
1209 TQString path = e->path;
1210 if (!fileName.isEmpty()) {
1211 if (!TQDir::isRelativePath(fileName))
1215 path +=
"/" + fileName;
1216 #elif defined(Q_WS_WIN)
1218 path += TQDir::currentDirPath().left(2) +
"/" + fileName;
1222 TQPtrListIterator<Client> cit( e->m_clients );
1223 for ( ; cit.current(); ++cit )
1225 Client* c = cit.current();
1227 if (c->instance==0 || c->count==0)
continue;
1229 if (c->watchingStopped) {
1231 if (event == Changed)
1232 c->pending |= event;
1233 else if (event == Created || event == Deleted)
1238 if (event == NoChange || event == Changed)
1239 event |= c->pending;
1240 c->pending = NoChange;
1241 if (event == NoChange)
continue;
1243 if (event & Deleted) {
1244 c->instance->setDeleted(path);
1249 if (event & Created) {
1250 c->instance->setCreated(path);
1254 if (event & Changed)
1255 c->instance->setDirty(path);
1260 void KSimpleDirWatchPrivate::slotRemoveDelayed()
1263 delayRemove =
false;
1264 for(e=removeList.first();e;e=removeList.next())
1265 removeEntry(0, e->path, 0);
1272 void KSimpleDirWatchPrivate::slotRescan()
1274 EntryMap::Iterator it;
1279 bool timerRunning = timer->isActive();
1287 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1288 TQPtrList<Entry> dList, cList;
1294 it = m_mapEntries.begin();
1295 for( ; it != m_mapEntries.end(); ++it )
1302 it = m_mapEntries.begin();
1303 for( ; it != m_mapEntries.end(); ++it )
1304 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
1305 (*it).propagate_dirty();
1308 it = m_mapEntries.begin();
1309 for( ; it != m_mapEntries.end(); ++it ) {
1311 if (!(*it).isValid())
continue;
1313 int ev = scanEntry( &(*it) );
1317 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1318 cList.append( &(*it) );
1319 if (! useINotify( &(*it) )) {
1326 if ((*it).m_mode == DNotifyMode) {
1327 if ((*it).isDir && (ev == Deleted)) {
1328 dList.append( &(*it) );
1332 ::close((*it).dn_fd);
1333 fd_Entry.remove((*it).dn_fd);
1338 else if ((*it).isDir && (ev == Created)) {
1340 if ( (*it).dn_fd == 0) {
1341 cList.append( &(*it) );
1342 if (! useDNotify( &(*it) )) {
1351 if ( ev != NoChange )
1352 emitEvent( &(*it), ev);
1356 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1359 for(e=dList.first();e;e=dList.next())
1360 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1363 for(e=cList.first();e;e=cList.next())
1364 removeEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e);
1370 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1373 bool KSimpleDirWatchPrivate::isNoisyFile(
const char * filename )
1376 if ( *filename ==
'.') {
1377 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1378 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1381 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1388 void KSimpleDirWatchPrivate::famEventReceived()
1394 while(use_fam && FAMPending(&fc)) {
1395 if (FAMNextEvent(&fc, &fe) == -1) {
1396 kdWarning(7001) <<
"FAM connection problem, switching to polling."
1402 EntryMap::Iterator it;
1403 it = m_mapEntries.begin();
1404 for( ; it != m_mapEntries.end(); ++it )
1405 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1407 if (useINotify( &(*it) ))
continue;
1410 if (useDNotify( &(*it) ))
continue;
1419 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1422 void KSimpleDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1425 if ((fe->code == FAMExists) ||
1426 (fe->code == FAMEndExist) ||
1427 (fe->code == FAMAcknowledge))
return;
1429 if ( isNoisyFile( fe->filename ) )
1433 EntryMap::Iterator it = m_mapEntries.begin();
1434 for( ; it != m_mapEntries.end(); ++it )
1435 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1436 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1444 kdDebug(7001) <<
"Processing FAM event ("
1445 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1446 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1447 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1448 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1449 (fe->code == FAMCreated) ?
"FAMCreated" :
1450 (fe->code == FAMMoved) ?
"FAMMoved" :
1451 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1452 (fe->code == FAMExists) ?
"FAMExists" :
1453 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1454 <<
", " << fe->filename
1455 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1465 if (e->m_status == NonExistent) {
1466 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path <<
endl;
1472 if (!rescan_timer.isActive())
1473 rescan_timer.start(m_PollInterval,
true);
1481 if (!TQDir::isRelativePath(fe->filename))
1485 e->m_status = NonExistent;
1486 FAMCancelMonitor(&fc, &(e->fr) );
1487 kdDebug(7001) <<
"Cancelled FAMReq "
1488 << FAMREQUEST_GETREQNUM(&(e->fr))
1489 <<
" for " << e->path <<
endl;
1491 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1497 Entry *sub_entry = e->m_entries.first();
1498 for(;sub_entry; sub_entry = e->m_entries.next())
1499 if (sub_entry->path == e->path +
"/" + fe->filename)
break;
1500 if (sub_entry && sub_entry->isDir) {
1501 TQString path = e->path;
1502 removeEntry(0,e->path,sub_entry);
1503 sub_entry->m_status = Normal;
1504 if (!useFAM(sub_entry))
1506 if (!useINotify(sub_entry ))
1518 void KSimpleDirWatchPrivate::famEventReceived() {}
1522 void KSimpleDirWatchPrivate::statistics()
1524 EntryMap::Iterator it;
1527 if (m_mapEntries.count()==0) {
1531 it = m_mapEntries.begin();
1532 for( ; it != m_mapEntries.end(); ++it ) {
1534 kdDebug(7001) <<
" " << e->path <<
" ("
1535 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1536 << (e->isDir ?
"Dir":
"File") <<
", using "
1537 << ((e->m_mode == FAMMode) ?
"FAM" :
1538 (e->m_mode == INotifyMode) ?
"INotify" :
1539 (e->m_mode == DNotifyMode) ?
"DNotify" :
1540 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1543 Client* c = e->m_clients.first();
1544 for(;c; c = e->m_clients.next()) {
1546 if (c->watchingStopped) {
1547 if (c->pending & Deleted) pending +=
"deleted ";
1548 if (c->pending & Created) pending +=
"created ";
1549 if (c->pending & Changed) pending +=
"changed ";
1550 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1551 pending =
", stopped" + pending;
1553 kdDebug(7001) <<
" by " << c->instance->name()
1554 <<
" (" << c->count <<
" times)"
1557 if (e->m_entries.count()>0) {
1559 Entry* d = e->m_entries.first();
1560 for(;d; d = e->m_entries.next()) {
1562 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " <<
endl;
1588 return s_pSelf != 0;
1592 : TQObject(parent,name)
1595 static int nameCounter = 0;
1598 setName(TQString(TQString(
"KSimpleDirWatch-%1").arg(nameCounter)).ascii());
1602 dwp_self =
new KSimpleDirWatchPrivate;
1611 d->removeEntries(
this);
1623 bool watchFiles,
bool recursive)
1625 if (watchFiles || recursive) {
1626 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in KDE 3.x" <<
endl;
1628 if (d) d->addEntry(
this, _path, 0,
true);
1633 if (d) d->addEntry(
this, _path, 0,
false);
1638 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1641 return TQDateTime();
1644 result.setTime_t(e->m_ctime);
1650 if (d) d->removeEntry(
this, _path, 0);
1655 if (d) d->removeEntry(
this, _path, 0);
1661 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1662 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1670 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1673 return d->restartEntryScan(
this, e,
false);
1680 if (d) d->stopScan(
this);
1687 if (d) d->startScan(
this, notify, skippedToo);
1693 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1697 KSimpleDirWatchPrivate::Client* c = e->m_clients.first();
1698 for(;c;c=e->m_clients.next())
1699 if (c->instance ==
this)
return true;
1707 kdDebug(7001) <<
"KSimpleDirWatch not used" <<
endl;
1710 dwp_self->statistics();
1716 kdDebug(7001) << name() <<
" emitting created " << _file <<
endl;
1722 kdDebug(7001) << name() <<
" emitting dirty " << _file <<
endl;
1723 emit
dirty( _file );
1728 kdDebug(7001) << name() <<
" emitting deleted " << _file <<
endl;
1736 return KSimpleDirWatch::FAM;
1739 if (d->supports_inotify)
1740 return KSimpleDirWatch::INotify;
1743 if (d->supports_dnotify)
1744 return KSimpleDirWatch::DNotify;
1746 return KSimpleDirWatch::Stat;
1750 #include "ksimpledirwatch.moc"
1751 #include "ksimpledirwatch_p.moc"