Subversion Repositories public

Rev

Rev 103 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 103 Rev 106
1
1
2
/// inotify C++ interface implementation
2
/// inotify C++ interface implementation
3
/**
3
/**
4
 * \file inotify-cxx.cpp
4
 * \file inotify-cxx.cpp
5
 *
5
 *
6
 * inotify C++ interface
6
 * inotify C++ interface
7
 *
7
 *
8
 * Copyright (C) 2006, 2007, 2009 Lukas Jelinek <lukas@aiken.cz>
8
 * Copyright (C) 2006, 2007, 2009, 2012 Lukas Jelinek <lukas@aiken.cz>
9
 *
9
 *
10
 * This program is free software; you can redistribute it and/or
10
 * This program is free software; you can redistribute it and/or
11
 * modify it under the terms of one of the following licenses:
11
 * modify it under the terms of one of the following licenses:
12
 *
12
 *
13
 * \li 1. X11-style license (see LICENSE-X11)
13
 * \li 1. X11-style license (see LICENSE-X11)
14
 * \li 2. GNU Lesser General Public License, version 2.1 (see LICENSE-LGPL)
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)
15
 * \li 3. GNU General Public License, version 2  (see LICENSE-GPL)
16
 *
16
 *
17
 * If you want to help with choosing the best license for you,
17
 * If you want to help with choosing the best license for you,
18
 * please visit http://www.gnu.org/licenses/license-list.html.
18
 * please visit http://www.gnu.org/licenses/license-list.html.
19
 *
19
 *
20
 * Credits:
20
 * Credits:
21
 *     Mike Frysinger (cleanup of includes)
21
 *     Mike Frysinger (cleanup of includes)
-
 
22
 *     Christian Ruppert (new include to build with GCC 4.4+)
22
 *
23
 *
23
 */
24
 */
24
 
25
 
25
26
26
#include <errno.h>
27
#include <errno.h>
27
#include <unistd.h>
28
#include <unistd.h>
28
#include <fcntl.h>
29
#include <fcntl.h>
29
#include <fstream>
30
#include <fstream>
-
 
31
#include <cstdio>
30
32
31
#include <sys/syscall.h>
33
#include <sys/syscall.h>
32
34
33
// Use this if syscalls not defined
35
// Use this if syscalls not defined
34
#ifndef __NR_inotify_init
36
#ifndef __NR_inotify_init
35
#include <sys/inotify-syscalls.h>
37
#include <sys/inotify-syscalls.h>
36
#endif // __NR_inotify_init
38
#endif // __NR_inotify_init
37
39
38
#include "inotify-cxx.h"
40
#include "inotify-cxx.h"
39
41
40
/// procfs inotify base path
42
/// procfs inotify base path
41
#define PROCFS_INOTIFY_BASE "/proc/sys/fs/inotify/"
43
#define PROCFS_INOTIFY_BASE "/proc/sys/fs/inotify/"
42
44
43
/// dump separator (between particular entries)
45
/// dump separator (between particular entries)
44
#define DUMP_SEP \
46
#define DUMP_SEP \
45
  ({ \
47
  ({ \
46
    if (!rStr.empty()) { \
48
    if (!rStr.empty()) { \
47
      rStr.append(","); \
49
      rStr.append(","); \
48
    } \
50
    } \
49
  })
51
  })
50
52
51
53
52
54
53
int32_t InotifyEvent::GetDescriptor() const
55
int32_t InotifyEvent::GetDescriptor() const
54
{
56
{
55
  return  m_pWatch != NULL            // if watch exists
57
  return  m_pWatch != NULL            // if watch exists
56
      ?   m_pWatch->GetDescriptor()   // return its descriptor
58
      ?   m_pWatch->GetDescriptor()   // return its descriptor
57
      :   -1;                         // else return -1
59
      :   -1;                         // else return -1
58
}
60
}
59
61
60
uint32_t InotifyEvent::GetMaskByName(const std::string& rName)
62
uint32_t InotifyEvent::GetMaskByName(const std::string& rName)
61
{
63
{
62
  if (rName == "IN_ACCESS")
64
  if (rName == "IN_ACCESS")
63
    return IN_ACCESS;
65
    return IN_ACCESS;
64
  else if (rName == "IN_MODIFY")
66
  else if (rName == "IN_MODIFY")
65
    return IN_MODIFY;
67
    return IN_MODIFY;
66
  else if (rName == "IN_ATTRIB")
68
  else if (rName == "IN_ATTRIB")
67
    return IN_ATTRIB;
69
    return IN_ATTRIB;
68
  else if (rName == "IN_CLOSE_WRITE")
70
  else if (rName == "IN_CLOSE_WRITE")
69
    return IN_CLOSE_WRITE;
71
    return IN_CLOSE_WRITE;
70
  else if (rName == "IN_CLOSE_NOWRITE")
72
  else if (rName == "IN_CLOSE_NOWRITE")
71
    return IN_CLOSE_NOWRITE;
73
    return IN_CLOSE_NOWRITE;
72
  else if (rName == "IN_OPEN")
74
  else if (rName == "IN_OPEN")
73
    return IN_OPEN;
75
    return IN_OPEN;
74
  else if (rName == "IN_MOVED_FROM")
76
  else if (rName == "IN_MOVED_FROM")
75
    return IN_MOVED_FROM;
77
    return IN_MOVED_FROM;
76
  else if (rName == "IN_MOVED_TO")
78
  else if (rName == "IN_MOVED_TO")
77
    return IN_MOVED_TO;
79
    return IN_MOVED_TO;
78
  else if (rName == "IN_CREATE")
80
  else if (rName == "IN_CREATE")
79
    return IN_CREATE;
81
    return IN_CREATE;
80
  else if (rName == "IN_DELETE")
82
  else if (rName == "IN_DELETE")
81
    return IN_DELETE;
83
    return IN_DELETE;
82
  else if (rName == "IN_DELETE_SELF")
84
  else if (rName == "IN_DELETE_SELF")
83
    return IN_DELETE_SELF;
85
    return IN_DELETE_SELF;
84
  else if (rName == "IN_UNMOUNT")
86
  else if (rName == "IN_UNMOUNT")
85
    return IN_UNMOUNT;
87
    return IN_UNMOUNT;
86
  else if (rName == "IN_Q_OVERFLOW")
88
  else if (rName == "IN_Q_OVERFLOW")
87
    return IN_Q_OVERFLOW;
89
    return IN_Q_OVERFLOW;
88
  else if (rName == "IN_IGNORED")
90
  else if (rName == "IN_IGNORED")
89
    return IN_IGNORED;
91
    return IN_IGNORED;
90
  else if (rName == "IN_CLOSE")
92
  else if (rName == "IN_CLOSE")
91
    return IN_CLOSE;
93
    return IN_CLOSE;
92
  else if (rName == "IN_MOVE")
94
  else if (rName == "IN_MOVE")
93
    return IN_MOVE;
95
    return IN_MOVE;
94
  else if (rName == "IN_ISDIR")
96
  else if (rName == "IN_ISDIR")
95
    return IN_ISDIR;
97
    return IN_ISDIR;
96
  else if (rName == "IN_ONESHOT")
98
  else if (rName == "IN_ONESHOT")
97
    return IN_ONESHOT;
99
    return IN_ONESHOT;
98
  else if (rName == "IN_ALL_EVENTS")
100
  else if (rName == "IN_ALL_EVENTS")
99
    return IN_ALL_EVENTS;
101
    return IN_ALL_EVENTS;
100
   
102
   
101
#ifdef IN_DONT_FOLLOW
103
#ifdef IN_DONT_FOLLOW
102
  else if (rName == "IN_DONT_FOLLOW")
104
  else if (rName == "IN_DONT_FOLLOW")
103
    return IN_DONT_FOLLOW;
105
    return IN_DONT_FOLLOW;
104
#endif // IN_DONT_FOLLOW
106
#endif // IN_DONT_FOLLOW
105
107
106
#ifdef IN_ONLYDIR
108
#ifdef IN_ONLYDIR
107
  else if (rName == "IN_ONLYDIR")
109
  else if (rName == "IN_ONLYDIR")
108
    return IN_ONLYDIR;
110
    return IN_ONLYDIR;
109
#endif // IN_ONLYDIR
111
#endif // IN_ONLYDIR
110
112
111
#ifdef IN_MOVE_SELF
113
#ifdef IN_MOVE_SELF
112
  else if (rName == "IN_MOVE_SELF")
114
  else if (rName == "IN_MOVE_SELF")
113
    return IN_MOVE_SELF;
115
    return IN_MOVE_SELF;
114
#endif // IN_MOVE_SELF
116
#endif // IN_MOVE_SELF
115
   
117
   
116
  return (uint32_t) 0;
118
  return (uint32_t) 0;
117
}
119
}
118
120
119
void InotifyEvent::DumpTypes(uint32_t uValue, std::string& rStr)
121
void InotifyEvent::DumpTypes(uint32_t uValue, std::string& rStr)
120
{
122
{
121
  rStr = "";
123
  rStr = "";
122
 
124
 
123
  if (IsType(uValue, IN_ALL_EVENTS)) {
125
  if (IsType(uValue, IN_ALL_EVENTS)) {
124
    rStr.append("IN_ALL_EVENTS");
126
    rStr.append("IN_ALL_EVENTS");
125
  }
127
  }
126
  else {
128
  else {
127
    if (IsType(uValue, IN_ACCESS)) {
129
    if (IsType(uValue, IN_ACCESS)) {
128
      DUMP_SEP;
130
      DUMP_SEP;
129
      rStr.append("IN_ACCESS");    
131
      rStr.append("IN_ACCESS");    
130
    }
132
    }
131
    if (IsType(uValue, IN_MODIFY)) {
133
    if (IsType(uValue, IN_MODIFY)) {
132
      DUMP_SEP;
134
      DUMP_SEP;
133
      rStr.append("IN_MODIFY");
135
      rStr.append("IN_MODIFY");
134
    }
136
    }
135
    if (IsType(uValue, IN_ATTRIB)) {
137
    if (IsType(uValue, IN_ATTRIB)) {
136
      DUMP_SEP;
138
      DUMP_SEP;
137
      rStr.append("IN_ATTRIB");
139
      rStr.append("IN_ATTRIB");
138
    }
140
    }
139
    if (IsType(uValue, IN_CREATE)) {
141
    if (IsType(uValue, IN_CREATE)) {
140
      DUMP_SEP;
142
      DUMP_SEP;
141
      rStr.append("IN_CREATE");
143
      rStr.append("IN_CREATE");
142
    }
144
    }
143
    if (IsType(uValue, IN_DELETE)) {
145
    if (IsType(uValue, IN_DELETE)) {
144
      DUMP_SEP;
146
      DUMP_SEP;
145
      rStr.append("IN_DELETE");
147
      rStr.append("IN_DELETE");
146
    }
148
    }
147
    if (IsType(uValue, IN_DELETE_SELF)) {
149
    if (IsType(uValue, IN_DELETE_SELF)) {
148
      DUMP_SEP;
150
      DUMP_SEP;
149
      rStr.append("IN_DELETE_SELF");
151
      rStr.append("IN_DELETE_SELF");
150
    }
152
    }
151
    if (IsType(uValue, IN_OPEN)) {
153
    if (IsType(uValue, IN_OPEN)) {
152
      DUMP_SEP;
154
      DUMP_SEP;
153
      rStr.append("IN_OPEN");
155
      rStr.append("IN_OPEN");
154
    }
156
    }
155
    if (IsType(uValue, IN_CLOSE)) {
157
    if (IsType(uValue, IN_CLOSE)) {
156
      DUMP_SEP;
158
      DUMP_SEP;
157
      rStr.append("IN_CLOSE");
159
      rStr.append("IN_CLOSE");
158
    }
160
    }
159
161
160
#ifdef IN_MOVE_SELF
162
#ifdef IN_MOVE_SELF
161
    if (IsType(uValue, IN_MOVE_SELF)) {
163
    if (IsType(uValue, IN_MOVE_SELF)) {
162
      DUMP_SEP;
164
      DUMP_SEP;
163
      rStr.append("IN_MOVE_SELF");    
165
      rStr.append("IN_MOVE_SELF");    
164
    }
166
    }
165
#endif // IN_MOVE_SELF
167
#endif // IN_MOVE_SELF
166
   
168
   
167
    else {
169
    else {
168
      if (IsType(uValue, IN_CLOSE_WRITE)) {
170
      if (IsType(uValue, IN_CLOSE_WRITE)) {
169
        DUMP_SEP;
171
        DUMP_SEP;
170
        rStr.append("IN_CLOSE_WRITE");
172
        rStr.append("IN_CLOSE_WRITE");
171
      }
173
      }
172
      if (IsType(uValue, IN_CLOSE_NOWRITE)) {
174
      if (IsType(uValue, IN_CLOSE_NOWRITE)) {
173
        DUMP_SEP;
175
        DUMP_SEP;
174
        rStr.append("IN_CLOSE_NOWRITE");
176
        rStr.append("IN_CLOSE_NOWRITE");
175
      }
177
      }
176
    }
178
    }
177
    if (IsType(uValue, IN_MOVE)) {
179
    if (IsType(uValue, IN_MOVE)) {
178
      DUMP_SEP;
180
      DUMP_SEP;
179
      rStr.append("IN_MOVE");
181
      rStr.append("IN_MOVE");
180
    }
182
    }
181
    else {
183
    else {
182
      if (IsType(uValue, IN_MOVED_FROM)) {
184
      if (IsType(uValue, IN_MOVED_FROM)) {
183
        DUMP_SEP;
185
        DUMP_SEP;
184
        rStr.append("IN_MOVED_FROM");
186
        rStr.append("IN_MOVED_FROM");
185
      }
187
      }
186
      if (IsType(uValue, IN_MOVED_TO)) {
188
      if (IsType(uValue, IN_MOVED_TO)) {
187
        DUMP_SEP;
189
        DUMP_SEP;
188
        rStr.append("IN_MOVED_TO");
190
        rStr.append("IN_MOVED_TO");
189
      }
191
      }
190
    }
192
    }
191
  }
193
  }
192
  if (IsType(uValue, IN_UNMOUNT)) {
194
  if (IsType(uValue, IN_UNMOUNT)) {
193
    DUMP_SEP;
195
    DUMP_SEP;
194
    rStr.append("IN_UNMOUNT");
196
    rStr.append("IN_UNMOUNT");
195
  }
197
  }
196
  if (IsType(uValue, IN_Q_OVERFLOW)) {
198
  if (IsType(uValue, IN_Q_OVERFLOW)) {
197
    DUMP_SEP;
199
    DUMP_SEP;
198
    rStr.append("IN_Q_OVERFLOW");
200
    rStr.append("IN_Q_OVERFLOW");
199
  }
201
  }
200
  if (IsType(uValue, IN_IGNORED)) {
202
  if (IsType(uValue, IN_IGNORED)) {
201
    DUMP_SEP;
203
    DUMP_SEP;
202
    rStr.append("IN_IGNORED");
204
    rStr.append("IN_IGNORED");
203
  }
205
  }
204
  if (IsType(uValue, IN_ISDIR)) {
206
  if (IsType(uValue, IN_ISDIR)) {
205
    DUMP_SEP;
207
    DUMP_SEP;
206
    rStr.append("IN_ISDIR");
208
    rStr.append("IN_ISDIR");
207
  }
209
  }
208
  if (IsType(uValue, IN_ONESHOT)) {
210
  if (IsType(uValue, IN_ONESHOT)) {
209
    DUMP_SEP;
211
    DUMP_SEP;
210
    rStr.append("IN_ONESHOT");
212
    rStr.append("IN_ONESHOT");
211
  }
213
  }
212
 
214
 
213
#ifdef IN_DONT_FOLLOW
215
#ifdef IN_DONT_FOLLOW
214
  if (IsType(uValue, IN_DONT_FOLLOW)) {
216
  if (IsType(uValue, IN_DONT_FOLLOW)) {
215
    DUMP_SEP;
217
    DUMP_SEP;
216
    rStr.append("IN_DONT_FOLLOW");
218
    rStr.append("IN_DONT_FOLLOW");
217
  }
219
  }
218
#endif // IN_DONT_FOLLOW
220
#endif // IN_DONT_FOLLOW
219
 
221
 
220
#ifdef IN_ONLYDIR
222
#ifdef IN_ONLYDIR
221
  if (IsType(uValue, IN_ONLYDIR)) {
223
  if (IsType(uValue, IN_ONLYDIR)) {
222
    DUMP_SEP;
224
    DUMP_SEP;
223
    rStr.append("IN_ONLYDIR");
225
    rStr.append("IN_ONLYDIR");
224
  }
226
  }
225
#endif // IN_ONLYDIR
227
#endif // IN_ONLYDIR
226
}
228
}
227
229
228
void InotifyEvent::DumpTypes(std::string& rStr) const
230
void InotifyEvent::DumpTypes(std::string& rStr) const
229
{
231
{
230
  DumpTypes(m_uMask, rStr);
232
  DumpTypes(m_uMask, rStr);
231
}
233
}
232
234
233
235
234
void InotifyWatch::SetMask(uint32_t uMask) throw (InotifyException)
236
void InotifyWatch::SetMask(uint32_t uMask) throw (InotifyException)
235
{
237
{
236
  IN_WRITE_BEGIN
238
  IN_WRITE_BEGIN
237
 
239
 
238
  if (m_wd != -1) {
240
  if (m_wd != -1) {
239
    int wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), uMask);
241
    int wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), uMask);
240
    if (wd != m_wd) {
242
    if (wd != m_wd) {
241
      IN_WRITE_END_NOTHROW
243
      IN_WRITE_END_NOTHROW
242
      throw InotifyException(IN_EXC_MSG("changing mask failed"), wd == -1 ? errno : EINVAL, this);
244
      throw InotifyException(IN_EXC_MSG("changing mask failed"), wd == -1 ? errno : EINVAL, this);
243
    }
245
    }
244
  }
246
  }
245
 
247
 
246
  m_uMask = uMask;
248
  m_uMask = uMask;
247
 
249
 
248
  IN_WRITE_END
250
  IN_WRITE_END
249
}
251
}
250
252
251
void InotifyWatch::SetEnabled(bool fEnabled) throw (InotifyException)
253
void InotifyWatch::SetEnabled(bool fEnabled) throw (InotifyException)
252
{
254
{
253
  IN_WRITE_BEGIN
255
  IN_WRITE_BEGIN
254
 
256
 
255
  if (fEnabled == m_fEnabled) {
257
  if (fEnabled == m_fEnabled) {
256
    IN_WRITE_END_NOTHROW
258
    IN_WRITE_END_NOTHROW
257
    return;
259
    return;
258
  }
260
  }
259
 
261
 
260
  if (m_pInotify != NULL) {
262
  if (m_pInotify != NULL) {
261
    if (fEnabled) {
263
    if (fEnabled) {
262
      m_wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), m_uMask);
264
      m_wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), m_uMask);
263
      if (m_wd == -1) {
265
      if (m_wd == -1) {
264
        IN_WRITE_END_NOTHROW
266
        IN_WRITE_END_NOTHROW
265
        throw InotifyException(IN_EXC_MSG("enabling watch failed"), errno, this);
267
        throw InotifyException(IN_EXC_MSG("enabling watch failed"), errno, this);
266
      }
268
      }
267
      m_pInotify->m_watches.insert(IN_WATCH_MAP::value_type(m_wd, this));
269
      m_pInotify->m_watches.insert(IN_WATCH_MAP::value_type(m_wd, this));
268
    }
270
    }
269
    else {
271
    else {
270
      if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) {
272
      if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) {
271
        IN_WRITE_END_NOTHROW
273
        IN_WRITE_END_NOTHROW
272
        throw InotifyException(IN_EXC_MSG("disabling watch failed"), errno, this);
274
        throw InotifyException(IN_EXC_MSG("disabling watch failed"), errno, this);
273
      }
275
      }
274
      m_pInotify->m_watches.erase(m_wd);
276
      m_pInotify->m_watches.erase(m_wd);
275
      m_wd = -1;
277
      m_wd = -1;
276
    }
278
    }
277
  }
279
  }
278
 
280
 
279
  m_fEnabled = fEnabled;
281
  m_fEnabled = fEnabled;
280
 
282
 
281
  IN_WRITE_END
283
  IN_WRITE_END
282
}
284
}
283
285
284
void InotifyWatch::__Disable()
286
void InotifyWatch::__Disable()
285
{
287
{
286
  IN_WRITE_BEGIN
288
  IN_WRITE_BEGIN
287
 
289
 
288
  if (!m_fEnabled) {
290
  if (!m_fEnabled) {
289
    IN_WRITE_END_NOTHROW
291
    IN_WRITE_END_NOTHROW
290
    throw InotifyException(IN_EXC_MSG("event cannot occur on disabled watch"), EINVAL, this);
292
    throw InotifyException(IN_EXC_MSG("event cannot occur on disabled watch"), EINVAL, this);
291
  }
293
  }
292
 
294
 
293
  if (m_pInotify != NULL) {
295
  if (m_pInotify != NULL) {
294
    m_pInotify->m_watches.erase(m_wd);
296
    m_pInotify->m_watches.erase(m_wd);
295
    m_wd = -1;
297
    m_wd = -1;
296
  }
298
  }
297
 
299
 
298
  m_fEnabled = false;
300
  m_fEnabled = false;
299
 
301
 
300
  IN_WRITE_END
302
  IN_WRITE_END
301
}
303
}
302
304
303
305
304
Inotify::Inotify() throw (InotifyException)
306
Inotify::Inotify() throw (InotifyException)
305
{
307
{
306
  IN_LOCK_INIT
308
  IN_LOCK_INIT
307
 
309
 
308
  m_fd = inotify_init();
310
  m_fd = inotify_init();
309
  if (m_fd == -1) {
311
  if (m_fd == -1) {
310
    IN_LOCK_DONE
312
    IN_LOCK_DONE
311
    throw InotifyException(IN_EXC_MSG("inotify init failed"), errno, NULL);
313
    throw InotifyException(IN_EXC_MSG("inotify init failed"), errno, NULL);
312
  }
314
  }
313
}
315
}
314
 
316
 
315
Inotify::~Inotify()
317
Inotify::~Inotify()
316
{
318
{
317
  Close();
319
  Close();
318
 
320
 
319
  IN_LOCK_DONE
321
  IN_LOCK_DONE
320
}
322
}
321
323
322
void Inotify::Close()
324
void Inotify::Close()
323
{
325
{
324
  IN_WRITE_BEGIN
326
  IN_WRITE_BEGIN
325
 
327
 
326
  if (m_fd != -1) {
328
  if (m_fd != -1) {
327
    RemoveAll();
329
    RemoveAll();
328
    close(m_fd);
330
    close(m_fd);
329
    m_fd = -1;
331
    m_fd = -1;
330
  }
332
  }
331
 
333
 
332
  IN_WRITE_END
334
  IN_WRITE_END
333
}
335
}
334
336
335
void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException)
337
void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException)
336
{
338
{
337
  IN_WRITE_BEGIN
339
  IN_WRITE_BEGIN
338
 
340
 
339
  // invalid descriptor - this case shouldn't occur - go away
341
  // invalid descriptor - this case shouldn't occur - go away
340
  if (m_fd == -1) {
342
  if (m_fd == -1) {
341
    IN_WRITE_END_NOTHROW
343
    IN_WRITE_END_NOTHROW
342
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
344
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
343
  }
345
  }
344
346
345
  // this path already watched - go away  
347
  // this path already watched - go away  
346
  if (FindWatch(pWatch->GetPath()) != NULL) {
348
  if (FindWatch(pWatch->GetPath()) != NULL) {
347
    IN_WRITE_END_NOTHROW
349
    IN_WRITE_END_NOTHROW
348
    throw InotifyException(IN_EXC_MSG("path already watched"), EBUSY, this);
350
    throw InotifyException(IN_EXC_MSG("path already watched"), EBUSY, this);
349
  }
351
  }
350
 
352
 
351
  // for enabled watch
353
  // for enabled watch
352
  if (pWatch->IsEnabled()) {
354
  if (pWatch->IsEnabled()) {
353
   
355
   
354
    // try to add watch to kernel
356
    // try to add watch to kernel
355
    int wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask());
357
    int wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask());
356
   
358
   
357
    // adding failed - go away
359
    // adding failed - go away
358
    if (wd == -1) {
360
    if (wd == -1) {
359
      IN_WRITE_END_NOTHROW
361
      IN_WRITE_END_NOTHROW
360
      throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this);
362
      throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this);
361
    }
363
    }
362
   
364
   
363
    // this path already watched (but defined another way)
365
    // this path already watched (but defined another way)
364
    InotifyWatch* pW = FindWatch(wd);
366
    InotifyWatch* pW = FindWatch(wd);
365
    if (pW != NULL) {
367
    if (pW != NULL) {
366
     
368
     
367
      // try to recover old watch because it may be modified - then go away
369
      // try to recover old watch because it may be modified - then go away
368
      if (inotify_add_watch(m_fd, pW->GetPath().c_str(), pW->GetMask()) < 0) {
370
      if (inotify_add_watch(m_fd, pW->GetPath().c_str(), pW->GetMask()) < 0) {
369
        IN_WRITE_END_NOTHROW
371
        IN_WRITE_END_NOTHROW
370
        throw InotifyException(IN_EXC_MSG("watch collision detected and recovery failed"), errno, this);
372
        throw InotifyException(IN_EXC_MSG("watch collision detected and recovery failed"), errno, this);
371
      }
373
      }
372
      else {
374
      else {
373
        // recovery failed - go away
375
        // recovery failed - go away
374
        IN_WRITE_END_NOTHROW
376
        IN_WRITE_END_NOTHROW
375
        throw InotifyException(IN_EXC_MSG("path already watched (but defined another way)"), EBUSY, this);
377
        throw InotifyException(IN_EXC_MSG("path already watched (but defined another way)"), EBUSY, this);
376
      }
378
      }
377
    }
379
    }
378
   
380
   
379
    pWatch->m_wd = wd;
381
    pWatch->m_wd = wd;
380
    m_watches.insert(IN_WATCH_MAP::value_type(pWatch->m_wd, pWatch));
382
    m_watches.insert(IN_WATCH_MAP::value_type(pWatch->m_wd, pWatch));
381
  }
383
  }
382
 
384
 
383
  m_paths.insert(IN_WP_MAP::value_type(pWatch->m_path, pWatch));
385
  m_paths.insert(IN_WP_MAP::value_type(pWatch->m_path, pWatch));
384
  pWatch->m_pInotify = this;
386
  pWatch->m_pInotify = this;
385
 
387
 
386
  IN_WRITE_END
388
  IN_WRITE_END
387
}
389
}
388
390
389
void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException)
391
void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException)
390
{
392
{
391
  IN_WRITE_BEGIN
393
  IN_WRITE_BEGIN
392
 
394
 
393
  // invalid descriptor - this case shouldn't occur - go away
395
  // invalid descriptor - this case shouldn't occur - go away
394
  if (m_fd == -1) {
396
  if (m_fd == -1) {
395
    IN_WRITE_END_NOTHROW
397
    IN_WRITE_END_NOTHROW
396
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
398
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
397
  }
399
  }
398
 
400
 
399
  // for enabled watch
401
  // for enabled watch
400
  if (pWatch->m_wd != -1) {  
402
  if (pWatch->m_wd != -1) {  
401
   
403
   
402
    // removing watch failed - go away
404
    // removing watch failed - go away
403
    if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) {
405
    if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) {
404
      IN_WRITE_END_NOTHROW
406
      IN_WRITE_END_NOTHROW
405
      throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this);
407
      throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this);
406
    }
408
    }
407
    m_watches.erase(pWatch->m_wd);
409
    m_watches.erase(pWatch->m_wd);
408
    pWatch->m_wd = -1;
410
    pWatch->m_wd = -1;
409
  }
411
  }
410
412
411
  m_paths.erase(pWatch->m_path);
413
  m_paths.erase(pWatch->m_path);
412
  pWatch->m_pInotify = NULL;
414
  pWatch->m_pInotify = NULL;
413
 
415
 
414
  IN_WRITE_END
416
  IN_WRITE_END
415
}
417
}
416
418
417
void Inotify::RemoveAll()
419
void Inotify::RemoveAll()
418
{
420
{
419
  IN_WRITE_BEGIN
421
  IN_WRITE_BEGIN
420
 
422
 
421
  IN_WP_MAP::iterator it = m_paths.begin();
423
  IN_WP_MAP::iterator it = m_paths.begin();
422
  while (it != m_paths.end()) {
424
  while (it != m_paths.end()) {
423
    InotifyWatch* pW = (*it).second;
425
    InotifyWatch* pW = (*it).second;
424
    if (pW->m_wd != -1) {
426
    if (pW->m_wd != -1) {
425
      inotify_rm_watch(m_fd, pW->m_wd);
427
      inotify_rm_watch(m_fd, pW->m_wd);
426
      pW->m_wd = -1;
428
      pW->m_wd = -1;
427
    }
429
    }
428
    pW->m_pInotify = NULL;
430
    pW->m_pInotify = NULL;
429
    it++;
431
    it++;
430
  }
432
  }
431
 
433
 
432
  m_watches.clear();
434
  m_watches.clear();
433
  m_paths.clear();
435
  m_paths.clear();
434
 
436
 
435
  IN_WRITE_END
437
  IN_WRITE_END
436
}
438
}
437
439
438
void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException)
440
void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException)
439
{
441
{
440
  ssize_t len = 0;
442
  ssize_t len = 0;
441
 
443
 
442
  do {
444
  do {
443
    len = read(m_fd, m_buf, INOTIFY_BUFLEN);
445
    len = read(m_fd, m_buf, INOTIFY_BUFLEN);
444
  } while (fNoIntr && len == -1 && errno == EINTR);
446
  } while (fNoIntr && len == -1 && errno == EINTR);
445
 
447
 
446
  if (len == -1 && !(errno == EWOULDBLOCK || errno == EINTR))
448
  if (len == -1 && !(errno == EWOULDBLOCK || errno == EINTR))
447
    throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
449
    throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
448
 
450
 
449
  if (len == -1)
451
  if (len == -1)
450
    return;
452
    return;
451
 
453
 
452
  IN_WRITE_BEGIN
454
  IN_WRITE_BEGIN
453
 
455
 
454
  ssize_t i = 0;
456
  ssize_t i = 0;
455
  while (i < len) {
457
  while (i < len) {
456
    struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i];
458
    struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i];
457
    InotifyWatch* pW = FindWatch(pEvt->wd);
459
    InotifyWatch* pW = FindWatch(pEvt->wd);
458
    if (pW != NULL) {
460
    if (pW != NULL) {
459
      InotifyEvent evt(pEvt, pW);
461
      InotifyEvent evt(pEvt, pW);
460
      if (    InotifyEvent::IsType(pW->GetMask(), IN_ONESHOT)
462
      if (    InotifyEvent::IsType(pW->GetMask(), IN_ONESHOT)
461
          ||  InotifyEvent::IsType(evt.GetMask(), IN_IGNORED))
463
          ||  InotifyEvent::IsType(evt.GetMask(), IN_IGNORED))
462
        pW->__Disable();
464
        pW->__Disable();
463
      m_events.push_back(evt);
465
      m_events.push_back(evt);
464
    }
466
    }
465
    i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len;
467
    i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len;
466
  }
468
  }
467
 
469
 
468
  IN_WRITE_END
470
  IN_WRITE_END
469
}
471
}
470
 
472
 
471
bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException)
473
bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException)
472
{
474
{
473
  if (pEvt == NULL)
475
  if (pEvt == NULL)
474
    throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
476
    throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
475
 
477
 
476
  IN_WRITE_BEGIN
478
  IN_WRITE_BEGIN
477
 
479
 
478
  bool b = !m_events.empty();
480
  bool b = !m_events.empty();
479
  if (b) {
481
  if (b) {
480
    *pEvt = m_events.front();
482
    *pEvt = m_events.front();
481
    m_events.pop_front();
483
    m_events.pop_front();
482
  }
484
  }
483
 
485
 
484
  IN_WRITE_END
486
  IN_WRITE_END
485
   
487
   
486
  return b;
488
  return b;
487
}
489
}
488
 
490
 
489
bool Inotify::PeekEvent(InotifyEvent* pEvt) throw (InotifyException)
491
bool Inotify::PeekEvent(InotifyEvent* pEvt) throw (InotifyException)
490
{
492
{
491
  if (pEvt == NULL)
493
  if (pEvt == NULL)
492
    throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
494
    throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
493
 
495
 
494
  IN_READ_BEGIN
496
  IN_READ_BEGIN
495
 
497
 
496
  bool b = !m_events.empty();
498
  bool b = !m_events.empty();
497
  if (b) {
499
  if (b) {
498
    *pEvt = m_events.front();
500
    *pEvt = m_events.front();
499
  }
501
  }
500
 
502
 
501
  IN_READ_END
503
  IN_READ_END
502
 
504
 
503
  return b;
505
  return b;
504
}
506
}
505
507
506
InotifyWatch* Inotify::FindWatch(int iDescriptor)
508
InotifyWatch* Inotify::FindWatch(int iDescriptor)
507
{
509
{
508
  IN_READ_BEGIN
510
  IN_READ_BEGIN
509
 
511
 
510
  IN_WATCH_MAP::iterator it = m_watches.find(iDescriptor);
512
  IN_WATCH_MAP::iterator it = m_watches.find(iDescriptor);
511
  InotifyWatch* pW = it == m_watches.end() ? NULL : (*it).second;
513
  InotifyWatch* pW = it == m_watches.end() ? NULL : (*it).second;
512
 
514
 
513
  IN_READ_END
515
  IN_READ_END
514
 
516
 
515
  return pW;
517
  return pW;
516
}
518
}
517
519
518
InotifyWatch* Inotify::FindWatch(const std::string& rPath)
520
InotifyWatch* Inotify::FindWatch(const std::string& rPath)
519
{
521
{
520
  IN_READ_BEGIN
522
  IN_READ_BEGIN
521
 
523
 
522
  IN_WP_MAP::iterator it = m_paths.find(rPath);
524
  IN_WP_MAP::iterator it = m_paths.find(rPath);
523
  InotifyWatch* pW = it == m_paths.end() ? NULL : (*it).second;
525
  InotifyWatch* pW = it == m_paths.end() ? NULL : (*it).second;
524
 
526
 
525
  IN_READ_END
527
  IN_READ_END
526
   
528
   
527
  return pW;
529
  return pW;
528
}
530
}
529
 
531
 
530
void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException)
532
void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException)
531
{
533
{
532
  IN_WRITE_BEGIN
534
  IN_WRITE_BEGIN
533
 
535
 
534
  if (m_fd == -1) {
536
  if (m_fd == -1) {
535
    IN_WRITE_END_NOTHROW
537
    IN_WRITE_END_NOTHROW
536
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
538
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
537
  }
539
  }
538
   
540
   
539
  int res = fcntl(m_fd, F_GETFL);
541
  int res = fcntl(m_fd, F_GETFL);
540
  if (res == -1) {
542
  if (res == -1) {
541
    IN_WRITE_END_NOTHROW
543
    IN_WRITE_END_NOTHROW
542
    throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
544
    throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
543
  }
545
  }
544
 
546
 
545
  if (fNonBlock) {
547
  if (fNonBlock) {
546
    res |= O_NONBLOCK;
548
    res |= O_NONBLOCK;
547
  }
549
  }
548
  else {
550
  else {
549
    res &= ~O_NONBLOCK;
551
    res &= ~O_NONBLOCK;
550
  }
552
  }
551
     
553
     
552
  if (fcntl(m_fd, F_SETFL, res) == -1) {
554
  if (fcntl(m_fd, F_SETFL, res) == -1) {
553
    IN_WRITE_END_NOTHROW
555
    IN_WRITE_END_NOTHROW
554
    throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
556
    throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
555
  }
557
  }
556
   
558
   
557
  IN_WRITE_END
559
  IN_WRITE_END
558
}
560
}
559
561
560
void Inotify::SetCloseOnExec(bool fClOnEx) throw (InotifyException)
562
void Inotify::SetCloseOnExec(bool fClOnEx) throw (InotifyException)
561
{
563
{
562
  IN_WRITE_BEGIN
564
  IN_WRITE_BEGIN
563
 
565
 
564
  if (m_fd == -1) {
566
  if (m_fd == -1) {
565
    IN_WRITE_END_NOTHROW
567
    IN_WRITE_END_NOTHROW
566
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
568
    throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
567
  }
569
  }
568
   
570
   
569
  int res = fcntl(m_fd, F_GETFD);
571
  int res = fcntl(m_fd, F_GETFD);
570
  if (res == -1) {
572
  if (res == -1) {
571
    IN_WRITE_END_NOTHROW
573
    IN_WRITE_END_NOTHROW
572
    throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
574
    throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
573
  }
575
  }
574
 
576
 
575
  if (fClOnEx) {
577
  if (fClOnEx) {
576
    res |= FD_CLOEXEC;
578
    res |= FD_CLOEXEC;
577
  }
579
  }
578
  else {
580
  else {
579
    res &= ~FD_CLOEXEC;
581
    res &= ~FD_CLOEXEC;
580
  }
582
  }
581
     
583
     
582
  if (fcntl(m_fd, F_SETFD, res) == -1) {
584
  if (fcntl(m_fd, F_SETFD, res) == -1) {
583
    IN_WRITE_END_NOTHROW
585
    IN_WRITE_END_NOTHROW
584
    throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
586
    throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
585
  }
587
  }
586
   
588
   
587
  IN_WRITE_END
589
  IN_WRITE_END
588
}
590
}
589
591
590
uint32_t Inotify::GetCapability(InotifyCapability_t cap) throw (InotifyException)
592
uint32_t Inotify::GetCapability(InotifyCapability_t cap) throw (InotifyException)
591
{
593
{
592
  FILE* f = fopen(GetCapabilityPath(cap).c_str(), "r");
594
  FILE* f = fopen(GetCapabilityPath(cap).c_str(), "r");
593
  if (f == NULL)
595
  if (f == NULL)
594
    throw InotifyException(IN_EXC_MSG("cannot get capability"), errno, NULL);
596
    throw InotifyException(IN_EXC_MSG("cannot get capability"), errno, NULL);
595
   
597
   
596
  unsigned int val = 0;
598
  unsigned int val = 0;
597
  if (fscanf(f, "%u", &val) != 1) {
599
  if (fscanf(f, "%u", &val) != 1) {
598
    fclose(f);
600
    fclose(f);
599
    throw InotifyException(IN_EXC_MSG("cannot get capability"), EIO, NULL);
601
    throw InotifyException(IN_EXC_MSG("cannot get capability"), EIO, NULL);
600
  }
602
  }
601
 
603
 
602
  fclose(f);
604
  fclose(f);
603
 
605
 
604
  return (uint32_t) val;
606
  return (uint32_t) val;
605
}
607
}
606
608
607
void Inotify::SetCapability(InotifyCapability_t cap, uint32_t val) throw (InotifyException)
609
void Inotify::SetCapability(InotifyCapability_t cap, uint32_t val) throw (InotifyException)
608
{
610
{
609
  FILE* f = fopen(GetCapabilityPath(cap).c_str(), "w");
611
  FILE* f = fopen(GetCapabilityPath(cap).c_str(), "w");
610
  if (f == NULL)
612
  if (f == NULL)
611
    throw InotifyException(IN_EXC_MSG("cannot set capability"), errno, NULL);
613
    throw InotifyException(IN_EXC_MSG("cannot set capability"), errno, NULL);
612
   
614
   
613
  if (fprintf(f, "%u", (unsigned int) val) <= 0) {
615
  if (fprintf(f, "%u", (unsigned int) val) <= 0) {
614
    fclose(f);
616
    fclose(f);
615
    throw InotifyException(IN_EXC_MSG("cannot set capability"), EIO, NULL);
617
    throw InotifyException(IN_EXC_MSG("cannot set capability"), EIO, NULL);
616
  }
618
  }
617
 
619
 
618
  fclose(f);
620
  fclose(f);
619
}
621
}
620
622
621
std::string Inotify::GetCapabilityPath(InotifyCapability_t cap) throw (InotifyException)
623
std::string Inotify::GetCapabilityPath(InotifyCapability_t cap) throw (InotifyException)
622
{
624
{
623
  std::string path(PROCFS_INOTIFY_BASE);
625
  std::string path(PROCFS_INOTIFY_BASE);
624
 
626
 
625
  switch (cap) {
627
  switch (cap) {
626
    case IN_MAX_EVENTS:
628
    case IN_MAX_EVENTS:
627
      path.append("max_queued_events");
629
      path.append("max_queued_events");
628
      break;
630
      break;
629
    case IN_MAX_INSTANCES:
631
    case IN_MAX_INSTANCES:
630
      path.append("max_user_instances");
632
      path.append("max_user_instances");
631
      break;
633
      break;
632
    case IN_MAX_WATCHES:
634
    case IN_MAX_WATCHES:
633
      path.append("max_user_watches");
635
      path.append("max_user_watches");
634
      break;
636
      break;
635
    default:
637
    default:
636
      throw InotifyException(IN_EXC_MSG("unknown capability type"), EINVAL, NULL);
638
      throw InotifyException(IN_EXC_MSG("unknown capability type"), EINVAL, NULL);
637
  }
639
  }
638
 
640
 
639
  return path;
641
  return path;
640
}
642
}
641
643
642
 
644