mirror of https://github.com/sipwise/sems.git
parent
6a96ffdbbc
commit
2bf464e5cb
@ -0,0 +1,37 @@
|
||||
#ifndef _DBTypes_h_
|
||||
#define _DBTypes_h_
|
||||
|
||||
#include "AmArg.h"
|
||||
#include "AmConfigReader.h"
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
using std::map;
|
||||
using std::list;
|
||||
|
||||
struct DBIdxType
|
||||
{
|
||||
string key;
|
||||
string value;
|
||||
|
||||
DBIdxType() {}
|
||||
DBIdxType(const string& key, const string& value)
|
||||
: key(key), value(value)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef map<string,AmArg> DBMapType;
|
||||
typedef list<DBIdxType> DBIdxList;
|
||||
|
||||
#define DB_E_OK 0
|
||||
#define DB_E_CONNECTION -1
|
||||
#define DB_E_WRITE -2
|
||||
#define DB_E_READ -3
|
||||
|
||||
enum RestoreResult {
|
||||
SUCCESS,
|
||||
FAILURE, // an error
|
||||
NOT_FOUND // object not found in the storage
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,194 @@
|
||||
#include "DRedisConnection.h"
|
||||
#include "jsonArg.h"
|
||||
#include "AmUtils.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
DRedisConfig::DRedisConfig(const string& host, unsigned int port,
|
||||
bool unix_socket, bool full_logging,
|
||||
bool use_transactions, int connect_timeout)
|
||||
: host(host), port(port),
|
||||
unix_socket(unix_socket),
|
||||
full_logging(full_logging),
|
||||
use_transactions(use_transactions)
|
||||
{
|
||||
tv_timeout.tv_sec = connect_timeout / 1000;
|
||||
tv_timeout.tv_usec = (connect_timeout - 1000 * tv_timeout.tv_sec) * 1000;
|
||||
}
|
||||
|
||||
DRedisConnection::DRedisConnection(const DRedisConfig& cfg)
|
||||
: redis_context(NULL), cfg(cfg)
|
||||
{
|
||||
}
|
||||
|
||||
DRedisConnection::~DRedisConnection()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void DRedisConnection::disconnect()
|
||||
{
|
||||
if(redis_context) {
|
||||
DBG("disconnecting from Redis...");
|
||||
redisFree(redis_context);
|
||||
redis_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool DRedisConnection::connect()
|
||||
{
|
||||
if(redis_context)
|
||||
return true;
|
||||
|
||||
if(!cfg.unix_socket) {
|
||||
DBG("connecting to REDIS at %s:%u\n", cfg.host.c_str(), cfg.port);
|
||||
redis_context = redisConnectWithTimeout((char*)cfg.host.c_str(),
|
||||
cfg.port, cfg.tv_timeout);
|
||||
}
|
||||
else {
|
||||
DBG("connecting to REDIS at %s\n", cfg.host.c_str());
|
||||
redis_context = redisConnectUnixWithTimeout(cfg.host.c_str(),
|
||||
cfg.tv_timeout);
|
||||
}
|
||||
|
||||
if (redis_context->err) {
|
||||
ERROR("REDIS Connection error: %s\n", redis_context->errstr);
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int DRedisConnection::handle_redis_reply(redisReply *reply, const char* _cmd) {
|
||||
if (!reply) {
|
||||
switch (redis_context->err) {
|
||||
case REDIS_ERR_IO:
|
||||
ERROR("I/O error: %s (%s)\n", redis_context->errstr,_cmd);
|
||||
disconnect();
|
||||
return DB_E_CONNECTION;
|
||||
|
||||
case REDIS_ERR_EOF: // silently reconnect
|
||||
case REDIS_ERR_OTHER:
|
||||
disconnect();
|
||||
return DB_E_CONNECTION;
|
||||
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
ERROR("REDIS Protocol error detected\n");
|
||||
disconnect();
|
||||
return DB_E_CONNECTION;
|
||||
}
|
||||
}
|
||||
|
||||
switch (reply->type) {
|
||||
case REDIS_REPLY_ERROR:
|
||||
ERROR("REDIS %s ERROR: %s\n", _cmd, reply->str);
|
||||
return DB_E_WRITE;
|
||||
|
||||
case REDIS_REPLY_STATUS:
|
||||
case REDIS_REPLY_STRING:
|
||||
if (reply->len>=0) {
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS %s: str: %.*s\n", _cmd, reply->len, reply->str);
|
||||
}
|
||||
} break;
|
||||
|
||||
case REDIS_REPLY_INTEGER:
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS %s: int: %lld\n", _cmd, reply->integer);
|
||||
} break;
|
||||
|
||||
case REDIS_REPLY_ARRAY: {
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS %s: array START\n", _cmd);
|
||||
};
|
||||
for (size_t i=0;i<reply->elements;i++) {
|
||||
switch(reply->element[i]->type) {
|
||||
case REDIS_REPLY_ERROR: ERROR("REDIS %s ERROR: %.*s\n",
|
||||
_cmd, reply->element[i]->len,
|
||||
reply->element[i]->str);
|
||||
return DB_E_WRITE;
|
||||
|
||||
case REDIS_REPLY_INTEGER:
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS %s: %lld\n", _cmd, reply->element[i]->integer);
|
||||
} break;
|
||||
|
||||
case REDIS_REPLY_NIL:
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS %s: nil\n", _cmd);
|
||||
} break;
|
||||
|
||||
case REDIS_REPLY_ARRAY:
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS : %zd elements\n", reply->elements);
|
||||
} break;
|
||||
|
||||
case REDIS_REPLY_STATUS:
|
||||
case REDIS_REPLY_STRING:
|
||||
if (cfg.full_logging) {
|
||||
if (reply->element[i]->len >= 0) {
|
||||
DBG("REDIS %s: %.*s\n", _cmd,
|
||||
reply->element[i]->len, reply->element[i]->str);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERROR("unknown REDIS reply %d to %s!",reply->element[i]->type, _cmd); break;
|
||||
}
|
||||
}
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS %s: array END\n", _cmd);
|
||||
};
|
||||
}; break;
|
||||
|
||||
default: ERROR("unknown REDIS reply %d to %s!", reply->type, _cmd); break;
|
||||
}
|
||||
|
||||
if (cfg.full_logging) {
|
||||
DBG("REDIS cmd %s executed successfully\n", _cmd);
|
||||
}
|
||||
return DB_E_OK;
|
||||
}
|
||||
|
||||
#define RETURN_READ_ERROR \
|
||||
freeReplyObject(reply); \
|
||||
return DB_E_READ;
|
||||
|
||||
int DRedisConnection::exec_cmd(const char* cmd, redisReply*& reply) {
|
||||
|
||||
if(!redis_context) {
|
||||
ERROR("REDIS cmd '%s': not connected",cmd);
|
||||
return DB_E_CONNECTION;
|
||||
}
|
||||
reply = NULL;
|
||||
|
||||
reply = (redisReply *)redisCommand(redis_context, cmd);
|
||||
int ret = handle_redis_reply(reply, cmd);
|
||||
if (ret != DB_E_OK)
|
||||
return ret;
|
||||
|
||||
DBG("successfully executed redis cmd\n");
|
||||
return DB_E_OK;
|
||||
}
|
||||
|
||||
int DRedisConnection::append_cmd(const char* cmd) {
|
||||
if(!redis_context) {
|
||||
ERROR("REDIS append cmd '%s': not connected",cmd);
|
||||
return DB_E_CONNECTION;
|
||||
}
|
||||
return redisAppendCommand(redis_context, cmd) == REDIS_OK ?
|
||||
DB_E_OK : DB_E_CONNECTION;
|
||||
}
|
||||
|
||||
int DRedisConnection::get_reply(redisReply*& reply) {
|
||||
if(!redis_context) {
|
||||
ERROR("REDIS get_reply: not connected");
|
||||
return DB_E_CONNECTION;
|
||||
}
|
||||
|
||||
redisGetReply(redis_context, (void**)&reply);
|
||||
int ret = handle_redis_reply(reply, "<pipelined>");
|
||||
return ret;
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
#ifndef _DRedisConnection_h_
|
||||
#define _DRedisConnection_h_
|
||||
|
||||
#include "hiredis/hiredis.h"
|
||||
#include "DBTypes.h"
|
||||
|
||||
#define DEFAULT_REDIS_HOST "127.0.0.1"
|
||||
#define DEFAULT_REDIS_PORT 6379
|
||||
|
||||
#define DEFAULT_REDIS_CONNECT_TIMEOUT 500
|
||||
|
||||
struct DRedisConfig
|
||||
{
|
||||
string host;
|
||||
unsigned int port;
|
||||
bool unix_socket;
|
||||
bool full_logging;
|
||||
bool use_transactions;
|
||||
struct timeval tv_timeout;
|
||||
|
||||
DRedisConfig(const string& host = DEFAULT_REDIS_HOST,
|
||||
unsigned int port = DEFAULT_REDIS_PORT,
|
||||
bool unix_socket = false,
|
||||
bool full_logging = false,
|
||||
bool use_transactions = false,
|
||||
int connect_timeout = DEFAULT_REDIS_CONNECT_TIMEOUT);
|
||||
};
|
||||
|
||||
class DRedisConnection
|
||||
{
|
||||
DRedisConfig cfg;
|
||||
redisContext* redis_context;
|
||||
|
||||
int handle_redis_reply(redisReply *reply, const char* _cmd);
|
||||
|
||||
public:
|
||||
DRedisConnection(const DRedisConfig& cfg);
|
||||
~DRedisConnection();
|
||||
|
||||
bool connect();
|
||||
void disconnect();
|
||||
|
||||
bool connected() { return redis_context != NULL; }
|
||||
|
||||
int exec_cmd(const char* cmd, redisReply*& reply);
|
||||
int append_cmd(const char* cmd);
|
||||
int get_reply(redisReply*& reply);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,10 @@
|
||||
plug_in_name = mod_redis
|
||||
|
||||
DSMPATH ?= ../..
|
||||
|
||||
module_ldflags = -lhiredis
|
||||
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
|
@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Stefan Sayer
|
||||
*
|
||||
* 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 "ModRedis.h"
|
||||
#include "log.h"
|
||||
#include "AmUtils.h"
|
||||
|
||||
#include "DSMSession.h"
|
||||
#include "AmSession.h"
|
||||
#include "AmPlaylist.h"
|
||||
#include "DSMCoreModule.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
|
||||
SC_EXPORT(DSMRedisModule);
|
||||
|
||||
DSMRedisModule::DSMRedisModule() {
|
||||
}
|
||||
|
||||
DSMRedisModule::~DSMRedisModule() {
|
||||
}
|
||||
|
||||
|
||||
DSMAction* DSMRedisModule::getAction(const string& from_str) {
|
||||
string cmd;
|
||||
string params;
|
||||
splitCmd(from_str, cmd, params);
|
||||
|
||||
DEF_CMD("redis.connect", DSMRedisConnectAction);
|
||||
DEF_CMD("redis.disconnect", DSMRedisDisconnectAction);
|
||||
DEF_CMD("redis.execCommand", DSMRedisExecCommandAction);
|
||||
DEF_CMD("redis.appendCommand", DSMRedisAppendCommandAction);
|
||||
DEF_CMD("redis.getReply", DSMRedisGetReplyAction);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DSMCondition* DSMRedisModule::getCondition(const string& from_str) {
|
||||
// string cmd;
|
||||
// string params;
|
||||
// splitCmd(from_str, cmd, params);
|
||||
|
||||
// if (cmd == "redis.hasResult") {
|
||||
// return new MyHasResultCondition(params, false);
|
||||
// }
|
||||
|
||||
// if (cmd == "redis.connected") {
|
||||
// return new MyConnectedCondition(params, true);
|
||||
// }
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DSMRedisResult::~DSMRedisResult() {
|
||||
if (NULL != result) {
|
||||
freeReplyObject(result);
|
||||
}
|
||||
}
|
||||
|
||||
void DSMRedisResult::release() {
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
DSMRedisConnection* getRedisDSMSessionConnection(DSMSession* sc_sess) {
|
||||
if (sc_sess->avar.find(REDIS_AKEY_CONNECTION) == sc_sess->avar.end()) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "No connection to redis database");
|
||||
return NULL;
|
||||
}
|
||||
AmObject* ao = NULL; DSMRedisConnection* res = NULL;
|
||||
try {
|
||||
if (!isArgAObject(sc_sess->avar[REDIS_AKEY_CONNECTION])) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "No connection to redis database (not AmObject)");
|
||||
return NULL;
|
||||
}
|
||||
ao = sc_sess->avar[REDIS_AKEY_CONNECTION].asObject();
|
||||
} catch (...){
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "No connection to redis database (not AmObject)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (NULL == ao || NULL == (res = dynamic_cast<DSMRedisConnection*>(ao))) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "No connection to database (not a RedisConnection)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
DSMRedisConnection* getConnectedRedisDSMSessionConnection(DSMSession* sc_sess) {
|
||||
DSMRedisConnection* res = getRedisDSMSessionConnection(sc_sess);
|
||||
if (!res || res->connected() || res->connect())
|
||||
return res;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
DSMRedisResult* getRedisDSMResult(DSMSession* sc_sess) {
|
||||
if (sc_sess->avar.find(REDIS_AKEY_RESULT) == sc_sess->avar.end()) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_NORESULT, "No result available");
|
||||
return NULL;
|
||||
}
|
||||
AmObject* ao = NULL; DSMRedisResult* res = NULL;
|
||||
try {
|
||||
assertArgAObject(sc_sess->avar[REDIS_AKEY_RESULT]);
|
||||
ao = sc_sess->avar[REDIS_AKEY_RESULT].asObject();
|
||||
} catch (...){
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_NORESULT, "Result object has wrong type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (NULL == ao || NULL == (res = dynamic_cast<DSMRedisResult*>(ao))) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_NORESULT, "Result object has wrong type");
|
||||
return NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
string replaceQueryParams(const string& q, DSMSession* sc_sess,
|
||||
map<string,string>* event_params) {
|
||||
string res = q;
|
||||
size_t repl_pos = 0;
|
||||
while (repl_pos<res.length()) {
|
||||
size_t rstart = res.find_first_of("$#", repl_pos);
|
||||
repl_pos = rstart+1;
|
||||
if (rstart == string::npos)
|
||||
break;
|
||||
if (rstart && res[rstart-1] == '\\') // escaped
|
||||
continue;
|
||||
|
||||
size_t rend = res.find_first_of(" ,()$#\t;'\"", rstart+1);
|
||||
if (rend==string::npos)
|
||||
rend = res.length();
|
||||
switch(res[rstart]) {
|
||||
case '$':
|
||||
res.replace(rstart, rend-rstart,
|
||||
sc_sess->var[res.substr(rstart+1, rend-rstart-1)]); break;
|
||||
case '#':
|
||||
if (NULL!=event_params) {
|
||||
res.replace(rstart, rend-rstart,
|
||||
(*event_params)[res.substr(rstart+1, rend-rstart-1)]); break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
string skip_till(string& s, string sep) {
|
||||
size_t pos = s.find_first_of(sep);
|
||||
if (pos == string::npos) {
|
||||
string res = s;
|
||||
s.clear();
|
||||
return res;
|
||||
} else {
|
||||
string res = s.substr(0, pos);
|
||||
if (s.length()>pos)
|
||||
s = s.substr(pos+1);
|
||||
else
|
||||
s.clear();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
EXEC_ACTION_START(DSMRedisConnectAction) {
|
||||
string f_arg = resolveVars(arg, sess, sc_sess, event_params);
|
||||
string db_url = f_arg.length()?f_arg:sc_sess->var["config.redis_db_url"];
|
||||
if (db_url.empty() || db_url.length() < 11 || db_url.substr(0, 8) != "redis://") {
|
||||
ERROR("missing correct db_url config or connect parameter - must start with redis://\n");
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_UNKNOWN_ARG,
|
||||
"missing correct db_url config or connect parameter - must start with redis://\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
db_url = db_url.substr(8);
|
||||
|
||||
// split url - tcp:host;param=value or unix:host;param=value
|
||||
|
||||
string db_proto = skip_till(db_url, ":");
|
||||
if (db_proto != "unix" && db_proto != "tcp") {
|
||||
ERROR("missing correct redis_db_url config or connect parameter - must start with unix or tcp protocol\n");
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_UNKNOWN_ARG,
|
||||
"missing correct db_url config or connect parameter - must start with unix or tcp protocol\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
bool unix_socket = db_proto=="unix";
|
||||
|
||||
string db_host = skip_till(db_url, ":;");
|
||||
if (db_host.empty()) {
|
||||
ERROR("missing correct redis_db_url config or connect parameter - host must be non-empty\n");
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_UNKNOWN_ARG,
|
||||
"missing correct db_url config or connect parameter - host must be non-empty\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
unsigned int redis_port = DEFAULT_REDIS_PORT;
|
||||
bool full_logging = false;
|
||||
bool use_transactions = false;
|
||||
int connect_timeout = DEFAULT_REDIS_CONNECT_TIMEOUT;
|
||||
|
||||
while (!db_url.empty()) {
|
||||
string param = skip_till(db_url, ";");
|
||||
vector<string> p = explode(param, "=");
|
||||
if (p.size() != 2) {
|
||||
ERROR("missing correct redis_db_url config or connect parameter - "
|
||||
"parameter '%s' must be param=value\n", param.c_str());
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_UNKNOWN_ARG,
|
||||
"missing correct db_url config or connect parameter - parameter must be param=value\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
if (p[0] == "full_logging") {
|
||||
full_logging = p[1] == "true";
|
||||
} else if (p[0] == "use_transactions") {
|
||||
use_transactions = p[1] == "true";
|
||||
} else if (p[0] == "connect_timeout"){
|
||||
str2int(p[1], connect_timeout);
|
||||
} else if (p[0] == "port"){
|
||||
str2i(p[1], redis_port);
|
||||
} else {
|
||||
ERROR("unknown redis_db_url config or connect parameter - "
|
||||
"parameter '%s'\n", p[0].c_str());
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_UNKNOWN_ARG, "missing correct db_url config or connect parameter\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
DSMRedisConnection* conn =
|
||||
new DSMRedisConnection(db_host, redis_port, unix_socket, full_logging,
|
||||
use_transactions, connect_timeout);
|
||||
|
||||
if (!conn->connect()) {
|
||||
delete conn;
|
||||
ERROR("Could not connect to redis DB\n");
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "Could not connect to redis DB");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
// save connection for later use
|
||||
AmArg c_arg;
|
||||
c_arg.setBorrowedPointer(conn);
|
||||
sc_sess->avar[REDIS_AKEY_CONNECTION] = c_arg;
|
||||
// for garbage collection
|
||||
sc_sess->transferOwnership(conn);
|
||||
CLR_ERROR(sc_sess);
|
||||
|
||||
} EXEC_ACTION_END;
|
||||
|
||||
EXEC_ACTION_START(DSMRedisDisconnectAction) {
|
||||
DSMRedisConnection* conn = getRedisDSMSessionConnection(sc_sess);
|
||||
if (NULL == conn) {
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
conn->disconnect();
|
||||
// connection object might be reused - but its safer to create a new one
|
||||
sc_sess->releaseOwnership(conn);
|
||||
delete conn;
|
||||
sc_sess->avar.erase(REDIS_AKEY_CONNECTION);
|
||||
sc_sess->CLR_ERRNO;
|
||||
} EXEC_ACTION_END;
|
||||
|
||||
void decodeRedisResult(VarMapT& dst, const string& varname, redisReply* reply) {
|
||||
if (!reply)
|
||||
return;
|
||||
switch (reply->type) {
|
||||
case REDIS_REPLY_STRING: dst[varname] = string(reply->str, reply->len); break;
|
||||
case REDIS_REPLY_INTEGER: dst[varname] = int2str((int)reply->integer); break; // todo: long long?
|
||||
case REDIS_REPLY_NIL: dst[varname] = "nil"; break;
|
||||
case REDIS_REPLY_STATUS: dst[varname] = string(reply->str, reply->len); break;
|
||||
case REDIS_REPLY_ERROR: ERROR("decoding REDIS reply - ERROR type"); break;
|
||||
case REDIS_REPLY_ARRAY: {
|
||||
for (size_t i=0;i<reply->elements;i++) {
|
||||
decodeRedisResult(dst, varname+"["+int2str((unsigned int)i)+"]", reply->element[i]);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleResult(DSMSession* sc_sess, int res, redisReply* reply, const string& resultvar) {
|
||||
switch (res) {
|
||||
case DB_E_OK: {
|
||||
decodeRedisResult(sc_sess->var, resultvar, reply);
|
||||
freeReplyObject(reply);
|
||||
} break;
|
||||
case DB_E_CONNECTION: SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "REDIS connection error"); return;
|
||||
case DB_E_WRITE: SET_ERROR(sc_sess, DSM_ERRNO_REDIS_WRITE, "REDIS write error"); return;
|
||||
case DB_E_READ: SET_ERROR(sc_sess, DSM_ERRNO_REDIS_READ, "REDIS read error"); return;
|
||||
default: SET_ERROR(sc_sess, DSM_ERRNO_REDIS_UNKNOWN, "REDIS unknown error"); return;
|
||||
}
|
||||
}
|
||||
|
||||
CONST_ACTION_2P(DSMRedisExecCommandAction, '=', false);
|
||||
EXEC_ACTION_START(DSMRedisExecCommandAction) {
|
||||
string resultvar = par1;
|
||||
if (resultvar.length() && resultvar[0]=='$') resultvar=resultvar.substr(1);
|
||||
string cmd = replaceParams(par2, sess, sc_sess, event_params);
|
||||
DBG("executing redis command $%s='%s'\n", resultvar.c_str(), cmd.c_str());
|
||||
|
||||
DSMRedisConnection* conn = getConnectedRedisDSMSessionConnection(sc_sess);
|
||||
if (!conn) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "Not connected to REDIS\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
redisReply* reply;
|
||||
int res = conn->exec_cmd(cmd.c_str(), reply);
|
||||
|
||||
handleResult(sc_sess, res, reply, resultvar);
|
||||
} EXEC_ACTION_END;
|
||||
|
||||
EXEC_ACTION_START(DSMRedisAppendCommandAction) {
|
||||
string cmd = replaceParams(arg, sess, sc_sess, event_params);
|
||||
DBG("appending redis command '%s' - from '%s'\n", cmd.c_str(), arg.c_str());
|
||||
|
||||
DSMRedisConnection* conn = getConnectedRedisDSMSessionConnection(sc_sess);
|
||||
if (!conn) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "Not connected to REDIS\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
if (conn->append_cmd(cmd.c_str()) == DB_E_OK) {
|
||||
CLR_ERROR(sc_sess);
|
||||
} else {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "Error appending command - no memory?\n");
|
||||
}
|
||||
} EXEC_ACTION_END;
|
||||
|
||||
EXEC_ACTION_START(DSMRedisGetReplyAction) {
|
||||
string resultvar = arg;
|
||||
if (resultvar.length() && resultvar[0]=='$') resultvar=resultvar.substr(1);
|
||||
DBG("getting result for redis command in $%s\n", resultvar.c_str());
|
||||
|
||||
DSMRedisConnection* conn = getConnectedRedisDSMSessionConnection(sc_sess);
|
||||
if (!conn) {
|
||||
SET_ERROR(sc_sess, DSM_ERRNO_REDIS_CONNECTION, "Not connected to REDIS\n");
|
||||
EXEC_ACTION_STOP;
|
||||
}
|
||||
|
||||
redisReply* reply;
|
||||
int res = conn->get_reply(reply);
|
||||
|
||||
handleResult(sc_sess, res, reply, resultvar);
|
||||
} EXEC_ACTION_END;
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Stefan Sayer
|
||||
*
|
||||
* 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_REDIS_H
|
||||
#define _MOD_REDIS_H
|
||||
#include "DSMModule.h"
|
||||
#include "DSMSession.h"
|
||||
|
||||
#include "DRedisConnection.h"
|
||||
|
||||
#define REDIS_AKEY_CONNECTION "db_redis.con"
|
||||
#define REDIS_AKEY_RESULT "db_redis.res"
|
||||
|
||||
#define DSM_ERRNO_REDIS_CONNECTION "connection"
|
||||
#define DSM_ERRNO_REDIS_WRITE "write"
|
||||
#define DSM_ERRNO_REDIS_READ "read"
|
||||
#define DSM_ERRNO_REDIS_QUERY "query"
|
||||
#define DSM_ERRNO_REDIS_NORESULT "result"
|
||||
#define DSM_ERRNO_REDIS_NOROW "result"
|
||||
#define DSM_ERRNO_REDIS_NOCOLUMN "result"
|
||||
#define DSM_ERRNO_REDIS_UNKNOWN "unknown"
|
||||
|
||||
class DSMRedisModule
|
||||
: public DSMModule {
|
||||
|
||||
public:
|
||||
DSMRedisModule();
|
||||
~DSMRedisModule();
|
||||
|
||||
DSMAction* getAction(const string& from_str);
|
||||
DSMCondition* getCondition(const string& from_str);
|
||||
};
|
||||
|
||||
class DSMRedisConnection
|
||||
: public DRedisConnection,
|
||||
public AmObject,
|
||||
public DSMDisposable
|
||||
{
|
||||
public:
|
||||
DSMRedisConnection(const string& host, unsigned int port,
|
||||
bool unix_socket, bool full_logging, bool use_transactions, int connect_timeout)
|
||||
: DRedisConnection(DRedisConfig(host, port, unix_socket, full_logging, use_transactions, connect_timeout))
|
||||
{ }
|
||||
~DSMRedisConnection() { }
|
||||
};
|
||||
|
||||
class DSMRedisResult
|
||||
: public AmObject,
|
||||
public DSMDisposable
|
||||
{
|
||||
redisReply* result;
|
||||
|
||||
public:
|
||||
DSMRedisResult(redisReply* result) : result(result) { }
|
||||
~DSMRedisResult();
|
||||
void release();
|
||||
};
|
||||
|
||||
DEF_ACTION_1P(DSMRedisConnectAction);
|
||||
DEF_ACTION_1P(DSMRedisDisconnectAction);
|
||||
DEF_ACTION_2P(DSMRedisExecCommandAction);
|
||||
DEF_ACTION_1P(DSMRedisAppendCommandAction);
|
||||
DEF_ACTION_1P(DSMRedisGetReplyAction);
|
||||
#endif
|
@ -0,0 +1,71 @@
|
||||
# Skeleton of a call center style application that answers, plays beep_file
|
||||
# to caller and then tries attendants on callee_list in round robin fashion
|
||||
# as long as one of them replies
|
||||
|
||||
# Author Juha Heinanen <jh@tutpro.com>
|
||||
|
||||
import time
|
||||
|
||||
from log import *
|
||||
from ivr import *
|
||||
|
||||
beep_file = "/var/lib/sems/audio/general/beep_snd.wav"
|
||||
callee_list = ['sip:foo@test.tutpro.com', 'sip:test@test.tutpro.com']
|
||||
|
||||
beeping = 1
|
||||
connecting = 2
|
||||
connected = 3
|
||||
|
||||
class IvrDialog(IvrDialogBase):
|
||||
|
||||
def onSessionStart(self):
|
||||
|
||||
self.callee_list = callee_list
|
||||
self.callee_index = 0
|
||||
self.setNoRelayonly()
|
||||
self.state = beeping
|
||||
self.audio_msg = IvrAudioFile()
|
||||
self.audio_msg.open(beep_file, AUDIO_READ)
|
||||
self.enqueue(self.audio_msg, None)
|
||||
|
||||
def onBye(self):
|
||||
|
||||
self.stopSession()
|
||||
|
||||
def onEmptyQueue(self):
|
||||
|
||||
if self.state == beeping:
|
||||
self.state = connecting
|
||||
self.connectTry()
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
def onOtherReply(self, code, reason):
|
||||
|
||||
debug('call_center: got reply: ' + str(code) + ' ' + reason)
|
||||
|
||||
if self.state == connecting:
|
||||
|
||||
if code < 200:
|
||||
return
|
||||
if code >= 200 and code < 300:
|
||||
self.flush()
|
||||
self.disconnectMedia()
|
||||
self.setRelayonly()
|
||||
self.state = connected
|
||||
debug('call_center: connected to ' + self.callee_uri)
|
||||
return
|
||||
if code >= 300:
|
||||
time.sleep(3)
|
||||
self.connectTry()
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
||||
def connectTry(self):
|
||||
|
||||
self.callee_uri = self.callee_list[self.callee_index]
|
||||
self.callee_index = (self.callee_index + 1) % 2
|
||||
debug('call_center: trying to connectCallee ' + self.callee_uri)
|
||||
self.connectCallee(self.callee_uri, self.callee_uri)
|
@ -0,0 +1,18 @@
|
||||
COREPATH_TOOLS ?= ../../../core
|
||||
COREPATH ?= ../../../core
|
||||
include $(COREPATH_TOOLS)/../Makefile.defs
|
||||
|
||||
wc_scripts = $(wildcard sems-webconference-*)
|
||||
|
||||
all: install_tools
|
||||
install: install_tools
|
||||
|
||||
install_tools: $(DESTDIR)$(bin-prefix)/$(bin-dir)
|
||||
-@for r in $(wc_scripts) ; do \
|
||||
x=`echo $$r | sed s/sems-/$(APP_NAME)-/g` ; \
|
||||
echo "installing $$r -> $$x" ; \
|
||||
$(INSTALL-TOUCH) $(DESTDIR)$(bin-prefix)/$(bin-dir)/$$x ; \
|
||||
$(INSTALL-BIN) $$r $(DESTDIR)$(bin-prefix)/$(bin-dir)/$$x ; \
|
||||
done
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
from xmlrpclib import *
|
||||
|
||||
if len(sys.argv) != 4:
|
||||
print "usage: %s <room> <participant_id> <number>" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
s = ServerProxy('http://localhost:8090')
|
||||
print "Active calls: %d" % s.calls()
|
||||
print s.di('webconference','addParticipant', sys.argv[1], sys.argv[2], sys.argv[3])
|
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
from xmlrpclib import *
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "usage: %s <room name>" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
s = ServerProxy('http://localhost:8090')
|
||||
print "Active calls: %d" % s.calls()
|
||||
print s.di('webconference','roomCreate', sys.argv[1])
|
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
from xmlrpclib import *
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print "usage: %s <room> <adminpin>" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
s = ServerProxy('http://localhost:8090')
|
||||
print "Active calls: %d" % s.calls()
|
||||
print s.di('webconference','roomInfo', sys.argv[1], sys.argv[2])
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue