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> 56 #include <sys/filio.h> 62 #include <sys/syscall.h> 64 #include <linux/types.h> 67 #define _S390_BITOPS_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 92 #ifndef IN_DONT_FOLLOW 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 109 static KSimpleDirWatchPrivate* dwp_self = 0;
113 static int dnotify_signal = 0;
123 void 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);
144 static struct sigaction old_sigio_act;
149 void 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);
212 KSimpleDirWatchPrivate::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;
340 KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
350 kdDebug(7001) <<
"KSimpleDirWatch deleted (FAM closed)" <<
endl;
354 if ( supports_inotify )
355 ::close( m_inotify_fd );
365 void 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 );
471 void 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();
488 void KSimpleDirWatchPrivate::Entry::addClient(
KSimpleDirWatch* instance)
490 Client* client = m_clients.first();
491 for(;client; client = m_clients.next())
492 if (client->instance == instance)
break;
500 client->instance = instance;
502 client->watchingStopped = instance->
isStopped();
503 client->pending = NoChange;
505 m_clients.append(client);
508 void 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);
524 int KSimpleDirWatchPrivate::Entry::clients()
527 Client* client = m_clients.first();
528 for(;client; client = m_clients.next())
529 clients += client->count;
535 KSimpleDirWatchPrivate::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() )
555 void 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;
570 bool 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;
630 bool 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);
702 bool 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);
737 bool 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;
764 void 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(
""))
866 << (instance ? TQString(TQString(
" [%1]").arg(instance->
name())) : 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 ) {
985 kdDebug(7001) <<
" Stopped Polling Timer" <<
endl;
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 );
1000 void 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;
1031 bool 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;
1042 kdDebug(7001) << instance->name() <<
" stopped scanning " << e->path
1043 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
1045 if (stillWatching == 0) {
1047 e->m_ctime = invalid_ctime;
1048 e->m_status = NonExistent;
1055 bool 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)
1071 kdDebug(7001) << (instance ? instance->name() :
"all") <<
" restarted scanning " << e->path
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;
1139 int 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;
1212 void 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);
1265 void KSimpleDirWatchPrivate::slotRemoveDelayed()
1268 delayRemove =
false;
1269 for(e=removeList.first();e;e=removeList.next())
1270 removeEntry(0, e->path, 0);
1277 void 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()));
1378 bool 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;
1393 void 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()));
1427 void 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 ))
1527 void KSimpleDirWatchPrivate::famEventReceived() {}
1531 void KSimpleDirWatchPrivate::statistics()
1533 EntryMap::Iterator it;
1535 kdDebug(7001) <<
"Entries watched:" <<
endl;
1536 if (m_mapEntries.count()==0) {
1537 kdDebug(7001) <<
" None." <<
endl;
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) {
1567 kdDebug(7001) <<
" dependent entries:" <<
endl;
1568 Entry* d = e->m_entries.first();
1569 for(;d; d = e->m_entries.next()) {
1570 kdDebug(7001) <<
" " << d <<
endl;
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" 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.