From cad6589d67cfe0da4204d301aed94fd5bfd7a297 Mon Sep 17 00:00:00 2001 From: Stefan Sayer Date: Fri, 3 Dec 2010 03:00:24 +0100 Subject: [PATCH] DSM: groups module Development of this module was sponsored by TelTech Systems Inc. The groups module implements a group event broadcast system. DSM calls can join and leave groups, and post events to groups. Events are passed to all members of the group that the event is posted to. Events can, just like the `postEvent` function of DSM, contain either some variables, or all variables. If a call ends, it is automatically removed from all groups it belongs to. Actions: groups.join(groupname) - Join a group groups.leave(groupname) - Leave a group groups.leaveAll() - Leave all groups groups.postEvent(groupname, var1;var2) - post event to groupname with var1 and var2 groups.postEvent(groupname, var) - post event to groupname with all variables --- apps/dsm/mods/mod_groups/CMakeLists.txt | 7 + apps/dsm/mods/mod_groups/Makefile | 10 ++ apps/dsm/mods/mod_groups/ModGroups.cpp | 175 ++++++++++++++++++++++++ apps/dsm/mods/mod_groups/ModGroups.h | 67 +++++++++ doc/dsm/mods/Readme.mod_groups.txt | 56 ++++++++ 5 files changed, 315 insertions(+) create mode 100644 apps/dsm/mods/mod_groups/CMakeLists.txt create mode 100644 apps/dsm/mods/mod_groups/Makefile create mode 100644 apps/dsm/mods/mod_groups/ModGroups.cpp create mode 100644 apps/dsm/mods/mod_groups/ModGroups.h create mode 100644 doc/dsm/mods/Readme.mod_groups.txt diff --git a/apps/dsm/mods/mod_groups/CMakeLists.txt b/apps/dsm/mods/mod_groups/CMakeLists.txt new file mode 100644 index 00000000..58e57db0 --- /dev/null +++ b/apps/dsm/mods/mod_groups/CMakeLists.txt @@ -0,0 +1,7 @@ +set (mod_groups_SRCS +ModGroups.cpp +) + +SET(sems_dsm_module_name mod_groups) +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/dsm.lib.rules.txt) + diff --git a/apps/dsm/mods/mod_groups/Makefile b/apps/dsm/mods/mod_groups/Makefile new file mode 100644 index 00000000..54b2dd06 --- /dev/null +++ b/apps/dsm/mods/mod_groups/Makefile @@ -0,0 +1,10 @@ +plug_in_name = mod_groups + +DSMPATH ?= ../.. + +module_ldflags = +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_groups/ModGroups.cpp b/apps/dsm/mods/mod_groups/ModGroups.cpp new file mode 100644 index 00000000..15a19f14 --- /dev/null +++ b/apps/dsm/mods/mod_groups/ModGroups.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 Stefan Sayer + * + * Development of this module was sponsored by TelTech Systems Inc + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. This program is released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ModGroups.h" +#include "log.h" +#include "AmUtils.h" + +#include "DSMSession.h" +#include "AmSession.h" +#include "AmSessionContainer.h" + +SC_EXPORT(MOD_CLS_NAME); + +AmMutex GroupsModule::groups_mut; +GroupMap GroupsModule::groups; +GroupMap GroupsModule::groups_rev; + + +MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { + + DEF_CMD("groups.join", GroupsJoinAction); + DEF_CMD("groups.leave", GroupsLeaveAction); + DEF_CMD("groups.leaveAll", GroupsLeaveAllAction); + // DEF_CMD("groups.get", GroupsGetAction); + // DEF_CMD("groups.getMembers", GroupsGetMembersAction); + DEF_CMD("groups.postEvent", GroupsPostEventAction); + +} MOD_ACTIONEXPORT_END; + +MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); + +void GroupsModule::onBeforeDestroy(DSMSession* sc_sess, AmSession* sess) { + leave_all_groups(sess->getLocalTag()); +} + +void GroupsModule::leave_all_groups(const string& ltag) { + GroupsModule::groups_mut.lock(); + + GroupMap::iterator it = GroupsModule::groups_rev.find(ltag); + if (it != GroupsModule::groups_rev.end()) { + set& active_groups = it->second; + for (set::iterator ag_it = + active_groups.begin(); ag_it != active_groups.end(); ag_it++) { + GroupsModule::groups[*ag_it].erase(ltag); + if (GroupsModule::groups[*ag_it].empty()) { + DBG("clearing empty group '%s'\n", ag_it->c_str()); + GroupsModule::groups.erase(*ag_it); + } + } + GroupsModule::groups_rev.erase(it); + } + + GroupsModule::groups_mut.unlock(); +} + +EXEC_ACTION_START(GroupsJoinAction) { + string groupname = resolveVars(arg, sess, sc_sess, event_params); + DBG("call '%s' joining group '%s'\n", + sess->getLocalTag().c_str(), groupname.c_str()); + + GroupsModule::groups_mut.lock(); + GroupsModule::groups[groupname].insert(sess->getLocalTag()); + GroupsModule::groups_rev[sess->getLocalTag()].insert(groupname); + GroupsModule::groups_mut.unlock(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(GroupsLeaveAction) { + string groupname = resolveVars(arg, sess, sc_sess, event_params); + string ltag = sess->getLocalTag(); + DBG("call '%s' leaving group '%s'\n", + ltag.c_str(), groupname.c_str()); + + GroupsModule::groups_mut.lock(); + + GroupMap::iterator it = GroupsModule::groups.find(groupname); + if (it != GroupsModule::groups.end()) { + it->second.erase(ltag); + if (it->second.empty()) { + DBG("clearing empty group '%s'\n", groupname.c_str()); + GroupsModule::groups.erase(it); + } + } + + it = GroupsModule::groups_rev.find(ltag); + if (it != GroupsModule::groups_rev.end()) { + it->second.erase(groupname); + if (it->second.empty()) { + DBG("call '%s' in no group any more\n", ltag.c_str()); + GroupsModule::groups_rev.erase(it); + } + } + GroupsModule::groups_mut.unlock(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(GroupsLeaveAllAction) { + string ltag = sess->getLocalTag(); + DBG("call '%s' leaving all groups\n", ltag.c_str()); + + GroupsModule::leave_all_groups(ltag); +} EXEC_ACTION_END; + +CONST_ACTION_2P(GroupsPostEventAction, ',', true); +EXEC_ACTION_START(GroupsPostEventAction) { + string groupname = resolveVars(par1, sess, sc_sess, event_params); + string var = resolveVars(par2, sess, sc_sess, event_params); + map ev_params; + + if (!var.empty()) { + if (var == "var") + ev_params = sc_sess->var; + else { + vector vars = explode(var, ";"); + for (vector::iterator it = + vars.begin(); it != vars.end(); it++) + ev_params[*it] = sc_sess->var[*it]; + } + } + + + DBG("posting event to group '%s'\n", groupname.c_str()); + GroupsModule::groups_mut.unlock(); + GroupMap::iterator grp = GroupsModule::groups.find(groupname); + bool posted = false; + if (grp != GroupsModule::groups.end()) { + for (set::iterator it = + grp->second.begin(); it != grp->second.end(); it++) { + if (*it == sess->getLocalTag()) + continue; // don't post to myself + + DSMEvent* ev = new DSMEvent(); + ev->params = ev_params; + if (!AmSessionContainer::instance()->postEvent(*it, ev)) { + DBG("could not post to call '%s'\n", it->c_str()); + } else { + DBG("posted event to call '%s'\n", it->c_str()); + posted = true; + } + } + } + GroupsModule::groups_mut.unlock(); + + if (posted) { + sc_sess->CLR_ERRNO; + } else { + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("event could not be posted\n"); + } + +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_groups/ModGroups.h b/apps/dsm/mods/mod_groups/ModGroups.h new file mode 100644 index 00000000..4305e9ae --- /dev/null +++ b/apps/dsm/mods/mod_groups/ModGroups.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 Stefan Sayer + * + * Development of this module was sponsored by TelTech Systems Inc + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. This program is released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MOD_MYSQL_H +#define _MOD_MYSQL_H +#include "DSMModule.h" +#include "DSMSession.h" +#include "AmThread.h" + +#define MOD_CLS_NAME GroupsModule + +#include +using std::set; +#include +using std::map; +#include +using std::string; + +typedef map > GroupMap; + +DECLARE_MODULE_BEGIN(MOD_CLS_NAME); + +static AmMutex groups_mut; +// group name ltags + +static GroupMap groups; +// ltags groups +static GroupMap groups_rev; + +static void leave_all_groups(const string& ltag); +void onBeforeDestroy(DSMSession* sc_sess, AmSession* sess); + +DECLARE_MODULE_END; + +DEF_ACTION_1P(GroupsJoinAction); +DEF_ACTION_1P(GroupsLeaveAction); +DEF_ACTION_1P(GroupsLeaveAllAction); +/* DEF_ACTION_1P(GroupsGetAction); */ +/* DEF_ACTION_1P(GroupsGetMembersAction); */ +DEF_ACTION_2P(GroupsPostEventAction); + +#endif diff --git a/doc/dsm/mods/Readme.mod_groups.txt b/doc/dsm/mods/Readme.mod_groups.txt new file mode 100644 index 00000000..63248a1f --- /dev/null +++ b/doc/dsm/mods/Readme.mod_groups.txt @@ -0,0 +1,56 @@ +mod_groups - Group event passing + + Copyright (C) 2010 Stefan Sayer + + Development of this module was sponsored by TelTech Systems Inc + +Overview +-------- + +The groups module implements a group event broadcast system. DSM calls can +join and leave groups, and post events to groups. Events are passed to all +members of the group that the event is posted to. Events can, just like the +`postEvent` function of DSM, contain either some variables, or all variables. +If a call ends, it is automatically removed from all groups it belongs to. + +Actions: + + groups.join(groupname) - Join a group + groups.leave(groupname) - Leave a group + groups.leaveAll() - Leave all groups + groups.postEvent(groupname, var1;var2) - post event to groupname with var1 and var2 + groups.postEvent(groupname, var) - post event to groupname with all variables + + +Example: + import(mod_groups); + initial state START + enter { + groups.join("yeah"); + repost(); + }; + + transition "in the call" START --> IN_CALL; + + state IN_CALL; + + transition "got key" IN_CALL - key / { + set($event="key_press"); + set($key=#key); + set($duration=#duration); + set($type=groups); + logVars(1); + groups.postEvent("yeah", event;key;duration;type); + } -> IN_CALL; + + transition "got group event" IN_CALL - event(#type==groups) / { + logParams(); + } -> IN_CALL; + + transition "hangup" IN_CALL - hangup / { + -- groups.leave("yeah"); + groups.leaveAll(); + stop(false); + } -> END; + + state END;