* wrapping DLT calls in a new Class because of performance, codesize and lazyness...
[profile/ivi/audiomanager.git] / AudioManagerDaemon / src / SocketHandler.cpp
1 /**
2  * Copyright (C) 2011, BMW AG
3  *
4  * GeniviAudioMananger AudioManagerDaemon
5  *
6  * \file DBusWrapper.cpp
7  *
8  * \date 20-Oct-2011 3:42:04 PM
9  * \author Christian Mueller (christian.ei.mueller@bmw.de)
10  *
11  * \section License
12  * GNU Lesser General Public License, version 2.1, with special exception (GENIVI clause)
13  * Copyright (C) 2011, BMW AG Christian Mueller  Christian.ei.mueller@bmw.de
14  *
15  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License, version 2.1, as published by the Free Software Foundation.
16  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License, version 2.1, for more details.
17  * You should have received a copy of the GNU Lesser General Public License, version 2.1, along with this program; if not, see <http://www.gnu.org/licenses/lgpl-2.1.html>.
18  * Note that the copyright holders assume that the GNU Lesser General Public License, version 2.1, may also be applicable to programs even in cases in which the program is not a library in the technical sense.
19  * Linking AudioManager statically or dynamically with other modules is making a combined work based on AudioManager. You may license such other modules under the GNU Lesser General Public License, version 2.1. If you do not want to license your linked modules under the GNU Lesser General Public License, version 2.1, you may use the program under the following exception.
20  * As a special exception, the copyright holders of AudioManager give you permission to combine AudioManager with software programs or libraries that are released under any license unless such a combination is not permitted by the license of such a software program or library. You may copy and distribute such a system following the terms of the GNU Lesser General Public License, version 2.1, including this special exception, for AudioManager and the licenses of the other code concerned.
21  * Note that people who make modified versions of AudioManager are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU Lesser General Public License, version 2.1, gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception.
22  *
23  */
24
25 #include "SocketHandler.h"
26 #include <config.h>
27 #include <assert.h>
28 #include <sys/fcntl.h>
29 #include <sys/errno.h>
30 #include <sys/poll.h>
31 #include <algorithm>
32 #include <time.h>
33 #include <features.h>
34 #include <signal.h>
35 #include "DLTWrapper.h"
36
37 //todo: implement time correction if timer was interrupted by call
38
39 using namespace am;
40
41 SocketHandler::SocketHandler() :
42         mListPoll(), //
43         mListTimer(), //
44         mListActiveTimer(), //
45         mNextTimer(), //
46         mLastInsertedHandle(0), //
47         mLastInsertedPollHandle(0), //
48         mRecreatePollfds(true), //
49         mTimeout()
50 {
51     mTimeout.tv_nsec = -1;
52     mTimeout.tv_sec = -1;
53     gDispatchDone = 0;
54 }
55
56 SocketHandler::~SocketHandler()
57 {
58 }
59
60 //todo: maybe have some: give me more time returned?
61 /**
62  * start the block listening for filedescriptors. This is the mainloop.
63  */
64 void SocketHandler::start_listenting()
65 {
66     int16_t pollStatus;
67     std::list<int16_t> hitList;
68
69     //init the timer
70     initTimer();
71
72     //prepare the signalmask
73     sigset_t sigmask;
74     sigemptyset(&sigmask);
75     sigaddset(&sigmask, SIGINT);
76     sigaddset(&sigmask, SIGQUIT);
77     sigaddset(&sigmask, SIGTERM);
78     sigaddset(&sigmask, SIGHUP);
79     sigaddset(&sigmask, SIGQUIT);
80
81     while (!gDispatchDone)
82     {
83         //first we go through the registered filedescriptors and check if someone needs preparation:
84         mListPoll_t::iterator prepIter = mListPoll.begin();
85         shPollPrepare *prep = NULL;
86         for (; prepIter != mListPoll.end(); ++prepIter)
87         {
88             if ((prep = prepIter->prepareCB) != NULL) prep->Call(prepIter->handle, prepIter->userData);
89         }
90
91         if (mRecreatePollfds)
92         {
93             mfdPollingArray.clear();
94             //there was a change in the setup, so we need to recreate the fdarray from the list
95             std::for_each(mListPoll.begin(), mListPoll.end(), CopyPollfd(mfdPollingArray));
96             mRecreatePollfds = false;
97         }
98
99         //block until something is on a filedescriptor
100 #ifdef WITH_PPOLL
101
102         timespec buffertime;
103         if ((pollStatus = ppoll(&mfdPollingArray.front(), mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0)
104         {
105             if (errno == EINTR)
106             {
107                 //a signal was received, that means it's time to go...
108                 pollStatus = 0;
109             }
110             else
111             {
112                 logError("SocketHandler::start_listenting ppoll returned with error",errno);
113                 exit(0);
114             }
115         }
116
117 #else
118         //sigprocmask (SIG_SETMASK, &mask, &oldmask);
119         if((pollStatus=poll(&mfdPollingArray.front(),mfdPollingArray.size(),timespec2ms(mTimeout)))<0)
120         {
121
122             if(errno==EINTR)
123             {
124                 //a signal was received, that means it's time to go...
125                 //todo: add things to do here before going to sleep
126                 exit(0);
127             }
128             logError("SocketHandler::start_listenting poll returned with error",errno);
129             exit(0);
130         }
131         //sigprocmask (SIG_SETMASK, &oldmask, NULL);
132 #endif
133
134         if (pollStatus != 0) //only check filedescriptors if there was a change
135         {
136             //todo: here could be a timer that makes sure naughty plugins return!
137
138             //get all indexes of the fired events and save them int hitList
139             hitList.clear();
140             std::vector<pollfd>::iterator it = mfdPollingArray.begin();
141             do
142             {
143                 it = std::find_if(it, mfdPollingArray.end(), onlyFiredEvents);
144                 if (it != mfdPollingArray.end()) hitList.push_back(std::distance(mfdPollingArray.begin(), it++));
145
146             } while (it != mfdPollingArray.end());
147
148             //stage 1, call firedCB for all matched events, but only if callback is not zero!
149             std::list<int16_t>::iterator hListIt = hitList.begin();
150             for (; hListIt != hitList.end(); ++hListIt)
151             {
152                 shPollFired* fire = NULL;
153                 if ((fire = mListPoll.at(*hListIt).firedCB) != NULL) fire->Call(mfdPollingArray.at(*hListIt), mListPoll.at(*hListIt).handle, mListPoll.at(*hListIt).userData);
154             }
155
156             //stage 2, lets ask around if some dispatching is necessary, if not, they are taken from the hitlist
157             hListIt = hitList.begin();
158             for (; hListIt != hitList.end(); ++hListIt)
159             {
160                 shPollCheck* check = NULL;
161                 if ((check = mListPoll.at(*hListIt).checkCB) != NULL)
162                 {
163                     if (!check->Call(mListPoll.at(*hListIt).handle, mListPoll.at(*hListIt).userData))
164                     {
165                         hListIt = hitList.erase(hListIt);
166                     }
167                 }
168             }
169
170             //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
171             do
172             {
173                 hListIt = hitList.begin();
174                 for (; hListIt != hitList.end(); ++hListIt)
175                 {
176                     shPollDispatch *dispatch = NULL;
177                     if ((dispatch = mListPoll.at(*hListIt).dispatchCB) != NULL)
178                     {
179                         if (!dispatch->Call(mListPoll.at(*hListIt).handle, mListPoll.at(*hListIt).userData))
180                         {
181                             hListIt = hitList.erase(hListIt);
182                         }
183                     }
184                     else //there is no dispatch function, so we just remove the file from the list...
185                     {
186                         hListIt = hitList.erase(hListIt);
187                     }
188                 }
189             } while (!hitList.empty());
190
191         }
192         else //Timerevent
193         {
194             //this was a timer event, we need to take care about the timers
195             timerUp();
196         }
197     }
198 }
199
200 /**
201  * exits the loop
202  */
203 void SocketHandler::stop_listening()
204 {
205     gDispatchDone = 1;
206 }
207
208 /**
209  * Adds a filedescriptor to the polling loop
210  * @param fd this is a valid filedescriptor
211  * @param event the event flags
212  * @param callback the callback that shall be called if the filedescriptor poll succeeded
213  * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
214  */
215 am_Error_e SocketHandler::addFDPoll(const int fd, const int16_t event, shPollPrepare *prepare, shPollFired *fired, shPollCheck *check, shPollDispatch *dispatch, void* userData, sh_pollHandle_t& handle)
216 {
217     if (!fdIsValid(fd)) return E_NON_EXISTENT;
218
219     sh_poll_s pollData;
220     pollData.pollfdValue.fd = fd;
221     pollData.handle = ++mLastInsertedPollHandle;
222     pollData.pollfdValue.events = event;
223     pollData.pollfdValue.revents = 0;
224     pollData.userData = userData;
225     pollData.prepareCB = prepare;
226     pollData.firedCB = fired;
227     pollData.checkCB = check;
228     pollData.dispatchCB = dispatch;
229
230     //add new data to the list
231     mListPoll.push_back(pollData);
232
233     mRecreatePollfds = true;
234
235     handle = pollData.handle;
236     return E_OK;
237 }
238
239 /**
240  * removes a filedescriptor from the poll loop
241  * @param fd the filedescriptor to be removed
242  * @return E_OK in case of sucess, E_NON_EXISTENT or E_UNKNOWN if the fd in not registered
243  */
244 am_Error_e SocketHandler::removeFDPoll(const sh_pollHandle_t handle)
245 {
246     mListPoll_t::iterator iterator = mListPoll.begin();
247
248     for (; iterator != mListPoll.end(); ++iterator)
249     {
250         if (iterator->handle == handle)
251         {
252             iterator = mListPoll.erase(iterator);
253             mRecreatePollfds = true;
254             return E_OK;
255         }
256     }
257     return E_UNKNOWN;
258 }
259
260 /**
261  * adds a timer to the list of timers. The callback will be fired when the timer is up.
262  * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
263  * for an answer via a filedescriptor.
264  * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
265  * @param timeouts time until the callback is fired
266  * @param callback the callback
267  * @param handle the handle that is created for the timer is returned. Can be used to remove the timer
268  * @return E_OK in case of success
269  */
270 am_Error_e SocketHandler::addTimer(const timespec timeouts, shTimerCallBack*& callback, sh_timerHandle_t& handle, void * userData)
271 {
272     assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
273     assert(callback!=NULL);
274
275     timer_s timerItem;
276
277     //create a new handle for the timer
278     handle = ++mLastInsertedHandle; //todo: overflow ruling !
279     timerItem.handle = handle;
280     timerItem.countdown = timeouts;
281     timerItem.timeout = timeouts;
282     timerItem.callback = callback;
283     timerItem.userData = userData;
284
285     //add timer to the list
286     mListActiveTimer.push_back(timerItem);
287     mListTimer.push_back(timerItem);
288
289     //very important: sort the list so that the smallest value is front
290     mListActiveTimer.sort(compareCountdown);
291     mTimeout = mListActiveTimer.front().countdown;
292     return E_OK;
293 }
294
295 /**
296  * removes a timer from the list of timers
297  * @param handle the handle to the timer
298  * @return E_OK in case of success, E_UNKNOWN if timer was not found.
299  */
300 am_Error_e SocketHandler::removeTimer(const sh_timerHandle_t handle)
301 {
302     assert(handle!=0);
303
304     //stop the current timer
305     stopTimer(handle);
306
307     std::list<timer_s>::iterator it = mListTimer.begin();
308     for (; it != mListTimer.end(); ++it)
309     {
310         if (it->handle == handle)
311         {
312             it = mListTimer.erase(it);
313             return E_OK;
314         }
315     }
316     return E_UNKNOWN;
317 }
318
319 /**
320  * restarts a timer and updates with a new interval
321  * @param handle handle to the timer
322  * @param timeouts new timout time
323  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
324  */
325 am_Error_e SocketHandler::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
326 {
327     timer_s timerItem;
328     std::list<timer_s>::iterator it = mListTimer.begin();
329     for (; it != mListTimer.end(); ++it)
330     {
331         if (it->handle == handle)
332         {
333             timerItem = *it;
334             break;
335         }
336     }
337
338     if (timeouts.tv_nsec != -1 && timeouts.tv_sec != -1)
339     {
340         timerItem.timeout = timeouts;
341     }
342
343     mListActiveTimer.push_back(timerItem);
344
345     //very important: sort the list so that the smallest value is front
346     mListActiveTimer.sort(compareCountdown);
347     mTimeout = mListActiveTimer.front().countdown;
348     return E_OK;
349 }
350
351 am_Error_e SocketHandler::stopTimer(const sh_timerHandle_t handle)
352 {
353     //go through the list and remove the timer with the handle
354     std::list<timer_s>::iterator it = mListActiveTimer.begin();
355     for (; it != mListActiveTimer.end(); ++it)
356     {
357         if (it->handle == handle)
358         {
359             it = mListActiveTimer.erase(it);
360             if (!mListActiveTimer.empty())
361             {
362                 mTimeout = mListActiveTimer.front().countdown;
363             }
364             else
365             {
366                 mTimeout.tv_nsec = -1;
367                 mTimeout.tv_sec = -1;
368             }
369             return E_OK;
370         }
371     }
372     return E_NON_EXISTENT;
373 }
374
375 /**
376  * updates the eventFlags of a poll
377  * @param fd the filedescriptor of the poll
378  * @param event the event flags
379  * @return E_OK on succsess, E_NON_EXISTENT if fd was not found
380  */
381 am_Error_e SocketHandler::updateEventFlags(const sh_pollHandle_t handle, const int16_t events)
382 {
383     mListPoll_t::iterator iterator = mListPoll.begin();
384
385     for (; iterator != mListPoll.end(); ++iterator)
386     {
387         if (iterator->handle == handle)
388         {
389             iterator->pollfdValue.events = events;
390             mRecreatePollfds = true;
391             return E_OK;
392         }
393     }
394     return E_UNKNOWN;
395 }
396
397 /**
398  * checks if a filedescriptor is valid
399  * @param fd the filedescriptor
400  * @return true if the fd is valid
401  */
402 bool SocketHandler::fdIsValid(const int fd) const
403 {
404     return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
405 }
406
407 /**
408  * whenever a timer is up, this function needs to be called.
409  * Removes the fired timer, calls the callback and resets mTimeout
410  */
411 void SocketHandler::timerUp()
412 {
413     //first fire the event
414     mListActiveTimer.front().callback->Call(mListActiveTimer.front().handle, mListActiveTimer.front().userData);
415
416     //then remove the first timer, the one who fired
417     mListActiveTimer.pop_front();
418     if (!mListActiveTimer.empty())
419     {
420         //substract the old value from all timers in the list
421         std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), SubstractTime(mTimeout));
422         mTimeout = mListActiveTimer.front().countdown;
423     }
424     else
425     {
426         mTimeout.tv_nsec = -1;
427         mTimeout.tv_sec = -1;
428     }
429 }
430
431 /**
432  * init the timers
433  */
434 void SocketHandler::initTimer()
435 {
436     if (!mListActiveTimer.empty())
437     {
438         mTimeout = mListActiveTimer.front().countdown;
439     }
440     else
441     {
442         mTimeout.tv_nsec = -1;
443         mTimeout.tv_sec = -1;
444     }
445 }
446
447 /**
448  * convert timespec to milliseconds
449  * @param time time in timespec
450  * @return time in milliseconds
451  */
452 inline int SocketHandler::timespec2ms(const timespec & time)
453 {
454     return (time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000;
455 }
456
457 inline timespec* am::SocketHandler::insertTime(timespec& buffertime)
458 {
459     buffertime.tv_nsec = mTimeout.tv_nsec;
460     buffertime.tv_sec = mTimeout.tv_sec;
461     return (mTimeout.tv_nsec == -1 && mTimeout.tv_sec == -1) ? NULL : &buffertime;
462 }
463
464 /**
465  * functor to easy substract from each countdown
466  * @param t value to substract from
467  */
468 void SocketHandler::SubstractTime::operator ()(timer_s & t) const
469 {
470     int val = 0;
471     if ((val = t.countdown.tv_nsec - param.tv_nsec) < 0)
472     {
473         t.countdown.tv_nsec = 1000000000 + val;
474         t.countdown.tv_sec--;
475     }
476     else
477     {
478         t.countdown.tv_nsec = val;
479     }
480     (t.countdown.tv_sec - param.tv_sec) < 0 ? 0 : (t.countdown.tv_sec -= param.tv_sec);
481 }
482
483 void SocketHandler::CopyPollfd::operator ()(const sh_poll_s & row)
484 {
485     pollfd temp = row.pollfdValue;
486     temp.revents = 0;
487     mArray.push_back(temp);
488 }
489