Subversion Repositories public

Rev

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

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