* [GAM-74] resolving issues found by coverity scan
[profile/ivi/genivi/genivi-audio-manager.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 <time.h>
29 #include <algorithm>
30 #include <features.h>
31 #include <csignal>
32 #include "shared/CAmDltWrapper.h"
33
34 namespace am
35 {
36
37 CAmSocketHandler::CAmSocketHandler() :
38         mListPoll(), //
39         mListTimer(), //
40         mListActiveTimer(), //
41         mLastInsertedHandle(0), //
42         mLastInsertedPollHandle(0), //
43         mRecreatePollfds(true), //
44         mStartTime()
45 {
46     gDispatchDone = 1;
47 }
48
49 CAmSocketHandler::~CAmSocketHandler()
50 {
51 }
52
53 //todo: maybe have some: give me more time returned?
54 /**
55  * start the block listening for filedescriptors. This is the mainloop.
56  */
57 void CAmSocketHandler::start_listenting()
58 {
59     gDispatchDone = 0;
60     int16_t pollStatus;
61
62     //prepare the signalmask
63     sigset_t sigmask;
64     sigemptyset(&sigmask);
65     sigaddset(&sigmask, SIGINT);
66     sigaddset(&sigmask, SIGQUIT);
67     sigaddset(&sigmask, SIGTERM);
68     sigaddset(&sigmask, SIGHUP);
69     sigaddset(&sigmask, SIGQUIT);
70
71     clock_gettime(CLOCK_MONOTONIC, &mStartTime);
72     while (!gDispatchDone)
73     {
74         //first we go through the registered filedescriptors and check if someone needs preparation:
75         std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCallPrep());
76
77         if (mRecreatePollfds)
78         {
79             mfdPollingArray.clear();
80             //there was a change in the setup, so we need to recreate the fdarray from the list
81             std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCopyPollfd(mfdPollingArray));
82             mRecreatePollfds = false;
83         }
84
85         timerCorrection();
86
87         //block until something is on a filedescriptor
88
89         timespec buffertime;
90         if ((pollStatus = ppoll(&mfdPollingArray[0], mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0)
91         {
92             if (errno == EINTR)
93             {
94                 //a signal was received, that means it's time to go...
95                 pollStatus = 0;
96             }
97             else
98             {
99                 logError("SocketHandler::start_listenting ppoll returned with error", errno);
100                 exit(0);
101             }
102         }
103
104         if (pollStatus != 0) //only check filedescriptors if there was a change
105         {
106             //todo: here could be a timer that makes sure naughty plugins return!
107
108             //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines
109             std::list<sh_poll_s> listPoll;
110             mListPoll_t::iterator listmPollIt;
111
112             //remove all filedescriptors who did not fire
113             std::vector<pollfd>::iterator it = mfdPollingArray.begin();
114             do
115             {
116                 it = std::find_if(it, mfdPollingArray.end(), eventFired);
117                 if (it != mfdPollingArray.end())
118                 {
119                     listmPollIt = mListPoll.begin();
120                     std::advance(listmPollIt, std::distance(mfdPollingArray.begin(), it));
121                     listPoll.push_back(*listmPollIt);
122                     listPoll.back().pollfdValue = *it;
123                     it++;
124                 }
125             } while (it != mfdPollingArray.end());
126
127             //stage 1, call firedCB
128             std::for_each(listPoll.begin(), listPoll.end(), CAmShCallFire());
129
130             //stage 2, lets ask around if some dispatching is necessary, the ones who need stay on the list
131             listPoll.remove_if(noDispatching);
132
133             //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
134             do
135             {
136                 listPoll.remove_if(dispatchingFinished);
137             } while (!listPoll.empty());
138
139         }
140         else //Timerevent
141         {
142             //this was a timer event, we need to take care about the timers
143             timerUp();
144         }
145     }
146 }
147
148 /**
149  * exits the loop
150  */
151 void CAmSocketHandler::stop_listening()
152 {
153     gDispatchDone = 1;
154
155     //this is for all running timers only - we need to handle the additional offset here
156     if (!mListActiveTimer.empty())
157     {
158         timespec currentTime, correctionTime;
159         clock_gettime(CLOCK_MONOTONIC, &currentTime);
160         correctionTime = timespecSub(currentTime, mStartTime);
161         std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
162     }
163
164 }
165
166 /**
167  * Adds a filedescriptor to the polling loop
168  * @param fd the filedescriptor
169  * @param event the event flags
170  * @param prepare a callback that is called before the loop is entered
171  * @param fired a callback that is called when the filedescriptor needs to be read
172  * @param check a callback that is called to check if further actions are neccessary
173  * @param dispatch a callback that is called to dispatch the received data
174  * @param userData a pointer to userdata that is always passed around
175  * @param handle the handle of this poll
176  * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
177  */
178 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)
179 {
180     if (!fdIsValid(fd))
181         return (E_NON_EXISTENT);
182
183     sh_poll_s pollData;
184     pollData.pollfdValue.fd = fd;
185     pollData.handle = ++mLastInsertedPollHandle;
186     pollData.pollfdValue.events = event;
187     pollData.pollfdValue.revents = 0;
188     pollData.userData = userData;
189     pollData.prepareCB = prepare;
190     pollData.firedCB = fired;
191     pollData.checkCB = check;
192     pollData.dispatchCB = dispatch;
193
194     //add new data to the list
195     mListPoll.push_back(pollData);
196
197     mRecreatePollfds = true;
198
199     handle = pollData.handle;
200     return (E_OK);
201 }
202
203 /**
204  * removes a filedescriptor from the poll loop
205  * @param handle
206  * @return
207  */
208 am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle)
209 {
210     mListPoll_t::iterator iterator = mListPoll.begin();
211
212     for (; iterator != mListPoll.end(); ++iterator)
213     {
214         if (iterator->handle == handle)
215         {
216             iterator = mListPoll.erase(iterator);
217             mRecreatePollfds = true;
218             return (E_OK);
219         }
220     }
221     return (E_UNKNOWN);
222 }
223
224 /**
225  * adds a timer to the list of timers. The callback will be fired when the timer is up.
226  * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
227  * for an answer via a filedescriptor.
228  * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
229  * @param timeouts timeouts time until the callback is fired
230  * @param callback callback the callback
231  * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer
232  * @param userData pointer always passed with the call
233  * @return E_OK in case of success
234  */
235 am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack* callback, sh_timerHandle_t& handle, void * userData)
236 {
237     assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
238     assert(callback!=NULL);
239
240     sh_timer_s timerItem;
241
242     //create a new handle for the timer
243     handle = ++mLastInsertedHandle; //todo: overflow ruling !o
244     timerItem.handle = handle;
245     timerItem.countdown = timeouts;
246     timerItem.callback = callback;
247     timerItem.userData = userData;
248
249     mListTimer.push_back(timerItem);
250
251     //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
252     timespec currentTime;
253     clock_gettime(CLOCK_MONOTONIC, &currentTime);
254     if (!gDispatchDone) //the mainloop is started
255         timerItem.countdown = timespecAdd(timeouts, timespecSub(currentTime, mStartTime));
256
257     mListActiveTimer.push_back(timerItem);
258     mListActiveTimer.sort(compareCountdown);
259     return (E_OK);
260 }
261
262 /**
263  * removes a timer from the list of timers
264  * @param handle the handle to the timer
265  * @return E_OK in case of success, E_UNKNOWN if timer was not found.
266  */
267 am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle)
268 {
269     assert(handle!=0);
270
271     //stop the current timer
272     stopTimer(handle);
273
274     std::list<sh_timer_s>::iterator it(mListTimer.begin());
275     for (; it != mListTimer.end(); ++it)
276     {
277         if (it->handle == handle)
278         {
279             it = mListTimer.erase(it);
280             return (E_OK);
281         }
282     }
283     return (E_UNKNOWN);
284 }
285
286 /**
287  * restarts a timer and updates with a new interva
288  * @param handle handle to the timer
289  * @param timeouts new timout time
290  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
291  */
292 am_Error_e CAmSocketHandler::updateTimer(const sh_timerHandle_t handle, const timespec timeouts)
293 {
294     //update the mList ....
295     sh_timer_s timerItem;
296     std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin());
297     bool found(false);
298     for (; it != mListTimer.end(); ++it)
299     {
300         if (it->handle == handle)
301         {
302             it->countdown = timeouts;
303             timerItem = *it;
304             found = true;
305             break;
306         }
307     }
308     if (!found)
309         return (E_NON_EXISTENT);
310
311     found = false;
312
313     //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
314     timespec currentTime, timeoutsCorrected;
315     currentTime.tv_nsec=timeoutsCorrected.tv_nsec=0;
316     currentTime.tv_sec=timeoutsCorrected.tv_sec=0;
317     clock_gettime(CLOCK_MONOTONIC, &currentTime);
318     if (!gDispatchDone) //the mainloop is started
319         timeoutsCorrected = timespecAdd(timeouts, timespecSub(currentTime, mStartTime));
320
321     for (; activeIt != mListActiveTimer.end(); ++activeIt)
322     {
323         if (activeIt->handle == handle)
324         {
325             activeIt->countdown = timeoutsCorrected;
326             found = true;
327             break;
328         }
329     }
330
331     if (!found)
332         timerItem.countdown = timeoutsCorrected;
333     mListActiveTimer.push_back(timerItem);
334
335     mListActiveTimer.sort(compareCountdown);
336     return (E_OK);
337 }
338
339 /**
340  * restarts a timer with the original value
341  * @param handle
342  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
343  */
344 am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle)
345 {
346     sh_timer_s timerItem; //!<the original timer value
347     //find the original value
348     std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin());
349     bool found(false);
350     for (; it != mListTimer.end(); ++it)
351     {
352         if (it->handle == handle)
353         {
354             timerItem = *it;
355             found = true;
356             break;
357         }
358     }
359     if (!found)
360         return (E_NON_EXISTENT);
361
362     found = false;
363
364     //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
365     timespec currentTime, timeoutsCorrected;
366     clock_gettime(CLOCK_MONOTONIC, &currentTime);
367     if (!gDispatchDone) //the mainloop is started
368     {
369         timeoutsCorrected = timespecAdd(timerItem.countdown, timespecSub(currentTime, mStartTime));
370         timerItem.countdown = timeoutsCorrected;
371     }
372
373     for (; activeIt != mListActiveTimer.end(); ++activeIt)
374     {
375         if (activeIt->handle == handle)
376         {
377             activeIt->countdown = timerItem.countdown;
378             found = true;
379             break;
380         }
381     }
382
383     if (!found)
384         mListActiveTimer.push_back(timerItem);
385
386     mListActiveTimer.sort(compareCountdown);
387
388     return (E_OK);
389 }
390
391 /**
392  * stops a timer
393  * @param handle
394  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
395  */
396 am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle)
397 {
398     //go through the list and remove the timer with the handle
399     std::list<sh_timer_s>::iterator it(mListActiveTimer.begin());
400     for (; it != mListActiveTimer.end(); ++it)
401     {
402         if (it->handle == handle)
403         {
404             it = mListActiveTimer.erase(it);
405             return (E_OK);
406         }
407     }
408     return (E_NON_EXISTENT);
409 }
410
411 /**
412  * updates the eventFlags of a poll
413  * @param handle
414  * @param events
415  * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found
416  */
417 am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events)
418 {
419     mListPoll_t::iterator iterator = mListPoll.begin();
420
421     for (; iterator != mListPoll.end(); ++iterator)
422     {
423         if (iterator->handle == handle)
424         {
425             iterator->pollfdValue.events = events;
426             mRecreatePollfds = true;
427             return (E_OK);
428         }
429     }
430     return (E_UNKNOWN);
431 }
432
433 /**
434  * checks if a filedescriptor is validCAmShSubstractTime
435  * @param fd the filedescriptor
436  * @return true if the fd is valid
437  */
438 bool CAmSocketHandler::fdIsValid(const int fd) const
439 {
440     return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
441 }
442
443 /**
444  * timer is up.
445  */
446 void CAmSocketHandler::timerUp()
447 {
448     //find out the timedifference to starttime
449     timespec currentTime, diffTime;
450     clock_gettime(CLOCK_MONOTONIC, &currentTime);
451     diffTime = timespecSub(currentTime, mStartTime);
452
453     //now we need to substract the diffTime from all timers and see if they are up
454     std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownUp(diffTime));
455
456     //copy all fired timers into a list
457     std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
458
459     //erase all fired timers
460     std::list<sh_timer_s>::iterator it(overflowIter.base());
461     mListActiveTimer.erase(mListActiveTimer.begin(), it);
462
463     //call the callbacks for the timers
464     std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
465 }
466
467 /**
468  * correct timers and fire the ones who are up
469  */
470 void CAmSocketHandler::timerCorrection()
471 {
472     //get the current time and calculate the correction value
473     timespec currentTime, correctionTime;
474     clock_gettime(CLOCK_MONOTONIC, &currentTime);
475     correctionTime = timespecSub(currentTime, mStartTime);
476     mStartTime = currentTime;
477
478     if (!mListActiveTimer.empty())
479     {
480
481         //subtract the correction value from all items in the list
482         std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
483
484         //find the last occurrence of zero -> timer overflowed
485         std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownZero());
486
487         //only if a timer overflowed
488         if (overflowIter != mListActiveTimer.rend())
489         {
490             //copy all timers that need to be called to a new list
491             std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
492
493             //erase all fired timers
494             std::list<sh_timer_s>::iterator it(overflowIter.base());
495             mListActiveTimer.erase(mListActiveTimer.begin(), it);
496
497             //call the callbacks for the timers
498             std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
499         }
500     }
501 }
502
503 /**
504  * is used to set the pointer for the ppoll command
505  * @param buffertime
506  * @return
507  */
508 inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
509 {
510     if (!mListActiveTimer.empty())
511     {
512         buffertime = mListActiveTimer.front().countdown;
513         return (&buffertime);
514     }
515     else
516     {
517         return (NULL);
518     }
519 }
520
521 }
522