Subversion Repositories public

Rev

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