1 /*
2 * (c) 2007 iptego GmbH
3 *
4 * This file is part of SEMS, a free SIP media server.
5 *
6 * SEMS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. This program is released under
10 * the GPL with the additional exemption that compiling, linking,
11 * and/or using OpenSSL is allowed.
12 *
13 * For a license to use the SEMS software under conditions
14 * other than those described here, or to purchase support for this
15 * software, please contact iptel.org by e-mail at the following addresses:
16 * info@iptel.org
17 *
18 * SEMS is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28 #include <sys/time.h>
29 #include <unistd.h>
30
31 #include "log.h"
32
33 #include "AmCallWatcher.h"
34
35
AmCallWatcher()36 AmCallWatcher::AmCallWatcher()
37 : AmEventQueue(this),
38 garbage_collector(new AmCallWatcherGarbageCollector(soft_states_mut, soft_states))
39 {
40 }
41
~AmCallWatcher()42 AmCallWatcher::~AmCallWatcher()
43 {
44 }
45
run()46 void AmCallWatcher::run() {
47 DBG("starting call watcher.\n");
48 garbage_collector->start();
49 while (true) {
50 waitForEvent();
51 processEvents();
52 }
53 }
54
on_stop()55 void AmCallWatcher::on_stop() {
56 ERROR("The call watcher cannot be stopped.\n");
57 }
58
process(AmEvent * ev)59 void AmCallWatcher::process(AmEvent* ev) {
60 CallStatusUpdateEvent* csu = dynamic_cast<CallStatusUpdateEvent*>(ev);
61 if (NULL == csu) {
62 ERROR("received invalid event!\n");
63 return;
64 }
65
66
67 switch (csu->event_id) {
68 case CallStatusUpdateEvent::Initialize: {
69 states_mut.lock();
70 DBG("adding call state '%s'\n",
71 csu->get_call_id().c_str());
72
73 // check whether already there
74 CallStatusMap::iterator it = states.find(csu->get_call_id());
75 if (it != states.end()) {
76 WARN("implementation error: state '%s' already in list!\n",
77 csu->get_call_id().c_str());
78 // avoid leak - delete the old state
79 delete it->second;
80 }
81
82 // insert the new one
83 states[csu->get_call_id()] = csu->get_init_status();
84 states_mut.unlock();
85 } break;
86
87 case CallStatusUpdateEvent::Update: {
88 states_mut.lock();
89 CallStatusMap::iterator it = states.find(csu->get_call_id());
90 if (it != states.end()) {
91 it->second->update(csu);
92 it->second->dump();
93 states_mut.unlock();
94 } else {
95 states_mut.unlock();
96
97 soft_states_mut.lock();
98 CallStatusTimedMap::iterator it =
99 soft_states.find(csu->get_call_id());
100 if (it != soft_states.end()) {
101 it->second.first->update(csu);
102 it->second.first->dump();
103 } else {
104 DBG("received update event for inexistent call '%s'\n",
105 csu->get_call_id().c_str());
106 }
107 soft_states_mut.unlock();
108 }
109 } break;
110
111 case CallStatusUpdateEvent::Obsolete: {
112 states_mut.lock();
113 CallStatusMap::iterator it = states.find(csu->get_call_id());
114 if (it != states.end()) {
115
116 CallStatus* cs = it->second;
117 states.erase(it);
118 size_t s_size = states.size();
119 states_mut.unlock();
120
121 struct timeval now;
122 gettimeofday(&now, NULL);
123
124 soft_states_mut.lock();
125 soft_states[csu->get_call_id()] =
126 std::make_pair(cs, now.tv_sec + WATCHER_SOFT_EXPIRE_SECONDS);
127 size_t soft_size = soft_states.size();
128 soft_states_mut.unlock();
129
130 DBG("moved call state '%s' to soft-state map (%u states, %u soft-states)\n",
131 csu->get_call_id().c_str(), (unsigned int) s_size, (unsigned int)soft_size);
132
133 } else {
134 DBG("received obsolete event for inexistent call '%s'\n",
135 csu->get_call_id().c_str());
136 states_mut.unlock();
137 }
138 }break;
139 }
140 }
141
dump()142 void AmCallWatcher::dump() {
143 states_mut.lock();
144 for (CallStatusMap::iterator it = states.begin();
145 it != states.end(); it++) {
146 it->second->dump();
147 }
148 states_mut.unlock();
149 }
150
getStatus(const string & call_id)151 CallStatus* AmCallWatcher::getStatus(const string& call_id) {
152 CallStatus* res = NULL;
153
154 states_mut.lock();
155
156 CallStatusMap::iterator it = states.find(call_id);
157 if (it != states.end()) {
158 res = it->second->copy();
159 states_mut.unlock();
160 } else {
161 states_mut.unlock();
162
163 // check obsolete states
164 soft_states_mut.lock();
165 CallStatusTimedMap::iterator it =
166 soft_states.find(call_id);
167 if (it != soft_states.end()) {
168 // got it. return and remove from map
169 res = it->second.first;
170 soft_states.erase(it);
171 DBG("erased call state '%s' (%u in list).\n",
172 call_id.c_str(), (unsigned int)soft_states.size());
173 } else {
174 DBG("state for call '%s' not found.\n",
175 call_id.c_str());
176 }
177 soft_states_mut.unlock();
178 }
179 return res;
180 }
181
run()182 void AmCallWatcherGarbageCollector::run() {
183 DBG("AmCallWatcherGarbageCollector started.\n");
184 while (true) {
185 sleep(2);
186 struct timeval now;
187 gettimeofday(&now, NULL);
188
189 bool erased = false;
190
191 mut.lock();
192 AmCallWatcher::CallStatusTimedMap::iterator it = garbage.begin();
193 while (it != garbage.end()) {
194 if (it->second.second < now.tv_sec) {
195 AmCallWatcher::CallStatusTimedMap::iterator d_it = it;
196 it++;
197 delete (d_it->second.first);
198 garbage.erase(d_it);
199 erased = true;
200 } else {
201 it++;
202 }
203 }
204 if (erased){
205 DBG("cleared old soft-states (%u soft-states remaining)\n",
206 (unsigned int)garbage.size());
207 }
208 mut.unlock();
209 }
210 }
211