1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/globals.h" |
6 | #if defined(HOST_OS_LINUX) |
7 | |
8 | #include "bin/file_system_watcher.h" |
9 | |
10 | #include <errno.h> // NOLINT |
11 | #include <sys/inotify.h> // NOLINT |
12 | |
13 | #include "bin/fdutils.h" |
14 | #include "bin/file.h" |
15 | #include "bin/socket.h" |
16 | #include "platform/signal_blocker.h" |
17 | |
18 | namespace dart { |
19 | namespace bin { |
20 | |
21 | bool FileSystemWatcher::IsSupported() { |
22 | return true; |
23 | } |
24 | |
25 | intptr_t FileSystemWatcher::Init() { |
26 | int id = NO_RETRY_EXPECTED(inotify_init1(IN_CLOEXEC)); |
27 | if (id < 0) { |
28 | return -1; |
29 | } |
30 | // Some systems dosn't support setting this as non-blocking. Since watching |
31 | // internals are kept away from the user, we know it's possible to continue, |
32 | // even if setting non-blocking fails. |
33 | FDUtils::SetNonBlocking(id); |
34 | return id; |
35 | } |
36 | |
37 | void FileSystemWatcher::Close(intptr_t id) { |
38 | USE(id); |
39 | } |
40 | |
41 | intptr_t FileSystemWatcher::WatchPath(intptr_t id, |
42 | Namespace* namespc, |
43 | const char* path, |
44 | int events, |
45 | bool recursive) { |
46 | int list_events = IN_DELETE_SELF | IN_MOVE_SELF; |
47 | if ((events & kCreate) != 0) { |
48 | list_events |= IN_CREATE; |
49 | } |
50 | if ((events & kModifyContent) != 0) { |
51 | list_events |= IN_CLOSE_WRITE | IN_ATTRIB; |
52 | } |
53 | if ((events & kDelete) != 0) { |
54 | list_events |= IN_DELETE; |
55 | } |
56 | if ((events & kMove) != 0) { |
57 | list_events |= IN_MOVE; |
58 | } |
59 | const char* resolved_path = File::GetCanonicalPath(namespc, path); |
60 | path = resolved_path != NULL ? resolved_path : path; |
61 | int path_id = NO_RETRY_EXPECTED(inotify_add_watch(id, path, list_events)); |
62 | if (path_id < 0) { |
63 | return -1; |
64 | } |
65 | return path_id; |
66 | } |
67 | |
68 | void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) { |
69 | VOID_NO_RETRY_EXPECTED(inotify_rm_watch(id, path_id)); |
70 | } |
71 | |
72 | intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) { |
73 | USE(path_id); |
74 | return id; |
75 | } |
76 | |
77 | static int InotifyEventToMask(struct inotify_event* e) { |
78 | int mask = 0; |
79 | if ((e->mask & IN_CLOSE_WRITE) != 0) { |
80 | mask |= FileSystemWatcher::kModifyContent; |
81 | } |
82 | if ((e->mask & IN_ATTRIB) != 0) { |
83 | mask |= FileSystemWatcher::kModefyAttribute; |
84 | } |
85 | if ((e->mask & IN_CREATE) != 0) { |
86 | mask |= FileSystemWatcher::kCreate; |
87 | } |
88 | if ((e->mask & IN_MOVE) != 0) { |
89 | mask |= FileSystemWatcher::kMove; |
90 | } |
91 | if ((e->mask & IN_DELETE) != 0) { |
92 | mask |= FileSystemWatcher::kDelete; |
93 | } |
94 | if ((e->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) != 0) { |
95 | mask |= FileSystemWatcher::kDeleteSelf; |
96 | } |
97 | if ((e->mask & IN_ISDIR) != 0) { |
98 | mask |= FileSystemWatcher::kIsDir; |
99 | } |
100 | return mask; |
101 | } |
102 | |
103 | Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) { |
104 | USE(path_id); |
105 | const intptr_t kEventSize = sizeof(struct inotify_event); |
106 | const intptr_t kBufferSize = kEventSize + NAME_MAX + 1; |
107 | uint8_t buffer[kBufferSize]; |
108 | intptr_t bytes = |
109 | SocketBase::Read(id, buffer, kBufferSize, SocketBase::kAsync); |
110 | if (bytes < 0) { |
111 | return DartUtils::NewDartOSError(); |
112 | } |
113 | const intptr_t kMaxCount = bytes / kEventSize; |
114 | Dart_Handle events = Dart_NewList(kMaxCount); |
115 | intptr_t offset = 0; |
116 | intptr_t i = 0; |
117 | while (offset < bytes) { |
118 | struct inotify_event* e = |
119 | reinterpret_cast<struct inotify_event*>(buffer + offset); |
120 | if ((e->mask & IN_IGNORED) == 0) { |
121 | Dart_Handle event = Dart_NewList(5); |
122 | int mask = InotifyEventToMask(e); |
123 | Dart_ListSetAt(event, 0, Dart_NewInteger(mask)); |
124 | Dart_ListSetAt(event, 1, Dart_NewInteger(e->cookie)); |
125 | if (e->len > 0) { |
126 | Dart_Handle name = Dart_NewStringFromUTF8( |
127 | reinterpret_cast<uint8_t*>(e->name), strlen(e->name)); |
128 | if (Dart_IsError(name)) { |
129 | return name; |
130 | } |
131 | Dart_ListSetAt(event, 2, name); |
132 | } else { |
133 | Dart_ListSetAt(event, 2, Dart_Null()); |
134 | } |
135 | Dart_ListSetAt(event, 3, Dart_NewBoolean((e->mask & IN_MOVED_TO) != 0u)); |
136 | Dart_ListSetAt(event, 4, Dart_NewInteger(e->wd)); |
137 | Dart_ListSetAt(events, i, event); |
138 | i++; |
139 | } |
140 | offset += kEventSize + e->len; |
141 | } |
142 | ASSERT(offset == bytes); |
143 | return events; |
144 | } |
145 | |
146 | } // namespace bin |
147 | } // namespace dart |
148 | |
149 | #endif // defined(HOST_OS_LINUX) |
150 | |