source: Plugins/dynamicResources/inotify-cxx.cpp @ 32

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