mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
6.4 KiB
258 lines
6.4 KiB
|
|
#include <sys/poll.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "AmUtils.h"
|
|
#include "AmConfig.h"
|
|
#include "AmSipHeaders.h"
|
|
#include "AmConfigReader.h"
|
|
#include "AmSession.h"
|
|
#include "sems.h"
|
|
#include "log.h"
|
|
|
|
#include "UnixCtrlInterface.h"
|
|
|
|
#ifndef MOD_NAME
|
|
#define MOD_NAME "unixsockctrl"
|
|
#endif
|
|
|
|
EXPORT_CONTROL_INTERFACE_FACTORY(UnixCtrlInterfaceFactory, MOD_NAME);
|
|
|
|
/*
|
|
* Config paramters names.
|
|
*/
|
|
#define SOCKET_NAME_PARAM "socket_name"
|
|
#define REPLY_SOCKET_NAME_PARAM "reply_socket_name"
|
|
#define SER_SOCKET_NAME_PARAM "ser_socket_name"
|
|
/*
|
|
* Config paramters default values.
|
|
*/
|
|
#define SOCKET_NAME_DEFAULT "/tmp/sems_sock"
|
|
#define REPLY_SOCKET_NAME_DEFAULT "/tmp/sems_rsp_sock"
|
|
#define SER_SOCKET_NAME_DEFAULT "/tmp/ser_sock"
|
|
|
|
#define SIP_POLL_TIMEOUT 50 /*50 ms*/
|
|
#define MAX_MSG_ERR 5
|
|
#define SND_USOCK_TEMPLATE "/tmp/sems_send_sock_XXXXXX" //TODO: configurable
|
|
|
|
//UnixCtrlInterface* UnixCtrlInterface::_instance = 0;
|
|
|
|
AmCtrlInterface* UnixCtrlInterfaceFactory::instance()
|
|
{
|
|
UnixCtrlInterface* ctrl = new UnixCtrlInterface(reply_socket_name,ser_socket_name);
|
|
|
|
if(ctrl->init(socket_name) < 0){
|
|
|
|
delete ctrl;
|
|
return NULL;
|
|
}
|
|
|
|
return ctrl;
|
|
}
|
|
|
|
int UnixCtrlInterfaceFactory::onLoad()
|
|
{
|
|
AmConfigReader cfg;
|
|
|
|
if (cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
|
|
WARN("failed to read/parse config file `%s' - assuming defaults\n",
|
|
(AmConfig::ModConfigPath + string(MOD_NAME ".conf")).c_str());
|
|
socket_name = string(SOCKET_NAME_DEFAULT);
|
|
reply_socket_name = string(REPLY_SOCKET_NAME_DEFAULT);
|
|
ser_socket_name = string(SER_SOCKET_NAME_DEFAULT);
|
|
} else {
|
|
socket_name = cfg.getParameter(SOCKET_NAME_PARAM, SOCKET_NAME_DEFAULT);
|
|
reply_socket_name = cfg.getParameter(REPLY_SOCKET_NAME_PARAM,
|
|
REPLY_SOCKET_NAME_DEFAULT);
|
|
ser_socket_name = cfg.getParameter(SER_SOCKET_NAME_PARAM,
|
|
SER_SOCKET_NAME_DEFAULT);
|
|
}
|
|
|
|
INFO(SOCKET_NAME_PARAM ": `%s'.\n", socket_name.c_str());
|
|
INFO(REPLY_SOCKET_NAME_PARAM ": `%s'.\n", reply_socket_name.c_str());
|
|
INFO(SER_SOCKET_NAME_PARAM ": `%s'.\n", ser_socket_name.c_str());
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
UnixCtrlInterface::~UnixCtrlInterface()
|
|
{
|
|
reqAdapt.close();
|
|
rplAdapt.close();
|
|
sndAdapt.close();
|
|
}
|
|
|
|
UnixCtrlInterface::UnixCtrlInterface(const string& reply_socket_name, const string& ser_socket_name)
|
|
: reply_socket_name(reply_socket_name), ser_socket_name(ser_socket_name)
|
|
{
|
|
sipDispatcher = AmSipDispatcher::instance();
|
|
|
|
}
|
|
|
|
int UnixCtrlInterface::init(const string& socket_name)
|
|
{
|
|
//initialize the listening adapters
|
|
if (! reqAdapt.init(socket_name)) {
|
|
ERROR("failed to initialize requests listner.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (! rplAdapt.init(reply_socket_name)) {
|
|
ERROR("failed to initialize replies listner.\n");
|
|
return -1;
|
|
}
|
|
|
|
int errcnt = -1;
|
|
char buff[sizeof(SND_USOCK_TEMPLATE)];
|
|
do {
|
|
if (MAX_MSG_ERR < ++errcnt) {
|
|
ERROR("failed to create a unix socket as a temproary file with "
|
|
"template `%s'.\n",SND_USOCK_TEMPLATE);
|
|
|
|
return -1;
|
|
}
|
|
|
|
//buff will hold the string for a temporary file that we'll than bind the
|
|
//sending unix socket to.
|
|
//Bind is needed so that replies can be received from SER.
|
|
memcpy(buff, SND_USOCK_TEMPLATE, sizeof(SND_USOCK_TEMPLATE));
|
|
int fd = mkstemp(buff);
|
|
if (0 <= fd) {
|
|
close(fd);
|
|
//by unlinking (here or in init(), there's a race created [the one
|
|
//mkstemp tries to fix :-) ) => try a bunch of times
|
|
unlink(buff);
|
|
}
|
|
} while (! sndAdapt.init(string(buff)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int UnixCtrlInterface::send(const AmSipRequest &req, char *, unsigned int &)
|
|
{
|
|
return rplAdapt.send(req, reply_socket_name, ser_socket_name);
|
|
}
|
|
|
|
int UnixCtrlInterface::send(const AmSipReply &rpl)
|
|
{
|
|
return sndAdapt.send(rpl, ser_socket_name);
|
|
}
|
|
|
|
void UnixCtrlInterface::run()
|
|
{
|
|
struct pollfd ufds[2];
|
|
AmSipRequest req;
|
|
AmSipReply rpl;
|
|
|
|
if (! sipDispatcher) {
|
|
ERROR("SIP dispacher hook not set.\n");
|
|
return;
|
|
}
|
|
|
|
#define POS_REQ 0
|
|
#define POS_RPL 1
|
|
ufds[POS_REQ].fd = reqAdapt.getFd();
|
|
ufds[POS_RPL].fd = rplAdapt.getFd();
|
|
ufds[POS_REQ].events = POLLIN;
|
|
ufds[POS_RPL].events = POLLIN;
|
|
// FIXME: is this really needed?!
|
|
ufds[POS_REQ].revents = 0;
|
|
ufds[POS_RPL].revents = 0;
|
|
|
|
#define SERVICE_EVENT(_adapter, _msg) \
|
|
do { \
|
|
if ((_adapter).receive(_msg)) \
|
|
sipDispatcher->handleSipMsg(_msg); \
|
|
else \
|
|
ERROR("failed to fetch %s.\n", #_msg); \
|
|
} while (0)
|
|
|
|
#ifdef OpenSER
|
|
DBG("Unix socket control interface built for OpenSER.\n");
|
|
#else
|
|
DBG("Unix socket control interface built for SER.\n");
|
|
#endif
|
|
|
|
DBG("Running UnixCtrlInterface thread.\n");
|
|
|
|
while (!is_stopped()) {
|
|
int ret = poll(ufds,2,/*FIXME: configurable*/SIP_POLL_TIMEOUT);
|
|
switch (ret) {
|
|
case -1:
|
|
ERROR("AmServer: poll: %s\n",strerror(errno));
|
|
continue;
|
|
case 0:
|
|
continue; //timedout
|
|
case 1:
|
|
if (ufds[POS_REQ].revents & POLLIN)
|
|
SERVICE_EVENT(reqAdapt, req);
|
|
else //rpl
|
|
SERVICE_EVENT(rplAdapt, rpl);
|
|
break;
|
|
case 2:
|
|
assert(ufds[POS_REQ].revents & POLLIN);
|
|
assert(ufds[POS_RPL].revents & POLLIN);
|
|
SERVICE_EVENT(reqAdapt, req);
|
|
SERVICE_EVENT(rplAdapt, rpl);
|
|
break;
|
|
default:
|
|
ERROR("unexpected poll events count: %i\n", ret);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#undef SERVICE_EVENT
|
|
#undef POS_REQ
|
|
#undef POS_RPL
|
|
}
|
|
|
|
string UnixCtrlInterface::getContact(const string &displayName,
|
|
const string &userName, const string &hostName,
|
|
const string &uriParams, const string &hdrParams)
|
|
{
|
|
string localUri;
|
|
|
|
if (displayName.length()) {
|
|
// quoting is safer (the check for quote need doesn't really pay off)
|
|
if (displayName.c_str()[0] == '"') {
|
|
assert(displayName.c_str()[displayName.length() - 1] == '"');
|
|
localUri += displayName;
|
|
} else {
|
|
localUri += '"';
|
|
localUri += displayName;
|
|
localUri += '"';
|
|
}
|
|
localUri += " ";
|
|
}
|
|
|
|
// angular brackets not always needed (unless contact)
|
|
localUri += "<";
|
|
localUri += SIP_SCHEME_SIP; //TODO: sips|tel|tels
|
|
localUri += ":";
|
|
if (userName.length()) {
|
|
localUri += userName;
|
|
localUri += "@";
|
|
}
|
|
if (hostName.length())
|
|
localUri += hostName;
|
|
else
|
|
localUri += "!!"; // Ser will replace that...
|
|
|
|
if (uriParams.length()) {
|
|
if (uriParams.c_str()[0] != ';')
|
|
localUri += ';';
|
|
localUri += uriParams;
|
|
}
|
|
localUri += ">";
|
|
|
|
if (hdrParams.length()) {
|
|
if (hdrParams.c_str()[0] != ';')
|
|
localUri += ';';
|
|
localUri += hdrParams;
|
|
}
|
|
|
|
return localUri;
|
|
}
|