mirror of https://github.com/sipwise/kamailio.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.
454 lines
9.7 KiB
454 lines
9.7 KiB
/**
|
|
* Copyright (C) 2015 Bicom Systems Ltd, (bicomsystems.com)
|
|
*
|
|
* Author: Seudin Kasumovic (seudin.kasumovic@gmail.com)
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio 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
|
|
*
|
|
* Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "worker.h"
|
|
#include "cnode.h"
|
|
#include "../../mem/mem.h"
|
|
|
|
#include "erl_api.h"
|
|
|
|
int worker_rpc_impl(ei_cnode *ec, int s, int wpid);
|
|
int worker_reg_send_impl(ei_cnode *ec, int s, int wpid);
|
|
int worker_send_impl(ei_cnode *ec, int s, int wpid);
|
|
|
|
int worker_init(worker_handler_t *phandler, int fd, const ei_cnode *ec)
|
|
{
|
|
if (erl_set_nonblock(fd)){
|
|
LM_ERR("set non blocking failed\n");
|
|
}
|
|
|
|
phandler->handle_f = handle_worker;
|
|
phandler->wait_tmo_f = wait_tmo_worker;
|
|
phandler->destroy_f = NULL;
|
|
phandler->sockfd = fd;
|
|
phandler->ec = *ec;
|
|
phandler->next = NULL;
|
|
phandler->new = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int handle_worker(handler_common_t *phandler)
|
|
{
|
|
worker_handler_t* w = (worker_handler_t*)phandler;
|
|
struct msghdr msg;
|
|
struct iovec cnt[2];
|
|
int wpid = 0;
|
|
eapi_t api;
|
|
int rc;
|
|
|
|
/* ensure be connected */
|
|
enode_connect();
|
|
|
|
memset((void*)&msg,0,sizeof(msg));
|
|
|
|
/* Kamailio worker PID */
|
|
cnt[0].iov_base = &wpid;
|
|
cnt[0].iov_len = sizeof(wpid);
|
|
|
|
/* method */
|
|
cnt[1].iov_base = &api;
|
|
cnt[1].iov_len = sizeof(api);
|
|
|
|
msg.msg_iov = cnt;
|
|
msg.msg_iovlen = 2;
|
|
|
|
while ((rc = recvmsg(w->sockfd, &msg, MSG_WAITALL)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc < 0){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",w->sockfd,strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
switch(api) {
|
|
case API_RPC_CALL:
|
|
if (worker_rpc_impl(&w->ec,w->sockfd,wpid))
|
|
return -1;
|
|
break;
|
|
case API_REG_SEND:
|
|
if (worker_reg_send_impl(&w->ec,w->sockfd,wpid))
|
|
return -1;
|
|
break;
|
|
case API_SEND:
|
|
if (worker_send_impl(&w->ec,w->sockfd,wpid))
|
|
return -1;
|
|
break;
|
|
default:
|
|
LM_ERR("BUG: bad method or not implemented!\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wait_tmo_worker(handler_common_t* phandler){
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* internal implementation
|
|
*/
|
|
int worker_rpc_impl(ei_cnode *ec, int s,int wpid)
|
|
{
|
|
str module = STR_NULL;
|
|
str function = STR_NULL;
|
|
ei_x_buff args;
|
|
ei_x_buff reply;
|
|
struct msghdr msgh;
|
|
struct iovec cnt[6];
|
|
int rc;
|
|
|
|
memset((void*)&args,0,sizeof(args));
|
|
memset((void*)&reply,0,sizeof(reply));
|
|
memset((void*)&msgh,0,sizeof(msgh));
|
|
|
|
/* module name length */
|
|
cnt[0].iov_base = &module.len;
|
|
cnt[0].iov_len = sizeof(int);
|
|
|
|
/* function name length */
|
|
cnt[1].iov_base = &function.len;
|
|
cnt[1].iov_len = sizeof(int);
|
|
|
|
/* Erlang args size */
|
|
cnt[2].iov_base = &args.buffsz;
|
|
cnt[2].iov_len = sizeof(int);
|
|
|
|
/* get data size */
|
|
msgh.msg_iov = cnt;
|
|
msgh.msg_iovlen = 3;
|
|
while ((rc = recvmsg(s, &msgh, MSG_PEEK)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",s,strerror(errno));
|
|
goto err;
|
|
}
|
|
|
|
/* allocate space */
|
|
module.s = (char*)pkg_malloc(module.len+1);
|
|
if (!module.s) {
|
|
LM_ERR("not enough memory\n");
|
|
goto err;
|
|
}
|
|
|
|
function.s = (char*)pkg_malloc(function.len+1);
|
|
if (!function.s) {
|
|
LM_ERR("not enough memory\n");
|
|
goto err;
|
|
}
|
|
|
|
args.buff = (char*)malloc(args.buffsz);
|
|
if (!args.buff) {
|
|
LM_ERR("malloc: not enough memory\n");
|
|
goto err;
|
|
}
|
|
|
|
/* buffers */
|
|
cnt[3].iov_base = module.s;
|
|
cnt[3].iov_len = module.len;
|
|
|
|
cnt[4].iov_base = function.s;
|
|
cnt[4].iov_len = function.len;
|
|
|
|
cnt[5].iov_base = args.buff;
|
|
cnt[5].iov_len = args.buffsz;
|
|
|
|
/* get whole data */
|
|
msgh.msg_iovlen = 6;
|
|
while ((rc = recvmsg(s, &msgh, MSG_WAITALL)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",s,strerror(errno));
|
|
goto err;
|
|
}
|
|
|
|
/* fix str */
|
|
module.s[module.len] = 0;
|
|
function.s[function.len] = 0;
|
|
|
|
LM_DBG("rpc: %.*s:%.*s(args)\n",STR_FMT(&module),STR_FMT(&function));
|
|
|
|
EI_X_BUFF_PRINT(&args);
|
|
|
|
if(!enode) {
|
|
LM_NOTICE("there is no connected Erlang node\n");
|
|
/* reply up with error */
|
|
ei_x_format(&reply, "{error,cnode,~a}", "no_erlang_node");
|
|
goto reply;
|
|
}
|
|
|
|
/* do RPC */
|
|
if ((rc = ei_rpc(ec,enode->sockfd,module.s,function.s,args.buff,args.buffsz,&reply)) == ERL_ERROR)
|
|
{
|
|
reply.index = 0; /* re-use reply buffer */
|
|
|
|
if (erl_errno)
|
|
{
|
|
ei_x_format(&reply, "{error,cnode,~s}", strerror(erl_errno));
|
|
LM_ERR("ei_rpc failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(erl_errno));
|
|
}
|
|
else if (errno)
|
|
{
|
|
ei_x_format(&reply, "{error,cnode,~s}", strerror(errno));
|
|
LM_ERR("ei_rpc failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
ei_x_format(&reply, "{error,cnode,~s}", "Unknown error.");
|
|
LM_ERR("ei_rpc failed on node=<%s> socket=<%d>, Unknown error.\n",ec->thisalivename,enode->sockfd);
|
|
}
|
|
}
|
|
|
|
reply:
|
|
EI_X_BUFF_PRINT(&reply);
|
|
|
|
cnt[0].iov_base = (void*)&wpid;
|
|
cnt[0].iov_len = sizeof(int);
|
|
|
|
/* send reply to Kamailio worker */
|
|
cnt[1].iov_base = (void*)&reply.buffsz;
|
|
cnt[1].iov_len = sizeof(int);
|
|
|
|
cnt[2].iov_base = (void*)reply.buff;
|
|
cnt[2].iov_len = reply.buffsz;
|
|
|
|
msgh.msg_iovlen = 3;
|
|
while ((rc = sendmsg(s, &msgh, 0)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1) {
|
|
LM_ERR("sendmsg failed on socket=%d rpid_no=%d; %s\n",s, wpid, strerror(errno));
|
|
goto err;
|
|
};
|
|
|
|
pkg_free(module.s);
|
|
pkg_free(function.s);
|
|
free(args.buff);
|
|
ei_x_free(&reply);
|
|
return 0;
|
|
|
|
err:
|
|
pkg_free(module.s);
|
|
pkg_free(function.s);
|
|
free(args.buff);
|
|
ei_x_free(&reply);
|
|
abort(); /* cant't recover */
|
|
return -1;
|
|
}
|
|
|
|
int worker_reg_send_impl(ei_cnode *ec, int s,int wpid)
|
|
{
|
|
str server = STR_NULL;
|
|
ei_x_buff emsg;
|
|
struct msghdr msgh;
|
|
struct iovec cnt[6];
|
|
int rc;
|
|
|
|
memset((void*)&emsg,0,sizeof(emsg));
|
|
|
|
memset((void*)&msgh,0,sizeof(msgh));
|
|
|
|
/* server name length */
|
|
cnt[0].iov_base = &server.len;
|
|
cnt[0].iov_len = sizeof(int);
|
|
|
|
/* Erlang args size */
|
|
cnt[1].iov_base = &emsg.buffsz;
|
|
cnt[1].iov_len = sizeof(int);
|
|
|
|
/* get data size */
|
|
msgh.msg_iov = cnt;
|
|
msgh.msg_iovlen = 2;
|
|
|
|
while ((rc = recvmsg(s, &msgh, MSG_PEEK)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",s,strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* allocate space */
|
|
server.s = (char*)pkg_malloc(server.len+1);
|
|
if (!server.s) {
|
|
LM_ERR("not enough memory\n");
|
|
goto err;
|
|
}
|
|
|
|
emsg.buff = (char*)malloc(emsg.buffsz);
|
|
if (!emsg.buff) {
|
|
LM_ERR("malloc: not enough memory\n");
|
|
goto err;
|
|
}
|
|
|
|
/* buffers */
|
|
cnt[2].iov_base = server.s;
|
|
cnt[2].iov_len = server.len;
|
|
|
|
cnt[3].iov_base = emsg.buff;
|
|
cnt[3].iov_len = emsg.buffsz;
|
|
|
|
/* get whole data */
|
|
msgh.msg_iovlen = 4;
|
|
while ((rc = recvmsg(s, &msgh, MSG_WAITALL)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",s,strerror(errno));
|
|
goto err;
|
|
}
|
|
|
|
/* fix str */
|
|
server.s[server.len] = 0;
|
|
|
|
if(!enode) {
|
|
LM_NOTICE("there is no connected Erlang node\n");
|
|
goto err;
|
|
}
|
|
|
|
LM_DBG(">> {%.*s,'%s'} ! emsg\n",STR_FMT(&server),enode->conn.nodename);
|
|
|
|
EI_X_BUFF_PRINT(&emsg);
|
|
|
|
/* do ERL_REG_SEND */
|
|
if ((rc = ei_reg_send(ec,enode->sockfd,server.s,emsg.buff,emsg.buffsz)) == ERL_ERROR)
|
|
{
|
|
if (erl_errno)
|
|
{
|
|
LM_ERR("ei_reg_send failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(erl_errno));
|
|
}
|
|
else if (errno)
|
|
{
|
|
LM_ERR("ei_reg_send failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("ei_reg_send failed on node=<%s> socket=<%d>, Unknown error.\n",ec->thisalivename,enode->sockfd);
|
|
}
|
|
}
|
|
|
|
pkg_free(server.s);
|
|
free(emsg.buff);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
pkg_free(server.s);
|
|
free(emsg.buff);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int worker_send_impl(ei_cnode *ec, int s,int wpid)
|
|
{
|
|
erlang_pid pid;
|
|
ei_x_buff emsg;
|
|
struct msghdr msgh;
|
|
struct iovec cnt[6];
|
|
int rc;
|
|
|
|
memset((void*)&emsg,0,sizeof(emsg));
|
|
|
|
memset((void*)&msgh,0,sizeof(msgh));
|
|
|
|
/* Erlang args size */
|
|
cnt[0].iov_base = &emsg.buffsz;
|
|
cnt[0].iov_len = sizeof(int);
|
|
|
|
/* get data size */
|
|
msgh.msg_iov = cnt;
|
|
msgh.msg_iovlen = 1;
|
|
|
|
while ((rc = recvmsg(s, &msgh, MSG_PEEK)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",s,strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
emsg.buff = (char*)malloc(emsg.buffsz);
|
|
if (!emsg.buff) {
|
|
LM_ERR("malloc: not enough memory\n");
|
|
goto err;
|
|
}
|
|
|
|
/* buffers */
|
|
cnt[1].iov_base = &pid;
|
|
cnt[1].iov_len = sizeof(erlang_pid);
|
|
|
|
cnt[2].iov_base = emsg.buff;
|
|
cnt[2].iov_len = emsg.buffsz;
|
|
|
|
/* get whole data */
|
|
msgh.msg_iovlen = 3;
|
|
while ((rc = recvmsg(s, &msgh, MSG_WAITALL)) == -1 && errno == EAGAIN)
|
|
;
|
|
|
|
if (rc == -1){
|
|
LM_ERR("recvmsg failed (socket=%d): %s\n",s,strerror(errno));
|
|
goto err;
|
|
}
|
|
|
|
if(!enode) {
|
|
LM_NOTICE("there is no connected Erlang node\n");
|
|
goto err;
|
|
}
|
|
|
|
LM_DBG(">> <%s.%d.%d> ! emsg\n",pid.node,pid.num,pid.serial);
|
|
|
|
EI_X_BUFF_PRINT(&emsg);
|
|
|
|
/* do ERL_SEND */
|
|
if ((rc = ei_send(enode->sockfd,&pid,emsg.buff,emsg.buffsz)) == ERL_ERROR)
|
|
{
|
|
if (erl_errno)
|
|
{
|
|
LM_ERR("ei_send failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(erl_errno));
|
|
}
|
|
else if (errno)
|
|
{
|
|
LM_ERR("ei_send failed on node=<%s> socket=<%d>: %s\n",enode->conn.nodename,enode->sockfd,strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("ei_send failed on node=<%s> socket=<%d>, Unknown error.\n",ec->thisalivename,enode->sockfd);
|
|
}
|
|
}
|
|
|
|
free(emsg.buff);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
free(emsg.buff);
|
|
|
|
return -1;
|
|
}
|