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