Subversion Repositories public

Rev

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