TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kdirwatch.cpp 46KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774
  1. // -*- c-basic-offset: 2 -*-
  2. /* This file is part of the KDE libraries
  3. Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License version 2 as published by the Free Software Foundation.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Library General Public License for more details.
  11. You should have received a copy of the GNU Library General Public License
  12. along with this library; see the file COPYING.LIB. If not, write to
  13. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  14. Boston, MA 02110-1301, USA.
  15. */
  16. // CHANGES:
  17. // Oct 4, 2005 - Inotify support (Dirk Mueller)
  18. // Februar 2002 - Add file watching and remote mount check for STAT
  19. // Mar 30, 2001 - Native support for Linux dir change notification.
  20. // Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
  21. // May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)1
  22. // May 23. 1998 - Removed static pointer - you can have more instances.
  23. // It was Needed for KRegistry. KDirWatch now emits signals and doesn't
  24. // call (or need) KFM. No more URL's - just plain paths. (sven)
  25. // Mar 29. 1998 - added docs, stop/restart for particular Dirs and
  26. // deep copies for list of dirs. (sven)
  27. // Mar 28. 1998 - Created. (sven)
  28. #include <config.h>
  29. #include <errno.h>
  30. #ifdef HAVE_DNOTIFY
  31. #include <unistd.h>
  32. #include <time.h>
  33. #include <fcntl.h>
  34. #include <signal.h>
  35. #include <errno.h>
  36. #endif
  37. #include <sys/stat.h>
  38. #include <assert.h>
  39. #include <tqdir.h>
  40. #include <tqfile.h>
  41. #include <tqintdict.h>
  42. #include <tqptrlist.h>
  43. #include <tqsocketnotifier.h>
  44. #include <tqstringlist.h>
  45. #include <tqtimer.h>
  46. #include <kapplication.h>
  47. #include <kdebug.h>
  48. #include <tdeconfig.h>
  49. #include <kglobal.h>
  50. #include <kstaticdeleter.h>
  51. #include <kde_file.h>
  52. // debug
  53. #include <sys/ioctl.h>
  54. #ifdef HAVE_INOTIFY
  55. #include <unistd.h>
  56. #include <fcntl.h>
  57. #include <sys/syscall.h>
  58. #include <linux/types.h>
  59. // Linux kernel headers are documented to not compile
  60. #define _S390_BITOPS_H
  61. #include <sys/inotify.h>
  62. #ifndef __NR_inotify_init
  63. #if defined(__i386__)
  64. #define __NR_inotify_init 291
  65. #define __NR_inotify_add_watch 292
  66. #define __NR_inotify_rm_watch 293
  67. #endif
  68. #if defined(__PPC__)
  69. #define __NR_inotify_init 275
  70. #define __NR_inotify_add_watch 276
  71. #define __NR_inotify_rm_watch 277
  72. #endif
  73. #if defined(__x86_64__)
  74. #define __NR_inotify_init 253
  75. #define __NR_inotify_add_watch 254
  76. #define __NR_inotify_rm_watch 255
  77. #endif
  78. #endif
  79. #ifndef IN_ONLYDIR
  80. #define IN_ONLYDIR 0x01000000
  81. #endif
  82. #ifndef IN_DONT_FOLLOW
  83. #define IN_DONT_FOLLOW 0x02000000
  84. #endif
  85. #ifndef IN_MOVE_SELF
  86. #define IN_MOVE_SELF 0x00000800
  87. #endif
  88. #endif
  89. #include <sys/utsname.h>
  90. #include "kdirwatch.h"
  91. #include "kdirwatch_p.h"
  92. #include "global.h" // TDEIO::probably_slow_mounted
  93. #define NO_NOTIFY (time_t) 0
  94. static KDirWatchPrivate* dwp_self = 0;
  95. #ifdef HAVE_DNOTIFY
  96. static int dnotify_signal = 0;
  97. /* DNOTIFY signal handler
  98. *
  99. * As this is called asynchronously, only a flag is set and
  100. * a rescan is requested.
  101. * This is done by writing into a pipe to trigger a TQSocketNotifier
  102. * watching on this pipe: a timer is started and after a timeout,
  103. * the rescan is done.
  104. */
  105. void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
  106. {
  107. if (!dwp_self) return;
  108. // write might change errno, we have to save it and restore it
  109. // (Richard Stevens, Advanced programming in the Unix Environment)
  110. int saved_errno = errno;
  111. Entry* e = dwp_self->fd_Entry.find(si->si_fd);
  112. // kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
  113. // << TQString(e ? e->path:"unknown") << endl;
  114. if(e && e->dn_fd == si->si_fd)
  115. e->dirty = true;
  116. char c = 0;
  117. write(dwp_self->mPipe[1], &c, 1);
  118. errno = saved_errno;
  119. }
  120. static struct sigaction old_sigio_act;
  121. /* DNOTIFY SIGIO signal handler
  122. *
  123. * When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
  124. */
  125. void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
  126. {
  127. if (dwp_self)
  128. {
  129. // write might change errno, we have to save it and restore it
  130. // (Richard Stevens, Advanced programming in the Unix Environment)
  131. int saved_errno = errno;
  132. dwp_self->rescan_all = true;
  133. char c = 0;
  134. write(dwp_self->mPipe[1], &c, 1);
  135. errno = saved_errno;
  136. }
  137. // Call previous signal handler
  138. if (old_sigio_act.sa_flags & SA_SIGINFO)
  139. {
  140. if (old_sigio_act.sa_sigaction)
  141. (*old_sigio_act.sa_sigaction)(sig, si, p);
  142. }
  143. else
  144. {
  145. if ((old_sigio_act.sa_handler != SIG_DFL) &&
  146. (old_sigio_act.sa_handler != SIG_IGN))
  147. (*old_sigio_act.sa_handler)(sig);
  148. }
  149. }
  150. #endif
  151. //
  152. // Class KDirWatchPrivate (singleton)
  153. //
  154. /* All entries (files/directories) to be watched in the
  155. * application (coming from multiple KDirWatch instances)
  156. * are registered in a single KDirWatchPrivate instance.
  157. *
  158. * At the moment, the following methods for file watching
  159. * are supported:
  160. * - Polling: All files to be watched are polled regularly
  161. * using stat (more precise: TQFileInfo.lastModified()).
  162. * The polling frequency is determined from global tdeconfig
  163. * settings, defaulting to 500 ms for local directories
  164. * and 5000 ms for remote mounts
  165. * - FAM (File Alternation Monitor): first used on IRIX, SGI
  166. * has ported this method to LINUX. It uses a kernel part
  167. * (IMON, sending change events to /dev/imon) and a user
  168. * level damon (fam), to which applications connect for
  169. * notification of file changes. For NFS, the fam damon
  170. * on the NFS server machine is used; if IMON is not built
  171. * into the kernel, fam uses polling for local files.
  172. * - DNOTIFY: In late LINUX 2.3.x, directory notification was
  173. * introduced. By opening a directory, you can request for
  174. * UNIX signals to be sent to the process when a directory
  175. * is changed.
  176. * - INOTIFY: In LINUX 2.6.13, inode change notification was
  177. * introduced. You're now able to watch arbitrary inode's
  178. * for changes, and even get notification when they're
  179. * unmounted.
  180. */
  181. KDirWatchPrivate::KDirWatchPrivate()
  182. : rescan_timer(0, "KDirWatchPrivate::rescan_timer")
  183. {
  184. timer = new TQTimer(this, "KDirWatchPrivate::timer");
  185. connect (timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotRescan()));
  186. freq = 3600000; // 1 hour as upper bound
  187. statEntries = 0;
  188. delayRemove = false;
  189. m_ref = 0;
  190. TDEConfigGroup config(TDEGlobal::config(), TQCString("DirWatch"));
  191. m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
  192. m_PollInterval = config.readNumEntry("PollInterval", 500);
  193. TQString available("Stat");
  194. // used for FAM and DNOTIFY
  195. rescan_all = false;
  196. connect(&rescan_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotRescan()));
  197. #ifdef HAVE_FAM
  198. // It's possible that FAM server can't be started
  199. if (FAMOpen(&fc) ==0) {
  200. available += ", FAM";
  201. use_fam=true;
  202. sn = new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
  203. TQSocketNotifier::Read, this);
  204. connect( sn, TQT_SIGNAL(activated(int)),
  205. this, TQT_SLOT(famEventReceived()) );
  206. }
  207. else {
  208. kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
  209. use_fam=false;
  210. }
  211. #endif
  212. #ifdef HAVE_INOTIFY
  213. supports_inotify = true;
  214. m_inotify_fd = inotify_init();
  215. if ( m_inotify_fd <= 0 ) {
  216. kdDebug(7001) << "Can't use Inotify, kernel doesn't support it" << endl;
  217. supports_inotify = false;
  218. }
  219. {
  220. struct utsname uts;
  221. int major, minor, patch;
  222. if (uname(&uts) < 0)
  223. supports_inotify = false; // *shrug*
  224. else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
  225. supports_inotify = false; // *shrug*
  226. else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14
  227. kdDebug(7001) << "Can't use INotify, Linux kernel too old" << endl;
  228. supports_inotify = false;
  229. }
  230. }
  231. if ( supports_inotify ) {
  232. available += ", Inotify";
  233. fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
  234. mSn = new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read, this );
  235. connect( mSn, TQT_SIGNAL(activated( int )), this, TQT_SLOT( slotActivated() ) );
  236. }
  237. #endif
  238. #ifdef HAVE_DNOTIFY
  239. // if we have inotify, disable dnotify.
  240. #ifdef HAVE_INOTIFY
  241. supports_dnotify = !supports_inotify;
  242. #else
  243. // otherwise, not guilty until proven guilty.
  244. supports_dnotify = true;
  245. #endif
  246. struct utsname uts;
  247. int major, minor, patch;
  248. if (uname(&uts) < 0)
  249. supports_dnotify = false; // *shrug*
  250. else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
  251. supports_dnotify = false; // *shrug*
  252. else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
  253. kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
  254. supports_dnotify = false;
  255. }
  256. if( supports_dnotify ) {
  257. available += ", DNotify";
  258. pipe(mPipe);
  259. fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
  260. fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
  261. fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
  262. fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
  263. mSn = new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read, this);
  264. connect(mSn, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated()));
  265. // Install the signal handler only once
  266. if ( dnotify_signal == 0 )
  267. {
  268. dnotify_signal = SIGRTMIN + 8;
  269. struct sigaction act;
  270. act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
  271. sigemptyset(&act.sa_mask);
  272. act.sa_flags = SA_SIGINFO;
  273. #ifdef SA_RESTART
  274. act.sa_flags |= SA_RESTART;
  275. #endif
  276. sigaction(dnotify_signal, &act, NULL);
  277. act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
  278. sigaction(SIGIO, &act, &old_sigio_act);
  279. }
  280. }
  281. else
  282. {
  283. mPipe[0] = -1;
  284. mPipe[1] = -1;
  285. }
  286. #endif
  287. kdDebug(7001) << "Available methods: " << available << endl;
  288. }
  289. /* This is called on app exit (KStaticDeleter) */
  290. KDirWatchPrivate::~KDirWatchPrivate()
  291. {
  292. timer->stop();
  293. /* remove all entries being watched */
  294. removeEntries(0);
  295. #ifdef HAVE_FAM
  296. if (use_fam) {
  297. FAMClose(&fc);
  298. kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
  299. }
  300. #endif
  301. #ifdef HAVE_INOTIFY
  302. if ( supports_inotify )
  303. ::close( m_inotify_fd );
  304. #endif
  305. #ifdef HAVE_DNOTIFY
  306. close(mPipe[0]);
  307. close(mPipe[1]);
  308. #endif
  309. }
  310. #include <stdlib.h>
  311. void KDirWatchPrivate::slotActivated()
  312. {
  313. #ifdef HAVE_DNOTIFY
  314. if ( supports_dnotify )
  315. {
  316. char dummy_buf[4096];
  317. read(mPipe[0], &dummy_buf, 4096);
  318. if (!rescan_timer.isActive())
  319. rescan_timer.start(m_PollInterval, true /* singleshot */);
  320. return;
  321. }
  322. #endif
  323. #ifdef HAVE_INOTIFY
  324. if ( !supports_inotify )
  325. return;
  326. int pending = -1;
  327. int offset = 0;
  328. char buf[4096];
  329. assert( m_inotify_fd > -1 );
  330. ioctl( m_inotify_fd, FIONREAD, &pending );
  331. while ( pending > 0 ) {
  332. if ( pending > (int)sizeof( buf ) )
  333. pending = sizeof( buf );
  334. pending = read( m_inotify_fd, buf, pending);
  335. while ( pending > 0 ) {
  336. struct inotify_event *event = (struct inotify_event *) &buf[offset];
  337. pending -= sizeof( struct inotify_event ) + event->len;
  338. offset += sizeof( struct inotify_event ) + event->len;
  339. TQString path;
  340. if ( event->len )
  341. path = TQFile::decodeName( TQCString( event->name, event->len ) );
  342. if ( path.length() && isNoisyFile( path.latin1() ) )
  343. continue;
  344. kdDebug(7001) << "ev wd: " << event->wd << " mask " << event->mask << " path: " << path << endl;
  345. // now we're in deep trouble of finding the
  346. // associated entries
  347. // for now, we suck and iterate
  348. for ( EntryMap::Iterator it = m_mapEntries.begin();
  349. it != m_mapEntries.end(); ++it ) {
  350. Entry* e = &( *it );
  351. if ( e->wd == event->wd ) {
  352. e->dirty = true;
  353. if ( 1 || e->isDir) {
  354. if( event->mask & IN_DELETE_SELF) {
  355. kdDebug(7001) << "-->got deleteself signal for " << e->path << endl;
  356. e->m_status = NonExistent;
  357. if (e->isDir)
  358. addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
  359. else
  360. addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
  361. }
  362. if ( event->mask & IN_IGNORED ) {
  363. e->wd = 0;
  364. }
  365. if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
  366. Entry *sub_entry = e->m_entries.first();
  367. for(;sub_entry; sub_entry = e->m_entries.next())
  368. if (sub_entry->path == e->path + "/" + path) break;
  369. if (sub_entry /*&& sub_entry->isDir*/) {
  370. removeEntry(0,e->path, sub_entry);
  371. KDE_struct_stat stat_buf;
  372. TQCString tpath = TQFile::encodeName(path);
  373. KDE_stat(tpath, &stat_buf);
  374. //sub_entry->isDir = S_ISDIR(stat_buf.st_mode);
  375. //sub_entry->m_ctime = stat_buf.st_ctime;
  376. //sub_entry->m_status = Normal;
  377. //sub_entry->m_nlink = stat_buf.st_nlink;
  378. if(!useINotify(sub_entry))
  379. useStat(sub_entry);
  380. sub_entry->dirty = true;
  381. }
  382. }
  383. }
  384. if (!rescan_timer.isActive())
  385. rescan_timer.start(m_PollInterval, true /* singleshot */);
  386. break; // there really should be only one matching wd
  387. }
  388. }
  389. }
  390. }
  391. #endif
  392. }
  393. /* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned.
  394. * We first need to mark all yet nonexistent, but possible created
  395. * entries as dirty...
  396. */
  397. void KDirWatchPrivate::Entry::propagate_dirty()
  398. {
  399. for (TQPtrListIterator<Entry> sub_entry (m_entries);
  400. sub_entry.current(); ++sub_entry)
  401. {
  402. if (!sub_entry.current()->dirty)
  403. {
  404. sub_entry.current()->dirty = true;
  405. sub_entry.current()->propagate_dirty();
  406. }
  407. }
  408. }
  409. /* A KDirWatch instance is interested in getting events for
  410. * this file/Dir entry.
  411. */
  412. void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
  413. {
  414. Client* client = m_clients.first();
  415. for(;client; client = m_clients.next())
  416. if (client->instance == instance) break;
  417. if (client) {
  418. client->count++;
  419. return;
  420. }
  421. client = new Client;
  422. client->instance = instance;
  423. client->count = 1;
  424. client->watchingStopped = instance->isStopped();
  425. client->pending = NoChange;
  426. m_clients.append(client);
  427. }
  428. void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
  429. {
  430. Client* client = m_clients.first();
  431. for(;client; client = m_clients.next())
  432. if (client->instance == instance) break;
  433. if (client) {
  434. client->count--;
  435. if (client->count == 0) {
  436. m_clients.removeRef(client);
  437. delete client;
  438. }
  439. }
  440. }
  441. /* get number of clients */
  442. int KDirWatchPrivate::Entry::clients()
  443. {
  444. int clients = 0;
  445. Client* client = m_clients.first();
  446. for(;client; client = m_clients.next())
  447. clients += client->count;
  448. return clients;
  449. }
  450. KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const TQString& _path)
  451. {
  452. // we only support absolute paths
  453. if (TQDir::isRelativePath(_path)) {
  454. return 0;
  455. }
  456. TQString path = _path;
  457. if ( path.length() > 1 && path.right(1) == "/" )
  458. path.truncate( path.length() - 1 );
  459. EntryMap::Iterator it = m_mapEntries.find( path );
  460. if ( it == m_mapEntries.end() )
  461. return 0;
  462. else
  463. return &(*it);
  464. }
  465. // set polling frequency for a entry and adjust global freq if needed
  466. void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
  467. {
  468. e->freq = newFreq;
  469. // a reasonable frequency for the global polling timer
  470. if (e->freq < freq) {
  471. freq = e->freq;
  472. if (timer->isActive()) timer->changeInterval(freq);
  473. kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
  474. }
  475. }
  476. #ifdef HAVE_FAM
  477. // setup FAM notification, returns false if not possible
  478. bool KDirWatchPrivate::useFAM(Entry* e)
  479. {
  480. if (!use_fam) return false;
  481. // handle FAM events to avoid deadlock
  482. // (FAM sends back all files in a directory when monitoring)
  483. famEventReceived();
  484. e->m_mode = FAMMode;
  485. e->dirty = false;
  486. if (e->isDir) {
  487. if (e->m_status == NonExistent) {
  488. // If the directory does not exist we watch the parent directory
  489. addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
  490. }
  491. else {
  492. int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
  493. &(e->fr), e);
  494. if (res<0) {
  495. e->m_mode = UnknownMode;
  496. use_fam=false;
  497. return false;
  498. }
  499. kdDebug(7001) << " Setup FAM (Req "
  500. << FAMREQUEST_GETREQNUM(&(e->fr))
  501. << ") for " << e->path << endl;
  502. }
  503. }
  504. else {
  505. if (e->m_status == NonExistent) {
  506. // If the file does not exist we watch the directory
  507. addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
  508. }
  509. else {
  510. int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
  511. &(e->fr), e);
  512. if (res<0) {
  513. e->m_mode = UnknownMode;
  514. use_fam=false;
  515. return false;
  516. }
  517. kdDebug(7001) << " Setup FAM (Req "
  518. << FAMREQUEST_GETREQNUM(&(e->fr))
  519. << ") for " << e->path << endl;
  520. }
  521. }
  522. // handle FAM events to avoid deadlock
  523. // (FAM sends back all files in a directory when monitoring)
  524. famEventReceived();
  525. return true;
  526. }
  527. #endif
  528. #ifdef HAVE_DNOTIFY
  529. // setup DNotify notification, returns false if not possible
  530. bool KDirWatchPrivate::useDNotify(Entry* e)
  531. {
  532. e->dn_fd = 0;
  533. e->dirty = false;
  534. if (!supports_dnotify) return false;
  535. e->m_mode = DNotifyMode;
  536. if (e->isDir) {
  537. if (e->m_status == Normal) {
  538. int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
  539. // Migrate fd to somewhere above 128. Some libraries have
  540. // constructs like:
  541. // fd = socket(...)
  542. // if (fd > ARBITRARY_LIMIT)
  543. // return error;
  544. //
  545. // Since programs might end up using a lot of KDirWatch objects
  546. // for a rather long time the above braindamage could get
  547. // triggered.
  548. //
  549. // By moving the kdirwatch fd's to > 128, calls like socket() will keep
  550. // returning fd's < ARBITRARY_LIMIT for a bit longer.
  551. int fd2 = fcntl(fd, F_DUPFD, 128);
  552. if (fd2 >= 0)
  553. {
  554. close(fd);
  555. fd = fd2;
  556. }
  557. if (fd<0) {
  558. e->m_mode = UnknownMode;
  559. return false;
  560. }
  561. int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
  562. // if dependant is a file watch, we check for MODIFY & ATTRIB too
  563. for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
  564. if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
  565. if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
  566. fcntl(fd, F_NOTIFY, mask) < 0) {
  567. kdDebug(7001) << "Not using Linux Directory Notifications."
  568. << endl;
  569. supports_dnotify = false;
  570. ::close(fd);
  571. e->m_mode = UnknownMode;
  572. return false;
  573. }
  574. fd_Entry.replace(fd, e);
  575. e->dn_fd = fd;
  576. kdDebug(7001) << " Setup DNotify (fd " << fd
  577. << ") for " << e->path << endl;
  578. }
  579. else { // NotExisting
  580. addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
  581. }
  582. }
  583. else { // File
  584. // we always watch the directory (DNOTIFY can't watch files alone)
  585. // this notifies us about changes of files therein
  586. addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
  587. }
  588. return true;
  589. }
  590. #endif
  591. #ifdef HAVE_INOTIFY
  592. // setup INotify notification, returns false if not possible
  593. bool KDirWatchPrivate::useINotify( Entry* e )
  594. {
  595. e->wd = 0;
  596. e->dirty = false;
  597. if (!supports_inotify) return false;
  598. e->m_mode = INotifyMode;
  599. int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
  600. if(!e->isDir)
  601. mask |= IN_MODIFY|IN_ATTRIB;
  602. else
  603. mask |= IN_ONLYDIR;
  604. // if dependant is a file watch, we check for MODIFY & ATTRIB too
  605. for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
  606. if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; }
  607. }
  608. if ( ( e->wd = inotify_add_watch( m_inotify_fd,
  609. TQFile::encodeName( e->path ), mask) ) > 0 )
  610. return true;
  611. if ( e->m_status == NonExistent ) {
  612. if (e->isDir)
  613. addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
  614. else
  615. addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
  616. return true;
  617. }
  618. return false;
  619. }
  620. #endif
  621. bool KDirWatchPrivate::useStat(Entry* e)
  622. {
  623. if ( e->path.startsWith("/media/") || (e->path == "/media")
  624. || (TDEIO::probably_slow_mounted(e->path)) )
  625. useFreq(e, m_nfsPollInterval);
  626. else
  627. useFreq(e, m_PollInterval);
  628. if (e->m_mode != StatMode) {
  629. e->m_mode = StatMode;
  630. statEntries++;
  631. if ( statEntries == 1 ) {
  632. // if this was first STAT entry (=timer was stopped)
  633. timer->start(freq); // then start the timer
  634. kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
  635. }
  636. }
  637. kdDebug(7001) << " Setup Stat (freq " << e->freq
  638. << ") for " << e->path << endl;
  639. return true;
  640. }
  641. /* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
  642. * providing in <isDir> the type of the entry to be watched.
  643. * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
  644. * this entry needs another entry to watch himself (when notExistent).
  645. */
  646. void KDirWatchPrivate::addEntry(KDirWatch* instance, const TQString& _path,
  647. Entry* sub_entry, bool isDir)
  648. {
  649. TQString path = _path;
  650. if (path.startsWith("/dev/") || (path == "/dev"))
  651. return; // Don't even go there.
  652. if ( path.length() > 1 && path.right(1) == "/" )
  653. path.truncate( path.length() - 1 );
  654. EntryMap::Iterator it = m_mapEntries.find( path );
  655. if ( it != m_mapEntries.end() )
  656. {
  657. if (sub_entry) {
  658. (*it).m_entries.append(sub_entry);
  659. kdDebug(7001) << "Added already watched Entry " << path
  660. << " (for " << sub_entry->path << ")" << endl;
  661. #ifdef HAVE_DNOTIFY
  662. {
  663. Entry* e = &(*it);
  664. if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
  665. int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
  666. // if dependant is a file watch, we check for MODIFY & ATTRIB too
  667. for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
  668. if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
  669. if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
  670. ::close(e->dn_fd);
  671. e->m_mode = UnknownMode;
  672. fd_Entry.remove(e->dn_fd);
  673. e->dn_fd = 0;
  674. useStat( e );
  675. }
  676. }
  677. }
  678. #endif
  679. #ifdef HAVE_INOTIFY
  680. {
  681. Entry* e = &(*it);
  682. if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
  683. int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
  684. if(!e->isDir)
  685. mask |= IN_MODIFY|IN_ATTRIB;
  686. else
  687. mask |= IN_ONLYDIR;
  688. inotify_rm_watch (m_inotify_fd, e->wd);
  689. e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
  690. }
  691. }
  692. #endif
  693. }
  694. else {
  695. (*it).addClient(instance);
  696. kdDebug(7001) << "Added already watched Entry " << path
  697. << " (now " << (*it).clients() << " clients)"
  698. << TQString(TQString(" [%1]").arg(instance->name())) << endl;
  699. }
  700. return;
  701. }
  702. // we have a new path to watch
  703. KDE_struct_stat stat_buf;
  704. TQCString tpath = TQFile::encodeName(path);
  705. bool exists = (KDE_stat(tpath, &stat_buf) == 0);
  706. Entry newEntry;
  707. m_mapEntries.insert( path, newEntry );
  708. // the insert does a copy, so we have to use <e> now
  709. Entry* e = &(m_mapEntries[path]);
  710. if (exists) {
  711. e->isDir = S_ISDIR(stat_buf.st_mode);
  712. if (e->isDir && !isDir)
  713. kdWarning() << "KDirWatch: " << path << " is a directory. Use addDir!" << endl;
  714. else if (!e->isDir && isDir)
  715. kdWarning() << "KDirWatch: " << path << " is a file. Use addFile!" << endl;
  716. e->m_ctime = stat_buf.st_ctime;
  717. e->m_status = Normal;
  718. e->m_nlink = stat_buf.st_nlink;
  719. }
  720. else {
  721. e->isDir = isDir;
  722. e->m_ctime = invalid_ctime;
  723. e->m_status = NonExistent;
  724. e->m_nlink = 0;
  725. }
  726. e->path = path;
  727. if (sub_entry)
  728. e->m_entries.append(sub_entry);
  729. else
  730. e->addClient(instance);
  731. kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
  732. << (e->m_status == NonExistent ? " NotExisting" : "")
  733. << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
  734. << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
  735. << endl;
  736. // now setup the notification method
  737. e->m_mode = UnknownMode;
  738. e->msecLeft = 0;
  739. if ( isNoisyFile( tpath ) )
  740. return;
  741. #ifdef HAVE_FAM
  742. if (useFAM(e)) return;
  743. #endif
  744. #ifdef HAVE_INOTIFY
  745. if (useINotify(e)) return;
  746. #endif
  747. #ifdef HAVE_DNOTIFY
  748. if (useDNotify(e)) return;
  749. #endif
  750. useStat(e);
  751. }
  752. void KDirWatchPrivate::removeEntry( KDirWatch* instance,
  753. const TQString& _path, Entry* sub_entry )
  754. {
  755. kdDebug(7001) << "KDirWatchPrivate::removeEntry for '" << _path << "' sub_entry: " << sub_entry << endl;
  756. Entry* e = entry(_path);
  757. if (!e) {
  758. kdDebug(7001) << "KDirWatchPrivate::removeEntry can't handle '" << _path << "'" << endl;
  759. return;
  760. }
  761. if (sub_entry)
  762. e->m_entries.removeRef(sub_entry);
  763. else
  764. e->removeClient(instance);
  765. if (e->m_clients.count() || e->m_entries.count()) {
  766. kdDebug(7001) << "removeEntry: unwatched " << e->path << " " << _path << endl;
  767. return;
  768. }
  769. if (delayRemove) {
  770. // removeList is allowed to contain any entry at most once
  771. if (removeList.findRef(e)==-1)
  772. removeList.append(e);
  773. // now e->isValid() is false
  774. return;
  775. }
  776. #ifdef HAVE_FAM
  777. if (e->m_mode == FAMMode) {
  778. if ( e->m_status == Normal) {
  779. FAMCancelMonitor(&fc, &(e->fr) );
  780. kdDebug(7001) << "Cancelled FAM (Req "
  781. << FAMREQUEST_GETREQNUM(&(e->fr))
  782. << ") for " << e->path << endl;
  783. }
  784. else {
  785. if (e->isDir)
  786. removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
  787. else
  788. removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
  789. }
  790. }
  791. #endif
  792. #ifdef HAVE_INOTIFY
  793. kdDebug(7001) << "inotify remove " << ( e->m_mode == INotifyMode ) << " " << ( e->m_status == Normal ) << endl;
  794. if (e->m_mode == INotifyMode) {
  795. if ( e->m_status == Normal ) {
  796. (void) inotify_rm_watch( m_inotify_fd, e->wd );
  797. kdDebug(7001) << "Cancelled INotify (fd " <<
  798. m_inotify_fd << ", " << e->wd <<
  799. ") for " << e->path << endl;
  800. }
  801. else {
  802. if (e->isDir)
  803. removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
  804. else
  805. removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
  806. }
  807. }
  808. #endif
  809. #ifdef HAVE_DNOTIFY
  810. if (e->m_mode == DNotifyMode) {
  811. if (!e->isDir) {
  812. removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
  813. }
  814. else { // isDir
  815. // must close the FD.
  816. if ( e->m_status == Normal) {
  817. if (e->dn_fd) {
  818. ::close(e->dn_fd);
  819. fd_Entry.remove(e->dn_fd);
  820. kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
  821. << ") for " << e->path << endl;
  822. e->dn_fd = 0;
  823. }
  824. }
  825. else {
  826. removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
  827. }
  828. }
  829. }
  830. #endif
  831. if (e->m_mode == StatMode) {
  832. statEntries--;
  833. if ( statEntries == 0 ) {
  834. timer->stop(); // stop timer if lists are empty
  835. kdDebug(7001) << " Stopped Polling Timer" << endl;
  836. }
  837. }
  838. kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
  839. << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
  840. << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
  841. << endl;
  842. m_mapEntries.remove( e->path ); // <e> not valid any more
  843. }
  844. /* Called from KDirWatch destructor:
  845. * remove <instance> as client from all entries
  846. */
  847. void KDirWatchPrivate::removeEntries( KDirWatch* instance )
  848. {
  849. TQPtrList<Entry> list;
  850. int minfreq = 3600000;
  851. // put all entries where instance is a client in list
  852. EntryMap::Iterator it = m_mapEntries.begin();
  853. for( ; it != m_mapEntries.end(); ++it ) {
  854. Client* c = (*it).m_clients.first();
  855. for(;c;c=(*it).m_clients.next())
  856. if (c->instance == instance) break;
  857. if (c) {
  858. c->count = 1; // forces deletion of instance as client
  859. list.append(&(*it));
  860. }
  861. else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
  862. minfreq = (*it).freq;
  863. }
  864. for(Entry* e=list.first();e;e=list.next())
  865. removeEntry(instance, e->path, 0);
  866. if (minfreq > freq) {
  867. // we can decrease the global polling frequency
  868. freq = minfreq;
  869. if (timer->isActive()) timer->changeInterval(freq);
  870. kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
  871. }
  872. }
  873. // instance ==0: stop scanning for all instances
  874. bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
  875. {
  876. int stillWatching = 0;
  877. Client* c = e->m_clients.first();
  878. for(;c;c=e->m_clients.next()) {
  879. if (!instance || instance == c->instance)
  880. c->watchingStopped = true;
  881. else if (!c->watchingStopped)
  882. stillWatching += c->count;
  883. }
  884. kdDebug(7001) << instance->name() << " stopped scanning " << e->path
  885. << " (now " << stillWatching << " watchers)" << endl;
  886. if (stillWatching == 0) {
  887. // if nobody is interested, we don't watch
  888. e->m_ctime = invalid_ctime; // invalid
  889. e->m_status = NonExistent;
  890. // e->m_status = Normal;
  891. }
  892. return true;
  893. }
  894. // instance ==0: start scanning for all instances
  895. bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
  896. bool notify)
  897. {
  898. int wasWatching = 0, newWatching = 0;
  899. Client* c = e->m_clients.first();
  900. for(;c;c=e->m_clients.next()) {
  901. if (!c->watchingStopped)
  902. wasWatching += c->count;
  903. else if (!instance || instance == c->instance) {
  904. c->watchingStopped = false;
  905. newWatching += c->count;
  906. }
  907. }
  908. if (newWatching == 0)
  909. return false;
  910. kdDebug(7001) << (instance ? instance->name() : "all") << " restarted scanning " << e->path
  911. << " (now " << wasWatching+newWatching << " watchers)" << endl;
  912. // restart watching and emit pending events
  913. int ev = NoChange;
  914. if (wasWatching == 0) {
  915. if (!notify) {
  916. KDE_struct_stat stat_buf;
  917. bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
  918. if (exists) {
  919. e->m_ctime = stat_buf.st_ctime;
  920. e->m_status = Normal;
  921. e->m_nlink = stat_buf.st_nlink;
  922. }
  923. else {
  924. e->m_ctime = invalid_ctime;
  925. e->m_status = NonExistent;
  926. e->m_nlink = 0;
  927. }
  928. }
  929. e->msecLeft = 0;
  930. ev = scanEntry(e);
  931. }
  932. emitEvent(e,ev);
  933. return true;
  934. }
  935. // instance ==0: stop scanning for all instances
  936. void KDirWatchPrivate::stopScan(KDirWatch* instance)
  937. {
  938. EntryMap::Iterator it = m_mapEntries.begin();
  939. for( ; it != m_mapEntries.end(); ++it )
  940. stopEntryScan(instance, &(*it));
  941. }
  942. void KDirWatchPrivate::startScan(KDirWatch* instance,
  943. bool notify, bool skippedToo )
  944. {
  945. if (!notify)
  946. resetList(instance,skippedToo);
  947. EntryMap::Iterator it = m_mapEntries.begin();
  948. for( ; it != m_mapEntries.end(); ++it )
  949. restartEntryScan(instance, &(*it), notify);
  950. // timer should still be running when in polling mode
  951. }
  952. // clear all pending events, also from stopped
  953. void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
  954. bool skippedToo )
  955. {
  956. EntryMap::Iterator it = m_mapEntries.begin();
  957. for( ; it != m_mapEntries.end(); ++it ) {
  958. Client* c = (*it).m_clients.first();
  959. for(;c;c=(*it).m_clients.next())
  960. if (!c->watchingStopped || skippedToo)
  961. c->pending = NoChange;
  962. }
  963. }
  964. // Return event happened on <e>
  965. //
  966. int KDirWatchPrivate::scanEntry(Entry* e)
  967. {
  968. #ifdef HAVE_FAM
  969. if (e->m_mode == FAMMode) {
  970. // we know nothing has changed, no need to stat
  971. if(!e->dirty) return NoChange;
  972. e->dirty = false;
  973. }
  974. if (e->isDir) return Changed;
  975. #endif
  976. // Shouldn't happen: Ignore "unknown" notification method
  977. if (e->m_mode == UnknownMode) return NoChange;
  978. #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
  979. if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
  980. // we know nothing has changed, no need to stat
  981. if(!e->dirty) return NoChange;
  982. kdDebug(7001) << "scanning " << e->path << " " << e->m_status << " " << e->m_ctime << endl;
  983. e->dirty = false;
  984. }
  985. #endif
  986. if (e->m_mode == StatMode) {
  987. // only scan if timeout on entry timer happens;
  988. // e.g. when using 500msec global timer, a entry
  989. // with freq=5000 is only watched every 10th time
  990. e->msecLeft -= freq;
  991. if (e->msecLeft>0) return NoChange;
  992. e->msecLeft += e->freq;
  993. }
  994. KDE_struct_stat stat_buf;
  995. bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
  996. if (exists) {
  997. if (e->m_status == NonExistent) {
  998. // ctime is the 'creation time' on windows, but with qMax
  999. // we get the latest change of any kind, on any platform.
  1000. e->m_ctime = stat_buf.st_ctime;
  1001. e->m_status = Normal;
  1002. e->m_nlink = stat_buf.st_nlink;
  1003. return Created;
  1004. }
  1005. if ( (e->m_ctime != invalid_ctime) &&
  1006. ((stat_buf.st_ctime != e->m_ctime) ||
  1007. (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
  1008. e->m_ctime = stat_buf.st_ctime;
  1009. e->m_nlink = stat_buf.st_nlink;
  1010. return Changed;
  1011. }
  1012. return NoChange;
  1013. }
  1014. // dir/file doesn't exist
  1015. if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
  1016. e->m_nlink = 0;
  1017. e->m_status = NonExistent;
  1018. return NoChange;
  1019. }
  1020. e->m_ctime = invalid_ctime;
  1021. e->m_nlink = 0;
  1022. e->m_status = NonExistent;
  1023. return Deleted;
  1024. }
  1025. /* Notify all interested KDirWatch instances about a given event on an entry
  1026. * and stored pending events. When watching is stopped, the event is
  1027. * added to the pending events.
  1028. */
  1029. void KDirWatchPrivate::emitEvent(Entry* e, int event, const TQString &fileName)
  1030. {
  1031. TQString path = e->path;
  1032. if (!fileName.isEmpty()) {
  1033. if (!TQDir::isRelativePath(fileName))
  1034. path = fileName;
  1035. else
  1036. #ifdef Q_OS_UNIX
  1037. path += "/" + fileName;
  1038. #elif defined(Q_WS_WIN)
  1039. //current drive is passed instead of /
  1040. path += TQDir::currentDirPath().left(2) + "/" + fileName;
  1041. #endif
  1042. }
  1043. TQPtrListIterator<Client> cit( e->m_clients );
  1044. for ( ; cit.current(); ++cit )
  1045. {
  1046. Client* c = cit.current();
  1047. if (c->instance==0 || c->count==0) continue;
  1048. if (c->watchingStopped) {
  1049. // add event to pending...
  1050. if (event == Changed)
  1051. c->pending |= event;
  1052. else if (event == Created || event == Deleted)
  1053. c->pending = event;
  1054. continue;
  1055. }
  1056. // not stopped
  1057. if (event == NoChange || event == Changed)
  1058. event |= c->pending;
  1059. c->pending = NoChange;
  1060. if (event == NoChange) continue;
  1061. if (event & Deleted) {
  1062. c->instance->setDeleted(path);
  1063. // emit only Deleted event...
  1064. continue;
  1065. }
  1066. if (event & Created) {
  1067. c->instance->setCreated(path);
  1068. // possible emit Change event after creation
  1069. }
  1070. if (event & Changed)
  1071. c->instance->setDirty(path);
  1072. }
  1073. }
  1074. // Remove entries which were marked to be removed
  1075. void KDirWatchPrivate::slotRemoveDelayed()
  1076. {
  1077. Entry* e;
  1078. delayRemove = false;
  1079. for(e=removeList.first();e;e=removeList.next())
  1080. removeEntry(0, e->path, 0);
  1081. removeList.clear();
  1082. }
  1083. /* Scan all entries to be watched for changes. This is done regularly
  1084. * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
  1085. */
  1086. void KDirWatchPrivate::slotRescan()
  1087. {
  1088. EntryMap::Iterator it;
  1089. // People can do very long things in the slot connected to dirty(),
  1090. // like showing a message box. We don't want to keep polling during
  1091. // that time, otherwise the value of 'delayRemove' will be reset.
  1092. bool timerRunning = timer->isActive();
  1093. if ( timerRunning )
  1094. timer->stop();
  1095. // We delay deletions of entries this way.
  1096. // removeDir(), when called in slotDirty(), can cause a crash otherwise
  1097. delayRemove = true;
  1098. #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
  1099. TQPtrList<Entry> dList, cList;
  1100. #endif
  1101. if (rescan_all)
  1102. {
  1103. // mark all as dirty
  1104. it = m_mapEntries.begin();
  1105. for( ; it != m_mapEntries.end(); ++it )
  1106. (*it).dirty = true;
  1107. rescan_all = false;
  1108. }
  1109. else
  1110. {
  1111. // progate dirty flag to dependant entries (e.g. file watches)
  1112. it = m_mapEntries.begin();
  1113. for( ; it != m_mapEntries.end(); ++it )
  1114. if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
  1115. (*it).propagate_dirty();
  1116. }
  1117. it = m_mapEntries.begin();
  1118. for( ; it != m_mapEntries.end(); ++it ) {
  1119. // we don't check invalid entries (i.e. remove delayed)
  1120. if (!(*it).isValid()) continue;
  1121. int ev = scanEntry( &(*it) );
  1122. #ifdef HAVE_INOTIFY
  1123. if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
  1124. cList.append( &(*it) );
  1125. if (! useINotify( &(*it) )) {
  1126. useStat( &(*it) );
  1127. }
  1128. }
  1129. #endif
  1130. #ifdef HAVE_DNOTIFY
  1131. if ((*it).m_mode == DNotifyMode) {
  1132. if ((*it).isDir && (ev == Deleted)) {
  1133. dList.append( &(*it) );
  1134. // must close the FD.
  1135. if ((*it).dn_fd) {
  1136. ::close((*it).dn_fd);
  1137. fd_Entry.remove((*it).dn_fd);
  1138. (*it).dn_fd = 0;
  1139. }
  1140. }
  1141. else if ((*it).isDir && (ev == Created)) {
  1142. // For created, but yet without DNOTIFYing ...
  1143. if ( (*it).dn_fd == 0) {
  1144. cList.append( &(*it) );
  1145. if (! useDNotify( &(*it) )) {
  1146. // if DNotify setup fails...
  1147. useStat( &(*it) );
  1148. }
  1149. }
  1150. }
  1151. }
  1152. #endif
  1153. if ( ev != NoChange )
  1154. emitEvent( &(*it), ev);
  1155. }
  1156. #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
  1157. // Scan parent of deleted directories for new creation
  1158. Entry* e;
  1159. for(e=dList.first();e;e=dList.next())
  1160. addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
  1161. // Remove watch of parent of new created directories
  1162. for(e=cList.first();e;e=cList.next())
  1163. removeEntry(0, TQDir::cleanDirPath( e->path+"/.."), e);
  1164. #endif
  1165. if ( timerRunning )
  1166. timer->start(freq);
  1167. TQTimer::singleShot(0, this, TQT_SLOT(slotRemoveDelayed()));
  1168. }
  1169. bool KDirWatchPrivate::isNoisyFile( const char * filename )
  1170. {
  1171. // $HOME/.X.err grows with debug output, so don't notify change
  1172. if ( *filename == '.') {
  1173. if (strncmp(filename, ".X.err", 6) == 0) return true;
  1174. if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
  1175. // fontconfig updates the cache on every KDE app start
  1176. // (inclusive tdeio_thumbnail slaves)
  1177. if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
  1178. }
  1179. return false;
  1180. }
  1181. #ifdef HAVE_FAM
  1182. void KDirWatchPrivate::famEventReceived()
  1183. {
  1184. static FAMEvent fe;
  1185. delayRemove = true;
  1186. while(use_fam && FAMPending(&fc)) {
  1187. if (FAMNextEvent(&fc, &fe) == -1) {
  1188. kdWarning(7001) << "FAM connection problem, switching to polling."
  1189. << endl;
  1190. use_fam = false;
  1191. delete sn; sn = 0;
  1192. // Replace all FAMMode entries with DNotify/Stat
  1193. EntryMap::Iterator it;
  1194. it = m_mapEntries.begin();
  1195. for( ; it != m_mapEntries.end(); ++it )
  1196. if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
  1197. #ifdef HAVE_INOTIFY
  1198. if (useINotify( &(*it) )) continue;
  1199. #endif
  1200. #ifdef HAVE_DNOTIFY
  1201. if (useDNotify( &(*it) )) continue;
  1202. #endif
  1203. useStat( &(*it) );
  1204. }
  1205. }
  1206. else
  1207. checkFAMEvent(&fe);
  1208. }
  1209. TQTimer::singleShot(0, this, TQT_SLOT(slotRemoveDelayed()));
  1210. }
  1211. void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
  1212. {
  1213. // Don't be too verbose ;-)
  1214. if ((fe->code == FAMExists) ||
  1215. (fe->code == FAMEndExist) ||
  1216. (fe->code == FAMAcknowledge)) return;
  1217. if ( isNoisyFile( fe->filename ) )
  1218. return;
  1219. Entry* e = 0;
  1220. EntryMap::Iterator it = m_mapEntries.begin();
  1221. for( ; it != m_mapEntries.end(); ++it )
  1222. if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
  1223. FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
  1224. e = &(*it);
  1225. break;
  1226. }
  1227. // Entry* e = static_cast<Entry*>(fe->userdata);
  1228. #if 0 // #88538
  1229. kdDebug(7001) << "Processing FAM event ("
  1230. << ((fe->code == FAMChanged) ? "FAMChanged" :
  1231. (fe->code == FAMDeleted) ? "FAMDeleted" :
  1232. (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
  1233. (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
  1234. (fe->code == FAMCreated) ? "FAMCreated" :
  1235. (fe->code == FAMMoved) ? "FAMMoved" :
  1236. (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
  1237. (fe->code == FAMExists) ? "FAMExists" :
  1238. (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
  1239. << ", " << fe->filename
  1240. << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
  1241. << ")" << endl;
  1242. #endif
  1243. if (!e) {
  1244. // this happens e.g. for FAMAcknowledge after deleting a dir...
  1245. // kdDebug(7001) << "No entry for FAM event ?!" << endl;
  1246. return;
  1247. }
  1248. if (e->m_status == NonExistent) {
  1249. kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
  1250. return;
  1251. }
  1252. // Delayed handling. This rechecks changes with own stat calls.
  1253. e->dirty = true;
  1254. if (!rescan_timer.isActive())
  1255. rescan_timer.start(m_PollInterval, true);
  1256. // needed FAM control actions on FAM events
  1257. if (e->isDir)
  1258. switch (fe->code)
  1259. {
  1260. case FAMDeleted:
  1261. // file absolute: watched dir
  1262. if (!TQDir::isRelativePath(fe->filename))
  1263. {
  1264. // a watched directory was deleted
  1265. e->m_status = NonExistent;
  1266. FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
  1267. kdDebug(7001) << "Cancelled FAMReq "
  1268. << FAMREQUEST_GETREQNUM(&(e->fr))
  1269. << " for " << e->path << endl;
  1270. // Scan parent for a new creation
  1271. addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
  1272. }
  1273. break;
  1274. case FAMCreated: {
  1275. // check for creation of a directory we have to watch
  1276. Entry *sub_entry = e->m_entries.first();
  1277. for(;sub_entry; sub_entry = e->m_entries.next())
  1278. if (sub_entry->path == e->path + "/" + fe->filename) break;
  1279. if (sub_entry && sub_entry->isDir) {
  1280. TQString path = e->path;
  1281. removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
  1282. sub_entry->m_status = Normal;
  1283. if (!useFAM(sub_entry))
  1284. #ifdef HAVE_INOTIFY
  1285. if (!useINotify(sub_entry ))
  1286. #endif
  1287. useStat(sub_entry);
  1288. }
  1289. break;
  1290. }
  1291. default:
  1292. break;
  1293. }
  1294. }
  1295. #else
  1296. void KDirWatchPrivate::famEventReceived() {}
  1297. #endif
  1298. void KDirWatchPrivate::statistics()
  1299. {
  1300. EntryMap::Iterator it;
  1301. kdDebug(7001) << "Entries watched:" << endl;
  1302. if (m_mapEntries.count()==0) {
  1303. kdDebug(7001) << " None." << endl;
  1304. }
  1305. else {
  1306. it = m_mapEntries.begin();
  1307. for( ; it != m_mapEntries.end(); ++it ) {
  1308. Entry* e = &(*it);
  1309. kdDebug(7001) << " " << e->path << " ("
  1310. << ((e->m_status==Normal)?"":"Nonexistent ")
  1311. << (e->isDir ? "Dir":"File") << ", using "
  1312. << ((e->m_mode == FAMMode) ? "FAM" :
  1313. (e->m_mode == INotifyMode) ? "INotify" :
  1314. (e->m_mode == DNotifyMode) ? "DNotify" :
  1315. (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
  1316. << ")" << endl;
  1317. Client* c = e->m_clients.first();
  1318. for(;c; c = e->m_clients.next()) {
  1319. TQString pending;
  1320. if (c->watchingStopped) {
  1321. if (c->pending & Deleted) pending += "deleted ";
  1322. if (c->pending & Created) pending += "created ";
  1323. if (c->pending & Changed) pending += "changed ";
  1324. if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
  1325. pending = ", stopped" + pending;
  1326. }
  1327. kdDebug(7001) << " by " << c->instance->name()
  1328. << " (" << c->count << " times)"
  1329. << pending << endl;
  1330. }
  1331. if (e->m_entries.count()>0) {
  1332. kdDebug(7001) << " dependent entries:" << endl;
  1333. Entry* d = e->m_entries.first();
  1334. for(;d; d = e->m_entries.next()) {
  1335. kdDebug(7001) << " " << d << endl;
  1336. kdDebug(7001) << " " << d->path << " (" << d << ") " << endl;
  1337. }
  1338. }
  1339. }
  1340. }
  1341. }
  1342. //
  1343. // Class KDirWatch
  1344. //
  1345. static KStaticDeleter<KDirWatch> sd_dw;
  1346. KDirWatch* KDirWatch::s_pSelf = 0L;
  1347. KDirWatch* KDirWatch::self()
  1348. {
  1349. if ( !s_pSelf ) {
  1350. sd_dw.setObject( s_pSelf, new KDirWatch );
  1351. }
  1352. return s_pSelf;
  1353. }
  1354. bool KDirWatch::exists()
  1355. {
  1356. return s_pSelf != 0;
  1357. }
  1358. KDirWatch::KDirWatch (TQObject* parent, const char* name)
  1359. : TQObject(parent,name)
  1360. {
  1361. if (!name) {
  1362. static int nameCounter = 0;
  1363. nameCounter++;
  1364. setName(TQString(TQString("KDirWatch-%1").arg(nameCounter)).ascii());
  1365. }
  1366. if (!dwp_self)
  1367. dwp_self = new KDirWatchPrivate;
  1368. d = dwp_self;
  1369. d->ref();
  1370. _isStopped = false;
  1371. }
  1372. KDirWatch::~KDirWatch()
  1373. {
  1374. d->removeEntries(this);
  1375. if ( d->deref() )
  1376. {
  1377. // delete it if it's the last one
  1378. delete d;
  1379. dwp_self = 0L;
  1380. }
  1381. }
  1382. // TODO: add watchFiles/recursive support
  1383. void KDirWatch::addDir( const TQString& _path,
  1384. bool watchFiles, bool recursive)
  1385. {
  1386. if (watchFiles || recursive) {
  1387. kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
  1388. }
  1389. if (d) d->addEntry(this, _path, 0, true);
  1390. }
  1391. void KDirWatch::addFile( const TQString& _path )
  1392. {
  1393. if (d) d->addEntry(this, _path, 0, false);
  1394. }
  1395. TQDateTime KDirWatch::ctime( const TQString &_path )
  1396. {
  1397. KDirWatchPrivate::Entry* e = d->entry(_path);
  1398. if (!e)
  1399. return TQDateTime();
  1400. TQDateTime result;
  1401. result.setTime_t(e->m_ctime);
  1402. return result;
  1403. }
  1404. void KDirWatch::removeDir( const TQString& _path )
  1405. {
  1406. if (d) d->removeEntry(this, _path, 0);
  1407. }
  1408. void KDirWatch::removeFile( const TQString& _path )
  1409. {
  1410. if (d) d->removeEntry(this, _path, 0);
  1411. }
  1412. bool KDirWatch::stopDirScan( const TQString& _path )
  1413. {
  1414. if (d) {
  1415. KDirWatchPrivate::Entry *e = d->entry(_path);
  1416. if (e && e->isDir) return d->stopEntryScan(this, e);
  1417. }
  1418. return false;
  1419. }
  1420. bool KDirWatch::restartDirScan( const TQString& _path )
  1421. {
  1422. if (d) {
  1423. KDirWatchPrivate::Entry *e = d->entry(_path);
  1424. if (e && e->isDir)
  1425. // restart without notifying pending events
  1426. return d->restartEntryScan(this, e, false);
  1427. }
  1428. return false;
  1429. }
  1430. void KDirWatch::stopScan()
  1431. {
  1432. if (d) d->stopScan(this);
  1433. _isStopped = true;
  1434. }
  1435. void KDirWatch::startScan( bool notify, bool skippedToo )
  1436. {
  1437. _isStopped = false;
  1438. if (d) d->startScan(this, notify, skippedToo);
  1439. }
  1440. bool KDirWatch::contains( const TQString& _path ) const
  1441. {
  1442. KDirWatchPrivate::Entry* e = d->entry(_path);
  1443. if (!e)
  1444. return false;
  1445. KDirWatchPrivate::Client* c = e->m_clients.first();
  1446. for(;c;c=e->m_clients.next())
  1447. if (c->instance == this) return true;
  1448. return false;
  1449. }
  1450. void KDirWatch::statistics()
  1451. {
  1452. if (!dwp_self) {
  1453. kdDebug(7001) << "KDirWatch not used" << endl;
  1454. return;
  1455. }
  1456. dwp_self->statistics();
  1457. }
  1458. void KDirWatch::setCreated( const TQString & _file )
  1459. {
  1460. kdDebug(7001) << name() << " emitting created " << _file << endl;
  1461. emit created( _file );
  1462. }
  1463. void KDirWatch::setDirty( const TQString & _file )
  1464. {
  1465. kdDebug(7001) << name() << " emitting dirty " << _file << endl;
  1466. emit dirty( _file );
  1467. }
  1468. void KDirWatch::setDeleted( const TQString & _file )
  1469. {
  1470. kdDebug(7001) << name() << " emitting deleted " << _file << endl;
  1471. emit deleted( _file );
  1472. }
  1473. KDirWatch::Method KDirWatch::internalMethod()
  1474. {
  1475. #ifdef HAVE_FAM
  1476. if (d->use_fam)
  1477. return KDirWatch::FAM;
  1478. #endif
  1479. #ifdef HAVE_INOTIFY
  1480. if (d->supports_inotify)
  1481. return KDirWatch::INotify;
  1482. #endif
  1483. #ifdef HAVE_DNOTIFY
  1484. if (d->supports_dnotify)
  1485. return KDirWatch::DNotify;
  1486. #endif
  1487. return KDirWatch::Stat;
  1488. }
  1489. #include "kdirwatch.moc"
  1490. #include "kdirwatch_p.moc"
  1491. //sven
  1492. // vim: sw=2 ts=8 et