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 |