* add setSinkVolume to telnetserver
[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 <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 = 0;
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         if (!mListActiveTimer.empty())
86             timerCorrection();
87
88         //block until something is on a filedescriptor
89
90         timespec buffertime;
91         if ((pollStatus = ppoll(&mfdPollingArray[0], mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0)
92         {
93             if (errno == EINTR)
94             {
95                 //a signal was received, that means it's time to go...
96                 pollStatus = 0;
97             }
98             else
99             {
100                 logError("SocketHandler::start_listenting ppoll returned with error", errno);
101                 exit(0);
102             }
103         }
104
105         clock_gettime(CLOCK_MONOTONIC, &mStartTime);
106         if (pollStatus != 0) //only check filedescriptors if there was a change
107         {
108             //todo: here could be a timer that makes sure naughty plugins return!
109
110             //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines
111             std::list<sh_poll_s> listPoll;
112             mListPoll_t::iterator listmPollIt;
113
114             //remove all filedescriptors who did not fire
115             std::vector<pollfd>::iterator it = mfdPollingArray.begin();
116             do
117             {
118                 it = std::find_if(it, mfdPollingArray.end(), eventFired);
119                 if (it != mfdPollingArray.end())
120                 {
121                     listmPollIt = mListPoll.begin();
122                     std::advance(listmPollIt, std::distance(mfdPollingArray.begin(), it));
123                     listPoll.push_back(*listmPollIt);
124                     listPoll.back().pollfdValue = *it;
125                     it++;
126                 }
127             } while (it != mfdPollingArray.end());
128
129             //stage 1, call firedCB
130             std::for_each(listPoll.begin(), listPoll.end(), CAmShCallFire());
131
132             //stage 2, lets ask around if some dispatching is necessary, the ones who need stay on the list
133             listPoll.remove_if(noDispatching);
134
135             //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
136             do
137             {
138                 listPoll.remove_if(dispatchingFinished);
139             } while (!listPoll.empty());
140
141         }
142         else //Timerevent
143         {
144             //this was a timer event, we need to take care about the timers
145             timerUp();
146         }
147     }
148 }
149
150 /**
151  * exits the loop
152  */
153 void CAmSocketHandler::stop_listening()
154 {
155     gDispatchDone = 1;
156 }
157
158 /**
159  * Adds a filedescriptor to the polling loop
160  * @param fd the filedescriptor
161  * @param event the event flags
162  * @param prepare a callback that is called before the loop is entered
163  * @param fired a callback that is called when the filedescriptor needs to be read
164  * @param check a callback that is called to check if further actions are neccessary
165  * @param dispatch a callback that is called to dispatch the received data
166  * @param userData a pointer to userdata that is always passed around
167  * @param handle the handle of this poll
168  * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
169  */
170 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)
171 {
172     if (!fdIsValid(fd))
173         return (E_NON_EXISTENT);
174
175     sh_poll_s pollData;
176     pollData.pollfdValue.fd = fd;
177     pollData.handle = ++mLastInsertedPollHandle;
178     pollData.pollfdValue.events = event;
179     pollData.pollfdValue.revents = 0;
180     pollData.userData = userData;
181     pollData.prepareCB = prepare;
182     pollData.firedCB = fired;
183     pollData.checkCB = check;
184     pollData.dispatchCB = dispatch;
185
186     //add new data to the list
187     mListPoll.push_back(pollData);
188
189     mRecreatePollfds = true;
190
191     handle = pollData.handle;
192     return (E_OK);
193 }
194
195 /**
196  * removes a filedescriptor from the poll loop
197  * @param handle
198  * @return
199  */
200 am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle)
201 {
202     mListPoll_t::iterator iterator = mListPoll.begin();
203
204     for (; iterator != mListPoll.end(); ++iterator)
205     {
206         if (iterator->handle == handle)
207         {
208             iterator = mListPoll.erase(iterator);
209             mRecreatePollfds = true;
210             return (E_OK);
211         }
212     }
213     return (E_UNKNOWN);
214 }
215
216 /**
217  * adds a timer to the list of timers. The callback will be fired when the timer is up.
218  * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
219  * for an answer via a filedescriptor.
220  * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
221  * @param timeouts timeouts time until the callback is fired
222  * @param callback callback the callback
223  * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer
224  * @param userData pointer always passed with the call
225  * @return E_OK in case of success
226  */
227 am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack*& callback, sh_timerHandle_t& handle, void * userData)
228 {
229     assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
230     assert(callback!=NULL);
231
232     sh_timer_s timerItem;
233
234     //create a new handle for the timer
235     handle = ++mLastInsertedHandle; //todo: overflow ruling !o
236     timerItem.handle = handle;
237     timerItem.countdown = timeouts;
238     timerItem.timeout = timeouts;
239     timerItem.callback = callback;
240     timerItem.userData = userData;
241
242     mListTimer.push_back(timerItem);
243     mListActiveTimer.push_back(timerItem);
244     mListActiveTimer.sort(compareCountdown);
245     return (E_OK);
246 }
247
248 /**
249  * removes a timer from the list of timers
250  * @param handle the handle to the timer
251  * @return E_OK in case of success, E_UNKNOWN if timer was not found.
252  */
253 am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle)
254 {
255     assert(handle!=0);
256
257     //stop the current timer
258     stopTimer(handle);
259
260     std::list<sh_timer_s>::iterator it = mListTimer.begin();
261     for (; it != mListTimer.end(); ++it)
262     {
263         if (it->handle == handle)
264         {
265             it = mListTimer.erase(it);
266             return (E_OK);
267         }
268     }
269     return (E_UNKNOWN);
270 }
271
272 /**
273  * restarts a timer and updates with a new interval
274  * @param handle handle to the timer
275  * @param timeouts new timout time
276  * @return E_OK on success, E_NON_EXISTENT if the handle was not found
277  */
278 am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
279 {
280     sh_timer_s timerItem;
281     std::list<sh_timer_s>::iterator it = mListTimer.begin();
282     for (; it != mListTimer.end(); ++it)
283     {
284         if (it->handle == handle)
285         {
286             timerItem = *it;
287             break;
288         }
289     }
290
291     timerItem.timeout=timeouts;
292     timerItem.countdown=timeouts;
293
294     mListActiveTimer.push_back(timerItem);
295     mListActiveTimer.sort(compareCountdown);
296     return (E_OK);
297 }
298
299 am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle)
300 {
301     //go through the list and remove the timer with the handle
302     std::list<sh_timer_s>::iterator it = mListActiveTimer.begin();
303     for (; it != mListActiveTimer.end(); ++it)
304     {
305         if (it->handle == handle)
306         {
307             it = mListActiveTimer.erase(it);
308             return (E_OK);
309         }
310     }
311     return (E_NON_EXISTENT);
312 }
313
314 /**
315  * updates the eventFlags of a poll
316  * @param handle    buffertime.tv_nsec = mTimeout.tv_nsec;
317  buffertime.tv_sec = mTimeout.tv_sec;
318  return ((mTimeout.tv_nsec == -1 && mTimeout.tv_sec == -1) ? NULL : &buffertime);
319  * @param events
320  * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found
321  */
322 am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events)
323 {
324     mListPoll_t::iterator iterator = mListPoll.begin();
325
326     for (; iterator != mListPoll.end(); ++iterator)
327     {
328         if (iterator->handle == handle)
329         {
330             iterator->pollfdValue.events = events;
331             mRecreatePollfds = true;
332             return (E_OK);
333         }
334     }
335     return (E_UNKNOWN);
336 }
337
338 /**
339  * checks if a filedescriptor is validCAmShSubstractTime
340  * @param fd the filedescriptor
341  * @return true if the fd is valid
342  */
343 bool CAmSocketHandler::fdIsValid(const int fd) const
344 {
345     return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
346 }
347
348 /**
349  * timer is up.
350  */
351 void CAmSocketHandler::timerUp()
352 {
353     //first we need to correct all the countdown values by the one who fired
354     std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(mListActiveTimer.begin()->countdown));
355
356     //then find the last value that is zero. Since the first value is subtracted, we will always have the first one
357     std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownUp());
358
359     //copy all fired timers into a list
360     std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
361
362     //erase all fired timers
363     std::list<sh_timer_s>::iterator it(overflowIter.base());
364     mListActiveTimer.erase(mListActiveTimer.begin(), it);
365
366     //call the callbacks for the timers
367     std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
368 }
369
370 /**
371  * convert timespec to milliseconds
372  * @param time time in timespec
373  * @return time in milliseconds
374  */
375 inline int CAmSocketHandler::timespec2ms(const timespec & time)
376 {
377     return ((time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000);
378 }
379
380 /**
381  * correct timers
382  */
383 void CAmSocketHandler::timerCorrection()
384 {
385     //get the current time and calculate the correction value
386     timespec currentTime, correctionTime;
387     clock_gettime(CLOCK_MONOTONIC, &currentTime);
388     correctionTime = timespecSub(currentTime, mStartTime);
389
390     //subtract the correction value from all items in the list
391     std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
392
393     //find the last occurrence of zero -> timer overflowed
394     std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownUp());
395
396     //only if a timer overflowed
397     if (overflowIter != mListActiveTimer.rend())
398     {
399         //copy all timer that need to be called to a new list
400         std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
401
402         //erase all fired timers
403         std::list<sh_timer_s>::iterator it(overflowIter.base());
404         mListActiveTimer.erase(mListActiveTimer.begin(), it);
405
406         //call the callbacks for the timers
407         std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
408     }
409 }
410
411 inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
412 {
413     if (!mListActiveTimer.empty())
414     {
415         buffertime = mListActiveTimer.front().countdown;
416         return (&buffertime);
417     }
418     else
419     {
420         return (NULL);
421     }
422 }
423
424 /**
425  * functor to easy substract from each countdown
426  * @param t value to substract from
427  */
428 void CAmSocketHandler::CAmShSubstractTime::operator ()(sh_timer_s & t) const
429 {
430     t.countdown = timespecSub(t.countdown, param);
431 }
432
433 }
434