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