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