* removed all documentation to new place
[profile/ivi/audiomanager.git] / AudioManagerDaemon / src / CAmSocketHandler.cpp
1 /**
2  * Copyright (C) 2012, BMW AG
3  *
4  * This file is part of GENIVI Project AudioManager.
5  *
6  * Contributions are licensed to the GENIVI Alliance under one or more
7  * Contribution License Agreements.
8  *
9  * \copyright
10  * This Source Code Form is subject to the terms of the
11  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed with
12  * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
13  *
14  *
15  * \author Christian Mueller, christian.ei.mueller@bmw.de BMW 2011,2012
16  *
17  * \file CAmSocketHandler.cpp
18  * For further information see http://www.genivi.org/.
19  *
20  */
21
22 #include "shared/CAmSocketHandler.h"
23 #include <config.h>
24 #include <cassert>
25 #include <sys/fcntl.h>
26 #include <sys/errno.h>
27 #include <sys/poll.h>
28 #include <algorithm>
29 #include <time.h>
30 #include <features.h>
31 #include <csignal>
32 #include "shared/CAmDltWrapper.h"
33
34 //todo: implement time correction if timer was interrupted by call
35 //todo: change hitlist to a list that holds all information, because entering and removing items will be cheaper than with std::vector
36
37 namespace am
38 {
39
40 CAmSocketHandler::CAmSocketHandler() :
41         mListPoll(), //
42         mListTimer(), //
43         mListActiveTimer(), //
44         mNextTimer(), //
45         mLastInsertedHandle(0), //
46         mLastInsertedPollHandle(0), //
47         mRecreatePollfds(true), //
48         mTimeout()
49 {
50     mTimeout.tv_nsec = -1;
51     mTimeout.tv_sec = -1;
52     gDispatchDone = 0;
53 }
54
55 CAmSocketHandler::~CAmSocketHandler()
56 {
57 }
58
59 //todo: maybe have some: give me more time returned?
60 /**
61  * start the block listening for filedescriptors. This is the mainloop.
62  */
63 void CAmSocketHandler::start_listenting()
64 {
65     gDispatchDone = 0;
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         IAmShPollPrepare *prep = NULL;
86         for (; prepIter != mListPoll.end(); ++prepIter)
87         {
88             if ((prep = prepIter->prepareCB) != NULL)
89                 prep->Call(prepIter->handle, prepIter->userData);
90         }
91
92         if (mRecreatePollfds)
93         {
94             mfdPollingArray.clear();
95             //there was a change in the setup, so we need to recreate the fdarray from the list
96             std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCopyPollfd(mfdPollingArray));
97             mRecreatePollfds = false;
98         }
99
100         //block until something is on a filedescriptor
101 #ifdef WITH_PPOLL
102
103         timespec buffertime;
104         if ((pollStatus = ppoll(&mfdPollingArray[0], mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0)
105         {
106             if (errno == EINTR)
107             {
108                 //a signal was received, that means it's time to go...
109                 pollStatus = 0;
110             }
111             else
112             {
113                 logError("SocketHandler::start_listenting ppoll returned with error", errno);
114                 exit(0);
115             }
116         }
117
118 #else
119         //sigprocmask (SIG_SETMASK, &mask, &oldmask);
120         if((pollStatus=poll(&mfdPollingArray[0],mfdPollingArray.size(),timespec2ms(mTimeout)))<0)
121         {
122
123             if(errno==EINTR)
124             {
125                 //a signal was received, that means it's time to go...
126                 //todo: add things to do here before going to sleep
127                 exit(0);
128             }
129             logError("SocketHandler::start_listenting poll returned with error",errno);
130             exit(0);
131         }
132         //sigprocmask (SIG_SETMASK, &oldmask, NULL);
133 #endif
134
135         if (pollStatus != 0) //only check filedescriptors if there was a change
136         {
137             //todo: here could be a timer that makes sure naughty plugins return!
138
139             //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines
140             mListPoll_t listPoll(mListPoll);
141
142             //get all indexes of the fired events and save them int hitList
143             hitList.clear();
144             std::vector<pollfd>::iterator it = mfdPollingArray.begin();
145             do
146             {
147                 it = std::find_if(it, mfdPollingArray.end(), onlyFiredEvents);
148                 if (it != mfdPollingArray.end())
149                     hitList.push_back(std::distance(mfdPollingArray.begin(), it++));
150
151             } while (it != mfdPollingArray.end());
152
153             //stage 1, call firedCB for all matched events, but only if callback is not zero!
154             std::list<int16_t>::iterator hListIt = hitList.begin();
155             for (; hListIt != hitList.end(); ++hListIt)
156             {
157                 IAmShPollFired* fire = NULL;
158                 if ((fire = listPoll.at(*hListIt).firedCB) != NULL)
159                     fire->Call(mfdPollingArray.at(*hListIt), listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData);
160             }
161
162             //stage 2, lets ask around if some dispatching is necessary, if not, they are taken from the hitlist
163             hListIt = hitList.begin();
164             for (; hListIt != hitList.end(); ++hListIt)
165             {
166                 IAmShPollCheck* check = NULL;
167                 if ((check = listPoll.at(*hListIt).checkCB) != NULL)
168                 {
169                     if (!check->Call(listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData))
170                     {
171                         hListIt = hitList.erase(hListIt);
172                     }
173                 }
174             }
175
176             //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
177             do
178             {
179                 hListIt = hitList.begin();
180                 for (; hListIt != hitList.end(); ++hListIt)
181                 {
182                     IAmShPollDispatch *dispatch = NULL;
183                     if ((dispatch = listPoll.at(*hListIt).dispatchCB) != NULL)
184                     {
185                         if (!dispatch->Call(listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData))
186                         {
187                             hListIt = hitList.erase(hListIt);
188                         }
189                     }
190                     else //there is no dispatch function, so we just remove the file from the list...
191                     {
192                         hListIt = hitList.erase(hListIt);
193                     }
194                 }
195             } while (!hitList.empty());
196
197         }
198         else //Timerevent
199         {
200             //this was a timer event, we need to take care about the timers
201             timerUp();
202         }
203     }
204 }
205
206 /**
207  * exits the loop
208  */
209 void CAmSocketHandler::stop_listening()
210 {
211     gDispatchDone = 1;
212 }
213
214 /**
215  * Adds a filedescriptor to the polling loop
216  * @param fd the filedescriptor
217  * @param event the event flags
218  * @param prepare a callback that is called before the loop is entered
219  * @param fired a callback that is called when the filedescriptor needs to be read
220  * @param check a callback that is called to check if further actions are neccessary
221  * @param dispatch a callback that is called to dispatch the received data
222  * @param userData a pointer to userdata that is always passed around
223  * @param handle the handle of this poll
224  * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
225  */
226 am_Error_e CAmSocketHandler::addFDPoll(const int fd, const short event, IAmShPollPrepare *prepare, IAmShPollFired *fired, IAmShPollCheck *check, IAmShPollDispatch *dispatch, void *userData, sh_pollHandle_t & handle)
227 {
228     if (!fdIsValid(fd))
229         return (E_NON_EXISTENT);
230
231     sh_poll_s pollData;
232     pollData.pollfdValue.fd = fd;
233     pollData.handle = ++mLastInsertedPollHandle;
234     pollData.pollfdValue.events = event;
235     pollData.pollfdValue.revents = 0;
236     pollData.userData = userData;
237     pollData.prepareCB = prepare;
238     pollData.firedCB = fired;
239     pollData.checkCB = check;
240     pollData.dispatchCB = dispatch;
241
242     //add new data to the list
243     mListPoll.push_back(pollData);
244
245     mRecreatePollfds = true;
246
247     handle = pollData.handle;
248     return (E_OK);
249 }
250
251 /**
252  * removes a filedescriptor from the poll loop
253  * @param handle
254  * @return
255  */
256 am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle)
257 {
258     mListPoll_t::iterator iterator = mListPoll.begin();
259
260     for (; iterator != mListPoll.end(); ++iterator)
261     {
262         if (iterator->handle == handle)
263         {
264             iterator = mListPoll.erase(iterator);
265             mRecreatePollfds = true;
266             return (E_OK);
267         }
268     }
269     return (E_UNKNOWN);
270 }
271
272 /**
273  * adds a timer to the list of timers. The callback will be fired when the timer is up.
274  * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
275  * for an answer via a filedescriptor.
276  * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
277  * @param timeouts timeouts time until the callback is fired
278  * @param callback callback the callback
279  * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer
280  * @param userData pointer always passed with the call
281  * @return E_OK in case of success
282  */
283 am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack*& callback, sh_timerHandle_t& handle, void * userData)
284 {
285     assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
286     assert(callback!=NULL);
287
288     sh_timer_s timerItem;
289
290     //create a new handle for the timer
291     handle = ++mLastInsertedHandle; //todo: overflow ruling !
292     timerItem.handle = handle;
293     timerItem.countdown = timeouts;
294     timerItem.timeout = timeouts;
295     timerItem.callback = callback;
296     timerItem.userData = userData;
297
298     //add timer to the list
299     mListActiveTimer.push_back(timerItem);
300     mListTimer.push_back(timerItem);
301
302     //very important: sort the list so that the smallest value is front
303     mListActiveTimer.sort(compareCountdown);
304     mTimeout = mListActiveTimer.front().countdown;
305     return (E_OK);
306 }
307
308 /**
309  * removes a timer from the list of timers
310  * @param handle the handle to the timer
311  * @return E_OK in case of success, E_UNKNOWN if timer was not found.
312  */
313 am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle)
314 {
315     assert(handle!=0);
316
317     //stop the current timer
318     stopTimer(handle);
319
320     std::list<sh_timer_s>::iterator it = mListTimer.begin();
321     for (; it != mListTimer.end(); ++it)
322     {
323         if (it->handle == handle)
324         {
325             it = mListTimer.erase(it);
326             return (E_OK);
327         }
328     }
329     return (E_UNKNOWN);
330 }
331
332 /**
333  * restarts a timer and updates with a new interval
334  * @param handle handle to the timer
335  * @param timeouts new timout time
336  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
337  */
338 am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
339 {
340     sh_timer_s timerItem;
341     std::list<sh_timer_s>::iterator it = mListTimer.begin();
342     for (; it != mListTimer.end(); ++it)
343     {
344         if (it->handle == handle)
345         {
346             timerItem = *it;
347             break;
348         }
349     }
350
351     if (timeouts.tv_nsec != -1 && timeouts.tv_sec != -1)
352     {
353         timerItem.timeout = timeouts;
354     }
355
356     mListActiveTimer.push_back(timerItem);
357
358     //very important: sort the list so that the smallest value is front
359     mListActiveTimer.sort(compareCountdown);
360     mTimeout = mListActiveTimer.front().countdown;
361     return (E_OK);
362 }
363
364 am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle)
365 {
366     //go through the list and remove the timer with the handle
367     std::list<sh_timer_s>::iterator it = mListActiveTimer.begin();
368     for (; it != mListActiveTimer.end(); ++it)
369     {
370         if (it->handle == handle)
371         {
372             it = mListActiveTimer.erase(it);
373             if (!mListActiveTimer.empty())
374             {
375                 mTimeout = mListActiveTimer.front().countdown;
376             }
377             else
378             {
379                 mTimeout.tv_nsec = -1;
380                 mTimeout.tv_sec = -1;
381             }
382             return (E_OK);
383         }
384     }
385     return (E_NON_EXISTENT);
386 }
387
388 /**
389  * updates the eventFlags of a poll
390  * @param handle
391  * @param events
392  * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found
393  */
394 am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events)
395 {
396     mListPoll_t::iterator iterator = mListPoll.begin();
397
398     for (; iterator != mListPoll.end(); ++iterator)
399     {
400         if (iterator->handle == handle)
401         {
402             iterator->pollfdValue.events = events;
403             mRecreatePollfds = true;
404             return (E_OK);
405         }
406     }
407     return (E_UNKNOWN);
408 }
409
410 /**
411  * checks if a filedescriptor is valid
412  * @param fd the filedescriptor
413  * @return true if the fd is valid
414  */
415 bool CAmSocketHandler::fdIsValid(const int fd) const
416 {
417     return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
418 }
419
420 /**
421  * whenever a timer is up, this function needs to be called.
422  * Removes the fired timer, calls the callback and resets mTimeout
423  */
424 void CAmSocketHandler::timerUp()
425 {
426     //first fire the event
427     mListActiveTimer.front().callback->Call(mListActiveTimer.front().handle, mListActiveTimer.front().userData);
428
429     //then remove the first timer, the one who fired
430     mListActiveTimer.pop_front();
431     if (!mListActiveTimer.empty())
432     {
433         //substract the old value from all timers in the list
434         std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(mTimeout));
435         mTimeout = mListActiveTimer.front().countdown;
436     }
437     else
438     {
439         mTimeout.tv_nsec = -1;
440         mTimeout.tv_sec = -1;
441     }
442 }
443
444 /**
445  * init the timers
446  */
447 void CAmSocketHandler::initTimer()
448 {
449     if (!mListActiveTimer.empty())
450     {
451         mTimeout = mListActiveTimer.front().countdown;
452     }
453     else
454     {
455         mTimeout.tv_nsec = -1;
456         mTimeout.tv_sec = -1;
457     }
458 }
459
460 /**
461  * convert timespec to milliseconds
462  * @param time time in timespec
463  * @return time in milliseconds
464  */
465 inline int CAmSocketHandler::timespec2ms(const timespec & time)
466 {
467     return ((time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000);
468 }
469
470 inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
471 {
472     buffertime.tv_nsec = mTimeout.tv_nsec;
473     buffertime.tv_sec = mTimeout.tv_sec;
474     return ((mTimeout.tv_nsec == -1 && mTimeout.tv_sec == -1) ? NULL : &buffertime);
475 }
476
477 /**
478  * functor to easy substract from each countdown
479  * @param t value to substract from
480  */
481 void CAmSocketHandler::CAmShSubstractTime::operator ()(sh_timer_s & t) const
482 {
483     int val = 0;
484     if ((val = t.countdown.tv_nsec - param.tv_nsec) < 0)
485     {
486         t.countdown.tv_nsec = 1000000000 + val;
487         t.countdown.tv_sec--;
488     }
489     else
490     {
491         t.countdown.tv_nsec = val;
492     }
493     (t.countdown.tv_sec - param.tv_sec) < 0 ? 0 : (t.countdown.tv_sec -= param.tv_sec);
494 }
495
496 void CAmSocketHandler::CAmShCopyPollfd::operator ()(const sh_poll_s & row)
497 {
498     pollfd temp = row.pollfdValue;
499     temp.revents = 0;
500     mArray.push_back(temp);
501 }
502 }