1 /*
2 * Copyright (C) 2008 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 #include "DSM.h"
28 #include "AmConfig.h"
29 #include "AmUtils.h"
30 #include "AmPlugIn.h"
31 #include "log.h"
32 #include "AmConfigReader.h"
33 #include "AmSessionContainer.h"
34 #include "ampi/MonitoringAPI.h"
35 #include "AmUriParser.h"
36 #include "DSMCall.h"
37 #include "DSMChartReader.h"
38 #include "AmSipHeaders.h"
39 #include "AmEventDispatcher.h"
40 #include "SystemDSM.h"
41
42 #include <string>
43 #include <fstream>
44
45 #include <sys/types.h>
46 #include <dirent.h>
47
48 #define MOD_NAME "dsm"
49
plugin_class_create()50 extern "C" void* plugin_class_create()
51 {
52 return DSMFactory::instance();
53 }
54
55 DSMFactory* DSMFactory::_instance=0;
56
instance()57 DSMFactory* DSMFactory::instance()
58 {
59 if(_instance == NULL)
60 _instance = new DSMFactory(MOD_NAME);
61 return _instance;
62 }
63
64 bool DSMFactory::DebugDSM;
65 string DSMFactory::InboundStartDiag;
66 string DSMFactory::OutboundStartDiag;
67 bool DSMFactory::CheckDSM;
68
69 #ifdef USE_MONITORING
70 bool DSMFactory::MonitoringFullCallgraph;
71 bool DSMFactory::MonitoringFullTransitions;
72
73 MonSelectType DSMFactory::MonSelectCaller;
74 MonSelectType DSMFactory::MonSelectCallee;
75 vector<string> DSMFactory::MonSelectFilters;
76
77 string DSMFactory::MonSelectFallback;
78
79 #endif // USE_MONITORING
80
DSMFactory(const string & _app_name)81 DSMFactory::DSMFactory(const string& _app_name)
82 : AmSessionFactory(_app_name),AmDynInvokeFactory(_app_name),
83 loaded(false),
84 session_timer_f(NULL)
85 {
86 AmEventDispatcher::instance()->addEventQueue("dsm", this);
87
88 MainScriptConfig.diags = new DSMStateDiagramCollection();
89 }
90
postEvent(AmEvent * e)91 void DSMFactory::postEvent(AmEvent* e) {
92 AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(e);
93 if (sys_ev &&
94 sys_ev->sys_event == AmSystemEvent::ServerShutdown) {
95 DBG("stopping DSM...\n");
96 preload_reader.cleanup();
97 AmEventDispatcher::instance()->delEventQueue("dsm");
98 return;
99 }
100
101 WARN("received unknown event\n");
102 }
103
~DSMFactory()104 DSMFactory::~DSMFactory() {
105 for (map<string, AmPromptCollection*>::iterator it=
106 prompt_sets.begin(); it != prompt_sets.end(); it++)
107 delete it->second;
108
109 for (std::set<DSMStateDiagramCollection*>::iterator it=
110 old_diags.begin(); it != old_diags.end(); it++)
111 delete *it;
112
113 delete MainScriptConfig.diags;
114 }
115
onLoad()116 int DSMFactory::onLoad()
117 {
118 if (loaded)
119 return 0;
120 loaded = true;
121
122 if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
123 return -1;
124
125 // get application specific global parameters
126 configureModule(cfg);
127
128 DebugDSM = cfg.getParameter("debug_raw_dsm") == "yes";
129 CheckDSM = cfg.getParameter("dsm_consistency_check", "yes") == "yes";
130
131 if (!loadPrompts(cfg))
132 return -1;
133
134 if (!loadPromptSets(cfg))
135 return -1;
136
137 if (!loadDiags(cfg, MainScriptConfig.diags))
138 return -1;
139
140 vector<string> registered_apps;
141 if (!registerApps(cfg, MainScriptConfig.diags, registered_apps))
142 return -1;
143
144 InboundStartDiag = cfg.getParameter("inbound_start_diag");
145 if (InboundStartDiag.empty()) {
146 INFO("no 'inbound_start_diag' set in config. "
147 "inbound calls with application 'dsm' disabled.\n");
148 }
149
150 OutboundStartDiag = cfg.getParameter("outbound_start_diag");
151 if (OutboundStartDiag.empty()) {
152 INFO("no 'outbound_start_diag' set in config. "
153 "outbound calls with application 'dsm' disabled.\n");
154 }
155
156 if (!InboundStartDiag.empty())
157 AmPlugIn::instance()->registerFactory4App("dsm",this);
158
159 MainScriptConfig.config_vars.insert(cfg.begin(), cfg.end());
160
161 // for (std::map<string,string>::const_iterator it =
162 // cfg.begin(); it != cfg.end(); it++)
163 // MainScriptConfig.config_vars[it->first] = it->second;
164
165 MainScriptConfig.RunInviteEvent = cfg.getParameter("run_invite_event")=="yes";
166
167 MainScriptConfig.SetParamVariables = cfg.getParameter("set_param_variables")=="yes";
168
169 vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
170 for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
171 string status;
172 if (createSystemDSM("main", *it, false /* reload */, status)) {
173 DBG("created SystemDSM '%s'\n", it->c_str());
174 } else {
175 ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
176 return -1;
177 }
178 }
179
180 #ifdef USE_MONITORING
181 string monitoring_full_callgraph = cfg.getParameter("monitoring_full_stategraph");
182 MonitoringFullCallgraph = monitoring_full_callgraph == "yes";
183 DBG("%sogging full call graph (states) to monitoring.\n",
184 MonitoringFullCallgraph?"L":"Not l");
185
186 string monitoring_full_transitions = cfg.getParameter("monitoring_full_transitions");
187 MonitoringFullTransitions = monitoring_full_transitions == "yes";
188 DBG("%sogging full call graph (transitions) to monitoring.\n",
189 MonitoringFullTransitions?"L":"Not l");
190
191 string cfg_usecaller = cfg.getParameter("monitor_select_use_caller");
192 if (cfg_usecaller.empty() || cfg_usecaller=="from")
193 MonSelectCaller = MonSelect_FROM;
194 else if (cfg_usecaller=="no")
195 MonSelectCaller = MonSelect_NONE;
196 else if (cfg_usecaller=="pai")
197 MonSelectCaller = MonSelect_PAI;
198 else {
199 ERROR("monitor_select_use_caller value '%s' not understood\n",
200 cfg_usecaller.c_str());
201 }
202
203 string cfg_usecallee = cfg.getParameter("monitor_select_use_callee");
204 if (cfg_usecallee.empty() || cfg_usecallee=="ruri")
205 MonSelectCallee = MonSelect_RURI;
206 else if (cfg_usecallee=="no")
207 MonSelectCallee = MonSelect_NONE;
208 else if (cfg_usecallee=="from")
209 MonSelectCallee = MonSelect_FROM;
210 else {
211 ERROR("monitor_select_use_callee value '%s' not understood\n",
212 cfg_usecallee.c_str());
213 }
214
215 MonSelectFilters = explode(cfg.getParameter("monitor_select_filters"), ",");
216 string filters;
217 for (vector<string>::iterator it =
218 MonSelectFilters.begin(); it != MonSelectFilters.end(); it++) {
219 if (it != MonSelectFilters.begin())
220 filters += ", ";
221 filters+=*it;
222 }
223 if (MonSelectFilters.size()) {
224 DBG("using additional monitor app select filters: %s\n",
225 filters.c_str());
226 } else {
227 DBG("not using additional monitor app select filters\n");
228 }
229 MonSelectFallback = cfg.getParameter("monitor_select_fallback");
230 #endif
231
232 string conf_d_dir = cfg.getParameter("conf_dir");
233 if (!conf_d_dir.empty()) {
234 if (conf_d_dir[conf_d_dir.length()-1] != '/')
235 conf_d_dir += '/';
236
237 DBG("processing configurations in '%s'...\n", conf_d_dir.c_str());
238 int err=0;
239 struct dirent* entry;
240 DIR* dir = opendir(conf_d_dir.c_str());
241
242 if(!dir){
243 INFO("DSM config files loader (%s): %s\n",
244 conf_d_dir.c_str(), strerror(errno));
245 } else {
246 while( ((entry = readdir(dir)) != NULL) && (err == 0) ){
247 string conf_name = string(entry->d_name);
248
249 if (conf_name.find(".conf",conf_name.length()-5) == string::npos){
250 continue;
251 }
252
253 string conf_file_name = conf_d_dir + conf_name;
254
255 DBG("loading %s ...\n",conf_file_name.c_str());
256 if (!loadConfig(conf_file_name, conf_name, false, NULL))
257 return -1;
258
259 }
260 closedir(dir);
261 }
262 }
263
264 if(cfg.hasParameter("enable_session_timer") &&
265 (cfg.getParameter("enable_session_timer") == string("yes")) ){
266 DBG("enabling session timers\n");
267 session_timer_f = AmPlugIn::instance()->getFactory4Seh("session_timer");
268 if(session_timer_f == NULL){
269 ERROR("Could not load the session_timer module: disabling session timers.\n");
270 }
271 }
272
273 return 0;
274 }
275
loadPrompts(AmConfigReader & cfg)276 bool DSMFactory::loadPrompts(AmConfigReader& cfg) {
277
278 vector<string> prompts_files =
279 explode(cfg.getParameter("load_prompts"), ",");
280 for (vector<string>::iterator it=
281 prompts_files.begin(); it != prompts_files.end(); it++) {
282 DBG("loading prompts from '%s'\n", it->c_str());
283 std::ifstream ifs(it->c_str());
284 string s;
285 while (ifs.good() && !ifs.eof()) {
286 getline(ifs, s);
287 if (s.length() && s.find_first_not_of(" \t")!= string::npos &&
288 s[s.find_first_not_of(" \t")] != '#') {
289 vector<string> p=explode(s, "=");
290 if (p.size()==2) {
291 prompts.setPrompt(p[0], p[1], MOD_NAME);
292 DBG("added prompt '%s' as '%s'\n",
293 p[0].c_str(), p[1].c_str());
294 }
295 }
296 }
297 }
298
299 bool has_all_prompts = true;
300 vector<string> required_prompts =
301 explode(cfg.getParameter("required_prompts"), ",");
302
303 for (vector<string>::iterator it=required_prompts.begin();
304 it != required_prompts.end(); it++) {
305 if (!prompts.hasPrompt(*it)) {
306 ERROR("required prompt '%s' not loaded.\n",
307 it->c_str());
308 has_all_prompts = false;
309 }
310 }
311 if (!has_all_prompts)
312 return false;
313
314 return true;
315 }
316
loadPromptSets(AmConfigReader & cfg)317 bool DSMFactory::loadPromptSets(AmConfigReader& cfg) {
318 string prompt_sets_path = cfg.getParameter("prompts_sets_path");
319
320 vector<string> prompt_sets_names =
321 explode(cfg.getParameter("load_prompts_sets"), ",");
322 for (vector<string>::iterator it=
323 prompt_sets_names.begin(); it != prompt_sets_names.end(); it++) {
324 string fname = prompt_sets_path.empty() ? "": prompt_sets_path + "/";
325 fname += *it;
326 DBG("loading prompts for '%s' (file '%s')\n", it->c_str(), fname.c_str());
327 std::ifstream ifs(fname.c_str());
328 string s;
329 if (!ifs.good()) {
330 WARN("prompts set file '%s' could not be read\n", fname.c_str());
331 }
332 AmPromptCollection* pc = new AmPromptCollection();
333 while (ifs.good() && !ifs.eof()) {
334 getline(ifs, s);
335 if (s.length() && s.find_first_not_of(" \t")!= string::npos &&
336 s[s.find_first_not_of(" \t")] != '#') {
337 vector<string> p=explode(s, "=");
338 if (p.size()==2) {
339 pc->setPrompt(p[0], p[1], MOD_NAME);
340 DBG("set '%s' added prompt '%s' as '%s'\n",
341 it->c_str(), p[0].c_str(), p[1].c_str());
342 }
343 }
344 }
345 prompt_sets[*it] = pc;
346 }
347 return true;
348 }
349
loadDiags(AmConfigReader & cfg,DSMStateDiagramCollection * m_diags)350 bool DSMFactory::loadDiags(AmConfigReader& cfg, DSMStateDiagramCollection* m_diags) {
351 string DiagPath = cfg.getParameter("diag_path");
352 if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/')
353 DiagPath += '/';
354
355 string ModPath = cfg.getParameter("mod_path");
356
357 string err;
358 int res = preloadModules(cfg, err, ModPath);
359 if (res<0) {
360 ERROR("%s\n", err.c_str());
361 return false;
362 }
363
364 // todo: pass preloaded mods to chart reader
365
366 string LoadDiags = cfg.getParameter("load_diags");
367 vector<string> diags_names = explode(LoadDiags, ",");
368 for (vector<string>::iterator it=
369 diags_names.begin(); it != diags_names.end(); it++) {
370 if (!m_diags->loadFile(DiagPath+*it+".dsm", *it, DiagPath, ModPath, DebugDSM, CheckDSM)) {
371 ERROR("loading %s from %s\n",
372 it->c_str(), (DiagPath+*it+".dsm").c_str());
373 return false;
374 }
375 }
376
377 return true;
378 }
379
registerApps(AmConfigReader & cfg,DSMStateDiagramCollection * m_diags,vector<string> & register_names)380 bool DSMFactory::registerApps(AmConfigReader& cfg, DSMStateDiagramCollection* m_diags,
381 vector<string>& register_names) {
382 string RegisterDiags = cfg.getParameter("register_apps");
383 register_names = explode(RegisterDiags, ",");
384 for (vector<string>::iterator it=
385 register_names.begin(); it != register_names.end(); it++) {
386 if (m_diags->hasDiagram(*it)) {
387 bool res = AmPlugIn::instance()->registerFactory4App(*it,this);
388 if(res)
389 INFO("DSM state machine registered: %s.\n",
390 it->c_str());
391 } else {
392 ERROR("trying to register application '%s' which is not loaded.\n",
393 it->c_str());
394 return false;
395 }
396 }
397 return true;
398 }
399
400 // DI interface function
loadConfig(const AmArg & args,AmArg & ret)401 void DSMFactory::loadConfig(const AmArg& args, AmArg& ret) {
402 string file_name = args.get(0).asCStr();
403 string diag_name = args.get(1).asCStr();
404
405 if (loadConfig(file_name, diag_name, true, NULL)) {
406 ret.push(200);
407 ret.push("OK");
408 } else {
409 ret.push(500);
410 ret.push("reload config failed");
411 }
412 }
413
loadConfig(const string & conf_file_name,const string & conf_name,bool live_reload,DSMStateDiagramCollection * m_diags)414 bool DSMFactory::loadConfig(const string& conf_file_name, const string& conf_name,
415 bool live_reload, DSMStateDiagramCollection* m_diags) {
416
417 string script_name = conf_name.substr(0, conf_name.length()-5); // - .conf
418 DBG("loading %s from %s ...\n", script_name.c_str(), conf_file_name.c_str());
419 AmConfigReader cfg;
420 if(cfg.loadFile(conf_file_name))
421 return false;
422
423 DSMScriptConfig script_config;
424 script_config.RunInviteEvent =
425 cfg.getParameter("run_invite_event")=="yes";
426
427 script_config.SetParamVariables =
428 cfg.getParameter("set_param_variables")=="yes";
429
430 script_config.config_vars.insert(cfg.begin(), cfg.end());
431
432 if (live_reload) {
433 INFO("live DSM config reload does NOT reload prompts and prompt sets!\n");
434 } else {
435 if (!loadPrompts(cfg))
436 return false;
437
438 if (!loadPromptSets(cfg))
439 return false;
440 }
441
442 DSMStateDiagramCollection* used_diags;
443 if (m_diags != NULL)
444 used_diags = m_diags; // got this from caller (main diags)
445 else {
446 // create a new set of diags
447 used_diags = script_config.diags = new DSMStateDiagramCollection();
448 }
449
450 if (!loadDiags(cfg, used_diags))
451 return false;
452
453 vector<string> registered_apps;
454 if (!registerApps(cfg, used_diags, registered_apps))
455 return false;
456
457 ScriptConfigs_mut.lock();
458 try {
459 Name2ScriptConfig[script_name] = script_config;
460 // set ScriptConfig to this for all registered apps' names
461 for (vector<string>::iterator reg_app_it=
462 registered_apps.begin(); reg_app_it != registered_apps.end(); reg_app_it++) {
463 string& app_name = *reg_app_it;
464 // dispose of the old one, if it exists
465 map<string, DSMScriptConfig>::iterator it=ScriptConfigs.find(app_name);
466 if (it != ScriptConfigs.end()) {
467 // may be in use by active call - don't delete but save to
468 // old_diags for garbage collection (destructor)
469 if (it->second.diags != NULL)
470 old_diags.insert(it->second.diags);
471 }
472
473 // overwrite with new config
474 ScriptConfigs[app_name] = script_config;
475 }
476 } catch(...) {
477 ScriptConfigs_mut.unlock();
478 throw;
479 }
480 ScriptConfigs_mut.unlock();
481
482 bool res = true;
483
484 vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
485 for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
486 string status;
487 if (createSystemDSM(script_name, *it, live_reload, status)) {
488 } else {
489 ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
490 res = false;
491 }
492 }
493
494 return res;
495 }
496
497
prepareSession(DSMCall * s)498 void DSMFactory::prepareSession(DSMCall* s) {
499 s->setPromptSets(prompt_sets);
500 setupSessionTimer(s);
501 }
502
setupSessionTimer(AmSession * s)503 void DSMFactory::setupSessionTimer(AmSession* s) {
504 if (NULL != session_timer_f) {
505
506 AmSessionEventHandler* h = session_timer_f->getHandler(s);
507 if (NULL == h)
508 return;
509
510 if(h->configure(cfg)){
511 ERROR("Could not configure the session timer: disabling session timers.\n");
512 delete h;
513 } else {
514 s->addHandler(h);
515 }
516 }
517 }
518
addVariables(DSMCall * s,const string & prefix,map<string,string> & vars)519 void DSMFactory::addVariables(DSMCall* s, const string& prefix,
520 map<string, string>& vars) {
521 for (map<string, string>::iterator it =
522 vars.begin(); it != vars.end(); it++)
523 s->var[prefix+it->first] = it->second;
524 }
525
addParams(DSMCall * s,const string & hdrs)526 void DSMFactory::addParams(DSMCall* s, const string& hdrs) {
527 // TODO: use real parser with quoting and optimize
528 map<string, string> params;
529 vector<string> items = explode(getHeader(hdrs, PARAM_HDR, true), ";");
530 for (vector<string>::iterator it=items.begin();
531 it != items.end(); it++) {
532 vector<string> kv = explode(*it, "=");
533 if (kv.size()==2)
534 params.insert(make_pair(kv[0], kv[1]));
535 }
536 addVariables(s, "", params);
537 }
538
AmArg2DSMStrMap(const AmArg & arg,map<string,string> & vars)539 void AmArg2DSMStrMap(const AmArg& arg,
540 map<string, string>& vars) {
541 for (AmArg::ValueStruct::const_iterator it=arg.begin();
542 it != arg.end(); it++) {
543 if (it->second.getType() == AmArg::CStr)
544 vars[it->first] = it->second.asCStr();
545 else if (it->second.getType() == AmArg::Array) {
546 vars[it->first+"_size"] = int2str((unsigned int)it->second.size());
547 for (size_t i=0;i<it->second.size();i++) {
548 if (it->second.get(i).getType() == AmArg::CStr)
549 vars[it->first+"_"+int2str((unsigned int)i)] = it->second.get(i).asCStr();
550 else
551 vars[it->first+"_"+int2str((unsigned int)i)] = AmArg::print(it->second.get(i));
552 }
553 } else {
554 vars[it->first] = AmArg::print(it->second);
555 }
556 }
557 }
558
runMonitorAppSelect(const AmSipRequest & req,string & start_diag,map<string,string> & vars)559 void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag,
560 map<string, string>& vars) {
561 #define FALLBACK_OR_EXCEPTION(code, reason) \
562 if (MonSelectFallback.empty()) { \
563 throw AmSession::Exception(code, reason); \
564 } else { \
565 DBG("falling back to '%s'\n", MonSelectFallback.c_str()); \
566 start_diag = MonSelectFallback; \
567 return; \
568 }
569
570 #ifdef USE_MONITORING
571 if (NULL == MONITORING_GLOBAL_INTERFACE) {
572 ERROR("using $(mon_select) but monitoring not loaded\n");
573 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
574 }
575
576 AmArg di_args, ret;
577 if (MonSelectCaller != MonSelect_NONE) {
578 AmUriParser from_parser;
579 if (MonSelectCaller == MonSelect_FROM)
580 from_parser.uri = req.from_uri;
581 else {
582 size_t end;
583 string pai = getHeader(req.hdrs, SIP_HDR_P_ASSERTED_IDENTITY, true);
584 if (!from_parser.parse_contact(pai, 0, end)) {
585 WARN("Failed to parse " SIP_HDR_P_ASSERTED_IDENTITY " '%s'\n",
586 pai.c_str());
587 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
588 }
589 }
590
591 if (!from_parser.parse_uri()) {
592 DBG("failed to parse caller uri '%s'\n", from_parser.uri.c_str());
593 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
594 }
595
596 AmArg caller_filter;
597 caller_filter.push("caller");
598 caller_filter.push(from_parser.uri_user);
599 DBG(" && looking for caller=='%s'\n", from_parser.uri_user.c_str());
600 di_args.push(caller_filter);
601 }
602
603
604 if (MonSelectCallee != MonSelect_NONE) {
605 AmArg callee_filter;
606 callee_filter.push("callee");
607 if (MonSelectCallee == MonSelect_RURI)
608 callee_filter.push(req.user);
609 else {
610 AmUriParser to_parser;
611 size_t end;
612 if (!to_parser.parse_contact(req.to, 0, end)) {
613 ERROR("Failed to parse To '%s'\n", req.to.c_str());
614 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
615 }
616 if (!to_parser.parse_uri()) {
617 DBG("failed to parse callee uri '%s'\n", to_parser.uri.c_str());
618 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
619 }
620 callee_filter.push(to_parser.uri_user);
621 }
622
623 DBG(" && looking for callee=='%s'\n", req.user.c_str());
624 di_args.push(callee_filter);
625 }
626 // apply additional filters
627 if (MonSelectFilters.size()) {
628 string app_params = getHeader(req.hdrs, PARAM_HDR);
629 for (vector<string>::iterator it =
630 MonSelectFilters.begin(); it != MonSelectFilters.end(); it++) {
631 AmArg filter;
632 filter.push(*it); // avp name
633 string app_param_val = get_header_keyvalue(app_params, *it);
634 filter.push(app_param_val);
635 di_args.push(filter);
636 DBG(" && looking for %s=='%s'\n", it->c_str(), app_param_val.c_str());
637 }
638 }
639
640 MONITORING_GLOBAL_INTERFACE->invoke("listByFilter",di_args,ret);
641
642 if ((ret.getType()!=AmArg::Array)||
643 !ret.size()) {
644 DBG("call info not found. caller uri %s, r-uri %s\n",
645 req.from_uri.c_str(), req.r_uri.c_str());
646 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
647 }
648
649 AmArg sess_id, sess_params;
650 if (ret.size()>1) {
651 DBG("multiple call info found - picking the first one\n");
652 }
653 const char* session_id = ret.get(0).asCStr();
654 sess_id.push(session_id);
655 MONITORING_GLOBAL_INTERFACE->invoke("get",sess_id,sess_params);
656
657 if ((sess_params.getType()!=AmArg::Array)||
658 !sess_params.size() ||
659 sess_params.get(0).getType() != AmArg::Struct) {
660 INFO("call parameters not found. caller uri %s, r-uri %s, id %s\n",
661 req.from_uri.c_str(), req.r_uri.c_str(), ret.get(0).asCStr());
662 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
663 }
664
665 AmArg& sess_dict = sess_params.get(0);
666 if (sess_dict.hasMember("app")) {
667 start_diag = sess_dict["app"].asCStr();
668 DBG("selected application '%s' for session\n", start_diag.c_str());
669 } else {
670 ERROR("selected session params don't contain 'app'\n");
671 FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
672 }
673 AmArg2DSMStrMap(sess_dict["appParams"], vars);
674 vars["mon_session_record"] = session_id;
675
676 #else
677 ERROR("using $(mon_select) for dsm application, "
678 "but compiled without monitoring support!\n");
679 throw AmSession::Exception(500, "Internal Server Error");
680 #endif
681
682 #undef FALLBACK_OR_EXCEPTION
683 }
684
onInvite(const AmSipRequest & req,const string & app_name,const map<string,string> & app_params)685 AmSession* DSMFactory::onInvite(const AmSipRequest& req, const string& app_name,
686 const map<string,string>& app_params)
687 {
688 string start_diag;
689 map<string, string> vars;
690
691 if (app_name == MOD_NAME) {
692 if (InboundStartDiag.empty()) {
693 ERROR("no inbound calls allowed\n");
694 throw AmSession::Exception(488, SIP_REPLY_NOT_ACCEPTABLE_HERE);
695 }
696 if (InboundStartDiag=="$(mon_select)") {
697 runMonitorAppSelect(req, start_diag, vars);
698 } else {
699 start_diag = InboundStartDiag;
700 }
701 } else {
702 start_diag = app_name;
703 }
704
705 DBG("start_diag = %s\n",start_diag.c_str());
706
707 // determine run configuration for script
708 DSMScriptConfig call_config;
709 ScriptConfigs_mut.lock();
710 map<string, DSMScriptConfig>::iterator sc=ScriptConfigs.find(start_diag);
711 if (sc == ScriptConfigs.end())
712 call_config = MainScriptConfig;
713 else
714 call_config = sc->second;
715
716 DSMCall* s = new DSMCall(call_config, &prompts, *call_config.diags, start_diag, NULL);
717
718 ScriptConfigs_mut.unlock();
719
720 prepareSession(s);
721 addVariables(s, "config.", call_config.config_vars);
722
723 if (call_config.SetParamVariables)
724 addParams(s, req.hdrs);
725
726 if (!vars.empty())
727 addVariables(s, "", vars);
728
729 return s;
730 }
731
732 // outgoing call
onInvite(const AmSipRequest & req,const string & app_name,AmArg & session_params)733 AmSession* DSMFactory::onInvite(const AmSipRequest& req, const string& app_name,
734 AmArg& session_params)
735 {
736
737 string start_diag;
738
739 if (app_name == MOD_NAME) {
740 if (OutboundStartDiag.empty()) {
741 ERROR("no outbound calls allowed\n");
742 throw AmSession::Exception(488, SIP_REPLY_NOT_ACCEPTABLE_HERE);
743 }
744 } else {
745 start_diag = app_name;
746 }
747
748 UACAuthCred* cred = NULL;
749 map<string, string> vars;
750 // Creds
751 if (session_params.getType() == AmArg::AObject) {
752 AmObject* cred_obj = session_params.asObject();
753 if (cred_obj)
754 cred = dynamic_cast<UACAuthCred*>(cred_obj);
755 } else if (session_params.getType() == AmArg::Array) {
756 DBG("session params is array - size %zd\n", session_params.size());
757 // Creds
758 cred = AmUACAuth::unpackCredentials(session_params.get(0));
759 // Creds + vars
760 if (session_params.size()>1 &&
761 session_params.get(1).getType() == AmArg::Struct) {
762 AmArg2DSMStrMap(session_params.get(1), vars);
763 }
764 } else if (session_params.getType() == AmArg::Struct) {
765 // vars
766 AmArg2DSMStrMap(session_params, vars);
767 }
768
769 DSMScriptConfig call_config;
770 ScriptConfigs_mut.lock();
771 map<string, DSMScriptConfig>::iterator sc=ScriptConfigs.find(start_diag);
772 if (sc == ScriptConfigs.end())
773 call_config = MainScriptConfig;
774 else
775 call_config = sc->second;
776
777 DSMCall* s = new DSMCall(call_config, &prompts, *call_config.diags, start_diag, cred);
778
779 ScriptConfigs_mut.unlock();
780
781 prepareSession(s);
782
783 addVariables(s, "config.", call_config.config_vars);
784 if (!vars.empty())
785 addVariables(s, "", vars);
786
787 if (call_config.SetParamVariables)
788 addParams(s, req.hdrs);
789
790 if (NULL == cred) {
791 DBG("outgoing DSM call will not be authenticated.\n");
792 } else {
793 AmUACAuth::enable(s);
794 }
795
796 return s;
797 }
798
createSystemDSM(const string & config_name,const string & start_diag,bool reload,string & status)799 bool DSMFactory::createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status) {
800 bool res = true;
801
802 DSMScriptConfig* script_config = NULL;
803 ScriptConfigs_mut.lock();
804 if (config_name == "main")
805 script_config = &MainScriptConfig;
806 else {
807 map<string, DSMScriptConfig>::iterator it = Name2ScriptConfig.find(config_name);
808 if (it != Name2ScriptConfig.end())
809 script_config = &it->second;
810 }
811 if (script_config==NULL) {
812 status = "Error: Script config '"+config_name+"' not found, in [";
813 for (map<string, DSMScriptConfig>::iterator it =
814 Name2ScriptConfig.begin(); it != Name2ScriptConfig.end(); it++) {
815 if (it != Name2ScriptConfig.begin())
816 status+=", ";
817 status += it->first;
818 }
819 status += "]";
820 res = false;
821 } else {
822 SystemDSM* s = new SystemDSM(*script_config, start_diag, reload);
823 s->start();
824 // add to garbage collector
825 AmThreadWatcher::instance()->add(s);
826 status = "OK";
827 }
828 ScriptConfigs_mut.unlock();
829 return res;
830 }
831
reloadDSMs(const AmArg & args,AmArg & ret)832 void DSMFactory::reloadDSMs(const AmArg& args, AmArg& ret) {
833 AmConfigReader cfg;
834 if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
835 ret.push(500);
836 ret.push("loading config file " +AmConfig::ModConfigPath + string(MOD_NAME ".conf"));
837 return ;
838 }
839
840 DSMStateDiagramCollection* new_diags = new DSMStateDiagramCollection();
841
842 string DiagPath = cfg.getParameter("diag_path");
843 if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/')
844 DiagPath += '/';
845
846 string ModPath = cfg.getParameter("mod_path");
847
848 string LoadDiags = cfg.getParameter("load_diags");
849 vector<string> diags_names = explode(LoadDiags, ",");
850 for (vector<string>::iterator it=
851 diags_names.begin(); it != diags_names.end(); it++) {
852 if (!new_diags->loadFile(DiagPath+*it+".dsm", *it, DiagPath, ModPath,
853 DebugDSM, CheckDSM)) {
854 ERROR("loading %s from %s\n",
855 it->c_str(), (DiagPath+*it+".dsm").c_str());
856 ret.push(500);
857 ret.push("loading " +*it+ " from "+ DiagPath+*it+".dsm");
858 return;
859 }
860 }
861 ScriptConfigs_mut.lock();
862 old_diags.insert(MainScriptConfig.diags);
863 MainScriptConfig.diags = new_diags;
864 ScriptConfigs_mut.unlock();
865
866 ret.push(200);
867 ret.push("DSMs reloaded");
868 }
869
870
preloadModules(AmConfigReader & cfg,string & res,const string & ModPath)871 int DSMFactory::preloadModules(AmConfigReader& cfg, string& res, const string& ModPath) {
872 string preload_mods = cfg.getParameter("preload_mods");
873 vector<string> preload_names = explode(preload_mods, ",");
874 if (preload_names.size()) {
875 for (vector<string>::iterator it=
876 preload_names.begin(); it != preload_names.end(); it++) {
877 DBG("preloading '%s'...\n", it->c_str());
878 if (!preload_reader.importModule("import("+*it+")", ModPath)) {
879 res = "importing module '"+*it+"' for preload\n";
880 return -1;
881 }
882 DSMModule* last_loaded = preload_reader.mods.back();
883 if (last_loaded) {
884 if (last_loaded->preload()) {
885 res = "Error while preloading '"+*it+"'\n";
886 return -1;
887 }
888 }
889 }
890 }
891
892 return 0;
893 }
894
preloadModules(const AmArg & args,AmArg & ret)895 void DSMFactory::preloadModules(const AmArg& args, AmArg& ret) {
896 AmConfigReader cfg;
897 if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
898 ret.push(500);
899 ret.push("loading config file " +AmConfig::ModConfigPath + string(MOD_NAME ".conf"));
900 return ;
901 }
902 string err;
903
904 string ModPath = cfg.getParameter("mod_path");
905
906 int res = preloadModules(cfg, err, ModPath);
907 if (res<0) {
908 ret.push(500);
909 ret.push(err);
910 } else {
911 ret.push(200);
912 ret.push("modules preloaded");
913 }
914 }
915
preloadModule(const AmArg & args,AmArg & ret)916 void DSMFactory::preloadModule(const AmArg& args, AmArg& ret) {
917 string mod_name = args.get(0).asCStr();
918 string mod_path = args.get(1).asCStr();
919
920 if (!preload_reader.importModule("import("+mod_name+")", mod_path)) {
921 ret.push(500);
922 ret.push("importing module '"+mod_name+"' for preload");
923 return;
924 }
925 DSMModule* last_loaded = preload_reader.mods.back();
926 if (last_loaded) {
927 if (last_loaded->preload()) {
928 ret.push(500);
929 ret.push("Error while preloading '"+mod_name+"'");
930 return;
931 }
932 }
933 ret.push(200);
934 ret.push("module preloaded.");
935 return;
936 }
937
listDSMs(const AmArg & args,AmArg & ret)938 void DSMFactory::listDSMs(const AmArg& args, AmArg& ret) {
939 vector<string> names;
940 ScriptConfigs_mut.lock();
941
942 try {
943 if (isArgUndef(args) || !args.size())
944 names = MainScriptConfig.diags->getDiagramNames();
945 else {
946 if (isArgCStr(args.get(0))) {
947 map<string, DSMScriptConfig>::iterator i=
948 ScriptConfigs.find(args.get(0).asCStr());
949 if (i!= ScriptConfigs.end())
950 names = i->second.diags->getDiagramNames();
951 }
952 }
953 } catch (...) {
954 ScriptConfigs_mut.unlock();
955 throw;
956 }
957
958 ScriptConfigs_mut.unlock();
959
960 for (vector<string>::iterator it=
961 names.begin(); it != names.end(); it++) {
962 ret.push(*it);
963 }
964 }
965
hasDSM(const string & dsm_name,const string & conf_name)966 bool DSMFactory::hasDSM(const string& dsm_name, const string& conf_name) {
967 bool res = false;
968 if (conf_name.empty())
969 res = MainScriptConfig.diags->hasDiagram(dsm_name);
970 else {
971 map<string, DSMScriptConfig>::iterator i=
972 ScriptConfigs.find(conf_name);
973 if (i!= ScriptConfigs.end())
974 res = i->second.diags->hasDiagram(dsm_name);
975 }
976 return res;
977 }
978
hasDSM(const AmArg & args,AmArg & ret)979 void DSMFactory::hasDSM(const AmArg& args, AmArg& ret) {
980 string conf_name;
981 if (args.size()>1 && isArgCStr(args.get(1)))
982 conf_name = args.get(1).asCStr();
983
984 bool res;
985
986 ScriptConfigs_mut.lock();
987 try {
988 res = hasDSM(args.get(0).asCStr(), conf_name);
989 } catch(...) {
990 ScriptConfigs_mut.unlock();
991 throw;
992 }
993 ScriptConfigs_mut.unlock();
994
995 if (res)
996 ret.push("1");
997 else
998 ret.push("0");
999 }
1000
registerApplication(const AmArg & args,AmArg & ret)1001 void DSMFactory::registerApplication(const AmArg& args, AmArg& ret) {
1002 string diag_name = args.get(0).asCStr();
1003 string conf_name;
1004 if (args.size()>1 && isArgCStr(args.get(1)))
1005 conf_name = args.get(1).asCStr();
1006 bool has_diag;
1007
1008 ScriptConfigs_mut.lock();
1009 try {
1010 has_diag = hasDSM(diag_name, conf_name);
1011 } catch(...) {
1012 ScriptConfigs_mut.unlock();
1013 throw;
1014 }
1015 ScriptConfigs_mut.unlock();
1016
1017 if (!has_diag) {
1018 ret.push(400);
1019 ret.push("unknown application (DSM)");
1020 return;
1021 }
1022
1023 bool res = AmPlugIn::instance()->registerFactory4App(diag_name,this);
1024 if(res) {
1025 INFO("DSM state machine registered: %s.\n",diag_name.c_str());
1026 ret.push(200);
1027 ret.push("registered DSM application");
1028 } else {
1029 ret.push(500);
1030 ret.push("Error registering DSM application (already registered?)");
1031 }
1032 }
1033
loadDSM(const AmArg & args,AmArg & ret)1034 void DSMFactory::loadDSM(const AmArg& args, AmArg& ret) {
1035 string dsm_name = args.get(0).asCStr();
1036
1037 AmConfigReader cfg;
1038 if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
1039 ret.push(500);
1040 ret.push("loading config file " +AmConfig::ModConfigPath + string(MOD_NAME ".conf"));
1041 return;
1042 }
1043
1044 string DiagPath = cfg.getParameter("diag_path");
1045 if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/')
1046 DiagPath += '/';
1047
1048 string ModPath = cfg.getParameter("mod_path");
1049
1050 string dsm_file_name = DiagPath+dsm_name+".dsm";
1051 string res = "OK";
1052
1053 ScriptConfigs_mut.lock();
1054 try {
1055 if (MainScriptConfig.diags->hasDiagram(dsm_name)) {
1056 ret.push(400);
1057 ret.push("DSM named '" + dsm_name + "' already loaded (use reloadDSMs to reload all)");
1058 } else {
1059 if (!MainScriptConfig.diags->loadFile(dsm_file_name, dsm_name, DiagPath, ModPath, DebugDSM, CheckDSM)) {
1060 ret.push(500);
1061 ret.push("error loading "+dsm_name+" from "+ dsm_file_name);
1062 } else {
1063 ret.push(200);
1064 ret.push("loaded "+dsm_name+" from "+ dsm_file_name);
1065 }
1066 }
1067 } catch(...) {
1068 ScriptConfigs_mut.unlock();
1069 throw;
1070 }
1071 ScriptConfigs_mut.unlock();
1072 }
1073
loadDSMWithPaths(const AmArg & args,AmArg & ret)1074 void DSMFactory::loadDSMWithPaths(const AmArg& args, AmArg& ret) {
1075 string dsm_name = args.get(0).asCStr();
1076 string diag_path = args.get(1).asCStr();
1077 string mod_path = args.get(2).asCStr();
1078
1079 string res = "OK";
1080 ScriptConfigs_mut.lock();
1081 try {
1082 if (MainScriptConfig.diags->hasDiagram(dsm_name)) {
1083 ret.push(400);
1084 ret.push("DSM named '" + dsm_name + "' already loaded (use reloadDSMs to reload all)");
1085 } else {
1086 if (!MainScriptConfig.diags->loadFile(diag_path+dsm_name+".dsm", dsm_name, diag_path, mod_path, DebugDSM, CheckDSM)) {
1087 ret.push(500);
1088 ret.push("error loading "+dsm_name+" from "+ diag_path+dsm_name+".dsm");
1089 } else {
1090 ret.push(200);
1091 ret.push("loaded "+dsm_name+" from "+ diag_path+dsm_name+".dsm");
1092 }
1093 }
1094 } catch(...) {
1095 ScriptConfigs_mut.unlock();
1096 throw;
1097 }
1098 ScriptConfigs_mut.unlock();
1099 }
1100
addScriptDiagsToEngine(const string & config_set,DSMStateEngine * engine,map<string,string> & config_vars,bool & SetParamVariables)1101 bool DSMFactory::addScriptDiagsToEngine(const string& config_set,
1102 DSMStateEngine* engine,
1103 map<string,string>& config_vars,
1104 bool& SetParamVariables) {
1105 bool res = false;
1106 ScriptConfigs_mut.lock();
1107 try {
1108 map<string, DSMScriptConfig>::iterator it=Name2ScriptConfig.find(config_set);
1109 if (it!=Name2ScriptConfig.end()) {
1110 res = true;
1111 it->second.diags->addToEngine(engine);
1112 config_vars = it->second.config_vars;
1113 SetParamVariables = it->second.SetParamVariables;
1114 }
1115 } catch(...) {
1116 ScriptConfigs_mut.unlock();
1117 throw;
1118 }
1119 ScriptConfigs_mut.unlock();
1120 return res;
1121 }
1122
invoke(const string & method,const AmArg & args,AmArg & ret)1123 void DSMFactory::invoke(const string& method, const AmArg& args,
1124 AmArg& ret)
1125 {
1126 if (method == "postDSMEvent"){
1127 assertArgCStr(args.get(0))
1128
1129 DSMEvent* ev = new DSMEvent();
1130 for (size_t i=0;i<args[1].size();i++)
1131 ev->params[args[1][i][0].asCStr()] = args[1][i][1].asCStr();
1132
1133 if (AmSessionContainer::instance()->postEvent(args.get(0).asCStr(), ev)) {
1134 ret.push(AmArg(200));
1135 ret.push(AmArg("OK"));
1136 } else {
1137 ret.push(AmArg(404));
1138 ret.push(AmArg("Session not found"));
1139 }
1140 } else if (method == "reloadDSMs"){
1141 reloadDSMs(args,ret);
1142 } else if (method == "loadDSM"){
1143 args.assertArrayFmt("s");
1144 loadDSM(args,ret);
1145 } else if (method == "loadDSMWithPath"){
1146 args.assertArrayFmt("sss");
1147 loadDSMWithPaths(args,ret);
1148 } else if (method == "preloadModules"){
1149 preloadModules(args,ret);
1150 } else if (method == "preloadModule"){
1151 args.assertArrayFmt("ss");
1152 preloadModule(args,ret);
1153 } else if (method == "hasDSM"){
1154 args.assertArrayFmt("s");
1155 hasDSM(args,ret);
1156 } else if (method == "listDSMs"){
1157 listDSMs(args,ret);
1158 } else if (method == "registerApplication"){
1159 args.assertArrayFmt("s");
1160 registerApplication(args,ret);
1161 } else if (method == "loadConfig"){
1162 args.assertArrayFmt("ss");
1163 loadConfig(args,ret);
1164 } else if (method == "createSystemDSM"){
1165 args.assertArrayFmt("ss");
1166 string status;
1167 if (createSystemDSM(args.get(0).asCStr(), args.get(1).asCStr(), false, status)) {
1168 ret.push(200);
1169 ret.push(status);
1170 } else {
1171 ret.push(500);
1172 ret.push(status);
1173 }
1174 } else if(method == "_list"){
1175 ret.push(AmArg("postDSMEvent"));
1176 ret.push(AmArg("reloadDSMs"));
1177 ret.push(AmArg("loadDSM"));
1178 ret.push(AmArg("loadDSMWithPaths"));
1179 ret.push(AmArg("preloadModules"));
1180 ret.push(AmArg("preloadModule"));
1181 ret.push(AmArg("loadConfig"));
1182 ret.push(AmArg("hasDSM"));
1183 ret.push(AmArg("listDSMs"));
1184 ret.push(AmArg("registerApplication"));
1185 ret.push(AmArg("createSystemDSM"));
1186 } else
1187 throw AmDynInvoke::NotImplemented(method);
1188 }
1189
1190