41#include <tqsocketnotifier.h>
42#include <tqstringlist.h>
45#include <tdeapplication.h>
49#include <kstaticdeleter.h>
62#include <sys/syscall.h>
64#include <linux/types.h>
68#include <sys/inotify.h>
70#ifndef __NR_inotify_init
72#define __NR_inotify_init 291
73#define __NR_inotify_add_watch 292
74#define __NR_inotify_rm_watch 293
77#define __NR_inotify_init 275
78#define __NR_inotify_add_watch 276
79#define __NR_inotify_rm_watch 277
81#if defined(__x86_64__)
82#define __NR_inotify_init 253
83#define __NR_inotify_add_watch 254
84#define __NR_inotify_rm_watch 255
89#define IN_ONLYDIR 0x01000000
93#define IN_DONT_FOLLOW 0x02000000
97#define IN_MOVE_SELF 0x00000800
102#include <sys/utsname.h>
104#include "ksimpledirwatch.h"
105#include "ksimpledirwatch_p.h"
107#define NO_NOTIFY (time_t) 0
109static KSimpleDirWatchPrivate* dwp_self = 0;
113static int dnotify_signal = 0;
123void KSimpleDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
125 if (!dwp_self)
return;
129 int saved_errno = errno;
131 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
136 if(e && e->dn_fd == si->si_fd)
140 write(dwp_self->mPipe[1], &c, 1);
144static struct sigaction old_sigio_act;
149void KSimpleDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
155 int saved_errno = errno;
157 dwp_self->rescan_all =
true;
159 write(dwp_self->mPipe[1], &c, 1);
165 if (old_sigio_act.sa_flags & SA_SIGINFO)
167 if (old_sigio_act.sa_sigaction)
168 (*old_sigio_act.sa_sigaction)(sig, si, p);
172 if ((old_sigio_act.sa_handler != SIG_DFL) &&
173 (old_sigio_act.sa_handler != SIG_IGN))
174 (*old_sigio_act.sa_handler)(sig);
212KSimpleDirWatchPrivate::KSimpleDirWatchPrivate()
213 : rescan_timer(0,
"KSimpleDirWatchPrivate::rescan_timer")
215 timer =
new TQTimer(
this,
"KSimpleDirWatchPrivate::timer");
216 connect (timer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(slotRescan()));
223 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
224 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
226 TQString available(
"Stat");
230 connect(&rescan_timer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(slotRescan()));
234 if (FAMOpen(&fc) ==0) {
235 available +=
", FAM";
237 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
238 TQSocketNotifier::Read,
this);
239 connect( sn, TQ_SIGNAL(activated(
int)),
240 this, TQ_SLOT(famEventReceived()) );
243 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" <<
endl;
249 supports_inotify =
true;
251 m_inotify_fd = inotify_init();
253 if ( m_inotify_fd <= 0 ) {
254 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" <<
endl;
255 supports_inotify =
false;
260 int major, minor, patch;
262 supports_inotify =
false;
263 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
264 supports_inotify =
false;
265 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
266 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" <<
endl;
267 supports_inotify =
false;
271 if ( supports_inotify ) {
272 available +=
", Inotify";
273 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
275 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
276 connect( mSn, TQ_SIGNAL(activated(
int )),
this, TQ_SLOT( slotActivated() ) );
284 supports_dnotify = !supports_inotify;
287 supports_dnotify =
true;
291 int major, minor, patch;
293 supports_dnotify =
false;
294 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
295 supports_dnotify =
false;
296 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
297 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" <<
endl;
298 supports_dnotify =
false;
301 if( supports_dnotify ) {
302 available +=
", DNotify";
305 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
306 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
307 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
308 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
309 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
310 connect(mSn, TQ_SIGNAL(activated(
int)),
this, TQ_SLOT(slotActivated()));
312 if ( dnotify_signal == 0 )
314 dnotify_signal = SIGRTMIN + 8;
316 struct sigaction act;
317 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_handler;
318 sigemptyset(&act.sa_mask);
319 act.sa_flags = SA_SIGINFO;
321 act.sa_flags |= SA_RESTART;
323 sigaction(dnotify_signal, &act, NULL);
325 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_sigio_handler;
326 sigaction(SIGIO, &act, &old_sigio_act);
336 kdDebug(7001) <<
"Available methods: " << available <<
endl;
340KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
350 kdDebug(7001) <<
"KSimpleDirWatch deleted (FAM closed)" <<
endl;
354 if ( supports_inotify )
355 ::close( m_inotify_fd );
365void KSimpleDirWatchPrivate::slotActivated()
368 if ( supports_dnotify )
370 char dummy_buf[4096];
371 read(mPipe[0], &dummy_buf, 4096);
373 if (!rescan_timer.isActive())
374 rescan_timer.start(m_PollInterval,
true );
381 if ( !supports_inotify )
387 assert( m_inotify_fd > -1 );
388 ioctl( m_inotify_fd, FIONREAD, &pending );
390 while ( pending > 0 ) {
392 if ( pending > (
int)
sizeof( buf ) )
393 pending =
sizeof( buf );
395 pending = read( m_inotify_fd, buf, pending);
397 while ( pending > 0 ) {
398 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
399 pending -=
sizeof(
struct inotify_event ) +
event->len;
400 offset +=
sizeof(
struct inotify_event ) +
event->len;
404 path = TQFile::decodeName( TQCString(
event->name,
event->len ) );
406 if ( path.length() && isNoisyFile( path.latin1() ) )
409 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path <<
endl;
414 for ( EntryMap::Iterator it = m_mapEntries.begin();
415 it != m_mapEntries.end(); ++it ) {
417 if ( e->wd ==
event->wd ) {
420 if ( 1 || e->isDir) {
421 if(
event->mask & IN_DELETE_SELF) {
422 kdDebug(7001) <<
"-->got deleteself signal for " << e->path <<
endl;
423 e->m_status = NonExistent;
425 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
427 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
429 if (
event->mask & IN_IGNORED ) {
432 if (
event->mask & (IN_CREATE|IN_MOVED_TO) ) {
433 Entry *sub_entry = e->m_entries.first();
434 for(;sub_entry; sub_entry = e->m_entries.next())
435 if (sub_entry->path == e->path +
"/" + path)
break;
438 removeEntry(0,e->path, sub_entry);
439 KDE_struct_stat stat_buf;
440 TQCString tpath = TQFile::encodeName(path);
441 KDE_stat(tpath, &stat_buf);
448 if(!useINotify(sub_entry))
450 sub_entry->dirty =
true;
455 if (!rescan_timer.isActive())
456 rescan_timer.start(m_PollInterval,
true );
471void KSimpleDirWatchPrivate::Entry::propagate_dirty()
473 for (TQPtrListIterator<Entry> sub_entry (m_entries);
474 sub_entry.current(); ++sub_entry)
476 if (!sub_entry.current()->dirty)
478 sub_entry.current()->dirty =
true;
479 sub_entry.current()->propagate_dirty();
488void KSimpleDirWatchPrivate::Entry::addClient(
KSimpleDirWatch* instance)
490 Client* client = m_clients.first();
491 for(;client; client = m_clients.next())
492 if (client->instance == instance)
break;
502 client->watchingStopped =
instance->isStopped();
503 client->pending = NoChange;
505 m_clients.append(client);
508void KSimpleDirWatchPrivate::Entry::removeClient(
KSimpleDirWatch* instance)
510 Client* client = m_clients.first();
511 for(;client; client = m_clients.next())
512 if (client->instance == instance)
break;
516 if (client->count == 0) {
517 m_clients.removeRef(client);
524int KSimpleDirWatchPrivate::Entry::clients()
527 Client* client = m_clients.first();
528 for(;client; client = m_clients.next())
529 clients += client->count;
535KSimpleDirWatchPrivate::Entry* KSimpleDirWatchPrivate::entry(
const TQString& _path)
538 if (TQDir::isRelativePath(_path)) {
542 TQString path = _path;
544 if ( path.length() > 1 && path.right(1) ==
"/" )
545 path.truncate( path.length() - 1 );
547 EntryMap::Iterator it = m_mapEntries.find( path );
548 if ( it == m_mapEntries.end() )
555void KSimpleDirWatchPrivate::useFreq(Entry* e,
int newFreq)
560 if (e->freq < freq) {
562 if (timer->isActive()) timer->changeInterval(freq);
563 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" <<
endl;
570bool KSimpleDirWatchPrivate::useFAM(Entry* e)
572 if (!use_fam)
return false;
582 if (e->m_status == NonExistent) {
584 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
587 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
590 e->m_mode = UnknownMode;
594 kdDebug(7001) <<
" Setup FAM (Req "
595 << FAMREQUEST_GETREQNUM(&(e->fr))
596 <<
") for " << e->path <<
endl;
600 if (e->m_status == NonExistent) {
602 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
605 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
608 e->m_mode = UnknownMode;
613 kdDebug(7001) <<
" Setup FAM (Req "
614 << FAMREQUEST_GETREQNUM(&(e->fr))
615 <<
") for " << e->path <<
endl;
630bool KSimpleDirWatchPrivate::useDNotify(Entry* e)
634 if (!supports_dnotify)
return false;
636 e->m_mode = DNotifyMode;
639 if (e->m_status == Normal) {
640 int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
653 int fd2 = fcntl(fd, F_DUPFD, 128);
660 e->m_mode = UnknownMode;
664 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
666 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
667 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
669 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
670 fcntl(fd, F_NOTIFY, mask) < 0) {
672 kdDebug(7001) <<
"Not using Linux Directory Notifications."
674 supports_dnotify =
false;
676 e->m_mode = UnknownMode;
680 fd_Entry.replace(fd, e);
683 kdDebug(7001) <<
" Setup DNotify (fd " << fd
684 <<
") for " << e->path <<
endl;
687 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
693 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
702bool KSimpleDirWatchPrivate::useINotify( Entry* e )
706 if (!supports_inotify)
return false;
708 e->m_mode = INotifyMode;
710 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
712 mask |= IN_MODIFY|IN_ATTRIB;
717 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
718 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
721 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
722 TQFile::encodeName( e->path ), mask) ) > 0 )
725 if ( e->m_status == NonExistent ) {
727 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
729 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
737bool KSimpleDirWatchPrivate::useStat(Entry* e)
739 useFreq(e, m_PollInterval);
741 if (e->m_mode != StatMode) {
742 e->m_mode = StatMode;
745 if ( statEntries == 1 ) {
748 kdDebug(7001) <<
" Started Polling Timer, freq " << freq <<
endl;
752 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
753 <<
") for " << e->path <<
endl;
764void KSimpleDirWatchPrivate::addEntry(
KSimpleDirWatch* instance,
const TQString& _path,
765 Entry* sub_entry,
bool isDir)
767 TQString path = _path;
768 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
771 if ( path.length() > 1 && path.right(1) ==
"/" )
772 path.truncate( path.length() - 1 );
774 EntryMap::Iterator it = m_mapEntries.find( path );
775 if ( it != m_mapEntries.end() )
778 (*it).m_entries.append(sub_entry);
779 kdDebug(7001) <<
"Added already watched Entry " << path
780 <<
" (for " << sub_entry->path <<
")" <<
endl;
785 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
786 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
788 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
789 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
790 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
792 e->m_mode = UnknownMode;
793 fd_Entry.remove(e->dn_fd);
804 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
805 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
807 mask |= IN_MODIFY|IN_ATTRIB;
811 inotify_rm_watch (m_inotify_fd, e->wd);
812 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
819 (*it).addClient(instance);
820 kdDebug(7001) <<
"Added already watched Entry " << path
821 <<
" (now " << (*it).clients() <<
" clients)"
822 << TQString(TQString(
" [%1]").arg(
instance->name())) <<
endl;
829 KDE_struct_stat stat_buf;
830 TQCString tpath = TQFile::encodeName(path);
831 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
834 m_mapEntries.insert( path, newEntry );
836 Entry* e = &(m_mapEntries[path]);
839 e->isDir = S_ISDIR(stat_buf.st_mode);
841 if (e->isDir && !isDir)
842 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a directory. Use addDir!" <<
endl;
843 else if (!e->isDir && isDir)
844 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a file. Use addFile!" <<
endl;
846 e->m_ctime = stat_buf.st_ctime;
847 e->m_status = Normal;
848 e->m_nlink = stat_buf.st_nlink;
852 e->m_ctime = invalid_ctime;
853 e->m_status = NonExistent;
859 e->m_entries.append(sub_entry);
861 e->addClient(instance);
863 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
864 << (e->m_status == NonExistent ?
" NotExisting" :
"")
865 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
871 e->m_mode = UnknownMode;
874 if ( isNoisyFile( tpath ) )
878 if (useFAM(e))
return;
882 if (useINotify(e))
return;
886 if (useDNotify(e))
return;
894 const TQString& _path, Entry* sub_entry )
896 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry <<
endl;
897 Entry* e = entry(_path);
899 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" <<
endl;
904 e->m_entries.removeRef(sub_entry);
906 e->removeClient(instance);
908 if (e->m_clients.count() || e->m_entries.count()) {
909 kdDebug(7001) <<
"removeEntry: unwatched " << e->path <<
" " << _path <<
endl;
915 if (removeList.findRef(e)==-1)
916 removeList.append(e);
922 if (e->m_mode == FAMMode) {
923 if ( e->m_status == Normal) {
924 FAMCancelMonitor(&fc, &(e->fr) );
925 kdDebug(7001) <<
"Cancelled FAM (Req "
926 << FAMREQUEST_GETREQNUM(&(e->fr))
927 <<
") for " << e->path <<
endl;
931 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
933 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
939 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) <<
endl;
940 if (e->m_mode == INotifyMode) {
941 if ( e->m_status == Normal ) {
942 (void) inotify_rm_watch( m_inotify_fd, e->wd );
943 kdDebug(7001) <<
"Cancelled INotify (fd " <<
944 m_inotify_fd <<
", " << e->wd <<
945 ") for " << e->path <<
endl;
949 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
951 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
957 if (e->m_mode == DNotifyMode) {
959 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
963 if ( e->m_status == Normal) {
966 fd_Entry.remove(e->dn_fd);
968 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
969 <<
") for " << e->path <<
endl;
975 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
981 if (e->m_mode == StatMode) {
983 if ( statEntries == 0 ) {
989 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
990 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
991 << (instance ? TQString(TQString(
" [%1]").arg(
instance->name())) : TQString(
""))
993 m_mapEntries.remove( e->path );
1000void KSimpleDirWatchPrivate::removeEntries(
KSimpleDirWatch* instance )
1002 TQPtrList<Entry> list;
1003 int minfreq = 3600000;
1006 EntryMap::Iterator it = m_mapEntries.begin();
1007 for( ; it != m_mapEntries.end(); ++it ) {
1008 Client* c = (*it).m_clients.first();
1009 for(;c;c=(*it).m_clients.next())
1010 if (c->instance == instance)
break;
1013 list.append(&(*it));
1015 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1016 minfreq = (*it).freq;
1019 for(Entry* e=list.first();e;e=list.next())
1020 removeEntry(instance, e->path, 0);
1022 if (minfreq > freq) {
1025 if (timer->isActive()) timer->changeInterval(freq);
1026 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" <<
endl;
1031bool KSimpleDirWatchPrivate::stopEntryScan(
KSimpleDirWatch* instance, Entry* e)
1033 int stillWatching = 0;
1034 Client* c = e->m_clients.first();
1035 for(;c;c=e->m_clients.next()) {
1036 if (!instance || instance == c->instance)
1037 c->watchingStopped =
true;
1038 else if (!c->watchingStopped)
1039 stillWatching += c->count;
1043 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
1045 if (stillWatching == 0) {
1047 e->m_ctime = invalid_ctime;
1048 e->m_status = NonExistent;
1055bool KSimpleDirWatchPrivate::restartEntryScan(
KSimpleDirWatch* instance, Entry* e,
1058 int wasWatching = 0, newWatching = 0;
1059 Client* c = e->m_clients.first();
1060 for(;c;c=e->m_clients.next()) {
1061 if (!c->watchingStopped)
1062 wasWatching += c->count;
1063 else if (!instance || instance == c->instance) {
1064 c->watchingStopped =
false;
1065 newWatching += c->count;
1068 if (newWatching == 0)
1072 <<
" (now " << wasWatching+newWatching <<
" watchers)" <<
endl;
1077 if (wasWatching == 0) {
1079 KDE_struct_stat stat_buf;
1080 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1082 e->m_ctime = stat_buf.st_ctime;
1083 e->m_status = Normal;
1084 e->m_nlink = stat_buf.st_nlink;
1087 e->m_ctime = invalid_ctime;
1088 e->m_status = NonExistent;
1103 EntryMap::Iterator it = m_mapEntries.begin();
1104 for( ; it != m_mapEntries.end(); ++it )
1105 stopEntryScan(instance, &(*it));
1110 bool notify,
bool skippedToo )
1113 resetList(instance,skippedToo);
1115 EntryMap::Iterator it = m_mapEntries.begin();
1116 for( ; it != m_mapEntries.end(); ++it )
1117 restartEntryScan(instance, &(*it), notify);
1127 EntryMap::Iterator it = m_mapEntries.begin();
1128 for( ; it != m_mapEntries.end(); ++it ) {
1130 Client* c = (*it).m_clients.first();
1131 for(;c;c=(*it).m_clients.next())
1132 if (!c->watchingStopped || skippedToo)
1133 c->pending = NoChange;
1139int KSimpleDirWatchPrivate::scanEntry(Entry* e)
1142 if (e->m_mode == FAMMode) {
1144 if(!e->dirty)
return NoChange;
1150 if (e->m_mode == UnknownMode)
return NoChange;
1152#if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1153 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1155 if(!e->dirty)
return NoChange;
1156 kdDebug(7001) <<
"scanning " << e->path <<
" " << e->m_status <<
" " << e->m_ctime <<
endl;
1161 if (e->m_mode == StatMode) {
1166 e->msecLeft -= freq;
1167 if (e->msecLeft>0)
return NoChange;
1168 e->msecLeft += e->freq;
1171 KDE_struct_stat stat_buf;
1172 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1175 if (e->m_status == NonExistent) {
1176 e->m_ctime = stat_buf.st_ctime;
1177 e->m_status = Normal;
1178 e->m_nlink = stat_buf.st_nlink;
1182 if ( (e->m_ctime != invalid_ctime) &&
1183 ((stat_buf.st_ctime != e->m_ctime) ||
1184 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1185 e->m_ctime = stat_buf.st_ctime;
1186 e->m_nlink = stat_buf.st_nlink;
1195 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1197 e->m_status = NonExistent;
1201 e->m_ctime = invalid_ctime;
1203 e->m_status = NonExistent;
1212void KSimpleDirWatchPrivate::emitEvent(Entry* e,
int event,
const TQString &fileName)
1214 TQString path = e->path;
1215 if (!fileName.isEmpty()) {
1216 if (!TQDir::isRelativePath(fileName))
1220 path +=
"/" + fileName;
1221#elif defined(TQ_WS_WIN)
1223 path += TQDir::currentDirPath().left(2) +
"/" + fileName;
1227 TQPtrListIterator<Client> cit( e->m_clients );
1228 for ( ; cit.current(); ++cit )
1230 Client* c = cit.current();
1232 if (c->instance==0 || c->count==0)
continue;
1234 if (c->watchingStopped) {
1236 if (event == Changed)
1237 c->pending |=
event;
1238 else if (event == Created || event == Deleted)
1243 if (event == NoChange || event == Changed)
1244 event |= c->pending;
1245 c->pending = NoChange;
1246 if (event == NoChange)
continue;
1248 if (event & Deleted) {
1249 c->instance->setDeleted(path);
1254 if (event & Created) {
1255 c->instance->setCreated(path);
1259 if (event & Changed)
1260 c->instance->setDirty(path);
1265void KSimpleDirWatchPrivate::slotRemoveDelayed()
1268 delayRemove =
false;
1269 for(e=removeList.first();e;e=removeList.next())
1270 removeEntry(0, e->path, 0);
1277void KSimpleDirWatchPrivate::slotRescan()
1279 EntryMap::Iterator it;
1284 bool timerRunning = timer->isActive();
1292#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1293 TQPtrList<Entry> dList, cList;
1299 it = m_mapEntries.begin();
1300 for( ; it != m_mapEntries.end(); ++it )
1307 it = m_mapEntries.begin();
1308 for( ; it != m_mapEntries.end(); ++it )
1309 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
1310 (*it).propagate_dirty();
1313 it = m_mapEntries.begin();
1314 for( ; it != m_mapEntries.end(); ++it ) {
1316 if (!(*it).isValid())
continue;
1318 int ev = scanEntry( &(*it) );
1322 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1323 cList.append( &(*it) );
1324 if (! useINotify( &(*it) )) {
1331 if ((*it).m_mode == DNotifyMode) {
1332 if ((*it).isDir && (ev == Deleted)) {
1333 dList.append( &(*it) );
1337 ::close((*it).dn_fd);
1338 fd_Entry.remove((*it).dn_fd);
1343 else if ((*it).isDir && (ev == Created)) {
1345 if ( (*it).dn_fd == 0) {
1346 cList.append( &(*it) );
1347 if (! useDNotify( &(*it) )) {
1356 if ( ev != NoChange )
1357 emitEvent( &(*it), ev);
1361#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1364 for(e=dList.first();e;e=dList.next())
1365 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1368 for(e=cList.first();e;e=cList.next())
1369 removeEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e);
1375 TQTimer::singleShot(0,
this, TQ_SLOT(slotRemoveDelayed()));
1378bool KSimpleDirWatchPrivate::isNoisyFile(
const char * filename )
1381 if ( *filename ==
'.') {
1382 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1383 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1386 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1393void KSimpleDirWatchPrivate::famEventReceived()
1399 while(use_fam && FAMPending(&fc)) {
1400 if (FAMNextEvent(&fc, &fe) == -1) {
1401 kdWarning(7001) <<
"FAM connection problem, switching to polling."
1407 EntryMap::Iterator it;
1408 it = m_mapEntries.begin();
1409 for( ; it != m_mapEntries.end(); ++it )
1410 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1412 if (useINotify( &(*it) ))
continue;
1415 if (useDNotify( &(*it) ))
continue;
1424 TQTimer::singleShot(0,
this, TQ_SLOT(slotRemoveDelayed()));
1427void KSimpleDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1430 if ((fe->code == FAMExists) ||
1431 (fe->code == FAMEndExist) ||
1432 (fe->code == FAMAcknowledge))
return;
1434 if ( isNoisyFile( fe->filename ) )
1438 EntryMap::Iterator it = m_mapEntries.begin();
1439 for( ; it != m_mapEntries.end(); ++it )
1440 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1441 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1449 kdDebug(7001) <<
"Processing FAM event ("
1450 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1451 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1452 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1453 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1454 (fe->code == FAMCreated) ?
"FAMCreated" :
1455 (fe->code == FAMMoved) ?
"FAMMoved" :
1456 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1457 (fe->code == FAMExists) ?
"FAMExists" :
1458 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1459 <<
", " << fe->filename
1460 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1470 if (e->m_status == NonExistent) {
1471 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path <<
endl;
1477 if (!rescan_timer.isActive())
1478 rescan_timer.start(m_PollInterval,
true);
1486 if (!TQDir::isRelativePath(fe->filename))
1490 e->m_status = NonExistent;
1491 FAMCancelMonitor(&fc, &(e->fr) );
1492 kdDebug(7001) <<
"Cancelled FAMReq "
1493 << FAMREQUEST_GETREQNUM(&(e->fr))
1494 <<
" for " << e->path <<
endl;
1496 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1502 Entry *sub_entry = e->m_entries.first();
1503 for(;sub_entry; sub_entry = e->m_entries.next())
1504 if (sub_entry->path == e->path +
"/" + fe->filename)
break;
1505 if (sub_entry && sub_entry->isDir) {
1506 TQString path = e->path;
1507 removeEntry(0,e->path,sub_entry);
1508 sub_entry->m_status = Normal;
1509 if (!useFAM(sub_entry))
1512 if (!useINotify(sub_entry ))
1527void KSimpleDirWatchPrivate::famEventReceived() {}
1531void KSimpleDirWatchPrivate::statistics()
1533 EntryMap::Iterator it;
1536 if (m_mapEntries.count()==0) {
1540 it = m_mapEntries.begin();
1541 for( ; it != m_mapEntries.end(); ++it ) {
1543 kdDebug(7001) <<
" " << e->path <<
" ("
1544 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1545 << (e->isDir ?
"Dir":
"File") <<
", using "
1546 << ((e->m_mode == FAMMode) ?
"FAM" :
1547 (e->m_mode == INotifyMode) ?
"INotify" :
1548 (e->m_mode == DNotifyMode) ?
"DNotify" :
1549 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1552 Client* c = e->m_clients.first();
1553 for(;c; c = e->m_clients.next()) {
1555 if (c->watchingStopped) {
1556 if (c->pending & Deleted) pending +=
"deleted ";
1557 if (c->pending & Created) pending +=
"created ";
1558 if (c->pending & Changed) pending +=
"changed ";
1559 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1560 pending =
", stopped" + pending;
1562 kdDebug(7001) <<
" by " << c->instance->name()
1563 <<
" (" << c->count <<
" times)"
1566 if (e->m_entries.count()>0) {
1568 Entry* d = e->m_entries.first();
1569 for(;d; d = e->m_entries.next()) {
1571 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " <<
endl;
1597 return s_pSelf != 0;
1601 : TQObject(parent,name)
1604 static int nameCounter = 0;
1607 setName(TQString(TQString(
"KSimpleDirWatch-%1").arg(nameCounter)).ascii());
1611 dwp_self =
new KSimpleDirWatchPrivate;
1620 d->removeEntries(
this);
1632 bool watchFiles,
bool recursive)
1634 if (watchFiles || recursive) {
1635 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in KDE 3.x" <<
endl;
1637 if (d) d->addEntry(
this, _path, 0,
true);
1642 if (d) d->addEntry(
this, _path, 0,
false);
1647 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1650 return TQDateTime();
1653 result.setTime_t(e->m_ctime);
1659 if (d) d->removeEntry(
this, _path, 0);
1664 if (d) d->removeEntry(
this, _path, 0);
1670 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1671 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1679 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1682 return d->restartEntryScan(
this, e,
false);
1689 if (d) d->stopScan(
this);
1696 if (d) d->startScan(
this, notify, skippedToo);
1702 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1706 KSimpleDirWatchPrivate::Client* c = e->m_clients.first();
1707 for(;c;c=e->m_clients.next())
1708 if (c->instance ==
this)
return true;
1716 kdDebug(7001) <<
"KSimpleDirWatch not used" <<
endl;
1719 dwp_self->statistics();
1725 kdDebug(7001) << name() <<
" emitting created " << _file <<
endl;
1731 kdDebug(7001) << name() <<
" emitting dirty " << _file <<
endl;
1732 emit
dirty( _file );
1737 kdDebug(7001) << name() <<
" emitting deleted " << _file <<
endl;
1745 return KSimpleDirWatch::FAM;
1748 if (d->supports_inotify)
1749 return KSimpleDirWatch::INotify;
1752 if (d->supports_dnotify)
1753 return KSimpleDirWatch::DNotify;
1755 return KSimpleDirWatch::Stat;
1759#include "ksimpledirwatch.moc"
1760#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.
kdbgstream kdWarning(int area=0)
Returns a warning stream.
kndbgstream & endl(kndbgstream &s)
Does nothing.
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) TDE_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)