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.

ksimpledirwatch.cpp 46KB

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