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> 61 #include <sys/filio.h> 63 #include <linux/types.h> 66 #define _S390_BITOPS_H 67 #include <sys/inotify.h> 69 #ifndef __NR_inotify_init 71 #define __NR_inotify_init 291 72 #define __NR_inotify_add_watch 292 73 #define __NR_inotify_rm_watch 293 76 #define __NR_inotify_init 275 77 #define __NR_inotify_add_watch 276 78 #define __NR_inotify_rm_watch 277 80 #if defined(__x86_64__) 81 #define __NR_inotify_init 253 82 #define __NR_inotify_add_watch 254 83 #define __NR_inotify_rm_watch 255 88 #define IN_ONLYDIR 0x01000000 91 #ifndef IN_DONT_FOLLOW 92 #define IN_DONT_FOLLOW 0x02000000 96 #define IN_MOVE_SELF 0x00000800 101 #include <sys/utsname.h> 103 #include "ksimpledirwatch.h" 104 #include "ksimpledirwatch_p.h" 106 #define NO_NOTIFY (time_t) 0 108 static KSimpleDirWatchPrivate* dwp_self = 0;
112 static int dnotify_signal = 0;
122 void KSimpleDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
124 if (!dwp_self)
return;
128 int saved_errno = errno;
130 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
135 if(e && e->dn_fd == si->si_fd)
139 write(dwp_self->mPipe[1], &c, 1);
143 static struct sigaction old_sigio_act;
148 void KSimpleDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
154 int saved_errno = errno;
156 dwp_self->rescan_all =
true;
158 write(dwp_self->mPipe[1], &c, 1);
164 if (old_sigio_act.sa_flags & SA_SIGINFO)
166 if (old_sigio_act.sa_sigaction)
167 (*old_sigio_act.sa_sigaction)(sig, si, p);
171 if ((old_sigio_act.sa_handler != SIG_DFL) &&
172 (old_sigio_act.sa_handler != SIG_IGN))
173 (*old_sigio_act.sa_handler)(sig);
211 KSimpleDirWatchPrivate::KSimpleDirWatchPrivate()
212 : rescan_timer(0,
"KSimpleDirWatchPrivate::rescan_timer")
214 timer =
new TQTimer(
this,
"KSimpleDirWatchPrivate::timer");
215 connect (timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
222 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
223 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
225 TQString available(
"Stat");
229 connect(&rescan_timer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(slotRescan()));
233 if (FAMOpen(&fc) ==0) {
234 available +=
", FAM";
236 sn =
new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
237 TQSocketNotifier::Read,
this);
238 connect( sn, TQT_SIGNAL(activated(
int)),
239 this, TQT_SLOT(famEventReceived()) );
242 kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" <<
endl;
248 supports_inotify =
true;
250 m_inotify_fd = inotify_init();
252 if ( m_inotify_fd <= 0 ) {
253 kdDebug(7001) <<
"Can't use Inotify, kernel doesn't support it" <<
endl;
254 supports_inotify =
false;
259 int major, minor, patch;
261 supports_inotify =
false;
262 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
263 supports_inotify =
false;
264 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
265 kdDebug(7001) <<
"Can't use INotify, Linux kernel too old" <<
endl;
266 supports_inotify =
false;
270 if ( supports_inotify ) {
271 available +=
", Inotify";
272 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
274 mSn =
new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read,
this );
275 connect( mSn, TQT_SIGNAL(activated(
int )),
this, TQT_SLOT( slotActivated() ) );
283 supports_dnotify = !supports_inotify;
286 supports_dnotify =
true;
290 int major, minor, patch;
292 supports_dnotify =
false;
293 else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
294 supports_dnotify =
false;
295 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
296 kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" <<
endl;
297 supports_dnotify =
false;
300 if( supports_dnotify ) {
301 available +=
", DNotify";
304 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
305 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
306 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
307 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
308 mSn =
new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read,
this);
309 connect(mSn, TQT_SIGNAL(activated(
int)),
this, TQT_SLOT(slotActivated()));
311 if ( dnotify_signal == 0 )
313 dnotify_signal = SIGRTMIN + 8;
315 struct sigaction act;
316 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_handler;
317 sigemptyset(&act.sa_mask);
318 act.sa_flags = SA_SIGINFO;
320 act.sa_flags |= SA_RESTART;
322 sigaction(dnotify_signal, &act, NULL);
324 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_sigio_handler;
325 sigaction(SIGIO, &act, &old_sigio_act);
335 kdDebug(7001) <<
"Available methods: " << available <<
endl;
339 KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
349 kdDebug(7001) <<
"KSimpleDirWatch deleted (FAM closed)" <<
endl;
353 if ( supports_inotify )
354 ::close( m_inotify_fd );
364 void KSimpleDirWatchPrivate::slotActivated()
367 if ( supports_dnotify )
369 char dummy_buf[4096];
370 read(mPipe[0], &dummy_buf, 4096);
372 if (!rescan_timer.isActive())
373 rescan_timer.start(m_PollInterval,
true );
380 if ( !supports_inotify )
386 assert( m_inotify_fd > -1 );
387 ioctl( m_inotify_fd, FIONREAD, &pending );
389 while ( pending > 0 ) {
391 if ( pending > (
int)
sizeof( buf ) )
392 pending =
sizeof( buf );
394 pending = read( m_inotify_fd, buf, pending);
396 while ( pending > 0 ) {
397 struct inotify_event *
event = (
struct inotify_event *) &buf[offset];
398 pending -=
sizeof(
struct inotify_event ) +
event->len;
399 offset +=
sizeof(
struct inotify_event ) +
event->len;
403 path = TQFile::decodeName( TQCString(
event->name,
event->len ) );
405 if ( path.length() && isNoisyFile( path.latin1() ) )
408 kdDebug(7001) <<
"ev wd: " <<
event->wd <<
" mask " <<
event->mask <<
" path: " << path <<
endl;
413 for ( EntryMap::Iterator it = m_mapEntries.begin();
414 it != m_mapEntries.end(); ++it ) {
416 if ( e->wd ==
event->wd ) {
419 if ( 1 || e->isDir) {
420 if(
event->mask & IN_DELETE_SELF) {
421 kdDebug(7001) <<
"-->got deleteself signal for " << e->path <<
endl;
422 e->m_status = NonExistent;
424 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
426 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
428 if (
event->mask & IN_IGNORED ) {
431 if (
event->mask & (IN_CREATE|IN_MOVED_TO) ) {
432 Entry *sub_entry = e->m_entries.first();
433 for(;sub_entry; sub_entry = e->m_entries.next())
434 if (sub_entry->path == e->path +
"/" + path)
break;
437 removeEntry(0,e->path, sub_entry);
438 KDE_struct_stat stat_buf;
439 TQCString tpath = TQFile::encodeName(path);
440 KDE_stat(tpath, &stat_buf);
447 if(!useINotify(sub_entry))
449 sub_entry->dirty =
true;
454 if (!rescan_timer.isActive())
455 rescan_timer.start(m_PollInterval,
true );
470 void KSimpleDirWatchPrivate::Entry::propagate_dirty()
472 for (TQPtrListIterator<Entry> sub_entry (m_entries);
473 sub_entry.current(); ++sub_entry)
475 if (!sub_entry.current()->dirty)
477 sub_entry.current()->dirty =
true;
478 sub_entry.current()->propagate_dirty();
487 void KSimpleDirWatchPrivate::Entry::addClient(
KSimpleDirWatch* instance)
489 Client* client = m_clients.first();
490 for(;client; client = m_clients.next())
491 if (client->instance == instance)
break;
499 client->instance = instance;
501 client->watchingStopped = instance->
isStopped();
502 client->pending = NoChange;
504 m_clients.append(client);
507 void KSimpleDirWatchPrivate::Entry::removeClient(
KSimpleDirWatch* instance)
509 Client* client = m_clients.first();
510 for(;client; client = m_clients.next())
511 if (client->instance == instance)
break;
515 if (client->count == 0) {
516 m_clients.removeRef(client);
523 int KSimpleDirWatchPrivate::Entry::clients()
526 Client* client = m_clients.first();
527 for(;client; client = m_clients.next())
528 clients += client->count;
534 KSimpleDirWatchPrivate::Entry* KSimpleDirWatchPrivate::entry(
const TQString& _path)
537 if (TQDir::isRelativePath(_path)) {
541 TQString path = _path;
543 if ( path.length() > 1 && path.right(1) ==
"/" )
544 path.truncate( path.length() - 1 );
546 EntryMap::Iterator it = m_mapEntries.find( path );
547 if ( it == m_mapEntries.end() )
554 void KSimpleDirWatchPrivate::useFreq(Entry* e,
int newFreq)
559 if (e->freq < freq) {
561 if (timer->isActive()) timer->changeInterval(freq);
562 kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" <<
endl;
569 bool KSimpleDirWatchPrivate::useFAM(Entry* e)
571 if (!use_fam)
return false;
581 if (e->m_status == NonExistent) {
583 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
586 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
589 e->m_mode = UnknownMode;
593 kdDebug(7001) <<
" Setup FAM (Req " 594 << FAMREQUEST_GETREQNUM(&(e->fr))
595 <<
") for " << e->path <<
endl;
599 if (e->m_status == NonExistent) {
601 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
604 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
607 e->m_mode = UnknownMode;
612 kdDebug(7001) <<
" Setup FAM (Req " 613 << FAMREQUEST_GETREQNUM(&(e->fr))
614 <<
") for " << e->path <<
endl;
629 bool KSimpleDirWatchPrivate::useDNotify(Entry* e)
633 if (!supports_dnotify)
return false;
635 e->m_mode = DNotifyMode;
638 if (e->m_status == Normal) {
639 int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
652 int fd2 = fcntl(fd, F_DUPFD, 128);
659 e->m_mode = UnknownMode;
663 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
665 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
666 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
668 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
669 fcntl(fd, F_NOTIFY, mask) < 0) {
671 kdDebug(7001) <<
"Not using Linux Directory Notifications." 673 supports_dnotify =
false;
675 e->m_mode = UnknownMode;
679 fd_Entry.replace(fd, e);
682 kdDebug(7001) <<
" Setup DNotify (fd " << fd
683 <<
") for " << e->path <<
endl;
686 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
692 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
701 bool KSimpleDirWatchPrivate::useINotify( Entry* e )
705 if (!supports_inotify)
return false;
707 e->m_mode = INotifyMode;
709 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
711 mask |= IN_MODIFY|IN_ATTRIB;
716 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
717 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB;
break; }
720 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
721 TQFile::encodeName( e->path ), mask) ) > 0 )
724 if ( e->m_status == NonExistent ) {
726 addEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e,
true);
728 addEntry(0, TQFileInfo(e->path).dirPath(
true), e,
true);
736 bool KSimpleDirWatchPrivate::useStat(Entry* e)
738 useFreq(e, m_PollInterval);
740 if (e->m_mode != StatMode) {
741 e->m_mode = StatMode;
744 if ( statEntries == 1 ) {
747 kdDebug(7001) <<
" Started Polling Timer, freq " << freq <<
endl;
751 kdDebug(7001) <<
" Setup Stat (freq " << e->freq
752 <<
") for " << e->path <<
endl;
763 void KSimpleDirWatchPrivate::addEntry(
KSimpleDirWatch* instance,
const TQString& _path,
764 Entry* sub_entry,
bool isDir)
766 TQString path = _path;
767 if (path.startsWith(
"/dev/") || (path ==
"/dev"))
770 if ( path.length() > 1 && path.right(1) ==
"/" )
771 path.truncate( path.length() - 1 );
773 EntryMap::Iterator it = m_mapEntries.find( path );
774 if ( it != m_mapEntries.end() )
777 (*it).m_entries.append(sub_entry);
778 kdDebug(7001) <<
"Added already watched Entry " << path
779 <<
" (for " << sub_entry->path <<
")" <<
endl;
784 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
785 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
787 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
788 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
789 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
791 e->m_mode = UnknownMode;
792 fd_Entry.remove(e->dn_fd);
803 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
804 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
806 mask |= IN_MODIFY|IN_ATTRIB;
810 inotify_rm_watch (m_inotify_fd, e->wd);
811 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
818 (*it).addClient(instance);
819 kdDebug(7001) <<
"Added already watched Entry " << path
820 <<
" (now " << (*it).clients() <<
" clients)" 821 << TQString(TQString(
" [%1]").arg(instance->name())) <<
endl;
828 KDE_struct_stat stat_buf;
829 TQCString tpath = TQFile::encodeName(path);
830 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
833 m_mapEntries.insert( path, newEntry );
835 Entry* e = &(m_mapEntries[path]);
838 e->isDir = S_ISDIR(stat_buf.st_mode);
840 if (e->isDir && !isDir)
841 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a directory. Use addDir!" <<
endl;
842 else if (!e->isDir && isDir)
843 kdWarning() <<
"KSimpleDirWatch: " << path <<
" is a file. Use addFile!" <<
endl;
845 e->m_ctime = stat_buf.st_ctime;
846 e->m_status = Normal;
847 e->m_nlink = stat_buf.st_nlink;
851 e->m_ctime = invalid_ctime;
852 e->m_status = NonExistent;
858 e->m_entries.append(sub_entry);
860 e->addClient(instance);
862 kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
863 << (e->m_status == NonExistent ?
" NotExisting" :
"")
864 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
865 << (instance ? TQString(TQString(
" [%1]").arg(instance->
name())) : TQString(
""))
870 e->m_mode = UnknownMode;
873 if ( isNoisyFile( tpath ) )
877 if (useFAM(e))
return;
881 if (useINotify(e))
return;
885 if (useDNotify(e))
return;
893 const TQString& _path, Entry* sub_entry )
895 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry for '" << _path <<
"' sub_entry: " << sub_entry <<
endl;
896 Entry* e = entry(_path);
898 kdDebug(7001) <<
"KSimpleDirWatchPrivate::removeEntry can't handle '" << _path <<
"'" <<
endl;
903 e->m_entries.removeRef(sub_entry);
905 e->removeClient(instance);
907 if (e->m_clients.count() || e->m_entries.count()) {
908 kdDebug(7001) <<
"removeEntry: unwatched " << e->path <<
" " << _path <<
endl;
914 if (removeList.findRef(e)==-1)
915 removeList.append(e);
921 if (e->m_mode == FAMMode) {
922 if ( e->m_status == Normal) {
923 FAMCancelMonitor(&fc, &(e->fr) );
924 kdDebug(7001) <<
"Cancelled FAM (Req " 925 << FAMREQUEST_GETREQNUM(&(e->fr))
926 <<
") for " << e->path <<
endl;
930 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
932 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
938 kdDebug(7001) <<
"inotify remove " << ( e->m_mode == INotifyMode ) <<
" " << ( e->m_status == Normal ) <<
endl;
939 if (e->m_mode == INotifyMode) {
940 if ( e->m_status == Normal ) {
941 (void) inotify_rm_watch( m_inotify_fd, e->wd );
942 kdDebug(7001) <<
"Cancelled INotify (fd " <<
943 m_inotify_fd <<
", " << e->wd <<
944 ") for " << e->path <<
endl;
948 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
950 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
956 if (e->m_mode == DNotifyMode) {
958 removeEntry(0, TQFileInfo(e->path).dirPath(
true), e);
962 if ( e->m_status == Normal) {
965 fd_Entry.remove(e->dn_fd);
967 kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
968 <<
") for " << e->path <<
endl;
974 removeEntry(0, TQDir::cleanDirPath(e->path+
"/.."), e);
980 if (e->m_mode == StatMode) {
982 if ( statEntries == 0 ) {
984 kdDebug(7001) <<
" Stopped Polling Timer" <<
endl;
988 kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
989 << (sub_entry ? TQString(TQString(
" for %1").arg(sub_entry->path)) : TQString(
""))
990 << (instance ? TQString(TQString(
" [%1]").arg(instance->name())) : TQString(
""))
992 m_mapEntries.remove( e->path );
999 void KSimpleDirWatchPrivate::removeEntries(
KSimpleDirWatch* instance )
1001 TQPtrList<Entry> list;
1002 int minfreq = 3600000;
1005 EntryMap::Iterator it = m_mapEntries.begin();
1006 for( ; it != m_mapEntries.end(); ++it ) {
1007 Client* c = (*it).m_clients.first();
1008 for(;c;c=(*it).m_clients.next())
1009 if (c->instance == instance)
break;
1012 list.append(&(*it));
1014 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1015 minfreq = (*it).freq;
1018 for(Entry* e=list.first();e;e=list.next())
1019 removeEntry(instance, e->path, 0);
1021 if (minfreq > freq) {
1024 if (timer->isActive()) timer->changeInterval(freq);
1025 kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" <<
endl;
1030 bool KSimpleDirWatchPrivate::stopEntryScan(
KSimpleDirWatch* instance, Entry* e)
1032 int stillWatching = 0;
1033 Client* c = e->m_clients.first();
1034 for(;c;c=e->m_clients.next()) {
1035 if (!instance || instance == c->instance)
1036 c->watchingStopped =
true;
1037 else if (!c->watchingStopped)
1038 stillWatching += c->count;
1041 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path
1042 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
1044 if (stillWatching == 0) {
1046 e->m_ctime = invalid_ctime;
1047 e->m_status = NonExistent;
1054 bool KSimpleDirWatchPrivate::restartEntryScan(
KSimpleDirWatch* instance, Entry* e,
1057 int wasWatching = 0, newWatching = 0;
1058 Client* c = e->m_clients.first();
1059 for(;c;c=e->m_clients.next()) {
1060 if (!c->watchingStopped)
1061 wasWatching += c->count;
1062 else if (!instance || instance == c->instance) {
1063 c->watchingStopped =
false;
1064 newWatching += c->count;
1067 if (newWatching == 0)
1070 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path
1071 <<
" (now " << wasWatching+newWatching <<
" watchers)" << endl;
1076 if (wasWatching == 0) {
1078 KDE_struct_stat stat_buf;
1079 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1081 e->m_ctime = stat_buf.st_ctime;
1082 e->m_status = Normal;
1083 e->m_nlink = stat_buf.st_nlink;
1086 e->m_ctime = invalid_ctime;
1087 e->m_status = NonExistent;
1102 EntryMap::Iterator it = m_mapEntries.begin();
1103 for( ; it != m_mapEntries.end(); ++it )
1104 stopEntryScan(instance, &(*it));
1109 bool notify,
bool skippedToo )
1112 resetList(instance,skippedToo);
1114 EntryMap::Iterator it = m_mapEntries.begin();
1115 for( ; it != m_mapEntries.end(); ++it )
1116 restartEntryScan(instance, &(*it), notify);
1126 EntryMap::Iterator it = m_mapEntries.begin();
1127 for( ; it != m_mapEntries.end(); ++it ) {
1129 Client* c = (*it).m_clients.first();
1130 for(;c;c=(*it).m_clients.next())
1131 if (!c->watchingStopped || skippedToo)
1132 c->pending = NoChange;
1138 int KSimpleDirWatchPrivate::scanEntry(Entry* e)
1141 if (e->m_mode == FAMMode) {
1143 if(!e->dirty)
return NoChange;
1149 if (e->m_mode == UnknownMode)
return NoChange;
1151 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY ) 1152 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1154 if(!e->dirty)
return NoChange;
1155 kdDebug(7001) <<
"scanning " << e->path <<
" " << e->m_status <<
" " << e->m_ctime <<
endl;
1160 if (e->m_mode == StatMode) {
1165 e->msecLeft -= freq;
1166 if (e->msecLeft>0)
return NoChange;
1167 e->msecLeft += e->freq;
1170 KDE_struct_stat stat_buf;
1171 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1174 if (e->m_status == NonExistent) {
1175 e->m_ctime = stat_buf.st_ctime;
1176 e->m_status = Normal;
1177 e->m_nlink = stat_buf.st_nlink;
1181 if ( (e->m_ctime != invalid_ctime) &&
1182 ((stat_buf.st_ctime != e->m_ctime) ||
1183 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1184 e->m_ctime = stat_buf.st_ctime;
1185 e->m_nlink = stat_buf.st_nlink;
1194 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1196 e->m_status = NonExistent;
1200 e->m_ctime = invalid_ctime;
1202 e->m_status = NonExistent;
1211 void KSimpleDirWatchPrivate::emitEvent(Entry* e,
int event,
const TQString &fileName)
1213 TQString path = e->path;
1214 if (!fileName.isEmpty()) {
1215 if (!TQDir::isRelativePath(fileName))
1219 path +=
"/" + fileName;
1220 #elif defined(Q_WS_WIN) 1222 path += TQDir::currentDirPath().left(2) +
"/" + fileName;
1226 TQPtrListIterator<Client> cit( e->m_clients );
1227 for ( ; cit.current(); ++cit )
1229 Client* c = cit.current();
1231 if (c->instance==0 || c->count==0)
continue;
1233 if (c->watchingStopped) {
1235 if (event == Changed)
1236 c->pending |= event;
1237 else if (event == Created || event == Deleted)
1242 if (event == NoChange || event == Changed)
1243 event |= c->pending;
1244 c->pending = NoChange;
1245 if (event == NoChange)
continue;
1247 if (event & Deleted) {
1248 c->instance->setDeleted(path);
1253 if (event & Created) {
1254 c->instance->setCreated(path);
1258 if (event & Changed)
1259 c->instance->setDirty(path);
1264 void KSimpleDirWatchPrivate::slotRemoveDelayed()
1267 delayRemove =
false;
1268 for(e=removeList.first();e;e=removeList.next())
1269 removeEntry(0, e->path, 0);
1276 void KSimpleDirWatchPrivate::slotRescan()
1278 EntryMap::Iterator it;
1283 bool timerRunning = timer->isActive();
1291 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) 1292 TQPtrList<Entry> dList, cList;
1298 it = m_mapEntries.begin();
1299 for( ; it != m_mapEntries.end(); ++it )
1306 it = m_mapEntries.begin();
1307 for( ; it != m_mapEntries.end(); ++it )
1308 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
1309 (*it).propagate_dirty();
1312 it = m_mapEntries.begin();
1313 for( ; it != m_mapEntries.end(); ++it ) {
1315 if (!(*it).isValid())
continue;
1317 int ev = scanEntry( &(*it) );
1321 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1322 cList.append( &(*it) );
1323 if (! useINotify( &(*it) )) {
1330 if ((*it).m_mode == DNotifyMode) {
1331 if ((*it).isDir && (ev == Deleted)) {
1332 dList.append( &(*it) );
1336 ::close((*it).dn_fd);
1337 fd_Entry.remove((*it).dn_fd);
1342 else if ((*it).isDir && (ev == Created)) {
1344 if ( (*it).dn_fd == 0) {
1345 cList.append( &(*it) );
1346 if (! useDNotify( &(*it) )) {
1355 if ( ev != NoChange )
1356 emitEvent( &(*it), ev);
1360 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) 1363 for(e=dList.first();e;e=dList.next())
1364 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1367 for(e=cList.first();e;e=cList.next())
1368 removeEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e);
1374 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1377 bool KSimpleDirWatchPrivate::isNoisyFile(
const char * filename )
1380 if ( *filename ==
'.') {
1381 if (strncmp(filename,
".X.err", 6) == 0)
return true;
1382 if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
1385 if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
1392 void KSimpleDirWatchPrivate::famEventReceived()
1398 while(use_fam && FAMPending(&fc)) {
1399 if (FAMNextEvent(&fc, &fe) == -1) {
1400 kdWarning(7001) <<
"FAM connection problem, switching to polling." 1406 EntryMap::Iterator it;
1407 it = m_mapEntries.begin();
1408 for( ; it != m_mapEntries.end(); ++it )
1409 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1411 if (useINotify( &(*it) ))
continue;
1414 if (useDNotify( &(*it) ))
continue;
1423 TQTimer::singleShot(0,
this, TQT_SLOT(slotRemoveDelayed()));
1426 void KSimpleDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1429 if ((fe->code == FAMExists) ||
1430 (fe->code == FAMEndExist) ||
1431 (fe->code == FAMAcknowledge))
return;
1433 if ( isNoisyFile( fe->filename ) )
1437 EntryMap::Iterator it = m_mapEntries.begin();
1438 for( ; it != m_mapEntries.end(); ++it )
1439 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1440 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1448 kdDebug(7001) <<
"Processing FAM event (" 1449 << ((fe->code == FAMChanged) ?
"FAMChanged" :
1450 (fe->code == FAMDeleted) ?
"FAMDeleted" :
1451 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
1452 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
1453 (fe->code == FAMCreated) ?
"FAMCreated" :
1454 (fe->code == FAMMoved) ?
"FAMMoved" :
1455 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
1456 (fe->code == FAMExists) ?
"FAMExists" :
1457 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
1458 <<
", " << fe->filename
1459 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1469 if (e->m_status == NonExistent) {
1470 kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path <<
endl;
1476 if (!rescan_timer.isActive())
1477 rescan_timer.start(m_PollInterval,
true);
1485 if (!TQDir::isRelativePath(fe->filename))
1489 e->m_status = NonExistent;
1490 FAMCancelMonitor(&fc, &(e->fr) );
1491 kdDebug(7001) <<
"Cancelled FAMReq " 1492 << FAMREQUEST_GETREQNUM(&(e->fr))
1493 <<
" for " << e->path <<
endl;
1495 addEntry(0, TQDir::cleanDirPath( e->path+
"/.."), e,
true);
1501 Entry *sub_entry = e->m_entries.first();
1502 for(;sub_entry; sub_entry = e->m_entries.next())
1503 if (sub_entry->path == e->path +
"/" + fe->filename)
break;
1504 if (sub_entry && sub_entry->isDir) {
1505 TQString path = e->path;
1506 removeEntry(0,e->path,sub_entry);
1507 sub_entry->m_status = Normal;
1508 if (!useFAM(sub_entry))
1510 if (!useINotify(sub_entry ))
1522 void KSimpleDirWatchPrivate::famEventReceived() {}
1526 void KSimpleDirWatchPrivate::statistics()
1528 EntryMap::Iterator it;
1530 kdDebug(7001) <<
"Entries watched:" <<
endl;
1531 if (m_mapEntries.count()==0) {
1532 kdDebug(7001) <<
" None." <<
endl;
1535 it = m_mapEntries.begin();
1536 for( ; it != m_mapEntries.end(); ++it ) {
1538 kdDebug(7001) <<
" " << e->path <<
" (" 1539 << ((e->m_status==Normal)?
"":
"Nonexistent ")
1540 << (e->isDir ?
"Dir":
"File") <<
", using " 1541 << ((e->m_mode == FAMMode) ?
"FAM" :
1542 (e->m_mode == INotifyMode) ?
"INotify" :
1543 (e->m_mode == DNotifyMode) ?
"DNotify" :
1544 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
1547 Client* c = e->m_clients.first();
1548 for(;c; c = e->m_clients.next()) {
1550 if (c->watchingStopped) {
1551 if (c->pending & Deleted) pending +=
"deleted ";
1552 if (c->pending & Created) pending +=
"created ";
1553 if (c->pending & Changed) pending +=
"changed ";
1554 if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
1555 pending =
", stopped" + pending;
1557 kdDebug(7001) <<
" by " << c->instance->name()
1558 <<
" (" << c->count <<
" times)" 1561 if (e->m_entries.count()>0) {
1562 kdDebug(7001) <<
" dependent entries:" <<
endl;
1563 Entry* d = e->m_entries.first();
1564 for(;d; d = e->m_entries.next()) {
1565 kdDebug(7001) <<
" " << d <<
endl;
1566 kdDebug(7001) <<
" " << d->path <<
" (" << d <<
") " <<
endl;
1592 return s_pSelf != 0;
1596 : TQObject(parent,name)
1599 static int nameCounter = 0;
1602 setName(TQString(TQString(
"KSimpleDirWatch-%1").arg(nameCounter)).ascii());
1606 dwp_self =
new KSimpleDirWatchPrivate;
1615 d->removeEntries(
this);
1627 bool watchFiles,
bool recursive)
1629 if (watchFiles || recursive) {
1630 kdDebug(7001) <<
"addDir - recursive/watchFiles not supported yet in KDE 3.x" <<
endl;
1632 if (d) d->addEntry(
this, _path, 0,
true);
1637 if (d) d->addEntry(
this, _path, 0,
false);
1642 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1645 return TQDateTime();
1648 result.setTime_t(e->m_ctime);
1654 if (d) d->removeEntry(
this, _path, 0);
1659 if (d) d->removeEntry(
this, _path, 0);
1665 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1666 if (e && e->isDir)
return d->stopEntryScan(
this, e);
1674 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1677 return d->restartEntryScan(
this, e,
false);
1684 if (d) d->stopScan(
this);
1691 if (d) d->startScan(
this, notify, skippedToo);
1697 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1701 KSimpleDirWatchPrivate::Client* c = e->m_clients.first();
1702 for(;c;c=e->m_clients.next())
1703 if (c->instance ==
this)
return true;
1711 kdDebug(7001) <<
"KSimpleDirWatch not used" <<
endl;
1714 dwp_self->statistics();
1720 kdDebug(7001) << name() <<
" emitting created " << _file <<
endl;
1726 kdDebug(7001) << name() <<
" emitting dirty " << _file <<
endl;
1727 emit
dirty( _file );
1732 kdDebug(7001) << name() <<
" emitting deleted " << _file <<
endl;
1740 return KSimpleDirWatch::FAM;
1743 if (d->supports_inotify)
1744 return KSimpleDirWatch::INotify;
1747 if (d->supports_dnotify)
1748 return KSimpleDirWatch::DNotify;
1750 return KSimpleDirWatch::Stat;
1754 #include "ksimpledirwatch.moc" 1755 #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.
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.
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 isStopped()
Is scanning stopped? After creation of a KSimpleDirWatch instance, this is false. ...
bool contains(const TQString &path) const
Check if a directory is being watched by this KSimpleDirWatch instance.
void deleted(const TQString &path)
Emitted when a file or directory is deleted.