Change event sync to async
[platform/framework/native/appfw.git] / src / io / FIo_FileEventManagerImpl.cpp
1 //
2 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Apache License, Version 2.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 /**
18  * @file        FIo_FileEventManager.cpp
19  * @brief       This is the implementation file for FileEventManager class.
20  */
21
22 #include <unistd.h>
23 #include <sys/inotify.h>
24 #include <errno.h>
25 #include <new>
26 #include <unique_ptr.h>
27
28 #include <FBaseInteger.h>
29 #include <FBaseString.h>
30 #include <FBaseSysLog.h>
31 #include <FBaseColAllElementsDeleter.h>
32 #include <FBaseRtIEventArg.h>
33 #include <FIoFile.h>
34 #include <FIoIFileEventListener.h>
35 #include <FIoFileEventManager.h>
36
37 #include <FBase_StringConverter.h>
38 #include <FBase_NativeError.h>
39 #include <FIo_FileEventManagerImpl.h>
40
41 #define INOTIFY_BUFFER_LEN      (1024*(sizeof(struct inotify_event) + 16))
42
43 using namespace std;
44 using namespace Tizen::Base;
45 using namespace Tizen::Base::Runtime;
46 using namespace Tizen::Base::Collection;
47
48 namespace Tizen { namespace Io
49 {
50
51 class _FileEventArg
52         : public IEventArg
53 {
54 public:
55         _FileEventArg(unsigned long event, String path, unsigned int eventId)
56                 : __event(event)
57                 , __path(path)
58                 , __eventId(eventId)
59         {
60         }
61         unsigned long __event;
62         String __path;
63         unsigned int __eventId;
64 };
65
66 class _FileEvent
67         : public _Event
68 {
69 public:
70         _FileEvent(void)
71         {
72                 _Event::Initialize();
73         }
74 protected:
75         virtual void FireImpl(IEventListener& listener, const IEventArg& arg)
76         {
77                 IFileEventListener* pListener = dynamic_cast<IFileEventListener*> (&listener);
78                 if (pListener != null)
79                 {
80                         const _FileEventArg* pArg = dynamic_cast<const _FileEventArg*>(&arg);
81                         if (pArg != null)
82                         {
83                                 pListener->OnFileEventOccured(pArg->__event, pArg->__path, pArg->__eventId);
84                         }
85                 }
86         }
87 };
88
89 _FileEventManagerImpl::_FileEventManagerImpl(void)
90         : __pMonitorFileList(null)
91         , __pEvent(null)
92         , __inotifyFd(-1)
93         , __pGSource(null)
94         , __pGIOChannel(null)
95 {
96 }
97
98 _FileEventManagerImpl::~_FileEventManagerImpl(void)
99 {
100         if (__inotifyFd != -1)
101         {
102                 close(__inotifyFd);
103         }
104
105         if (__pGIOChannel != null)
106         {
107                 g_io_channel_unref(__pGIOChannel);
108         }
109
110         if (__pGSource != null)
111         {
112                 g_source_destroy(__pGSource);
113                 g_source_unref(__pGSource);
114         }
115
116         if (__pMonitorFileList != null)
117         {
118                 __pMonitorFileList->RemoveAll(true);
119                 delete __pMonitorFileList;
120         }
121
122         delete __pEvent;
123 }
124
125 gboolean
126 _FileEventManagerImpl::OnFileEventOccured(GIOChannel* source, GIOCondition condition, gpointer data)
127 {
128         _FileEventManagerImpl* pFileEventManagerImpl = (_FileEventManagerImpl*)data;
129
130         pFileEventManagerImpl->SendEvent();
131
132         return true;
133 }
134
135 result
136 _FileEventManagerImpl::SendEvent(void)
137 {
138         unsigned long iter = 0;
139         ssize_t length = 0;
140         char buffer[INOTIFY_BUFFER_LEN + 1] = {0,};
141
142         unsigned long event = 0;
143         String path;
144         unsigned int eventId = 0;
145         result r = E_IO;
146
147         SysTryReturnResult(NID_IO, __inotifyFd != -1, E_IO, "Failed to inotify.");
148
149         length = read(__inotifyFd, buffer, INOTIFY_BUFFER_LEN);
150         SysTryReturnResult(NID_IO, length < (long)INOTIFY_BUFFER_LEN, E_IO, "Read buffer has failed.");
151
152         while (iter < static_cast<unsigned long> (length))
153         {
154                 struct inotify_event* pEvent = (struct inotify_event*)&buffer[iter];
155                 SysTryReturnResult(NID_IO, pEvent != null, E_IO, "Event info not found.");
156                 event = (unsigned long)pEvent->mask;
157                 eventId = pEvent->cookie;
158
159                 if (pEvent->len)
160                 {
161                         path = pEvent->name;
162                 }
163                 else
164                 {
165                         path = GetRegisteredPath(pEvent->wd);
166                 }
167
168                 _FileEventArg* pEventArg= new (std::nothrow) _FileEventArg(event, path, eventId);
169                  if(pEventArg != null)
170                  {
171                         SysLog(NID_IO, "_FileEventManagerImpl::SendEvent Event Fire");
172                         __pEvent->FireAsync(*pEventArg);
173                         r = E_SUCCESS;
174                  }
175
176                 iter += sizeof(struct inotify_event) + pEvent->len;
177         }
178
179         return r;
180 }
181
182 result
183 _FileEventManagerImpl::Construct(IFileEventListener& listener)
184 {
185         GMainContext* pGContext = null;
186         result r = E_SUCCESS;
187         _FileEvent* pEvent = null;
188         __inotifyFd = inotify_init();
189
190         if (__inotifyFd == -1 && errno == EMFILE)
191         {
192                 SysLogException(NID_IO, E_MAX_EXCEEDED, "The number of opened files has exceeded the maximum limit.");
193                 return E_MAX_EXCEEDED;
194         }
195         SysTryReturnResult(NID_IO, __inotifyFd != -1, E_IO, "Failed to init inotify.");
196
197         unique_ptr<HashMap, AllElementsDeleter> pMonitorFileList(new (std::nothrow) HashMap());
198         SysTryCatch(NID_IO, pMonitorFileList != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Failed to create monitored file list.");
199
200         r = pMonitorFileList->Construct();
201         SysTryCatch(NID_IO, r == E_SUCCESS, , r, "[%s] Propagated.", GetErrorMessage(r));
202
203         pGContext = g_main_context_get_thread_default(); //get own gmain context except default thread
204
205         if (pGContext == null)
206         {
207                 pGContext = g_main_context_default(); //get gmain context from me (default)
208                 SysTryCatch(NID_IO, pGContext != null, r = E_IO, E_IO, "[E_IO] Failed to get glib context.");
209         }
210
211         __pEvent = new (std::nothrow) _FileEvent();
212         SysTryReturnResult(NID_IO, __pEvent != null, E_OUT_OF_MEMORY, "The memory is insufficient.");
213
214         __pEvent->AddListener(listener);
215
216         __pGIOChannel = g_io_channel_unix_new(__inotifyFd); //fd wrapping
217         SysTryCatch(NID_IO, __pGIOChannel != null, r = E_IO, E_IO, "[E_IO] Failed to create glib channel.");
218
219         __pGSource = g_io_create_watch(__pGIOChannel, (GIOCondition)(G_IO_IN | G_IO_ERR | G_IO_NVAL | G_IO_HUP));
220         SysTryCatch(NID_IO, __pGSource != null, r = E_IO, E_IO, "[E_IO] Failed to create glib watch.");
221
222         g_source_set_callback(__pGSource, (GSourceFunc)OnFileEventOccured, this, NULL);
223         g_source_attach(__pGSource, pGContext);
224
225         __pMonitorFileList = pMonitorFileList.release();
226
227         // fall thru
228 CATCH:
229         if (r != E_SUCCESS)
230         {
231                 if (__inotifyFd != -1)
232                 {
233                         close(__inotifyFd);
234                 }
235
236                 if (__pGIOChannel != null)
237                 {
238                         g_io_channel_unref(__pGIOChannel);
239                         __pGIOChannel = null;
240                 }
241
242                 if (__pGSource != null)
243                 {
244                         g_source_destroy(__pGSource);
245                         g_source_unref(__pGSource);
246                         __pGSource = null;
247                 }
248         }
249
250         return r;
251 }
252
253 result
254 _FileEventManagerImpl::AddPath(const String& path, unsigned long eventsToMonitor)
255 {
256         int monitoredFd = 0;
257         result r = E_SUCCESS;
258
259         SysTryReturnResult(NID_IO, File::IsFileExist(path) == true, E_FILE_NOT_FOUND,
260                         "path[%ls] dose not exist.", path.GetPointer());
261
262         SysLog(NID_IO, "path:%ls", path.GetPointer());
263
264         unique_ptr<char[]> pPath(_StringConverter::CopyToCharArrayN(path));
265         SysTryReturnResult(NID_IO, pPath != null, E_IO, "String covert is failed.");
266
267         monitoredFd = inotify_add_watch(__inotifyFd, pPath.get(), eventsToMonitor);
268
269         if (monitoredFd == -1)
270         {
271                 r = E_IO;
272                 switch (errno)
273                 {
274                 case EACCES:
275                         SysLogException(NID_IO, E_IO, "Read access to the given file[%s] is not permitted.", pPath.get());
276                         r = E_ILLEGAL_ACCESS;
277                         break;
278                 case EFAULT:
279                         SysLogException(NID_IO, E_IO, "The path[%s] points outside of the process's accessible address space.", pPath.get());
280                         r = E_ILLEGAL_ACCESS;
281                         break;
282                 case EBADF:
283                         SysLogException(NID_IO, E_IO, "The given file descriptor is not valid.");
284                         break;
285                 case EINVAL:
286                         SysLogException(NID_IO, E_INVALID_ARG, "The given event mask contains no valid events; or fd is not an inotify file descriptor.");
287                         r = E_INVALID_ARG;
288                         break;
289                 case ENOMEM:
290                         SysLogException(NID_IO, E_IO, "Insufficient kernel memory was available.");
291                         break;
292                 case ENOSPC:
293                         SysLogException(NID_IO, E_IO, "The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource");
294                         break;
295                 default:
296                         break;
297                 }
298
299                 return r;
300         }
301
302         unique_ptr<String> pMonitoredPath(new (std::nothrow) String(path));
303         unique_ptr<Integer> pMonitoredFd(new (std::nothrow) Integer(monitoredFd));
304         SysTryReturnResult(NID_IO, pMonitoredPath != null && pMonitoredFd != null, E_IO, "Failed to allocate memory");
305
306         SysTryReturnResult(NID_IO, __pMonitorFileList != null, E_IO, "Monitored file list is null");
307
308         r = __pMonitorFileList->Add(*pMonitoredPath.release(), *pMonitoredFd.release());
309         SysTryReturnResult(NID_IO, r == E_SUCCESS || r == E_OBJ_ALREADY_EXIST, E_IO, "Failed to add on monitored list.");
310
311         return r;
312 }
313
314 result
315 _FileEventManagerImpl::RemovePath(const String& path)
316 {
317         Integer* pMonitoredFd = null;
318
319         SysTryReturnResult(NID_IO, __pMonitorFileList != null, E_IO, "Monitored file list is null");
320
321         pMonitoredFd = (Integer*)__pMonitorFileList->GetValue(path);
322
323         SysTryReturnResult(NID_IO, pMonitoredFd != null, E_IO, "path[%ls] is not registered.", path.GetPointer());
324
325         SysTryReturnResult(NID_IO, inotify_rm_watch(__inotifyFd, pMonitoredFd->ToInt()) == 0,
326                                 E_IO, "path[%ls] is not registered.", path.GetPointer());
327
328         SysTryReturnResult(NID_IO, __pMonitorFileList->Remove(path, true) == E_SUCCESS,
329                                 E_IO, "path[%ls] is not registered.", path.GetPointer());
330         return E_SUCCESS;
331 }
332
333 String
334 _FileEventManagerImpl::GetRegisteredPath(int fd)
335 {
336         String* pRegisteredPath = null;
337         Integer* pRegisteredFD = null;
338
339         if (__pMonitorFileList == null)
340         {
341                 SysLog(NID_IO, "There is no path for fd:%d", fd);
342                 return "";
343         }
344
345         unique_ptr<IMapEnumerator> pEnumerator(__pMonitorFileList->GetMapEnumeratorN());
346         SysTryReturn(NID_IO, pEnumerator != null, L"", GetLastResult(), "[%s] Monitored file list is empty", GetErrorMessage(GetLastResult()));
347
348         while (pEnumerator && pEnumerator->MoveNext() == E_SUCCESS)
349         {
350                 pRegisteredPath = static_cast<String*> (pEnumerator->GetKey());
351                 pRegisteredFD = static_cast<Integer*> (pEnumerator->GetValue());
352
353                 if (fd == pRegisteredFD->ToInt())
354                 {
355                         SysLog(NID_IO, "Path is %ls", pRegisteredPath->GetPointer());
356                         return *pRegisteredPath;
357                 }
358
359         }
360
361         SysLog(NID_IO, "There is no path for fd:%d", fd);
362         return "";
363 }
364
365 _FileEventManagerImpl*
366 _FileEventManagerImpl::GetInstance(FileEventManager& fileEventManager)
367 {
368         return fileEventManager.__pFileEventManagerImpl;
369 }
370
371 const _FileEventManagerImpl*
372 _FileEventManagerImpl::GetInstance(const FileEventManager& fileEventManager)
373 {
374         return fileEventManager.__pFileEventManagerImpl;
375 }
376
377 }} // Tizen::Io
378