Subversion Repositories public

Rev

Rev 51 | Rev 61 | 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 implementation
3
/**
4
 * \file inotify-cxx.cpp
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
#include <errno.h>
24
#include <unistd.h>
47 luk 25
#include <fcntl.h>
45 luk 26
 
27
#include "inotify-cxx.h"
28
 
47 luk 29
/// dump separator (between particular entries)
45 luk 30
#define DUMP_SEP \
31
  ({ \
32
    if (!rStr.empty()) { \
33
      rStr.append(","); \
34
    } \
35
  })
36
 
47 luk 37
 
55 luk 38
 
47 luk 39
int32_t InotifyEvent::GetDescriptor() const
40
{
41
  return  m_pWatch != NULL            // if watch exists
42
      ?   m_pWatch->GetDescriptor()   // return its descriptor
43
      :   -1;                         // else return -1
44
}
45
 
45 luk 46
uint32_t InotifyEvent::GetMaskByName(const std::string& rName)
47
{
48
  if (rName == "IN_ACCESS")
49
    return IN_ACCESS;
50
  else if (rName == "IN_MODIFY")
51
    return IN_MODIFY;
52
  else if (rName == "IN_ATTRIB")
53
    return IN_ATTRIB;
54
  else if (rName == "IN_CLOSE_WRITE")
55
    return IN_CLOSE_WRITE;
56
  else if (rName == "IN_CLOSE_NOWRITE")
57
    return IN_CLOSE_NOWRITE;
55 luk 58
  else if (rName == "IN_OPEN")
59
    return IN_OPEN;
45 luk 60
  else if (rName == "IN_MOVED_FROM")
61
    return IN_MOVED_FROM;
62
  else if (rName == "IN_MOVED_TO")
63
    return IN_MOVED_TO;
64
  else if (rName == "IN_CREATE")
65
    return IN_CREATE;
66
  else if (rName == "IN_DELETE")
67
    return IN_DELETE;
68
  else if (rName == "IN_DELETE_SELF")
69
    return IN_DELETE_SELF;
70
  else if (rName == "IN_UNMOUNT")
71
    return IN_UNMOUNT;
72
  else if (rName == "IN_Q_OVERFLOW")
73
    return IN_Q_OVERFLOW;
74
  else if (rName == "IN_IGNORED")
75
    return IN_IGNORED;
76
  else if (rName == "IN_CLOSE")
77
    return IN_CLOSE;
78
  else if (rName == "IN_MOVE")
79
    return IN_MOVE;
80
  else if (rName == "IN_ISDIR")
81
    return IN_ISDIR;
82
  else if (rName == "IN_ONESHOT")
83
    return IN_ONESHOT;
84
  else if (rName == "IN_ALL_EVENTS")
85
    return IN_ALL_EVENTS;
86
 
49 luk 87
#ifdef IN_DONT_FOLLOW
88
  else if (rName == "IN_DONT_FOLLOW")
89
    return IN_DONT_FOLLOW;
90
#endif // IN_DONT_FOLLOW
91
 
92
#ifdef IN_ONLYDIR
93
  else if (rName == "IN_ONLYDIR")
94
    return IN_ONLYDIR;
95
#endif // IN_ONLYDIR
96
 
45 luk 97
  return (uint32_t) 0;
98
}
99
 
100
void InotifyEvent::DumpTypes(uint32_t uValue, std::string& rStr)
101
{
102
  rStr = "";
103
 
104
  if (IsType(uValue, IN_ALL_EVENTS)) {
105
    rStr.append("IN_ALL_EVENTS");
106
  }
107
  else {
108
    if (IsType(uValue, IN_ACCESS)) {
109
      DUMP_SEP;
110
      rStr.append("IN_ACCESS");    
111
    }
112
    if (IsType(uValue, IN_MODIFY)) {
113
      DUMP_SEP;
114
      rStr.append("IN_MODIFY");
115
    }
116
    if (IsType(uValue, IN_ATTRIB)) {
117
      DUMP_SEP;
118
      rStr.append("IN_ATTRIB");
119
    }
120
    if (IsType(uValue, IN_CREATE)) {
121
      DUMP_SEP;
122
      rStr.append("IN_CREATE");
123
    }
124
    if (IsType(uValue, IN_DELETE)) {
125
      DUMP_SEP;
126
      rStr.append("IN_DELETE");
127
    }
128
    if (IsType(uValue, IN_DELETE_SELF)) {
129
      DUMP_SEP;
130
      rStr.append("IN_DELETE_SELF");
131
    }
132
    if (IsType(uValue, IN_OPEN)) {
133
      DUMP_SEP;
134
      rStr.append("IN_OPEN");
135
    }
136
    if (IsType(uValue, IN_CLOSE)) {
137
      DUMP_SEP;
138
      rStr.append("IN_CLOSE");
139
    }
140
    else {
141
      if (IsType(uValue, IN_CLOSE_WRITE)) {
142
        DUMP_SEP;
143
        rStr.append("IN_CLOSE_WRITE");
144
      }
145
      if (IsType(uValue, IN_CLOSE_NOWRITE)) {
146
        DUMP_SEP;
147
        rStr.append("IN_CLOSE_NOWRITE");
148
      }
149
    }
150
    if (IsType(uValue, IN_MOVE)) {
151
      DUMP_SEP;
152
      rStr.append("IN_MOVE");
153
    }
154
    else {
155
      if (IsType(uValue, IN_MOVED_FROM)) {
156
        DUMP_SEP;
157
        rStr.append("IN_MOVED_FROM");
158
      }
159
      if (IsType(uValue, IN_MOVED_TO)) {
160
        DUMP_SEP;
161
        rStr.append("IN_MOVED_TO");
162
      }
163
    }
164
  }
165
  if (IsType(uValue, IN_UNMOUNT)) {
166
    DUMP_SEP;
167
    rStr.append("IN_UNMOUNT");
168
  }
169
  if (IsType(uValue, IN_Q_OVERFLOW)) {
170
    DUMP_SEP;
171
    rStr.append("IN_Q_OVERFLOW");
172
  }
173
  if (IsType(uValue, IN_IGNORED)) {
174
    DUMP_SEP;
175
    rStr.append("IN_IGNORED");
176
  }
177
  if (IsType(uValue, IN_ISDIR)) {
178
    DUMP_SEP;
179
    rStr.append("IN_ISDIR");
180
  }
181
  if (IsType(uValue, IN_ONESHOT)) {
182
    DUMP_SEP;
183
    rStr.append("IN_ONESHOT");
184
  }
49 luk 185
 
186
#ifdef IN_DONT_FOLLOW
187
  if (IsType(uValue, IN_DONT_FOLLOW)) {
188
    DUMP_SEP;
189
    rStr.append("IN_DONT_FOLLOW");
190
  }
191
#endif // IN_DONT_FOLLOW
192
 
193
#ifdef IN_ONLYDIR
194
  if (IsType(uValue, IN_ONLYDIR)) {
195
    DUMP_SEP;
196
    rStr.append("IN_ONLYDIR");
197
  }
198
#endif // IN_ONLYDIR
45 luk 199
}
200
 
201
void InotifyEvent::DumpTypes(std::string& rStr) const
202
{
47 luk 203
  DumpTypes(m_uMask, rStr);
45 luk 204
}
205
 
206
 
49 luk 207
void InotifyWatch::SetMask(uint32_t uMask) throw (InotifyException)
208
{
51 luk 209
  IN_WRITE_BEGIN
210
 
49 luk 211
  if (m_wd != -1) {
212
    int wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), uMask);
51 luk 213
    if (wd != m_wd) {
214
      IN_WRITE_END_NOTHROW
49 luk 215
      throw InotifyException(IN_EXC_MSG("changing mask failed"), wd == -1 ? errno : EINVAL, this);
51 luk 216
    }
49 luk 217
  }
218
 
219
  m_uMask = uMask;
51 luk 220
 
221
  IN_WRITE_END
49 luk 222
}
223
 
224
void InotifyWatch::SetEnabled(bool fEnabled) throw (InotifyException)
225
{
51 luk 226
  IN_WRITE_BEGIN
227
 
228
  if (fEnabled == m_fEnabled) {
229
    IN_WRITE_END_NOTHROW
49 luk 230
    return;
51 luk 231
  }
49 luk 232
 
233
  if (m_pInotify != NULL) {
234
    if (fEnabled) {
235
      m_wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), m_uMask);
51 luk 236
      if (m_wd == -1) {
237
        IN_WRITE_END_NOTHROW
49 luk 238
        throw InotifyException(IN_EXC_MSG("enabling watch failed"), errno, this);
51 luk 239
      }
49 luk 240
      m_pInotify->m_watches.insert(IN_WATCH_MAP::value_type(m_wd, this));
241
    }
242
    else {
51 luk 243
      if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) {
244
        IN_WRITE_END_NOTHROW
49 luk 245
        throw InotifyException(IN_EXC_MSG("disabling watch failed"), errno, this);
51 luk 246
      }
49 luk 247
      m_pInotify->m_watches.erase(m_wd);
248
      m_wd = -1;
249
    }
250
  }
251
 
252
  m_fEnabled = fEnabled;
51 luk 253
 
254
  IN_WRITE_END
49 luk 255
}
256
 
257
 
47 luk 258
Inotify::Inotify() throw (InotifyException)
45 luk 259
{
51 luk 260
  IN_LOCK_INIT
261
 
47 luk 262
  m_fd = inotify_init();
51 luk 263
  if (m_fd == -1) {
264
    IN_LOCK_DONE
49 luk 265
    throw InotifyException(IN_EXC_MSG("inotify init failed"), errno, NULL);
51 luk 266
  }
45 luk 267
}
268
 
269
Inotify::~Inotify()
270
{
271
  Close();
51 luk 272
 
273
  IN_LOCK_DONE
45 luk 274
}
275
 
276
void Inotify::Close()
277
{
51 luk 278
  IN_WRITE_BEGIN
279
 
45 luk 280
  if (m_fd != -1) {
281
    RemoveAll();
282
    close(m_fd);
283
    m_fd = -1;
284
  }
51 luk 285
 
286
  IN_WRITE_END
45 luk 287
}
288
 
47 luk 289
void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException)
45 luk 290
{
51 luk 291
  IN_WRITE_BEGIN
292
 
49 luk 293
  // invalid descriptor - this case shouldn't occur - go away
51 luk 294
  if (m_fd == -1) {
295
    IN_WRITE_END_NOTHROW
47 luk 296
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
51 luk 297
  }
49 luk 298
 
299
  // this path already watched - go away  
51 luk 300
  if (FindWatch(pWatch->GetPath()) != NULL) {
301
    IN_WRITE_END_NOTHROW
49 luk 302
    throw InotifyException(IN_EXC_MSG("path already watched"), EBUSY, this);
51 luk 303
  }
49 luk 304
 
305
  // for enabled watch
306
  if (pWatch->IsEnabled()) {
45 luk 307
 
49 luk 308
    // try to add watch to kernel
309
    int wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask());
310
 
311
    // adding failed - go away
51 luk 312
    if (wd == -1) {
313
      IN_WRITE_END_NOTHROW
49 luk 314
      throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this);
51 luk 315
    }
49 luk 316
 
317
    // this path already watched (but defined another way)
318
    InotifyWatch* pW = FindWatch(wd);
319
    if (pW != NULL) {
320
 
321
      // try to recover old watch because it may be modified - then go away
322
      if (inotify_add_watch(m_fd, pW->GetPath().c_str(), pW->GetMask()) < 0) {
51 luk 323
        IN_WRITE_END_NOTHROW
49 luk 324
        throw InotifyException(IN_EXC_MSG("watch collision detected and recovery failed"), errno, this);
325
      }
326
      else {
327
        // recovery failed - go away
51 luk 328
        IN_WRITE_END_NOTHROW
49 luk 329
        throw InotifyException(IN_EXC_MSG("path already watched (but defined another way)"), EBUSY, this);
330
      }
331
    }
332
 
333
    pWatch->m_wd = wd;
334
    m_watches.insert(IN_WATCH_MAP::value_type(pWatch->m_wd, pWatch));
335
  }
336
 
337
  m_paths.insert(IN_WP_MAP::value_type(pWatch->m_path, pWatch));
47 luk 338
  pWatch->m_pInotify = this;
51 luk 339
 
340
  IN_WRITE_END
45 luk 341
}
342
 
47 luk 343
void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException)
45 luk 344
{
51 luk 345
  IN_WRITE_BEGIN
346
 
49 luk 347
  // invalid descriptor - this case shouldn't occur - go away
51 luk 348
  if (m_fd == -1) {
349
    IN_WRITE_END_NOTHROW
47 luk 350
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
51 luk 351
  }
49 luk 352
 
353
  // for enabled watch
354
  if (pWatch->m_wd != -1) {  
45 luk 355
 
49 luk 356
    // removing watch failed - go away
51 luk 357
    if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) {
358
      IN_WRITE_END_NOTHROW
49 luk 359
      throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this);
51 luk 360
    }
49 luk 361
    m_watches.erase(pWatch->m_wd);
362
    pWatch->m_wd = -1;
363
  }
364
 
365
  m_paths.erase(pWatch->m_path);
47 luk 366
  pWatch->m_pInotify = NULL;
51 luk 367
 
368
  IN_WRITE_END
45 luk 369
}
370
 
371
void Inotify::RemoveAll()
372
{
51 luk 373
  IN_WRITE_BEGIN
374
 
49 luk 375
  IN_WP_MAP::iterator it = m_paths.begin();
376
  while (it != m_paths.end()) {
45 luk 377
    InotifyWatch* pW = (*it).second;
49 luk 378
    if (pW->m_wd != -1) {
379
      inotify_rm_watch(m_fd, pW->m_wd);
380
      pW->m_wd = -1;
381
    }
45 luk 382
    pW->m_pInotify = NULL;
383
    it++;
384
  }
385
 
386
  m_watches.clear();
49 luk 387
  m_paths.clear();
51 luk 388
 
389
  IN_WRITE_END
45 luk 390
}
391
 
47 luk 392
void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException)
45 luk 393
{
394
  ssize_t len = 0;
395
 
396
  do {
397
    len = read(m_fd, m_buf, INOTIFY_BUFLEN);
398
  } while (fNoIntr && len == -1 && errno == EINTR);
399
 
47 luk 400
  if (errno == EWOULDBLOCK)
401
    return;
45 luk 402
 
47 luk 403
  if (len < 0)
404
    throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
405
 
51 luk 406
  IN_WRITE_BEGIN
407
 
45 luk 408
  ssize_t i = 0;
409
  while (i < len) {
47 luk 410
    struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i];
411
    InotifyWatch* pW = FindWatch(pEvt->wd);
49 luk 412
    if (pW != NULL) {
47 luk 413
      InotifyEvent evt(pEvt, pW);
414
      m_events.push_back(evt);
415
    }
416
    i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len;
45 luk 417
  }
418
 
51 luk 419
  IN_WRITE_END
45 luk 420
}
421
 
47 luk 422
bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException)
45 luk 423
{
51 luk 424
  if (pEvt == NULL)
425
    throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
47 luk 426
 
51 luk 427
  IN_WRITE_BEGIN
428
 
429
  bool b = !m_events.empty();
430
  if (b) {
431
    *pEvt = m_events.front();
45 luk 432
    m_events.pop_front();
51 luk 433
  }
434
 
435
  IN_WRITE_END
47 luk 436
 
45 luk 437
  return b;
438
}
439
 
47 luk 440
bool Inotify::PeekEvent(InotifyEvent* pEvt) throw (InotifyException)
45 luk 441
{
47 luk 442
  if (pEvt == NULL)
443
    throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
45 luk 444
 
51 luk 445
  IN_READ_BEGIN
446
 
447
  bool b = !m_events.empty();
448
  if (b) {
47 luk 449
    *pEvt = m_events.front();
450
  }
451
 
51 luk 452
  IN_READ_END
453
 
454
  return b;
45 luk 455
}
456
 
457
InotifyWatch* Inotify::FindWatch(int iDescriptor)
458
{
51 luk 459
  IN_READ_BEGIN
460
 
45 luk 461
  IN_WATCH_MAP::iterator it = m_watches.find(iDescriptor);
51 luk 462
  InotifyWatch* pW = it == m_watches.end() ? NULL : (*it).second;
463
 
464
  IN_READ_END
465
 
466
  return pW;
45 luk 467
}
49 luk 468
 
469
InotifyWatch* Inotify::FindWatch(const std::string& rPath)
470
{
51 luk 471
  IN_READ_BEGIN
472
 
49 luk 473
  IN_WP_MAP::iterator it = m_paths.find(rPath);
51 luk 474
  InotifyWatch* pW = it == m_paths.end() ? NULL : (*it).second;
475
 
476
  IN_READ_END
49 luk 477
 
51 luk 478
  return pW;
49 luk 479
}
45 luk 480
 
47 luk 481
void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException)
482
{
51 luk 483
  IN_WRITE_BEGIN
484
 
485
  if (m_fd == -1) {
486
    IN_WRITE_END_NOTHROW
47 luk 487
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
51 luk 488
  }
47 luk 489
 
490
  int res = fcntl(m_fd, F_GETFL);
51 luk 491
  if (res == -1) {
492
    IN_WRITE_END_NOTHROW
47 luk 493
    throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
51 luk 494
  }
47 luk 495
 
496
  if (fNonBlock) {
497
    res |= O_NONBLOCK;
498
  }
499
  else {
500
    res &= ~O_NONBLOCK;
501
  }
502
 
51 luk 503
  if (fcntl(m_fd, F_SETFL, res) == -1) {
504
    IN_WRITE_END_NOTHROW
47 luk 505
    throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
51 luk 506
  }
507
 
508
  IN_WRITE_END
47 luk 509
}  
510