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.
393 lines
8.8 KiB
393 lines
8.8 KiB
/*
|
|
* $Id: SerClient.cpp,v 1.2.2.1 2005/04/13 10:57:09 rco Exp $
|
|
*
|
|
* Copyright (C) 2002-2003 Fhg Fokus
|
|
*
|
|
* 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
|
|
*
|
|
* For a license to use the ser 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 "SerClient.h"
|
|
#include "AmConfig.h"
|
|
#include "AmSession.h"
|
|
#include "AmUtils.h"
|
|
#include "log.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/select.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
/** @return NULL on failure. */
|
|
static int open_reply_fifo(const string& filename);
|
|
|
|
static int write_to_fifo(int reply_fd, const char * buf, unsigned int len);
|
|
static int write_to_socket(int reply_fd, const char * buf, unsigned int len);
|
|
|
|
static int read_from_fifo(int fd,char* buffer,int buf_size,int timeout);
|
|
static int read_from_socket(int fd,char* buffer,int buf_size,int timeout);
|
|
|
|
|
|
extern "C" {
|
|
|
|
struct ser_client_cb_t fifo_cbs = {
|
|
open_reply_fifo,
|
|
write_to_fifo,
|
|
read_from_fifo
|
|
};
|
|
|
|
struct ser_client_cb_t socket_cbs = {
|
|
create_unix_socket,
|
|
write_to_socket,
|
|
read_from_socket
|
|
};
|
|
}
|
|
|
|
SerClientData::SerClientData(int id,const string& addr)
|
|
: id(id), addr(addr)
|
|
{
|
|
msg_buf[0] = '\n';
|
|
}
|
|
|
|
SerClient* SerClient::_client=0;
|
|
|
|
SerClient* SerClient::getInstance()
|
|
{
|
|
if(_client)
|
|
return _client;
|
|
|
|
SerClient* c = new SerClient();
|
|
c->cbs=0;
|
|
|
|
if(AmConfig::SendMethod == "fifo")
|
|
c->cbs = &fifo_cbs;
|
|
else if(AmConfig::SendMethod == "unix_socket")
|
|
c->cbs = &socket_cbs;
|
|
else {
|
|
ERROR("unknown send method\n");
|
|
delete c;
|
|
return 0;
|
|
}
|
|
|
|
return _client = c;
|
|
}
|
|
|
|
SerClientData* SerClient::id2scd(int id)
|
|
{
|
|
map<int,SerClientData*>::iterator scd_it;
|
|
if((scd_it = datas.find(id)) == datas.end()){
|
|
ERROR("SerClient <%i> does not exist.\n",id);
|
|
return 0;
|
|
}
|
|
|
|
return scd_it->second;
|
|
}
|
|
|
|
int SerClient::open()
|
|
{
|
|
string addr = AmSession::getNewId();
|
|
string res_fifo = "/tmp/" + addr;
|
|
|
|
int reply_fd = cbs->open_reply(res_fifo);
|
|
if (reply_fd == -1){
|
|
ERROR("could not open reply fifo\n");
|
|
return -1;
|
|
}
|
|
|
|
datas[reply_fd] = new SerClientData(reply_fd,addr);
|
|
|
|
return reply_fd;
|
|
}
|
|
|
|
int SerClient::send(int id, const string& cmd,
|
|
const string& msg, int timeout)
|
|
{
|
|
SerClientData* scd = id2scd(id);
|
|
if(!scd) return -1;
|
|
|
|
string buf = ":" + cmd + ":"
|
|
+ scd->addr + "\n" + msg;
|
|
|
|
if(cbs->write(scd->id,buf.c_str(),buf.length()) == -1){
|
|
ERROR("while sending request to Ser.\n");
|
|
return -1;
|
|
}
|
|
|
|
if(cbs->read(scd->id,scd->msg_buf,
|
|
MSG_BUFFER_SIZE,timeout) == -1){
|
|
ERROR("while reading Ser's response.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char* SerClient::getResponseBuffer(int id)
|
|
{
|
|
SerClientData* scd = id2scd(id);
|
|
if(!scd) return NULL;
|
|
return scd->msg_buf;
|
|
}
|
|
|
|
void SerClient::close(int id)
|
|
{
|
|
SerClientData* scd = id2scd(id);
|
|
if(scd){
|
|
::close(scd->id);
|
|
::unlink(("/tmp/"+scd->addr).c_str());
|
|
}
|
|
delete scd;
|
|
}
|
|
|
|
/*
|
|
* Creates a reply fifo, opens it nonblocking,
|
|
* then sets flags to blocking returns -1 on failure
|
|
*/
|
|
static int open_reply_fifo(const string& reply_fifo_filename)
|
|
{
|
|
|
|
if( (mkfifo(reply_fifo_filename.c_str(),FIFO_PERM)<0) && (errno!=EEXIST) ) {
|
|
ERROR("while creating fifo `%s': %s \n",
|
|
reply_fifo_filename.c_str(),strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
struct stat filestat;
|
|
if (stat(reply_fifo_filename.c_str(), &filestat)==-1) { /* FIFO doesn't exist yet ... */
|
|
return 0;
|
|
} else { /* file can be stat-ed, check if it is really a FIFO */
|
|
if (!(S_ISFIFO(filestat.st_mode))) {
|
|
ERROR("ERROR: the file is not a FIFO: %s\n", reply_fifo_filename.c_str() );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* filehandle for open, being used by fdopen and therefor
|
|
* does not have to be closed if stream gets closed */
|
|
int reply_filehandle;
|
|
|
|
reply_filehandle = open (reply_fifo_filename.c_str(), O_RDONLY | O_NONBLOCK , 0);
|
|
if (reply_filehandle == -1) {
|
|
ERROR("could not open reply_fifo, reason %s\n", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
/* now set blocking mode to make shure answer is read */
|
|
int flags;
|
|
if ( (flags=fcntl(reply_filehandle, F_GETFL, 0))<0) {
|
|
ERROR("getfl failed: %s\n", strerror(errno));
|
|
goto error;
|
|
}
|
|
flags&=~O_NONBLOCK;
|
|
if (fcntl(reply_filehandle, F_SETFL, flags)<0) {
|
|
ERROR("setfl cntl failed: %s\n", strerror(errno));
|
|
goto error;
|
|
}
|
|
|
|
return reply_filehandle;
|
|
|
|
error:
|
|
close(reply_filehandle);
|
|
unlink(reply_fifo_filename.c_str());
|
|
return 0;
|
|
}
|
|
|
|
static int write_to_fifo(int reply_fd, const char * buf, unsigned int len)
|
|
{
|
|
return write_to_fifo(AmConfig::SerFIFO,buf,len);
|
|
}
|
|
|
|
int write_to_fifo(const string& fifo, const char * buf, unsigned int len)
|
|
{
|
|
int fd_fifo;
|
|
int retry = SER_WRITE_TIMEOUT / SER_WRITE_INTERVAL;
|
|
|
|
for(;retry>0; retry--){
|
|
|
|
if((fd_fifo = open(fifo.c_str(),
|
|
O_WRONLY | O_NONBLOCK)) == -1) {
|
|
ERROR("while opening %s: %s\n",
|
|
fifo.c_str(),strerror(errno));
|
|
|
|
if(retry)
|
|
sleep_us(50000);
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!retry)
|
|
return -1;
|
|
|
|
DBG("write_to_fifo: <%s>\n",buf);
|
|
int l = write(fd_fifo,buf,len);
|
|
close(fd_fifo);
|
|
|
|
if(l==-1)
|
|
ERROR("while writing: %s\n",strerror(errno));
|
|
else
|
|
DBG("Write to fifo: completed\n");
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
int read_from_fifo(int fd, char* buffer, int buf_size, int timeout)
|
|
{
|
|
int retry = timeout / SER_READ_INTERVAL;
|
|
int len;
|
|
|
|
for(;retry>0;retry--) {
|
|
|
|
len = read(fd,buffer,buf_size-1);
|
|
if(len > 0){
|
|
|
|
int l,rest_len=0;
|
|
while((l=read(fd,buffer,buf_size))>0)
|
|
rest_len += l;
|
|
|
|
if(rest_len){
|
|
ERROR("Ser's response is too long (size=%i, max=%i)\n",
|
|
rest_len+len,MSG_BUFFER_SIZE);
|
|
return -1;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(retry){
|
|
/* fifo not ready */
|
|
sleep_us(SER_READ_INTERVAL);
|
|
}
|
|
}
|
|
|
|
if(!retry){
|
|
ERROR("no more retries!\n");
|
|
ERROR("last error: %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
buffer[len]='\0';
|
|
//DBG("received: <%s>\n",buffer);
|
|
return len;
|
|
}
|
|
|
|
#ifndef UNIX_PATH_MAX
|
|
#define UNIX_PATH_MAX 108
|
|
#endif
|
|
|
|
int write_to_socket(int reply_fd, const char * buf, unsigned int len)
|
|
{
|
|
int retry = SER_WRITE_TIMEOUT / SER_WRITE_INTERVAL;
|
|
|
|
int sd = reply_fd;
|
|
// int sd = socket(PF_UNIX,SOCK_DGRAM,0);
|
|
// if(sd == -1){
|
|
// ERROR("could not create unix socket: %s\n",strerror(errno));
|
|
// return -1;
|
|
// }
|
|
|
|
int ret=-1;
|
|
if(AmConfig::SerSocketName.empty()){
|
|
ERROR("config parameter 'ser_socket_name' has not been configured !!!\n");
|
|
goto error;
|
|
}
|
|
|
|
struct sockaddr_un ser_addr;
|
|
ser_addr.sun_family = AF_UNIX;
|
|
strncpy(ser_addr.sun_path,AmConfig::SerSocketName.c_str(),
|
|
UNIX_PATH_MAX);
|
|
|
|
for(;retry>0;retry--){
|
|
|
|
if( (sendto(sd,buf,len,MSG_DONTWAIT,
|
|
(struct sockaddr*)&ser_addr,
|
|
sizeof(struct sockaddr_un)) == -1) ) {
|
|
|
|
if(errno == EAGAIN){
|
|
if(retry)
|
|
sleep_us(SER_WRITE_INTERVAL);
|
|
continue;
|
|
}
|
|
|
|
ERROR("while sending request to %s: %s\n",
|
|
ser_addr.sun_path,strerror(errno));
|
|
goto error;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(!retry){
|
|
ERROR("timeout while sending request to %s\n",ser_addr.sun_path);
|
|
goto error;
|
|
}
|
|
|
|
DBG("Write to unix socket: completed\n");
|
|
ret = 0;
|
|
|
|
error:
|
|
// close(sd);
|
|
return (ret == -1 ? ret : len);
|
|
}
|
|
|
|
int read_from_socket(int fd,char* buffer,int buf_size,int timeout)
|
|
{
|
|
fd_set fds;
|
|
FD_ZERO(&fds);
|
|
FD_SET(fd,&fds);
|
|
|
|
struct timeval to;
|
|
to.tv_sec = 0;
|
|
to.tv_usec = timeout;
|
|
|
|
int err = select(fd+1,&fds,NULL,NULL,&to);
|
|
switch(err){
|
|
case 1:// normal case!
|
|
break;
|
|
case 0:
|
|
ERROR("timeout while waiting for Ser to respond\n");
|
|
return -1;
|
|
case -1:
|
|
ERROR("select failed: %s\n",strerror(errno));
|
|
return -1;
|
|
default:
|
|
ERROR(" unexcepted return value for select: %i\n",err);
|
|
return -1;
|
|
}
|
|
|
|
int msg_sz = recv(fd,buffer,buf_size,MSG_TRUNC);
|
|
if(msg_sz == -1){
|
|
ERROR("while receiving return code from Ser (recv failed): %s\n",strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if(msg_sz >= (buf_size-1)){
|
|
ERROR("Ser's response is too long (size=%i, max=%i)\n",msg_sz,buf_size-1);
|
|
return -1;
|
|
}
|
|
|
|
buffer[msg_sz-1] = '\0';
|
|
return msg_sz;
|
|
}
|