Subversion Repositories public

Rev

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