Subversion Repositories public

Rev

Rev 49 | Rev 55 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
45 luk 1
 
2
/// inotify C++ interface header
3
/**
4
 * \file inotify-cxx.h
5
 *
6
 * inotify C++ interface
7
 *
8
 * Copyright (C) 2006 Lukas Jelinek, <lukas@aiken.cz>
9
 *
10
 * This program is free software; you can redistribute it and/or
11
 * modify it under the terms of one of the following licenses:
12
 *
13
 * \li 1. X11-style license (see LICENSE-X11)
14
 * \li 2. GNU Lesser General Public License, version 2.1 (see LICENSE-LGPL)
15
 * \li 3. GNU General Public License, version 2  (see LICENSE-GPL)
16
 *
17
 * If you want to help with choosing the best license for you,
18
 * please visit http://www.gnu.org/licenses/license-list.html.
19
 *
20
 */
21
 
22
 
23
 
24
 
25
 
26
#ifndef _INOTIFYCXX_H_
27
#define _INOTIFYCXX_H_
28
 
29
#include <string>
30
#include <deque>
31
#include <map>
32
 
49 luk 33
// Please ensure that the following headers take the right place
45 luk 34
#include <sys/inotify.h>
49 luk 35
 
36
// Use this if syscalls not defined
37
#ifndef __NR_inotify_init
45 luk 38
#include <sys/inotify-syscalls.h>
49 luk 39
#endif // __NR_inotify_init
45 luk 40
 
41
/// Event struct size
42
#define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
43
 
44
/// Event buffer length
45
#define INOTIFY_BUFLEN (1024 * (INOTIFY_EVENT_SIZE + 16))
46
 
47 luk 47
/// Helper macro for creating exception messages.
48
/**
49
 * It prepends the message by the function name.
50
 */
51
#define IN_EXC_MSG(msg) (std::string(__PRETTY_FUNCTION__) + ": " + msg)
45 luk 52
 
51 luk 53
/// inotify-cxx thread safety
54
/**
55
 * If this symbol is defined you can use this interface safely
56
 * threaded applications. Remember that it slightly degrades
57
 * performance.
58
 *
59
 * Even if INOTIFY_THREAD_SAFE is defined some classes stay
60
 * unsafe. If you must use them (must you?) in more than one
61
 * thread concurrently you need to implement explicite locking.
62
 *
63
 * You need not to define INOTIFY_THREAD_SAFE in that cases
64
 * where the application is multithreaded but all the inotify
65
 * infrastructure will be managed only in one thread. This is
66
 * the recommended way.
67
 *
68
 * Locking may fail (it is very rare but not impossible). In this
69
 * case an exception is thrown. But if unlocking fails in case
70
 * of an error it does nothing (this failure is ignored).
71
 */
72
#ifdef INOTIFY_THREAD_SAFE
47 luk 73
 
51 luk 74
#include <pthread.h>
47 luk 75
 
51 luk 76
#define IN_LOCK_DECL mutable pthread_rwlock_t __m_lock;
77
 
78
#define IN_LOCK_INIT \
79
  { \
80
    pthread_rwlockattr_t attr; \
81
    int res = 0; \
82
    if ((res = pthread_rwlockattr_init(&attr)) != 0) \
83
      throw InotifyException(IN_EXC_MSG("cannot initialize lock attributes"), res, this); \
84
    if ((res = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP)) != 0) \
85
      throw InotifyException(IN_EXC_MSG("cannot set lock kind"), res, this); \
86
    if ((res = pthread_rwlock_init(&__m_lock, &attr)) != 0) \
87
      throw InotifyException(IN_EXC_MSG("cannot initialize lock"), res, this); \
88
    pthread_rwlockattr_destroy(&attr); \
89
  }
90
 
91
#define IN_LOCK_DONE pthread_rwlock_destroy(&__m_lock);
92
 
93
#define IN_READ_BEGIN \
94
  { \
95
    int res = pthread_rwlock_rdlock(&__m_lock); \
96
    if (res != 0) \
97
      throw InotifyException(IN_EXC_MSG("locking for reading failed"), res, (void*) this); \
98
  }
99
 
100
#define IN_READ_END \
101
  { \
102
    int res = pthread_rwlock_unlock(&__m_lock); \
103
    if (res != 0) \
104
      throw InotifyException(IN_EXC_MSG("unlocking failed"), res, (void*) this); \
105
  }
106
 
107
#define IN_READ_END_NOTHROW pthread_rwlock_unlock(&__m_lock);
108
 
109
#define IN_WRITE_BEGIN \
110
  { \
111
    int res = pthread_rwlock_wrlock(&__m_lock); \
112
    if (res != 0) \
113
      throw InotifyException(IN_EXC_MSG("locking for writing failed"), res, (void*) this); \
114
  }
115
 
116
#define IN_WRITE_END IN_READ_END
117
#define IN_WRITE_END_NOTHROW IN_READ_END_NOTHROW
118
 
119
#else // INOTIFY_THREAD_SAFE
120
 
121
#define IN_LOCK_DECL
122
#define IN_LOCK_INIT
123
#define IN_LOCK_DONE
124
#define IN_READ_BEGIN
125
#define IN_READ_END
126
#define IN_READ_END_NOTHROW
127
#define IN_WRITE_BEGIN
128
#define IN_WRITE_END
129
#define IN_WRITE_END_NOTHROW
130
 
131
#endif // INOTIFY_THREAD_SAFE
132
 
133
 
134
 
135
 
45 luk 136
// forward declaration
137
class InotifyWatch;
138
class Inotify;
139
 
140
 
47 luk 141
/// Class for inotify exceptions
51 luk 142
/**
143
 * This class allows to acquire information about exceptional
144
 * events. It makes easier to log or display error messages
145
 * and to identify problematic code locations.
146
 *
147
 * Although this class is basically thread-safe it is not intended
148
 * to be shared between threads.
149
 */
47 luk 150
class InotifyException
151
{
152
public:
153
  /// Constructor
154
  /**
155
   * \param[in] rMsg message
156
   * \param[in] iErr error number (see errno.h)
157
   * \param[in] pSrc source
158
   */
159
  InotifyException(const std::string& rMsg = "", int iErr = 0, void* pSrc = NULL)
160
  : m_msg(rMsg),
161
    m_err(iErr)
162
  {
163
    m_pSrc = pSrc;
164
  }
165
 
166
  /// Returns the exception message.
167
  /**
168
   * \return message
169
   */
170
  inline const std::string& GetMessage() const
171
  {
172
    return m_msg;
173
  }
174
 
175
  /// Returns the exception error number.
176
  /**
177
   * If not applicable this value is 0 (zero).
178
   *
179
   * \return error number (standardized; see errno.h)
180
   */
181
  inline int GetErrorNumber() const
182
  {
183
    return m_err;
184
  }
185
 
186
  /// Returns the exception source.
187
  /**
188
   * \return source
189
   */
190
  inline void* GetSource() const
191
  {
192
    return m_pSrc;
193
  }
194
 
195
protected:
196
  std::string m_msg;      ///< message
197
  int m_err;              ///< error number
198
  mutable void* m_pSrc;   ///< source
199
};
200
 
201
 
45 luk 202
/// inotify event class
203
/**
204
 * It holds all information about inotify event and provides
205
 * access to its particular values.
51 luk 206
 *
207
 * This class is not (and is not intended to be) thread-safe
208
 * and therefore it must not be used concurrently in multiple
209
 * threads.
45 luk 210
 */
211
class InotifyEvent
212
{
213
public:
214
  /// Constructor.
215
  /**
216
   * Creates a plain event.
217
   */
218
  InotifyEvent()
47 luk 219
  : m_uMask(0),
220
    m_uCookie(0)
45 luk 221
  {
222
    m_pWatch = NULL;
223
  }
224
 
225
  /// Constructor.
226
  /**
227
   * Creates an event based on inotify event data.
228
   * For NULL pointers it works the same way as InotifyEvent().
229
   *
230
   * \param[in] pEvt event data
231
   * \param[in] pWatch inotify watch
232
   */
233
  InotifyEvent(const struct inotify_event* pEvt, InotifyWatch* pWatch)
47 luk 234
  : m_uMask(0),
235
    m_uCookie(0)
45 luk 236
  {
237
    if (pEvt != NULL) {
47 luk 238
      m_uMask = (uint32_t) pEvt->mask;
239
      m_uCookie = (uint32_t) pEvt->cookie;
45 luk 240
      if (pEvt->name != NULL)
241
        m_name = pEvt->name;
242
      m_pWatch = pWatch;
243
    }
244
    else {
245
      m_pWatch = NULL;
246
    }
247
  }
248
 
249
  /// Destructor.
250
  ~InotifyEvent() {}
251
 
252
  /// Returns the event watch descriptor.
253
  /**
254
   * \return watch descriptor
255
   *
256
   * \sa InotifyWatch::GetDescriptor()
257
   */
47 luk 258
  int32_t GetDescriptor() const;
45 luk 259
 
260
  /// Returns the event mask.
261
  /**
262
   * \return event mask
263
   *
264
   * \sa InotifyWatch::GetMask()
265
   */
266
  inline uint32_t GetMask() const
267
  {
47 luk 268
    return m_uMask;
45 luk 269
  }
270
 
271
  /// Checks a value for the event type.
272
  /**
273
   * \param[in] uValue checked value
274
   * \param[in] uType type which is checked for
275
   * \return true = the value contains the given type, false = otherwise
276
   */
277
  inline static bool IsType(uint32_t uValue, uint32_t uType)
278
  {
279
    return ((uValue & uType) != 0) && ((~uValue & uType) == 0);
280
  }
281
 
282
  /// Checks for the event type.
283
  /**
284
   * \param[in] uType type which is checked for
285
   * \return true = event mask contains the given type, false = otherwise
286
   */
287
  inline bool IsType(uint32_t uType) const
288
  {
47 luk 289
    return IsType(m_uMask, uType);
45 luk 290
  }
291
 
292
  /// Returns the event cookie.
293
  /**
294
   * \return event cookie
295
   */
296
  inline uint32_t GetCookie() const
297
  {
47 luk 298
    return m_uCookie;
45 luk 299
  }
300
 
301
  /// Returns the event name length.
302
  /**
303
   * \return event name length
304
   */
305
  inline uint32_t GetLength() const
306
  {
47 luk 307
    return (uint32_t) m_name.length();
45 luk 308
  }
309
 
310
  /// Returns the event name.
311
  /**
312
   * \return event name
313
   */
314
  inline const std::string& GetName() const
315
  {
316
    return m_name;
317
  }
318
 
319
  /// Extracts the event name.
320
  /**
321
   * \param[out] rName event name
322
   */
323
  inline void GetName(std::string& rName) const
324
  {
325
    rName = GetName();
326
  }
327
 
328
  /// Returns the source watch.
329
  /**
330
   * \return source watch
331
   */
332
  inline InotifyWatch* GetWatch()
333
  {
334
    return m_pWatch;
335
  }
336
 
337
  /// Finds the appropriate mask for a name.
338
  /**
339
   * \param[in] rName mask name
340
   * \return mask for name; 0 on failure
341
   */
342
  static uint32_t GetMaskByName(const std::string& rName);
343
 
344
  /// Fills the string with all types contained in an event mask value.
345
  /**
346
   * \param[in] uValue event mask value
347
   * \param[out] rStr dumped event types
348
   */
349
  static void DumpTypes(uint32_t uValue, std::string& rStr);
350
 
351
  /// Fills the string with all types contained in the event mask.
352
  /**
353
   * \param[out] rStr dumped event types
354
   */
355
  void DumpTypes(std::string& rStr) const;
356
 
357
private:
47 luk 358
  uint32_t m_uMask;           ///< mask
359
  uint32_t m_uCookie;         ///< cookie
360
  std::string m_name;         ///< name
45 luk 361
  InotifyWatch* m_pWatch;     ///< source watch
362
};
363
 
364
 
365
 
366
/// inotify watch class
51 luk 367
/**
368
 * It holds information about the inotify watch on a particular
369
 * inode.
370
 *
371
 * If the INOTIFY_THREAD_SAFE is defined this class is thread-safe.
372
 */
45 luk 373
class InotifyWatch
374
{
375
public:
376
  /// Constructor.
377
  /**
378
   * Creates an inotify watch. Because this watch is
379
   * inactive it has an invalid descriptor (-1).
380
   *
381
   * \param[in] rPath watched file path
382
   * \param[in] uMask mask for events
47 luk 383
   * \param[in] fEnabled events enabled yes/no
45 luk 384
   */
47 luk 385
  InotifyWatch(const std::string& rPath, int32_t uMask, bool fEnabled = true)
386
  : m_path(rPath),
387
    m_uMask(uMask),
388
    m_wd((int32_t) -1),
389
    m_fEnabled(fEnabled)
45 luk 390
  {
51 luk 391
    IN_LOCK_INIT
45 luk 392
  }
393
 
394
  /// Destructor.
51 luk 395
  ~InotifyWatch()
396
  {
397
    IN_LOCK_DONE
398
  }
45 luk 399
 
400
  /// Returns the watch descriptor.
401
  /**
402
   * \return watch descriptor; -1 for inactive watch
403
   */
404
  inline int32_t GetDescriptor() const
405
  {
406
    return m_wd;
407
  }
408
 
409
  /// Returns the watched file path.
410
  /**
411
   * \return file path
412
   */
413
  inline const std::string& GetPath() const
414
  {
415
    return m_path;
416
  }
417
 
418
  /// Returns the watch event mask.
419
  /**
420
   * \return event mask
421
   */
422
  inline uint32_t GetMask() const
423
  {
424
    return (uint32_t) m_uMask;
425
  }
426
 
49 luk 427
  /// Sets the watch event mask.
428
  /**
51 luk 429
   * If the watch is active (added to an instance of Inotify)
49 luk 430
   * this method may fail due to unsuccessful re-setting
431
   * the watch in the kernel.
432
   *
433
   * \param[in] uMask event mask
434
   *
435
   * \throw InotifyException thrown if changing fails
436
   */
437
  void SetMask(uint32_t uMask) throw (InotifyException);  
438
 
45 luk 439
  /// Returns the appropriate inotify class instance.
440
  /**
441
   * \return inotify instance
442
   */
443
  inline Inotify* GetInotify()
444
  {
445
    return m_pInotify;
446
  }
447
 
49 luk 448
  /// Enables/disables the watch.
449
  /**
51 luk 450
   * If the watch is active (added to an instance of Inotify)
49 luk 451
   * this method may fail due to unsuccessful re-setting
452
   * the watch in the kernel.
453
   *
454
   * Re-setting the current state has no effect.
455
   *
456
   * \param[in] fEnabled set enabled yes/no
457
   *
458
   * \throw InotifyException thrown if enabling/disabling fails
459
   */
460
  void SetEnabled(bool fEnabled) throw (InotifyException);
47 luk 461
 
49 luk 462
  /// Checks whether the watch is enabled.
463
  /**
464
   * \return true = enables, false = disabled
465
   */
47 luk 466
  inline bool IsEnabled() const
467
  {
468
    return m_fEnabled;
469
  }
470
 
45 luk 471
private:
472
  friend class Inotify;
473
 
474
  std::string m_path;   ///< watched file path
475
  uint32_t m_uMask;     ///< event mask
476
  int32_t m_wd;         ///< watch descriptor
477
  Inotify* m_pInotify;  ///< inotify object
47 luk 478
  bool m_fEnabled;      ///< events enabled yes/no
51 luk 479
 
480
  IN_LOCK_DECL
45 luk 481
};
482
 
483
 
484
/// Mapping from watch descriptors to watch objects.
485
typedef std::map<int32_t, InotifyWatch*> IN_WATCH_MAP;
486
 
49 luk 487
/// Mapping from paths to watch objects.
488
typedef std::map<std::string, InotifyWatch*> IN_WP_MAP;
45 luk 489
 
49 luk 490
 
45 luk 491
/// inotify class
51 luk 492
/**
493
 * It holds information about the inotify device descriptor
494
 * and manages the event queue.
495
 *
496
 * If the INOTIFY_THREAD_SAFE is defined this class is thread-safe.
497
 */
45 luk 498
class Inotify
499
{
500
public:
501
  /// Constructor.
502
  /**
503
   * Creates and initializes an instance of inotify communication
504
   * object (opens the inotify device).
47 luk 505
   *
506
   * \throw InotifyException thrown if inotify isn't available
45 luk 507
   */
47 luk 508
  Inotify() throw (InotifyException);
45 luk 509
 
510
  /// Destructor.
511
  /**
47 luk 512
   * Calls Close() due to clean-up.
45 luk 513
   */
514
  ~Inotify();
515
 
516
  /// Removes all watches and closes the inotify device.
517
  void Close();
47 luk 518
 
45 luk 519
  /// Adds a new watch.
520
  /**
521
   * \param[in] pWatch inotify watch
47 luk 522
   *
523
   * \throw InotifyException thrown if adding failed
45 luk 524
   */
47 luk 525
  void Add(InotifyWatch* pWatch) throw (InotifyException);
45 luk 526
 
527
  /// Adds a new watch.
528
  /**
529
   * \param[in] rWatch inotify watch
47 luk 530
   *
531
   * \throw InotifyException thrown if adding failed
45 luk 532
   */
47 luk 533
  inline void Add(InotifyWatch& rWatch) throw (InotifyException)
45 luk 534
  {
47 luk 535
    Add(&rWatch);
45 luk 536
  }
537
 
538
  /// Removes a watch.
539
  /**
540
   * If the given watch is not present it does nothing.
541
   *
542
   * \param[in] pWatch inotify watch
47 luk 543
   *
544
   * \throw InotifyException thrown if removing failed
45 luk 545
   */
47 luk 546
  void Remove(InotifyWatch* pWatch) throw (InotifyException);
45 luk 547
 
548
  /// Removes a watch.
549
  /**
550
   * If the given watch is not present it does nothing.
551
   *
552
   * \param[in] rWatch inotify watch
47 luk 553
   *
554
   * \throw InotifyException thrown if removing failed
45 luk 555
   */
47 luk 556
  inline void Remove(InotifyWatch& rWatch) throw (InotifyException)
45 luk 557
  {
558
    Remove(&rWatch);
559
  }
560
 
561
  /// Removes all watches.
562
  void RemoveAll();
563
 
564
  /// Returns the count of watches.
565
  /**
49 luk 566
   * This is the total count of all watches (regardless whether
567
   * enabled or not).
568
   *
45 luk 569
   * \return count of watches
51 luk 570
   *
571
   * \sa GetEnabledCount()
45 luk 572
   */
573
  inline size_t GetWatchCount() const
574
  {
51 luk 575
    IN_READ_BEGIN
576
    size_t n = (size_t) m_paths.size();
577
    IN_READ_END
578
    return n;
45 luk 579
  }
580
 
51 luk 581
  /// Returns the count of enabled watches.
582
  /**
583
   * \return count of enabled watches
584
   *
585
   * \sa GetWatchCount()
586
   */  
587
  inline size_t GetEnabledCount() const
588
  {
589
    IN_READ_BEGIN
590
    size_t n = (size_t) m_watches.size();
591
    IN_READ_END
592
    return n;
593
  }
594
 
45 luk 595
  /// Waits for inotify events.
596
  /**
47 luk 597
   * It waits until one or more events occur. When called
598
   * in nonblocking mode it only retrieves occurred events
599
   * to the internal queue and exits.
45 luk 600
   *
601
   * \param[in] fNoIntr if true it re-calls the system call after a handled signal
47 luk 602
   *
603
   * \throw InotifyException thrown if reading events failed
604
   *
605
   * \sa SetNonBlock()
45 luk 606
   */
47 luk 607
  void WaitForEvents(bool fNoIntr = false) throw (InotifyException);
45 luk 608
 
609
  /// Returns the count of received and queued events.
610
  /**
611
   * This number is related to the events in the queue inside
612
   * this object, not to the events pending in the kernel.
613
   *
614
   * \return count of events
615
   */
51 luk 616
  inline size_t GetEventCount()
617
  {
618
    IN_READ_BEGIN
619
    size_t n = (size_t) m_events.size();
620
    IN_READ_END
621
    return n;
622
  }
45 luk 623
 
624
  /// Extracts a queued inotify event.
625
  /**
626
   * The extracted event is removed from the queue.
627
   * If the pointer is NULL it does nothing.
628
   *
629
   * \param[in,out] pEvt event object
47 luk 630
   *
631
   * \throw InotifyException thrown if the provided pointer is NULL
45 luk 632
   */
47 luk 633
  bool GetEvent(InotifyEvent* pEvt) throw (InotifyException);
45 luk 634
 
635
  /// Extracts a queued inotify event.
636
  /**
637
   * The extracted event is removed from the queue.
638
   *
639
   * \param[in,out] rEvt event object
47 luk 640
   *
641
   * \throw InotifyException thrown only in very anomalous cases
45 luk 642
   */
47 luk 643
  bool GetEvent(InotifyEvent& rEvt) throw (InotifyException)
45 luk 644
  {
645
    return GetEvent(&rEvt);
646
  }
647
 
648
  /// Extracts a queued inotify event (without removing).
649
  /**
650
   * The extracted event stays in the queue.
651
   * If the pointer is NULL it does nothing.
652
   *
653
   * \param[in,out] pEvt event object
47 luk 654
   *
655
   * \throw InotifyException thrown if the provided pointer is NULL
45 luk 656
   */
47 luk 657
  bool PeekEvent(InotifyEvent* pEvt) throw (InotifyException);
45 luk 658
 
659
  /// Extracts a queued inotify event (without removing).
660
  /**
661
   * The extracted event stays in the queue.
662
   *
663
   * \param[in,out] rEvt event object
47 luk 664
   *
665
   * \throw InotifyException thrown only in very anomalous cases
45 luk 666
   */
47 luk 667
  bool PeekEvent(InotifyEvent& rEvt) throw (InotifyException)
45 luk 668
  {
669
    return PeekEvent(&rEvt);
670
  }
671
 
49 luk 672
  /// Searches for a watch by a watch descriptor.
45 luk 673
  /**
674
   * It tries to find a watch by the given descriptor.
675
   *
676
   * \param[in] iDescriptor watch descriptor
49 luk 677
   * \return pointer to a watch; NULL if no such watch exists
45 luk 678
   */
47 luk 679
  InotifyWatch* FindWatch(int iDescriptor);
680
 
49 luk 681
  /// Searches for a watch by a filesystem path.
682
  /**
683
   * It tries to find a watch by the given filesystem path.
684
   *
685
   * \param[in] rPath filesystem path
686
   * \return pointer to a watch; NULL if no such watch exists
687
   *
688
   * \attention The path must be exactly identical to the one
689
   *            used for the searched watch. Be careful about
690
   *            absolute/relative and case-insensitive paths.
691
   */
692
   InotifyWatch* FindWatch(const std::string& rPath);
693
 
47 luk 694
  /// Returns the file descriptor.
695
  /**
696
   * The descriptor can be used in standard low-level file
697
   * functions (poll(), select(), fcntl() etc.).
698
   *
699
   * \return valid file descriptor or -1 for inactive object
700
   *
701
   * \sa SetNonBlock()
702
   */
703
  inline int GetDescriptor() const
704
  {
705
    return m_fd;
706
  }
707
 
708
  /// Enables/disables non-blocking mode.
709
  /**
710
   * Use this mode if you want to monitor the descriptor
711
   * (acquired thru GetDescriptor()) in functions such as
712
   * poll(), select() etc.
713
   *
714
   * \param[in] fNonBlock enable/disable non-blocking mode
715
   *
716
   * \throw InotifyException thrown if setting mode failed
717
   *
718
   * \sa GetDescriptor()
719
   */
720
  void SetNonBlock(bool fNonBlock) throw (InotifyException);
45 luk 721
 
722
private:
723
  int m_fd;                             ///< file descriptor
49 luk 724
  IN_WATCH_MAP m_watches;               ///< watches (by descriptors)
725
  IN_WP_MAP m_paths;                    ///< watches (by paths)
45 luk 726
  unsigned char m_buf[INOTIFY_BUFLEN];  ///< buffer for events
727
  std::deque<InotifyEvent> m_events;    ///< event queue
49 luk 728
 
51 luk 729
  IN_LOCK_DECL
730
 
49 luk 731
  friend class InotifyWatch;
45 luk 732
};
733
 
734
 
735
#endif //_INOTIFYCXX_H_
51 luk 736